summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-03 15:44:41 +0100
committerMark Brown <broonie@kernel.org>2026-07-03 15:44:41 +0100
commit5a42c9f18870162ae14f0d8e193671017421a982 (patch)
tree5c84a4cc4c90157c7e9c833c037d4e825f21f1bb /drivers/gpu
parentcd4f513931c31ca3ed852f370bb573fcbd0721c5 (diff)
parent903f8773ee96c5dc5fb9aec65227f39fd3e7a1dc (diff)
downloadlinux-next-5a42c9f18870162ae14f0d8e193671017421a982.tar.gz
linux-next-5a42c9f18870162ae14f0d8e193671017421a982.zip
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/misc/kernel.git
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/buddy.c107
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c115
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c2
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c9
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.h4
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c32
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c2
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c9
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c8
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c2
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c38
-rw-r--r--drivers/gpu/drm/bridge/fsl-ldb.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qm-ldb.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c2
-rw-r--r--drivers/gpu/drm/bridge/inno-hdmi.c2
-rw-r--r--drivers/gpu/drm/bridge/ite-it6263.c2
-rw-r--r--drivers/gpu/drm/bridge/ite-it6505.c2
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c2
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9211.c2
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c2
-rw-r--r--drivers/gpu/drm/bridge/lvds-codec.c2
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c2
-rw-r--r--drivers/gpu/drm/bridge/of-display-mode-bridge.c2
-rw-r--r--drivers/gpu/drm/bridge/panel.c2
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c2
-rw-r--r--drivers/gpu/drm/bridge/samsung-dsim.c2
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c6
-rw-r--r--drivers/gpu/drm/bridge/ssd2825.c2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c8
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c3
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358762.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c4
-rw-r--r--drivers/gpu/drm/bridge/tc358768.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358775.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-dlpc3433.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c3
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-tdp158.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-tfp410.c2
-rw-r--r--drivers/gpu/drm/clients/drm_log.c10
-rw-r--r--drivers/gpu/drm/display/drm_bridge_connector.c41
-rw-r--r--drivers/gpu/drm/display/drm_hdmi_state_helper.c68
-rw-r--r--drivers/gpu/drm/drm_atomic.c80
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c86
-rw-r--r--drivers/gpu/drm/drm_atomic_state_helper.c154
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c4
-rw-r--r--drivers/gpu/drm/drm_bridge.c124
-rw-r--r--drivers/gpu/drm/drm_buddy.c30
-rw-r--r--drivers/gpu/drm/drm_colorop.c41
-rw-r--r--drivers/gpu/drm/drm_connector.c192
-rw-r--r--drivers/gpu/drm/drm_displayid_internal.h24
-rw-r--r--drivers/gpu/drm/drm_draw_internal.h7
-rw-r--r--drivers/gpu/drm/drm_drv.c4
-rw-r--r--drivers/gpu/drm/drm_dumb_buffers.c1
-rw-r--r--drivers/gpu/drm/drm_edid.c80
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c11
-rw-r--r--drivers/gpu/drm/drm_gem.c3
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c22
-rw-r--r--drivers/gpu/drm/drm_mode_config.c189
-rw-r--r--drivers/gpu/drm/drm_panic.c6
-rw-r--r--drivers/gpu/drm/drm_print.c4
-rw-r--r--drivers/gpu/drm/drm_writeback.c6
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c56
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/Kconfig4
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c110
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c78
-rw-r--r--drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h5
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm.h16
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c92
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_modeset.c110
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_proto.c70
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c43
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.c2
-rw-r--r--drivers/gpu/drm/i915/i915_active.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_drv.c2
-rw-r--r--drivers/gpu/drm/imx/ipuv3/parallel-display.c2
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c2
-rw-r--r--drivers/gpu/drm/lima/lima_device.c12
-rw-r--r--drivers/gpu/drm/loongson/lsdc_drv.c4
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c2
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_cvbs.c2
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_dsi.c2
-rw-r--r--drivers/gpu/drm/meson/meson_encoder_hdmi.c2
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c24
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c4
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c25
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_bridge.c2
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c2
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.c2
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_kms.c18
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_regs.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c35
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c11
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c31
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi4.c2
-rw-r--r--drivers/gpu/drm/omapdrm/dss/hdmi5.c2
-rw-r--r--drivers/gpu/drm/panel/Kconfig56
-rw-r--r--drivers/gpu/drm/panel/Makefile5
-rw-r--r--drivers/gpu/drm/panel/panel-anbernic-td4310.c257
-rw-r--r--drivers/gpu/drm/panel/panel-chipone-icna35xx.c422
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c19
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83121a.c39
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili7807s.c285
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9488.c289
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36672a.c522
-rw-r--r--drivers/gpu/drm/panel/panel-renesas-r63419.c350
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-vtdr6130.c42
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.h3
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c14
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c18
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.h2
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c259
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c15
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c20
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c2
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c2
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c31
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.c2
-rw-r--r--drivers/gpu/drm/rockchip/dw_dp-rockchip.c15
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c227
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c111
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h4
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c176
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop2_reg.c46
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c11
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c9
-rw-r--r--drivers/gpu/drm/scheduler/tests/tests_basic.c359
-rw-r--r--drivers/gpu/drm/scheduler/tests/tests_scheduler.c2
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c350
-rw-r--r--drivers/gpu/drm/stm/lvds.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c2
-rw-r--r--drivers/gpu/drm/sysfb/Kconfig7
-rw-r--r--drivers/gpu/drm/sysfb/simpledrm.c109
-rw-r--r--drivers/gpu/drm/tegra/drm.c2
-rw-r--r--drivers/gpu/drm/tegra/dsi.c126
-rw-r--r--drivers/gpu/drm/tegra/dsi.h10
-rw-r--r--drivers/gpu/drm/tests/Makefile3
-rw-r--r--drivers/gpu/drm/tests/drm_bridge_test.c973
-rw-r--r--drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c347
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_edid.c995
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_edid.h985
-rw-r--r--drivers/gpu/drm/tidss/tidss_crtc.c17
-rw-r--r--drivers/gpu/drm/tidss/tidss_encoder.c2
-rw-r--r--drivers/gpu/drm/tidss/tidss_oldi.c2
-rw-r--r--drivers/gpu/drm/tidss/tidss_plane.c2
-rw-r--r--drivers/gpu/drm/tiny/gm12u320.c5
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool.c37
-rw-r--r--drivers/gpu/drm/v3d/Kconfig1
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.c20
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h36
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c66
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c758
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c2
-rw-r--r--drivers/gpu/drm/verisilicon/vs_bridge.c4
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.c101
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h26
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c71
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c90
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_prime.c45
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c50
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vram.c5
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c2
-rw-r--r--drivers/gpu/drm/xe/xe_device.c4
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c2
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c2
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.c2
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c4
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp.c2
190 files changed, 8297 insertions, 3000 deletions
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index eb1457376307..dc81fe0301ce 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_ALLOCATED;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+ mm->used_scoreboard[gpu_buddy_block_order(block)]++;
+
rbtree_remove(mm, block);
}
@@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm,
{
enum gpu_buddy_free_tree tree;
+ if (gpu_buddy_block_is_allocated(block))
+ mm->used_scoreboard[gpu_buddy_block_order(block)]--;
+
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_FREE;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]++;
+
tree = get_block_tree(block);
rbtree_insert(mm, block, tree);
}
@@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_SPLIT;
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+
rbtree_remove(mm, block);
}
@@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
}
rbtree_remove(mm, buddy);
+ mm->free_scoreboard[gpu_buddy_block_order(buddy)]--;
if (force_merge && gpu_buddy_block_is_clear(buddy))
mm->clear_avail -= gpu_buddy_block_size(mm, buddy);
+ if (gpu_buddy_block_is_allocated(block))
+ mm->used_scoreboard[gpu_buddy_block_order(block)]--;
+
gpu_block_free(mm, block);
gpu_block_free(mm, buddy);
@@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm,
iter = rb_prev(iter);
rbtree_remove(mm, block);
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
if (gpu_buddy_block_is_clear(block))
mm->clear_avail -= gpu_buddy_block_size(mm, block);
@@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER);
+ mm->free_scoreboard = kcalloc(mm->max_order + 1,
+ sizeof(*mm->free_scoreboard),
+ GFP_KERNEL);
+ if (!mm->free_scoreboard)
+ return -ENOMEM;
+
+ mm->used_scoreboard = kcalloc(mm->max_order + 1,
+ sizeof(*mm->used_scoreboard),
+ GFP_KERNEL);
+ if (!mm->used_scoreboard)
+ goto out_free_free_scoreboard;
+
mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES,
sizeof(*mm->free_trees),
GFP_KERNEL);
if (!mm->free_trees)
- return -ENOMEM;
+ goto out_free_used_scoreboard;
for_each_free_tree(i) {
mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
@@ -450,6 +477,10 @@ out_free_tree:
while (i--)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
+out_free_used_scoreboard:
+ kfree(mm->used_scoreboard);
+out_free_free_scoreboard:
+ kfree(mm->free_scoreboard);
return -ENOMEM;
}
EXPORT_SYMBOL(gpu_buddy_init);
@@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
gpu_buddy_assert(mm->avail == mm->size);
+ for (i = 0; i <= mm->max_order; ++i)
+ gpu_buddy_assert(!mm->used_scoreboard[i]);
+
for_each_free_tree(i)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
kfree(mm->roots);
+ kfree(mm->free_scoreboard);
+ kfree(mm->used_scoreboard);
}
EXPORT_SYMBOL(gpu_buddy_fini);
@@ -650,6 +686,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags
return needs_clear != gpu_buddy_block_is_clear(block);
}
+static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
+ struct gpu_buddy_block *block)
+{
+ struct gpu_buddy_block *buddy = __get_buddy(block);
+
+ if (buddy &&
+ (gpu_buddy_block_is_free(block) &&
+ gpu_buddy_block_is_free(buddy))) {
+ rbtree_remove(mm, block);
+ mm->free_scoreboard[gpu_buddy_block_order(block)]--;
+ __gpu_buddy_free(mm, block, false);
+ }
+}
+
static struct gpu_buddy_block *
__alloc_range_bias(struct gpu_buddy *mm,
u64 start, u64 end,
@@ -659,7 +709,6 @@ __alloc_range_bias(struct gpu_buddy *mm,
{
u64 req_size = mm->chunk_size << order;
struct gpu_buddy_block *block;
- struct gpu_buddy_block *buddy;
LIST_HEAD(dfs);
int err;
int i;
@@ -734,11 +783,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -847,8 +892,7 @@ alloc_from_freetree(struct gpu_buddy *mm,
return block;
err_undo:
- if (tmp != order)
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -912,7 +956,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
{
struct gpu_buddy_block *block = NULL;
unsigned int order, tmp, alignment;
- struct gpu_buddy_block *buddy;
enum gpu_buddy_free_tree tree;
unsigned long pages;
int err;
@@ -965,11 +1008,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -980,7 +1019,6 @@ static int __alloc_range(struct gpu_buddy *mm,
u64 *total_allocated_on_err)
{
struct gpu_buddy_block *block;
- struct gpu_buddy_block *buddy;
u64 total_allocated = 0;
LIST_HEAD(allocated);
u64 end;
@@ -1051,11 +1089,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
- buddy = __get_buddy(block);
- if (buddy &&
- (gpu_buddy_block_is_free(block) &&
- gpu_buddy_block_is_free(buddy)))
- __gpu_buddy_free(mm, block, false);
+ __gpu_buddy_undo_splits(mm, block);
err_free:
if (err == -ENOSPC && total_allocated_on_err) {
@@ -1490,27 +1524,18 @@ void gpu_buddy_print(struct gpu_buddy *mm)
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
for (order = mm->max_order; order >= 0; order--) {
- struct gpu_buddy_block *block, *tmp;
- struct rb_root *root;
- u64 count = 0, free;
- unsigned int tree;
-
- for_each_free_tree(tree) {
- root = &mm->free_trees[tree][order];
-
- rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
- BUG_ON(!gpu_buddy_block_is_free(block));
- count++;
- }
- }
-
- free = count * (mm->chunk_size << order);
- if (free < SZ_1M)
- pr_info("order-%2d free: %8llu KiB, blocks: %llu\n",
- order, free >> 10, count);
+ u64 free_count = mm->free_scoreboard[order];
+ u64 used_count = mm->used_scoreboard[order];
+ u64 block_size = mm->chunk_size << order;
+ u64 free = free_count * block_size;
+ u64 used = used_count * block_size;
+
+ if (block_size < SZ_1M)
+ pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n",
+ order, free >> 10, used >> 10, free_count, used_count);
else
- pr_info("order-%2d free: %8llu MiB, blocks: %llu\n",
- order, free >> 20, count);
+ pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n",
+ order, free >> 20, used >> 20, free_count, used_count);
}
}
EXPORT_SYMBOL(gpu_buddy_print);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 18145d78334f..97ae3e32e4a4 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4018,11 +4018,13 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
link->panel_type = PANEL_TYPE_OLED;
}
- /*
- * TODO: get panel type from DID2 that has device technology field
- * to specify if it's OLED or not. But we need to wait for DID2
- * support in DC and EDID parser to be able to use it here.
- */
+ /* If VSDB and DPCD didn't determine panel type, check DID */
+ if (link->panel_type == PANEL_TYPE_NONE) {
+ if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD)
+ link->panel_type = PANEL_TYPE_LCD;
+ else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED)
+ link->panel_type = PANEL_TYPE_OLED;
+ }
if (link->panel_type == PANEL_TYPE_NONE) {
struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
@@ -4040,6 +4042,10 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
drm_object_property_set_value(&connector->base,
adev_to_drm(adev)->mode_config.panel_type_property,
DRM_MODE_PANEL_TYPE_OLED);
+ else if (link->panel_type == PANEL_TYPE_LCD)
+ drm_object_property_set_value(&connector->base,
+ adev_to_drm(adev)->mode_config.panel_type_property,
+ DRM_MODE_PANEL_TYPE_LCD);
else
drm_object_property_set_value(&connector->base,
adev_to_drm(adev)->mode_config.panel_type_property,
@@ -6964,11 +6970,14 @@ static void fill_stream_properties_from_drm_display_mode(
const struct dc_stream_state *old_stream,
int requested_bpc)
{
+ bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal);
struct dc_crtc_timing *timing_out = &stream->timing;
const struct drm_display_info *info = &connector->display_info;
struct amdgpu_dm_connector *aconnector = NULL;
struct hdmi_vendor_infoframe hv_frame;
struct hdmi_avi_infoframe avi_frame;
+ bool want_420;
+ bool want_422;
ssize_t err;
if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
@@ -6981,31 +6990,41 @@ static void fill_stream_properties_from_drm_display_mode(
timing_out->h_border_right = 0;
timing_out->v_border_top = 0;
timing_out->v_border_bottom = 0;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420)
+
+ want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) ||
+ (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420);
+ want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) ||
+ (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422);
+
+ if (drm_mode_is_420_only(info, mode_in) &&
+ (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420
- || aconnector->force_yuv420_output))
+ } else if (drm_mode_is_420_also(info, mode_in) && want_420) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422
- || aconnector->force_yuv422_output))
+ } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) &&
+ want_422 && is_dp_or_hdmi) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444)
+ } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 &&
+ (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) &&
+ is_dp_or_hdmi) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- else
+ } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 ||
+ connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) {
timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+ } else {
+ /*
+ * If a format was explicitly requested but the requested format
+ * can't be satisfied, set it to an invalid value so that an
+ * error bubbles up to userspace. This way, userspace knows it
+ * needs to make a better choice.
+ */
+ if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO)
+ timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED;
+ else if (drm_mode_is_420_only(info, mode_in))
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else
+ timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+ }
timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
timing_out->display_color_depth = convert_color_depth_from_display_info(
@@ -8419,6 +8438,38 @@ cleanup:
return dc_result;
}
+static enum dc_status
+dm_validate_stream_color_format(const struct drm_connector_state *drm_state,
+ const struct dc_stream_state *stream)
+{
+ enum dc_pixel_encoding encoding;
+
+ if (!drm_state->color_format)
+ return DC_OK;
+
+ switch (drm_state->color_format) {
+ case DRM_CONNECTOR_COLOR_FORMAT_AUTO:
+ case DRM_CONNECTOR_COLOR_FORMAT_RGB444:
+ encoding = PIXEL_ENCODING_RGB;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444:
+ encoding = PIXEL_ENCODING_YCBCR444;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422:
+ encoding = PIXEL_ENCODING_YCBCR422;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420:
+ encoding = PIXEL_ENCODING_YCBCR420;
+ break;
+ default:
+ encoding = PIXEL_ENCODING_UNDEFINED;
+ break;
+ }
+
+ return encoding == stream->timing.pixel_encoding ?
+ DC_OK : DC_UNSUPPORTED_VALUE;
+}
+
struct dc_stream_state *
create_validate_stream_for_sink(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
@@ -8466,6 +8517,9 @@ create_validate_stream_for_sink(struct drm_connector *connector,
if (dc_result == DC_OK)
dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
+ if (dc_result == DC_OK)
+ dc_result = dm_validate_stream_color_format(drm_state, stream);
+
if (dc_result != DC_OK) {
drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
drm_mode->hdisplay,
@@ -9295,6 +9349,12 @@ static const u32 supported_colorspaces =
BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
+static const u32 supported_colorformats =
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
+
void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
struct amdgpu_dm_connector *aconnector,
int connector_type,
@@ -9411,8 +9471,11 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
connector_type == DRM_MODE_CONNECTOR_eDP) {
drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
- if (!aconnector->mst_root)
+ if (!aconnector->mst_root) {
drm_connector_attach_vrr_capable_property(&aconnector->base);
+ drm_connector_attach_color_format_property(&aconnector->base,
+ supported_colorformats);
+ }
if (adev->dm.hdcp_workqueue)
drm_connector_attach_content_protection_property(&aconnector->base, true);
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 02f8f7e78a16..db99e252aeeb 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -1004,7 +1004,7 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = {
.atomic_disable = adv7511_bridge_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .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,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 8cf6b73bceac..7a85774aaac1 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -619,7 +619,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
for (;;) {
timeout_loop++;
- if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
+ if (analogix_dp_is_slave_video_stream_clock_on(dp))
break;
if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
dev_err(dp->dev, "Timeout of slave video streamclk ok\n");
@@ -647,7 +647,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
for (;;) {
timeout_loop++;
- if (analogix_dp_is_video_stream_on(dp) == 0) {
+ if (analogix_dp_is_video_stream_on(dp)) {
done_count++;
if (done_count > 10)
break;
@@ -870,7 +870,7 @@ static int analogix_dp_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_display_info *di = &conn_state->connector->display_info;
u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
- if (is_rockchip(dp->plat_data->dev_type)) {
+ if (analogix_dp_is_rockchip(dp->plat_data->dev_type)) {
if ((di->color_formats & mask)) {
DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
di->color_formats &= ~mask;
@@ -1223,7 +1223,7 @@ static void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable,
.atomic_enable = analogix_dp_bridge_atomic_enable,
.atomic_disable = analogix_dp_bridge_atomic_disable,
@@ -1249,6 +1249,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
video_info->max_link_rate = 0x0A;
video_info->max_lane_count = 0x04;
break;
+ case RK3576_EDP:
case RK3588_EDP:
video_info->max_link_rate = 0x14;
video_info->max_lane_count = 0x04;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 17347448c6b0..94348c4e3623 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -211,7 +211,7 @@ void analogix_dp_reset_macro(struct analogix_dp_device *dp);
void analogix_dp_init_video(struct analogix_dp_device *dp);
void analogix_dp_set_video_color_format(struct analogix_dp_device *dp);
-int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp);
+bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp);
void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
enum clock_recovery_m_value_type type,
u32 m_value,
@@ -220,7 +220,7 @@ void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type);
void analogix_dp_enable_video_master(struct analogix_dp_device *dp,
bool enable);
void analogix_dp_start_video(struct analogix_dp_device *dp);
-int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
+bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 38fd8d5014d2..ea8401293a23 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -72,7 +72,7 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
reg = SEL_24M | TX_DVDD_BIT_1_0625V;
writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2);
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) {
reg = REF_CLK_24M;
if (dp->plat_data->dev_type == RK3288_DP)
reg ^= REF_CLK_MASK;
@@ -123,7 +123,7 @@ void analogix_dp_reset(struct analogix_dp_device *dp)
analogix_dp_stop_video(dp);
analogix_dp_enable_video_mute(dp, 0);
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N |
SW_FUNC_EN_N;
else
@@ -233,7 +233,7 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
u32 mask = DP_PLL_PD;
u32 pd_addr = ANALOGIX_DP_PLL_CTL;
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) {
pd_addr = ANALOGIX_DP_PD;
mask = RK_PLL_PD;
}
@@ -254,12 +254,12 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
u32 phy_pd_addr = ANALOGIX_DP_PHY_PD;
u32 mask;
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
phy_pd_addr = ANALOGIX_DP_PD;
switch (block) {
case AUX_BLOCK:
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
mask = RK_AUX_PD;
else
mask = AUX_PD;
@@ -317,7 +317,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
* to power off everything instead of DP_PHY_PD in
* Rockchip
*/
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
mask = DP_INC_BG;
else
mask = DP_PHY_PD;
@@ -329,7 +329,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
reg &= ~mask;
writel(reg, dp->reg_base + phy_pd_addr);
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
usleep_range(10, 15);
break;
case POWER_ALL:
@@ -465,7 +465,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
analogix_dp_reset_aux(dp);
/* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type))
reg = 0;
else
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3);
@@ -713,7 +713,7 @@ void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
}
-int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
+bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
{
u32 reg;
@@ -724,7 +724,7 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
if (!(reg & DET_STA)) {
dev_dbg(dp->dev, "Input stream clock not detected.\n");
- return -EINVAL;
+ return false;
}
reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
@@ -735,10 +735,10 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
if (reg & CHA_STA) {
dev_dbg(dp->dev, "Input stream clk is changing\n");
- return -EINVAL;
+ return false;
}
- return 0;
+ return true;
}
void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
@@ -816,7 +816,7 @@ void analogix_dp_start_video(struct analogix_dp_device *dp)
writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
}
-int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
+bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
{
u32 reg;
@@ -826,10 +826,10 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
if (!(reg & STRM_VALID)) {
dev_dbg(dp->dev, "Input video stream is not detected.\n");
- return -EINVAL;
+ return false;
}
- return 0;
+ return true;
}
void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
@@ -837,7 +837,7 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
u32 reg;
reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
- if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+ if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) {
reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N);
} else {
reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index fffcd6154c71..ebd34657908e 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -2679,7 +2679,7 @@ static const struct drm_bridge_funcs anx7625_bridge_funcs = {
.atomic_disable = anx7625_bridge_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.detect = anx7625_bridge_detect,
.edid_read = anx7625_bridge_edid_read,
.hpd_enable = anx7625_bridge_hpd_enable,
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
index cf90d4468b5c..c3e466ded84a 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
@@ -1009,16 +1009,15 @@ cdns_dsi_bridge_atomic_destroy_state(struct drm_bridge *bridge,
}
static struct drm_bridge_state *
-cdns_dsi_bridge_atomic_reset(struct drm_bridge *bridge)
+cdns_dsi_bridge_atomic_create_state(struct drm_bridge *bridge)
{
struct cdns_dsi_bridge_state *dsi_state;
dsi_state = kzalloc_obj(*dsi_state);
if (!dsi_state)
- return NULL;
+ return ERR_PTR(-ENOMEM);
- memset(dsi_state, 0, sizeof(*dsi_state));
- dsi_state->base.bridge = bridge;
+ __drm_atomic_helper_bridge_state_init(&dsi_state->base, bridge);
return &dsi_state->base;
}
@@ -1029,7 +1028,7 @@ static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = {
.atomic_pre_enable = cdns_dsi_bridge_atomic_pre_enable,
.atomic_post_disable = cdns_dsi_bridge_atomic_post_disable,
.atomic_check = cdns_dsi_bridge_atomic_check,
- .atomic_reset = cdns_dsi_bridge_atomic_reset,
+ .atomic_create_state = cdns_dsi_bridge_atomic_create_state,
.atomic_duplicate_state = cdns_dsi_bridge_atomic_duplicate_state,
.atomic_destroy_state = cdns_dsi_bridge_atomic_destroy_state,
.atomic_get_input_bus_fmts = cdns_dsi_bridge_get_input_bus_fmts,
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index 36c07b71fe04..504a3186ebb3 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -1921,15 +1921,15 @@ cdns_mhdp_bridge_atomic_destroy_state(struct drm_bridge *bridge,
}
static struct drm_bridge_state *
-cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge)
+cdns_mhdp_bridge_atomic_create_state(struct drm_bridge *bridge)
{
struct cdns_mhdp_bridge_state *cdns_mhdp_state;
cdns_mhdp_state = kzalloc_obj(*cdns_mhdp_state);
if (!cdns_mhdp_state)
- return NULL;
+ return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_bridge_reset(bridge, &cdns_mhdp_state->base);
+ __drm_atomic_helper_bridge_state_init(&cdns_mhdp_state->base, bridge);
return &cdns_mhdp_state->base;
}
@@ -2051,7 +2051,7 @@ static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = {
.detach = cdns_mhdp_detach,
.atomic_duplicate_state = cdns_mhdp_bridge_atomic_duplicate_state,
.atomic_destroy_state = cdns_mhdp_bridge_atomic_destroy_state,
- .atomic_reset = cdns_mhdp_bridge_atomic_reset,
+ .atomic_create_state = cdns_mhdp_bridge_atomic_create_state,
.atomic_get_input_bus_fmts = cdns_mhdp_get_input_bus_fmts,
.detect = cdns_mhdp_bridge_detect,
.edid_read = cdns_mhdp_bridge_edid_read,
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index d8344e011818..eb628a0ce6ec 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -618,7 +618,7 @@ chipone_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs chipone_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = chipone_atomic_pre_enable,
.atomic_enable = chipone_atomic_enable,
.atomic_post_disable = chipone_atomic_post_disable,
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index 16c0631adeb1..d1e74dd2e051 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -12,6 +12,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@@ -25,6 +26,8 @@ struct display_connector {
struct regulator *supply;
struct gpio_desc *ddc_en;
+
+ struct work_struct hpd_work;
};
static inline struct display_connector *
@@ -87,6 +90,34 @@ display_connector_bridge_detect(struct drm_bridge *bridge, struct drm_connector
return display_connector_detect(bridge);
}
+static void display_connector_hpd_enable(struct drm_bridge *bridge)
+{
+ struct display_connector *conn = to_display_connector(bridge);
+
+ enable_irq(conn->hpd_irq);
+
+ if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort)
+ schedule_work(&conn->hpd_work);
+}
+
+static void display_connector_hpd_disable(struct drm_bridge *bridge)
+{
+ struct display_connector *conn = to_display_connector(bridge);
+
+ if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort)
+ cancel_work_sync(&conn->hpd_work);
+
+ disable_irq(conn->hpd_irq);
+}
+
+static void display_connector_hpd_work(struct work_struct *work)
+{
+ struct display_connector *conn = container_of(work, struct display_connector, hpd_work);
+ struct drm_bridge *bridge = &conn->bridge;
+
+ drm_bridge_hpd_notify(bridge, display_connector_detect(bridge));
+}
+
static const struct drm_edid *display_connector_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -178,12 +209,14 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
.attach = display_connector_attach,
.detect = display_connector_bridge_detect,
+ .hpd_enable = display_connector_hpd_enable,
+ .hpd_disable = display_connector_hpd_disable,
.edid_read = display_connector_edid_read,
.atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts,
.atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static irqreturn_t display_connector_hpd_irq(int irq, void *arg)
@@ -307,6 +340,7 @@ static int display_connector_probe(struct platform_device *pdev)
NULL, display_connector_hpd_irq,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
+ IRQF_NO_AUTOEN |
IRQF_ONESHOT,
"HPD", conn);
if (ret) {
@@ -378,6 +412,8 @@ static int display_connector_probe(struct platform_device *pdev)
conn->bridge.ops |= DRM_BRIDGE_OP_DETECT;
if (conn->hpd_irq >= 0)
conn->bridge.ops |= DRM_BRIDGE_OP_HPD;
+ if (conn->hpd_irq >= 0 && type == DRM_MODE_CONNECTOR_DisplayPort)
+ INIT_WORK(&conn->hpd_work, display_connector_hpd_work);
dev_dbg(&pdev->dev,
"Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n",
diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
index 9bfaa3f93370..cc1f88e7873e 100644
--- a/drivers/gpu/drm/bridge/fsl-ldb.c
+++ b/drivers/gpu/drm/bridge/fsl-ldb.c
@@ -289,7 +289,7 @@ static const struct drm_bridge_funcs funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = fsl_ldb_atomic_get_input_bus_fmts,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = fsl_ldb_mode_valid,
};
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
index 7d5fda7173e5..a31d4fc76f35 100644
--- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
@@ -131,7 +131,7 @@ static const struct drm_bridge_funcs imx_hdmi_pvi_bridge_funcs = {
.atomic_get_input_bus_fmts = imx8mp_hdmi_pvi_bridge_get_input_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c
index a6ca4f5c6cc6..5884af991547 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c
@@ -389,7 +389,7 @@ imx8qm_ldb_bridge_mode_valid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs imx8qm_ldb_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = imx8qm_ldb_bridge_mode_valid,
.attach = ldb_bridge_attach_helper,
.atomic_check = imx8qm_ldb_bridge_atomic_check,
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
index a7906314ade1..b95c640564e4 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
@@ -404,7 +404,7 @@ static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = {
.destroy = imx8qxp_ldb_bridge_destroy,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = imx8qxp_ldb_bridge_mode_valid,
.attach = ldb_bridge_attach_helper,
.atomic_check = imx8qxp_ldb_bridge_atomic_check,
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
index e0ee51a9ca7f..a5a1bf42325b 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
@@ -251,7 +251,7 @@ imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = imx8qxp_pc_bridge_mode_valid,
.attach = imx8qxp_pc_bridge_attach,
.mode_set = imx8qxp_pc_bridge_mode_set,
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
index ee6b6dbbe952..56ce6aa76181 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
@@ -229,7 +229,7 @@ imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = imx8qxp_pixel_link_bridge_attach,
.mode_set = imx8qxp_pixel_link_bridge_mode_set,
.atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable,
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
index 87305d3e0c39..dce1a5b36024 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
@@ -210,7 +210,7 @@ imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = imx8qxp_pxl2dpi_bridge_attach,
.destroy = imx8qxp_pxl2dpi_bridge_destroy,
.atomic_check = imx8qxp_pxl2dpi_bridge_atomic_check,
diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index 9a62bf59a403..dd35f5a875d3 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -878,7 +878,7 @@ static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = inno_hdmi_bridge_atomic_enable,
.atomic_disable = inno_hdmi_bridge_atomic_disable,
.detect = inno_hdmi_bridge_detect,
diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c
index c7d588be12cb..4c0b670806cc 100644
--- a/drivers/gpu/drm/bridge/ite-it6263.c
+++ b/drivers/gpu/drm/bridge/ite-it6263.c
@@ -829,7 +829,7 @@ static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
static const struct drm_bridge_funcs it6263_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .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,
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 6b7cb132a437..8ecb43611dba 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -3239,7 +3239,7 @@ static const struct drm_edid *it6505_bridge_edid_read(struct drm_bridge *bridge,
static const struct drm_bridge_funcs it6505_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = it6505_bridge_attach,
.detach = it6505_bridge_detach,
.mode_valid = it6505_bridge_mode_valid,
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index a2f48db369e3..51fc68ffdf8f 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -1543,7 +1543,7 @@ static int it66121_hdmi_audio_mute_stream(struct drm_bridge *bridge,
static const struct drm_bridge_funcs it66121_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = it66121_bridge_attach,
.atomic_get_output_bus_fmts = it66121_bridge_atomic_get_output_bus_fmts,
.atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts,
diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c
index 870175a84839..f39d83a5ae37 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9211.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9211.c
@@ -619,7 +619,7 @@ static const struct drm_bridge_funcs lt9211_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = lt9211_atomic_get_input_bus_fmts,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int lt9211_parse_dt(struct lt9211 *ctx)
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index 21305296e111..b6a368f71285 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -1026,7 +1026,7 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
.atomic_post_disable = lt9611_bridge_atomic_post_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .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,
diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c
index e6a7147e141b..d1135dc3b99f 100644
--- a/drivers/gpu/drm/bridge/lvds-codec.c
+++ b/drivers/gpu/drm/bridge/lvds-codec.c
@@ -104,7 +104,7 @@ static const struct drm_bridge_funcs funcs = {
.disable = lvds_codec_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = lvds_codec_atomic_get_input_bus_fmts,
};
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index 3c9ae93c4f67..09992529f3d8 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -960,7 +960,7 @@ static u32 *nwl_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_check = nwl_dsi_bridge_atomic_check,
.atomic_enable = nwl_dsi_bridge_atomic_enable,
.atomic_disable = nwl_dsi_bridge_atomic_disable,
diff --git a/drivers/gpu/drm/bridge/of-display-mode-bridge.c b/drivers/gpu/drm/bridge/of-display-mode-bridge.c
index cb15713f3a79..e66dae168fd0 100644
--- a/drivers/gpu/drm/bridge/of-display-mode-bridge.c
+++ b/drivers/gpu/drm/bridge/of-display-mode-bridge.c
@@ -51,7 +51,7 @@ static int of_display_mode_bridge_get_modes(struct drm_bridge *bridge,
return 0;
}
-struct drm_bridge_funcs of_display_mode_bridge_funcs = {
+static const struct drm_bridge_funcs of_display_mode_bridge_funcs = {
.attach = of_display_mode_bridge_attach,
.get_modes = of_display_mode_bridge_get_modes,
};
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 4978ec98a082..62af499f1f5c 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -213,7 +213,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.atomic_disable = panel_bridge_atomic_disable,
.atomic_post_disable = panel_bridge_atomic_post_disable,
.get_modes = panel_bridge_get_modes,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index b93514023baa..96332721cb69 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -556,7 +556,7 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = {
.atomic_pre_enable = ps8640_atomic_pre_enable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 9ee0515074c7..5788066d479e 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1835,7 +1835,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = samsung_dsim_atomic_get_input_bus_fmts,
.atomic_check = samsung_dsim_atomic_check,
.atomic_pre_enable = samsung_dsim_atomic_pre_enable,
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index ea543be4ae3e..f575b2dbe32b 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -544,7 +544,7 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = {
.atomic_enable = sii902x_bridge_atomic_enable,
.detect = sii902x_bridge_detect,
.edid_read = sii902x_bridge_edid_read,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = sii902x_bridge_atomic_get_input_bus_fmts,
@@ -1158,9 +1158,7 @@ static int sii902x_probe(struct i2c_client *client)
static const char * const supplies[] = {"iovcc", "cvcc12"};
int ret;
- ret = i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA);
- if (!ret) {
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "I2C adapter not suitable\n");
return -EIO;
}
diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c
index 54ca6bd6883a..00a4ed4a0700 100644
--- a/drivers/gpu/drm/bridge/ssd2825.c
+++ b/drivers/gpu/drm/bridge/ssd2825.c
@@ -679,7 +679,7 @@ static const struct drm_bridge_funcs ssd2825_bridge_funcs = {
.atomic_enable = ssd2825_bridge_atomic_enable,
.atomic_disable = ssd2825_bridge_atomic_disable,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 21541be094c4..3445c82e6f50 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1816,7 +1816,7 @@ static struct drm_bridge_state *dw_dp_bridge_atomic_duplicate_state(struct drm_b
static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.atomic_duplicate_state = dw_dp_bridge_atomic_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.atomic_get_output_bus_fmts = dw_dp_bridge_atomic_get_output_bus_fmts,
.atomic_check = dw_dp_bridge_atomic_check,
@@ -2093,6 +2093,12 @@ unregister_aux:
}
EXPORT_SYMBOL_GPL(dw_dp_bind);
+void dw_dp_unbind(struct dw_dp *dp)
+{
+ drm_dp_aux_unregister(&dp->aux);
+}
+EXPORT_SYMBOL_GPL(dw_dp_unbind);
+
MODULE_AUTHOR("Andy Yan <andyshrk@163.com>");
MODULE_DESCRIPTION("DW DP Core Library");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 1c214a8e6dc2..cfd1b18c06e6 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -1190,9 +1190,10 @@ static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts,
#endif /* CONFIG_DRM_DW_HDMI_QP_CEC */
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,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
.detect = dw_hdmi_qp_bridge_detect,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 17d5caedb32e..a408dbd414bf 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3027,7 +3027,7 @@ static const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = dw_hdmi_bridge_attach,
.detach = dw_hdmi_bridge_detach,
.atomic_check = dw_hdmi_bridge_atomic_check,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index af176e136f85..5dd475fcaacf 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -1095,7 +1095,7 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = dw_mipi_dsi_bridge_atomic_get_input_bus_fmts,
.atomic_check = dw_mipi_dsi_bridge_atomic_check,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = dw_mipi_dsi_bridge_atomic_pre_enable,
.atomic_enable = dw_mipi_dsi_bridge_atomic_enable,
.atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
index b2a022d266d9..5f06cb03b4c0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
@@ -893,7 +893,7 @@ static const struct drm_bridge_funcs dw_mipi_dsi2_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts,
.atomic_check = dw_mipi_dsi2_bridge_atomic_check,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = dw_mipi_dsi2_bridge_atomic_pre_enable,
.atomic_enable = dw_mipi_dsi2_bridge_atomic_enable,
.atomic_post_disable = dw_mipi_dsi2_bridge_post_atomic_disable,
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index 6aa93b3274dd..6124d3123f57 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -225,7 +225,7 @@ static const struct drm_bridge_funcs tc358762_bridge_funcs = {
.atomic_enable = tc358762_enable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = tc358762_attach,
.mode_set = tc358762_bridge_mode_set,
};
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 7188935fdb82..966c3eae437e 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -1933,7 +1933,7 @@ static const struct drm_bridge_funcs tc_dpi_bridge_funcs = {
.atomic_disable = tc_dpi_bridge_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = tc_dpi_atomic_get_input_bus_fmts,
};
@@ -1949,7 +1949,7 @@ static const struct drm_bridge_funcs tc_edp_bridge_funcs = {
.edid_read = tc_edid_read,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.atomic_get_output_bus_fmts = tc_edp_atomic_get_output_bus_fmts,
};
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 0f2820b37302..0d85120fcc7a 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -1290,7 +1290,7 @@ static const struct drm_bridge_funcs tc358768_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = tc358768_atomic_get_input_bus_fmts,
};
diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c
index e067b671cece..728938f9dcb4 100644
--- a/drivers/gpu/drm/bridge/tc358775.c
+++ b/drivers/gpu/drm/bridge/tc358775.c
@@ -598,7 +598,7 @@ static const struct drm_bridge_funcs tc_bridge_funcs = {
.atomic_enable = tc_bridge_atomic_enable,
.mode_valid = tc_mode_valid,
.atomic_post_disable = tc_bridge_atomic_post_disable,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
};
diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c
index 9c35eade0a78..c16eb3a05cfa 100644
--- a/drivers/gpu/drm/bridge/ti-dlpc3433.c
+++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c
@@ -254,7 +254,7 @@ static const struct drm_bridge_funcs dlpc_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = dlpc_atomic_pre_enable,
.atomic_enable = dlpc_atomic_enable,
.atomic_post_disable = dlpc_atomic_post_disable,
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index 42b451432bbb..e6dbe51d0dba 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -801,7 +801,7 @@ static const struct drm_bridge_funcs sn65dsi83_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts,
};
@@ -977,7 +977,6 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
dsi->lanes = dsi_lanes;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP |
MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET;
ret = devm_mipi_dsi_attach(dev, dsi);
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 0884ec459cc5..1da242f17413 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -1302,7 +1302,7 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
.atomic_enable = ti_sn_bridge_atomic_enable,
.atomic_disable = ti_sn_bridge_atomic_disable,
.atomic_post_disable = ti_sn_bridge_atomic_post_disable,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.debugfs_init = ti_sn65dsi86_debugfs_init,
diff --git a/drivers/gpu/drm/bridge/ti-tdp158.c b/drivers/gpu/drm/bridge/ti-tdp158.c
index 3a19967f213f..01fc643bd78f 100644
--- a/drivers/gpu/drm/bridge/ti-tdp158.c
+++ b/drivers/gpu/drm/bridge/ti-tdp158.c
@@ -60,7 +60,7 @@ static const struct drm_bridge_funcs tdp158_bridge_funcs = {
.atomic_disable = tdp158_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int tdp158_probe(struct i2c_client *client)
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index bf4ab4eaf269..6f06f7b4e992 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -245,7 +245,7 @@ static const struct drm_bridge_funcs tfp410_bridge_funcs = {
.enable = tfp410_enable,
.disable = tfp410_disable,
.mode_valid = tfp410_mode_valid,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = tfp410_get_input_bus_fmts,
diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c
index 8d21b785bead..e3e02c84a4cf 100644
--- a/drivers/gpu/drm/clients/drm_log.c
+++ b/drivers/gpu/drm/clients/drm_log.c
@@ -122,10 +122,12 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
iosys_map_incr(&map, r.y1 * fb->pitches[0]);
for (i = 0; i < len && i < scanout->columns; i++) {
u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color;
- src = drm_draw_get_char_bitmap(font, s[i], font_pitch);
- drm_log_blit(&map, fb->pitches[0], src, font_pitch,
- scanout->scaled_font_h, scanout->scaled_font_w,
- px_width, color);
+ src = font_data_glyph_buf(font->data, font->width, font->height,
+ (unsigned char)s[i]);
+ if (src)
+ drm_log_blit(&map, fb->pitches[0], src, font_pitch,
+ scanout->scaled_font_h, scanout->scaled_font_w,
+ px_width, color);
iosys_map_incr(&map, scanout->scaled_font_w * px_width);
}
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 649969fca141..92f8a2d7aab4 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -265,26 +265,57 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
}
}
-static void drm_bridge_connector_reset(struct drm_connector *connector)
+static struct drm_connector_state *
+drm_bridge_connector_create_state(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
+ struct drm_connector_state *conn_state;
+
+ conn_state = drm_atomic_helper_connector_create_state(connector);
+ if (IS_ERR(conn_state))
+ return conn_state;
- drm_atomic_helper_connector_reset(connector);
if (bridge_connector->bridge_hdmi)
- __drm_atomic_helper_connector_hdmi_reset(connector,
- connector->state);
+ __drm_atomic_helper_connector_hdmi_state_init(connector,
+ conn_state);
+
+ return conn_state;
+}
+
+static enum drm_connector_color_format
+drm_bridge_connector_color_format(const struct drm_connector_state *conn_state)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(conn_state->connector);
+
+ if (bridge_connector->bridge_hdmi) {
+ switch (conn_state->hdmi.output_format) {
+ default:
+ case DRM_OUTPUT_COLOR_FORMAT_RGB444:
+ return DRM_CONNECTOR_COLOR_FORMAT_RGB444;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR444:
+ return DRM_CONNECTOR_COLOR_FORMAT_YCBCR444;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR422:
+ return DRM_CONNECTOR_COLOR_FORMAT_YCBCR422;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR420:
+ return DRM_CONNECTOR_COLOR_FORMAT_YCBCR420;
+ }
+ }
+
+ return conn_state->color_format;
}
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
- .reset = drm_bridge_connector_reset,
.detect = drm_bridge_connector_detect,
.force = drm_bridge_connector_force,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .atomic_create_state = drm_bridge_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
+ .color_format = drm_bridge_connector_color_format,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index 4867edbf2622..ce17eeefc2da 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -306,17 +306,18 @@
*/
/**
- * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources
+ * __drm_atomic_helper_connector_hdmi_state_init() - Initialize all HDMI @drm_connector_state resources
* @connector: DRM connector
- * @new_conn_state: connector state to reset
+ * @new_conn_state: connector state to initialize
*
* Initializes all HDMI resources from a @drm_connector_state without
* actually allocating it. This is useful for HDMI drivers, in
- * combination with __drm_atomic_helper_connector_reset() or
- * drm_atomic_helper_connector_reset().
+ * combination with __drm_atomic_helper_connector_state_init(),
+ * drm_atomic_helper_connector_reset(), or
+ * drm_atomic_helper_connector_create_state().
*/
-void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
- struct drm_connector_state *new_conn_state)
+void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector,
+ struct drm_connector_state *new_conn_state)
{
unsigned int max_bpc = connector->max_bpc;
@@ -324,7 +325,7 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
new_conn_state->max_requested_bpc = max_bpc;
new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
}
-EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
+EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_state_init);
static enum hdmi_colorspace
output_color_format_to_hdmi_colorspace(const struct drm_connector *connector,
@@ -541,10 +542,11 @@ sink_supports_format_bpc(const struct drm_connector *connector,
drm_dbg_kms(dev, "YUV444 format supported in that configuration.\n");
return true;
- }
- drm_dbg_kms(dev, "Unsupported pixel format.\n");
- return false;
+ default:
+ drm_dbg_kms(dev, "Unsupported pixel format.\n");
+ return false;
+ }
}
static enum drm_mode_status
@@ -669,8 +671,39 @@ hdmi_compute_config(const struct drm_connector *connector,
unsigned int max_bpc = clamp_t(unsigned int,
conn_state->max_bpc,
8, connector->max_bpc);
+ enum drm_output_color_format fmt;
int ret;
+ if (conn_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) {
+ switch (conn_state->color_format) {
+ case DRM_CONNECTOR_COLOR_FORMAT_AUTO:
+ drm_warn(connector->dev, "AUTO format in non-AUTO path.\n");
+ fallthrough;
+ case DRM_CONNECTOR_COLOR_FORMAT_RGB444:
+ fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444:
+ fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR444;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422:
+ fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR422;
+ break;
+ case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420:
+ fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420;
+ break;
+ default:
+ drm_dbg_kms(connector->dev, "HDMI does not support color format '%d'.\n",
+ conn_state->color_format);
+ return -EINVAL;
+ }
+
+ return hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, fmt);
+ }
+
+ /*
+ * For %DRM_CONNECTOR_COLOR_FORMAT_AUTO, try RGB first, and fall back
+ * to the less bandwidth-intensive YCBCR420 if RGB fails.
+ */
ret = hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc,
DRM_OUTPUT_COLOR_FORMAT_RGB444);
if (ret) {
@@ -910,8 +943,21 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
{
unsigned long long clock;
+ enum drm_output_color_format fmt;
+
+ if (drm_mode_is_420_only(&connector->display_info, mode)) {
+ if (connector->ycbcr_420_allowed)
+ fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420;
+ else
+ return MODE_NO_420;
+ } else if (drm_mode_is_420_also(&connector->display_info, mode) &&
+ connector->ycbcr_420_allowed) {
+ fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420;
+ } else {
+ fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444;
+ }
- clock = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ clock = drm_hdmi_compute_mode_clock(mode, 8, fmt);
if (!clock)
return MODE_ERROR;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 080aec5a9774..735ab7badc2e 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -47,6 +47,86 @@
#include "drm_crtc_internal.h"
#include "drm_internal.h"
+/**
+ * DOC: state lifetime
+ *
+ * &drm_atomic_commit represents an update to modeset pipeline state.
+ * It's a transient object that holds a state update as a collection of
+ * pointers to individual objects' states. &drm_atomic_commit has a much
+ * shorter lifetime than the objects' states, since it's only allocated
+ * while preparing, checking or committing the update, while object
+ * states are allocated when preparing the update and kept alive as long
+ * as they are active in the device.
+ *
+ * Their respective lifetimes are:
+ *
+ * - at driver initialization time, the driver calls
+ * drm_mode_config_create_initial_state() to allocate an initial,
+ * pristine, state for each object and stores it in the objects state
+ * pointer. Historically, this was one of drm_mode_config_reset() job,
+ * so one might still encounter it in a driver.
+ *
+ * - When resuming from suspend, drm_mode_config_reset() resets the
+ * software and hardware state to a known default and stores it in the
+ * object's state pointer. Not all objects are affected by
+ * drm_mode_config_reset() though.
+ *
+ * - whenever a new update is needed:
+ *
+ * + drm_atomic_commit_alloc() allocates a new &drm_atomic_commit
+ * instance.
+ *
+ * + The code triggering the commit (ioctl, client modeset,
+ * drm_atomic_helper_reset_crtc(), etc.) copies the current active
+ * state of all entities affected by the update into this new
+ * &drm_atomic_commit using drm_atomic_get_plane_state(),
+ * drm_atomic_get_crtc_state(), drm_atomic_get_connector_state(), or
+ * drm_atomic_get_private_obj_state(). This new state can then be
+ * modified.
+ *
+ * At that point, &drm_atomic_commit stores three state pointers for
+ * any affected entity: the "old" and "new" states, and
+ * state_to_destroy. The old state is the state currently active in
+ * the hardware, which is either the one initialized by reset() or a
+ * newer one if a commit has been made. The new state is the state
+ * we just allocated and we might eventually commit to the hardware.
+ * The state_to_destroy points to the state we'll eventually have to
+ * free when the drm_atomic_commit will be destroyed, and points to
+ * the new state for now since the old state is still the active
+ * state.
+ *
+ * + After the calling code populated the commit with the entities
+ * states, it updates the new states with the new values we need to
+ * commit. The new commit instance is now ready.
+ *
+ * + Then we have two branches depending on the calling code intent:
+ *
+ * - If the calling code only wants to check that the commit would
+ * work (for example because of the DRM_MODE_ATOMIC_TEST_ONLY
+ * flag). It calls drm_atomic_check_only(), which in turn checks
+ * all these states by invoking atomic_check on all affected
+ * pipeline stages.
+ *
+ * - If the calling code actually wants to trigger a commit, it
+ * calls drm_atomic_commit(). The first stage is the check
+ * mentioned above, and if the check is successful, it performs
+ * the commit. Part of the commit is a call to
+ * drm_atomic_helper_swap_state() which turns the new states into
+ * the active states. After swapping states, each object's state
+ * pointer now refers to the formerly new state. The
+ * state_to_destroy now refers to the formerly old state.
+ *
+ * + Once done, and when the last reference to our &drm_atomic_commit
+ * is given up through drm_atomic_commit_put(), it calls
+ * __drm_atomic_commit_free(). In turn, __drm_atomic_commit_free()
+ * calls drm_atomic_commit_clear() that will free all
+ * state_to_destroy (ie. old states), and it finally frees
+ * &drm_atomic_commit instance.
+ *
+ * + Now, we don't have any active &drm_atomic_commit anymore, and
+ * only the entity active states remain allocated.
+ */
+
void __drm_crtc_commit_free(struct kref *kref)
{
struct drm_crtc_commit *commit =
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 51f39edc31ed..285aac3554df 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -28,6 +28,7 @@
#include <linux/export.h>
#include <linux/dma-fence.h>
#include <linux/ktime.h>
+#include <linux/media-bus-format.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -737,6 +738,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
if (old_connector_state->max_requested_bpc !=
new_connector_state->max_requested_bpc)
new_crtc_state->connectors_changed = true;
+
+ if (old_connector_state->color_format !=
+ new_connector_state->color_format)
+ new_crtc_state->connectors_changed = true;
+
}
if (funcs->atomic_check)
@@ -4102,3 +4108,83 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
return input_fmts;
}
EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
+
+/**
+ * drm_atomic_helper_bridge_get_hdmi_output_bus_fmts - helper implementing
+ * atomic_get_output_bus_fmts for HDMI
+ * @bridge: pointer to &struct drm_bridge
+ * @bridge_state: pointer to the current bridge state
+ * @crtc_state: pointer to the current CRTC state
+ * @conn_state: pointer to the current connector state
+ * @num_output_fmts: pointer to where the number of entries in the returned array
+ * will be stored. Set to 0 if unsuccessful.
+ *
+ * Common implementation for the &drm_bridge_funcs.atomic_get_output_bus_fmts
+ * operation that's applicable to HDMI connectors.
+ *
+ * Returns: a newly allocated array of u32 values of length \*@num_output_fmts,
+ * representing all the MEDIA_BUS_FMTS\_ for the current connector state's
+ * chosen HDMI output bits per compoennt, or %NULL if it fails to allocate one.
+ */
+u32 *
+drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ unsigned int num_fmts = 0;
+ u32 *out_fmts;
+
+ /*
+ * bridge->supported_formats is a bit field of BIT(enum drm_output_color_format)
+ * values. The smallest hweight that is smaller than or equal to
+ * %DRM_OUTPUT_COLOR_FORMAT_COUNT will do for counting set bits here.
+ */
+ BUILD_BUG_ON(const_true(DRM_OUTPUT_COLOR_FORMAT_COUNT > 8));
+ out_fmts = kmalloc_array(hweight8(bridge->supported_formats),
+ sizeof(u32), GFP_KERNEL);
+ if (!out_fmts) {
+ *num_output_fmts = 0;
+ return NULL;
+ }
+
+ switch (conn_state->hdmi.output_bpc) {
+ case 12:
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV12_1X36;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
+ break;
+ case 10:
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV10_1X30;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ break;
+ default:
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB888_1X24;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV8_1X24;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
+ out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ break;
+ }
+
+ *num_output_fmts = num_fmts;
+
+ return out_fmts;
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_get_hdmi_output_bus_fmts);
+
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index cc70508d4fdb..d90d1d7c9cf9 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -63,7 +63,7 @@
*/
/**
- * __drm_atomic_helper_crtc_state_reset - reset the CRTC state
+ * __drm_atomic_helper_crtc_state_init - Initialize the CRTC state
* @crtc_state: atomic CRTC state, must not be NULL
* @crtc: CRTC object, must not be NULL
*
@@ -71,13 +71,13 @@
* values. This is useful for drivers that subclass the CRTC state.
*/
void
-__drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state,
- struct drm_crtc *crtc)
+__drm_atomic_helper_crtc_state_init(struct drm_crtc_state *crtc_state,
+ struct drm_crtc *crtc)
{
crtc_state->crtc = crtc;
crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0);
}
-EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset);
+EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_init);
/**
* __drm_atomic_helper_crtc_reset - reset state on CRTC
@@ -96,7 +96,7 @@ __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc,
struct drm_crtc_state *crtc_state)
{
if (crtc_state)
- __drm_atomic_helper_crtc_state_reset(crtc_state, crtc);
+ __drm_atomic_helper_crtc_state_init(crtc_state, crtc);
if (drm_dev_has_vblank(crtc->dev))
drm_crtc_vblank_reset(crtc);
@@ -125,6 +125,31 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
/**
+ * drm_atomic_helper_crtc_create_state - default &drm_crtc_funcs.atomic_create_state hook for crtcs
+ * @crtc: crtc object
+ *
+ * Allocates and initializes pristine @drm_crtc_state.
+ *
+ * This is useful for drivers that don't subclass @drm_crtc_state.
+ *
+ * RETURNS:
+ * Pointer to new crtc state, or ERR_PTR on failure.
+ */
+struct drm_crtc_state *drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc)
+{
+ struct drm_crtc_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(state, crtc);
+
+ return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_crtc_create_state);
+
+/**
* __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
* @crtc: CRTC object
* @state: atomic CRTC state
@@ -237,15 +262,15 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
/**
- * __drm_atomic_helper_plane_state_reset - resets plane state to default values
+ * __drm_atomic_helper_plane_state_init - Initialize the plane state
* @plane_state: atomic plane state, must not be NULL
* @plane: plane object, must not be NULL
*
* Initializes the newly allocated @plane_state with default
- * values. This is useful for drivers that subclass the CRTC state.
+ * values. This is useful for drivers that subclass the plane state.
*/
-void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state,
- struct drm_plane *plane)
+void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state,
+ struct drm_plane *plane)
{
u64 val;
@@ -297,7 +322,7 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state,
plane_state->hotspot_y = val;
}
}
-EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset);
+EXPORT_SYMBOL(__drm_atomic_helper_plane_state_init);
/**
* __drm_atomic_helper_plane_reset - reset state on plane
@@ -305,7 +330,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset);
* @plane_state: plane state to assign
*
* Initializes the newly allocated @plane_state and assigns it to
- * the &drm_crtc->state pointer of @plane, usually required when
+ * the &drm_plane->state pointer of @plane, usually required when
* initializing the drivers or when called from the &drm_plane_funcs.reset
* hook.
*
@@ -315,7 +340,7 @@ void __drm_atomic_helper_plane_reset(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
if (plane_state)
- __drm_atomic_helper_plane_state_reset(plane_state, plane);
+ __drm_atomic_helper_plane_state_init(plane_state, plane);
plane->state = plane_state;
}
@@ -341,6 +366,31 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
/**
+ * drm_atomic_helper_plane_create_state - default &drm_plane_funcs.atomic_create_state hook for planes
+ * @plane: plane object
+ *
+ * Allocates and initializes pristine @drm_plane_state.
+ *
+ * This is useful for drivers that don't subclass @drm_plane_state.
+ *
+ * RETURNS:
+ * Pointer to new plane state, or ERR_PTR on failure.
+ */
+struct drm_plane_state *drm_atomic_helper_plane_create_state(struct drm_plane *plane)
+{
+ struct drm_plane_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_plane_state_init(state, plane);
+
+ return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_plane_create_state);
+
+/**
* __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
* @plane: plane object
* @state: atomic plane state
@@ -426,20 +476,20 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
/**
- * __drm_atomic_helper_connector_state_reset - reset the connector state
+ * __drm_atomic_helper_connector_state_init - Initialize the connector state
* @conn_state: atomic connector state, must not be NULL
- * @connector: connectotr object, must not be NULL
+ * @connector: connector object, must not be NULL
*
* Initializes the newly allocated @conn_state with default
* values. This is useful for drivers that subclass the connector state.
*/
void
-__drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state,
- struct drm_connector *connector)
+__drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state,
+ struct drm_connector *connector)
{
conn_state->connector = connector;
}
-EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset);
+EXPORT_SYMBOL(__drm_atomic_helper_connector_state_init);
/**
* __drm_atomic_helper_connector_reset - reset state on connector
@@ -458,7 +508,7 @@ __drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
if (conn_state)
- __drm_atomic_helper_connector_state_reset(conn_state, connector);
+ __drm_atomic_helper_connector_state_init(conn_state, connector);
connector->state = conn_state;
}
@@ -485,6 +535,32 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/**
+ * drm_atomic_helper_connector_create_state - default &drm_connector_funcs.atomic_create_state hook for connectors
+ * @connector: connector object
+ *
+ * Allocates and initializes pristine @drm_connector_state.
+ *
+ * This is useful for drivers that don't subclass @drm_connector_state.
+ *
+ * RETURNS:
+ * Pointer to new connector state, or ERR_PTR on failure.
+ */
+struct drm_connector_state *
+drm_atomic_helper_connector_create_state(struct drm_connector *connector)
+{
+ struct drm_connector_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_connector_state_init(state, connector);
+
+ return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_create_state);
+
+/**
* drm_atomic_helper_connector_tv_margins_reset - Resets TV connector properties
* @connector: DRM connector
*
@@ -731,8 +807,6 @@ void __drm_atomic_helper_private_obj_create_state(struct drm_private_obj *obj,
{
if (state)
state->obj = obj;
-
- obj->state = state;
}
EXPORT_SYMBOL(__drm_atomic_helper_private_obj_create_state);
@@ -799,7 +873,7 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state);
* @state: bridge state to destroy
*
* Destroys a bridge state previously created by
- * &drm_atomic_helper_bridge_reset() or
+ * &drm_atomic_helper_bridge_create_state() or
* &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be
* used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges
* that don't subclass the bridge state.
@@ -812,35 +886,39 @@ void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
/**
- * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
+ * __drm_atomic_helper_bridge_state_init() - Initialize a bridge state to its
* default
- * @bridge: the bridge this state refers to
* @state: bridge state to initialize
+ * @bridge: the bridge this state refers to
+ *
+ * @state is assumed to be zeroed.
*
* Initializes the bridge state to default values. This is meant to be called
- * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass
- * the bridge state.
+ * by the bridge &drm_bridge_funcs.atomic_create_state hook for bridges that
+ * subclass the bridge state.
*/
-void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
- struct drm_bridge_state *state)
+void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state,
+ struct drm_bridge *bridge)
{
- memset(state, 0, sizeof(*state));
__drm_atomic_helper_private_obj_create_state(&bridge->base, &state->base);
state->bridge = bridge;
}
-EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_state_init);
/**
- * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state
- * to its default
- * @bridge: the bridge this state refers to
+ * drm_atomic_helper_bridge_create_state - default
+ * &drm_bridge_funcs.atomic_create_state hook for bridges
+ * @bridge: bridge object
+ *
+ * Allocates and initializes pristine @drm_bridge_state.
+ *
+ * This is useful for drivers that don't subclass @drm_bridge_state.
*
- * Allocates the bridge state and initializes it to default values. This helper
- * is meant to be used as a bridge &drm_bridge_funcs.atomic_reset hook for
- * bridges that don't subclass the bridge state.
+ * RETURNS:
+ * Pointer to new bridge state, or ERR_PTR on failure.
*/
struct drm_bridge_state *
-drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
+drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge)
{
struct drm_bridge_state *bridge_state;
@@ -848,7 +926,7 @@ drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
if (!bridge_state)
return ERR_PTR(-ENOMEM);
- __drm_atomic_helper_bridge_reset(bridge, bridge_state);
+ __drm_atomic_helper_bridge_state_init(bridge_state, bridge);
return bridge_state;
}
-EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
+EXPORT_SYMBOL(drm_atomic_helper_bridge_create_state);
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index e997917819e8..1050dddadb17 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -960,6 +960,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
state->privacy_screen_sw_state = val;
} else if (property == connector->broadcast_rgb_property) {
state->hdmi.broadcast_rgb = val;
+ } else if (property == connector->color_format_property) {
+ state->color_format = val;
} else if (connector->funcs->atomic_set_property) {
return connector->funcs->atomic_set_property(connector,
state, property, val);
@@ -1045,6 +1047,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
*val = state->privacy_screen_sw_state;
} else if (property == connector->broadcast_rgb_property) {
*val = state->hdmi.broadcast_rgb;
+ } else if (property == connector->color_format_property) {
+ *val = state->color_format;
} else if (connector->funcs->atomic_get_property) {
return connector->funcs->atomic_get_property(connector,
state, property, val);
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 687b36eea0c7..6ead9b75ae3d 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -198,6 +198,46 @@
* driver.
*/
+/**
+ * DOC: bridge chain format selection
+ *
+ * A bridge chain, from display output processor to connector, may contain
+ * bridges capable of converting between bus formats on their inputs, and
+ * output formats on their outputs. For example, a bridge may be able to convert
+ * from RGB to YCbCr 4:4:4, and pass through YCbCr 4:2:0 as-is, but not convert
+ * from RGB to YCbCr 4:2:0. This means not all input formats map to all output
+ * formats.
+ *
+ * Further adding to this, a desired output color format, as specified with the
+ * "color format" DRM property, might not correspond 1:1 to what the display
+ * driver should set at its output. The bridge chain it feeds into may only be
+ * able to reach the desired output format, if a conversion from a different
+ * starting format is performed.
+ *
+ * To deal with this complexity, the recursive bridge chain bus format selection
+ * logic starts with the last bridge in the chain, usually the connector, and
+ * then recursively walks the chain of bridges backwards to the first bridge,
+ * trying to find a path.
+ *
+ * For a display driver to work in such a scenario, it should read the first
+ * bridge's bridge state to figure out which bus format the chain resolved to.
+ * If the first bridge's input format resolved to %MEDIA_BUS_FMT_FIXED, then its
+ * output format should be used.
+ *
+ * Special handling is done for HDMI as it relates to format selection. Instead
+ * of directly using the "color format" DRM property for bridge chains that end
+ * in HDMI bridges, the bridge chain format selection logic will trust the logic
+ * that set the HDMI output format. For the common HDMI state helper
+ * functionality, this means that %DRM_CONNECTOR_COLOR_FORMAT_AUTO will allow
+ * fallbacks to YCBCr 4:2:0 if the bandwidth requirements would otherwise be too
+ * high but the mode and connector allow it.
+ *
+ * For bridge chains that do not end in an HDMI bridge,
+ * %DRM_CONNECTOR_COLOR_FORMAT_AUTO will be satisfied with the first output
+ * format on the last bridge for which it can find a path back to the first
+ * bridge.
+ */
+
/* Protect bridge_list and bridge_lingering_list */
static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list);
@@ -417,6 +457,7 @@ void drm_bridge_add(struct drm_bridge *bridge)
if (!list_empty(&bridge->list))
list_del_init(&bridge->list);
+ mutex_init(&bridge->hpd_state_mutex);
mutex_init(&bridge->hpd_mutex);
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
@@ -469,6 +510,7 @@ void drm_bridge_remove(struct drm_bridge *bridge)
mutex_unlock(&bridge_lock);
mutex_destroy(&bridge->hpd_mutex);
+ mutex_destroy(&bridge->hpd_state_mutex);
drm_bridge_put(bridge);
}
@@ -500,7 +542,7 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj)
struct drm_bridge *bridge = drm_priv_to_bridge(obj);
struct drm_bridge_state *state;
- state = bridge->funcs->atomic_reset(bridge);
+ state = bridge->funcs->atomic_create_state(bridge);
if (IS_ERR(state))
return ERR_CAST(state);
@@ -515,7 +557,7 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
static bool drm_bridge_is_atomic(struct drm_bridge *bridge)
{
- return bridge->funcs->atomic_reset != NULL;
+ return bridge->funcs->atomic_create_state != NULL;
}
/**
@@ -1149,6 +1191,47 @@ static int select_bus_fmt_recursive(struct drm_bridge *first_bridge,
return ret;
}
+static bool __pure bus_format_is_color_fmt(u32 bus_fmt, enum drm_connector_color_format fmt)
+{
+ if (fmt == DRM_CONNECTOR_COLOR_FORMAT_AUTO)
+ return true;
+
+ switch (bus_fmt) {
+ case MEDIA_BUS_FMT_FIXED:
+ return true;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ return fmt == DRM_CONNECTOR_COLOR_FORMAT_RGB444;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ case MEDIA_BUS_FMT_VYUY10_1X20:
+ case MEDIA_BUS_FMT_YVYU10_1X20:
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ case MEDIA_BUS_FMT_VYUY12_1X24:
+ case MEDIA_BUS_FMT_YUYV12_1X24:
+ case MEDIA_BUS_FMT_YVYU12_1X24:
+ return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420;
+ default:
+ return false;
+ }
+}
+
/*
* This function is called by &drm_atomic_bridge_chain_check() just before
* calling &drm_bridge_funcs.atomic_check() on all elements of the chain.
@@ -1192,6 +1275,7 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
struct drm_encoder *encoder = bridge->encoder;
struct drm_bridge_state *last_bridge_state;
unsigned int i, num_out_bus_fmts = 0;
+ enum drm_connector_color_format fmt;
u32 *out_bus_fmts;
int ret = 0;
@@ -1233,11 +1317,31 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
}
+ /*
+ * Instead of directly accessing conn_state.color_format, call into a
+ * connector function that allows connector implementations (e.g. for
+ * bridge connectors including HDMI bridges, where the HDMI helpers will
+ * have already chosen an appropriate output format) to override the
+ * selected format.
+ */
+ fmt = drm_connector_get_color_format(conn_state);
+
for (i = 0; i < num_out_bus_fmts; i++) {
+ if (!bus_format_is_color_fmt(out_bus_fmts[i], fmt)) {
+ drm_dbg_kms(last_bridge->dev,
+ "Skipping bus format 0x%04x as it doesn't match format %d\n",
+ out_bus_fmts[i], fmt);
+ ret = -ENOTSUPP;
+ continue;
+ }
ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state,
conn_state, out_bus_fmts[i]);
- if (ret != -ENOTSUPP)
+ if (ret != -ENOTSUPP) {
+ drm_dbg_kms(last_bridge->dev,
+ "Found bridge chain ending with bus format 0x%04x\n",
+ out_bus_fmts[i]);
break;
+ }
}
kfree(out_bus_fmts);
@@ -1451,19 +1555,25 @@ void drm_bridge_hpd_enable(struct drm_bridge *bridge,
if (!(bridge->ops & DRM_BRIDGE_OP_HPD))
return;
+ mutex_lock(&bridge->hpd_state_mutex);
+
mutex_lock(&bridge->hpd_mutex);
- if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n"))
+ if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) {
+ mutex_unlock(&bridge->hpd_mutex);
goto unlock;
+ }
bridge->hpd_cb = cb;
bridge->hpd_data = data;
+ mutex_unlock(&bridge->hpd_mutex);
+
if (bridge->funcs->hpd_enable)
bridge->funcs->hpd_enable(bridge);
unlock:
- mutex_unlock(&bridge->hpd_mutex);
+ mutex_unlock(&bridge->hpd_state_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable);
@@ -1484,13 +1594,15 @@ void drm_bridge_hpd_disable(struct drm_bridge *bridge)
if (!(bridge->ops & DRM_BRIDGE_OP_HPD))
return;
- mutex_lock(&bridge->hpd_mutex);
+ mutex_lock(&bridge->hpd_state_mutex);
if (bridge->funcs->hpd_disable)
bridge->funcs->hpd_disable(bridge);
+ mutex_lock(&bridge->hpd_mutex);
bridge->hpd_cb = NULL;
bridge->hpd_data = NULL;
mutex_unlock(&bridge->hpd_mutex);
+ mutex_unlock(&bridge->hpd_state_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable);
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index faa025498de4..1536e59c6fe7 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -47,29 +47,23 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p)
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
for (order = mm->max_order; order >= 0; order--) {
- struct gpu_buddy_block *block, *tmp;
- struct rb_root *root;
- u64 count = 0, free;
- unsigned int tree;
-
- for_each_free_tree(tree) {
- root = &mm->free_trees[tree][order];
-
- rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
- BUG_ON(!gpu_buddy_block_is_free(block));
- count++;
- }
- }
+ u64 free_count = mm->free_scoreboard[order];
+ u64 used_count = mm->used_scoreboard[order];
+ u64 block_size = mm->chunk_size << order;
+ u64 free = free_count * block_size;
+ u64 used = used_count * block_size;
drm_printf(p, "order-%2d ", order);
- free = count * (mm->chunk_size << order);
- if (free < SZ_1M)
- drm_printf(p, "free: %8llu KiB", free >> 10);
+ if (block_size < SZ_1M)
+ drm_printf(p, "free: %8llu KiB, used: %8llu KiB",
+ free >> 10, used >> 10);
else
- drm_printf(p, "free: %8llu MiB", free >> 20);
+ drm_printf(p, "free: %8llu MiB, used: %8llu MiB",
+ free >> 20, used >> 20);
- drm_printf(p, ", blocks: %llu\n", count);
+ drm_printf(p, ", free_blocks: %llu, used_blocks: %llu\n",
+ free_count, used_count);
}
}
EXPORT_SYMBOL(drm_buddy_print);
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 65fec53f70fa..682fcc651525 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -498,15 +498,15 @@ void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop,
}
/**
- * __drm_colorop_state_reset - resets colorop state to default values
+ * __drm_colorop_state_init - Initializes colorop state to default values
* @colorop_state: atomic colorop state, must not be NULL
* @colorop: colorop object, must not be NULL
*
* Initializes the newly allocated @colorop_state with default
- * values. This is useful for drivers that subclass the CRTC state.
+ * values. This is useful for drivers that subclass the colorop state.
*/
-static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state,
- struct drm_colorop *colorop)
+static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state,
+ struct drm_colorop *colorop)
{
u64 val;
@@ -536,14 +536,37 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state,
}
/**
+ * drm_atomic_helper_colorop_create_state - Allocates and initializes colorop atomic state
+ * @colorop: drm colorop
+ *
+ * Initializes a pristine @drm_colorop_state.
+ *
+ * RETURNS:
+ * Pointer to new colorop state, or ERR_PTR on failure.
+ */
+struct drm_colorop_state *
+drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop)
+{
+ struct drm_colorop_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_colorop_state_init(state, colorop);
+
+ return state;
+}
+
+/**
* __drm_colorop_reset - reset state on colorop
* @colorop: drm colorop
* @colorop_state: colorop state to assign
*
- * Initializes the newly allocated @colorop_state and assigns it to
- * the &drm_crtc->state pointer of @colorop, usually required when
- * initializing the drivers or when called from the &drm_colorop_funcs.reset
- * hook.
+ * Initializes the newly allocated @colorop_state and assigns it to the
+ * &drm_colorop->state pointer of @colorop, usually required when
+ * initializing the drivers or when called from the
+ * &drm_colorop_funcs.reset hook.
*
* This is useful for drivers that subclass the colorop state.
*/
@@ -551,7 +574,7 @@ static void __drm_colorop_reset(struct drm_colorop *colorop,
struct drm_colorop_state *colorop_state)
{
if (colorop_state)
- __drm_colorop_state_reset(colorop_state, colorop);
+ __drm_colorop_state_init(colorop_state, colorop);
colorop->state = colorop_state;
}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 11646453aaac..8b4baed060f3 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -618,8 +618,17 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
* drm_connector_attach_max_bpc_property() requires the
* connector to have a state.
*/
- if (connector->funcs->reset)
+ if (connector->funcs->atomic_create_state) {
+ struct drm_connector_state *state;
+
+ state = connector->funcs->atomic_create_state(connector);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+
+ connector->state = state;
+ } else if (connector->funcs->reset) {
connector->funcs->reset(connector);
+ }
drm_connector_attach_max_bpc_property(connector, 8, max_bpc);
connector->max_bpc = max_bpc;
@@ -627,6 +636,10 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (max_bpc > 8)
drm_connector_attach_hdr_output_metadata_property(connector);
+ ret = drm_connector_attach_color_format_property(connector, supported_formats);
+ if (ret)
+ return ret;
+
connector->hdmi.funcs = hdmi_funcs;
return 0;
@@ -1176,6 +1189,7 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
static const struct drm_prop_enum_list drm_panel_type_enum_list[] = {
{ DRM_MODE_PANEL_TYPE_UNKNOWN, "unknown" },
{ DRM_MODE_PANEL_TYPE_OLED, "OLED" },
+ { DRM_MODE_PANEL_TYPE_LCD, "LCD" },
};
/**
@@ -1388,6 +1402,18 @@ static const u32 hdmi_colorspaces =
BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65) |
BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER);
+static const u32 hdmi_colorformats =
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
+
+static const u32 dp_colorformats =
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
+
/*
* As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry
* Format Table 2-120
@@ -1508,7 +1534,7 @@ EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name);
* never read back the value of "DPMS" because it can be incorrect.
* panel_type:
* Immutable enum property to indicate the type of connected panel.
- * Possible values are "unknown" (default) and "OLED".
+ * Possible values are "unknown" (default), "OLED", and "LCD".
* PATH:
* Connector path property to identify how this sink is physically
* connected. Used by DP MST. This should be set by calling
@@ -2557,7 +2583,8 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
* conversion matrix and convert to the appropriate quantization
* range.
* The variants BT2020_RGB and BT2020_YCC are equivalent and the
- * driver chooses between RGB and YCbCr on its own.
+ * driver chooses between RGB and YCbCr based on the color format
+ * property.
*
* SMPTE_170M_YCC:
* BT709_YCC:
@@ -2936,6 +2963,149 @@ int drm_connector_attach_colorspace_property(struct drm_connector *connector)
EXPORT_SYMBOL(drm_connector_attach_colorspace_property);
/**
+ * DOC: Color format
+ *
+ * The connector "color format" property allows userspace to request a specific
+ * color model on the output of the connector. Not all values listed by the
+ * property are guaranteed to work for every sink; rather, it is an optimistic
+ * listing of color formats that the source could output depending on
+ * circumstances.
+ *
+ * Whether it actually can output a certain color format is determined during
+ * the atomic check phase. Consequently, a userspace application that sets the
+ * color format to a value other than "AUTO" should check whether its atomic
+ * commit succeeded.
+ *
+ * Possible values for "color format":
+ *
+ * "AUTO":
+ * The driver or display protocol helpers should pick a suitable color
+ * format. All implementations of a specific display protocol will behave
+ * the same way with "AUTO", but different display protocols do not
+ * necessarily have the same "AUTO" semantics.
+ *
+ * For HDMI connectors, "AUTO" picks RGB, but falls back to YUV 4:2:0 if
+ * the bandwidth required for full-scale RGB is not available, or the mode
+ * is YUV 4:2:0-only, as long as the mode, source, and sink all support
+ * YUV 4:2:0.
+ * "RGB":
+ * RGB output format. The quantization range (limited/full) depends on the
+ * value of the "Broadcast RGB" property if it is present on the connector.
+ * "YUV 4:4:4":
+ * YUV 4:4:4 (a.k.a. YCbCr 4:4:4) output format. Chroma is not subsampled.
+ * The quantization range defaults to limited.
+ * "YUV 4:2:2":
+ * YUV 4:2:2 (a.k.a. YCbCr 4:2:2) output format. Chroma has half the
+ * horizontal resolution of Luma. The quantization range defaults to
+ * limited.
+ * "YUV 4:2:0":
+ * YUV 4:2:0 (a.k.a. YCbCr 4:2:0) output format. Chroma has half the
+ * horizontal and vertical resolution of Luma. The quantization range
+ * defaults to limited.
+ *
+ * A sink may only support some color formats in specific modes and at specific
+ * bit depths. The atomic modesetting API should be used to set a working
+ * configuration in one go, as an unsupported combination of parameters is
+ * rejected.
+ */
+
+/**
+ * drm_connector_attach_color_format_property - create and attach color format property
+ * @connector: connector to create the color format property on
+ * @supported_color_formats: bitmask of bit-shifted &enum drm_output_color_format
+ * values the connector supports
+ *
+ * Called by a driver to create a color format property. The property is
+ * attached to the connector automatically on success.
+ *
+ * @supported_color_formats should only include color formats the connector
+ * type can actually support.
+ *
+ * Returns:
+ * 0 on success, negative errno on error
+ */
+int drm_connector_attach_color_format_property(struct drm_connector *connector,
+ unsigned long supported_color_formats)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_prop_enum_list enum_list[DRM_CONNECTOR_COLOR_FORMAT_COUNT];
+ unsigned int i = 0;
+ unsigned long fmt;
+
+ if (connector->color_format_property)
+ return 0;
+
+ if (!supported_color_formats) {
+ drm_err(dev, "No supported color formats provided on [CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+ return -EINVAL;
+ }
+
+ if (supported_color_formats & ~GENMASK(DRM_OUTPUT_COLOR_FORMAT_COUNT - 1, 0)) {
+ drm_err(dev, "Unknown color formats provided on [CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+ return -EINVAL;
+ }
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_HDMIA:
+ case DRM_MODE_CONNECTOR_HDMIB:
+ if (supported_color_formats & ~hdmi_colorformats) {
+ drm_err(dev, "Color formats not allowed for HDMI on [CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+ return -EINVAL;
+ }
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_eDP:
+ if (supported_color_formats & ~dp_colorformats) {
+ drm_err(dev, "Color formats not allowed for DP on [CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ enum_list[0].name = "AUTO";
+ enum_list[0].type = DRM_CONNECTOR_COLOR_FORMAT_AUTO;
+
+ for_each_set_bit(fmt, &supported_color_formats, DRM_OUTPUT_COLOR_FORMAT_COUNT) {
+ switch (fmt) {
+ case DRM_OUTPUT_COLOR_FORMAT_RGB444:
+ enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_RGB444;
+ break;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR444:
+ enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444;
+ break;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR422:
+ enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422;
+ break;
+ case DRM_OUTPUT_COLOR_FORMAT_YCBCR420:
+ enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420;
+ break;
+ default:
+ drm_warn(dev, "Unknown supported format %ld on [CONNECTOR:%d:%s]\n",
+ fmt, connector->base.id, connector->name);
+ continue;
+ }
+ enum_list[i].name = drm_hdmi_connector_get_output_format_name(fmt);
+ }
+
+ connector->color_format_property =
+ drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "color format",
+ enum_list, i + 1);
+
+ if (!connector->color_format_property)
+ return -ENOMEM;
+
+ drm_object_attach_property(&connector->base, connector->color_format_property,
+ DRM_CONNECTOR_COLOR_FORMAT_AUTO);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_color_format_property);
+
+/**
* drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed
* @old_state: old connector state to compare
* @new_state: new connector state to compare
@@ -3530,6 +3700,22 @@ void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
}
EXPORT_SYMBOL(drm_connector_oob_hotplug_event);
+/**
+ * drm_connector_get_color_format - Return connector color format of @conn_state
+ * @conn_state: pointer to the &struct drm_connector_state to go check
+ *
+ */
+enum drm_connector_color_format
+drm_connector_get_color_format(const struct drm_connector_state *conn_state)
+{
+ struct drm_connector *connector = conn_state->connector;
+
+ if (connector->funcs->color_format)
+ return connector->funcs->color_format(conn_state);
+
+ return conn_state->color_format;
+}
+EXPORT_SYMBOL(drm_connector_get_color_format);
/**
* DOC: Tile group
diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h
index 4590d6a3d821..ef7e6c9bd4db 100644
--- a/drivers/gpu/drm/drm_displayid_internal.h
+++ b/drivers/gpu/drm/drm_displayid_internal.h
@@ -142,6 +142,30 @@ struct displayid_formula_timing_block {
struct displayid_formula_timings_9 timings[];
} __packed;
+#define DISPLAYID_DEVICE_TECH_UNSPECIFIED 0
+#define DISPLAYID_DEVICE_TECH_LCD 1
+#define DISPLAYID_DEVICE_TECH_OLED 2
+
+#define DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH GENMASK(6, 4)
+
+struct displayid_display_params_block {
+ struct displayid_block base;
+ __le16 horiz_image_size;
+ __le16 vert_image_size;
+ __le16 horiz_pixel_count;
+ __le16 vert_pixel_count;
+ u8 features;
+ u8 primary_color1[3];
+ u8 primary_color2[3];
+ u8 primary_color3[3];
+ u8 white_point[3];
+ __le16 max_luminance_full;
+ __le16 max_luminance_10;
+ __le16 min_luminance;
+ u8 color_depth_and_tech; /* [2:0] depth, [6:4] device tech, [7] theme */
+ u8 gamma_eotf;
+} __packed;
+
#define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0)
#define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5)
diff --git a/drivers/gpu/drm/drm_draw_internal.h b/drivers/gpu/drm/drm_draw_internal.h
index 261967145635..44ddcee4744c 100644
--- a/drivers/gpu/drm/drm_draw_internal.h
+++ b/drivers/gpu/drm/drm_draw_internal.h
@@ -7,7 +7,6 @@
#ifndef __DRM_DRAW_INTERNAL_H__
#define __DRM_DRAW_INTERNAL_H__
-#include <linux/font.h>
#include <linux/types.h>
struct iosys_map;
@@ -18,12 +17,6 @@ static inline bool drm_draw_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, in
return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0;
}
-static inline const u8 *drm_draw_get_char_bitmap(const struct font_desc *font,
- char c, size_t font_pitch)
-{
- return font->data + (c * font->height) * font_pitch;
-}
-
bool drm_draw_can_convert_from_xrgb8888(u32 format);
u32 drm_draw_color_from_xrgb8888(u32 color, u32 format);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 675675480da4..1ff0bf7cba6a 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -342,7 +342,9 @@ void drm_minor_release(struct drm_minor *minor)
*
* platform_set_drvdata(pdev, drm);
*
- * drm_mode_config_reset(drm);
+ * ret = drm_mode_config_create_initial_state(drm);
+ * if (ret)
+ * return ret;
*
* ret = drm_dev_register(drm);
* if (ret)
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index 2156dbe601c9..8e9ff17538e7 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -32,7 +32,6 @@
#include <drm/drm_print.h>
#include "drm_crtc_internal.h"
-#include "drm_internal.h"
/**
* DOC: overview
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index df3c25bac761..07970e5b5f65 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6713,32 +6713,82 @@ static void drm_reset_display_info(struct drm_connector *connector)
info->source_physical_address = CEC_PHYS_ADDR_INVALID;
memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb));
+
+ info->panel_type = DRM_MODE_PANEL_TYPE_UNKNOWN;
+}
+
+static void drm_displayid_process_base_section_header(struct drm_connector *connector,
+ const struct displayid_iter *iter)
+{
+ struct drm_display_info *info = &connector->display_info;
+
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] DisplayID extension version 0x%02x, primary use 0x%02x\n",
+ connector->base.id, connector->name,
+ displayid_version(iter),
+ displayid_primary_use(iter));
+ if (displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
+ (displayid_primary_use(iter) == PRIMARY_USE_HEAD_MOUNTED_VR ||
+ displayid_primary_use(iter) == PRIMARY_USE_HEAD_MOUNTED_AR))
+ info->non_desktop = true;
+}
+
+static void
+drm_displayid_parse_display_params(struct drm_connector *connector,
+ const struct displayid_block *block)
+{
+ struct drm_display_info *info = &connector->display_info;
+ const struct displayid_display_params_block *params =
+ (const struct displayid_display_params_block *)block;
+ u8 tech;
+
+ if (block->num_bytes < sizeof(*params) - sizeof(params->base)) {
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] DisplayID Display Parameters block too short (%u < %zu)\n",
+ connector->base.id, connector->name,
+ block->num_bytes,
+ sizeof(*params) - sizeof(params->base));
+ return;
+ }
+
+ tech = FIELD_GET(DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH,
+ params->color_depth_and_tech);
+
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] DisplayID Display Parameters: device technology %s\n",
+ connector->base.id, connector->name,
+ tech == DISPLAYID_DEVICE_TECH_LCD ? "LCD" :
+ tech == DISPLAYID_DEVICE_TECH_OLED ? "OLED" : "unspecified");
+
+ switch (tech) {
+ case DISPLAYID_DEVICE_TECH_LCD:
+ info->panel_type = DRM_MODE_PANEL_TYPE_LCD;
+ break;
+ case DISPLAYID_DEVICE_TECH_OLED:
+ info->panel_type = DRM_MODE_PANEL_TYPE_OLED;
+ break;
+ default:
+ break;
+ }
}
static void update_displayid_info(struct drm_connector *connector,
const struct drm_edid *drm_edid)
{
- struct drm_display_info *info = &connector->display_info;
const struct displayid_block *block;
struct displayid_iter iter;
+ bool base_section_header_processed = false;
displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
- drm_dbg_kms(connector->dev,
- "[CONNECTOR:%d:%s] DisplayID extension version 0x%02x, primary use 0x%02x\n",
- connector->base.id, connector->name,
- displayid_version(&iter),
- displayid_primary_use(&iter));
- if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
- (displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_VR ||
- displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_AR))
- info->non_desktop = true;
+ if (!base_section_header_processed) {
+ drm_displayid_process_base_section_header(connector, &iter);
+ base_section_header_processed = true;
+ }
- /*
- * We're only interested in the base section here, no need to
- * iterate further.
- */
- break;
+ if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
+ block->tag == DATA_BLOCK_2_DISPLAY_PARAMETERS)
+ drm_displayid_parse_display_params(connector, block);
}
displayid_iter_end(&iter);
}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 7b11a582f8ec..3316bb00a662 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1744,21 +1744,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
*/
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
{
- int err = 0;
-
if (!drm_fbdev_emulation || !fb_helper)
return 0;
mutex_lock(&fb_helper->lock);
- if (fb_helper->deferred_setup) {
- err = __drm_fb_helper_initial_config_and_unlock(fb_helper);
- return err;
- }
+ if (fb_helper->deferred_setup)
+ return __drm_fb_helper_initial_config_and_unlock(fb_helper);
if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) {
fb_helper->delayed_hotplug = true;
mutex_unlock(&fb_helper->lock);
- return err;
+ return 0;
}
drm_master_internal_release(fb_helper->dev);
@@ -1802,4 +1798,3 @@ bool drm_fb_helper_gem_is_fb(const struct drm_fb_helper *fb_helper,
return gem == obj;
}
EXPORT_SYMBOL_GPL(drm_fb_helper_gem_is_fb);
-
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index e3ed684ddcf2..018df97d590d 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -232,8 +232,7 @@ void drm_gem_private_object_init(struct drm_device *dev,
if (!obj->resv)
obj->resv = &obj->_resv;
- if (drm_core_check_feature(dev, DRIVER_GEM_GPUVA))
- drm_gem_gpuva_init(obj);
+ drm_gem_gpuva_init(obj);
drm_vma_node_reset(&obj->vma_node);
INIT_LIST_HEAD(&obj->lru_node);
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index c989459eb215..22ec52e2ffb8 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -473,10 +473,23 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem,
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap_locked);
-static int
-drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
- struct drm_device *dev, size_t size,
- uint32_t *handle)
+/**
+ * drm_gem_shmem_create_with_handle - Allocate an object with the given size and
+ * returns a GEM handle
+ * @file_priv: DRM file structure to create the dumb buffer for
+ * @dev: DRM device
+ * @size: Size of the object to allocate
+ * @handle: Returns the GEM handle on success
+ *
+ * Allocates an shmem GEM buffer using drm_gem_shmem_create() and returns
+ * a GEM handle to it.
+ *
+ * Returns:
+ * Zero on success, or an error code otherwise.
+ */
+int drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
+ struct drm_device *dev, size_t size,
+ uint32_t *handle)
{
struct drm_gem_shmem_object *shmem;
int ret;
@@ -495,6 +508,7 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
return ret;
}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_handle);
/* Update madvise status, returns true if not purged, else
* false or -errno.
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 66f7dc37b597..f432f485a914 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -23,6 +23,7 @@
#include <linux/export.h>
#include <linux/uaccess.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_file.h>
@@ -182,6 +183,87 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
return ret;
}
+static int drm_mode_config_plane_create_state(struct drm_plane *plane)
+{
+ struct drm_plane_state *plane_state;
+
+ if (!plane->funcs->atomic_create_state)
+ return 0;
+
+ plane_state = plane->funcs->atomic_create_state(plane);
+ if (IS_ERR(plane_state))
+ return PTR_ERR(plane_state);
+
+ plane->state = plane_state;
+
+ return 0;
+}
+
+static int drm_mode_config_plane_reset_with_create_state(struct drm_plane *plane)
+{
+ if (plane->state) {
+ plane->funcs->atomic_destroy_state(plane, plane->state);
+ plane->state = NULL;
+ }
+
+ return drm_mode_config_plane_create_state(plane);
+}
+
+static int drm_mode_config_crtc_create_state(struct drm_crtc *crtc)
+{
+ struct drm_crtc_state *crtc_state;
+
+ if (!crtc->funcs->atomic_create_state)
+ return 0;
+
+ crtc_state = crtc->funcs->atomic_create_state(crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ if (drm_dev_has_vblank(crtc->dev))
+ drm_crtc_vblank_reset(crtc);
+
+ crtc->state = crtc_state;
+
+ return 0;
+}
+
+static int drm_mode_config_crtc_reset_with_create_state(struct drm_crtc *crtc)
+{
+ if (crtc->state) {
+ crtc->funcs->atomic_destroy_state(crtc, crtc->state);
+ crtc->state = NULL;
+ }
+
+ return drm_mode_config_crtc_create_state(crtc);
+}
+
+static int drm_mode_config_connector_create_state(struct drm_connector *connector)
+{
+ struct drm_connector_state *conn_state;
+
+ if (!connector->funcs->atomic_create_state)
+ return 0;
+
+ conn_state = connector->funcs->atomic_create_state(connector);
+ if (IS_ERR(conn_state))
+ return PTR_ERR(conn_state);
+
+ connector->state = conn_state;
+
+ return 0;
+}
+
+static int drm_mode_config_connector_reset_with_create_state(struct drm_connector *connector)
+{
+ if (connector->state) {
+ connector->funcs->atomic_destroy_state(connector, connector->state);
+ connector->state = NULL;
+ }
+
+ return drm_mode_config_connector_create_state(connector);
+}
+
/**
* drm_mode_config_reset - call ->reset callbacks
* @dev: drm device
@@ -189,6 +271,10 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
* This functions calls all the crtc's, encoder's and connector's ->reset
* callback. Drivers can use this in e.g. their driver load or resume code to
* reset hardware and software state.
+ *
+ * Note that &drm_private_obj structures are expected to be stable across
+ * suspend/resume cycles, and drm_mode_config_reset() does not affect these
+ * structures.
*/
void drm_mode_config_reset(struct drm_device *dev)
{
@@ -202,26 +288,123 @@ void drm_mode_config_reset(struct drm_device *dev)
drm_for_each_colorop(colorop, dev)
drm_colorop_reset(colorop);
- drm_for_each_plane(plane, dev)
+ drm_for_each_plane(plane, dev) {
if (plane->funcs->reset)
plane->funcs->reset(plane);
+ else if (plane->funcs->atomic_create_state)
+ drm_mode_config_plane_reset_with_create_state(plane);
+ }
- drm_for_each_crtc(crtc, dev)
+ drm_for_each_crtc(crtc, dev) {
if (crtc->funcs->reset)
crtc->funcs->reset(crtc);
+ else if (crtc->funcs->atomic_create_state)
+ drm_mode_config_crtc_reset_with_create_state(crtc);
+ }
drm_for_each_encoder(encoder, dev)
if (encoder->funcs && encoder->funcs->reset)
encoder->funcs->reset(encoder);
drm_connector_list_iter_begin(dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter)
+ drm_for_each_connector_iter(connector, &conn_iter) {
if (connector->funcs->reset)
connector->funcs->reset(connector);
+ else if (connector->funcs->atomic_create_state)
+ drm_mode_config_connector_reset_with_create_state(connector);
+ }
drm_connector_list_iter_end(&conn_iter);
}
EXPORT_SYMBOL(drm_mode_config_reset);
+/**
+ * drm_mode_config_create_initial_state - Allocates the initial state
+ * @dev: drm device
+ *
+ * This functions creates the initial state for all the objects. Drivers
+ * can use this in e.g. probe to initialize their software state.
+ *
+ * It has two main differences with drm_mode_config_reset(): the reset()
+ * hooks aren't called and thus the hardware will be left untouched, but
+ * also the &drm_private_obj structures will be initialized as opposed
+ * to drm_mode_config_reset() that skips them.
+ *
+ * Returns: 0 on success, negative error value on failure.
+ */
+int drm_mode_config_create_initial_state(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_colorop *colorop;
+ struct drm_plane *plane;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct drm_private_obj *privobj;
+ int ret;
+
+ drm_for_each_privobj(privobj, dev) {
+ struct drm_private_state *privobj_state;
+
+ if (privobj->state)
+ continue;
+
+ if (!privobj->funcs->atomic_create_state)
+ continue;
+
+ privobj_state = privobj->funcs->atomic_create_state(privobj);
+ if (IS_ERR(privobj_state))
+ return PTR_ERR(privobj_state);
+
+ privobj->state = privobj_state;
+ }
+
+ drm_for_each_colorop(colorop, dev) {
+ struct drm_colorop_state *colorop_state;
+
+ if (colorop->state)
+ continue;
+
+ colorop_state = drm_atomic_helper_colorop_create_state(colorop);
+ if (IS_ERR(colorop_state))
+ return PTR_ERR(colorop_state);
+
+ colorop->state = colorop_state;
+ }
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->state)
+ continue;
+
+ ret = drm_mode_config_plane_create_state(plane);
+ if (ret)
+ return ret;
+ }
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->state)
+ continue;
+
+ ret = drm_mode_config_crtc_create_state(crtc);
+ if (ret)
+ return ret;
+ }
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->state)
+ continue;
+
+ ret = drm_mode_config_connector_create_state(connector);
+ if (ret) {
+ drm_connector_list_iter_end(&conn_iter);
+ return ret;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_config_create_initial_state);
+
/*
* Global properties
*/
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index d6d3b8d85dea..e576c4791861 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -443,9 +443,11 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2;
for (j = 0; j < line_len; j++) {
- src = drm_draw_get_char_bitmap(font, msg[i].txt[j], font_pitch);
+ src = font_data_glyph_buf(font->data, font->width, font->height,
+ (unsigned char)msg[i].txt[j]);
rec.x2 = rec.x1 + font->width;
- drm_panic_blit(sb, &rec, src, font_pitch, 1, color);
+ if (src)
+ drm_panic_blit(sb, &rec, src, font_pitch, 1, color);
rec.x1 += font->width;
}
}
diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c
index ded9461df5f2..86cef1a37678 100644
--- a/drivers/gpu/drm/drm_print.c
+++ b/drivers/gpu/drm/drm_print.c
@@ -50,8 +50,10 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat
"\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n"
"\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n"
"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n"
+"\t\tBit 6 (0x40) will enable STATE messages (atomic state code)\n"
"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n"
-"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
+"\t\tBit 8 (0x100) will enable DP messages (displayport code)\n"
+"\t\tBit 9 (0x200) will enable DRMRES messages (managed resources code)");
#if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG)
module_param_named(debug, __drm_debug, ulong, 0600);
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index 4da5d6094721..b4a002e6d043 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -58,6 +58,12 @@
* framebuffer to be written by the writeback connector. This property is
* similar to the FB_ID property on planes, but will always read as zero
* and is not preserved across commits.
+ * If the width and height of the framebuffer do not match those of the
+ * attached CRTC, the driver must either fail or scale (not crop) the
+ * content to exactly fit the framebuffer.
+ * If the driver is unable to exactly fill the framebuffer for any reason,
+ * such as hardware scaler constraints or an odd width for a sub-sampled
+ * format, the writeback must fail instead of partially filling the buffer.
* Userspace must set this property to an output buffer every time it
* wishes the buffer to get filled.
*
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 947f7c4ff4e9..d17cb5b4a4bf 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -55,36 +55,36 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
*/
static const struct pci_device_id pciidlist[] = {
/* Poulsbo */
- { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
- { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
+ { PCI_DEVICE(0x8086, 0x8108), .driver_data = (long)&psb_chip_ops },
+ { PCI_DEVICE(0x8086, 0x8109), .driver_data = (long)&psb_chip_ops },
/* Oak Trail */
- { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
- { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4100), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4101), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4102), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4103), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4104), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4105), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4106), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4107), .driver_data = (long)&oaktrail_chip_ops },
+ { PCI_DEVICE(0x8086, 0x4108), .driver_data = (long)&oaktrail_chip_ops },
/* Cedar Trail */
- { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
- { 0, }
+ { PCI_DEVICE(0x8086, 0x0be0), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be1), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be2), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be3), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be4), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be5), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be6), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be7), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be8), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0be9), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0bea), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0beb), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0bec), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0bed), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0bee), .driver_data = (long)&cdv_chip_ops },
+ { PCI_DEVICE(0x8086, 0x0bef), .driver_data = (long)&cdv_chip_ops },
+ { }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
index d1f3f5793f34..adf4516bf8f6 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig
+++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig
@@ -5,10 +5,8 @@ config DRM_HISI_HIBMC
select DRM_CLIENT_SELECTION
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DP_HELPER
+ select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
- select DRM_VRAM_HELPER
- select DRM_TTM
- select DRM_TTM_HELPER
select I2C
select I2C_ALGOBIT
help
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index 3066dc9ebc64..b4ab53db1c08 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -15,8 +15,10 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
-#include <drm/drm_gem_vram_helper.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_vblank.h>
#include "hibmc_drm_drv.h"
@@ -72,74 +74,69 @@ static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode)
static int hibmc_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
- plane);
- struct drm_framebuffer *fb = new_plane_state->fb;
- struct drm_crtc *crtc = new_plane_state->crtc;
- struct drm_crtc_state *crtc_state;
- u32 src_w = new_plane_state->src_w >> 16;
- u32 src_h = new_plane_state->src_h >> 16;
-
- if (!crtc || !fb)
- return 0;
-
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
-
- if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
- drm_dbg_atomic(plane->dev, "scale not support\n");
- return -EINVAL;
- }
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc_state *new_crtc_state = NULL;
+ int ret;
- if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) {
- drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
- return -EINVAL;
- }
+ if (new_plane_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
- if (!crtc_state->enable)
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, true);
+ if (ret)
+ return ret;
+ else if (!new_plane_state->visible)
return 0;
- if (new_plane_state->crtc_x + new_plane_state->crtc_w >
- crtc_state->adjusted_mode.hdisplay ||
- new_plane_state->crtc_y + new_plane_state->crtc_h >
- crtc_state->adjusted_mode.vdisplay) {
- drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
- return -EINVAL;
- }
-
if (new_plane_state->fb->pitches[0] % 128 != 0) {
drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
return -EINVAL;
}
+
return 0;
}
static void hibmc_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
- struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
- plane);
+ struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev);
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state);
+ struct drm_framebuffer *fb = new_state->fb;
+ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+ u32 gpu_addr = 0;
u32 reg;
- s64 gpu_addr = 0;
u32 line_l;
- struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev);
- struct drm_gem_vram_object *gbo;
- if (!new_state->fb)
+ if (!fb)
return;
- gbo = drm_gem_vram_of_gem(new_state->fb->obj[0]);
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
+ struct drm_rect damage;
+ struct drm_atomic_helper_damage_iter iter;
- gpu_addr = drm_gem_vram_offset(gbo);
- if (WARN_ON_ONCE(gpu_addr < 0))
- return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */
+ drm_atomic_helper_damage_iter_init(&iter, old_state, new_state);
+ drm_atomic_for_each_plane_damage(&iter, &damage) {
+ struct iosys_map dst[DRM_FORMAT_MAX_PLANES] = {
+ IOSYS_MAP_INIT_VADDR_IOMEM(priv->vram + gpu_addr),
+ };
+
+ iosys_map_incr(&dst[0],
+ drm_fb_clip_offset(fb->pitches[0], fb->format, &damage));
+ drm_fb_memcpy(dst, fb->pitches, shadow_plane_state->data, fb, &damage);
+ }
+
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+ }
writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS);
- reg = new_state->fb->width * (new_state->fb->format->cpp[0]);
+ reg = drm_format_info_min_pitch(fb->format, 0, fb->width);
- line_l = new_state->fb->pitches[0];
+ line_l = fb->pitches[0];
writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) |
HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l),
priv->mmio + HIBMC_CRT_FB_WIDTH);
@@ -147,29 +144,31 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane,
/* SET PIXEL FORMAT */
reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL);
reg &= ~HIBMC_CRT_DISP_CTL_FORMAT_MASK;
- reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT,
- new_state->fb->format->cpp[0] * 8 / 16);
+ switch (fb->format->format) {
+ case DRM_FORMAT_XRGB8888:
+ reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, 2);
+ break;
+ case DRM_FORMAT_RGB565:
+ reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, 1);
+ break;
+ }
writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL);
}
static const u32 channel_formats1[] = {
- DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
- DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
- DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
- DRM_FORMAT_ABGR8888
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB565,
};
static const struct drm_plane_funcs hibmc_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
- .reset = drm_atomic_helper_plane_reset,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
};
static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = {
- DRM_GEM_VRAM_PLANE_HELPER_FUNCS,
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.atomic_check = hibmc_plane_atomic_check,
.atomic_update = hibmc_plane_atomic_update,
};
@@ -529,6 +528,7 @@ int hibmc_de_init(struct hibmc_drm_private *priv)
}
drm_plane_helper_add(plane, &hibmc_plane_helper_funcs);
+ drm_plane_enable_fb_damage_clips(plane);
ret = drm_crtc_init_with_planes(dev, crtc, plane,
NULL, &hibmc_crtc_funcs, NULL);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 99b36de1fe13..4d85c89f3f88 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -14,13 +14,15 @@
#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/sizes.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
-#include <drm/drm_fbdev_ttm.h>
+#include <drm/drm_dumb_buffers.h>
+#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_gem_vram_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_vblank.h>
@@ -71,7 +73,13 @@ static irqreturn_t hibmc_dp_interrupt(int irq, void *arg)
static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- return drm_gem_vram_fill_create_dumb(file, dev, 0, 128, args);
+ int ret;
+
+ ret = drm_mode_size_dumb(dev, args, SZ_128, 0);
+ if (ret)
+ return ret;
+
+ return drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
}
static const struct drm_driver hibmc_driver = {
@@ -81,10 +89,9 @@ static const struct drm_driver hibmc_driver = {
.desc = "hibmc drm driver",
.major = 1,
.minor = 0,
- .debugfs_init = drm_vram_mm_debugfs_init,
- .dumb_create = hibmc_dumb_create,
- .dumb_map_offset = drm_gem_ttm_dumb_map_offset,
- DRM_FBDEV_TTM_DRIVER_OPS,
+ .gem_prime_import = drm_gem_shmem_prime_import_no_map,
+ .dumb_create = hibmc_dumb_create,
+ DRM_FBDEV_SHMEM_DRIVER_OPS,
};
static int __maybe_unused hibmc_pm_suspend(struct device *dev)
@@ -106,11 +113,47 @@ static const struct dev_pm_ops hibmc_pm_ops = {
hibmc_pm_resume)
};
+static enum drm_mode_status hibmc_mode_config_mode_valid(struct drm_device *dev,
+ const struct drm_display_mode *mode)
+{
+ const struct drm_format_info *info =
+ drm_get_format_info(dev, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR);
+ struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
+ unsigned long max_fb_size = priv->vram_size;
+ u64 pitch;
+
+ if (drm_WARN_ON_ONCE(dev, !info))
+ return MODE_ERROR; /* driver bug */
+
+ pitch = drm_format_info_min_pitch(info, 0, mode->hdisplay);
+ if (!pitch)
+ return MODE_BAD_WIDTH;
+ else if (pitch > max_fb_size / mode->vdisplay)
+ return MODE_MEM;
+
+ return MODE_OK;
+}
+
+static struct drm_framebuffer *hibmc_mode_config_fb_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ const struct drm_format_info *info,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ int i;
+
+ for (i = 0; i < info->num_planes; ++i) {
+ if (mode_cmd->pitches[i] % SZ_128)
+ return ERR_PTR(-EINVAL);
+ }
+
+ return drm_gem_fb_create_with_dirty(dev, file_priv, info, mode_cmd);
+}
+
static const struct drm_mode_config_funcs hibmc_mode_funcs = {
- .mode_valid = drm_vram_helper_mode_valid,
+ .mode_valid = hibmc_mode_config_mode_valid,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
- .fb_create = drm_gem_fb_create,
+ .fb_create = hibmc_mode_config_fb_create,
};
static int hibmc_kms_init(struct hibmc_drm_private *priv)
@@ -130,7 +173,6 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv)
dev->mode_config.max_height = 1200;
dev->mode_config.preferred_depth = 24;
- dev->mode_config.prefer_shadow = 1;
dev->mode_config.funcs = (void *)&hibmc_mode_funcs;
@@ -335,18 +377,22 @@ static int hibmc_load(struct drm_device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
+ resource_size_t vram_base, vram_size;
int ret;
ret = hibmc_hw_init(priv);
if (ret)
return ret;
- ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
- if (ret) {
- drm_err(dev, "Error initializing VRAM MM; %d\n", ret);
- return ret;
- }
+ vram_base = pci_resource_start(pdev, 0);
+ vram_size = pci_resource_len(pdev, 0);
+
+ priv->vram = devm_ioremap_wc(dev->dev, vram_base, vram_size);
+ if (!priv->vram)
+ return -ENOMEM;
+
+ priv->vram_base = vram_base;
+ priv->vram_size = vram_size;
ret = hibmc_kms_init(priv);
if (ret)
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index cd3a3fca1fe6..dce8572bf63e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -38,6 +38,11 @@ struct hibmc_drm_private {
/* hw */
void __iomem *mmio;
+ /* vram */
+ void __iomem *vram;
+ resource_size_t vram_base;
+ resource_size_t vram_size;
+
/* drm */
struct drm_device dev;
struct drm_plane primary_plane;
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h
index 9e776112c03e..78136ec2c2f4 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm.h
+++ b/drivers/gpu/drm/hyperv/hyperv_drm.h
@@ -8,7 +8,7 @@
#define VMBUS_MAX_PACKET_SIZE 0x4000
-struct hyperv_drm_device {
+struct hv_drm_device {
/* drm */
struct drm_device dev;
struct drm_plane plane;
@@ -39,17 +39,17 @@ struct hyperv_drm_device {
struct hv_device *hdev;
};
-#define to_hv(_dev) container_of(_dev, struct hyperv_drm_device, dev)
+#define to_hv_drm(_dev) container_of(_dev, struct hv_drm_device, dev)
/* hyperv_drm_modeset */
-int hyperv_mode_config_init(struct hyperv_drm_device *hv);
+int hv_drm_mode_config_init(struct hv_drm_device *hv);
/* hyperv_drm_proto */
-int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp);
-int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
+int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp);
+int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
u32 w, u32 h, u32 pitch);
-int hyperv_hide_hw_ptr(struct hv_device *hdev);
-int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect);
-int hyperv_connect_vsp(struct hv_device *hdev);
+int hv_drm_hide_hw_ptr(struct hv_device *hdev);
+int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect);
+int hv_drm_connect_vsp(struct hv_device *hdev);
#endif
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index b6bf6412ae34..20f35c48c0b8 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -24,9 +24,9 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-DEFINE_DRM_GEM_FOPS(hv_fops);
+DEFINE_DRM_GEM_FOPS(hv_drm_fops);
-static struct drm_driver hyperv_driver = {
+static struct drm_driver hv_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
.name = DRIVER_NAME,
@@ -34,22 +34,22 @@ static struct drm_driver hyperv_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
- .fops = &hv_fops,
+ .fops = &hv_drm_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
DRM_FBDEV_SHMEM_DRIVER_OPS,
};
-static int hyperv_pci_probe(struct pci_dev *pdev,
+static int hv_drm_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
return 0;
}
-static void hyperv_pci_remove(struct pci_dev *pdev)
+static void hv_drm_pci_remove(struct pci_dev *pdev)
{
}
-static const struct pci_device_id hyperv_pci_tbl[] = {
+static const struct pci_device_id hv_drm_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_MICROSOFT,
.device = PCI_DEVICE_ID_HYPERV_VIDEO,
@@ -60,14 +60,14 @@ static const struct pci_device_id hyperv_pci_tbl[] = {
/*
* PCI stub to support gen1 VM.
*/
-static struct pci_driver hyperv_pci_driver = {
+static struct pci_driver hv_drm_pci_driver = {
.name = KBUILD_MODNAME,
- .id_table = hyperv_pci_tbl,
- .probe = hyperv_pci_probe,
- .remove = hyperv_pci_remove,
+ .id_table = hv_drm_pci_tbl,
+ .probe = hv_drm_pci_probe,
+ .remove = hv_drm_pci_remove,
};
-static int hyperv_setup_vram(struct hyperv_drm_device *hv,
+static int hv_drm_setup_vram(struct hv_drm_device *hv,
struct hv_device *hdev)
{
struct drm_device *dev = &hv->dev;
@@ -102,15 +102,15 @@ error:
return ret;
}
-static int hyperv_vmbus_probe(struct hv_device *hdev,
+static int hv_drm_vmbus_probe(struct hv_device *hdev,
const struct hv_vmbus_device_id *dev_id)
{
- struct hyperv_drm_device *hv;
+ struct hv_drm_device *hv;
struct drm_device *dev;
int ret;
- hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver,
- struct hyperv_drm_device, dev);
+ hv = devm_drm_dev_alloc(&hdev->device, &hv_drm_driver,
+ struct hv_drm_device, dev);
if (IS_ERR(hv))
return PTR_ERR(hv);
@@ -119,15 +119,15 @@ static int hyperv_vmbus_probe(struct hv_device *hdev,
hv_set_drvdata(hdev, hv);
hv->hdev = hdev;
- ret = hyperv_connect_vsp(hdev);
+ ret = hv_drm_connect_vsp(hdev);
if (ret) {
drm_err(dev, "Failed to connect to vmbus.\n");
goto err_hv_set_drv_data;
}
- aperture_remove_all_conflicting_devices(hyperv_driver.name);
+ aperture_remove_all_conflicting_devices(hv_drm_driver.name);
- ret = hyperv_setup_vram(hv, hdev);
+ ret = hv_drm_setup_vram(hv, hdev);
if (ret)
goto err_vmbus_close;
@@ -136,11 +136,11 @@ static int hyperv_vmbus_probe(struct hv_device *hdev,
* vram location is not fatal. Device will update dirty area till
* preferred resolution only.
*/
- ret = hyperv_update_vram_location(hdev, hv->fb_base);
+ ret = hv_drm_update_vram_location(hdev, hv->fb_base);
if (ret)
drm_warn(dev, "Failed to update vram location.\n");
- ret = hyperv_mode_config_init(hv);
+ ret = hv_drm_mode_config_init(hv);
if (ret)
goto err_free_mmio;
@@ -168,10 +168,10 @@ err_hv_set_drv_data:
return ret;
}
-static void hyperv_vmbus_remove(struct hv_device *hdev)
+static void hv_drm_vmbus_remove(struct hv_device *hdev)
{
struct drm_device *dev = hv_get_drvdata(hdev);
- struct hyperv_drm_device *hv = to_hv(dev);
+ struct hv_drm_device *hv = to_hv_drm(dev);
vmbus_set_skip_unload(false);
drm_dev_unplug(dev);
@@ -183,12 +183,12 @@ static void hyperv_vmbus_remove(struct hv_device *hdev)
vmbus_free_mmio(hv->mem->start, hv->fb_size);
}
-static void hyperv_vmbus_shutdown(struct hv_device *hdev)
+static void hv_drm_vmbus_shutdown(struct hv_device *hdev)
{
drm_atomic_helper_shutdown(hv_get_drvdata(hdev));
}
-static int hyperv_vmbus_suspend(struct hv_device *hdev)
+static int hv_drm_vmbus_suspend(struct hv_device *hdev)
{
struct drm_device *dev = hv_get_drvdata(hdev);
int ret;
@@ -202,67 +202,67 @@ static int hyperv_vmbus_suspend(struct hv_device *hdev)
return 0;
}
-static int hyperv_vmbus_resume(struct hv_device *hdev)
+static int hv_drm_vmbus_resume(struct hv_device *hdev)
{
struct drm_device *dev = hv_get_drvdata(hdev);
- struct hyperv_drm_device *hv = to_hv(dev);
+ struct hv_drm_device *hv = to_hv_drm(dev);
int ret;
- ret = hyperv_connect_vsp(hdev);
+ ret = hv_drm_connect_vsp(hdev);
if (ret)
return ret;
- ret = hyperv_update_vram_location(hdev, hv->fb_base);
+ ret = hv_drm_update_vram_location(hdev, hv->fb_base);
if (ret)
return ret;
return drm_mode_config_helper_resume(dev);
}
-static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = {
+static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = {
/* Synthetic Video Device GUID */
{HV_SYNTHVID_GUID},
{}
};
-static struct hv_driver hyperv_hv_driver = {
+static struct hv_driver hv_drm_hv_driver = {
.name = KBUILD_MODNAME,
- .id_table = hyperv_vmbus_tbl,
- .probe = hyperv_vmbus_probe,
- .remove = hyperv_vmbus_remove,
- .shutdown = hyperv_vmbus_shutdown,
- .suspend = hyperv_vmbus_suspend,
- .resume = hyperv_vmbus_resume,
+ .id_table = hv_drm_vmbus_tbl,
+ .probe = hv_drm_vmbus_probe,
+ .remove = hv_drm_vmbus_remove,
+ .shutdown = hv_drm_vmbus_shutdown,
+ .suspend = hv_drm_vmbus_suspend,
+ .resume = hv_drm_vmbus_resume,
.driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
-static int __init hyperv_init(void)
+static int __init hv_drm_init(void)
{
int ret;
if (drm_firmware_drivers_only())
return -ENODEV;
- ret = pci_register_driver(&hyperv_pci_driver);
+ ret = pci_register_driver(&hv_drm_pci_driver);
if (ret != 0)
return ret;
- return vmbus_driver_register(&hyperv_hv_driver);
+ return vmbus_driver_register(&hv_drm_hv_driver);
}
-static void __exit hyperv_exit(void)
+static void __exit hv_drm_exit(void)
{
- vmbus_driver_unregister(&hyperv_hv_driver);
- pci_unregister_driver(&hyperv_pci_driver);
+ vmbus_driver_unregister(&hv_drm_hv_driver);
+ pci_unregister_driver(&hv_drm_pci_driver);
}
-module_init(hyperv_init);
-module_exit(hyperv_exit);
+module_init(hv_drm_init);
+module_exit(hv_drm_exit);
-MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl);
-MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl);
+MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl);
+MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>");
MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 793dbbf61893..1855749c1e41 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -25,11 +25,11 @@
#include "hyperv_drm.h"
-static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb,
+static int hv_drm_blit_to_vram_rect(struct drm_framebuffer *fb,
const struct iosys_map *vmap,
struct drm_rect *rect)
{
- struct hyperv_drm_device *hv = to_hv(fb->dev);
+ struct hv_drm_device *hv = to_hv_drm(fb->dev);
struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram);
int idx;
@@ -44,9 +44,9 @@ static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb,
return 0;
}
-static int hyperv_connector_get_modes(struct drm_connector *connector)
+static int hv_drm_connector_get_modes(struct drm_connector *connector)
{
- struct hyperv_drm_device *hv = to_hv(connector->dev);
+ struct hv_drm_device *hv = to_hv_drm(connector->dev);
int count;
count = drm_add_modes_noedid(connector,
@@ -58,11 +58,11 @@ static int hyperv_connector_get_modes(struct drm_connector *connector)
return count;
}
-static const struct drm_connector_helper_funcs hyperv_connector_helper_funcs = {
- .get_modes = hyperv_connector_get_modes,
+static const struct drm_connector_helper_funcs hv_drm_connector_helper_funcs = {
+ .get_modes = hv_drm_connector_get_modes,
};
-static const struct drm_connector_funcs hyperv_connector_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,
@@ -70,15 +70,15 @@ static const struct drm_connector_funcs hyperv_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
-static inline int hyperv_conn_init(struct hyperv_drm_device *hv)
+static inline int hv_drm_conn_init(struct hv_drm_device *hv)
{
- drm_connector_helper_add(&hv->connector, &hyperv_connector_helper_funcs);
+ drm_connector_helper_add(&hv->connector, &hv_drm_connector_helper_funcs);
return drm_connector_init(&hv->dev, &hv->connector,
- &hyperv_connector_funcs,
+ &hv_drm_connector_funcs,
DRM_MODE_CONNECTOR_VIRTUAL);
}
-static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h,
+static int hv_drm_check_size(struct hv_drm_device *hv, int w, int h,
struct drm_framebuffer *fb)
{
u32 pitch = w * (hv->screen_depth / 8);
@@ -92,25 +92,25 @@ static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h,
return 0;
}
-static const uint32_t hyperv_formats[] = {
+static const uint32_t hv_drm_formats[] = {
DRM_FORMAT_XRGB8888,
};
-static const uint64_t hyperv_modifiers[] = {
+static const uint64_t hv_drm_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
-static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+static void hv_drm_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_commit *state)
{
- struct hyperv_drm_device *hv = to_hv(crtc->dev);
+ struct hv_drm_device *hv = to_hv_drm(crtc->dev);
struct drm_plane *plane = &hv->plane;
struct drm_plane_state *plane_state = plane->state;
struct drm_crtc_state *crtc_state = crtc->state;
- hyperv_hide_hw_ptr(hv->hdev);
- hyperv_update_situation(hv->hdev, 1, hv->screen_depth,
+ hv_drm_hide_hw_ptr(hv->hdev);
+ hv_drm_update_situation(hv->hdev, 1, hv->screen_depth,
crtc_state->mode.hdisplay,
crtc_state->mode.vdisplay,
plane_state->fb->pitches[0]);
@@ -118,14 +118,14 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc,
drm_crtc_vblank_on(crtc);
}
-static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = {
+static const struct drm_crtc_helper_funcs hv_drm_crtc_helper_funcs = {
.atomic_check = drm_crtc_helper_atomic_check,
.atomic_flush = drm_crtc_vblank_atomic_flush,
- .atomic_enable = hyperv_crtc_helper_atomic_enable,
+ .atomic_enable = hv_drm_crtc_helper_atomic_enable,
.atomic_disable = drm_crtc_vblank_atomic_disable,
};
-static const struct drm_crtc_funcs hyperv_crtc_funcs = {
+static const struct drm_crtc_funcs hv_drm_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
@@ -135,11 +135,11 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = {
DRM_CRTC_VBLANK_TIMER_FUNCS,
};
-static int hyperv_plane_atomic_check(struct drm_plane *plane,
+static int hv_drm_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct hyperv_drm_device *hv = to_hv(plane->dev);
+ struct hv_drm_device *hv = to_hv_drm(plane->dev);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_crtc *crtc = plane_state->crtc;
struct drm_crtc_state *crtc_state = NULL;
@@ -167,10 +167,10 @@ static int hyperv_plane_atomic_check(struct drm_plane *plane,
return 0;
}
-static void hyperv_plane_atomic_update(struct drm_plane *plane,
+static void hv_drm_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
- struct hyperv_drm_device *hv = to_hv(plane->dev);
+ struct hv_drm_device *hv = to_hv_drm(plane->dev);
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state);
@@ -185,15 +185,15 @@ static void hyperv_plane_atomic_update(struct drm_plane *plane,
if (!drm_rect_intersect(&dst_clip, &damage))
continue;
- hyperv_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage);
- hyperv_update_dirt(hv->hdev, &damage);
+ hv_drm_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage);
+ hv_drm_update_dirt(hv->hdev, &damage);
}
}
-static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane,
+static int hv_drm_plane_get_scanout_buffer(struct drm_plane *plane,
struct drm_scanout_buffer *sb)
{
- struct hyperv_drm_device *hv = to_hv(plane->dev);
+ struct hv_drm_device *hv = to_hv_drm(plane->dev);
struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram);
if (plane->state && plane->state->fb) {
@@ -207,9 +207,9 @@ static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane,
return -ENODEV;
}
-static void hyperv_plane_panic_flush(struct drm_plane *plane)
+static void hv_drm_plane_panic_flush(struct drm_plane *plane)
{
- struct hyperv_drm_device *hv = to_hv(plane->dev);
+ struct hv_drm_device *hv = to_hv_drm(plane->dev);
struct drm_rect rect;
if (plane->state && plane->state->fb) {
@@ -218,32 +218,32 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane)
rect.x2 = plane->state->fb->width;
rect.y2 = plane->state->fb->height;
- hyperv_update_dirt(hv->hdev, &rect);
+ hv_drm_update_dirt(hv->hdev, &rect);
}
vmbus_initiate_unload(true);
}
-static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = {
+static const struct drm_plane_helper_funcs hv_drm_plane_helper_funcs = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
- .atomic_check = hyperv_plane_atomic_check,
- .atomic_update = hyperv_plane_atomic_update,
- .get_scanout_buffer = hyperv_plane_get_scanout_buffer,
- .panic_flush = hyperv_plane_panic_flush,
+ .atomic_check = hv_drm_plane_atomic_check,
+ .atomic_update = hv_drm_plane_atomic_update,
+ .get_scanout_buffer = hv_drm_plane_get_scanout_buffer,
+ .panic_flush = hv_drm_plane_panic_flush,
};
-static const struct drm_plane_funcs hyperv_plane_funcs = {
+static const struct drm_plane_funcs hv_drm_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
DRM_GEM_SHADOW_PLANE_FUNCS,
};
-static const struct drm_encoder_funcs hyperv_drm_simple_encoder_funcs_cleanup = {
+static const struct drm_encoder_funcs hv_drm_simple_encoder_funcs_cleanup = {
.destroy = drm_encoder_cleanup,
};
-static inline int hyperv_pipe_init(struct hyperv_drm_device *hv)
+static inline int hv_drm_pipe_init(struct hv_drm_device *hv)
{
struct drm_device *dev = &hv->dev;
struct drm_encoder *encoder = &hv->encoder;
@@ -253,29 +253,29 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv)
int ret;
ret = drm_universal_plane_init(dev, plane, 0,
- &hyperv_plane_funcs,
- hyperv_formats, ARRAY_SIZE(hyperv_formats),
- hyperv_modifiers,
+ &hv_drm_plane_funcs,
+ hv_drm_formats, ARRAY_SIZE(hv_drm_formats),
+ hv_drm_modifiers,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
- drm_plane_helper_add(plane, &hyperv_plane_helper_funcs);
+ drm_plane_helper_add(plane, &hv_drm_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(plane);
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
- &hyperv_crtc_funcs, NULL);
+ &hv_drm_crtc_funcs, NULL);
if (ret)
return ret;
- drm_crtc_helper_add(crtc, &hyperv_crtc_helper_funcs);
+ drm_crtc_helper_add(crtc, &hv_drm_crtc_helper_funcs);
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = drm_encoder_init(dev, encoder,
- &hyperv_drm_simple_encoder_funcs_cleanup,
+ &hv_drm_simple_encoder_funcs_cleanup,
DRM_MODE_ENCODER_NONE, NULL);
if (ret)
return ret;
- ret = hyperv_conn_init(hv);
+ ret = hv_drm_conn_init(hv);
if (ret) {
drm_err(dev, "Failed to initialized connector.\n");
return ret;
@@ -285,25 +285,25 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv)
}
static enum drm_mode_status
-hyperv_mode_valid(struct drm_device *dev,
+hv_drm_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode)
{
- struct hyperv_drm_device *hv = to_hv(dev);
+ struct hv_drm_device *hv = to_hv_drm(dev);
- if (hyperv_check_size(hv, mode->hdisplay, mode->vdisplay, NULL))
+ if (hv_drm_check_size(hv, mode->hdisplay, mode->vdisplay, NULL))
return MODE_BAD;
return MODE_OK;
}
-static const struct drm_mode_config_funcs hyperv_mode_config_funcs = {
+static const struct drm_mode_config_funcs hv_drm_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
- .mode_valid = hyperv_mode_valid,
+ .mode_valid = hv_drm_mode_valid,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
-int hyperv_mode_config_init(struct hyperv_drm_device *hv)
+int hv_drm_mode_config_init(struct hv_drm_device *hv)
{
struct drm_device *dev = &hv->dev;
int ret;
@@ -322,9 +322,9 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv)
dev->mode_config.preferred_depth = hv->screen_depth;
dev->mode_config.prefer_shadow = 0;
- dev->mode_config.funcs = &hyperv_mode_config_funcs;
+ dev->mode_config.funcs = &hv_drm_mode_config_funcs;
- ret = hyperv_pipe_init(hv);
+ ret = hv_drm_pipe_init(hv);
if (ret) {
drm_err(dev, "Failed to initialized pipe.\n");
return ret;
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
index 6e09b0218df4..f0ef627b4898 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
@@ -181,7 +181,7 @@ struct synthvid_msg {
};
} __packed;
-static inline bool hyperv_version_ge(u32 ver1, u32 ver2)
+static inline bool hv_drm_version_ge(u32 ver1, u32 ver2)
{
if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) ||
(SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) &&
@@ -191,10 +191,10 @@ static inline bool hyperv_version_ge(u32 ver1, u32 ver2)
return false;
}
-static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg)
+static inline int hv_drm_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg)
{
static atomic64_t request_id = ATOMIC64_INIT(0);
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
int ret;
msg->pipe_hdr.type = PIPE_MSG_DATA;
@@ -211,9 +211,9 @@ static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg
return ret;
}
-static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver)
+static int hv_drm_negotiate_version(struct hv_device *hdev, u32 ver)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
@@ -223,7 +223,7 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver)
msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_version_req);
msg->ver_req.version = ver;
- hyperv_sendpacket(hdev, msg);
+ hv_drm_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
@@ -243,9 +243,9 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver)
return 0;
}
-int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
+int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
@@ -257,7 +257,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
msg->vram.user_ctx = vram_pp;
msg->vram.vram_gpa = vram_pp;
msg->vram.is_vram_gpa_specified = 1;
- hyperv_sendpacket(hdev, msg);
+ hv_drm_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
@@ -272,7 +272,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
return 0;
}
-int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
+int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
u32 w, u32 h, u32 pitch)
{
struct synthvid_msg msg;
@@ -292,7 +292,7 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
msg.situ.video_output[0].height_pixels = h;
msg.situ.video_output[0].pitch_bytes = pitch;
- hyperv_sendpacket(hdev, &msg);
+ hv_drm_sendpacket(hdev, &msg);
return 0;
}
@@ -306,11 +306,11 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
* the msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't
* work in tests.
*
- * The hyperv_hide_hw_ptr() is also called in the handler of the
+ * The hv_drm_hide_hw_ptr() is also called in the handler of the
* SYNTHVID_FEATURE_CHANGE event, otherwise the host still draws an extra
* unwanted mouse pointer after the VM Connection window is closed and reopened.
*/
-int hyperv_hide_hw_ptr(struct hv_device *hdev)
+int hv_drm_hide_hw_ptr(struct hv_device *hdev)
{
struct synthvid_msg msg;
@@ -322,7 +322,7 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev)
msg.ptr_pos.video_output = 0;
msg.ptr_pos.image_x = 0;
msg.ptr_pos.image_y = 0;
- hyperv_sendpacket(hdev, &msg);
+ hv_drm_sendpacket(hdev, &msg);
memset(&msg, 0, sizeof(struct synthvid_msg));
msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
@@ -338,14 +338,14 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev)
msg.ptr_shape.data[1] = 1;
msg.ptr_shape.data[2] = 1;
msg.ptr_shape.data[3] = 1;
- hyperv_sendpacket(hdev, &msg);
+ hv_drm_sendpacket(hdev, &msg);
return 0;
}
-int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
+int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg msg;
if (!hv->dirt_needed)
@@ -363,14 +363,14 @@ int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
msg.dirt.rect[0].x2 = rect->x2;
msg.dirt.rect[0].y2 = rect->y2;
- hyperv_sendpacket(hdev, &msg);
+ hv_drm_sendpacket(hdev, &msg);
return 0;
}
-static int hyperv_get_supported_resolution(struct hv_device *hdev)
+static int hv_drm_get_supported_resolution(struct hv_device *hdev)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
@@ -383,7 +383,7 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev)
sizeof(struct synthvid_supported_resolution_req);
msg->resolution_req.maximum_resolution_count =
SYNTHVID_MAX_RESOLUTION_COUNT;
- hyperv_sendpacket(hdev, msg);
+ hv_drm_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
@@ -420,9 +420,9 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev)
return 0;
}
-static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
+static void hv_drm_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg;
size_t hdr_size;
size_t need;
@@ -486,7 +486,7 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
}
hv->dirt_needed = msg->feature_chg.is_dirt_needed;
if (hv->dirt_needed)
- hyperv_hide_hw_ptr(hv->hdev);
+ hv_drm_hide_hw_ptr(hv->hdev);
return;
default:
return;
@@ -508,10 +508,10 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
complete(&hv->wait);
}
-static void hyperv_receive(void *ctx)
+static void hv_drm_receive(void *ctx)
{
struct hv_device *hdev = ctx;
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *recv_buf;
u32 bytes_recvd;
u64 req_id;
@@ -539,19 +539,19 @@ static void hyperv_receive(void *ctx)
ret, bytes_recvd);
} else if (bytes_recvd > 0 &&
recv_buf->pipe_hdr.type == PIPE_MSG_DATA) {
- hyperv_receive_sub(hdev, bytes_recvd);
+ hv_drm_receive_sub(hdev, bytes_recvd);
}
} while (bytes_recvd > 0 && ret == 0);
}
-int hyperv_connect_vsp(struct hv_device *hdev)
+int hv_drm_connect_vsp(struct hv_device *hdev)
{
- struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
+ struct hv_drm_device *hv = hv_get_drvdata(hdev);
struct drm_device *dev = &hv->dev;
int ret;
ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE,
- NULL, 0, hyperv_receive, hdev);
+ NULL, 0, hv_drm_receive, hdev);
if (ret) {
drm_err(dev, "Unable to open vmbus channel\n");
return ret;
@@ -561,16 +561,16 @@ int hyperv_connect_vsp(struct hv_device *hdev)
switch (vmbus_proto_version) {
case VERSION_WIN10:
case VERSION_WIN10_V5:
- ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
+ ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
if (!ret)
break;
fallthrough;
case VERSION_WIN8:
case VERSION_WIN8_1:
- ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
+ ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
break;
default:
- ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
+ ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
break;
}
@@ -581,8 +581,8 @@ int hyperv_connect_vsp(struct hv_device *hdev)
hv->screen_depth = SYNTHVID_DEPTH_WIN8;
- if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
- ret = hyperv_get_supported_resolution(hdev);
+ if (hv_drm_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
+ ret = hv_drm_get_supported_resolution(hdev);
if (ret)
drm_err(dev, "Failed to get supported resolution from host, use default\n");
}
diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index 7ce2b52297d1..10ed9bdfee76 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -182,7 +182,7 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state,
{
memset(crtc_state, 0, sizeof(*crtc_state));
- __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base);
+ __drm_atomic_helper_crtc_state_init(&crtc_state->uapi, &crtc->base);
crtc_state->cpu_transcoder = INVALID_TRANSCODER;
crtc_state->master_transcoder = INVALID_TRANSCODER;
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 85d3aa3b9894..c81fc00a334c 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -2203,17 +2203,56 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp,
return -EINVAL;
}
+static u16 intel_dp_dsc_max_delta_bppx16(const struct intel_connector *connector,
+ enum intel_output_format output_format)
+{
+ const u8 *dsc_dpcd = connector->dp.dsc_dpcd;
+ u8 max_bpp_delta_v1 = dsc_dpcd[DP_DSC_MAX_BPP_DELTA_VERSION_1 - DP_DSC_SUPPORT];
+ int max_bpp;
+
+ if (!(dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] &
+ DP_DSC_MAX_BPP_DELTA_AVAILABILITY))
+ return 0;
+
+ switch (output_format) {
+ case INTEL_OUTPUT_FORMAT_RGB:
+ case INTEL_OUTPUT_FORMAT_YCBCR444:
+ max_bpp = max_bpp_delta_v1 & DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK;
+ if (max_bpp >= 1 && max_bpp <= 21)
+ max_bpp = max_bpp + DP_DSC_BPP_DELTA_444 - 1;
+ else
+ max_bpp = 0;
+ break;
+ case INTEL_OUTPUT_FORMAT_YCBCR420:
+ max_bpp = (max_bpp_delta_v1 & DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK) >>
+ DP_DSC_BPP_DELTA_SHIFT_420;
+ if (max_bpp >= 1 && max_bpp <= 7)
+ max_bpp = max_bpp + DP_DSC_BPP_DELTA_420 - 1;
+ break;
+ default:
+ MISSING_CASE(output_format);
+ return 0;
+ }
+
+ return max_bpp << 4;
+}
+
static
u16 intel_dp_dsc_max_sink_compressed_bppx16(const struct intel_connector *connector,
enum intel_output_format output_format,
int bpc)
{
- u16 max_bppx16 = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd);
+ u16 max_bppx16 = intel_dp_dsc_max_delta_bppx16(connector, output_format);
+
+ if (max_bppx16)
+ return max_bppx16;
+
+ max_bppx16 = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd);
if (max_bppx16)
return max_bppx16;
/*
- * If support not given in DPCD 67h, 68h use the Maximum Allowed bit rate
+ * If support not given in DPCD 67h, 68h, 6Eh, 6Fh use the Maximum Allowed bit rate
* values as given in spec Table 2-157 DP v2.0
*/
switch (output_format) {
diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c
index e087554488b4..7017ed6cb9d1 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -67,7 +67,7 @@ static void intel_plane_state_reset(struct intel_plane_state *plane_state,
{
memset(plane_state, 0, sizeof(*plane_state));
- __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base);
+ __drm_atomic_helper_plane_state_init(&plane_state->uapi, &plane->base);
plane_state->scaler_id = -1;
plane_state->fence_id = -1;
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index aa77def0bc0d..b2442f05825e 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -548,7 +548,7 @@ static void enable_signaling(struct i915_active_fence *active)
if (!fence)
return;
- dma_fence_enable_sw_signaling(fence);
+ dma_fence_enable_signaling(fence);
dma_fence_put(fence);
}
diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
index 58087e8517d6..8897b3bdeb4a 100644
--- a/drivers/gpu/drm/imagination/pvr_drv.c
+++ b/drivers/gpu/drm/imagination/pvr_drv.c
@@ -1378,7 +1378,7 @@ pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev,
DEFINE_DRM_GEM_FOPS(pvr_drm_driver_fops);
static struct drm_driver pvr_drm_driver = {
- .driver_features = DRIVER_GEM | DRIVER_GEM_GPUVA | DRIVER_RENDER |
+ .driver_features = DRIVER_GEM | DRIVER_RENDER |
DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE,
.open = pvr_drm_driver_open,
.postclose = pvr_drm_driver_postclose,
diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
index 0f06db95f00f..eb75827394f8 100644
--- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c
+++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
@@ -180,7 +180,7 @@ static int imx_pd_bridge_attach(struct drm_bridge *bridge,
static const struct drm_bridge_funcs imx_pd_bridge_funcs = {
.attach = imx_pd_bridge_attach,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_check = imx_pd_bridge_atomic_check,
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 42c86f195c66..7e569af22391 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -1031,7 +1031,7 @@ static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = {
.atomic_enable = ingenic_drm_bridge_atomic_enable,
.atomic_disable = ingenic_drm_bridge_atomic_disable,
.atomic_check = ingenic_drm_bridge_atomic_check,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts,
diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c
index 0bf7105c8748..7c873e62c16d 100644
--- a/drivers/gpu/drm/lima/lima_device.c
+++ b/drivers/gpu/drm/lima/lima_device.c
@@ -368,12 +368,6 @@ int lima_device_init(struct lima_device *ldev)
if (err)
goto err_out0;
- ldev->empty_vm = lima_vm_create(ldev);
- if (!ldev->empty_vm) {
- err = -ENOMEM;
- goto err_out1;
- }
-
ldev->va_start = 0;
if (ldev->id == lima_gpu_mali450) {
ldev->va_end = LIMA_VA_RESERVE_START;
@@ -387,6 +381,12 @@ int lima_device_init(struct lima_device *ldev)
} else
ldev->va_end = LIMA_VA_RESERVE_END;
+ ldev->empty_vm = lima_vm_create(ldev);
+ if (!ldev->empty_vm) {
+ err = -ENOMEM;
+ goto err_out1;
+ }
+
ldev->iomem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ldev->iomem)) {
dev_err(ldev->dev, "fail to ioremap iomem\n");
diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c
index 34405073c4d4..3fd828d23271 100644
--- a/drivers/gpu/drm/loongson/lsdc_drv.c
+++ b/drivers/gpu/drm/loongson/lsdc_drv.c
@@ -444,8 +444,8 @@ static const struct dev_pm_ops lsdc_pm_ops = {
};
static const struct pci_device_id lsdc_pciid_list[] = {
- {PCI_VDEVICE(LOONGSON, 0x7a06), CHIP_LS7A1000},
- {PCI_VDEVICE(LOONGSON, 0x7a36), CHIP_LS7A2000},
+ { PCI_VDEVICE(LOONGSON, 0x7a06), .driver_data = CHIP_LS7A1000 },
+ { PCI_VDEVICE(LOONGSON, 0x7a36), .driver_data = CHIP_LS7A2000 },
{ }
};
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index eefbc7e0f9c8..f656b85b8421 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -2578,7 +2578,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
.atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = mtk_dp_bridge_attach,
.detach = mtk_dp_bridge_detach,
.atomic_enable = mtk_dp_bridge_atomic_enable,
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index bb4b7e6f1e29..959c994eef24 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -989,7 +989,7 @@ static const struct drm_bridge_funcs mtk_dpi_bridge_funcs = {
.atomic_get_input_bus_fmts = mtk_dpi_bridge_atomic_get_input_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.debugfs_init = mtk_dpi_debugfs_init,
};
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index eb325e68aa59..3f3f56eed3f9 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -894,7 +894,7 @@ static const struct drm_bridge_funcs mtk_dsi_bridge_funcs = {
.atomic_enable = mtk_dsi_bridge_atomic_enable,
.atomic_pre_enable = mtk_dsi_bridge_atomic_pre_enable,
.atomic_post_disable = mtk_dsi_bridge_atomic_post_disable,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = mtk_dsi_bridge_mode_valid,
.mode_set = mtk_dsi_bridge_mode_set,
};
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 38a7c7953874..738090a47176 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1072,7 +1072,7 @@ static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = {
.mode_valid = mtk_hdmi_bridge_mode_valid,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = mtk_hdmi_bridge_attach,
.mode_fixup = mtk_hdmi_bridge_mode_fixup,
.atomic_disable = mtk_hdmi_bridge_atomic_disable,
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
index 7bbf463056c9..d9316d30fd5f 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -1326,7 +1326,7 @@ static const struct drm_bridge_funcs mtk_v2_hdmi_bridge_funcs = {
.atomic_post_disable = mtk_hdmi_v2_bridge_post_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.detect = mtk_hdmi_v2_bridge_detect,
.edid_read = mtk_hdmi_v2_bridge_edid_read,
.hpd_enable = mtk_hdmi_v2_hpd_enable,
diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
index 8b26a0031cde..22cacb1660c4 100644
--- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c
+++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c
@@ -215,7 +215,7 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
.atomic_check = meson_encoder_cvbs_atomic_check,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
int meson_encoder_cvbs_probe(struct meson_drm *priv)
diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c
index c1f4685073bb..3e422b612f74 100644
--- a/drivers/gpu/drm/meson/meson_encoder_dsi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c
@@ -96,7 +96,7 @@ static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {
.atomic_disable = meson_encoder_dsi_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
int meson_encoder_dsi_probe(struct meson_drm *priv)
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
index 55c0601df3c6..0c7a72cb514a 100644
--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -366,7 +366,7 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
.atomic_check = meson_encoder_hdmi_atomic_check,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
int meson_encoder_hdmi_probe(struct meson_drm *priv)
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index d7b5dfdb28aa..dfae5812284d 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -204,18 +204,18 @@ int mgag200_device_init(struct mga_device *mdev,
*/
static const struct pci_device_id mgag200_pciidlist[] = {
- { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
- { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },
- { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
- { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
- { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
- { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },
- { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH },
- { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER },
- { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 },
- { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 },
- { PCI_VENDOR_ID_MATROX, 0x53a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH5 },
- {0,}
+ { PCI_VDEVICE(MATROX, 0x0520), .driver_data = G200_PCI },
+ { PCI_VDEVICE(MATROX, 0x0521), .driver_data = G200_AGP },
+ { PCI_VDEVICE(MATROX, 0x0522), .driver_data = G200_SE_A },
+ { PCI_VDEVICE(MATROX, 0x0524), .driver_data = G200_SE_B },
+ { PCI_VDEVICE(MATROX, 0x0530), .driver_data = G200_EV },
+ { PCI_VDEVICE(MATROX, 0x0532), .driver_data = G200_WB },
+ { PCI_VDEVICE(MATROX, 0x0533), .driver_data = G200_EH },
+ { PCI_VDEVICE(MATROX, 0x0534), .driver_data = G200_ER },
+ { PCI_VDEVICE(MATROX, 0x0536), .driver_data = G200_EW3 },
+ { PCI_VDEVICE(MATROX, 0x0538), .driver_data = G200_EH3 },
+ { PCI_VDEVICE(MATROX, 0x053a), .driver_data = G200_EH5 },
+ { }
};
MODULE_DEVICE_TABLE(pci, mgag200_pciidlist);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index b659d22f5f28..06881bfd6181 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -52,7 +52,7 @@ static void msm_dp_bridge_debugfs_init(struct drm_bridge *bridge, struct dentry
static const struct drm_bridge_funcs msm_dp_bridge_ops = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = msm_dp_bridge_atomic_enable,
.atomic_disable = msm_dp_bridge_atomic_disable,
.atomic_post_disable = msm_dp_bridge_atomic_post_disable,
@@ -235,7 +235,7 @@ static const struct drm_bridge_funcs msm_edp_bridge_ops = {
.atomic_post_disable = msm_edp_bridge_atomic_post_disable,
.mode_set = msm_dp_bridge_mode_set,
.mode_valid = msm_edp_bridge_mode_valid,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_check = msm_edp_bridge_atomic_check,
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 982abaaac00d..de1ba11987fc 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -166,6 +166,7 @@ struct msm_dsi_host {
struct drm_display_mode *mode;
struct drm_dsc_config *dsc;
+ unsigned int dsc_slice_per_pkt;
/* connected device info */
unsigned int channel;
@@ -938,17 +939,10 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod
slice_per_intf = dsc->slice_count;
total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf;
- bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */
+ bytes_per_pkt = dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt;
eol_byte_num = total_bytes_per_intf % 3;
-
- /*
- * Typically, pkt_per_line = slice_per_intf * slice_per_pkt.
- *
- * Since the current driver only supports slice_per_pkt = 1,
- * pkt_per_line will be equal to slice per intf for now.
- */
- pkt_per_line = slice_per_intf;
+ pkt_per_line = slice_per_intf / msm_host->dsc_slice_per_pkt;
if (is_cmd_mode) /* packet data type */
reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE);
@@ -1104,12 +1098,8 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
else
/*
* When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1.
- * Currently, the driver only supports default value of slice_per_pkt = 1
- *
- * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info
- * and adjust DSC math to account for slice_per_pkt.
*/
- wc = msm_host->dsc->slice_chunk_size + 1;
+ wc = msm_host->dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt + 1;
dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL,
DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) |
@@ -1718,8 +1708,13 @@ static int dsi_host_attach(struct mipi_dsi_host *host,
msm_host->lanes = dsi->lanes;
msm_host->format = dsi->format;
msm_host->mode_flags = dsi->mode_flags;
- if (dsi->dsc)
+ if (dsi->dsc) {
msm_host->dsc = dsi->dsc;
+ if (dsi->mode_flags & MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT)
+ msm_host->dsc_slice_per_pkt = dsi->dsc->slice_count;
+ else
+ msm_host->dsc_slice_per_pkt = 1;
+ }
if (msm_host->format == MIPI_DSI_FMT_RGB101010) {
if (!msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G,
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
index c4d5e0faf3b3..7abb9243dba5 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -455,7 +455,7 @@ static enum drm_mode_status msm_hdmi_bridge_tmds_char_rate_valid(const struct dr
static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable,
.atomic_post_disable = msm_hdmi_bridge_atomic_post_disable,
.edid_read = msm_hdmi_bridge_edid_read,
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 32d5ebea2596..ac86b427c0e5 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -824,7 +824,6 @@ static const struct file_operations fops = {
#define DRIVER_FEATURES_GPU ( \
DRIVER_GEM | \
- DRIVER_GEM_GPUVA | \
DRIVER_RENDER | \
DRIVER_SYNCOBJ | \
DRIVER_SYNCOBJ_TIMELINE | \
@@ -832,7 +831,6 @@ static const struct file_operations fops = {
#define DRIVER_FEATURES_KMS ( \
DRIVER_GEM | \
- DRIVER_GEM_GPUVA | \
DRIVER_ATOMIC | \
DRIVER_MODESET | \
0 )
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
index f5bb59cd5028..e2173c4d6fc2 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
@@ -186,7 +186,7 @@ static int lcdif_load(struct drm_device *drm)
ret = lcdif_attach_bridge(lcdif);
if (ret)
- return dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
+ return ret;
drm->mode_config.min_width = LCDIF_MIN_XRES;
drm->mode_config.min_height = LCDIF_MIN_YRES;
diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c
index ade76c3f4e4e..79e0f483ca20 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_kms.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c
@@ -338,7 +338,8 @@ static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags)
* Downstream set it to 256B burst size to improve the memory
* efficiency so set it here too.
*/
- ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) |
+ ctrl = CTRLDESCL0_3_STATE_CLEAR_VSYNC |
+ CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) |
CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]);
writel(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3);
}
@@ -374,14 +375,23 @@ static void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
int ret;
reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ /* Disable the layer for DMA. */
reg &= ~CTRLDESCL0_5_EN;
+ /*
+ * It is necessary to wait for the full frame to finish streaming
+ * through the DMA engine before we can safely disable it by removing
+ * the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the
+ * hardware confused and unable to resume streaming for the next frame.
+ */
+ reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ /* Wait for the frame to finish or timeout after 50 ms. */
ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
- reg, !(reg & CTRLDESCL0_5_EN),
- 0, 36000); /* Wait ~2 frame times max */
+ reg, !(reg & CTRLDESCL0_5_SHADOW_LOAD_EN),
+ 200, 50000);
if (ret)
- drm_err(lcdif->drm, "Failed to disable controller!\n");
+ drm_err(lcdif->drm, "Timed out waiting for final vblank!\n");
reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
reg &= ~DISP_PARA_DISP_ON;
diff --git a/drivers/gpu/drm/mxsfb/lcdif_regs.h b/drivers/gpu/drm/mxsfb/lcdif_regs.h
index c55dfb236c1d..17882c593d27 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_regs.h
+++ b/drivers/gpu/drm/mxsfb/lcdif_regs.h
@@ -190,6 +190,7 @@
#define CTRLDESCL0_1_WIDTH(n) ((n) & 0xffff)
#define CTRLDESCL0_1_WIDTH_MASK GENMASK(15, 0)
+#define CTRLDESCL0_3_STATE_CLEAR_VSYNC BIT(23)
#define CTRLDESCL0_3_P_SIZE(n) (((n) << 20) & CTRLDESCL0_3_P_SIZE_MASK)
#define CTRLDESCL0_3_P_SIZE_MASK GENMASK(22, 20)
#define CTRLDESCL0_3_T_SIZE(n) (((n) << 16) & CTRLDESCL0_3_T_SIZE_MASK)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index e16f59b00f6f..7ea95ab960a0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -153,22 +153,6 @@ nouveau_name(struct drm_device *dev)
return nouveau_platform_name(to_platform_device(dev->dev));
}
-static inline bool
-nouveau_cli_work_ready(struct dma_fence *fence)
-{
- unsigned long flags;
- bool ret = true;
-
- dma_fence_lock_irqsave(fence, flags);
- if (!dma_fence_is_signaled_locked(fence))
- ret = false;
- dma_fence_unlock_irqrestore(fence, flags);
-
- if (ret == true)
- dma_fence_put(fence);
- return ret;
-}
-
static void
nouveau_cli_work(struct work_struct *w)
{
@@ -176,9 +160,25 @@ nouveau_cli_work(struct work_struct *w)
struct nouveau_cli_work *work, *wtmp;
mutex_lock(&cli->lock);
list_for_each_entry_safe(work, wtmp, &cli->worker, head) {
- if (!work->fence || nouveau_cli_work_ready(work->fence)) {
+ struct dma_fence *fence = work->fence;
+
+ if (!fence || dma_fence_is_signaled(fence)) {
+ if (fence) {
+ unsigned long flags;
+
+ /*
+ * Because fence can still be in the process of
+ * processing the callback list, and the
+ * callback references the work we are about to
+ * free, we need to sync with the callback
+ * processing before freeing the work.
+ */
+ dma_fence_lock_irqsave(fence, flags);
+ dma_fence_unlock_irqrestore(fence, flags);
+ }
list_del(&work->head);
work->func(work);
+ dma_fence_put(fence);
}
}
mutex_unlock(&cli->lock);
@@ -1364,7 +1364,6 @@ static struct drm_driver
driver_stub = {
.driver_features = DRIVER_GEM |
DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE |
- DRIVER_GEM_GPUVA |
DRIVER_MODESET |
DRIVER_RENDER,
.open = nouveau_drm_open,
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 20dba02d6175..c5a24dff4b69 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -313,11 +313,20 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
rep->offset = nvbo->offset;
if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50 &&
!nouveau_cli_uvmm(cli)) {
+ int ret;
+
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
+ if (ret)
+ return ret;
+
vma = nouveau_vma_find(nvbo, vmm);
- if (!vma)
+ if (!vma) {
+ ttm_bo_unreserve(&nvbo->bo);
return -EINVAL;
+ }
rep->offset = vma->addr;
+ ttm_bo_unreserve(&nvbo->bo);
} else
rep->offset = 0;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
index dbd984da7501..0608266188d3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c
@@ -253,7 +253,7 @@ gv100_head_vblank_get(struct nvkm_head *head)
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000004);
}
-static void
+void
gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
@@ -263,7 +263,7 @@ gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
*hline = nvkm_rd32(device, 0x616334 + hoff) & 0x0000ffff;
}
-static void
+void
gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
index 856252bf559a..b642729c254f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
@@ -53,6 +53,8 @@ void gf119_head_rgclk(struct nvkm_head *, int);
int gv100_head_cnt(struct nvkm_disp *, unsigned long *);
int gv100_head_new(struct nvkm_disp *, int id);
+void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
+void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
#define HEAD_MSG(h,l,f,a...) do { \
struct nvkm_head *_h = (h); \
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
index 1155f079b0c3..e77733a5d9c3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
@@ -625,14 +625,10 @@ r535_head_vblank_get(struct nvkm_head *head)
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
}
-static void
-r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
-{
-}
-
static const struct nvkm_head_func
r535_head = {
- .state = r535_head_state,
+ .state = gv100_head_state,
+ .rgpos = gv100_head_rgpos,
.vblank_get = r535_head_vblank_get,
.vblank_put = r535_head_vblank_put,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
index 107bdb642f22..190c082b12c8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
@@ -231,29 +231,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt,
* covered by a number of LPTEs, the LPTEs once again take
* control over their address range.
*
- * Determine how many LPTEs need to transition state.
+ * Transition each LPTE individually as each may have a
+ * different target state (sparse, invalid, or valid).
*/
- pgt->pte[ptei].s.spte_valid = false;
- for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) {
+ for (ptei++; ptei < lpti; ptei++) {
if (pgt->pte[ptei].s.sptes)
break;
- pgt->pte[ptei].s.spte_valid = false;
}
- if (pgt->pte[pteb].s.sparse) {
- TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes);
- pair->func->sparse(vmm, pgt->pt[0], pteb, ptes);
- } else if (!pgt->pte[pteb].s.lpte_valid) {
- if (pair->func->invalid) {
- /* If the MMU supports it, restore the LPTE to the
- * INVALID state to tell the MMU there is no point
- * trying to fetch the corresponding SPTEs.
- */
- TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes);
- pair->func->invalid(vmm, pgt->pt[0], pteb, ptes);
+ while (pteb < ptei) {
+ pgt->pte[pteb].s.spte_valid = false;
+ if (pgt->pte[pteb].s.sparse) {
+ TRA(it, "LPTE %05x: U -> S", pteb);
+ pair->func->sparse(vmm, pgt->pt[0], pteb, 1);
+ } else if (!pgt->pte[pteb].s.lpte_valid) {
+ if (pair->func->invalid) {
+ TRA(it, "LPTE %05x: U -> I", pteb);
+ pair->func->invalid(vmm, pgt->pt[0], pteb, 1);
+ }
}
- } else {
- TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes);
+ pteb++;
}
}
}
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index e306247ed8a0..61cfd003569c 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -497,7 +497,7 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
.mode_set = hdmi4_bridge_mode_set,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = hdmi4_bridge_enable,
.atomic_disable = hdmi4_bridge_disable,
.hpd_notify = hdmi4_bridge_hpd_notify,
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index ab2d4eb6787f..1ee3bbe20583 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -472,7 +472,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
.mode_set = hdmi5_bridge_mode_set,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = hdmi5_bridge_enable,
.atomic_disable = hdmi5_bridge_disable,
.edid_read = hdmi5_bridge_edid_read,
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 7450b27622a2..cfbfb371bc67 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -17,6 +17,16 @@ config DRM_PANEL_ABT_Y030XX067A
Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300
and RG-99 handheld gaming consoles.
+config DRM_PANEL_ANBERNIC_TD4310
+ tristate "Anbernic TD4310 LCD panel"
+ depends on GPIOLIB && OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here to enable support for Anbernic designed panels with the
+ TD4310 panel controller such as the ones used on the Anbernic RG
+ Vita Pro.
+
config DRM_PANEL_ARM_VERSATILE
tristate "ARM Versatile panel driver"
depends on OF
@@ -105,6 +115,17 @@ config DRM_PANEL_BOE_TV101WUM_LL2
Say Y here if you want to support for BOE TV101WUM-LL2
WUXGA PANEL DSI Video Mode panel
+config DRM_PANEL_CHIPONE_ICNA35XX
+ tristate "Chipone ICNA35XX panel driver"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
+ help
+ Say Y here if you want to enable support for the panels built
+ around the Chipone ICNA3512 and ICNA3520 display controllers,
+ such as some Tianma panels used in AYN Odin2 Portal and Thor.
+
config DRM_PANEL_CHIPWEALTH_CH13726A
tristate "CHIPWEALTH CH13726A-based DSI panel"
depends on OF
@@ -264,6 +285,18 @@ config DRM_PANEL_HYDIS_HV101HD1
If M is selected the module will be called panel-hydis-hv101hd1
+config DRM_PANEL_ILITEK_ILI7807S
+ tristate "Ilitek ILI7807S-based panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for panels based on the
+ Ilitek ILI7807S display controller, such as the DLC DLC0697
+ 1080x1920 MIPI DSI panel.
+
+ If M is selected the module will be called panel-ilitek-ili7807s.
+
config DRM_PANEL_ILITEK_IL9322
tristate "Ilitek ILI9322 320x240 QVGA panels"
depends on OF && SPI
@@ -284,6 +317,15 @@ config DRM_PANEL_ILITEK_ILI9341
QVGA (240x320) RGB panels. support serial & parallel rgb
interface.
+config DRM_PANEL_ILITEK_ILI9488
+ tristate "Ilitek ILI9488-based panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for panels based on the
+ Ilitek ILI9488 controller.
+
config DRM_PANEL_ILITEK_ILI9805
tristate "Ilitek ILI9805-based panels"
depends on OF
@@ -796,6 +838,18 @@ config DRM_PANEL_RENESAS_R61307
This panel controller can be found in LG Optimus Vu P895 smartphone
in combination with LCD panel.
+config DRM_PANEL_RENESAS_R63419
+ tristate "Renesas R63419 dual-DSI video mode panels"
+ depends on OF && GPIOLIB
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for Ayaneo WT0600 and WT0630
+ 1440x2560 60Hz dual-DSI video mode display panels with Renesas
+ R63419 IC.
+
+ These panels are used in Ayaneo handheld gaming devices.
+
config DRM_PANEL_RENESAS_R69328
tristate "Renesas R69328 720x1280 DSI video mode panel"
depends on OF
@@ -1322,6 +1376,8 @@ config DRM_PANEL_VISIONOX_VTDR6130
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 Visionox
VTDR6130 1080x2400 AMOLED DSI panel.
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index c2c5cf817116..0f29f22f589e 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o
+obj-$(CONFIG_DRM_PANEL_ANBERNIC_TD4310) += panel-anbernic-td4310.o
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o
@@ -9,6 +10,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o
obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o
obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o
obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
+obj-$(CONFIG_DRM_PANEL_CHIPONE_ICNA35XX) += panel-chipone-icna35xx.o
obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o
obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
@@ -26,8 +28,10 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.o
@@ -78,6 +82,7 @@ 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_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
obj-$(CONFIG_DRM_PANEL_RENESAS_R69328) += panel-renesas-r69328.o
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o
diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
new file mode 100644
index 000000000000..9a1b4525423c
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Anbernic panels with TD4310 panel controller.
+ *
+ * Copyright (C) 2026 Chris Morgan <macromorgan@hotmail.com>
+ *
+ */
+
+#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_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/mipi_display.h>
+
+struct anbernic_panel_td4310_info {
+ const struct drm_display_mode *display_mode;
+ u16 width_mm;
+ u16 height_mm;
+ u32 bus_flags;
+ unsigned long mode_flags;
+ u32 format;
+ u32 lanes;
+ u16 prepare_delay;
+ u16 reset_delay;
+ u16 init_delay;
+ u16 enable_delay;
+ u16 disable_delay;
+ u16 unprepare_delay;
+};
+
+struct anbernic_panel_td4310 {
+ struct device *dev;
+ struct mipi_dsi_device *dsi;
+ struct drm_panel panel;
+ const struct anbernic_panel_td4310_info *panel_info;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *enable_gpio;
+ struct regulator *vdd;
+ enum drm_panel_orientation orientation;
+};
+
+static inline struct anbernic_panel_td4310 *panel_to_anbernic_panel_td4310(struct drm_panel *panel)
+{
+ return container_of(panel, struct anbernic_panel_td4310, panel);
+}
+
+static int panel_anbernic_td4310_prepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ int ret;
+
+ ret = regulator_enable(ctx->vdd);
+ if (ret)
+ return ret;
+
+ ret = gpiod_set_value_cansleep(ctx->enable_gpio, 1);
+ if (ret)
+ goto err_enable;
+
+ if (ctx->panel_info->enable_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->enable_delay);
+
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ if (ret)
+ goto err_reset;
+
+ mipi_dsi_msleep(&dsi_ctx, 10);
+
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ if (ret)
+ goto err_reset;
+
+ if (ctx->panel_info->reset_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->reset_delay);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->prepare_delay);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->prepare_delay);
+
+ if (dsi_ctx.accum_err) {
+ ret = dsi_ctx.accum_err;
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+err_enable:
+ regulator_disable(ctx->vdd);
+ return ret;
+}
+
+static int panel_anbernic_td4310_unprepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->unprepare_delay);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->disable_delay);
+
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_disable(ctx->vdd);
+
+ return 0;
+}
+
+static int panel_anbernic_td4310_get_mode(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ const struct anbernic_panel_td4310_info *panel_info = ctx->panel_info;
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = panel_info->width_mm;
+ connector->display_info.height_mm = panel_info->height_mm;
+ connector->display_info.bus_flags = panel_info->bus_flags;
+
+ return drm_connector_helper_get_modes_fixed(connector, panel_info->display_mode);
+}
+
+static enum drm_panel_orientation panel_anbernic_td4310_get_orientation(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs panel_anbernic_td4310_funcs = {
+ .prepare = panel_anbernic_td4310_prepare,
+ .unprepare = panel_anbernic_td4310_unprepare,
+ .get_modes = panel_anbernic_td4310_get_mode,
+ .get_orientation = panel_anbernic_td4310_get_orientation,
+};
+
+static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct anbernic_panel_td4310 *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct anbernic_panel_td4310, panel,
+ &panel_anbernic_td4310_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->dev = dev;
+
+ ctx->panel_info = of_device_get_match_data(dev);
+ if (!ctx->panel_info)
+ return -EINVAL;
+
+ ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get panel orientation\n");
+
+ 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),
+ "Cannot get reset gpio\n");
+
+ ctx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->enable_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio),
+ "Cannot get enable gpio\n");
+
+ ctx->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(ctx->vdd))
+ return dev_err_probe(dev, PTR_ERR(ctx->vdd),
+ "Failed to request vdd regulator\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = ctx->panel_info->lanes;
+ dsi->format = ctx->panel_info->format;
+ dsi->mode_flags = ctx->panel_info->mode_flags;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ devm_drm_panel_add(dev, &ctx->panel);
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
+
+ return 0;
+}
+
+static const struct drm_display_mode anbernic_vitapro_mode = {
+ .clock = 140020,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 50,
+ .hsync_end = 1080 + 50 + 4,
+ .htotal = 1080 + 50 + 4 + 50,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 15,
+ .vsync_end = 1920 + 15 + 4,
+ .vtotal = 1920 + 15 + 4 + 32,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct anbernic_panel_td4310_info anbernic_vitapro_info = {
+ .display_mode = &anbernic_vitapro_mode,
+ .width_mm = 69,
+ .height_mm = 121,
+ .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .prepare_delay = 50,
+ .reset_delay = 220,
+ .enable_delay = 120,
+ .disable_delay = 50,
+ .unprepare_delay = 20,
+};
+
+static const struct of_device_id panel_anbernic_td4310_of_match[] = {
+ {
+ .compatible = "anbernic,panel-vita-pro",
+ .data = &anbernic_vitapro_info,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, panel_anbernic_td4310_of_match);
+
+static struct mipi_dsi_driver anbernic_panel_td4310_driver = {
+ .driver = {
+ .name = "panel-anbernic-td4310",
+ .of_match_table = panel_anbernic_td4310_of_match,
+ },
+ .probe = panel_anbernic_td4310_probe,
+};
+module_mipi_dsi_driver(anbernic_panel_td4310_driver);
+
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_DESCRIPTION("DRM driver for Anbernic TD4310 MIPI DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c
new file mode 100644
index 000000000000..86d096455caa
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Chipone ICNA35XX Driver IC panels driver
+ *
+ * Copyright (c) 2025 Teguh Sobirin <teguh@sobir.in>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.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_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct panel_info {
+ struct drm_panel panel;
+ struct drm_connector *connector;
+ struct mipi_dsi_device *dsi;
+ struct panel_desc *desc;
+ enum drm_panel_orientation orientation;
+
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+};
+
+struct panel_desc {
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ 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 panel_info *pinfo);
+
+ struct drm_dsc_config dsc;
+};
+
+static const struct regulator_bulk_data panel_supplies[] = {
+ { .supply = "vdd" },
+ { .supply = "vddio" },
+ { .supply = "vci" },
+ { .supply = "disp" },
+ { .supply = "blvdd" },
+};
+
+static inline struct panel_info *to_panel_info(struct drm_panel *panel)
+{
+ return container_of(panel, struct panel_info, panel);
+}
+
+static int icna3512_init_sequence(struct panel_info *pinfo)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
+ struct drm_dsc_picture_parameter_set pps;
+
+ pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0F);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xCE, 0x22);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01);
+
+ /* 165 hz */
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x20);
+
+ drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static int icna3520_init_sequence(struct panel_info *pinfo)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
+ struct drm_dsc_picture_parameter_set pps;
+
+ pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ /* 120 hz */
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB3,
+ 0x00, 0xD8, 0x00, 0x1C, 0x00, 0x4C);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x00);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0D);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x27);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB6, 0x03);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xBB, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x24);
+
+ drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct drm_display_mode odin2portal_modes[] = {
+ {
+ /* 165Hz */
+ .clock = (1080 + 98 + 1 + 23) * (1920 + 20 + 1 + 15) * 165 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 98,
+ .hsync_end = 1080 + 98 + 1,
+ .htotal = 1080 + 98 + 1 + 23,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 20,
+ .vsync_end = 1920 + 20 + 1,
+ .vtotal = 1920 + 20 + 1 + 15,
+ }
+};
+
+static const struct drm_display_mode thor_top_modes[] = {
+ {
+ /* 120Hz */
+ .clock = (1080 + 24 + 1 + 24) * (1920 + 28 + 1 + 28) * 120 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 24,
+ .hsync_end = 1080 + 24 + 1,
+ .htotal = 1080 + 24 + 1 + 24,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 28,
+ .vsync_end = 1920 + 28 + 1,
+ .vtotal = 1920 + 28 + 1 + 28,
+ }
+};
+
+static struct panel_desc odin2portal_desc = {
+ .modes = odin2portal_modes,
+ .num_modes = ARRAY_SIZE(odin2portal_modes),
+ .width_mm = 160,
+ .height_mm = 89,
+ .bpc = 8,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_LPM,
+ .init_sequence = icna3512_init_sequence,
+ .dsc = {
+ .dsc_version_major = 0x1,
+ .dsc_version_minor = 0x1,
+ .slice_height = 20,
+ .slice_width = 540,
+ .slice_count = 2,
+ .bits_per_component = 8,
+ .bits_per_pixel = 8 << 4,
+ .block_pred_enable = true,
+ },
+};
+
+static struct panel_desc thor_top_desc = {
+ .modes = thor_top_modes,
+ .num_modes = ARRAY_SIZE(thor_top_modes),
+ .width_mm = 136,
+ .height_mm = 68,
+ .bpc = 8,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_LPM,
+ .init_sequence = icna3520_init_sequence,
+ .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 void icna35xx_reset(struct panel_info *pinfo)
+{
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+ usleep_range(20000, 21000);
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+ usleep_range(20000, 21000);
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+ usleep_range(20000, 21000);
+}
+
+static int icna35xx_prepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
+ if (ret < 0) {
+ dev_err(panel->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ icna35xx_reset(pinfo);
+
+ ret = pinfo->desc->init_sequence(pinfo);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
+ dev_err(panel->dev, "failed to initialize panel: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int icna35xx_disable(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
+
+ pinfo->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 50);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return dsi_ctx.accum_err;
+}
+
+static int icna35xx_unprepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies);
+
+ return 0;
+}
+
+static int icna35xx_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, pinfo->desc->modes);
+}
+
+static enum drm_panel_orientation icna35xx_get_orientation(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+
+ return pinfo->orientation;
+}
+
+static const struct drm_panel_funcs icna35xx_panel_funcs = {
+ .disable = icna35xx_disable,
+ .prepare = icna35xx_prepare,
+ .unprepare = icna35xx_unprepare,
+ .get_modes = icna35xx_get_modes,
+ .get_orientation = icna35xx_get_orientation,
+};
+
+static int icna35xx_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);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return ret;
+}
+
+static int icna35xx_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return ret < 0 ? ret : brightness;
+}
+
+static const struct backlight_ops icna35xx_bl_ops = {
+ .update_status = icna35xx_bl_update_status,
+ .get_brightness = icna35xx_bl_get_brightness,
+};
+
+static struct backlight_device *icna35xx_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 4096,
+ .max_brightness = 4096,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &icna35xx_bl_ops, &props);
+}
+
+static int icna35xx_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct panel_info *pinfo;
+ int ret;
+
+ pinfo = devm_drm_panel_alloc(dev, __typeof(*pinfo), panel,
+ &icna35xx_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(pinfo))
+ return PTR_ERR(pinfo);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(panel_supplies),
+ panel_supplies, &pinfo->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pinfo->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio), "failed to get reset gpio\n");
+
+ pinfo->desc = (struct panel_desc *)of_device_get_match_data(dev);
+ if (!pinfo->desc)
+ return -ENODEV;
+
+ pinfo->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, pinfo);
+
+ ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation);
+ if (ret < 0) {
+ dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
+ return ret;
+ }
+
+ pinfo->panel.prepare_prev_first = true;
+
+ pinfo->panel.backlight = icna35xx_create_backlight(dsi);
+ if (IS_ERR(pinfo->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(pinfo->panel.backlight),
+ "Failed to create backlight\n");
+
+ ret = devm_drm_panel_add(dev, &pinfo->panel);
+ if (ret)
+ return ret;
+
+ pinfo->dsi->lanes = pinfo->desc->lanes;
+ pinfo->dsi->format = pinfo->desc->format;
+ pinfo->dsi->mode_flags = pinfo->desc->mode_flags;
+ pinfo->dsi->dsc = &pinfo->desc->dsc;
+
+ return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static const struct of_device_id icna35xx_of_match[] = {
+ { .compatible = "ayaneo,pocketds-panel-top", .data = &odin2portal_desc },
+ { .compatible = "ayntec,odin2portal-panel", .data = &odin2portal_desc },
+ { .compatible = "ayntec,odin3-panel", .data = &thor_top_desc },
+ { .compatible = "ayntec,thor-panel-top", .data = &thor_top_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, icna35xx_of_match);
+
+static struct mipi_dsi_driver icna35xx_driver = {
+ .probe = icna35xx_probe,
+ .driver = {
+ .name = "panel-chipone-icna35xx",
+ .of_match_table = icna35xx_of_match,
+ },
+};
+module_mipi_dsi_driver(icna35xx_driver);
+
+MODULE_AUTHOR("Teguh Sobirin <teguh@sobir.in>");
+MODULE_DESCRIPTION("DRM driver for Chipone ICNA35XX based MIPI DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index aa27d6cd932e..4aafdc7bef7e 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -1855,6 +1855,14 @@ static const struct panel_delay delay_200_500_e50_po2e200 = {
.powered_on_to_enable = 200,
};
+static const struct panel_delay delay_200_500_e50_po2e200_d100 = {
+ .hpd_absent = 200,
+ .unprepare = 500,
+ .enable = 50,
+ .powered_on_to_enable = 200,
+ .disable = 100,
+};
+
static const struct panel_delay delay_200_150_e50 = {
.hpd_absent = 200,
.unprepare = 150,
@@ -1934,6 +1942,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x29c0, &delay_200_500_e50, "B116XAT04.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x30ed, &delay_200_500_e50, "G156HAN03.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x3c9f, &delay_200_500_e50, "B140HAK03.5"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"),
@@ -2004,6 +2013,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0964, &delay_200_500_e50, "NV133WUM-N61"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x096e, &delay_200_500_e50_po2e200, "NV116WHM-T07 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0985, &delay_200_500_e50, "NE160QDM-NY1"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0993, &delay_200_500_e80, "NV116WHM-T14 V8.0"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x09ad, &delay_200_500_e80, "NV116WHM-N47"),
@@ -2036,8 +2046,13 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d45, &delay_200_500_e80, "NV116WHM-N4B"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d73, &delay_200_500_e80, "NE140WUM-N6S"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d98, &delay_200_500_e50_po2e200_d100, "NV140FHM-N5B"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0db3, &delay_200_500_e80, "NV153WUM-N42"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ddf, &delay_200_500_e80, "NV116WHM-T01"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x386e, &delay_200_500_e80, "NV116WH2-M30"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x3879, &delay_200_500_e80, "NT116WHM-N21"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x388b, &delay_200_500_e80, "NV116FH1-M31"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x388c, &delay_200_500_e80, "NV116FH1-M30"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1130, &delay_200_500_e50, "N116BGE-EB2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1132, &delay_200_500_e80_d50, "N116BGE-EA2"),
@@ -2060,6 +2075,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1163, &delay_200_500_e80_d50, "N116BCJ-EAK"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1169, &delay_200_500_e80_d50, "N116BCN-EA1"),
+ EDP_PANEL_ENTRY('C', 'M', 'N', 0x116d, &delay_200_500_e80_d50, "N116BCP-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x117a, &delay_200_500_e80_d50, "N116BCL-EAK"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x124c, &delay_200_500_e80_d50, "N122JCA-ENK"),
@@ -2086,6 +2102,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1103, &delay_200_500_e80_d50, "MNB601LS1-3"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1104, &delay_200_500_e50_d100, "MNB601LS1-4"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x110a, &delay_200_500_e50, "PNB601LS1-2"),
+ EDP_PANEL_ENTRY('C', 'S', 'W', 0x110d, &delay_200_500_e50, "MNB601LS1-8"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x143f, &delay_200_500_e50, "MNE007QS3-6"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1448, &delay_200_500_e50, "MNE007QS3-7"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"),
@@ -2099,6 +2116,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"),
+ EDP_PANEL_ENTRY('H', 'K', 'C', 0x1203, &delay_200_500_e80, "MB116AS01'3"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d51, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5b, &delay_200_500_e200, "MB116AN01"),
EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5c, &delay_200_500_e200, "MB116AN01-2"),
@@ -2151,6 +2169,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"),
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"),
+ EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e50_d100, "TM156VDXP25"),
{ /* sentinal */ }
};
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83121a.c b/drivers/gpu/drm/panel/panel-himax-hx83121a.c
index 8b11fce4c7c5..3b6a562c4d11 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx83121a.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx83121a.c
@@ -33,7 +33,9 @@ struct himax {
struct drm_dsc_config dsc;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *supplies;
+ struct regulator *bl_supply;
struct backlight_device *backlight;
+ bool backlight_enabled;
};
struct panel_desc {
@@ -192,10 +194,31 @@ static const struct drm_panel_funcs himax_panel_funcs = {
static int himax_bl_update_status(struct backlight_device *bl)
{
- struct mipi_dsi_device *dsi = bl_get_data(bl);
+ struct himax *ctx = bl_get_data(bl);
u16 brightness = backlight_get_brightness(bl);
+ int ret = 0;
+
+ if (!brightness) {
+ if (ctx->backlight_enabled)
+ ret = regulator_disable(ctx->bl_supply);
+ if (ret)
+ return ret;
+
+ ctx->backlight_enabled = false;
+
+ return 0;
+ }
+
/* TODO: brightness to raw map table */
- return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (!ctx->backlight_enabled)
+ ret = regulator_enable(ctx->bl_supply);
+ if (ret)
+ return ret;
+
+ ctx->backlight_enabled = true;
+
+ return mipi_dsi_dcs_set_display_brightness_large(to_primary_dsi(ctx),
+ brightness);
}
static const struct backlight_ops himax_bl_ops = {
@@ -204,9 +227,9 @@ static const struct backlight_ops himax_bl_ops = {
};
static struct backlight_device *
-himax_create_backlight(struct mipi_dsi_device *dsi)
+himax_create_backlight(struct himax *ctx)
{
- struct device *dev = &dsi->dev;
+ struct device *dev = &to_primary_dsi(ctx)->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 512,
@@ -214,7 +237,7 @@ himax_create_backlight(struct mipi_dsi_device *dsi)
.scale = BACKLIGHT_SCALE_NON_LINEAR,
};
- return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ return devm_backlight_device_register(dev, dev_name(dev), dev, ctx,
&himax_bl_ops, &props);
}
@@ -645,7 +668,11 @@ static int himax_probe(struct mipi_dsi_device *dsi)
ctx->panel.prepare_prev_first = true;
if (desc->has_dcs_backlight) {
- ctx->backlight = himax_create_backlight(to_primary_dsi(ctx));
+ ctx->bl_supply = devm_regulator_get_optional(dev, "bl");
+ if (IS_ERR(ctx->bl_supply))
+ return dev_err_probe(dev, PTR_ERR(ctx->bl_supply),
+ "Failed to get backlight supply\n");
+ ctx->backlight = himax_create_backlight(ctx);
if (IS_ERR(ctx->backlight))
return dev_err_probe(dev, PTR_ERR(ctx->backlight),
"Failed to create backlight\n");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c
new file mode 100644
index 000000000000..12b491b0bca4
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct panel_desc {
+ const struct drm_display_mode *mode;
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+ void (*init)(struct mipi_dsi_multi_context *dsi_ctx);
+};
+
+struct ili7807s {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ const struct panel_desc *desc;
+
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct regulator_bulk_data ili7807s_supplies[] = {
+ { .supply = "vddi" },
+ { .supply = "avdd" },
+ { .supply = "avee" },
+};
+
+static inline struct ili7807s *to_ili7807s(struct drm_panel *panel)
+{
+ return container_of(panel, struct ili7807s, panel);
+}
+
+static void ili7807s_reset(struct ili7807s *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(10000, 11000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ mipi_dsi_dcs_soft_reset_multi(dsi_ctx);
+ mipi_dsi_msleep(dsi_ctx, 120);
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00);
+ 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_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01);
+ mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
+ mipi_dsi_msleep(dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
+ mipi_dsi_msleep(dsi_ctx, 20);
+}
+
+static int ili7807s_on(struct ili7807s *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ ctx->desc->init(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static int ili7807s_off(struct ili7807s *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return dsi_ctx.accum_err;
+}
+
+static int ili7807s_prepare(struct drm_panel *panel)
+{
+ struct ili7807s *ctx = to_ili7807s(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ msleep(20);
+
+ ili7807s_reset(ctx);
+
+ ret = ili7807s_on(ctx);
+ if (ret < 0) {
+ dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
+ return ret;
+}
+
+static int ili7807s_unprepare(struct drm_panel *panel)
+{
+ struct ili7807s *ctx = to_ili7807s(panel);
+ int ret;
+
+ ret = ili7807s_off(ctx);
+ if (ret < 0)
+ dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static int ili7807s_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ili7807s *ctx = to_ili7807s(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode);
+}
+
+static const struct drm_panel_funcs ili7807s_panel_funcs = {
+ .prepare = ili7807s_prepare,
+ .unprepare = ili7807s_unprepare,
+ .get_modes = ili7807s_get_modes,
+};
+
+static int ili7807s_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);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return ret;
+}
+
+static const struct backlight_ops ili7807s_bl_ops = {
+ .update_status = ili7807s_bl_update_status,
+};
+
+static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 0x3fff,
+ .max_brightness = 0x3fff,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &ili7807s_bl_ops, &props);
+}
+
+static const struct drm_display_mode dlc0697_mode = {
+ .clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000,
+
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 18,
+ .hsync_end = 1080 + 18 + 2,
+ .htotal = 1080 + 18 + 2 + 16,
+
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 26,
+ .vsync_end = 1920 + 26 + 4,
+ .vtotal = 1920 + 26 + 4 + 20,
+
+ .width_mm = 87,
+ .height_mm = 154,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct panel_desc dlc0697_desc = {
+ .mode = &dlc0697_mode,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
+ .init = dlc0697_init_sequence,
+};
+
+static int ili7807s_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct panel_desc *desc;
+ struct ili7807s *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel,
+ &ili7807s_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ desc = of_device_get_match_data(dev);
+ ctx->desc = desc;
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies),
+ ili7807s_supplies, &ctx->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ 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 gpio\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = desc->lanes;
+ dsi->format = desc->format;
+ dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ if (!ctx->panel.backlight) {
+ ctx->panel.backlight = ili7807s_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 dev_err_probe(dev, ret, "failed to add panel\n");
+
+ return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static const struct of_device_id ili7807s_of_match[] = {
+ { .compatible = "dlc,dlc0697", .data = &dlc0697_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ili7807s_of_match);
+
+static struct mipi_dsi_driver ili7807s_dsi_driver = {
+ .probe = ili7807s_probe,
+ .driver = {
+ .name = "panel-ilitek-ili7807s",
+ .of_match_table = ili7807s_of_match,
+ },
+};
+module_mipi_dsi_driver(ili7807s_dsi_driver);
+
+MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>");
+MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c
new file mode 100644
index 000000000000..7302766034fc
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/gpio/consumer.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>
+
+#include <video/mipi_display.h>
+
+struct ili9488_desc {
+ const struct drm_display_mode *display_mode;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ unsigned int lanes;
+ unsigned int bpc;
+ void (*init_sequence)(struct mipi_dsi_multi_context *ctx);
+};
+
+struct ili9488 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct gpio_desc *reset;
+ struct regulator_bulk_data supplies[2];
+ const struct ili9488_desc *desc;
+ enum drm_panel_orientation orientation;
+};
+
+static const char * const regulator_names[] = {
+ "vci",
+ "iovcc",
+};
+
+static void e35gh_i_mw800cb_init(struct mipi_dsi_multi_context *ctx)
+{
+ /* Gamma control 1,2 */
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xE0, 0x00, 0x10, 0x14, 0x01, 0x0E, 0x04, 0x33,
+ 0x56, 0x48, 0x03, 0x0C, 0x0B, 0x2B, 0x34, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xE1, 0x00, 0x12, 0x18, 0x05, 0x12, 0x06, 0x40,
+ 0x34, 0x57, 0x06, 0x10, 0x0C, 0x3B, 0x3F, 0x0F);
+ /* Power control 1,2 */
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xC0, 0x0F, 0x0C);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xC1, 0x41);
+ /* VCOM Control */
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xC5, 0x00, 0x25, 0x80);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x36, 0x48);
+ /* Interface pixel format 18bpp */
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x3A, 0x66);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xB0, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xB1, 0xA0);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xB4, 0x02);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xB6, 0x02, 0x02, 0x3B);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xE9, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xF7, 0xA9, 0x51, 0x2C, 0x82);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x21);
+}
+
+static const struct drm_display_mode e35gh_i_mw800cb_display_mode = {
+ .clock = 14400,
+
+ .hdisplay = 320,
+ .hsync_start = 320 + 60,
+ .hsync_end = 320 + 60 + 20,
+ .htotal = 320 + 60 + 20 + 42,
+
+ .vdisplay = 480,
+ .vsync_start = 480 + 20,
+ .vsync_end = 480 + 20 + 10,
+ .vtotal = 480 + 20 + 10 + 33,
+
+ .width_mm = 48,
+ .height_mm = 73,
+
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static inline struct ili9488 *panel_to_ili9488(struct drm_panel *panel)
+{
+ return container_of(panel, struct ili9488, panel);
+}
+
+static int ili9488_power_on(struct ili9488 *ili)
+{
+ struct mipi_dsi_device *dsi = ili->dsi;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies);
+ if (ret < 0) {
+ dev_err(&dsi->dev, "regulator bulk enable failed: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ili->reset, 0);
+ usleep_range(1000, 5000);
+ gpiod_set_value_cansleep(ili->reset, 1);
+ usleep_range(1000, 5000);
+ gpiod_set_value_cansleep(ili->reset, 0);
+ usleep_range(5000, 10000);
+
+ return 0;
+}
+
+static int ili9488_power_off(struct ili9488 *ili)
+{
+ struct mipi_dsi_device *dsi = ili->dsi;
+ int ret;
+
+ gpiod_set_value_cansleep(ili->reset, 1);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies);
+ if (ret)
+ dev_err(&dsi->dev, "regulator bulk disable failed: %d\n", ret);
+
+ return ret;
+}
+
+static int ili9488_activate(struct ili9488 *ili)
+{
+ struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi };
+
+ if (ili->desc->init_sequence)
+ ili->desc->init_sequence(&ctx);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
+ mipi_dsi_msleep(&ctx, 120);
+ mipi_dsi_dcs_set_display_on_multi(&ctx);
+
+ return ctx.accum_err;
+}
+
+static int ili9488_prepare(struct drm_panel *panel)
+{
+ struct ili9488 *ili = panel_to_ili9488(panel);
+ int ret;
+
+ ret = ili9488_power_on(ili);
+ if (ret)
+ return ret;
+
+ ret = ili9488_activate(ili);
+ if (ret) {
+ ili9488_power_off(ili);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ili9488_deactivate(struct ili9488 *ili)
+{
+ struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
+ mipi_dsi_msleep(&ctx, 120);
+
+ return ctx.accum_err;
+}
+
+static int ili9488_unprepare(struct drm_panel *panel)
+{
+ struct ili9488 *ili = panel_to_ili9488(panel);
+ struct mipi_dsi_device *dsi = ili->dsi;
+ int ret;
+
+ ili9488_deactivate(ili);
+ ret = ili9488_power_off(ili);
+ if (ret < 0)
+ dev_err(&dsi->dev, "power off failed: %d\n", ret);
+
+ return ret;
+}
+
+static int ili9488_get_modes(struct drm_panel *panel, struct drm_connector *connector)
+{
+ struct ili9488 *ili = panel_to_ili9488(panel);
+ const struct drm_display_mode *mode = ili->desc->display_mode;
+
+ connector->display_info.bpc = ili->desc->bpc;
+
+ return drm_connector_helper_get_modes_fixed(connector, mode);
+}
+
+static enum drm_panel_orientation ili9488_get_orientation(struct drm_panel *panel)
+{
+ struct ili9488 *ili = panel_to_ili9488(panel);
+
+ return ili->orientation;
+}
+
+static const struct drm_panel_funcs ili9488_funcs = {
+ .prepare = ili9488_prepare,
+ .unprepare = ili9488_unprepare,
+ .get_modes = ili9488_get_modes,
+ .get_orientation = ili9488_get_orientation,
+};
+
+static int ili9488_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct ili9488 *ili;
+ int i, ret;
+
+ ili = devm_drm_panel_alloc(dev, struct ili9488, panel, &ili9488_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ili))
+ return PTR_ERR(ili);
+
+ ili->desc = device_get_match_data(dev);
+ ili->dsi = dsi;
+
+ dsi->mode_flags = ili->desc->mode_flags;
+ dsi->format = ili->desc->format;
+ dsi->lanes = ili->desc->lanes;
+
+ ili->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ili->reset))
+ return dev_err_probe(dev, PTR_ERR(ili->reset),
+ "failed to get reset-gpios\n");
+
+ for (i = 0; i < ARRAY_SIZE(ili->supplies); i++)
+ ili->supplies[i].supply = regulator_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
+ ili->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ ret = of_drm_get_panel_orientation(dev->of_node, &ili->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get orientation\n");
+
+ ret = drm_panel_of_backlight(&ili->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get backlight\n");
+
+ ili->panel.prepare_prev_first = true;
+
+ ret = devm_drm_panel_add(dev, &ili->panel);
+ if (ret)
+ return ret;
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to attach to DSI host\n");
+
+ return 0;
+}
+
+static const struct ili9488_desc e35gh_i_mw800cb_desc = {
+ .init_sequence = e35gh_i_mw800cb_init,
+ .display_mode = &e35gh_i_mw800cb_display_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB666_PACKED,
+ .lanes = 1,
+ .bpc = 6,
+};
+
+static const struct of_device_id ili9488_of_match[] = {
+ { .compatible = "focuslcds,e35gh-i-mw800cb", .data = &e35gh_i_mw800cb_desc },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ili9488_of_match);
+
+static struct mipi_dsi_driver ili9488_dsi_driver = {
+ .probe = ili9488_dsi_probe,
+ .driver = {
+ .name = "ili9488-dsi",
+ .of_match_table = ili9488_of_match,
+ },
+};
+module_mipi_dsi_driver(ili9488_dsi_driver);
+
+MODULE_AUTHOR("Igor Reznichenko <igor@reznichenko.net>");
+MODULE_DESCRIPTION("Ilitek ILI9488 Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
index 7e8b5e059575..464d9ce47d87 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
@@ -28,10 +28,6 @@
#include <video/mipi_display.h>
-struct nt36672a_panel_cmd {
- const char data[2];
-};
-
static const char * const nt36672a_regulator_names[] = {
"vddio",
"vddpos",
@@ -55,13 +51,9 @@ struct nt36672a_panel_desc {
enum mipi_dsi_pixel_format format;
unsigned int lanes;
- unsigned int num_on_cmds_1;
- const struct nt36672a_panel_cmd *on_cmds_1;
- unsigned int num_on_cmds_2;
- const struct nt36672a_panel_cmd *on_cmds_2;
-
- unsigned int num_off_cmds;
- const struct nt36672a_panel_cmd *off_cmds;
+ void (*send_init_cmds_1)(struct mipi_dsi_multi_context *dsi_ctx);
+ void (*send_init_cmds_2)(struct mipi_dsi_multi_context *dsi_ctx);
+ void (*send_deinit_cmds)(struct mipi_dsi_multi_context *dsi_ctx);
};
struct nt36672a_panel {
@@ -79,19 +71,6 @@ static inline struct nt36672a_panel *to_nt36672a_panel(struct drm_panel *panel)
return container_of(panel, struct nt36672a_panel, base);
}
-static void nt36672a_send_cmds(struct mipi_dsi_multi_context *dsi_ctx,
- const struct nt36672a_panel_cmd *cmds, int num)
-{
- unsigned int i;
-
- for (i = 0; i < num; i++) {
- const struct nt36672a_panel_cmd *cmd = &cmds[i];
-
- /* cmd->data[0] is the DCS command, cmd->data[1] is the parameter */
- mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd->data, sizeof(cmd->data));
- }
-}
-
static void nt36672a_panel_power_off(struct drm_panel *panel)
{
struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
@@ -110,8 +89,8 @@ static int nt36672a_panel_unprepare(struct drm_panel *panel)
struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link };
/* send off cmds */
- nt36672a_send_cmds(&dsi_ctx, pinfo->desc->off_cmds,
- pinfo->desc->num_off_cmds);
+ if (pinfo->desc->send_deinit_cmds)
+ pinfo->desc->send_deinit_cmds(&dsi_ctx);
/* Reset error to continue with display off even if send_cmds failed */
dsi_ctx.accum_err = 0;
@@ -162,8 +141,8 @@ static int nt36672a_panel_prepare(struct drm_panel *panel)
dsi_ctx.accum_err = nt36672a_panel_power_on(pinfo);
/* send first part of init cmds */
- nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_1,
- pinfo->desc->num_on_cmds_1);
+ if (pinfo->desc->send_init_cmds_1)
+ pinfo->desc->send_init_cmds_1(&dsi_ctx);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
@@ -173,8 +152,8 @@ static int nt36672a_panel_prepare(struct drm_panel *panel)
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
/* Send rest of the init cmds */
- nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_2,
- pinfo->desc->num_on_cmds_2);
+ if (pinfo->desc->send_init_cmds_2)
+ pinfo->desc->send_init_cmds_2(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
@@ -213,310 +192,184 @@ static const struct drm_panel_funcs panel_funcs = {
.get_modes = nt36672a_panel_get_modes,
};
-static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_1[] = {
+static void tianma_fhd_video_send_init_cmds_1(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ u8 reg;
+
/* skin enhancement mode */
- { .data = {0xFF, 0x22} },
- { .data = {0x00, 0x40} },
- { .data = {0x01, 0xC0} },
- { .data = {0x02, 0x40} },
- { .data = {0x03, 0x40} },
- { .data = {0x04, 0x40} },
- { .data = {0x05, 0x40} },
- { .data = {0x06, 0x40} },
- { .data = {0x07, 0x40} },
- { .data = {0x08, 0x40} },
- { .data = {0x09, 0x40} },
- { .data = {0x0A, 0x40} },
- { .data = {0x0B, 0x40} },
- { .data = {0x0C, 0x40} },
- { .data = {0x0D, 0x40} },
- { .data = {0x0E, 0x40} },
- { .data = {0x0F, 0x40} },
- { .data = {0x10, 0x40} },
- { .data = {0x11, 0x50} },
- { .data = {0x12, 0x60} },
- { .data = {0x13, 0x70} },
- { .data = {0x14, 0x58} },
- { .data = {0x15, 0x68} },
- { .data = {0x16, 0x78} },
- { .data = {0x17, 0x77} },
- { .data = {0x18, 0x39} },
- { .data = {0x19, 0x2D} },
- { .data = {0x1A, 0x2E} },
- { .data = {0x1B, 0x32} },
- { .data = {0x1C, 0x37} },
- { .data = {0x1D, 0x3A} },
- { .data = {0x1E, 0x40} },
- { .data = {0x1F, 0x40} },
- { .data = {0x20, 0x40} },
- { .data = {0x21, 0x40} },
- { .data = {0x22, 0x40} },
- { .data = {0x23, 0x40} },
- { .data = {0x24, 0x40} },
- { .data = {0x25, 0x40} },
- { .data = {0x26, 0x40} },
- { .data = {0x27, 0x40} },
- { .data = {0x28, 0x40} },
- { .data = {0x2D, 0x00} },
- { .data = {0x2F, 0x40} },
- { .data = {0x30, 0x40} },
- { .data = {0x31, 0x40} },
- { .data = {0x32, 0x40} },
- { .data = {0x33, 0x40} },
- { .data = {0x34, 0x40} },
- { .data = {0x35, 0x40} },
- { .data = {0x36, 0x40} },
- { .data = {0x37, 0x40} },
- { .data = {0x38, 0x40} },
- { .data = {0x39, 0x40} },
- { .data = {0x3A, 0x40} },
- { .data = {0x3B, 0x40} },
- { .data = {0x3D, 0x40} },
- { .data = {0x3F, 0x40} },
- { .data = {0x40, 0x40} },
- { .data = {0x41, 0x40} },
- { .data = {0x42, 0x40} },
- { .data = {0x43, 0x40} },
- { .data = {0x44, 0x40} },
- { .data = {0x45, 0x40} },
- { .data = {0x46, 0x40} },
- { .data = {0x47, 0x40} },
- { .data = {0x48, 0x40} },
- { .data = {0x49, 0x40} },
- { .data = {0x4A, 0x40} },
- { .data = {0x4B, 0x40} },
- { .data = {0x4C, 0x40} },
- { .data = {0x4D, 0x40} },
- { .data = {0x4E, 0x40} },
- { .data = {0x4F, 0x40} },
- { .data = {0x50, 0x40} },
- { .data = {0x51, 0x40} },
- { .data = {0x52, 0x40} },
- { .data = {0x53, 0x01} },
- { .data = {0x54, 0x01} },
- { .data = {0x55, 0xFE} },
- { .data = {0x56, 0x77} },
- { .data = {0x58, 0xCD} },
- { .data = {0x59, 0xD0} },
- { .data = {0x5A, 0xD0} },
- { .data = {0x5B, 0x50} },
- { .data = {0x5C, 0x50} },
- { .data = {0x5D, 0x50} },
- { .data = {0x5E, 0x50} },
- { .data = {0x5F, 0x50} },
- { .data = {0x60, 0x50} },
- { .data = {0x61, 0x50} },
- { .data = {0x62, 0x50} },
- { .data = {0x63, 0x50} },
- { .data = {0x64, 0x50} },
- { .data = {0x65, 0x50} },
- { .data = {0x66, 0x50} },
- { .data = {0x67, 0x50} },
- { .data = {0x68, 0x50} },
- { .data = {0x69, 0x50} },
- { .data = {0x6A, 0x50} },
- { .data = {0x6B, 0x50} },
- { .data = {0x6C, 0x50} },
- { .data = {0x6D, 0x50} },
- { .data = {0x6E, 0x50} },
- { .data = {0x6F, 0x50} },
- { .data = {0x70, 0x07} },
- { .data = {0x71, 0x00} },
- { .data = {0x72, 0x00} },
- { .data = {0x73, 0x00} },
- { .data = {0x74, 0x06} },
- { .data = {0x75, 0x0C} },
- { .data = {0x76, 0x03} },
- { .data = {0x77, 0x09} },
- { .data = {0x78, 0x0F} },
- { .data = {0x79, 0x68} },
- { .data = {0x7A, 0x88} },
- { .data = {0x7C, 0x80} },
- { .data = {0x7D, 0x80} },
- { .data = {0x7E, 0x80} },
- { .data = {0x7F, 0x00} },
- { .data = {0x80, 0x00} },
- { .data = {0x81, 0x00} },
- { .data = {0x83, 0x01} },
- { .data = {0x84, 0x00} },
- { .data = {0x85, 0x80} },
- { .data = {0x86, 0x80} },
- { .data = {0x87, 0x80} },
- { .data = {0x88, 0x40} },
- { .data = {0x89, 0x91} },
- { .data = {0x8A, 0x98} },
- { .data = {0x8B, 0x80} },
- { .data = {0x8C, 0x80} },
- { .data = {0x8D, 0x80} },
- { .data = {0x8E, 0x80} },
- { .data = {0x8F, 0x80} },
- { .data = {0x90, 0x80} },
- { .data = {0x91, 0x80} },
- { .data = {0x92, 0x80} },
- { .data = {0x93, 0x80} },
- { .data = {0x94, 0x80} },
- { .data = {0x95, 0x80} },
- { .data = {0x96, 0x80} },
- { .data = {0x97, 0x80} },
- { .data = {0x98, 0x80} },
- { .data = {0x99, 0x80} },
- { .data = {0x9A, 0x80} },
- { .data = {0x9B, 0x80} },
- { .data = {0x9C, 0x80} },
- { .data = {0x9D, 0x80} },
- { .data = {0x9E, 0x80} },
- { .data = {0x9F, 0x80} },
- { .data = {0xA0, 0x8A} },
- { .data = {0xA2, 0x80} },
- { .data = {0xA6, 0x80} },
- { .data = {0xA7, 0x80} },
- { .data = {0xA9, 0x80} },
- { .data = {0xAA, 0x80} },
- { .data = {0xAB, 0x80} },
- { .data = {0xAC, 0x80} },
- { .data = {0xAD, 0x80} },
- { .data = {0xAE, 0x80} },
- { .data = {0xAF, 0x80} },
- { .data = {0xB7, 0x76} },
- { .data = {0xB8, 0x76} },
- { .data = {0xB9, 0x05} },
- { .data = {0xBA, 0x0D} },
- { .data = {0xBB, 0x14} },
- { .data = {0xBC, 0x0F} },
- { .data = {0xBD, 0x18} },
- { .data = {0xBE, 0x1F} },
- { .data = {0xBF, 0x05} },
- { .data = {0xC0, 0x0D} },
- { .data = {0xC1, 0x14} },
- { .data = {0xC2, 0x03} },
- { .data = {0xC3, 0x07} },
- { .data = {0xC4, 0x0A} },
- { .data = {0xC5, 0xA0} },
- { .data = {0xC6, 0x55} },
- { .data = {0xC7, 0xFF} },
- { .data = {0xC8, 0x39} },
- { .data = {0xC9, 0x44} },
- { .data = {0xCA, 0x12} },
- { .data = {0xCD, 0x80} },
- { .data = {0xDB, 0x80} },
- { .data = {0xDC, 0x80} },
- { .data = {0xDD, 0x80} },
- { .data = {0xE0, 0x80} },
- { .data = {0xE1, 0x80} },
- { .data = {0xE2, 0x80} },
- { .data = {0xE3, 0x80} },
- { .data = {0xE4, 0x80} },
- { .data = {0xE5, 0x40} },
- { .data = {0xE6, 0x40} },
- { .data = {0xE7, 0x40} },
- { .data = {0xE8, 0x40} },
- { .data = {0xE9, 0x40} },
- { .data = {0xEA, 0x40} },
- { .data = {0xEB, 0x40} },
- { .data = {0xEC, 0x40} },
- { .data = {0xED, 0x40} },
- { .data = {0xEE, 0x40} },
- { .data = {0xEF, 0x40} },
- { .data = {0xF0, 0x40} },
- { .data = {0xF1, 0x40} },
- { .data = {0xF2, 0x40} },
- { .data = {0xF3, 0x40} },
- { .data = {0xF4, 0x40} },
- { .data = {0xF5, 0x40} },
- { .data = {0xF6, 0x40} },
- { .data = {0xFB, 0x1} },
- { .data = {0xFF, 0x23} },
- { .data = {0xFB, 0x01} },
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x22);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x00, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x01, 0xc0);
+ for (reg = 0x02; reg <= 0x10; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x11, 0x50);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x12, 0x60);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x13, 0x70);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x14, 0x58);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x15, 0x68);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x16, 0x78);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x17, 0x77);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x18, 0x39);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x19, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1a, 0x2e);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1b, 0x32);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0x37);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1d, 0x3a);
+ for (reg = 0x1e; reg <= 0x28; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2d, 0x00);
+ for (reg = 0x2f; reg <= 0x3b; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3d, 0x40);
+ for (reg = 0x3f; reg <= 0x52; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x54, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x55, 0xfe);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x56, 0x77);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x58, 0xcd);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x59, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x5a, 0xd0);
+ for (reg = 0x5b; reg <= 0x6f; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x50);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x70, 0x07);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x71, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x72, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x73, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x74, 0x06);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x75, 0x0c);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x76, 0x03);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x77, 0x09);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x78, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x79, 0x68);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7a, 0x88);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7c, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7d, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7e, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x81, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x83, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x84, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x85, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x86, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x87, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x88, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x89, 0x91);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8a, 0x98);
+ for (reg = 0x8b; reg <= 0x9f; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa0, 0x8a);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa2, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa6, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa7, 0x80);
+ for (reg = 0xa9; reg <= 0xaf; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x76);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb8, 0x76);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x05);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x0d);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x14);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x18);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbf, 0x05);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x0d);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x14);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x03);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x07);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc5, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0x55);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc7, 0xff);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x39);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc9, 0x44);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xca, 0x12);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcd, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdb, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdc, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x80);
+ for (reg = 0xe0; reg <= 0xe4; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80);
+ for (reg = 0xe5; reg <= 0xf6; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x23);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
/* dimming enable */
- { .data = {0x01, 0x84} },
- { .data = {0x05, 0x2D} },
- { .data = {0x06, 0x00} },
- /* resolution 1080*2246 */
- { .data = {0x11, 0x01} },
- { .data = {0x12, 0x7B} },
- { .data = {0x15, 0x6F} },
- { .data = {0x16, 0x0B} },
- /* UI mode */
- { .data = {0x29, 0x0A} },
- { .data = {0x30, 0xFF} },
- { .data = {0x31, 0xFF} },
- { .data = {0x32, 0xFF} },
- { .data = {0x33, 0xFF} },
- { .data = {0x34, 0xFF} },
- { .data = {0x35, 0xFF} },
- { .data = {0x36, 0xFF} },
- { .data = {0x37, 0xFF} },
- { .data = {0x38, 0xFC} },
- { .data = {0x39, 0xF8} },
- { .data = {0x3A, 0xF4} },
- { .data = {0x3B, 0xF1} },
- { .data = {0x3D, 0xEE} },
- { .data = {0x3F, 0xEB} },
- { .data = {0x40, 0xE8} },
- { .data = {0x41, 0xE5} },
- /* STILL mode */
- { .data = {0x2A, 0x13} },
- { .data = {0x45, 0xFF} },
- { .data = {0x46, 0xFF} },
- { .data = {0x47, 0xFF} },
- { .data = {0x48, 0xFF} },
- { .data = {0x49, 0xFF} },
- { .data = {0x4A, 0xFF} },
- { .data = {0x4B, 0xFF} },
- { .data = {0x4C, 0xFF} },
- { .data = {0x4D, 0xED} },
- { .data = {0x4E, 0xD5} },
- { .data = {0x4F, 0xBF} },
- { .data = {0x50, 0xA6} },
- { .data = {0x51, 0x96} },
- { .data = {0x52, 0x86} },
- { .data = {0x53, 0x76} },
- { .data = {0x54, 0x66} },
- /* MOVING mode */
- { .data = {0x2B, 0x0E} },
- { .data = {0x58, 0xFF} },
- { .data = {0x59, 0xFF} },
- { .data = {0x5A, 0xFF} },
- { .data = {0x5B, 0xFF} },
- { .data = {0x5C, 0xFF} },
- { .data = {0x5D, 0xFF} },
- { .data = {0x5E, 0xFF} },
- { .data = {0x5F, 0xFF} },
- { .data = {0x60, 0xF6} },
- { .data = {0x61, 0xEA} },
- { .data = {0x62, 0xE1} },
- { .data = {0x63, 0xD8} },
- { .data = {0x64, 0xCE} },
- { .data = {0x65, 0xC3} },
- { .data = {0x66, 0xBA} },
- { .data = {0x67, 0xB3} },
- { .data = {0xFF, 0x25} },
- { .data = {0xFB, 0x01} },
- { .data = {0x05, 0x04} },
- { .data = {0xFF, 0x26} },
- { .data = {0xFB, 0x01} },
- { .data = {0x1C, 0xAF} },
- { .data = {0xFF, 0x10} },
- { .data = {0xFB, 0x01} },
- { .data = {0x51, 0xFF} },
- { .data = {0x53, 0x24} },
- { .data = {0x55, 0x00} },
-};
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x01, 0x84);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x06, 0x00);
+ /* resolution 1080*2246 */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x11, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x12, 0x7b);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x15, 0x6f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x16, 0x0b);
+ /* UI mode */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x29, 0x0a);
+ for (reg = 0x30; reg <= 0x37; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x38, 0xfc);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x39, 0xf8);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3a, 0xf4);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3b, 0xf1);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3d, 0xee);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3f, 0xeb);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x40, 0xe8);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x41, 0xe5);
+ /* STILL mode */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2a, 0x13);
+ for (reg = 0x45; reg <= 0x4c; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4d, 0xed);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4e, 0xd5);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4f, 0xbf);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x50, 0xa6);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x51, 0x96);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x52, 0x86);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x76);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x54, 0x66);
+ /* MOVING mode */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2b, 0x0e);
+ for (reg = 0x58; reg <= 0x5f; reg++)
+ mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x60, 0xf6);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x61, 0xea);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x62, 0xe1);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x63, 0xd8);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x64, 0xce);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x65, 0xc3);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x66, 0xba);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x67, 0xb3);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x25);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x04);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x26);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0xaf);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x51, 0xff);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x24);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x55, 0x00);
+}
-static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_2[] = {
- { .data = {0xFF, 0x24} },
- { .data = {0xFB, 0x01} },
- { .data = {0xC3, 0x01} },
- { .data = {0xC4, 0x54} },
- { .data = {0xFF, 0x10} },
-};
+static void tianma_fhd_video_send_init_cmds_2(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x24);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x54);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+}
-static const struct nt36672a_panel_cmd tianma_fhd_video_off_cmds[] = {
- { .data = {0xFF, 0x24} },
- { .data = {0xFB, 0x01} },
- { .data = {0xC3, 0x01} },
- { .data = {0xFF, 0x10} },
-};
+static void tianma_fhd_video_send_deinit_cmds(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x24);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+}
static const struct drm_display_mode tianma_fhd_video_panel_default_mode = {
.clock = 161331,
@@ -546,12 +399,9 @@ static const struct nt36672a_panel_desc tianma_fhd_video_panel_desc = {
| MIPI_DSI_MODE_VIDEO_BURST,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
- .on_cmds_1 = tianma_fhd_video_on_cmds_1,
- .num_on_cmds_1 = ARRAY_SIZE(tianma_fhd_video_on_cmds_1),
- .on_cmds_2 = tianma_fhd_video_on_cmds_2,
- .num_on_cmds_2 = ARRAY_SIZE(tianma_fhd_video_on_cmds_2),
- .off_cmds = tianma_fhd_video_off_cmds,
- .num_off_cmds = ARRAY_SIZE(tianma_fhd_video_off_cmds),
+ .send_init_cmds_1 = tianma_fhd_video_send_init_cmds_1,
+ .send_init_cmds_2 = tianma_fhd_video_send_init_cmds_2,
+ .send_deinit_cmds = tianma_fhd_video_send_deinit_cmds,
};
static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
diff --git a/drivers/gpu/drm/panel/panel-renesas-r63419.c b/drivers/gpu/drm/panel/panel-renesas-r63419.c
new file mode 100644
index 000000000000..59ac323f5276
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-renesas-r63419.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DRM driver for Renesas R63419 based dual-DSI video mode panels
+ *
+ * Copyright (c) 2025, Kancy Joe <kancy2333@outlook.com>
+ * Copyright (C) 2026 Linaro Limited
+ * Author: Neil Armstrong <neil.armstrong@linaro.org>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct renesas_r63419_panel {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi[2];
+ const struct panel_desc *desc;
+
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *vdd_supplies;
+ struct regulator_bulk_data *vcc_supplies;
+ enum drm_panel_orientation orientation;
+};
+
+/* VDDIO/VDD Supplies */
+static const struct regulator_bulk_data renesas_r63419_vdd_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "vdd" },
+};
+
+/* VSP/VSN/VCI Supplies */
+static const struct regulator_bulk_data renesas_r63419_vcc_supplies[] = {
+ { .supply = "vsp" },
+ { .supply = "vsn" },
+ { .supply = "vci" },
+};
+
+struct panel_desc {
+ const struct drm_display_mode *mode;
+ unsigned int lanes;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ const struct mipi_dsi_device_info dsi_info;
+};
+
+static const struct drm_display_mode wt0600_mode = {
+ /* Dual dsi */
+ .clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000,
+ .hdisplay = 2 * 720,
+ .hsync_start = 2 * (720 + 100),
+ .hsync_end = 2 * (720 + 100 + 8),
+ .htotal = 2 * (720 + 100 + 8 + 40),
+ .vdisplay = 2560,
+ .vsync_start = 2560 + 15,
+ .vsync_end = 2560 + 15 + 2,
+ .vtotal = 2560 + 15 + 2 + 8,
+ .type = DRM_MODE_TYPE_DRIVER,
+ .width_mm = 74,
+ .height_mm = 131,
+};
+
+static const struct drm_display_mode wt0630_mode = {
+ /* Dual dsi */
+ .clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000,
+ .hdisplay = 2 * 720,
+ .hsync_start = 2 * (720 + 100),
+ .hsync_end = 2 * (720 + 100 + 8),
+ .htotal = 2 * (720 + 100 + 8 + 40),
+ .vdisplay = 2560,
+ .vsync_start = 2560 + 15,
+ .vsync_end = 2560 + 15 + 2,
+ .vtotal = 2560 + 15 + 2 + 8,
+ .type = DRM_MODE_TYPE_DRIVER,
+ .width_mm = 78,
+ .height_mm = 140,
+};
+
+static struct panel_desc wt0600_desc = {
+ .lanes = 4,
+ .mode = &wt0600_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
+ .format = MIPI_DSI_FMT_RGB888,
+};
+
+static struct panel_desc wt0630_desc = {
+ .lanes = 4,
+ .mode = &wt0630_mode, /* wt0600 only has different screen size */
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
+ .format = MIPI_DSI_FMT_RGB888,
+};
+
+static inline struct renesas_r63419_panel *
+to_renesas_r63419_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct renesas_r63419_panel, panel);
+}
+
+static int renesas_r63419_on(struct renesas_r63419_panel *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { 0 };
+
+ /*
+ * Panel registers are loaded from DDIC Non Volatile Memory
+ *
+ * The DDIC expects this sequence to get out of sleep and enable display
+ */
+
+ mipi_dsi_dual(mipi_dsi_dcs_set_display_on_multi,
+ &dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
+ mipi_dsi_msleep(&dsi_ctx, 50);
+
+ mipi_dsi_dual(mipi_dsi_dcs_exit_sleep_mode_multi,
+ &dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
+ mipi_dsi_msleep(&dsi_ctx, 150);
+
+ return dsi_ctx.accum_err;
+}
+
+static int renesas_r63419_disable(struct drm_panel *panel)
+{
+ struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { 0 };
+
+ mipi_dsi_dual(mipi_dsi_dcs_set_display_off_multi,
+ &dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
+ mipi_dsi_msleep(&dsi_ctx, 50);
+
+ mipi_dsi_dual(mipi_dsi_dcs_enter_sleep_mode_multi,
+ &dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return 0;
+}
+
+static int renesas_r63419_prepare(struct drm_panel *panel)
+{
+ struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
+ ctx->vdd_supplies);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vcc_supplies),
+ ctx->vcc_supplies);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
+ ctx->vdd_supplies);
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+
+ usleep_range(3000, 4000);
+
+ ret = renesas_r63419_on(ctx);
+ if (ret < 0) {
+ dev_err(panel->dev, "Failed to initialize panel: %d\n", ret);
+
+ /* Power off sequence from the r63419 datasheet */
+ regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
+ ctx->vdd_supplies);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies),
+ ctx->vcc_supplies);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int renesas_r63419_unprepare(struct drm_panel *panel)
+{
+ struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
+
+ /* Power off sequence from the r63419 datasheet */
+ regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies), ctx->vdd_supplies);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies), ctx->vcc_supplies);
+
+ return 0;
+}
+
+static int renesas_r63419_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
+ const struct drm_display_mode *mode = ctx->desc->mode;
+
+ return drm_connector_helper_get_modes_fixed(connector, mode);
+}
+
+static enum drm_panel_orientation
+renesas_r63419_get_orientation(struct drm_panel *panel)
+{
+ struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs renesas_r63419_panel_funcs = {
+ .disable = renesas_r63419_disable,
+ .prepare = renesas_r63419_prepare,
+ .unprepare = renesas_r63419_unprepare,
+ .get_modes = renesas_r63419_get_modes,
+ .get_orientation = renesas_r63419_get_orientation,
+};
+
+static int renesas_r63419_probe(struct mipi_dsi_device *dsi)
+{
+ struct mipi_dsi_device_info info = { 0 };
+ struct device *dev = &dsi->dev;
+ struct renesas_r63419_panel *ctx;
+ struct device_node *dsi1_node;
+ struct mipi_dsi_host *dsi1_host;
+ int ret, i;
+
+ ctx = devm_drm_panel_alloc(dev, struct renesas_r63419_panel, panel,
+ &renesas_r63419_panel_funcs, DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->desc = of_device_get_match_data(dev);
+ if (!ctx->desc)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to get panel description\n");
+
+ ret = devm_regulator_bulk_get_const(&dsi->dev,
+ ARRAY_SIZE(renesas_r63419_vdd_supplies),
+ renesas_r63419_vdd_supplies, &ctx->vdd_supplies);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_regulator_bulk_get_const(&dsi->dev,
+ ARRAY_SIZE(renesas_r63419_vcc_supplies),
+ renesas_r63419_vcc_supplies, &ctx->vcc_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 gpio\n");
+
+ /* Get second DSI host */
+ dsi1_node = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
+ if (!dsi1_node)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to get remote node for second DSI\n");
+
+ dsi1_host = of_find_mipi_dsi_host_by_node(dsi1_node);
+ of_node_put(dsi1_node);
+ if (!dsi1_host)
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "Failed to find second DSI host\n");
+
+ /* Copy current DSI info, do not provide OF node since no driver needs to be attached */
+ strscpy(info.type, dsi->name);
+ info.channel = dsi->channel;
+
+ /* Register the second DSI device */
+ ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host, &info);
+ if (IS_ERR(ctx->dsi[1]))
+ return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]),
+ "Failed to register second DSI device\n");
+
+ ctx->dsi[0] = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ /* Get panel orientation */
+ ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "Failed to get panel orientation\n");
+
+ 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 dev_err_probe(dev, ret, "Failed to add panel\n");
+
+ /* Configure and attach both DSI devices */
+ for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
+ ctx->dsi[i]->lanes = ctx->desc->lanes;
+ ctx->dsi[i]->format = ctx->desc->format;
+ ctx->dsi[i]->mode_flags = ctx->desc->mode_flags;
+
+ ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to attach DSI device %d\n", i);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id renesas_r63419_of_match[] = {
+ {
+ .compatible = "ayaneo,wt0600-2k",
+ .data = &wt0600_desc,
+ },
+ {
+ .compatible = "ayaneo,wt0630-2k",
+ .data = &wt0630_desc,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, renesas_r63419_of_match);
+
+static struct mipi_dsi_driver renesas_r63419_driver = {
+ .probe = renesas_r63419_probe,
+ .driver = {
+ .name = "panel-renesas-r63419",
+ .of_match_table = renesas_r63419_of_match,
+ },
+};
+module_mipi_dsi_driver(renesas_r63419_driver);
+
+MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>");
+MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
+MODULE_DESCRIPTION("DRM driver for Renesas R63419 based dual-DSI video mode panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
index 97a79411e1ec..6733e0e1d061 100644
--- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
+++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
@@ -9,6 +9,7 @@
#include <linux/of.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>
@@ -17,6 +18,7 @@
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;
@@ -47,9 +49,12 @@ static int visionox_vtdr6130_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,
@@ -123,6 +128,9 @@ static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx)
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
+ drm_dsc_pps_payload_pack(&pps, dsi->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+
return dsi_ctx.accum_err;
}
@@ -269,6 +277,16 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
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 |
@@ -280,28 +298,11 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
- drm_panel_add(&ctx->panel);
-
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
return ret;
- }
-
- return 0;
-}
-
-static void visionox_vtdr6130_remove(struct mipi_dsi_device *dsi)
-{
- struct visionox_vtdr6130 *ctx = mipi_dsi_get_drvdata(dsi);
- int ret;
-
- ret = mipi_dsi_detach(dsi);
- if (ret < 0)
- dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
- drm_panel_remove(&ctx->panel);
+ return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id visionox_vtdr6130_of_match[] = {
@@ -312,7 +313,6 @@ MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match);
static struct mipi_dsi_driver visionox_vtdr6130_driver = {
.probe = visionox_vtdr6130_probe,
- .remove = visionox_vtdr6130_remove,
.driver = {
.name = "panel-visionox-vtdr6130",
.of_match_table = visionox_vtdr6130_of_match,
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 98828e81db0b..0fda64fbe5f2 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -161,6 +161,9 @@ struct panthor_device {
/** @csif_info: Command stream interface information. */
struct drm_panthor_csif_info csif_info;
+ /** @mmu_info: MMU info */
+ struct drm_panthor_mmu_info mmu_info;
+
/** @hw: GPU-specific data. */
struct panthor_hw *hw;
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index e8dc4096c1d2..106da676fd2e 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -175,6 +175,7 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride,
_Generic(_obj_name, \
PANTHOR_UOBJ_DECL(struct drm_panthor_gpu_info, tiler_present), \
PANTHOR_UOBJ_DECL(struct drm_panthor_csif_info, pad), \
+ PANTHOR_UOBJ_DECL(struct drm_panthor_mmu_info, page_size_bitmap), \
PANTHOR_UOBJ_DECL(struct drm_panthor_timestamp_info, current_timestamp), \
PANTHOR_UOBJ_DECL(struct drm_panthor_group_priorities_info, pad), \
PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \
@@ -954,6 +955,10 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
args->size = sizeof(priorities_info);
return 0;
+ case DRM_PANTHOR_DEV_QUERY_MMU_INFO:
+ args->size = sizeof(ptdev->mmu_info);
+ return 0;
+
default:
return -EINVAL;
}
@@ -984,6 +989,9 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
panthor_query_group_priorities_info(file, &priorities_info);
return PANTHOR_UOBJ_SET(args->pointer, args->size, priorities_info);
+ case DRM_PANTHOR_DEV_QUERY_MMU_INFO:
+ return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->mmu_info);
+
default:
return -EINVAL;
}
@@ -1779,10 +1787,12 @@ static void panthor_debugfs_init(struct drm_minor *minor)
* - adds DRM_IOCTL_PANTHOR_BO_QUERY_INFO ioctl
* - adds drm_panthor_gpu_info::selected_coherency
* - 1.8 - extends DEV_QUERY_TIMESTAMP_INFO with flags
+ * - 1.9 - adds DRM_PANTHOR_DEV_QUERY_MMU_INFO query
+ * - adds DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE flag
*/
static const struct drm_driver panthor_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
- DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA,
+ DRIVER_SYNCOBJ_TIMELINE,
.open = panthor_open,
.postclose = panthor_postclose,
.show_fdinfo = panthor_show_fdinfo,
@@ -1792,7 +1802,7 @@ static const struct drm_driver panthor_drm_driver = {
.name = "panthor",
.desc = "Panthor DRM driver",
.major = 1,
- .minor = 8,
+ .minor = 9,
.gem_prime_import_sg_table = panthor_gem_prime_import_sg_table,
.gem_prime_import = panthor_gem_prime_import,
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index a1e2eb1ca7bb..9855df738194 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -1347,6 +1347,24 @@ err_free_kbo:
return ERR_PTR(ret);
}
+/**
+ * panthor_dummy_bo_create() - Create a Panthor BO meant to back sparse bindings.
+ * @ptdev: Device.
+ *
+ * Return: A valid pointer in case of success, an ERR_PTR() otherwise.
+ */
+struct panthor_gem_object *
+panthor_dummy_bo_create(struct panthor_device *ptdev)
+{
+ /* Since even when the DRM device's mount point has enabled THP we have no guarantee
+ * that drm_gem_get_pages() will return a single 2MiB PMD, and also we cannot be sure
+ * that the 2MiB won't be reclaimed and re-allocated later on as 4KiB chunks, it doesn't
+ * make sense to pre-populate this object's page array, nor to fall back on a BO size
+ * of 4KiB. Sticking to a dummy object size of 2MiB lets us keep things simple for now.
+ */
+ return panthor_gem_create(&ptdev->base, SZ_2M, DRM_PANTHOR_BO_NO_MMAP, NULL, 0);
+}
+
static bool can_swap(void)
{
return get_nr_swap_pages() > 0;
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 56d63137b4eb..5ae37d0d3646 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -325,6 +325,8 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo);
+struct panthor_gem_object *panthor_dummy_bo_create(struct panthor_device *ptdev);
+
#ifdef CONFIG_DEBUG_FS
void panthor_gem_debugfs_init(struct drm_minor *minor);
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index e592a8ebb478..f45ef5824ff2 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -116,6 +116,17 @@ struct panthor_mmu {
struct panthor_vm_pool {
/** @xa: Array used for VM handle tracking. */
struct xarray xa;
+
+ /**
+ * @dummy: Dummy object used for sparse mappings
+ *
+ * Sparse bindings map virtual address ranges onto a dummy
+ * BO in a modulo fashion. Even though sparse writes are meant
+ * to be discarded and reads undefined, writes are still reflected
+ * in the dummy buffer. That means we must keep a dummy object per
+ * file context, to avoid data leaks between them.
+ */
+ struct panthor_gem_object *dummy;
};
/**
@@ -199,14 +210,6 @@ struct panthor_vm_op_ctx {
/** @map.bo_offset: Offset in the buffer object. */
u64 bo_offset;
- /**
- * @map.sgt: sg-table pointing to pages backing the GEM object.
- *
- * This is gathered at job creation time, such that we don't have
- * to allocate in ::run_job().
- */
- struct sg_table *sgt;
-
/** @map.bo: the BO being mapped. */
struct panthor_gem_object *bo;
} map;
@@ -403,6 +406,15 @@ struct panthor_vm {
*/
struct list_head lru_node;
} reclaim;
+
+ /**
+ * @dummy: Dummy object used for sparse mappings.
+ *
+ * VM's must keep a reference to the file context-wide dummy BO because
+ * they can outlive the file context, which includes the VM pool holding
+ * the original dummy BO reference.
+ */
+ struct panthor_gem_object *dummy;
};
/**
@@ -1035,6 +1047,30 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot,
return 0;
}
+static int
+panthor_vm_map_sparse(struct panthor_vm *vm, u64 iova, int prot,
+ struct sg_table *sgt, u64 size)
+{
+ u64 mapped = 0;
+ int ret;
+
+ while (mapped < size) {
+ u64 addr = iova + mapped;
+ u32 chunk_size = min(size - mapped, SZ_2M - (addr & (SZ_2M - 1)));
+
+ ret = panthor_vm_map_pages(vm, addr, prot, sgt,
+ addr % SZ_2M, chunk_size);
+ if (ret) {
+ panthor_vm_unmap_pages(vm, iova, mapped);
+ return ret;
+ }
+
+ mapped += chunk_size;
+ }
+
+ return 0;
+}
+
static int flags_to_prot(u32 flags)
{
int prot = 0;
@@ -1277,15 +1313,15 @@ static int panthor_vm_op_ctx_prealloc_pts(struct panthor_vm_op_ctx *op_ctx)
(DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \
DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \
+ DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE | \
DRM_PANTHOR_VM_BIND_OP_TYPE_MASK)
static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
struct panthor_vm *vm,
struct panthor_gem_object *bo,
- u64 offset,
- u64 size, u64 va,
- u32 flags)
+ const struct drm_panthor_vm_bind_op *op)
{
+ bool is_sparse = op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE;
struct drm_gpuvm_bo *preallocated_vm_bo;
struct sg_table *sgt = NULL;
int ret;
@@ -1293,12 +1329,25 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
if (!bo)
return -EINVAL;
- if ((flags & ~PANTHOR_VM_BIND_OP_MAP_FLAGS) ||
- (flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) != DRM_PANTHOR_VM_BIND_OP_TYPE_MAP)
+ if ((op->flags & ~PANTHOR_VM_BIND_OP_MAP_FLAGS) ||
+ (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) != DRM_PANTHOR_VM_BIND_OP_TYPE_MAP)
return -EINVAL;
- /* Make sure the VA and size are in-bounds. */
- if (size > bo->base.size || offset > bo->base.size - size)
+ /* uAPI mandates sparsely bound regions must not be executable. */
+ if (is_sparse && !(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC))
+ return -EINVAL;
+
+ /* For non-sparse, make sure the VA and size are in-bounds.
+ * For sparse, this is not applicable, because the dummy BO is
+ * repeatedly mapped over a potentially wider VA range.
+ */
+ if (!is_sparse && (op->size > bo->base.size || op->bo_offset > bo->base.size - op->size))
+ return -EINVAL;
+
+ /* For sparse, we don't expect any user BO, the BO we get passed
+ * is the dummy BO attached to the VM pool.
+ */
+ if (is_sparse && (op->bo_handle || op->bo_offset))
return -EINVAL;
/* If the BO has an exclusive VM attached, it can't be mapped to other VMs. */
@@ -1306,7 +1355,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
bo->exclusive_vm_root_gem != panthor_vm_root_gem(vm))
return -EINVAL;
- panthor_vm_init_op_ctx(op_ctx, size, va, flags);
+ panthor_vm_init_op_ctx(op_ctx, op->size, op->va, op->flags);
ret = panthor_vm_op_ctx_prealloc_vmas(op_ctx);
if (ret)
@@ -1335,7 +1384,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
}
op_ctx->map.vm_bo = drm_gpuvm_bo_obtain_prealloc(preallocated_vm_bo);
- op_ctx->map.bo_offset = offset;
+ op_ctx->map.bo_offset = op->bo_offset;
ret = panthor_vm_op_ctx_prealloc_pts(op_ctx);
if (ret)
@@ -1446,7 +1495,9 @@ panthor_vm_get_bo_for_va(struct panthor_vm *vm, u64 va, u64 *bo_offset)
if (vma && vma->base.gem.obj) {
drm_gem_object_get(vma->base.gem.obj);
bo = to_panthor_bo(vma->base.gem.obj);
- *bo_offset = vma->base.gem.offset + (va - vma->base.va.addr);
+ *bo_offset = !(vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) ?
+ vma->base.gem.offset + (va - vma->base.va.addr) :
+ va & (SZ_2M - 1);
}
mutex_unlock(&vm->op_lock);
@@ -1551,6 +1602,9 @@ int panthor_vm_pool_create_vm(struct panthor_device *ptdev,
if (IS_ERR(vm))
return PTR_ERR(vm);
+ drm_gem_object_get(&pool->dummy->base);
+ vm->dummy = pool->dummy;
+
ret = xa_alloc(&pool->xa, &id, vm,
XA_LIMIT(1, PANTHOR_MAX_VMS_PER_FILE), GFP_KERNEL);
@@ -1650,6 +1704,8 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile)
xa_for_each(&pfile->vms->xa, i, vm)
panthor_vm_destroy(vm);
+ if (pfile->vms->dummy)
+ drm_gem_object_put(&pfile->vms->dummy->base);
xa_destroy(&pfile->vms->xa);
kfree(pfile->vms);
}
@@ -1662,12 +1718,28 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile)
*/
int panthor_vm_pool_create(struct panthor_file *pfile)
{
+ struct panthor_gem_object *dummy;
+ int ret;
+
pfile->vms = kzalloc_obj(*pfile->vms);
if (!pfile->vms)
return -ENOMEM;
xa_init_flags(&pfile->vms->xa, XA_FLAGS_ALLOC1);
+
+ dummy = panthor_dummy_bo_create(pfile->ptdev);
+ if (IS_ERR(dummy)) {
+ ret = PTR_ERR(dummy);
+ goto err_destroy_vm_pool;
+ }
+
+ pfile->vms->dummy = dummy;
+
return 0;
+
+err_destroy_vm_pool:
+ panthor_vm_pool_destroy(pfile);
+ return ret;
}
/* dummy TLB ops, the real TLB flush happens in panthor_vm_flush_range() */
@@ -2004,6 +2076,9 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm)
free_io_pgtable_ops(vm->pgtbl_ops);
+ if (vm->dummy)
+ drm_gem_object_put(&vm->dummy->base);
+
drm_mm_takedown(&vm->mm);
kfree(vm);
}
@@ -2163,7 +2238,30 @@ static void panthor_vma_init(struct panthor_vma *vma, u32 flags)
#define PANTHOR_VM_MAP_FLAGS \
(DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \
- DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED)
+ DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \
+ DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)
+
+static void
+panthor_fix_sparse_map_offset(struct drm_gpuva_op_map *op, u32 flags)
+{
+ if (op && (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE))
+ op->gem.offset = op->va.addr & (SZ_2M - 1);
+}
+
+static int
+panthor_vm_exec_map_op(struct panthor_vm *vm, u32 flags,
+ const struct drm_gpuva_op_map *op)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(op->gem.obj);
+ int prot = flags_to_prot(flags);
+
+ if (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)
+ return panthor_vm_map_sparse(vm, op->va.addr, prot,
+ bo->dmap.sgt, op->va.range);
+
+ return panthor_vm_map_pages(vm, op->va.addr, prot, bo->dmap.sgt,
+ op->gem.offset, op->va.range);
+}
static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
{
@@ -2176,10 +2274,9 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
return -EINVAL;
panthor_vma_init(vma, op_ctx->flags & PANTHOR_VM_MAP_FLAGS);
+ panthor_fix_sparse_map_offset(&op->map, vma->flags);
- ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags),
- op_ctx->map.bo->dmap.sgt, op->map.gem.offset,
- op->map.va.range);
+ ret = panthor_vm_exec_map_op(vm, vma->flags, &op->map);
if (ret) {
panthor_vm_op_ctx_return_vma(op_ctx, vma);
return ret;
@@ -2211,6 +2308,8 @@ static void
unmap_hugepage_align(const struct drm_gpuva_op_remap *op,
u64 *unmap_start, u64 *unmap_range)
{
+ struct panthor_vma *unmap_vma = container_of(op->unmap->va, struct panthor_vma, base);
+ bool is_sparse = unmap_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE;
u64 aligned_unmap_start, aligned_unmap_end, unmap_end;
unmap_end = *unmap_start + *unmap_range;
@@ -2218,11 +2317,15 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op,
aligned_unmap_end = ALIGN(unmap_end, SZ_2M);
/* If we're dealing with a huge page, make sure the unmap region is
- * aligned on the start of the page.
+ * aligned on the start of the page. If the unmapped VMA stands for
+ * a sparse mapping, always assume the backing storage is a THP, since
+ * the overhead of unmapping 2MiB worth of 4KiB pages and remapping
+ * some of them is offset by the logic of working out whether it's
+ * the opposite case right below. This also holds true for op->next.
*/
if (op->prev && aligned_unmap_start < *unmap_start &&
op->prev->va.addr <= aligned_unmap_start &&
- iova_mapped_as_huge_page(op->prev, *unmap_start)) {
+ (is_sparse || iova_mapped_as_huge_page(op->prev, *unmap_start))) {
*unmap_range += *unmap_start - aligned_unmap_start;
*unmap_start = aligned_unmap_start;
}
@@ -2232,7 +2335,7 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op,
*/
if (op->next && aligned_unmap_end > unmap_end &&
op->next->va.addr + op->next->va.range >= aligned_unmap_end &&
- iova_mapped_as_huge_page(op->next, unmap_end - 1)) {
+ (is_sparse || iova_mapped_as_huge_page(op->next, unmap_end - 1))) {
*unmap_range += aligned_unmap_end - unmap_end;
}
}
@@ -2249,33 +2352,43 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range);
- /*
- * ARM IOMMU page table management code disallows partial unmaps of huge pages,
- * so when a partial unmap is requested, we must first unmap the entire huge
- * page and then remap the difference between the huge page minus the requested
- * unmap region. Calculating the right start address and range for the expanded
- * unmap operation is the responsibility of the following function.
+ /* op->remap.prev's BO offset is always the same as the unmap va's, but
+ * that of op->remap.next must be adjusted so as to remain < SZ_2M
*/
- unmap_hugepage_align(&op->remap, &unmap_start, &unmap_range);
+ panthor_fix_sparse_map_offset(op->remap.next, unmap_vma->flags);
- /* If the range changed, we might have to lock a wider region to guarantee
- * atomicity. panthor_vm_lock_region() bails out early if the new region
- * is already part of the locked region, so no need to do this check here.
- */
if (!unmap_vma->evicted) {
+ /*
+ * ARM IOMMU page table management code disallows partial unmaps of huge pages,
+ * so when a partial unmap is requested, we must first unmap the entire huge
+ * page and then remap the difference between the huge page minus the requested
+ * unmap region. Calculating the right start address and range for the expanded
+ * unmap operation is the responsibility of the following function.
+ */
+ unmap_hugepage_align(&op->remap, &unmap_start, &unmap_range);
+
+ /* If the range changed, we might have to lock a wider region to guarantee
+ * atomicity. panthor_vm_lock_region() bails out early if the new region
+ * is already part of the locked region, so no need to do this check here.
+ */
panthor_vm_lock_region(vm, unmap_start, unmap_range);
panthor_vm_unmap_pages(vm, unmap_start, unmap_range);
}
if (op->remap.prev) {
- struct panthor_gem_object *bo = to_panthor_bo(op->remap.prev->gem.obj);
u64 offset = op->remap.prev->gem.offset + unmap_start - op->remap.prev->va.addr;
u64 size = op->remap.prev->va.addr + op->remap.prev->va.range - unmap_start;
- if (!unmap_vma->evicted) {
- ret = panthor_vm_map_pages(vm, unmap_start,
- flags_to_prot(unmap_vma->flags),
- bo->dmap.sgt, offset, size);
+ if (!unmap_vma->evicted && size > 0) {
+ struct drm_gpuva_op_map map_op = {
+ .va.addr = unmap_start,
+ .va.range = size,
+ .gem.obj = op->remap.prev->gem.obj,
+ .gem.offset = offset,
+ };
+ panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags);
+
+ ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op);
if (ret)
return ret;
}
@@ -2286,14 +2399,19 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
}
if (op->remap.next) {
- struct panthor_gem_object *bo = to_panthor_bo(op->remap.next->gem.obj);
u64 addr = op->remap.next->va.addr;
u64 size = unmap_start + unmap_range - op->remap.next->va.addr;
- if (!unmap_vma->evicted) {
- ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags),
- bo->dmap.sgt, op->remap.next->gem.offset,
- size);
+ if (!unmap_vma->evicted && size > 0) {
+ struct drm_gpuva_op_map map_op = {
+ .va.addr = addr,
+ .va.range = size,
+ .gem.obj = op->remap.next->gem.obj,
+ .gem.offset = op->remap.next->gem.offset,
+ };
+ panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags);
+
+ ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op);
if (ret)
return ret;
}
@@ -2490,11 +2608,17 @@ static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo,
ret = panthor_vm_lock_region(vm, evicted_vma->base.va.addr,
evicted_vma->base.va.range);
if (!ret) {
- ret = panthor_vm_map_pages(vm, evicted_vma->base.va.addr,
- flags_to_prot(evicted_vma->flags),
- bo->dmap.sgt,
- evicted_vma->base.gem.offset,
- evicted_vma->base.va.range);
+ struct drm_gpuva_op_map map_op = {
+ .va.addr = evicted_vma->base.va.addr,
+ .va.range = evicted_vma->base.va.range,
+ .gem.obj = &bo->base,
+ .gem.offset = evicted_vma->base.gem.offset,
+ };
+ if (evicted_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)
+ drm_WARN_ON_ONCE(&vm->ptdev->base, map_op.gem.offset !=
+ (map_op.va.addr & (SZ_2M - 1)));
+
+ ret = panthor_vm_exec_map_op(vm, evicted_vma->flags, &map_op);
if (!ret)
evicted_vma->evicted = false;
@@ -2781,7 +2905,7 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
refcount_set(&vm->as.active_cnt, 0);
pgtbl_cfg = (struct io_pgtable_cfg) {
- .pgsize_bitmap = SZ_4K | SZ_2M,
+ .pgsize_bitmap = ptdev->mmu_info.page_size_bitmap,
.ias = va_bits,
.oas = pa_bits,
.coherent_walk = ptdev->coherent,
@@ -2858,13 +2982,16 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file,
switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) {
case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP:
- gem = drm_gem_object_lookup(file, op->bo_handle);
+ if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) {
+ gem = drm_gem_object_lookup(file, op->bo_handle);
+ } else {
+ gem = &vm->dummy->base;
+ drm_gem_object_get(&vm->dummy->base);
+ }
+
ret = panthor_vm_prepare_map_op_ctx(op_ctx, vm,
gem ? to_panthor_bo(gem) : NULL,
- op->bo_offset,
- op->size,
- op->va,
- op->flags);
+ op);
drm_gem_object_put(gem);
return ret;
@@ -3060,10 +3187,19 @@ int panthor_vm_bind_exec_sync_op(struct drm_file *file,
int panthor_vm_map_bo_range(struct panthor_vm *vm, struct panthor_gem_object *bo,
u64 offset, u64 size, u64 va, u32 flags)
{
+ struct drm_panthor_vm_bind_op op = {
+ .bo_offset = offset,
+ .size = size,
+ .va = va,
+ .flags = flags,
+ };
struct panthor_vm_op_ctx op_ctx;
int ret;
- ret = panthor_vm_prepare_map_op_ctx(&op_ctx, vm, bo, offset, size, va, flags);
+ if (drm_WARN_ON(&vm->ptdev->base, flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE))
+ return -EINVAL;
+
+ ret = panthor_vm_prepare_map_op_ctx(&op_ctx, vm, bo, &op);
if (ret)
return ret;
@@ -3227,6 +3363,11 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res)
destroy_workqueue(res);
}
+static void panthor_mmu_info_init(struct panthor_device *ptdev)
+{
+ ptdev->mmu_info.page_size_bitmap = SZ_4K | SZ_2M;
+}
+
/**
* panthor_mmu_init() - Initialize the MMU logic.
* @ptdev: Device.
@@ -3239,6 +3380,8 @@ int panthor_mmu_init(struct panthor_device *ptdev)
struct panthor_mmu *mmu;
int ret, irq;
+ panthor_mmu_info_init(ptdev);
+
mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL);
if (!mmu)
return -ENOMEM;
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 1e6a2392d7c6..97df9d3d6957 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -50,11 +50,16 @@
#include "qxl_object.h"
static const struct pci_device_id pciidlist[] = {
- { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8,
- 0xffff00, 0 },
- { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_OTHER << 8,
- 0xffff00, 0 },
- { 0, 0, 0 },
+ {
+ PCI_DEVICE(0x1b36, 0x0100),
+ .class = PCI_CLASS_DISPLAY_VGA << 8,
+ .class_mask = 0xffff00
+ }, {
+ PCI_DEVICE(0x1b36, 0x0100),
+ .class = PCI_CLASS_DISPLAY_OTHER << 8,
+ .class_mask = 0xffff00
+ },
+ { },
};
MODULE_DEVICE_TABLE(pci, pciidlist);
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
index 031d07f4508e..d9926c24984e 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
@@ -137,7 +137,7 @@ static const struct rcar_du_device_info rcar_du_r8a774a1_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(1),
+ .dpll_mask = BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a774b1_info = {
@@ -168,7 +168,7 @@ static const struct rcar_du_device_info rcar_du_r8a774b1_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(1),
+ .dpll_mask = BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
@@ -196,7 +196,7 @@ static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
},
.num_lvds = 2,
.num_rpf = 4,
- .lvds_clk_mask = BIT(1) | BIT(0),
+ .lvds_clk_mask = BIT(1) | BIT(0),
};
static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
@@ -227,7 +227,7 @@ static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(1),
+ .dpll_mask = BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a7779_info = {
@@ -385,7 +385,7 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(2) | BIT(1),
+ .dpll_mask = BIT(2) | BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
@@ -416,7 +416,7 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(1),
+ .dpll_mask = BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a77965_info = {
@@ -447,7 +447,7 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = {
},
.num_lvds = 1,
.num_rpf = 5,
- .dpll_mask = BIT(1),
+ .dpll_mask = BIT(1),
};
static const struct rcar_du_device_info rcar_du_r8a77970_info = {
@@ -502,7 +502,7 @@ static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
},
.num_lvds = 2,
.num_rpf = 5,
- .lvds_clk_mask = BIT(1) | BIT(0),
+ .lvds_clk_mask = BIT(1) | BIT(0),
};
static const struct rcar_du_device_info rcar_du_r8a779a0_info = {
@@ -523,7 +523,7 @@ static const struct rcar_du_device_info rcar_du_r8a779a0_info = {
},
},
.num_rpf = 5,
- .dsi_clk_mask = BIT(1) | BIT(0),
+ .dsi_clk_mask = BIT(1) | BIT(0),
};
static const struct rcar_du_device_info rcar_du_r8a779g0_info = {
@@ -544,7 +544,7 @@ static const struct rcar_du_device_info rcar_du_r8a779g0_info = {
},
},
.num_rpf = 5,
- .dsi_clk_mask = BIT(1) | BIT(0),
+ .dsi_clk_mask = BIT(1) | BIT(0),
};
static const struct rcar_du_device_info rcar_du_r8a779h0_info = {
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
index 154410745a74..e433ce61d431 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
@@ -658,7 +658,7 @@ static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
.destroy = rcar_lvds_destroy,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = rcar_lvds_atomic_enable,
.atomic_disable = rcar_lvds_atomic_disable,
.mode_fixup = rcar_lvds_mode_fixup,
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
index aaafee1c060b..6e46d6d99f3c 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
@@ -924,7 +924,7 @@ static const struct drm_bridge_funcs rcar_mipi_dsi_bridge_ops = {
.attach = rcar_mipi_dsi_attach,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = rcar_mipi_dsi_atomic_enable,
.atomic_disable = rcar_mipi_dsi_atomic_disable,
.mode_valid = rcar_mipi_dsi_bridge_mode_valid,
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index a2c864860075..0590ade96b91 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
@@ -1109,7 +1109,7 @@ static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = {
.attach = rzg2l_mipi_dsi_attach,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = rzg2l_mipi_dsi_atomic_pre_enable,
.atomic_enable = rzg2l_mipi_dsi_atomic_enable,
.atomic_disable = rzg2l_mipi_dsi_atomic_disable,
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 06072efd7fca..776954c7d052 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -8,6 +8,7 @@
* Jeff Chen <jeff.chen@rock-chips.com>
*/
+#include <linux/cleanup.h>
#include <linux/component.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
@@ -206,7 +207,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
struct of_endpoint endpoint;
- struct device_node *remote_port, *remote_port_parent;
char name[32];
u32 port_id;
int ret;
@@ -230,18 +230,22 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
if (ret < 0)
return;
- remote_port_parent = of_graph_get_remote_port_parent(endpoint.local_node);
+ struct device_node *remote_port_parent __free(device_node) =
+ of_graph_get_remote_port_parent(endpoint.local_node);
if (remote_port_parent) {
- if (of_get_child_by_name(remote_port_parent, "ports")) {
- remote_port = of_graph_get_remote_port(endpoint.local_node);
+ struct device_node *ports __free(device_node) =
+ of_get_child_by_name(remote_port_parent, "ports");
+
+ if (ports) {
+ struct device_node *remote_port __free(device_node) =
+ of_graph_get_remote_port(endpoint.local_node);
+
of_property_read_u32(remote_port, "reg", &port_id);
- of_node_put(remote_port);
sprintf(name, "%s vp%d", remote_port_parent->full_name, port_id);
} else {
sprintf(name, "%s %s",
remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb");
}
- of_node_put(remote_port_parent);
DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
}
@@ -311,6 +315,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
{
struct device *dev = dp->dev;
struct device_node *np = dev->of_node;
+ struct clk *clk;
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(dp->grf))
@@ -327,6 +332,11 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
return dev_err_probe(dev, PTR_ERR(dp->pclk),
"failed to get pclk property\n");
+ clk = devm_clk_get_optional_enabled(dev, "hclk");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "failed to get hclk property\n");
+
dp->rst = devm_reset_control_get(dev, "dp");
if (IS_ERR(dp->rst))
return dev_err_probe(dev, PTR_ERR(dp->rst),
@@ -516,6 +526,14 @@ static const struct rockchip_dp_chip_data rk3288_dp[] = {
{ /* sentinel */ }
};
+static const struct rockchip_dp_chip_data rk3576_edp[] = {
+ {
+ .chip_type = RK3576_EDP,
+ .reg = 0x27dc0000,
+ },
+ { /* sentinel */ }
+};
+
static const struct rockchip_dp_chip_data rk3588_edp[] = {
{
.edp_mode = GRF_REG_FIELD(0x0000, 0, 0),
@@ -533,6 +551,7 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = {
static const struct of_device_id rockchip_dp_dt_ids[] = {
{.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp },
{.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp },
+ {.compatible = "rockchip,rk3576-edp", .data = &rk3576_edp },
{.compatible = "rockchip,rk3588-edp", .data = &rk3588_edp },
{}
};
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 190cce9530c8..e159e1627f3c 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -825,7 +825,7 @@ out:
static const struct drm_bridge_funcs cdn_dp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.detect = cdn_dp_bridge_detect,
.edid_read = cdn_dp_bridge_edid_read,
.atomic_enable = cdn_dp_bridge_atomic_enable,
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index 32bc73a1d5e4..0de822360c8d 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -107,15 +107,26 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
return PTR_ERR(dp->base);
connector = drm_bridge_connector_init(drm_dev, encoder);
- if (IS_ERR(connector))
+ if (IS_ERR(connector)) {
+ dw_dp_unbind(dp->base);
return dev_err_probe(dev, PTR_ERR(connector),
- "Failed to init bridge connector");
+ "Failed to init bridge connector\n");
+ }
return 0;
}
+static void dw_dp_rockchip_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
+
+ dw_dp_unbind(dp->base);
+}
+
static const struct component_ops dw_dp_rockchip_component_ops = {
.bind = dw_dp_rockchip_bind,
+ .unbind = dw_dp_rockchip_unbind,
};
static int dw_dp_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 7abb42e486c0..cffc0af5190f 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/hw_bitfield.h>
+#include <linux/media-bus-format.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -14,6 +15,7 @@
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -30,8 +32,8 @@
#define RK3288_GRF_SOC_CON6 0x025C
#define RK3288_HDMI_LCDC_SEL BIT(4)
-#define RK3328_GRF_SOC_CON2 0x0408
+#define RK3328_GRF_SOC_CON2 0x0408
#define RK3328_HDMI_SDAIN_MSK BIT(11)
#define RK3328_HDMI_SCLIN_MSK BIT(10)
#define RK3328_HDMI_HPD_IOE BIT(2)
@@ -78,6 +80,7 @@ struct rockchip_hdmi {
struct clk *hdmiphy_clk;
struct clk *ref_clk;
struct clk *grf_clk;
+ struct drm_bridge *bridge;
struct dw_hdmi *hdmi;
struct phy *phy;
};
@@ -188,48 +191,13 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
/*pixelclk symbol term vlev*/
- { 74250000, 0x8009, 0x0004, 0x0272},
- { 165000000, 0x802b, 0x0004, 0x0209},
- { 297000000, 0x8039, 0x0005, 0x028d},
- { 594000000, 0x8039, 0x0000, 0x019d},
- { ~0UL, 0x0000, 0x0000, 0x0000}
+ { 74250000, 0x8009, 0x0004, 0x0272 },
+ { 165000000, 0x802b, 0x0004, 0x0209 },
+ { 297000000, 0x8039, 0x0005, 0x028d },
+ { 594000000, 0x8039, 0x0000, 0x019d },
+ { ~0UL, 0x0000, 0x0000, 0x0000 },
};
-static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
-{
- struct device_node *np = hdmi->dev->of_node;
- int ret;
-
- hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(hdmi->regmap)) {
- dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
- return PTR_ERR(hdmi->regmap);
- }
-
- hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
- if (!hdmi->ref_clk)
- hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "vpll");
-
- if (IS_ERR(hdmi->ref_clk)) {
- ret = PTR_ERR(hdmi->ref_clk);
- return dev_err_probe(hdmi->dev, ret, "failed to get reference clock\n");
- }
-
- hdmi->grf_clk = devm_clk_get_optional(hdmi->dev, "grf");
- if (IS_ERR(hdmi->grf_clk)) {
- ret = PTR_ERR(hdmi->grf_clk);
- return dev_err_probe(hdmi->dev, ret, "failed to get grf clock\n");
- }
-
- ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
- if (ret)
- return ret;
-
- ret = devm_regulator_get_enable(hdmi->dev, "avdd-1v8");
-
- return ret;
-}
-
static enum drm_mode_status
dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
const struct drm_display_info *info,
@@ -259,23 +227,22 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
return MODE_OK;
}
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
+static void
+dw_hdmi_rockchip_encoder_atomic_mode_set(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
-}
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
-static bool
-dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- return true;
-}
+ if (hdmi->phy && conn_state->hdmi.tmds_char_rate) {
+ union phy_configure_opts opts = {};
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ opts.hdmi.bpc = conn_state->hdmi.output_bpc;
+ opts.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+
+ phy_configure(hdmi->phy, &opts);
+ }
clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
}
@@ -309,24 +276,71 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
dev_dbg(hdmi->dev, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
}
+static u32 dw_hdmi_rockchip_get_bus_format(struct drm_encoder *encoder,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
+ struct drm_bridge_state *bridge_state;
+
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ if (!bridge)
+ return 0;
+
+ bridge_state = drm_atomic_get_bridge_state(conn_state->state, bridge);
+ if (!bridge_state)
+ return 0;
+
+ if (bridge_state->input_bus_cfg.format != MEDIA_BUS_FMT_FIXED)
+ return bridge_state->input_bus_cfg.format;
+
+ return bridge_state->output_bus_cfg.format;
+}
+
static int
dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ union phy_configure_opts opts = {};
+ u32 bus_format;
+
+ bus_format = dw_hdmi_rockchip_get_bus_format(encoder, conn_state);
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_FIXED:
+ bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ fallthrough;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+ break;
+ default:
+ return -EINVAL;
+ }
- s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+ s->bus_format = bus_format;
- return 0;
+ if (!hdmi->phy || !conn_state->hdmi.tmds_char_rate)
+ return 0;
+
+ opts.hdmi.bpc = conn_state->hdmi.output_bpc;
+ opts.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+
+ return phy_validate(hdmi->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS, &opts);
}
static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
- .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
- .mode_set = dw_hdmi_rockchip_encoder_mode_set,
- .enable = dw_hdmi_rockchip_encoder_enable,
- .disable = dw_hdmi_rockchip_encoder_disable,
+ .atomic_mode_set = dw_hdmi_rockchip_encoder_atomic_mode_set,
+ .enable = dw_hdmi_rockchip_encoder_enable,
.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
};
@@ -508,6 +522,7 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
.phy_config = rockchip_phy_config,
.phy_data = &rk3568_chip_data,
.use_drm_infoframe = true,
+ .output_port = 1,
};
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
@@ -538,8 +553,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
{
struct platform_device *pdev = to_platform_device(dev);
struct device_node *np = dev_of_node(dev);
+ const struct dw_hdmi_plat_data *drv_data;
struct dw_hdmi_plat_data *plat_data;
- const struct of_device_id *match;
struct drm_device *drm = data;
struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi;
@@ -548,17 +563,20 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
if (!np)
return -ENODEV;
- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ drv_data = of_device_get_match_data(dev);
+ if (!drv_data)
+ return -ENODEV;
+
+ hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
- match = of_match_node(dw_hdmi_rockchip_dt_ids, np);
- plat_data = devm_kmemdup(&pdev->dev, match->data,
- sizeof(*plat_data), GFP_KERNEL);
+ plat_data = drmm_kzalloc(drm, sizeof(*drv_data), GFP_KERNEL);
if (!plat_data)
return -ENOMEM;
+ memcpy(plat_data, drv_data, sizeof(*drv_data));
- hdmi->dev = &pdev->dev;
+ hdmi->dev = dev;
hdmi->plat_data = plat_data;
hdmi->chip_data = plat_data->phy_data;
plat_data->phy_data = hdmi;
@@ -576,18 +594,39 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
* the required CRTC is added later.
*/
if (encoder->possible_crtcs == 0)
- return -EPROBE_DEFER;
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "failed to find possible crtcs\n");
- ret = rockchip_hdmi_parse_dt(hdmi);
- if (ret) {
- return dev_err_probe(hdmi->dev, ret, "Unable to parse OF data\n");
- }
+ hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(hdmi->regmap))
+ return dev_err_probe(dev, PTR_ERR(hdmi->regmap),
+ "failed to get rockchip,grf\n");
+
+ hdmi->ref_clk = devm_clk_get_optional_enabled(dev, "ref");
+ if (!hdmi->ref_clk)
+ hdmi->ref_clk = devm_clk_get_optional_enabled(dev, "vpll");
+
+ if (IS_ERR(hdmi->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(hdmi->ref_clk),
+ "failed to get reference clock\n");
+
+ hdmi->grf_clk = devm_clk_get_optional(dev, "grf");
+ if (IS_ERR(hdmi->grf_clk))
+ return dev_err_probe(dev, PTR_ERR(hdmi->grf_clk),
+ "failed to get grf clock\n");
+
+ ret = devm_regulator_get_enable(dev, "avdd-0v9");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable avdd-0v9\n");
+
+ ret = devm_regulator_get_enable(dev, "avdd-1v8");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable avdd-1v8\n");
hdmi->phy = devm_phy_optional_get(dev, "hdmi");
- if (IS_ERR(hdmi->phy)) {
- ret = PTR_ERR(hdmi->phy);
- return dev_err_probe(hdmi->dev, ret, "failed to get phy\n");
- }
+ if (IS_ERR(hdmi->phy))
+ return dev_err_probe(dev, PTR_ERR(hdmi->phy),
+ "failed to get phy\n");
index = of_property_match_string(np, "phy-names", "hdmi");
if (index >= 0) {
@@ -608,28 +647,27 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
FIELD_PREP_WM16(RK3568_HDMI_SCLIN_MSK, 1));
}
+ ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init encoder\n");
+
drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
platform_set_drvdata(pdev, hdmi);
hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
-
- /*
- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
- * which would have called the encoder cleanup. Do it manually.
- */
- if (IS_ERR(hdmi->hdmi)) {
- ret = PTR_ERR(hdmi->hdmi);
- goto err_bind;
+ if (IS_ERR(hdmi->hdmi))
+ return dev_err_probe(dev, PTR_ERR(hdmi->hdmi),
+ "failed to probe dw-hdmi bridge\n");
+
+ hdmi->bridge = of_drm_find_and_get_bridge(np);
+ if (!hdmi->bridge) {
+ dw_hdmi_unbind(hdmi->hdmi);
+ return dev_err_probe(dev, -ENODEV,
+ "failed to find dw-hdmi bridge\n");
}
return 0;
-
-err_bind:
- drm_encoder_cleanup(encoder);
-
- return ret;
}
static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
@@ -637,8 +675,8 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
{
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+ drm_bridge_put(hdmi->bridge);
dw_hdmi_unbind(hdmi->hdmi);
- drm_encoder_cleanup(&hdmi->encoder.encoder);
}
static const struct component_ops dw_hdmi_rockchip_ops = {
@@ -656,17 +694,18 @@ static void dw_hdmi_rockchip_remove(struct platform_device *pdev)
component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
}
-static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev)
+static int __maybe_unused dw_hdmi_rockchip_resume_early(struct device *dev)
{
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
- dw_hdmi_resume(hdmi->hdmi);
+ if (hdmi)
+ dw_hdmi_resume(hdmi->hdmi);
return 0;
}
static const struct dev_pm_ops dw_hdmi_rockchip_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume_early)
};
struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index f35484715c2d..815f9ea7bcbe 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -11,6 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
+#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
@@ -44,10 +45,6 @@
#define RK3576_8BPC 0x0
#define RK3576_10BPC 0x6
#define RK3576_COLOR_FORMAT_MASK GENMASK(7, 4)
-#define RK3576_RGB 0x9
-#define RK3576_YUV422 0x1
-#define RK3576_YUV444 0x2
-#define RK3576_YUV420 0x3
#define RK3576_CECIN_MASK BIT(3)
#define RK3576_VO0_GRF_SOC_CON14 0x0038
@@ -75,8 +72,6 @@
#define RK3588_8BPC 0x0
#define RK3588_10BPC 0x6
#define RK3588_COLOR_FORMAT_MASK GENMASK(3, 0)
-#define RK3588_RGB 0x0
-#define RK3588_YUV420 0x3
#define RK3588_SCLIN_MASK BIT(9)
#define RK3588_SDAIN_MASK BIT(10)
#define RK3588_MODE_MASK BIT(11)
@@ -88,6 +83,11 @@
#define HOTPLUG_DEBOUNCE_MS 150
#define MAX_HDMI_PORT_NUM 2
+#define RK_COLOR_FMT_RGB 0x0
+#define RK_COLOR_FMT_YUV422 0x1
+#define RK_COLOR_FMT_YUV444 0x2
+#define RK_COLOR_FMT_YUV420 0x3
+
struct rockchip_hdmi_qp {
struct device *dev;
struct regmap *regmap;
@@ -116,6 +116,33 @@ static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
}
+/**
+ * dw_hdmi_qp_rockchip_bus_fmt_to_reg - converts a bus format to a GRF reg value
+ * @bus_fmt: One of the MEDIA_BUS_FMT_s allowed by this driver's atomic_check
+ *
+ * Returns: an unshifted value to be written to the COLOR_FORMAT GRF register
+ * on success, or %-EINVAL if the bus format is not supported.
+ */
+static int __pure dw_hdmi_qp_rockchip_bus_fmt_to_reg(u32 bus_fmt)
+{
+ switch (bus_fmt) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ return RK_COLOR_FMT_RGB;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ return RK_COLOR_FMT_YUV422;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ return RK_COLOR_FMT_YUV444;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ return RK_COLOR_FMT_YUV420;
+ }
+
+ return -EINVAL;
+}
+
static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
@@ -131,29 +158,83 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
hdmi->ctrl_ops->enc_init(hdmi, to_rockchip_crtc_state(crtc->state));
}
+/**
+ * dw_hdmi_qp_rockchip_get_vop_format - get the bus format VOP should output
+ * @encoder: pointer to a &struct drm_encoder
+ * @conn_state: pointer to the current atomic &struct drm_connector_state
+ *
+ * Determines which bus format the Rockchip video processor should output as
+ * to feed into the bridge chain.
+ *
+ * Returns a MEDIA_BUS_FMT_* on success, or negative errno on error.
+ */
+static int dw_hdmi_qp_rockchip_get_vop_format(struct drm_encoder *encoder,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
+ struct drm_bridge_state *bstate;
+
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ if (!bridge)
+ return -ENODEV;
+
+ bstate = drm_atomic_get_bridge_state(conn_state->state, bridge);
+ if (IS_ERR(bstate))
+ return PTR_ERR(bstate);
+
+ if (bstate->input_bus_cfg.format != MEDIA_BUS_FMT_FIXED)
+ return bstate->input_bus_cfg.format;
+
+ return bstate->output_bus_cfg.format;
+}
+
static int
dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
- struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
union phy_configure_opts phy_cfg = {};
+ int ingest_fmt;
int ret;
+ ingest_fmt = dw_hdmi_qp_rockchip_get_vop_format(encoder, conn_state);
+ if (ingest_fmt < 0)
+ return -EINVAL;
+
if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate &&
- s->output_bpc == conn_state->hdmi.output_bpc)
+ s->output_bpc == conn_state->hdmi.output_bpc &&
+ s->bus_format == ingest_fmt)
return 0;
+ switch (ingest_fmt) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ s->output_mode = ROCKCHIP_OUT_MODE_YUV422;
+ break;
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+ break;
+ default:
+ return -EINVAL;
+ }
+
phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc;
ret = phy_configure(hdmi->phy, &phy_cfg);
if (!ret) {
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
- s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
s->output_bpc = conn_state->hdmi.output_bpc;
+ s->bus_format = ingest_fmt;
} else {
dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret);
}
@@ -383,6 +464,7 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
struct rockchip_crtc_state *state)
{
+ int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format);
u32 val;
if (state->output_bpc == 10)
@@ -390,12 +472,16 @@ static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
else
val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC);
+ if (likely(color >= 0))
+ val |= FIELD_PREP_WM16(RK3576_COLOR_FORMAT_MASK, color);
+
regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val);
}
static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi,
struct rockchip_crtc_state *state)
{
+ int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format);
u32 val;
if (state->output_bpc == 10)
@@ -403,6 +489,9 @@ static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi,
else
val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC);
+ if (likely(color >= 0))
+ val |= FIELD_PREP_WM16(RK3588_COLOR_FORMAT_MASK, color);
+
regmap_write(hdmi->vo_regmap,
hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
val);
@@ -513,6 +602,10 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
plat_data.phy_data = hdmi;
plat_data.max_bpc = 10;
+ plat_data.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
+
encoder = &hdmi->encoder.encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 1996f8656612..9b8981fa19ae 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -497,7 +497,7 @@ rk3066_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs rk3066_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_enable = rk3066_hdmi_bridge_atomic_enable,
.atomic_disable = rk3066_hdmi_bridge_atomic_disable,
.detect = rk3066_hdmi_bridge_detect,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 2e86ad00979c..4705dc6b8bd7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -30,10 +30,14 @@
#define ROCKCHIP_OUT_MODE_P565 2
#define ROCKCHIP_OUT_MODE_BT656 5
#define ROCKCHIP_OUT_MODE_S888 8
+#define ROCKCHIP_OUT_MODE_YUV422 9
#define ROCKCHIP_OUT_MODE_S888_DUMMY 12
#define ROCKCHIP_OUT_MODE_YUV420 14
/* for use special outface */
#define ROCKCHIP_OUT_MODE_AAAA 15
+/* SoC specific output modes */
+#define ROCKCHIP_OUT_MODE_YUV422_RK3576_DP 12
+#define ROCKCHIP_OUT_MODE_YUV422_RK3576_HDMI 13
/* output flags */
#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index a160077a507f..4cce3e336f5b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -337,7 +337,8 @@ static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode)
static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format)
{
- if (vop2->version == VOP_VERSION_RK3588) {
+ if (vop2->version == VOP_VERSION_RK3588 ||
+ vop2->version == VOP_VERSION_RK3576) {
if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
bus_format == MEDIA_BUS_FMT_YUV10_1X30)
return true;
@@ -351,6 +352,8 @@ static bool is_yuv_output(u32 bus_format)
switch (bus_format) {
case MEDIA_BUS_FMT_YUV8_1X24:
case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_YUYV8_2X8:
@@ -658,7 +661,7 @@ static int vop2_convert_csc_mode(int csc_mode)
case V4L2_COLORSPACE_JPEG:
return CSC_BT601F;
case V4L2_COLORSPACE_BT2020:
- return CSC_BT2020;
+ return CSC_BT2020L;
default:
return CSC_BT709L;
}
@@ -728,6 +731,89 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp,
vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode);
}
+/*
+ * RGB-to-YCbCr conversion based on color_to_ycbcr() and rgb2ycbcr() from
+ * drivers/media/common/v4l2-tpg/v4l2-tpg-core.c.
+ *
+ * Limited-range Y offset & chroma midpoint are expressed in 16-bit space.
+ */
+#define RGB2YUV_LIMITED_Y_OFFSET (16 << 8)
+#define RGB2YUV_CHROMA_OFFSET (128 << 8)
+#define COEFF(v, r) ((s32)(0.5 + (v) * (r) * 256.0))
+
+struct rgb2yuv_matrix {
+ s32 y_r, y_g, y_b;
+ s32 cb_r, cb_g, cb_b;
+ s32 cr_r, cr_g, cr_b;
+ s32 y_offset;
+};
+
+/* BT.601 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt601l = {
+ .y_r = COEFF(0.299, 219), .y_g = COEFF(0.587, 219), .y_b = COEFF(0.114, 219),
+ .cb_r = COEFF(-0.1687, 224), .cb_g = COEFF(-0.3313, 224), .cb_b = COEFF(0.5, 224),
+ .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4187, 224), .cr_b = COEFF(-0.0813, 224),
+ .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+/* BT.601 Full range */
+static const struct rgb2yuv_matrix rgb2yuv_bt601f = {
+ .y_r = COEFF(0.299, 255), .y_g = COEFF(0.587, 255), .y_b = COEFF(0.114, 255),
+ .cb_r = COEFF(-0.1687, 255), .cb_g = COEFF(-0.3313, 255), .cb_b = COEFF(0.5, 255),
+ .cr_r = COEFF(0.5, 255), .cr_g = COEFF(-0.4187, 255), .cr_b = COEFF(-0.0813, 255),
+ .y_offset = 0,
+};
+
+/* BT.709 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt709l = {
+ .y_r = COEFF(0.2126, 219), .y_g = COEFF(0.7152, 219), .y_b = COEFF(0.0722, 219),
+ .cb_r = COEFF(-0.1146, 224), .cb_g = COEFF(-0.3854, 224), .cb_b = COEFF(0.5, 224),
+ .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4542, 224), .cr_b = COEFF(-0.0458, 224),
+ .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+/* BT.2020 Limited range */
+static const struct rgb2yuv_matrix rgb2yuv_bt2020l = {
+ .y_r = COEFF(0.2627, 219), .y_g = COEFF(0.6780, 219), .y_b = COEFF(0.0593, 219),
+ .cb_r = COEFF(-0.1396, 224), .cb_g = COEFF(-0.3604, 224), .cb_b = COEFF(0.5, 224),
+ .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4598, 224), .cr_b = COEFF(-0.0402, 224),
+ .y_offset = RGB2YUV_LIMITED_Y_OFFSET,
+};
+
+static const struct rgb2yuv_matrix *
+vop2_rgb2yuv_get_matrix(enum vop_csc_format csc)
+{
+ switch (csc) {
+ case CSC_BT601L:
+ return &rgb2yuv_bt601l;
+ case CSC_BT601F:
+ return &rgb2yuv_bt601f;
+ case CSC_BT2020L:
+ return &rgb2yuv_bt2020l;
+ case CSC_BT709L:
+ default:
+ return &rgb2yuv_bt709l;
+ }
+}
+
+/* Convert an RGB (16bpc) to YUV444 (16bpc). */
+static void vop2_rgb16_to_yuv16(int v4l2_cs, u16 r, u16 g, u16 b,
+ u16 *y, u16 *cb, u16 *cr)
+{
+ enum vop_csc_format csc = vop2_convert_csc_mode(v4l2_cs);
+ const struct rgb2yuv_matrix *m = vop2_rgb2yuv_get_matrix(csc);
+ s64 rs = r, gs = g, bs = b;
+ s64 ys, cbs, crs;
+
+ ys = m->y_r * rs + m->y_g * gs + m->y_b * bs;
+ cbs = m->cb_r * rs + m->cb_g * gs + m->cb_b * bs;
+ crs = m->cr_r * rs + m->cr_g * gs + m->cr_b * bs;
+
+ *y = (ys >> 16) + m->y_offset;
+ *cb = (cbs >> 16) + RGB2YUV_CHROMA_OFFSET;
+ *cr = (crs >> 16) + RGB2YUV_CHROMA_OFFSET;
+}
+
static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq)
{
struct vop2 *vop2 = vp->vop2;
@@ -1554,12 +1640,58 @@ static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl)
DITHER_DOWN_ALLEGRO);
}
-static void vop2_post_config(struct drm_crtc *crtc)
+static void vop2_bgcolor_setup(struct drm_crtc *crtc, bool force,
+ struct drm_crtc_state *new_crtc_state,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct rockchip_crtc_state *new_vcstate = to_rockchip_crtc_state(new_crtc_state);
+ struct rockchip_crtc_state *old_vcstate = to_rockchip_crtc_state(old_crtc_state);
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
+ u64 bgcolor = new_crtc_state->background_color;
+ u16 y, cb, cr;
+ u32 val;
+
+ if (!force && old_crtc_state->background_color == bgcolor &&
+ old_vcstate->color_space == new_vcstate->color_space)
+ return;
+
+ /*
+ * Background color is programmed with 10 bits of precision, using YUV
+ * format when operating in YUV overlay mode, and RGB otherwise.
+ */
+ if (new_vcstate->yuv_overlay) {
+ vop2_rgb16_to_yuv16(new_vcstate->color_space,
+ DRM_ARGB64_GETR(bgcolor),
+ DRM_ARGB64_GETG(bgcolor),
+ DRM_ARGB64_GETB(bgcolor),
+ &y, &cb, &cr);
+
+ val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, cr >> 6);
+ FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, y >> 6);
+ FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, cb >> 6);
+ } else {
+ /*
+ * Since performance is more important than accuracy here, make
+ * use of the DRM_ARGB64_GET*_BPCS() helpers.
+ */
+ val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED,
+ DRM_ARGB64_GETR_BPCS(bgcolor, 10));
+ FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val,
+ DRM_ARGB64_GETG_BPCS(bgcolor, 10));
+ FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val,
+ DRM_ARGB64_GETB_BPCS(bgcolor, 10));
+ }
+
+ vop2_vp_write(vp, RK3568_VP_DSP_BG, val);
+}
+
+static void vop2_post_config(struct drm_crtc *crtc, bool force,
+ struct drm_crtc_state *new_crtc_state,
+ struct drm_crtc_state *old_crtc_state)
{
struct vop2_video_port *vp = to_vop2_video_port(crtc);
struct vop2 *vop2 = vp->vop2;
- struct drm_display_mode *mode = &crtc->state->adjusted_mode;
- u64 bgcolor = crtc->state->background_color;
+ struct drm_display_mode *mode = &new_crtc_state->adjusted_mode;
u16 vtotal = mode->crtc_vtotal;
u16 hdisplay = mode->crtc_hdisplay;
u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
@@ -1605,15 +1737,7 @@ static void vop2_post_config(struct drm_crtc *crtc)
vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val);
}
- /*
- * Background color is programmed with 10 bits of precision.
- * Since performance is more important than accuracy here,
- * make use of the DRM_ARGB64_GET*_BPCS() helpers.
- */
- val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, DRM_ARGB64_GETR_BPCS(bgcolor, 10));
- FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, DRM_ARGB64_GETG_BPCS(bgcolor, 10));
- FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, DRM_ARGB64_GETB_BPCS(bgcolor, 10));
- vop2_vp_write(vp, RK3568_VP_DSP_BG, val);
+ vop2_bgcolor_setup(crtc, force, new_crtc_state, old_crtc_state);
}
static int us_to_vertical_line(struct drm_display_mode *mode, int us)
@@ -1628,8 +1752,9 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
struct vop2 *vop2 = vp->vop2;
const struct vop2_data *vop2_data = vop2->data;
const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
+ struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
- struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc_state);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
unsigned long clock = mode->crtc_clock * 1000;
u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
@@ -1699,6 +1824,22 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
!(vp_data->feature & VOP2_VP_FEATURE_OUTPUT_10BIT))
out_mode = ROCKCHIP_OUT_MODE_P888;
+ else if (vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV422 &&
+ vop2->version == VOP_VERSION_RK3576)
+ switch (vcstate->output_type) {
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_eDP:
+ out_mode = ROCKCHIP_OUT_MODE_YUV422_RK3576_DP;
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ out_mode = ROCKCHIP_OUT_MODE_YUV422_RK3576_HDMI;
+ break;
+ default:
+ drm_err(vop2->drm, "Unknown DRM_MODE_CONNECTOR %d\n",
+ vcstate->output_type);
+ vop2_unlock(vop2);
+ return;
+ }
else
out_mode = vcstate->output_mode;
@@ -1799,7 +1940,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
clk_set_rate(vp->dclk, clock);
- vop2_post_config(crtc);
+ vop2_post_config(crtc, true, crtc_state, old_crtc_state);
vop2_cfg_done(vp);
@@ -1874,6 +2015,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_commit *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
struct vop2_video_port *vp = to_vop2_video_port(crtc);
struct vop2 *vop2 = vp->vop2;
@@ -1881,7 +2023,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed)
vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state);
- vop2_post_config(crtc);
+ vop2_post_config(crtc, false, crtc_state, old_crtc_state);
vop2_cfg_done(vp);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index 37722652844a..ffcb39c130aa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -373,7 +373,7 @@ enum vop_csc_format {
CSC_BT601L,
CSC_BT709L,
CSC_BT601F,
- CSC_BT2020,
+ CSC_BT2020L,
};
enum src_factor_mode {
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 7a0c4fa29f2f..31dc206bedeb 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -119,7 +119,7 @@ static const
struct drm_bridge_funcs rockchip_lvds_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.get_modes = rockchip_lvds_bridge_get_modes,
};
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
index 17eda592b183..c44648e1bc0d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -2114,7 +2114,7 @@ static u32 rk3568_vop2_read_layer_cfg(struct vop2 *vop2)
return vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
}
-static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2, u32 cfg)
+static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2)
{
u32 atv_layer_cfg;
int ret;
@@ -2123,21 +2123,19 @@ static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2, u32 cfg)
* Spin until the previous layer configuration is done.
*/
ret = readx_poll_timeout_atomic(rk3568_vop2_read_layer_cfg, vop2, atv_layer_cfg,
- atv_layer_cfg == cfg, 10, 50 * 1000);
+ atv_layer_cfg == vop2->old_layer_sel, 10, 50 * 1000);
if (ret)
drm_err_ratelimited(vop2->drm, "wait layer cfg done timeout: 0x%x--0x%x\n",
- atv_layer_cfg, cfg);
+ atv_layer_cfg, vop2->old_layer_sel);
}
static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
{
struct vop2 *vop2 = vp->vop2;
struct drm_plane *plane;
- u32 layer_sel = 0;
+ u32 layer_sel;
u32 port_sel;
- u32 old_layer_sel = 0;
- u32 atv_layer_sel = 0;
- u32 old_port_sel = 0;
+ u32 atv_layer_sel;
u8 layer_id;
u8 old_layer_id;
u8 layer_sel_id;
@@ -2160,8 +2158,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
else
ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id);
- old_port_sel = vop2->old_port_sel;
- port_sel = old_port_sel;
+ port_sel = vop2->old_port_sel;
port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT;
if (vp0->nlayers)
@@ -2187,8 +2184,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
port_sel |= FIELD_PREP(RK3588_OVL_PORT_SET__PORT3_MUX, 7);
atv_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL);
- old_layer_sel = vop2->old_layer_sel;
- layer_sel = old_layer_sel;
+ layer_sel = vop2->old_layer_sel;
ofs = 0;
for (i = 0; i < vp->id; i++)
@@ -2272,8 +2268,6 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
old_win->data->layer_sel_id[vp->id]);
}
- vop2->old_layer_sel = layer_sel;
- vop2->old_port_sel = port_sel;
/*
* As the RK3568_OVL_LAYER_SEL and RK3568_OVL_PORT_SEL are shared by all Video Ports,
* and the configuration take effect by one Video Port's vsync.
@@ -2288,17 +2282,8 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
* lead to the configuration of the previous VP being take effect along with the VSYNC
* of the new VP.
*/
- if (layer_sel != old_layer_sel || port_sel != old_port_sel)
- ovl_ctrl |= FIELD_PREP(RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL, vp->id);
- vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl);
- if (port_sel != old_port_sel) {
- vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
- vop2_cfg_done(vp);
- rk3568_vop2_wait_for_port_mux_done(vop2);
- }
-
- if (layer_sel != old_layer_sel && atv_layer_sel != old_layer_sel) {
+ if (layer_sel != vop2->old_layer_sel && atv_layer_sel != vop2->old_layer_sel) {
cfg_done = vop2_readl(vop2, RK3568_REG_CFG_DONE);
cfg_done &= (BIT(vop2->data->nr_vps) - 1);
cfg_done &= ~BIT(vp->id);
@@ -2306,10 +2291,23 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp)
* Changes of other VPs' overlays have not taken effect
*/
if (cfg_done)
- rk3568_vop2_wait_for_layer_cfg_done(vop2, vop2->old_layer_sel);
+ rk3568_vop2_wait_for_layer_cfg_done(vop2);
+ }
+
+ if (layer_sel != vop2->old_layer_sel || port_sel != vop2->old_port_sel)
+ ovl_ctrl |= FIELD_PREP(RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL, vp->id);
+ vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl);
+
+ if (port_sel != vop2->old_port_sel) {
+ vop2->old_port_sel = port_sel;
+ vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel);
+ vop2_cfg_done(vp);
+ rk3568_vop2_wait_for_port_mux_done(vop2);
}
+ vop2->old_layer_sel = layer_sel;
vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel);
+
mutex_unlock(&vop2->ovl_lock);
}
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index 4ebb513255ed..c51101ec70c1 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -129,7 +129,6 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
return -ENOMEM;
INIT_LIST_HEAD(&entity->list);
- entity->rq = NULL;
entity->guilty = guilty;
entity->priority = priority;
entity->last_user = current->group_leader;
@@ -280,9 +279,6 @@ void drm_sched_entity_kill(struct drm_sched_entity *entity)
struct drm_sched_job *job;
struct dma_fence *prev;
- if (!entity->rq)
- return;
-
spin_lock(&entity->lock);
entity->stopped = true;
drm_sched_rq_remove_entity(entity->rq, entity);
@@ -329,14 +325,11 @@ EXPORT_SYMBOL(drm_sched_entity_kill);
*/
long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
{
- struct drm_gpu_scheduler *sched;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
struct task_struct *last_user;
long ret = timeout;
- if (!entity->rq)
- return 0;
-
- sched = container_of(entity->rq, typeof(*sched), rq);
/*
* The client will not queue more jobs during this fini - consume
* existing queued ones, or discard them on SIGKILL.
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index 818d3d4434b5..d2ca01b31ee4 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -588,15 +588,6 @@ int drm_sched_job_init(struct drm_sched_job *job,
u32 credits, void *owner,
uint64_t drm_client_id)
{
- if (!entity->rq) {
- /* This will most likely be followed by missing frames
- * or worse--a blank screen--leave a trail in the
- * logs, so this can be debugged easier.
- */
- dev_err(job->sched->dev, "%s: entity has no rq!\n", __func__);
- return -ENOENT;
- }
-
if (unlikely(!credits)) {
pr_err("*ERROR* %s: credits cannot be 0!\n", __func__);
return -EINVAL;
diff --git a/drivers/gpu/drm/scheduler/tests/tests_basic.c b/drivers/gpu/drm/scheduler/tests/tests_basic.c
index a5a5a35a87b0..da5c4952780f 100644
--- a/drivers/gpu/drm/scheduler/tests/tests_basic.c
+++ b/drivers/gpu/drm/scheduler/tests/tests_basic.c
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Valve Corporation */
+#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/minmax.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
#include "sched_tests.h"
@@ -235,6 +239,360 @@ static void drm_sched_basic_cancel(struct kunit *test)
KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED);
}
+struct sched_concurrent_context {
+ struct drm_mock_scheduler *sched;
+ struct workqueue_struct *sub_wq;
+ struct kunit *test;
+ struct completion wait_go;
+};
+
+KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_fini_wrap, drm_mock_sched_fini,
+ struct drm_mock_scheduler *);
+
+KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_entity_free_wrap, drm_mock_sched_entity_free,
+ struct drm_mock_sched_entity *);
+
+static void complete_destroy_workqueue(void *context)
+{
+ struct sched_concurrent_context *ctx = context;
+
+ complete_all(&ctx->wait_go);
+
+ destroy_workqueue(ctx->sub_wq);
+}
+
+static int drm_sched_concurrent_init(struct kunit *test)
+{
+ struct sched_concurrent_context *ctx;
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ init_completion(&ctx->wait_go);
+
+ ctx->sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
+
+ ret = kunit_add_action_or_reset(test, drm_mock_sched_fini_wrap, ctx->sched);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /* Use an unbounded workqueue to maximize job submission concurrency */
+ ctx->sub_wq = alloc_workqueue("drm-sched-submitters-wq", WQ_UNBOUND,
+ WQ_UNBOUND_MAX_ACTIVE);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->sub_wq);
+
+ ret = kunit_add_action_or_reset(test, complete_destroy_workqueue, ctx);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ctx->test = test;
+ test->priv = ctx;
+
+ return 0;
+}
+
+struct drm_sched_parallel_params {
+ const char *description;
+ unsigned int num_jobs;
+ unsigned int num_workers;
+};
+
+static const struct drm_sched_parallel_params drm_sched_parallel_cases[] = {
+ {
+ .description = "Parallel submission of multiple jobs per worker",
+ .num_jobs = 8,
+ .num_workers = 16,
+ },
+};
+
+static void
+drm_sched_parallel_desc(const struct drm_sched_parallel_params *params, char *desc)
+{
+ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_sched_parallel, drm_sched_parallel_cases, drm_sched_parallel_desc);
+
+struct parallel_worker {
+ struct work_struct work;
+ struct sched_concurrent_context *ctx;
+ struct drm_mock_sched_entity *entity;
+ struct drm_mock_sched_job **jobs;
+ unsigned int id;
+};
+
+static void drm_sched_parallel_worker(struct work_struct *work)
+{
+ const struct drm_sched_parallel_params *params;
+ struct sched_concurrent_context *test_ctx;
+ struct parallel_worker *worker;
+ unsigned int i;
+
+ worker = container_of(work, struct parallel_worker, work);
+ test_ctx = worker->ctx;
+ params = test_ctx->test->param_value;
+
+ wait_for_completion(&test_ctx->wait_go);
+
+ kunit_info(test_ctx->test, "Parallel worker %u submitting %u jobs started\n",
+ worker->id, params->num_jobs);
+
+ for (i = 0; i < params->num_jobs; i++)
+ drm_mock_sched_job_submit(worker->jobs[i]);
+}
+
+/*
+ * Spawns workers that submit a sequence of jobs to the mock scheduler.
+ * Once all jobs are submitted, the timeline is manually advanced.
+ */
+static void drm_sched_parallel_submit_test(struct kunit *test)
+{
+ struct sched_concurrent_context *ctx = test->priv;
+ const struct drm_sched_parallel_params *params = test->param_value;
+ struct parallel_worker *workers, *worker;
+ struct drm_mock_sched_job *job;
+ unsigned int i, j, completed_jobs, total_jobs;
+ bool done;
+ int ret;
+
+ KUNIT_ASSERT_GT(test, params->num_workers, 0);
+ KUNIT_ASSERT_GT(test, params->num_jobs, 0);
+
+ workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, workers);
+
+ /*
+ * Init workers only after all jobs and entities have been successfully
+ * allocated. In this way, the cleanup logic for when an assertion fail
+ * can be simplified.
+ */
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ worker->id = i;
+ worker->ctx = ctx;
+ worker->entity = drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ ctx->sched);
+
+ worker->jobs = kunit_kcalloc(test, params->num_jobs,
+ sizeof(*worker->jobs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, worker->jobs);
+
+ for (j = 0; j < params->num_jobs; j++) {
+ job = drm_mock_sched_job_new(test, worker->entity);
+ worker->jobs[j] = job;
+ }
+
+ ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap,
+ worker->entity);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ INIT_WORK(&worker->work, drm_sched_parallel_worker);
+ queue_work(ctx->sub_wq, &worker->work);
+ }
+
+ complete_all(&ctx->wait_go);
+ flush_workqueue(ctx->sub_wq);
+
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ for (j = 0; j < params->num_jobs; j++) {
+ job = worker->jobs[j];
+ done = drm_mock_sched_job_wait_scheduled(job, HZ);
+ KUNIT_EXPECT_TRUE(test, done);
+ }
+ }
+
+ total_jobs = params->num_workers * params->num_jobs;
+ completed_jobs = drm_mock_sched_advance(ctx->sched, total_jobs);
+ KUNIT_EXPECT_EQ(test, completed_jobs, total_jobs);
+
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ for (j = 0; j < params->num_jobs; j++) {
+ job = worker->jobs[j];
+ done = drm_mock_sched_job_wait_finished(job, HZ);
+ KUNIT_EXPECT_TRUE(test, done);
+ }
+ }
+}
+
+struct drm_sched_interleaved_params {
+ const char *description;
+ unsigned int test_duration_ms;
+ unsigned int job_base_duration_us;
+ unsigned int num_workers;
+ unsigned int num_in_flight_jobs;
+};
+
+static const struct drm_sched_interleaved_params drm_sched_interleaved_cases[] = {
+ {
+ .description = "Interleaved submission of multiple jobs per worker",
+ .test_duration_ms = 1000,
+ .job_base_duration_us = 100,
+ .num_workers = 16,
+ .num_in_flight_jobs = 8,
+ },
+};
+
+static void
+drm_sched_interleaved_desc(const struct drm_sched_interleaved_params *params, char *desc)
+{
+ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_sched_interleaved, drm_sched_interleaved_cases,
+ drm_sched_interleaved_desc);
+
+struct interleaved_worker {
+ struct work_struct work;
+ struct sched_concurrent_context *ctx;
+ struct drm_mock_sched_entity *entity;
+ struct drm_mock_sched_job **jobs;
+ unsigned int id;
+ unsigned int job_count;
+ unsigned int job_duration_us;
+};
+
+static void drm_sched_interleaved_worker(struct work_struct *work)
+{
+ struct sched_concurrent_context *test_ctx;
+ const struct drm_sched_interleaved_params *params;
+ struct interleaved_worker *worker;
+ unsigned int i, j, max_in_flight_job;
+ unsigned long timeout;
+ bool done;
+
+ worker = container_of(work, struct interleaved_worker, work);
+ test_ctx = worker->ctx;
+ params = test_ctx->test->param_value;
+
+ wait_for_completion(&test_ctx->wait_go);
+
+ kunit_info(test_ctx->test, "Worker %u submitting %u jobs of %u us started\n",
+ worker->id, worker->job_count, worker->job_duration_us);
+
+ timeout = msecs_to_jiffies(params->test_duration_ms * 2);
+
+ /* Fill the submission window */
+ max_in_flight_job = min(worker->job_count, params->num_in_flight_jobs);
+ for (i = 0; i < max_in_flight_job; i++)
+ drm_mock_sched_job_submit(worker->jobs[i]);
+
+ /* Keep the window full by submitting a new job at once until done */
+ for (i = 0; i < worker->job_count; i++) {
+ done = drm_mock_sched_job_wait_finished(worker->jobs[i], timeout);
+ if (!done)
+ kunit_info(test_ctx->test, "Job %u of worker %u timed out\n",
+ i, worker->id);
+
+ j = i + max_in_flight_job;
+ if (j < worker->job_count)
+ drm_mock_sched_job_submit(worker->jobs[j]);
+ }
+}
+
+/*
+ * Spawns workers that submit a sequence of jobs to the mock scheduler. Job
+ * durations are chosen as multiples of a base duration value specified as
+ * a test parameter. Since the scheduler serializes jobs from all workers,
+ * the total test duration budget is divided into equal shares among workers.
+ * These shares are then used to compute the number of jobs that each worker
+ * can submit.
+ */
+static void drm_sched_interleaved_submit_test(struct kunit *test)
+{
+ const struct drm_sched_interleaved_params *params = test->param_value;
+ struct sched_concurrent_context *ctx = test->priv;
+ struct interleaved_worker *workers, *worker;
+ struct drm_mock_sched_job *job;
+ unsigned int worker_share_us;
+ unsigned int i, j;
+ bool done;
+ int ret;
+
+ KUNIT_ASSERT_GT(test, params->num_workers, 0);
+ KUNIT_ASSERT_GT(test, params->job_base_duration_us, 0);
+
+ workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, workers);
+
+ /* Divide the available test time into equal shares among the workers */
+ worker_share_us = (params->test_duration_ms * USEC_PER_MSEC) /
+ params->num_workers;
+
+ /*
+ * Init workers only after all jobs and entities have been successfully
+ * allocated. In this way, the cleanup logic for when an assertion fails
+ * can be simplified.
+ */
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ worker->id = i;
+ worker->ctx = ctx;
+
+ worker->job_duration_us = params->job_base_duration_us * (i + 1);
+ worker->job_count = worker_share_us / worker->job_duration_us;
+ worker->job_count = max(1U, worker->job_count);
+
+ worker->entity = drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ ctx->sched);
+
+ worker->jobs = kunit_kcalloc(test, worker->job_count,
+ sizeof(*worker->jobs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, worker->jobs);
+
+ for (j = 0; j < worker->job_count; j++) {
+ job = drm_mock_sched_job_new(test, worker->entity);
+ drm_mock_sched_job_set_duration_us(job, worker->job_duration_us);
+
+ worker->jobs[j] = job;
+ }
+
+ ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap,
+ worker->entity);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ INIT_WORK(&worker->work, drm_sched_interleaved_worker);
+ queue_work(ctx->sub_wq, &worker->work);
+ }
+
+ complete_all(&ctx->wait_go);
+ flush_workqueue(ctx->sub_wq);
+
+ for (i = 0; i < params->num_workers; i++) {
+ worker = &workers[i];
+ for (j = 0; j < worker->job_count; j++) {
+ job = worker->jobs[j];
+ done = drm_mock_sched_job_is_finished(job);
+ KUNIT_EXPECT_TRUE(test, done);
+ }
+ }
+}
+
+static struct kunit_case drm_sched_concurrent_tests[] = {
+ KUNIT_CASE_PARAM(drm_sched_parallel_submit_test, drm_sched_parallel_gen_params),
+ KUNIT_CASE_PARAM(drm_sched_interleaved_submit_test, drm_sched_interleaved_gen_params),
+ {}
+};
+
+static struct kunit_suite drm_sched_concurrent = {
+ .name = "drm_sched_concurrent_tests",
+ .init = drm_sched_concurrent_init,
+ .test_cases = drm_sched_concurrent_tests,
+ .attr = {
+ .speed = KUNIT_SPEED_SLOW,
+ },
+};
+
static struct kunit_case drm_sched_cancel_tests[] = {
KUNIT_CASE(drm_sched_basic_cancel),
{}
@@ -556,6 +914,7 @@ static struct kunit_suite drm_sched_credits = {
};
kunit_test_suites(&drm_sched_basic,
+ &drm_sched_concurrent,
&drm_sched_timeout,
&drm_sched_cancel,
&drm_sched_priority,
diff --git a/drivers/gpu/drm/scheduler/tests/tests_scheduler.c b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c
index 90d31888cf92..285546a2218f 100644
--- a/drivers/gpu/drm/scheduler/tests/tests_scheduler.c
+++ b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c
@@ -599,7 +599,7 @@ static void drm_sched_scheduler_two_clients_test(struct kunit *test)
* - qd: Number of outstanding jobs in the client/entity
*/
- pr_info(" [pct] - Job sumission progress\n"
+ pr_info(" [pct] - Job submission progress\n"
" [cps] - Cycles per second\n"
" [qd] - Number of outstanding jobs in the client/entity\n");
pr_info("%s:\n\t pct1 cps1 qd1; pct2 cps2 qd2\n",
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index cae92a3ae8a4..04da4f2f7d08 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -276,6 +276,49 @@ out_end:
return ret;
}
+/*
+ * Write a command byte sequence from a buffer.
+ *
+ * Like ssd130x_write_cmd() but takes a pre-built byte array instead of
+ * variadic arguments, handy when the command is already in an array or
+ * when the caller wants to use sizeof() for the length.
+ */
+static int ssd130x_write_cmds(struct ssd130x_device *ssd130x, const u8 *cmd,
+ size_t len)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = regmap_write(ssd130x->regmap, SSD13XX_COMMAND, cmd[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Run a packed command sequence. The format is a flat byte array where each
+ * entry starts with a length byte followed by that many command bytes. A
+ * zero length byte terminates the sequence.
+ *
+ * Example: { 2, 0x81, 0x80, 1, 0xAF, 0 }
+ * sends command {0x81, 0x80}, then command {0xAF}, then stops.
+ */
+static int ssd130x_run_cmd_seq(struct ssd130x_device *ssd130x, const u8 *seq)
+{
+ while (*seq) {
+ u8 len = *seq++;
+ int ret = ssd130x_write_cmds(ssd130x, seq, len);
+
+ if (ret)
+ return ret;
+ seq += len;
+ }
+
+ return 0;
+}
/* Set address range for horizontal/vertical addressing modes */
static int ssd130x_set_col_range(struct ssd130x_device *ssd130x,
u8 col_start, u8 cols)
@@ -408,39 +451,26 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
bool scan_mode;
int ret;
- /* Set initial contrast */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_CONTRAST, ssd130x->contrast);
- if (ret < 0)
- return ret;
-
/* Set segment re-map */
seg_remap = (SSD13XX_SET_SEG_REMAP |
SSD13XX_SET_SEG_REMAP_SET(ssd130x->seg_remap));
- ret = ssd130x_write_cmd(ssd130x, 1, seg_remap);
- if (ret < 0)
- return ret;
-
/* Set COM direction */
com_invdir = (SSD130X_SET_COM_SCAN_DIR |
SSD130X_SET_COM_SCAN_DIR_SET(ssd130x->com_invdir));
- ret = ssd130x_write_cmd(ssd130x, 1, com_invdir);
- if (ret < 0)
- return ret;
-
- /* Set multiplex ratio value */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
- if (ret < 0)
- return ret;
-
- /* set display offset value */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset);
- if (ret < 0)
- return ret;
-
/* Set clock frequency */
dclk = (SSD130X_SET_CLOCK_DIV_SET(ssd130x->dclk_div - 1) |
SSD130X_SET_CLOCK_FREQ_SET(ssd130x->dclk_frq));
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_CLOCK_FREQ, dclk);
+
+ const u8 *cmds = (const u8[]) {
+ 2, SSD13XX_CONTRAST, ssd130x->contrast,
+ 1, seg_remap,
+ 1, com_invdir, 0,
+ 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1,
+ 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset,
+ 2, SSD130X_SET_CLOCK_FREQ, dclk,
+ 0,
+ };
+ ret = ssd130x_run_cmd_seq(ssd130x, cmds);
if (ret < 0)
return ret;
@@ -462,9 +492,6 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
/* Set precharge period in number of ticks from the internal clock */
precharge = (SSD130X_SET_PRECHARGE_PERIOD1_SET(ssd130x->prechargep1) |
SSD130X_SET_PRECHARGE_PERIOD2_SET(ssd130x->prechargep2));
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_PRECHARGE_PERIOD, precharge);
- if (ret < 0)
- return ret;
/* Set COM pins configuration */
compins = BIT(1);
@@ -476,22 +503,20 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
scan_mode = !ssd130x->com_seq;
compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(scan_mode) |
SSD130X_SET_COM_PINS_CONFIG2_SET(ssd130x->com_lrremap));
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_COM_PINS_CONFIG, compins);
- if (ret < 0)
- return ret;
-
- /* Set VCOMH */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH, ssd130x->vcomh);
- if (ret < 0)
- return ret;
/* Turn on the DC-DC Charge Pump */
chargepump = BIT(4);
-
if (ssd130x->device_info->need_chargepump)
chargepump |= BIT(2);
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CHARGE_PUMP, chargepump);
+ cmds = (const u8[]) {
+ 2, SSD130X_SET_PRECHARGE_PERIOD, precharge,
+ 2, SSD130X_SET_COM_PINS_CONFIG, compins,
+ 2, SSD130X_SET_VCOMH, ssd130x->vcomh,
+ 2, SSD130X_CHARGE_PUMP, chargepump,
+ 0
+ };
+ ret = ssd130x_run_cmd_seq(ssd130x, cmds);
if (ret < 0)
return ret;
@@ -528,203 +553,68 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
static int ssd132x_init(struct ssd130x_device *ssd130x)
{
- int ret;
-
- /* Set initial contrast */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_CONTRAST, 0x80);
- if (ret < 0)
- return ret;
-
- /* Set column start and end */
- ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, 0x00,
- ssd130x->width / SSD132X_SEGMENT_WIDTH - 1);
- if (ret < 0)
- return ret;
-
- /* Set row start and end */
- ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1);
- if (ret < 0)
- return ret;
- /*
- * Horizontal Address Increment
- * Re-map for Column Address, Nibble and COM
- * COM Split Odd Even
- */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x53);
- if (ret < 0)
- return ret;
-
- /* Set display start and offset */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_START, 0x00);
- if (ret < 0)
- return ret;
-
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_OFFSET, 0x00);
- if (ret < 0)
- return ret;
-
- /* Set display mode normal */
- ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SET_DISPLAY_NORMAL);
- if (ret < 0)
- return ret;
-
- /* Set multiplex ratio value */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
- if (ret < 0)
- return ret;
-
- /* Set phase length */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PHASE_LENGTH, 0x55);
- if (ret < 0)
- return ret;
-
- /* Select default linear gray scale table */
- ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SELECT_DEFAULT_TABLE);
- if (ret < 0)
- return ret;
-
- /* Set clock frequency */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_CLOCK_FREQ, 0x01);
- if (ret < 0)
- return ret;
-
- /* Enable internal VDD regulator */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_A, 0x1);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge period */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_PERIOD, 0x01);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge voltage */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_VOLTAGE, 0x08);
- if (ret < 0)
- return ret;
-
- /* Set VCOMH voltage */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH_VOLTAGE, 0x07);
- if (ret < 0)
- return ret;
-
- /* Enable second pre-charge and internal VSL */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_B, 0x62);
- if (ret < 0)
- return ret;
-
- return 0;
+ const u8 cmds[] = {
+ 2, SSD13XX_CONTRAST, 0x80,
+ 3, SSD132X_SET_COL_RANGE, 0x00, ssd130x->width / SSD132X_SEGMENT_WIDTH - 1,
+ 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1,
+ /*
+ * Horizontal Address Increment
+ * Re-map for Column Address, Nibble and COM
+ * COM Split Odd Even
+ */
+ 2, SSD13XX_SET_SEG_REMAP, 0x53,
+ 2, SSD132X_SET_DISPLAY_START, 0x00,
+ 2, SSD132X_SET_DISPLAY_OFFSET, 0x00,
+ 1, SSD132X_SET_DISPLAY_NORMAL,
+ 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1,
+ 2, SSD132X_SET_PHASE_LENGTH, 0x55,
+ 1, SSD132X_SELECT_DEFAULT_TABLE,
+ 2, SSD132X_SET_CLOCK_FREQ, 0x01,
+ 2, SSD132X_SET_FUNCTION_SELECT_A, 0x1,
+ 2, SSD132X_SET_PRECHARGE_PERIOD, 0x01,
+ 2, SSD132X_SET_PRECHARGE_VOLTAGE, 0x08,
+ 2, SSD130X_SET_VCOMH_VOLTAGE, 0x07,
+ /* Enable second pre-charge and internal VSL */
+ 2, SSD132X_SET_FUNCTION_SELECT_B, 0x62,
+ 0,
+ };
+
+ return ssd130x_run_cmd_seq(ssd130x, cmds);
}
static int ssd133x_init(struct ssd130x_device *ssd130x)
{
- int ret;
-
- /* Set color A contrast */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_A, 0x91);
- if (ret < 0)
- return ret;
-
- /* Set color B contrast */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_B, 0x50);
- if (ret < 0)
- return ret;
-
- /* Set color C contrast */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_C, 0x7d);
- if (ret < 0)
- return ret;
-
- /* Set master current */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CURRENT, 0x06);
- if (ret < 0)
- return ret;
-
- /* Set column start and end */
- ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1);
- if (ret < 0)
- return ret;
-
- /* Set row start and end */
- ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1);
- if (ret < 0)
- return ret;
-
- /*
- * Horizontal Address Increment
- * Normal order SA,SB,SC (e.g. RGB)
- * COM Split Odd Even
- * 256 color format
- */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x20);
- if (ret < 0)
- return ret;
-
- /* Set display start and offset */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_START, 0x00);
- if (ret < 0)
- return ret;
-
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_OFFSET, 0x00);
- if (ret < 0)
- return ret;
-
- /* Set display mode normal */
- ret = ssd130x_write_cmd(ssd130x, 1, SSD133X_SET_DISPLAY_NORMAL);
- if (ret < 0)
- return ret;
-
- /* Set multiplex ratio value */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
- if (ret < 0)
- return ret;
-
- /* Set master configuration */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CONFIG, 0x8e);
- if (ret < 0)
- return ret;
-
- /* Set power mode */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_POWER_SAVE_MODE, 0x0b);
- if (ret < 0)
- return ret;
-
- /* Set Phase 1 and 2 period */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_PHASES_PERIOD, 0x31);
- if (ret < 0)
- return ret;
-
- /* Set clock divider */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_CLOCK_FREQ, 0xf0);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge A */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_A, 0x64);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge B */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_B, 0x78);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge C */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_C, 0x64);
- if (ret < 0)
- return ret;
-
- /* Set pre-charge level */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_PRECHARGE_VOLTAGE, 0x3a);
- if (ret < 0)
- return ret;
-
- /* Set VCOMH voltage */
- ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_VCOMH_VOLTAGE, 0x3e);
- if (ret < 0)
- return ret;
-
- return 0;
+ const u8 cmds[] = {
+ 2, SSD133X_CONTRAST_A, 0x91,
+ 2, SSD133X_CONTRAST_B, 0x50,
+ 2, SSD133X_CONTRAST_C, 0x7d,
+ 2, SSD133X_SET_MASTER_CURRENT, 0x06,
+ 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1,
+ 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1,
+ /*
+ * Horizontal Address Increment
+ * Normal order SA,SB,SC (e.g. RGB)
+ * COM Split Odd Even
+ * 256 color format
+ */
+ 2, SSD13XX_SET_SEG_REMAP, 0x20,
+ 2, SSD133X_SET_DISPLAY_START, 0x00,
+ 2, SSD133X_SET_DISPLAY_OFFSET, 0x00,
+ 1, SSD133X_SET_DISPLAY_NORMAL,
+ 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1,
+ 2, SSD133X_SET_MASTER_CONFIG, 0x8e,
+ 2, SSD133X_POWER_SAVE_MODE, 0x0b,
+ 2, SSD133X_PHASES_PERIOD, 0x31,
+ 2, SSD133X_SET_CLOCK_FREQ, 0xf0,
+ 2, SSD132X_SET_PRECHARGE_A, 0x64,
+ 2, SSD132X_SET_PRECHARGE_B, 0x78,
+ 2, SSD132X_SET_PRECHARGE_C, 0x64,
+ 2, SSD133X_SET_PRECHARGE_VOLTAGE, 0x3a,
+ 2, SSD133X_SET_VCOMH_VOLTAGE, 0x3e,
+ 0,
+ };
+
+ return ssd130x_run_cmd_seq(ssd130x, cmds);
}
static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c
index 50a878688e47..90a44e722057 100644
--- a/drivers/gpu/drm/stm/lvds.c
+++ b/drivers/gpu/drm/stm/lvds.c
@@ -1038,7 +1038,7 @@ static const struct drm_bridge_funcs lvds_bridge_funcs = {
.atomic_disable = lvds_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int lvds_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 74c7c3720ba8..8f64464621c9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -285,7 +285,7 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
static void sun4i_hdmi_connector_reset(struct drm_connector *connector)
{
drm_atomic_helper_connector_reset(connector);
- __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
+ __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state);
}
static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
diff --git a/drivers/gpu/drm/sysfb/Kconfig b/drivers/gpu/drm/sysfb/Kconfig
index 2559ead6cf1f..f7e48178885e 100644
--- a/drivers/gpu/drm/sysfb/Kconfig
+++ b/drivers/gpu/drm/sysfb/Kconfig
@@ -67,11 +67,8 @@ config DRM_SIMPLEDRM
This driver assumes that the display hardware has been initialized
by the firmware or bootloader before the kernel boots. Scanout
- buffer, size, and display format must be provided via device tree,
- UEFI, VESA, etc.
-
- On x86 BIOS or UEFI systems, you should also select SYSFB_SIMPLEFB
- to use UEFI and VESA framebuffers.
+ buffer, size, and display format must be provided via device tree's
+ simple-framebuffer node.
config DRM_VESADRM
tristate "VESA framebuffer driver"
diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c
index fc168920f2c6..9e0711e0095a 100644
--- a/drivers/gpu/drm/sysfb/simpledrm.c
+++ b/drivers/gpu/drm/sysfb/simpledrm.c
@@ -6,6 +6,7 @@
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/of_reserved_mem.h>
+#include <linux/overflow.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -41,20 +42,6 @@
* Helpers for simplefb
*/
-static int
-simplefb_get_validated_int(struct drm_device *dev, const char *name,
- uint32_t value)
-{
- return drm_sysfb_get_validated_int(dev, name, value, INT_MAX);
-}
-
-static int
-simplefb_get_validated_int0(struct drm_device *dev, const char *name,
- uint32_t value)
-{
- return drm_sysfb_get_validated_int0(dev, name, value, INT_MAX);
-}
-
static const struct drm_format_info *
simplefb_get_validated_format(struct drm_device *dev, const char *format_name)
{
@@ -88,21 +75,21 @@ static int
simplefb_get_width_pd(struct drm_device *dev,
const struct simplefb_platform_data *pd)
{
- return simplefb_get_validated_int0(dev, "width", pd->width);
+ return drm_sysfb_get_validated_int0(dev, "width", pd->width, U16_MAX);
}
static int
simplefb_get_height_pd(struct drm_device *dev,
const struct simplefb_platform_data *pd)
{
- return simplefb_get_validated_int0(dev, "height", pd->height);
+ return drm_sysfb_get_validated_int0(dev, "height", pd->height, U16_MAX);
}
static int
simplefb_get_stride_pd(struct drm_device *dev,
const struct simplefb_platform_data *pd)
{
- return simplefb_get_validated_int(dev, "stride", pd->stride);
+ return drm_sysfb_get_validated_int(dev, "stride", pd->stride, INT_MAX);
}
static const struct drm_format_info *
@@ -144,7 +131,7 @@ simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node)
if (ret)
return ret;
- return simplefb_get_validated_int0(dev, "width", width);
+ return drm_sysfb_get_validated_int0(dev, "width", width, U16_MAX);
}
static int
@@ -155,7 +142,7 @@ simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node)
if (ret)
return ret;
- return simplefb_get_validated_int0(dev, "height", height);
+ return drm_sysfb_get_validated_int0(dev, "height", height, U16_MAX);
}
static int
@@ -166,7 +153,7 @@ simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node)
if (ret)
return ret;
- return simplefb_get_validated_int(dev, "stride", stride);
+ return drm_sysfb_get_validated_int(dev, "stride", stride, INT_MAX);
}
static const struct drm_format_info *
@@ -200,6 +187,39 @@ simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node)
return res;
}
+static int __simplefb_get_panel_size_mm_of(struct drm_device *dev,
+ struct device_node *of_panel_node,
+ const char *name)
+{
+ int ret;
+ u32 value;
+
+ ret = of_property_read_u32(of_panel_node, name, &value);
+ if (ret) {
+ drm_dbg(dev, "simplefb: cannot parse panel %s: error %d\n",
+ name, ret);
+ return ret;
+ } else if (value > U16_MAX) {
+ drm_dbg(dev, "simplefb: panel %s of %u exceeds maximum value\n",
+ name, value);
+ return -EINVAL;
+ }
+
+ return value;
+}
+
+static int simplefb_get_panel_width_mm_of(struct drm_device *dev,
+ struct device_node *of_panel_node)
+{
+ return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "width-mm");
+}
+
+static int simplefb_get_panel_height_mm_of(struct drm_device *dev,
+ struct device_node *of_panel_node)
+{
+ return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "height-mm");
+}
+
/*
* Simple Framebuffer device
*/
@@ -601,9 +621,10 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
struct drm_sysfb_device *sysfb;
struct drm_device *dev;
int width, height, stride;
- int width_mm = 0, height_mm = 0;
+ u16 width_mm = 0, height_mm = 0;
struct device_node *panel_node;
const struct drm_format_info *format;
+ u64 size;
struct resource *res, *mem = NULL;
struct drm_plane *primary_plane;
struct drm_crtc *crtc;
@@ -665,8 +686,18 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
return ERR_CAST(mem);
panel_node = of_parse_phandle(of_node, "panel", 0);
if (panel_node) {
- simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm);
- simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm);
+ /*
+ * Ignore errors from parsing the physical panel
+ * size. Using the pre-initialized sizes of 0 will
+ * make drm_sysfb_mode() calculate a default physical
+ * size based on a resolution of 96 dpi.
+ */
+ ret = simplefb_get_panel_width_mm_of(dev, panel_node);
+ if (ret > 0)
+ width_mm = ret;
+ ret = simplefb_get_panel_height_mm_of(dev, panel_node);
+ if (ret > 0)
+ height_mm = ret;
of_node_put(panel_node);
}
} else {
@@ -674,9 +705,23 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
return ERR_PTR(-ENODEV);
}
if (!stride) {
- stride = drm_format_info_min_pitch(format, 0, width);
- if (drm_WARN_ON(dev, !stride))
+ u64 pitch = drm_format_info_min_pitch(format, 0, width);
+
+ if (drm_WARN_ON(dev, !pitch)) {
+ return ERR_PTR(-EINVAL); /* driver bug */
+ } else if (pitch > INT_MAX) {
+ drm_warn(dev, "stride of %llu exceeds maximum\n", pitch);
return ERR_PTR(-EINVAL);
+ }
+ stride = pitch;
+ }
+ if (check_mul_overflow(height, stride, &size)) {
+ drm_err(dev, "framebuffer size exceeds maximum\n");
+ return ERR_PTR(-EINVAL);
+ }
+ if (ALIGN(size, PAGE_SIZE) < PAGE_SIZE) {
+ drm_err(dev, "page-aligned framebuffer exceeds maximum\n");
+ return ERR_PTR(-EINVAL);
}
sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm);
@@ -703,6 +748,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
drm_dbg(dev, "using system memory framebuffer at %pr\n", mem);
+ if (size > resource_size(mem)) {
+ drm_err(dev,
+ "framebuffer size of %llu exceeds memory range %pr\n",
+ size, mem);
+ return ERR_PTR(-EINVAL);
+ }
+
screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC);
if (IS_ERR(screen_base))
return screen_base;
@@ -736,6 +788,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
mem = res;
}
+ if (size > resource_size(mem)) {
+ drm_err(dev,
+ "framebuffer size of %llu exceeds memory range %pr\n",
+ size, mem);
+ return ERR_PTR(-EINVAL);
+ }
+
screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
if (!screen_base)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index dd6c564ff408..24e8ef261836 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1358,10 +1358,12 @@ static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend,
static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra20-dc", },
+ { .compatible = "nvidia,tegra20-dsi", },
{ .compatible = "nvidia,tegra20-hdmi", },
{ .compatible = "nvidia,tegra20-gr2d", },
{ .compatible = "nvidia,tegra20-gr3d", },
{ .compatible = "nvidia,tegra30-dc", },
+ { .compatible = "nvidia,tegra30-dsi", },
{ .compatible = "nvidia,tegra30-hdmi", },
{ .compatible = "nvidia,tegra30-gr2d", },
{ .compatible = "nvidia,tegra30-gr3d", },
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 7f25c50621c9..e7fdd8c7ac12 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -54,6 +54,11 @@ to_dsi_state(struct drm_connector_state *state)
return container_of(state, struct tegra_dsi_state, base);
}
+struct tegra_dsi_config {
+ bool has_multiple_pad_controls;
+ bool has_mux_parent_clk;
+};
+
struct tegra_dsi {
struct host1x_client client;
struct tegra_output output;
@@ -83,6 +88,8 @@ struct tegra_dsi {
/* for ganged-mode support */
struct tegra_dsi *master;
struct tegra_dsi *slave;
+
+ const struct tegra_dsi_config *config;
};
static inline struct tegra_dsi *
@@ -665,39 +672,46 @@ static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
{
u32 value;
- value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
- tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
+ if (dsi->config->has_multiple_pad_controls) {
+ /*
+ * XXX Is this still needed? The module reset is deasserted right
+ * before this function is called.
+ */
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
+
+ value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
+
+ value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
+ DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
+ DSI_PAD_OUT_CLK(0x0);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
+
+ value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) |
+ DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3);
+ } else {
+ value = DSI_PAD_CONTROL_LPUPADJ(0x1) | DSI_PAD_CONTROL_LPDNADJ(0x1) |
+ DSI_PAD_CONTROL_PREEMP_EN(0x1) | DSI_PAD_CONTROL_SLEWDNADJ(0x6) |
+ DSI_PAD_CONTROL_SLEWUPADJ(0x6) | DSI_PAD_CONTROL_PDIO(0) |
+ DSI_PAD_CONTROL_PDIO_CLK(0) | DSI_PAD_CONTROL_PULLDN_ENAB(0);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
+ }
return 0;
}
static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
{
- u32 value;
int err;
- /*
- * XXX Is this still needed? The module reset is deasserted right
- * before this function is called.
- */
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
-
/* start calibration */
tegra_dsi_pad_enable(dsi);
- value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
- DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
- DSI_PAD_OUT_CLK(0x0);
- tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
-
- value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) |
- DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
- tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3);
-
err = tegra_mipi_start_calibration(dsi->mipi);
if (err < 0)
return err;
@@ -922,6 +936,15 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
return;
}
+ /* If the bootloader enabled DSI it needs to be disabled
+ * in order for the panel initialization commands to be
+ * properly sent.
+ */
+ value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
+
+ if (value & DSI_POWER_CONTROL_ENABLE)
+ tegra_dsi_disable(dsi);
+
state = tegra_dsi_get_state(dsi);
tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh);
@@ -1174,6 +1197,12 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
struct clk *parent;
int err;
+ /*
+ * Tegra124+ uses a clock gate, not a mux, so this step
+ * should be redundant for configuration; yet, DSI refuses
+ * to work without it.
+ */
+
parent = clk_get_parent(dsi->clk);
if (!parent)
return -EINVAL;
@@ -1562,6 +1591,10 @@ static int tegra_dsi_probe(struct platform_device *pdev)
if (!dsi)
return -ENOMEM;
+ dsi->config = device_get_match_data(&pdev->dev);
+ if (!dsi->config)
+ return -ENODEV;
+
dsi->output.dev = dsi->dev = &pdev->dev;
dsi->video_fifo_depth = 1920;
dsi->host_fifo_depth = 64;
@@ -1600,7 +1633,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)
goto remove;
}
- dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");
+ dsi->clk_lp = devm_clk_get_optional(&pdev->dev, "lp");
if (IS_ERR(dsi->clk_lp)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp),
"cannot get low-power clock\n");
@@ -1621,10 +1654,12 @@ static int tegra_dsi_probe(struct platform_device *pdev)
goto remove;
}
- err = tegra_dsi_setup_clocks(dsi);
- if (err < 0) {
- dev_err(&pdev->dev, "cannot setup clocks\n");
- goto remove;
+ if (dsi->config->has_mux_parent_clk) {
+ err = tegra_dsi_setup_clocks(dsi);
+ if (err < 0) {
+ dev_err(&pdev->dev, "cannot setup clocks\n");
+ goto remove;
+ }
}
dsi->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1688,11 +1723,40 @@ static void tegra_dsi_remove(struct platform_device *pdev)
tegra_mipi_free(dsi->mipi);
}
+static const struct tegra_dsi_config tegra20_dsi_config = {
+ .has_multiple_pad_controls = false,
+ .has_mux_parent_clk = false,
+};
+
+/*
+ * Tegra30 allows DSIA/DSIB to be muxed to either PLL_D or PLL_D2; this is
+ * simply not modeled in the clock driver yet. If this functionality is
+ * required, the has_mux_parent_clk flag can be set to true once the clock
+ * driver is patched.
+ */
+static const struct tegra_dsi_config tegra30_dsi_config = {
+ .has_multiple_pad_controls = false,
+ .has_mux_parent_clk = false,
+};
+
+static const struct tegra_dsi_config tegra114_dsi_config = {
+ .has_multiple_pad_controls = true,
+ .has_mux_parent_clk = true,
+};
+
+/* TODO: figure out why has_mux_parent_clk = true is necessary on Tegra124+ */
+static const struct tegra_dsi_config tegra124_dsi_config = {
+ .has_multiple_pad_controls = true,
+ .has_mux_parent_clk = true,
+};
+
static const struct of_device_id tegra_dsi_of_match[] = {
- { .compatible = "nvidia,tegra210-dsi", },
- { .compatible = "nvidia,tegra132-dsi", },
- { .compatible = "nvidia,tegra124-dsi", },
- { .compatible = "nvidia,tegra114-dsi", },
+ { .compatible = "nvidia,tegra210-dsi", .data = &tegra124_dsi_config },
+ { .compatible = "nvidia,tegra132-dsi", .data = &tegra124_dsi_config },
+ { .compatible = "nvidia,tegra124-dsi", .data = &tegra124_dsi_config },
+ { .compatible = "nvidia,tegra114-dsi", .data = &tegra114_dsi_config },
+ { .compatible = "nvidia,tegra30-dsi", .data = &tegra30_dsi_config },
+ { .compatible = "nvidia,tegra20-dsi", .data = &tegra20_dsi_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_dsi_of_match);
diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h
index f39594e65e97..d834ac0c47ab 100644
--- a/drivers/gpu/drm/tegra/dsi.h
+++ b/drivers/gpu/drm/tegra/dsi.h
@@ -95,6 +95,16 @@
#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8)
#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0)
#define DSI_PAD_CONTROL_0 0x4b
+/* Tegra20/Tegra30 */
+#define DSI_PAD_CONTROL_PULLDN_ENAB(x) (((x) & 0x1) << 28)
+#define DSI_PAD_CONTROL_SLEWUPADJ(x) (((x) & 0x7) << 24)
+#define DSI_PAD_CONTROL_SLEWDNADJ(x) (((x) & 0x7) << 20)
+#define DSI_PAD_CONTROL_PREEMP_EN(x) (((x) & 0x1) << 19)
+#define DSI_PAD_CONTROL_PDIO_CLK(x) (((x) & 0x1) << 18)
+#define DSI_PAD_CONTROL_PDIO(x) (((x) & 0x3) << 16)
+#define DSI_PAD_CONTROL_LPUPADJ(x) (((x) & 0x3) << 14)
+#define DSI_PAD_CONTROL_LPDNADJ(x) (((x) & 0x3) << 12)
+/* Tegra114+ */
#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
#define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8)
#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index 3b83e6616fb2..e7193b49cf91 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_probe_helper_test.o \
drm_rect_test.o \
drm_sysfb_modeset_test.o \
- drm_fixp_test.o
+ drm_fixp_test.o \
+ drm_kunit_edid.o
CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
index 64b665580a88..10a32620e962 100644
--- a/drivers/gpu/drm/tests/drm_bridge_test.c
+++ b/drivers/gpu/drm/tests/drm_bridge_test.c
@@ -2,15 +2,24 @@
/*
* Kunit test for drm_bridge functions
*/
+#include <linux/cleanup.h>
+#include <linux/media-bus-format.h>
+
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_atomic_uapi.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_bridge_helper.h>
+#include <drm/drm_edid.h>
#include <drm/drm_kunit_helpers.h>
+#include <drm/drm_managed.h>
#include <kunit/device.h>
#include <kunit/test.h>
+#include "drm_kunit_edid.h"
+
/*
* Mimick the typical "private" struct defined by a bridge driver, which
* embeds a bridge plus other fields.
@@ -37,6 +46,21 @@ struct drm_bridge_init_priv {
bool destroyed;
};
+struct drm_bridge_chain_priv {
+ struct drm_device drm;
+ struct drm_encoder encoder;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+ unsigned int num_bridges;
+
+ /**
+ * @test_bridges: array of pointers to &struct drm_bridge_priv entries
+ * of which the first @num_bridges entries are valid.
+ */
+ struct drm_bridge_priv **test_bridges;
+};
+
static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge)
{
return container_of(bridge, struct drm_bridge_priv, bridge);
@@ -92,9 +116,254 @@ static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = {
.atomic_disable = drm_test_bridge_atomic_disable,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+};
+
+static int dummy_clear_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int dummy_write_infoframe(struct drm_bridge *bridge, const u8 *buffer,
+ size_t len)
+{
+ return 0;
+}
+
+static const struct drm_bridge_funcs drm_test_bridge_bus_fmts_funcs = {
+ .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+ .hdmi_write_avi_infoframe = dummy_write_infoframe,
+ .hdmi_write_hdmi_infoframe = dummy_write_infoframe,
+ .hdmi_clear_avi_infoframe = dummy_clear_infoframe,
+ .hdmi_clear_hdmi_infoframe = dummy_clear_infoframe,
+};
+
+/**
+ * struct fmt_tuple - a tuple of input/output MEDIA_BUS_FMT_*
+ */
+struct fmt_tuple {
+ u32 in_fmt;
+ u32 out_fmt;
+};
+
+/*
+ * Format mapping that only accepts RGB888, and outputs only RGB888
+ */
+static const struct fmt_tuple rgb8_passthrough[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+};
+
+/*
+ * Format mapping that only accepts YUV444, and outputs only YUV444
+ */
+static const struct fmt_tuple yuv8_passthrough[] = {
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
};
+/*
+ * Format mapping where 8bpc RGB -> 8bpc YUV444, or ID(RGB) or ID(YUV444)
+ */
+static const struct fmt_tuple rgb8_to_yuv8_or_id[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+};
+
+static const struct fmt_tuple rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_UYVY8_1X16 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_UYYVYY8_0_5X24 },
+};
+
+/*
+ * Format mapping where 8bpc YUV444 -> 8bpc RGB, or ID(YUV444)
+ */
+static const struct fmt_tuple yuv8_to_rgb8_or_id[] = {
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+};
+
+/*
+ * A format mapping that acts like a video processor that generates an RGB signal
+ */
+static const struct fmt_tuple rgb_producer[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB101010_1X30 },
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB121212_1X36 },
+};
+
+/*
+ * A format mapping that acts like a video processor that generates an 8-bit RGB,
+ * YUV444 or YUV420 signal
+ */
+static const struct fmt_tuple rgb_yuv444_yuv420_producer[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_UYYVYY8_0_5X24 },
+};
+
+static const struct fmt_tuple rgb8_yuv444_yuv422_passthrough[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16 },
+};
+
+static const struct fmt_tuple yuv444_yuv422_rgb8_passthrough[] = {
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+};
+
+static bool fmt_in_list(const u32 fmt, const u32 *out_fmts, const size_t num_fmts)
+{
+ size_t i;
+
+ for (i = 0; i < num_fmts; i++)
+ if (out_fmts[i] == fmt)
+ return true;
+
+ return false;
+}
+
+/**
+ * get_tuples_out_fmts - Get unique output formats of a &struct fmt_tuple list
+ * @fmt_tuples: array of &struct fmt_tuple
+ * @num_fmt_tuples: number of entries in @fmt_tuples
+ * @out_fmts: target array to store the unique output bus formats
+ *
+ * Returns the number of unique output formats, i.e. the number of entries in
+ * @out_fmts that were populated with sensible values.
+ */
+static size_t get_tuples_out_fmts(const struct fmt_tuple *fmt_tuples,
+ const size_t num_fmt_tuples, u32 *out_fmts)
+{
+ size_t num_unique = 0;
+ size_t i;
+
+ for (i = 0; i < num_fmt_tuples; i++)
+ if (!fmt_in_list(fmt_tuples[i].out_fmt, out_fmts, num_unique))
+ out_fmts[num_unique++] = fmt_tuples[i].out_fmt;
+
+ return num_unique;
+}
+
+#define DEFINE_FMT_FUNCS_FROM_TUPLES(name) \
+static u32 *drm_test_bridge_ ## name ## _out_fmts(struct drm_bridge *bridge, \
+ struct drm_bridge_state *bridge_state, \
+ struct drm_crtc_state *crtc_state, \
+ struct drm_connector_state *conn_state, \
+ unsigned int *num_output_fmts) \
+{ \
+ u32 *out_fmts = kcalloc(ARRAY_SIZE((name)), sizeof(u32), GFP_KERNEL); \
+ \
+ if (out_fmts) \
+ *num_output_fmts = get_tuples_out_fmts((name), ARRAY_SIZE((name)), out_fmts); \
+ else \
+ *num_output_fmts = 0; \
+ \
+ return out_fmts; \
+} \
+ \
+static u32 *drm_test_bridge_ ## name ## _in_fmts(struct drm_bridge *bridge, \
+ struct drm_bridge_state *bridge_state, \
+ struct drm_crtc_state *crtc_state, \
+ struct drm_connector_state *conn_state, \
+ u32 output_fmt, \
+ unsigned int *num_input_fmts) \
+{ \
+ u32 *in_fmts = kcalloc(ARRAY_SIZE((name)), sizeof(u32), GFP_KERNEL); \
+ unsigned int num_fmts = 0; \
+ size_t i; \
+ \
+ if (!in_fmts) { \
+ *num_input_fmts = 0; \
+ return NULL; \
+ } \
+ \
+ for (i = 0; i < ARRAY_SIZE((name)); i++) \
+ if ((name)[i].out_fmt == output_fmt) \
+ in_fmts[num_fmts++] = (name)[i].in_fmt; \
+ \
+ *num_input_fmts = num_fmts; \
+ \
+ return in_fmts; \
+}
+
+#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(ident, input_fmts_func, output_fmts_func, \
+ hdmi_write_infoframe_func, \
+ hdmi_clear_infoframe_func) \
+static const struct drm_bridge_funcs (ident) = { \
+ .atomic_enable = drm_test_bridge_atomic_enable, \
+ .atomic_disable = drm_test_bridge_atomic_disable, \
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, \
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, \
+ .atomic_create_state = drm_atomic_helper_bridge_create_state, \
+ .atomic_get_input_bus_fmts = (input_fmts_func), \
+ .atomic_get_output_bus_fmts = (output_fmts_func), \
+ .hdmi_write_avi_infoframe = (hdmi_write_infoframe_func), \
+ .hdmi_clear_avi_infoframe = (hdmi_clear_infoframe_func), \
+ .hdmi_write_hdmi_infoframe = (hdmi_write_infoframe_func), \
+ .hdmi_clear_hdmi_infoframe = (hdmi_clear_infoframe_func), \
+}
+
+#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_FUNC(ident, input_fmts_func, output_fmts_func) \
+ DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(ident, input_fmts_func, output_fmts_func, \
+ NULL, NULL)
+
+#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(_name) \
+ DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_FUNC(_name ## _funcs, \
+ drm_test_bridge_ ## _name ## _in_fmts, \
+ drm_test_bridge_ ## _name ## _out_fmts)
+
+static int drm_test_bridge_write_infoframe_stub(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ return 0;
+}
+
+static int drm_test_bridge_clear_infoframe_stub(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI(_name) \
+ DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(_name ## _hdmi ## _funcs, \
+ drm_test_bridge_ ## _name ## _in_fmts, \
+ drm_test_bridge_ ## _name ## _out_fmts, \
+ drm_test_bridge_write_infoframe_stub, \
+ drm_test_bridge_clear_infoframe_stub)
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_passthrough)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_passthrough);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(yuv8_passthrough)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv8_passthrough);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_to_yuv8_or_id)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_to_yuv8_or_id);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(yuv8_to_rgb8_or_id)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv8_to_rgb8_or_id);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb_producer)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb_producer);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb_yuv444_yuv420_producer)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb_yuv444_yuv420_producer);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_yuv444_yuv422_passthrough)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_yuv444_yuv422_passthrough);
+
+DEFINE_FMT_FUNCS_FROM_TUPLES(yuv444_yuv422_rgb8_passthrough)
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv444_yuv422_rgb8_passthrough);
+DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI(yuv444_yuv422_rgb8_passthrough);
+
KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper,
drm_bridge_remove,
struct drm_bridge *);
@@ -178,6 +447,196 @@ drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs)
return priv;
}
+static struct drm_bridge_chain_priv *
+drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges,
+ const struct drm_bridge_funcs **funcs)
+{
+ struct drm_bridge_chain_priv *priv;
+ const struct drm_edid *edid;
+ struct drm_bridge *prev;
+ struct drm_encoder *enc;
+ struct drm_bridge *bridge;
+ struct drm_device *drm;
+ bool has_hdmi = false;
+ struct device *dev;
+ unsigned int i;
+ int ret;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+
+ priv = drm_kunit_helper_alloc_drm_device(test, dev, struct drm_bridge_chain_priv,
+ drm, DRIVER_MODESET | DRIVER_ATOMIC);
+ if (IS_ERR(priv))
+ return ERR_CAST(priv);
+
+ drm = &priv->drm;
+
+ priv->test_bridges = drmm_kmalloc_array(drm, num_bridges, sizeof(*priv->test_bridges),
+ GFP_KERNEL);
+ if (!priv->test_bridges)
+ return ERR_PTR(-ENOMEM);
+
+ priv->num_bridges = num_bridges;
+
+ for (i = 0; i < num_bridges; i++) {
+ priv->test_bridges[i] = devm_drm_bridge_alloc(dev, struct drm_bridge_priv,
+ bridge, funcs[i]);
+ if (IS_ERR(priv->test_bridges[i]))
+ return ERR_CAST(priv->test_bridges[i]);
+
+ priv->test_bridges[i]->data = priv;
+ }
+
+ priv->plane = drm_kunit_helper_create_primary_plane(test, drm, NULL, NULL,
+ NULL, 0, NULL);
+ if (IS_ERR(priv->plane))
+ return ERR_CAST(priv->plane);
+
+ priv->crtc = drm_kunit_helper_create_crtc(test, drm, priv->plane, NULL,
+ NULL, NULL);
+ if (IS_ERR(priv->crtc))
+ return ERR_CAST(priv->crtc);
+
+ enc = &priv->encoder;
+ ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ enc->possible_crtcs = drm_crtc_mask(priv->crtc);
+
+ prev = NULL;
+ for (i = 0; i < num_bridges; i++) {
+ bridge = &priv->test_bridges[i]->bridge;
+ bridge->type = DRM_MODE_CONNECTOR_VIRTUAL;
+
+ if (bridge->funcs->hdmi_write_hdmi_infoframe) {
+ has_hdmi = true;
+ bridge->ops |= DRM_BRIDGE_OP_HDMI;
+ bridge->type = DRM_MODE_CONNECTOR_HDMIA;
+ bridge->vendor = "LNX";
+ bridge->product = "KUnit";
+ bridge->supported_formats = (BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420));
+ }
+
+ ret = drm_kunit_bridge_add(test, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_bridge_attach(enc, bridge, prev, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ prev = bridge;
+ }
+
+ priv->connector = drm_bridge_connector_init(drm, enc);
+ if (IS_ERR(priv->connector))
+ return ERR_CAST(priv->connector);
+
+ drm_connector_attach_encoder(priv->connector, enc);
+
+ drm_mode_config_reset(drm);
+
+ if (!has_hdmi)
+ return priv;
+
+ scoped_guard(mutex, &drm->mode_config.mutex) {
+ edid = drm_edid_alloc(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz));
+ if (!edid)
+ return ERR_PTR(-EINVAL);
+
+ drm_edid_connector_update(priv->connector, edid);
+ KUNIT_ASSERT_GT(test, drm_edid_connector_add_modes(priv->connector), 0);
+
+ ret = priv->connector->funcs->fill_modes(priv->connector, 4096, 4096);
+ }
+
+ return priv;
+}
+
+static struct drm_bridge_init_priv *
+drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs,
+ unsigned int supported_formats, int max_bpc)
+{
+ struct drm_bridge_init_priv *priv;
+ struct drm_encoder *enc;
+ struct drm_bridge *bridge;
+ struct drm_device *drm;
+ struct device *dev;
+ int ret;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+
+ priv = drm_kunit_helper_alloc_drm_device(test, dev,
+ struct drm_bridge_init_priv, drm,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ if (IS_ERR(priv))
+ return ERR_CAST(priv);
+
+ priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs);
+ if (IS_ERR(priv->test_bridge))
+ return ERR_CAST(priv->test_bridge);
+
+ priv->test_bridge->data = priv;
+
+ drm = &priv->drm;
+ priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
+ NULL,
+ NULL,
+ NULL, 0,
+ NULL);
+ if (IS_ERR(priv->plane))
+ return ERR_CAST(priv->plane);
+
+ priv->crtc = drm_kunit_helper_create_crtc(test, drm,
+ priv->plane, NULL,
+ NULL,
+ NULL);
+ if (IS_ERR(priv->crtc))
+ return ERR_CAST(priv->crtc);
+
+ enc = &priv->encoder;
+ ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ enc->possible_crtcs = drm_crtc_mask(priv->crtc);
+
+ bridge = &priv->test_bridge->bridge;
+ bridge->type = DRM_MODE_CONNECTOR_HDMIA;
+ bridge->supported_formats = supported_formats;
+ bridge->max_bpc = max_bpc;
+ bridge->ops |= DRM_BRIDGE_OP_HDMI;
+ bridge->vendor = "LNX";
+ bridge->product = "KUnit";
+
+ ret = drm_kunit_bridge_add(test, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_bridge_attach(enc, bridge, NULL, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ priv->connector = drm_bridge_connector_init(drm, enc);
+ if (IS_ERR(priv->connector))
+ return ERR_CAST(priv->connector);
+
+ drm_connector_attach_encoder(priv->connector, enc);
+
+ drm_mode_config_reset(drm);
+
+ return priv;
+}
+
/*
* Test that drm_bridge_get_current_state() returns the last committed
* state for an atomic bridge.
@@ -425,10 +884,94 @@ retry_reset:
KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
}
+/*
+ * Test that a bridge using the drm_atomic_helper_bridge_get_hdmi_output_bus_fmts()
+ * function for &drm_bridge_funcs.atomic_get_output_bus_fmts behaves as expected
+ * for an HDMI connector bridge. Does so by creating an HDMI bridge connector
+ * with RGB444, YCBCR444, and YCBCR420 (but not YCBCR422) as supported formats,
+ * sets the output depth to 8 bits per component, and then validates the returned
+ * list of bus formats.
+ */
+static void drm_test_drm_bridge_helper_hdmi_output_bus_fmts(struct kunit *test)
+{
+ struct drm_connector_state *conn_state;
+ struct drm_bridge_state *bridge_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_init_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ unsigned int num_output_fmts;
+ struct drm_bridge *bridge;
+ u32 *out_bus_fmts;
+ int ret;
+
+ priv = drm_test_bridge_hdmi_init(test, &drm_test_bridge_bus_fmts_funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420),
+ 12);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ bridge = &priv->test_bridge->bridge;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ conn_state->hdmi.output_bpc = 8;
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ bridge_state = drm_atomic_get_bridge_state(state, bridge);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state);
+
+ out_bus_fmts = bridge->funcs->atomic_get_output_bus_fmts(
+ bridge, bridge_state, crtc_state, conn_state, &num_output_fmts);
+ KUNIT_EXPECT_NOT_NULL(test, out_bus_fmts);
+ KUNIT_EXPECT_EQ(test, num_output_fmts, 3);
+
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[0], MEDIA_BUS_FMT_RGB888_1X24);
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[1], MEDIA_BUS_FMT_YUV8_1X24);
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[2], MEDIA_BUS_FMT_UYYVYY8_0_5X24);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ kfree(out_bus_fmts);
+}
+
static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
+ KUNIT_CASE(drm_test_drm_bridge_helper_hdmi_output_bus_fmts),
{ }
};
@@ -506,14 +1049,442 @@ static struct kunit_suite drm_bridge_alloc_test_suite = {
.test_cases = drm_bridge_alloc_tests,
};
+/**
+ * drm_test_bridge_chain_verify_fmt - Verify bridge chain format selection
+ * @test: pointer to KUnit test object
+ * @priv: pointer to a &struct drm_bridge_chain_priv for this chain
+ * @expected: constant array of &struct fmt_tuple describing the expected
+ * input and output bus formats
+ * @num_expected: number of entries in @expected
+ *
+ * Runs the KUNIT_EXPECT clauses to verify the bridge chain format selection
+ * resulted in the expected formats. If %0 is given as a format in a
+ * &struct fmt_tuple, then it is understood to mean "any".
+ *
+ * Must be called with the modeset lock held.
+ */
+static void drm_test_bridge_chain_verify_fmt(struct kunit *test,
+ struct drm_bridge_chain_priv *priv,
+ const struct fmt_tuple *const expected,
+ const unsigned int num_expected)
+{
+ struct drm_bridge_state *bstate;
+ unsigned int i = 0;
+
+ drm_for_each_bridge_in_chain_scoped(&priv->encoder, bridge) {
+ KUNIT_ASSERT_LT(test, i, num_expected);
+
+ bstate = drm_bridge_get_current_state(bridge);
+ if (expected[i].in_fmt)
+ KUNIT_EXPECT_EQ(test, bstate->input_bus_cfg.format,
+ expected[i].in_fmt);
+ if (expected[i].out_fmt)
+ KUNIT_EXPECT_EQ(test, bstate->output_bus_cfg.format,
+ expected[i].out_fmt);
+
+ i++;
+ }
+
+ KUNIT_ASSERT_EQ_MSG(test, i, num_expected,
+ "Fewer bridges (%u) than expected (%u)\n", i, num_expected);
+}
+
+/*
+ * Test that constructs a bridge chain in which an RGB888 producer is chained to
+ * two bridges that will convert from RGB to YUV and from YUV to RGB respectively.
+ *
+ * The test requests an output color_format of RGB using the color_format property,
+ * so to satisfy this request, the bridge chain must take a detour over YUV.
+ */
+static void drm_test_bridge_rgb_yuv_rgb(struct kunit *test)
+{
+ static const struct drm_bridge_funcs *funcs[] = {
+ &rgb_producer_funcs,
+ &rgb8_to_yuv8_or_id_funcs,
+ &yuv8_to_rgb8_or_id_funcs,
+ };
+ static const struct fmt_tuple expected[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ };
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_chain_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ conn_state->color_format = DRM_CONNECTOR_COLOR_FORMAT_RGB444;
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected));
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test in which a bridge capable of producing RGB, YUV444 and YUV420 has to
+ * produce RGB and convert with a downstream bridge in the chain to reach the
+ * requested YUV444 color format, as no direct path exists between its YUV444
+ * and the last bridge.
+ *
+ * The rationale behind this test is to devise a scenario in which naively
+ * assuming any format the video processor can output, and the connector
+ * requests, is the right format to pick, does not work.
+ */
+static void drm_test_bridge_must_convert_to_yuv444(struct kunit *test)
+{
+ static const struct drm_bridge_funcs *funcs[] = {
+ &rgb_yuv444_yuv420_producer_funcs,
+ &rgb8_passthrough_funcs,
+ &rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420_funcs,
+ &rgb8_yuv444_yuv422_passthrough_funcs,
+ };
+ static const struct fmt_tuple expected[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ };
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_chain_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ conn_state->color_format = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444;
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected));
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test which checks that no matter the order of bus formats returned by an
+ * HDMI bridge, RGB is preferred on DRM_CONNECTOR_COLOR_FORMAT_AUTO if it's
+ * available.
+ */
+static void drm_test_bridge_hdmi_auto_rgb(struct kunit *test)
+{
+ static const struct drm_bridge_funcs *funcs[] = {
+ &rgb_yuv444_yuv420_producer_funcs,
+ &yuv444_yuv422_rgb8_passthrough_hdmi_funcs,
+ };
+ static const struct fmt_tuple expected[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 },
+ };
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_chain_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444);
+
+ drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected));
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test which checks that DRM_CONNECTOR_COLOR_FORMAT_AUTO on non-HDMI connectors
+ * will result in the first bus format on the output to be picked.
+ */
+static void drm_test_bridge_auto_first(struct kunit *test)
+{
+ static const struct drm_bridge_funcs *funcs[] = {
+ &rgb_yuv444_yuv420_producer_funcs,
+ &yuv444_yuv422_rgb8_passthrough_funcs,
+ };
+ static const struct fmt_tuple expected[] = {
+ { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_YUV8_1X24 },
+ { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 },
+ };
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_chain_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected));
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test which checks that in a configuration of bridge chains where an RGB
+ * producer is hooked to a YUV444-only pass-through, the atomic commit fails as
+ * the bridge format selection cannot find a valid sequence of bus formats.
+ */
+static void drm_test_bridge_rgb_yuv_no_path(struct kunit *test)
+{
+ static const struct drm_bridge_funcs *funcs[] = {
+ &rgb_producer_funcs,
+ &yuv8_passthrough_funcs,
+ &rgb8_yuv444_yuv422_passthrough_funcs,
+ };
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_chain_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_EXPECT_EQ(test, ret, -ENOTSUPP);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+static struct kunit_case drm_bridge_bus_fmt_tests[] = {
+ KUNIT_CASE(drm_test_bridge_rgb_yuv_rgb),
+ KUNIT_CASE(drm_test_bridge_must_convert_to_yuv444),
+ KUNIT_CASE(drm_test_bridge_hdmi_auto_rgb),
+ KUNIT_CASE(drm_test_bridge_auto_first),
+ KUNIT_CASE(drm_test_bridge_rgb_yuv_no_path),
+ { }
+};
+
+static struct kunit_suite drm_bridge_bus_fmt_test_suite = {
+ .name = "drm_bridge_bus_fmt",
+ .test_cases = drm_bridge_bus_fmt_tests,
+};
+
kunit_test_suites(
&drm_bridge_get_current_state_test_suite,
&drm_bridge_helper_reset_crtc_test_suite,
&drm_bridge_alloc_test_suite,
+ &drm_bridge_bus_fmt_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index c9819c3fc635..353a261d42da 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -60,6 +60,40 @@ static struct drm_display_mode *find_preferred_mode(struct drm_connector *connec
return preferred;
}
+static struct drm_display_mode *find_420_only_mode(struct drm_connector *connector)
+{
+ struct drm_device *drm = connector->dev;
+ struct drm_display_mode *mode;
+
+ mutex_lock(&drm->mode_config.mutex);
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_is_420_only(&connector->display_info, mode)) {
+ mutex_unlock(&drm->mode_config.mutex);
+ return mode;
+ }
+ }
+ mutex_unlock(&drm->mode_config.mutex);
+
+ return NULL;
+}
+
+static struct drm_display_mode *find_420_also_mode(struct drm_connector *connector)
+{
+ struct drm_device *drm = connector->dev;
+ struct drm_display_mode *mode;
+
+ mutex_lock(&drm->mode_config.mutex);
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_is_420_also(&connector->display_info, mode)) {
+ mutex_unlock(&drm->mode_config.mutex);
+ return mode;
+ }
+ }
+ mutex_unlock(&drm->mode_config.mutex);
+
+ return NULL;
+}
+
static int set_connector_edid(struct kunit *test, struct drm_connector *connector,
const void *edid, size_t edid_len)
{
@@ -168,7 +202,7 @@ static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = {
static void dummy_hdmi_connector_reset(struct drm_connector *connector)
{
drm_atomic_helper_connector_reset(connector);
- __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
+ __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state);
}
static const struct drm_connector_funcs dummy_connector_funcs = {
@@ -1547,6 +1581,7 @@ retry_conn_enable:
* RGB/10bpc
* - The chosen mode has a TMDS character rate lower than the display
* supports in YUV422/12bpc.
+ * - The HDMI connector state's color format property is unset (i.e. AUTO)
*
* Then we will prefer to keep the RGB format with a lower bpc over
* picking YUV422.
@@ -1609,6 +1644,7 @@ retry_conn_enable:
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
+ KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444);
@@ -1626,6 +1662,7 @@ retry_conn_enable:
* RGB/8bpc
* - The chosen mode has a TMDS character rate lower than the display
* supports in YUV420/12bpc.
+ * - The HDMI connector state's color format property is unset (i.e. AUTO)
*
* Then we will prefer to keep the RGB format with a lower bpc over
* picking YUV420.
@@ -1687,6 +1724,7 @@ retry_conn_enable:
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
+ KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444);
@@ -2198,6 +2236,217 @@ retry_conn_enable:
drm_modeset_acquire_fini(&ctx);
}
+struct color_format_test_param {
+ enum drm_connector_color_format fmt;
+ enum drm_output_color_format expected;
+ int expected_ret;
+ const char *desc;
+};
+
+/* Test that if:
+ * - an HDMI connector supports RGB, YUV444, YUV422, and YUV420
+ * - the display supports RGB, YUV444, YUV422, and YUV420
+ * - the "color format" property is set
+ * then, for the preferred mode, for a given "color format" option:
+ * - DRM_CONNECTOR_COLOR_FORMAT_AUTO results in an output format of RGB
+ * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR422 results in an output format of YUV422
+ * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR420 results in an output format of YUV420
+ * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 results in an output format of YUV444
+ * - DRM_CONNECTOR_COLOR_FORMAT_RGB results in an HDMI output format of RGB
+ */
+static void drm_test_check_hdmi_color_format(struct kunit *test)
+{
+ const struct color_format_test_param *param = test->param_value;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_info *info;
+ struct drm_display_mode *preferred;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
+ 12,
+ &dummy_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ KUNIT_ASSERT_TRUE(test, priv->connector.ycbcr_420_allowed);
+
+ info = &priv->connector.display_info;
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info);
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_TRUE(test, drm_mode_is_420(info, preferred));
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ conn_state = drm_atomic_get_connector_state(state, &priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ conn_state->color_format = param->fmt;
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, preferred);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_EXPECT_EQ(test, ret, param->expected_ret);
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, param->expected);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+static const struct color_format_test_param hdmi_color_format_params[] = {
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_AUTO,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444,
+ .expected_ret = 0,
+ .desc = "AUTO -> RGB"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR422,
+ .expected_ret = 0,
+ .desc = "YCBCR422 -> YUV422"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR420,
+ .expected_ret = 0,
+ .desc = "YCBCR420 -> YUV420"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR444,
+ .expected_ret = 0,
+ .desc = "YCBCR444 -> YUV444"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_RGB444,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444,
+ .expected_ret = 0,
+ .desc = "RGB -> RGB"
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(check_hdmi_color_format, hdmi_color_format_params, desc);
+
+/* Test that if:
+ * - the HDMI connector supports RGB, YUV422, YUV420, and YUV444
+ * - the display has a YUV420-only mode
+ * - the "color format" property is explicitly set (i.e. !AUTO)
+ * then:
+ * - color format DRM_CONNECTOR_COLOR_FORMAT_RGB444 will fail
+ * drm_atomic_check_only for the YUV420-only mode with -EINVAL
+ * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 will fail
+ * drm_atomic_check_only for the YUV420-only mode with -EINVAL
+ * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR422 will fail
+ * drm_atomic_check_only for the YUV420-only mode with -EINVAL
+ * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR420 passes
+ * drm_atomic_check_only for the YUV420-only mode
+ */
+static void drm_test_check_hdmi_color_format_420_only(struct kunit *test)
+{
+ const struct color_format_test_param *param = test->param_value;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *dank;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
+ 12,
+ &dummy_connector_hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ dank = find_420_only_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, dank);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ conn_state = drm_atomic_get_connector_state(state, &priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ conn_state->color_format = param->fmt;
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, dank);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_EXPECT_EQ(test, ret, param->expected_ret);
+ if (!param->expected_ret)
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, param->expected);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+};
+
+static const struct color_format_test_param hdmi_color_format_420_only_params[] = {
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_RGB444,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444,
+ .expected_ret = -EINVAL,
+ .desc = "RGB should fail"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR444,
+ .expected_ret = -EINVAL,
+ .desc = "YUV444 should fail"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR422,
+ .expected_ret = -EINVAL,
+ .desc = "YUV422 should fail"
+ },
+ {
+ .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420,
+ .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR420,
+ .expected_ret = 0,
+ .desc = "YUV420 should work"
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(check_hdmi_color_format_420_only,
+ hdmi_color_format_420_only_params, desc);
+
static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode),
KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode_vic_1),
@@ -2227,6 +2476,10 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc),
+ KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format,
+ check_hdmi_color_format_gen_params),
+ KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format_420_only,
+ check_hdmi_color_format_420_only_gen_params),
/*
* TODO: We should have tests to check that a change in the
* format triggers a CRTC mode change just like we do for the
@@ -2509,11 +2762,103 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
KUNIT_EXPECT_EQ(test, preferred->clock, 25200);
}
+/*
+ * Test that drm_hdmi_connector_mode_valid() will accept modes that require a
+ * 4:2:0 chroma subsampling, even if said mode would violate maximum clock
+ * constraints if it used RGB 4:4:4.
+ */
+static void drm_test_check_mode_valid_yuv420_only_max_clock(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *dank;
+ struct drm_connector *conn;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ BIT(HDMI_COLORSPACE_RGB) |
+ BIT(HDMI_COLORSPACE_YUV420),
+ 8,
+ &dummy_connector_hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 200 * 1000);
+
+ dank = find_420_only_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, dank);
+ KUNIT_EXPECT_EQ(test, dank->hdisplay, 3840);
+ KUNIT_EXPECT_EQ(test, dank->vdisplay, 2160);
+
+ /*
+ * Note: The mode's "clock" here is not accurate to the actual TMDS
+ * clock that HDMI will use for a subsampled mode. Hence, why the mode's
+ * clock is above the .max_tmds_clock of 200MHz.
+ */
+ KUNIT_EXPECT_EQ(test, dank->clock, 297000);
+}
+
+/*
+ * Test that drm_hdmi_connector_mode_valid() will reject modes that require
+ * 4:2:0 chroma subsampling, if the connector does not support 4:2:0.
+ */
+static void
+drm_test_check_mode_valid_reject_yuv420_only_connector(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *dank;
+ struct drm_connector *conn;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8,
+ &dummy_connector_hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 200 * 1000);
+
+ dank = find_420_only_mode(conn);
+ KUNIT_EXPECT_NULL(test, dank);
+}
+
+/*
+ * Test that drm_hdmi_connector_mode_valid() will accept modes that allow (among
+ * other color formats) 4:2:0 chroma subsampling, even if the connector does not
+ * support 4:2:0, but the mode's clock works for RGB 4:4:4.
+ */
+static void
+drm_test_check_mode_valid_accept_yuv420_also_connector_rgb(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *mode;
+ struct drm_connector *conn;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8,
+ &dummy_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 340 * 1000);
+
+ mode = find_420_also_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 3840);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 2160);
+ KUNIT_EXPECT_EQ(test, mode->clock, 297000);
+}
+
static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = {
KUNIT_CASE(drm_test_check_mode_valid),
KUNIT_CASE(drm_test_check_mode_valid_reject),
KUNIT_CASE(drm_test_check_mode_valid_reject_rate),
KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock),
+ KUNIT_CASE(drm_test_check_mode_valid_yuv420_only_max_clock),
+ KUNIT_CASE(drm_test_check_mode_valid_reject_yuv420_only_connector),
+ KUNIT_CASE(drm_test_check_mode_valid_accept_yuv420_also_connector_rgb),
{ }
};
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.c b/drivers/gpu/drm/tests/drm_kunit_edid.c
new file mode 100644
index 000000000000..c807ae0d9faa
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.c
@@ -0,0 +1,995 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+
+#include "drm_kunit_edid.h"
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * RGB color display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II: none
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Checksum: 0xab
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_dvi_1080p[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab
+};
+EXPORT_SYMBOL(test_edid_dvi_1080p);
+
+/*
+ *
+ * This edid is intentionally broken with the 100MHz limit. It's meant
+ * to be used only with tests in unusual situations.
+ *
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
+ *
+ * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
+ * 00 12 34 00 14 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * Monochrome or grayscale display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x92
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: No Data
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * Maximum TMDS clock: 100 MHz
+ * Checksum: 0x10 Unused space in Extension Block: 106 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * Failures:
+ *
+ * EDID:
+ * CTA-861: The maximum HDMI TMDS clock is 100000 kHz, but one or more video timings go up to 148500 kHz.
+ *
+ * EDID conformity: FAIL
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_100mhz);
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
+ *
+ * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
+ * 00 12 34 00 28 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * Monochrome or grayscale display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x92
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: No Data
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * Maximum TMDS clock: 200 MHz
+ * Checksum: 0xfc Unused space in Extension Block: 106 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_200mhz);
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
+ *
+ * 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c
+ * 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * Monochrome or grayscale display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x92
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * BT2020YCC
+ * BT2020RGB
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: No Data
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * Maximum TMDS clock: 200 MHz
+ * HDR Static Metadata Data Block:
+ * Electro optical transfer functions:
+ * Traditional gamma - SDR luminance range
+ * SMPTE ST2084
+ * Supported static metadata descriptors:
+ * Static metadata type 1
+ * Desired content max luminance: 82 (295.365 cd/m^2)
+ * Desired content max frame-average luminance: 82 (295.365 cd/m^2)
+ * Desired content min luminance: 81 (0.298 cd/m^2)
+ * Checksum: 0x4e Unused space in Extension Block: 99 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.31.0-5387
+ * edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81,
+ 0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xd6,
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
+ *
+ * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
+ * 00 12 34 00 44 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * Monochrome or grayscale display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x92
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: No Data
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * Maximum TMDS clock: 340 MHz
+ * Checksum: 0xe0 Unused space in Extension Block: 106 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe0
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_340mhz);
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a
+ *
+ * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
+ * 00 12 34 78 28 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d4
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * Undefined display color type
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x7a
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: Selectable (via AVI YQ)
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 200 MHz
+ * Checksum: 0xd4 Unused space in Extension Block: 106 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x15, 0xb1,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x78, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xd4
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
+
+/*
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
+ * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
+ * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
+ * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a
+ *
+ * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
+ * 00 12 34 78 44 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 42
+ * Made in: 2023
+ * Basic Display Parameters & Features:
+ * Digital display
+ * DFP 1.x compatible TMDS
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * RGB color display
+ * First detailed timing is the preferred timing
+ * Color Characteristics:
+ * Red : 0.0000, 0.0000
+ * Green: 0.0000, 0.0000
+ * Blue : 0.0000, 0.0000
+ * White: 0.0000, 0.0000
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0x8a
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * Native detailed modes: 1
+ * Colorimetry Data Block:
+ * sRGB
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
+ * Video Capability Data Block:
+ * YCbCr quantization: Selectable (via AVI YQ)
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.2.3.4
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 340 MHz
+ * Checksum: 0xb8 Unused space in Extension Block: 106 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.30.0-5367
+ * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+ 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x15, 0xb1,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x78, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xb8
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
+
+/*
+ * Max resolution:
+ * - 1920x1080@60Hz with RGB, YUV444, YUV422
+ * - 3840x2160@30Hz with YUV420 only
+ * Max BPC: 16 for all modes
+ * Max TMDS clock: 200 MHz
+ *
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
+ * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
+ * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+ * 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
+ * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb
+ *
+ * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20
+ * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00
+ * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 52
+ * Model year: 2025
+ * Basic Display Parameters & Features:
+ * Digital display
+ * Maximum image size: 96 cm x 54 cm
+ * Gamma: 2.20
+ * RGB color display
+ * Default (sRGB) color space is primary color space
+ * First detailed timing is the preferred timing
+ * Supports GTF timings within operating range
+ * Color Characteristics:
+ * Red : 0.6396, 0.3300
+ * Green: 0.2998, 0.5996
+ * Blue : 0.1503, 0.0595
+ * White: 0.3125, 0.3291
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm)
+ * Hfront 88 Hsync 44 Hback 148 Hpol P
+ * Vfront 4 Vsync 5 Vback 36 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0xbb
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * Native detailed modes: 1
+ * Video Data Block:
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native)
+ * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.0.0.0
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 200 MHz
+ * Extended HDMI video details:
+ * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
+ * Version: 1
+ * Maximum TMDS Character Rate: 200 MHz
+ * SCDC Present
+ * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
+ * YCbCr 4:2:0 Capability Map Data Block:
+ * Empty Capability Map
+ * YCbCr 4:2:0 Video Data Block:
+ * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
+ * Checksum: 0xca
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+ 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31,
+ 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20,
+ 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e,
+ 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xca
+};
+EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
+
+/*
+ * Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420
+ * Max BPC: 16 for all modes
+ * Max TMDS clock: 340 MHz
+ *
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
+ * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
+ * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58
+ * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
+ * 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce
+ *
+ * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00
+ * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00
+ * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 52
+ * Model year: 2025
+ * Basic Display Parameters & Features:
+ * Digital display
+ * Maximum image size: 96 cm x 54 cm
+ * Gamma: 2.20
+ * RGB color display
+ * Default (sRGB) color space is primary color space
+ * First detailed timing is the preferred timing
+ * Supports GTF timings within operating range
+ * Color Characteristics:
+ * Red : 0.6396, 0.3300
+ * Green: 0.2998, 0.5996
+ * Blue : 0.1503, 0.0595
+ * White: 0.3125, 0.3291
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm)
+ * Hfront 176 Hsync 88 Hback 296 Hpol P
+ * Vfront 8 Vsync 10 Vback 72 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0xce
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * Native detailed modes: 1
+ * Video Data Block:
+ * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.0.0.0
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 340 MHz
+ * Extended HDMI video details:
+ * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
+ * Version: 1
+ * Maximum TMDS Character Rate: 340 MHz
+ * SCDC Present
+ * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
+ * YCbCr 4:2:0 Capability Map Data Block:
+ * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
+ * YCbCr 4:2:0 Video Data Block:
+ * Checksum: 0x84
+ */
+const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70,
+ 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+ 0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31,
+ 0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00,
+ 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x84
+};
+EXPORT_SYMBOL(test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
+
+MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+MODULE_DESCRIPTION("EDID Definitions for KUnit tests");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
index f4923157f5bf..28b4df93a555 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.h
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
@@ -3,981 +3,14 @@
#ifndef DRM_KUNIT_EDID_H_
#define DRM_KUNIT_EDID_H_
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * RGB color display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II: none
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Checksum: 0xab
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_dvi_1080p[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab
-};
-
-/*
- *
- * This edid is intentionally broken with the 100MHz limit. It's meant
- * to be used only with tests in unusual situations.
- *
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
- *
- * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
- * 00 12 34 00 14 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * Monochrome or grayscale display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x92
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: No Data
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * Maximum TMDS clock: 100 MHz
- * Checksum: 0x10 Unused space in Extension Block: 106 bytes
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * Failures:
- *
- * EDID:
- * CTA-861: The maximum HDMI TMDS clock is 100000 kHz, but one or more video timings go up to 148500 kHz.
- *
- * EDID conformity: FAIL
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
- 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x10
-};
-
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
- *
- * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
- * 00 12 34 00 28 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * Monochrome or grayscale display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x92
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: No Data
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * Maximum TMDS clock: 200 MHz
- * Checksum: 0xfc Unused space in Extension Block: 106 bytes
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
- 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xfc
-};
-
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
- *
- * 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c
- * 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * Monochrome or grayscale display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x92
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * BT2020YCC
- * BT2020RGB
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: No Data
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * Maximum TMDS clock: 200 MHz
- * HDR Static Metadata Data Block:
- * Electro optical transfer functions:
- * Traditional gamma - SDR luminance range
- * SMPTE ST2084
- * Supported static metadata descriptors:
- * Static metadata type 1
- * Desired content max luminance: 82 (295.365 cd/m^2)
- * Desired content max frame-average luminance: 82 (295.365 cd/m^2)
- * Desired content min luminance: 81 (0.298 cd/m^2)
- * Checksum: 0x4e Unused space in Extension Block: 99 bytes
- *
- * ----------------
- *
- * edid-decode 1.31.0-5387
- * edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81,
- 0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xd6,
-};
-
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
- *
- * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
- * 00 12 34 00 44 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * Monochrome or grayscale display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x92
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: No Data
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * Maximum TMDS clock: 340 MHz
- * Checksum: 0xe0 Unused space in Extension Block: 106 bytes
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
- 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xe0
-};
-
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a
- *
- * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
- * 00 12 34 78 28 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d4
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * Undefined display color type
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x7a
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Supports YCbCr 4:4:4
- * Supports YCbCr 4:2:2
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: Selectable (via AVI YQ)
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * DC_48bit
- * DC_36bit
- * DC_30bit
- * DC_Y444
- * Maximum TMDS clock: 200 MHz
- * Checksum: 0xd4 Unused space in Extension Block: 106 bytes
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x15, 0xb1,
- 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x78, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xd4
-};
-
-/*
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
- * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
- * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
- * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a
- *
- * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
- * 00 12 34 78 44 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 42
- * Made in: 2023
- * Basic Display Parameters & Features:
- * Digital display
- * DFP 1.x compatible TMDS
- * Maximum image size: 160 cm x 90 cm
- * Gamma: 2.20
- * RGB color display
- * First detailed timing is the preferred timing
- * Color Characteristics:
- * Red : 0.0000, 0.0000
- * Green: 0.0000, 0.0000
- * Blue : 0.0000, 0.0000
- * White: 0.0000, 0.0000
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0x8a
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Underscans IT Video Formats by default
- * Supports YCbCr 4:4:4
- * Supports YCbCr 4:2:2
- * Native detailed modes: 1
- * Colorimetry Data Block:
- * sRGB
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
- * Video Capability Data Block:
- * YCbCr quantization: Selectable (via AVI YQ)
- * RGB quantization: Selectable (via AVI Q)
- * PT scan behavior: No Data
- * IT scan behavior: Always Underscanned
- * CE scan behavior: Always Underscanned
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.2.3.4
- * DC_48bit
- * DC_36bit
- * DC_30bit
- * DC_Y444
- * Maximum TMDS clock: 340 MHz
- * Checksum: 0xb8 Unused space in Extension Block: 106 bytes
- *
- * ----------------
- *
- * edid-decode 1.30.0-5367
- * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
- *
- * EDID conformity: PASS
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
- 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
- 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x15, 0xb1,
- 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
- 0x00, 0x12, 0x34, 0x78, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xb8
-};
-
-/*
- * Max resolution:
- * - 1920x1080@60Hz with RGB, YUV444, YUV422
- * - 3840x2160@30Hz with YUV420 only
- * Max BPC: 16 for all modes
- * Max TMDS clock: 200 MHz
- *
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
- * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
- * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
- * 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
- * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb
- *
- * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20
- * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00
- * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 52
- * Model year: 2025
- * Basic Display Parameters & Features:
- * Digital display
- * Maximum image size: 96 cm x 54 cm
- * Gamma: 2.20
- * RGB color display
- * Default (sRGB) color space is primary color space
- * First detailed timing is the preferred timing
- * Supports GTF timings within operating range
- * Color Characteristics:
- * Red : 0.6396, 0.3300
- * Green: 0.2998, 0.5996
- * Blue : 0.1503, 0.0595
- * White: 0.3125, 0.3291
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm)
- * Hfront 88 Hsync 44 Hback 148 Hpol P
- * Vfront 4 Vsync 5 Vback 36 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0xbb
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Supports YCbCr 4:4:4
- * Supports YCbCr 4:2:2
- * Native detailed modes: 1
- * Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native)
- * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.0.0.0
- * DC_48bit
- * DC_36bit
- * DC_30bit
- * DC_Y444
- * Maximum TMDS clock: 200 MHz
- * Extended HDMI video details:
- * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
- * Version: 1
- * Maximum TMDS Character Rate: 200 MHz
- * SCDC Present
- * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
- * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
- * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
- * YCbCr 4:2:0 Capability Map Data Block:
- * Empty Capability Map
- * YCbCr 4:2:0 Video Data Block:
- * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * Checksum: 0xca
- */
-static const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
- 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
- 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
- 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31,
- 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20,
- 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e,
- 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xca
-};
-
-/*
- * Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420
- * Max BPC: 16 for all modes
- * Max TMDS clock: 340 MHz
- *
- * edid-decode (hex):
- *
- * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
- * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
- * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
- * 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58
- * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
- * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
- * 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce
- *
- * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00
- * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00
- * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84
- *
- * ----------------
- *
- * Block 0, Base EDID:
- * EDID Structure Version & Revision: 1.3
- * Vendor & Product Identification:
- * Manufacturer: LNX
- * Model: 52
- * Model year: 2025
- * Basic Display Parameters & Features:
- * Digital display
- * Maximum image size: 96 cm x 54 cm
- * Gamma: 2.20
- * RGB color display
- * Default (sRGB) color space is primary color space
- * First detailed timing is the preferred timing
- * Supports GTF timings within operating range
- * Color Characteristics:
- * Red : 0.6396, 0.3300
- * Green: 0.2998, 0.5996
- * Blue : 0.1503, 0.0595
- * White: 0.3125, 0.3291
- * Established Timings I & II:
- * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
- * Standard Timings: none
- * Detailed Timing Descriptors:
- * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm)
- * Hfront 176 Hsync 88 Hback 296 Hpol P
- * Vfront 8 Vsync 10 Vback 72 Vpol P
- * Display Product Name: 'Test EDID'
- * Display Range Limits:
- * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz
- * Dummy Descriptor:
- * Extension blocks: 1
- * Checksum: 0xce
- *
- * ----------------
- *
- * Block 1, CTA-861 Extension Block:
- * Revision: 3
- * Supports YCbCr 4:4:4
- * Supports YCbCr 4:2:2
- * Native detailed modes: 1
- * Video Data Block:
- * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
- * Source physical address: 1.0.0.0
- * DC_48bit
- * DC_36bit
- * DC_30bit
- * DC_Y444
- * Maximum TMDS clock: 340 MHz
- * Extended HDMI video details:
- * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
- * Version: 1
- * Maximum TMDS Character Rate: 340 MHz
- * SCDC Present
- * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
- * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
- * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
- * YCbCr 4:2:0 Capability Map Data Block:
- * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * YCbCr 4:2:0 Video Data Block:
- * Checksum: 0x84
- */
-static const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
- 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
- 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70,
- 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
- 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
- 0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31,
- 0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00,
- 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x84
-};
+extern const unsigned char test_edid_dvi_1080p[128];
+extern const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[256];
+extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[256];
+extern const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[256];
#endif // DRM_KUNIT_EDID_H_
diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
index acab9307bdf3..400329aa2200 100644
--- a/drivers/gpu/drm/tidss/tidss_crtc.c
+++ b/drivers/gpu/drm/tidss/tidss_crtc.c
@@ -357,20 +357,17 @@ static void tidss_crtc_destroy_state(struct drm_crtc *crtc,
kfree(tstate);
}
-static void tidss_crtc_reset(struct drm_crtc *crtc)
+static struct drm_crtc_state *tidss_crtc_create_state(struct drm_crtc *crtc)
{
struct tidss_crtc_state *tstate;
- if (crtc->state)
- tidss_crtc_destroy_state(crtc, crtc->state);
-
tstate = kzalloc_obj(*tstate);
- if (!tstate) {
- crtc->state = NULL;
- return;
- }
+ if (!tstate)
+ return ERR_PTR(-ENOMEM);
+
+ __drm_atomic_helper_crtc_state_init(&tstate->base, crtc);
- __drm_atomic_helper_crtc_reset(crtc, &tstate->base);
+ return &tstate->base;
}
static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
@@ -405,10 +402,10 @@ static void tidss_crtc_destroy(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs tidss_crtc_funcs = {
- .reset = tidss_crtc_reset,
.destroy = tidss_crtc_destroy,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
+ .atomic_create_state = tidss_crtc_create_state,
.atomic_duplicate_state = tidss_crtc_duplicate_state,
.atomic_destroy_state = tidss_crtc_destroy_state,
.enable_vblank = tidss_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c
index 34db8d2a3792..698f8d964ca0 100644
--- a/drivers/gpu/drm/tidss/tidss_encoder.c
+++ b/drivers/gpu/drm/tidss/tidss_encoder.c
@@ -76,7 +76,7 @@ static int tidss_bridge_atomic_check(struct drm_bridge *bridge,
static const struct drm_bridge_funcs tidss_bridge_funcs = {
.attach = tidss_bridge_attach,
.atomic_check = tidss_bridge_atomic_check,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
};
diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
index 9c9725aac829..5eb749f495d5 100644
--- a/drivers/gpu/drm/tidss/tidss_oldi.c
+++ b/drivers/gpu/drm/tidss/tidss_oldi.c
@@ -335,7 +335,7 @@ static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
.atomic_get_input_bus_fmts = tidss_oldi_atomic_get_input_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.mode_valid = tidss_oldi_mode_valid,
};
diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c
index 1a8b44fb45f8..6d82976c2db1 100644
--- a/drivers/gpu/drm/tidss/tidss_plane.c
+++ b/drivers/gpu/drm/tidss/tidss_plane.c
@@ -178,8 +178,8 @@ static const struct drm_plane_helper_funcs tidss_primary_plane_helper_funcs = {
static const struct drm_plane_funcs tidss_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .reset = drm_atomic_helper_plane_reset,
.destroy = drm_plane_destroy,
+ .atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index d73dfebb4353..4ad074337af0 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -388,7 +388,7 @@ static void gm12u320_fb_update_work(struct work_struct *work)
* We must draw a frame every 2s otherwise the projector
* switches back to showing its logo.
*/
- queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
+ queue_delayed_work(system_dfl_long_wq, &gm12u320->fb_update.work,
msecs_to_jiffies(IDLE_TIMEOUT));
return;
@@ -427,7 +427,8 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
mutex_unlock(&gm12u320->fb_update.lock);
if (wakeup)
- mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
+ mod_delayed_work(system_dfl_long_wq,
+ &gm12u320->fb_update.work, 0);
if (old_fb)
drm_framebuffer_put(old_fb);
diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
index 2db221f6fc3a..56ad8ef32584 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
@@ -69,7 +69,7 @@ static void dma_resv_kunit_active_fence_init(struct kunit *test,
struct dma_fence *fence;
fence = alloc_mock_fence(test);
- dma_fence_enable_sw_signaling(fence);
+ dma_fence_enable_signaling(fence);
dma_resv_lock(resv, NULL);
dma_resv_reserve_fences(resv, 1);
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index bcd76f6bb7f0..3980f376e3ba 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -224,7 +224,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence)
- dma_fence_enable_sw_signaling(fence);
+ dma_fence_enable_signaling(fence);
dma_resv_iter_end(&cursor);
}
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 278bbe7a11ad..3d5f2ae0a456 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -165,8 +165,13 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags,
* Do not add latency to the allocation path for allocations orders
* device tolds us do not bring them additional performance gains.
*/
- if (beneficial_order && order > beneficial_order)
- gfp_flags &= ~__GFP_DIRECT_RECLAIM;
+ if (order && beneficial_order && order != beneficial_order)
+ gfp_flags &= ~__GFP_RECLAIM;
+
+ if (beneficial_order && order == beneficial_order) {
+ gfp_flags &= ~__GFP_NORETRY;
+ gfp_flags |= __GFP_RETRY_MAYFAIL;
+ }
if (!ttm_pool_uses_dma_alloc(pool)) {
p = alloc_pages_node(pool->nid, gfp_flags, order);
@@ -902,6 +907,7 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
{
struct ttm_pool_tt_restore *restore = tt->restore;
struct ttm_pool_alloc_state alloc;
+ int ret;
if (WARN_ON(!ttm_tt_is_backed_up(tt)))
return -EINVAL;
@@ -925,14 +931,22 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
} else {
alloc = restore->snapshot_alloc;
if (ttm_pool_restore_valid(restore)) {
- int ret = ttm_pool_restore_commit(restore, tt->backup,
- ctx, &alloc);
+ ret = ttm_pool_restore_commit(restore, tt->backup,
+ ctx, &alloc);
if (ret)
return ret;
}
- if (!alloc.remaining_pages)
+ if (!alloc.remaining_pages) {
+ ret = ttm_pool_apply_caching(&alloc);
+ if (ret)
+ return ret;
+
+ kfree(tt->restore);
+ tt->restore = NULL;
+
return 0;
+ }
}
return __ttm_pool_alloc(pool, tt, ctx, &alloc, restore);
@@ -1341,15 +1355,22 @@ static int ttm_pool_debugfs_shrink_show(struct seq_file *m, void *data)
.gfp_mask = GFP_NOFS,
.nr_to_scan = TTM_SHRINKER_BATCH,
};
- unsigned long count;
+ unsigned long count, scanned;
int nid;
fs_reclaim_acquire(GFP_KERNEL);
for_each_node(nid) {
sc.nid = nid;
count = ttm_pool_shrinker_count(mm_shrinker, &sc);
- seq_printf(m, "%d: %lu/%lu\n", nid, count,
- ttm_pool_shrinker_scan(mm_shrinker, &sc));
+ scanned = ttm_pool_shrinker_scan(mm_shrinker, &sc);
+
+ /* Convert shrinker API sentinel values to 0 for debugfs output */
+ if (count == SHRINK_EMPTY)
+ count = 0;
+ if (scanned == SHRINK_STOP)
+ scanned = 0;
+
+ seq_printf(m, "%d: %lu/%lu\n", nid, count, scanned);
}
fs_reclaim_release(GFP_KERNEL);
diff --git a/drivers/gpu/drm/v3d/Kconfig b/drivers/gpu/drm/v3d/Kconfig
index ce62c5908e1d..6a33e0ab30de 100644
--- a/drivers/gpu/drm/v3d/Kconfig
+++ b/drivers/gpu/drm/v3d/Kconfig
@@ -5,6 +5,7 @@ config DRM_V3D
depends on DRM
depends on COMMON_CLK
depends on MMU
+ select DRM_EXEC
select DRM_SCHED
select DRM_GEM_SHMEM_HELPER
help
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index fc81dd1247e3..8d914dbc3315 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -4,7 +4,11 @@
/**
* DOC: Broadcom V3D Graphics Driver
*
- * This driver supports the Broadcom V3D 3.3 and 4.1 OpenGL ES GPUs.
+ * This driver supports the Broadcom V3D 4.2 and 7.1 GPUs.
+ *
+ * Support for V3D 3.3 and 4.1 GPUs is deprecated and it will be removed
+ * in the next kernel release.
+ *
* For V3D 2.x support, see the VC4 driver.
*
* The V3D GPU includes a tiled render (composed of a bin and render
@@ -414,13 +418,25 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
*/
WARN_ON(v3d->ver != gen);
+ /* V3D 3.3 and V3D 4.1 has had no in-tree userspace since Mesa
+ * dropped support in 2024 (MR#25851) and they have no known users.
+ * Due to that, support is scheduled for removal in the next release.
+ */
+ if (v3d->ver <= V3D_GEN_41) {
+ dev_warn(dev,
+ "V3D %u.%u support is deprecated and will be removed "
+ "in the next kernel release. If you rely on this hardware, "
+ "please report it to dri-devel@lists.freedesktop.org.\n",
+ v3d->ver / 10, v3d->ver % 10);
+ }
+
v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES);
WARN_ON(v3d->cores > 1); /* multicore not yet implemented */
ident3 = V3D_READ(V3D_HUB_IDENT3);
v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV);
- pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_use_autosuspend(dev);
ret = drm_dev_register(drm, 0);
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 4ebe175a8c6b..2e298d7545c0 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -7,7 +7,8 @@
#include <linux/spinlock_types.h>
#include <linux/workqueue.h>
-#include <drm/drm_encoder.h>
+#include <drm/drm_device.h>
+#include <drm/drm_exec.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/gpu_scheduler.h>
@@ -288,6 +289,27 @@ to_v3d_fence(struct dma_fence *fence)
#define V3D_CORE_READ(core, offset) readl(v3d->core_regs[core] + offset)
#define V3D_CORE_WRITE(core, offset, val) writel(val, v3d->core_regs[core] + offset)
+#define V3D_MAX_JOBS_PER_SUBMISSION 3
+
+/* Per-ioctl submission context */
+struct v3d_submit {
+ struct v3d_dev *v3d;
+
+ struct drm_file *file_priv;
+
+ /* DRM exec context for this submission. */
+ struct drm_exec exec;
+
+ /* Ordered array of jobs forming the submission chain. Jobs are
+ * appended via v3d_submit_add_job(), then chained and pushed to
+ * the scheduler by v3d_submit_jobs().
+ */
+ struct v3d_job *jobs[V3D_MAX_JOBS_PER_SUBMISSION];
+
+ /* Number of jobs currently in @jobs. */
+ u32 job_count;
+};
+
struct v3d_job {
struct drm_sched_job base;
@@ -295,6 +317,9 @@ struct v3d_job {
struct v3d_dev *v3d;
+ /* The queue that the job was submitted on. */
+ enum v3d_queue queue;
+
/* This is the array of BOs that were looked up at the start
* of submission.
*/
@@ -401,8 +426,10 @@ struct v3d_indirect_csd_info {
/* Indirect CSD */
struct v3d_csd_job *job;
- /* Clean cache job associated to the Indirect CSD job */
- struct v3d_job *clean_job;
+ /* Indirect CSD args, stashed by the extension parser and later used
+ * to create the CSD job from them.
+ */
+ struct drm_v3d_submit_csd args;
/* Offset within the BO where the workgroup counts are stored */
u32 offset;
@@ -417,9 +444,6 @@ struct v3d_indirect_csd_info {
/* Indirect BO */
struct drm_gem_object *indirect;
-
- /* Context of the Indirect CSD job */
- struct ww_acquire_ctx acquire_ctx;
};
struct v3d_timestamp_query_info {
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index c35f9f70cf8d..e950cee31bcb 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -189,15 +189,11 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
struct v3d_dev *v3d = job->base.v3d;
struct v3d_queue_state *queue = &v3d->queue[V3D_BIN];
struct drm_device *dev = &v3d->drm;
- struct dma_fence *fence;
+ struct dma_fence *fence = NULL;
unsigned long irqflags;
- if (unlikely(job->base.base.s_fence->finished.error)) {
- spin_lock_irqsave(&queue->queue_lock, irqflags);
- queue->active_job = NULL;
- spin_unlock_irqrestore(&queue->queue_lock, irqflags);
- return NULL;
- }
+ if (unlikely(job->base.base.s_fence->finished.error))
+ goto out_clean_job;
/* Lock required around bin_job update vs
* v3d_overflow_mem_work().
@@ -214,7 +210,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_BIN);
if (IS_ERR(fence))
- return NULL;
+ goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -242,6 +238,12 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, V3D_CLE_CT0QEA, job->end);
return fence;
+
+out_clean_job:
+ spin_lock_irqsave(&queue->queue_lock, irqflags);
+ queue->active_job = NULL;
+ spin_unlock_irqrestore(&queue->queue_lock, irqflags);
+ return fence;
}
static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
@@ -249,12 +251,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
struct v3d_render_job *job = to_render_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
- struct dma_fence *fence;
+ struct dma_fence *fence = NULL;
- if (unlikely(job->base.base.s_fence->finished.error)) {
- v3d->queue[V3D_RENDER].active_job = NULL;
- return NULL;
- }
+ if (unlikely(job->base.base.s_fence->finished.error))
+ goto out_clean_job;
v3d->queue[V3D_RENDER].active_job = &job->base;
@@ -268,7 +268,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_RENDER);
if (IS_ERR(fence))
- return NULL;
+ goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -289,6 +289,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, V3D_CLE_CT1QEA, job->end);
return fence;
+
+out_clean_job:
+ v3d->queue[V3D_RENDER].active_job = NULL;
+ return fence;
}
static struct dma_fence *
@@ -297,18 +301,16 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
struct v3d_tfu_job *job = to_tfu_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
- struct dma_fence *fence;
+ struct dma_fence *fence = NULL;
- if (unlikely(job->base.base.s_fence->finished.error)) {
- v3d->queue[V3D_TFU].active_job = NULL;
- return NULL;
- }
+ if (unlikely(job->base.base.s_fence->finished.error))
+ goto out_clean_job;
v3d->queue[V3D_TFU].active_job = &job->base;
fence = v3d_fence_create(v3d, V3D_TFU);
if (IS_ERR(fence))
- return NULL;
+ goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -336,6 +338,10 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
V3D_WRITE(V3D_TFU_ICFG(v3d->ver), job->args.icfg | V3D_TFU_ICFG_IOC);
return fence;
+
+out_clean_job:
+ v3d->queue[V3D_TFU].active_job = NULL;
+ return fence;
}
static struct dma_fence *
@@ -344,13 +350,11 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
struct v3d_csd_job *job = to_csd_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
- struct dma_fence *fence;
+ struct dma_fence *fence = NULL;
int i, csd_cfg0_reg;
- if (unlikely(job->base.base.s_fence->finished.error)) {
- v3d->queue[V3D_CSD].active_job = NULL;
- return NULL;
- }
+ if (unlikely(job->base.base.s_fence->finished.error))
+ goto out_clean_job;
/* The HW interprets a workgroup size of 0 as 65536; however, the
* user-space driver exposes a maximum of 65535. Therefore, a 0 in
@@ -368,7 +372,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_CSD);
if (IS_ERR(fence))
- return NULL;
+ goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -395,6 +399,10 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]);
return fence;
+
+out_clean_job:
+ v3d->queue[V3D_CSD].active_job = NULL;
+ return fence;
}
static void
@@ -660,6 +668,9 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job)
struct v3d_cpu_job *job = to_cpu_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
+ if (unlikely(job->base.base.s_fence->finished.error))
+ return NULL;
+
if (job->job_type >= ARRAY_SIZE(cpu_job_function)) {
drm_dbg(&v3d->drm, "Unknown CPU job: %d\n", job->job_type);
return NULL;
@@ -683,6 +694,9 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job)
struct v3d_job *job = to_v3d_job(sched_job);
struct v3d_dev *v3d = job->v3d;
+ if (unlikely(job->base.s_fence->finished.error))
+ return NULL;
+
v3d_job_start_stats(job);
v3d_clean_caches(v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index 1db43c6a078d..ee2ac2540ed5 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -20,33 +20,51 @@
* to v3d, so we don't attach dma-buf fences to them.
*/
static int
-v3d_lock_bo_reservations(struct v3d_job *job,
- struct ww_acquire_ctx *acquire_ctx)
+v3d_submit_lock_reservations(struct v3d_submit *submit)
{
- int i, ret;
-
- ret = drm_gem_lock_reservations(job->bo, job->bo_count, acquire_ctx);
- if (ret)
- return ret;
-
- for (i = 0; i < job->bo_count; i++) {
- ret = dma_resv_reserve_fences(job->bo[i]->resv, 1);
+ int i, j, ret;
+
+ drm_exec_init(&submit->exec,
+ DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, 0);
+ drm_exec_until_all_locked(&submit->exec) {
+ for (i = 0; i < submit->job_count; i++) {
+ struct v3d_job *job = submit->jobs[i];
+
+ ret = drm_exec_prepare_array(&submit->exec, job->bo,
+ job->bo_count, 1);
+ if (ret)
+ break;
+ }
+ drm_exec_retry_on_contention(&submit->exec);
if (ret)
goto fail;
+ }
- ret = drm_sched_job_add_implicit_dependencies(&job->base,
- job->bo[i], true);
- if (ret)
- goto fail;
+ for (i = 0; i < submit->job_count; i++) {
+ struct v3d_job *job = submit->jobs[i];
+
+ for (j = 0; j < job->bo_count; j++) {
+ ret = drm_sched_job_add_implicit_dependencies(&job->base,
+ job->bo[j],
+ true);
+ if (ret)
+ goto fail;
+ }
}
return 0;
fail:
- drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx);
+ drm_exec_fini(&submit->exec);
return ret;
}
+static void
+v3d_submit_unlock_reservations(struct v3d_submit *submit)
+{
+ drm_exec_fini(&submit->exec);
+}
+
/**
* v3d_lookup_bos() - Sets up job->bo[] with the GEM objects
* referenced by the job.
@@ -64,25 +82,23 @@ fail:
* failure, because that will happen at `v3d_job_free()`.
*/
static int
-v3d_lookup_bos(struct drm_device *dev,
- struct drm_file *file_priv,
- struct v3d_job *job,
- u64 bo_handles,
- u32 bo_count)
+v3d_lookup_bos(struct v3d_submit *submit, u64 bo_handles, u32 bo_count)
{
- job->bo_count = bo_count;
+ struct v3d_job *last_job = submit->jobs[submit->job_count - 1];
- if (!job->bo_count) {
+ last_job->bo_count = bo_count;
+
+ if (!last_job->bo_count) {
/* See comment on bo_index for why we have to check
* this.
*/
- drm_warn(dev, "Rendering requires BOs\n");
+ drm_warn(&submit->v3d->drm, "Rendering requires BOs\n");
return -EINVAL;
}
- return drm_gem_objects_lookup(file_priv,
+ return drm_gem_objects_lookup(submit->file_priv,
(void __user *)(uintptr_t)bo_handles,
- job->bo_count, &job->bo);
+ last_job->bo_count, &last_job->bo);
}
static void
@@ -162,74 +178,87 @@ void v3d_job_put(struct v3d_job *job)
}
static int
-v3d_job_allocate(struct v3d_dev *v3d, void **container, size_t size)
+v3d_job_add_syncobjs(struct v3d_job *job, struct drm_file *file_priv,
+ u32 in_sync, struct v3d_submit_ext *se)
{
- *container = kcalloc(1, size, GFP_KERNEL);
- if (!*container) {
- drm_err(&v3d->drm, "Cannot allocate memory for V3D job.\n");
- return -ENOMEM;
+ bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC);
+ struct v3d_dev *v3d = job->v3d;
+ int ret = 0;
+
+ if (!has_multisync) {
+ /* Ignore syncobj if its handle is zero */
+ if (in_sync)
+ ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv,
+ in_sync, 0);
+ return ret;
+ }
+
+ if (se->in_sync_count && se->wait_stage == job->queue) {
+ struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs);
+
+ for (int i = 0; i < se->in_sync_count; i++) {
+ struct drm_v3d_sem in;
+
+ if (copy_from_user(&in, handle++, sizeof(in))) {
+ drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n");
+ return -EFAULT;
+ }
+
+ /* Ignore syncobj if its handle is zero */
+ if (in.handle) {
+ ret = drm_sched_job_add_syncobj_dependency(&job->base,
+ file_priv, in.handle, 0);
+ if (ret)
+ return ret;
+ }
+ }
}
return 0;
}
-static void
-v3d_job_deallocate(void **container)
-{
- kfree(*container);
- *container = NULL;
-}
+static const struct {
+ size_t size;
+ void (*free)(struct kref *ref);
+} v3d_job_types[] = {
+ [V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free },
+ [V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free },
+ [V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free },
+ [V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free },
+ [V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free },
+ [V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free },
+};
-static int
-v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
- struct v3d_job *job, void (*free)(struct kref *ref),
- u32 in_sync, struct v3d_submit_ext *se, enum v3d_queue queue)
+static struct v3d_job *
+v3d_submit_add_job(struct v3d_submit *submit, enum v3d_queue queue)
{
- struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
- bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC);
- int ret, i;
+ struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv;
+ struct v3d_dev *v3d = submit->v3d;
+ struct v3d_job *job;
+ int ret;
+
+ if (queue >= V3D_MAX_QUEUES)
+ return ERR_PTR(-EINVAL);
+
+ job = kzalloc(v3d_job_types[queue].size, GFP_KERNEL);
+ if (!job)
+ return ERR_PTR(-ENOMEM);
job->v3d = v3d;
- job->free = free;
+ job->queue = queue;
job->file_priv = v3d_priv;
+ job->free = v3d_job_types[queue].free;
ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue],
- 1, v3d_priv, file_priv->client_id);
+ 1, v3d_priv, submit->file_priv->client_id);
if (ret)
- return ret;
-
- if (has_multisync) {
- if (se->in_sync_count && se->wait_stage == queue) {
- struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs);
-
- for (i = 0; i < se->in_sync_count; i++) {
- struct drm_v3d_sem in;
-
- if (copy_from_user(&in, handle++, sizeof(in))) {
- ret = -EFAULT;
- drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n");
- goto fail_job_init;
- }
- ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0);
-
- // TODO: Investigate why this was filtered out for the IOCTL.
- if (ret && ret != -ENOENT)
- goto fail_job_init;
- }
- }
- } else {
- ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in_sync, 0);
-
- // TODO: Investigate why this was filtered out for the IOCTL.
- if (ret && ret != -ENOENT)
- goto fail_job_init;
- }
+ goto fail_free;
/* CPU jobs don't require hardware resources */
if (queue != V3D_CPU) {
ret = v3d_pm_runtime_get(v3d);
if (ret)
- goto fail_job_init;
+ goto fail_sched_job;
job->has_pm_ref = true;
}
@@ -238,52 +267,91 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
job->client_stats = v3d_stats_get(v3d_priv->stats[queue]);
job->global_stats = v3d_stats_get(v3d->queue[queue].stats);
- return 0;
+ submit->jobs[submit->job_count++] = job;
+
+ return job;
-fail_job_init:
+fail_sched_job:
drm_sched_job_cleanup(&job->base);
- return ret;
+fail_free:
+ kfree(job);
+ return ERR_PTR(ret);
}
static void
-v3d_push_job(struct v3d_job *job)
+v3d_submit_put_jobs(struct v3d_submit *submit)
{
- drm_sched_job_arm(&job->base);
+ for (int i = 0; i < submit->job_count; i++)
+ v3d_job_put(submit->jobs[i]);
+}
- job->done_fence = dma_fence_get(&job->base.s_fence->finished);
+static void
+v3d_submit_cleanup_jobs(struct v3d_submit *submit)
+{
+ for (int i = 0; i < submit->job_count; i++)
+ v3d_job_cleanup(submit->jobs[i]);
+}
+
+static int
+v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id)
+{
+ struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv;
+ struct v3d_dev *v3d = submit->v3d;
+ struct v3d_perfmon *perfmon;
+
+ if (!perfmon_id)
+ return 0;
+
+ if (v3d->global_perfmon)
+ return -EAGAIN;
- /* put by scheduler job completion */
- kref_get(&job->refcount);
+ perfmon = v3d_perfmon_find(v3d_priv, perfmon_id);
+ if (!perfmon)
+ return -ENOENT;
- drm_sched_entity_push_job(&job->base);
+ for (int i = 0; i < submit->job_count; i++) {
+ submit->jobs[i]->perfmon = perfmon;
+ if (i != 0)
+ v3d_perfmon_get(perfmon);
+ }
+
+ return 0;
}
static void
-v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,
- struct v3d_job *job,
- struct ww_acquire_ctx *acquire_ctx,
- u32 out_sync,
- struct v3d_submit_ext *se,
- struct dma_fence *done_fence)
+v3d_submit_attach_object_fences(struct v3d_submit *submit)
{
- struct drm_syncobj *sync_out;
- bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC);
- int i;
-
- for (i = 0; i < job->bo_count; i++) {
- /* XXX: Use shared fences for read-only objects. */
- dma_resv_add_fence(job->bo[i]->resv, job->done_fence,
- DMA_RESV_USAGE_WRITE);
+ struct v3d_job *last_job = submit->jobs[submit->job_count - 1];
+
+ /* The submission's last fence covers the entire submission. Attach it
+ * to every BO touched by any job in the submission.
+ */
+ for (int i = 0; i < submit->job_count; i++) {
+ struct v3d_job *job = submit->jobs[i];
+
+ for (int j = 0; j < job->bo_count; j++) {
+ /* XXX: Use shared fences for read-only objects. */
+ dma_resv_add_fence(job->bo[j]->resv, last_job->done_fence,
+ DMA_RESV_USAGE_WRITE);
+ }
}
+}
- drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx);
+static void
+v3d_submit_process_post_deps(struct v3d_submit *submit, struct drm_syncobj *sync_out,
+ struct v3d_submit_ext *se)
+{
+ bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC);
+ struct v3d_job *last_job = submit->jobs[submit->job_count - 1];
+
+ /* Make sure single syncobj and multisync are mutually exclusive */
+ WARN_ON_ONCE(sync_out && has_multisync);
/* Update the return sync object for the job */
/* If it only supports a single signal semaphore*/
if (!has_multisync) {
- sync_out = drm_syncobj_find(file_priv, out_sync);
if (sync_out) {
- drm_syncobj_replace_fence(sync_out, done_fence);
+ drm_syncobj_replace_fence(sync_out, last_job->done_fence);
drm_syncobj_put(sync_out);
}
return;
@@ -291,9 +359,9 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,
/* If multiple semaphores extension is supported */
if (se->out_sync_count) {
- for (i = 0; i < se->out_sync_count; i++) {
+ for (int i = 0; i < se->out_sync_count; i++) {
drm_syncobj_replace_fence(se->out_syncs[i].syncobj,
- done_fence);
+ last_job->done_fence);
drm_syncobj_put(se->out_syncs[i].syncobj);
}
kvfree(se->out_syncs);
@@ -301,53 +369,94 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,
}
static int
-v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv,
- struct v3d_dev *v3d,
- struct drm_v3d_submit_csd *args,
- struct v3d_csd_job **job,
- struct v3d_job **clean_job,
- struct v3d_submit_ext *se,
- struct ww_acquire_ctx *acquire_ctx)
+v3d_submit_jobs(struct v3d_submit *submit, struct drm_syncobj *sync_out,
+ struct v3d_submit_ext *se)
{
- int ret;
+ struct v3d_dev *v3d = submit->v3d;
+ int ret = 0;
- ret = v3d_job_allocate(v3d, (void *)job, sizeof(**job));
- if (ret)
- return ret;
+ mutex_lock(&v3d->sched_lock);
- ret = v3d_job_init(v3d, file_priv, &(*job)->base,
- v3d_job_free, args->in_sync, se, V3D_CSD);
- if (ret) {
- v3d_job_deallocate((void *)job);
- return ret;
- }
+ for (int i = 0; i < submit->job_count; i++) {
+ struct v3d_job *job = submit->jobs[i];
- ret = v3d_job_allocate(v3d, (void *)clean_job, sizeof(**clean_job));
- if (ret)
- return ret;
+ drm_sched_job_arm(&job->base);
+ job->done_fence = dma_fence_get(&job->base.s_fence->finished);
- ret = v3d_job_init(v3d, file_priv, *clean_job,
- v3d_job_free, 0, NULL, V3D_CACHE_CLEAN);
- if (ret) {
- v3d_job_deallocate((void *)clean_job);
- return ret;
+ /* put by scheduler job completion */
+ kref_get(&job->refcount);
}
- (*job)->args = *args;
+ for (int i = 1; i < submit->job_count; i++) {
+ ret = drm_sched_job_add_dependency(&submit->jobs[i]->base,
+ dma_fence_get(submit->jobs[i - 1]->done_fence));
+ if (ret)
+ goto err;
+ }
+
+ for (int i = 0; i < submit->job_count; i++)
+ drm_sched_entity_push_job(&submit->jobs[i]->base);
+
+ mutex_unlock(&v3d->sched_lock);
+
+ v3d_submit_attach_object_fences(submit);
+ v3d_submit_unlock_reservations(submit);
+ v3d_submit_process_post_deps(submit, sync_out, se);
+
+ v3d_submit_put_jobs(submit);
+
+ return 0;
+
+err:
+ /* Mark every armed job as failed so run_job() skips execution */
+ for (int i = 0; i < submit->job_count; i++)
+ dma_fence_set_error(&submit->jobs[i]->base.s_fence->finished, ret);
+
+ for (int i = 0; i < submit->job_count; i++)
+ drm_sched_entity_push_job(&submit->jobs[i]->base);
+
+ mutex_unlock(&v3d->sched_lock);
+
+ v3d_submit_unlock_reservations(submit);
+ v3d_submit_put_jobs(submit);
+
+ return ret;
+}
+
+static int
+v3d_setup_csd_jobs_and_bos(struct v3d_submit *submit,
+ struct drm_v3d_submit_csd *args,
+ struct v3d_submit_ext *se)
+{
+ struct v3d_csd_job *job;
+ struct v3d_job *clean_job;
+ int ret;
+
+ job = (struct v3d_csd_job *)v3d_submit_add_job(submit, V3D_CSD);
+ if (IS_ERR(job))
+ return PTR_ERR(job);
- ret = v3d_lookup_bos(&v3d->drm, file_priv, *clean_job,
- args->bo_handles, args->bo_handle_count);
+ ret = v3d_job_add_syncobjs(&job->base, submit->file_priv, args->in_sync, se);
if (ret)
return ret;
- return v3d_lock_bo_reservations(*clean_job, acquire_ctx);
+ job->args = *args;
+
+ clean_job = v3d_submit_add_job(submit, V3D_CACHE_CLEAN);
+ if (IS_ERR(clean_job))
+ return PTR_ERR(clean_job);
+
+ return v3d_lookup_bos(submit, args->bo_handles, args->bo_handle_count);
}
static void
-v3d_put_multisync_post_deps(struct v3d_submit_ext *se)
+v3d_submit_put_post_deps(struct drm_syncobj *sync_out, struct v3d_submit_ext *se)
{
unsigned int i;
+ if (sync_out)
+ drm_syncobj_put(sync_out);
+
if (!(se && se->out_sync_count))
return;
@@ -489,6 +598,7 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
}
job->job_type = V3D_CPU_JOB_TYPE_INDIRECT_CSD;
+ info->args = indirect_csd.submit;
info->offset = indirect_csd.offset;
info->wg_size = indirect_csd.wg_size;
memcpy(&info->wg_uniform_offsets, &indirect_csd.wg_uniform_offsets,
@@ -496,9 +606,7 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv,
info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect);
- return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit,
- &info->job, &info->clean_job,
- NULL, &info->acquire_ctx);
+ return 0;
}
/* Get data for the query timestamp job submission. */
@@ -905,18 +1013,16 @@ int
v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct v3d_dev *v3d = to_v3d_dev(dev);
- struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
+ struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv };
struct drm_v3d_submit_cl *args = data;
+ struct drm_syncobj *sync_out = NULL;
struct v3d_submit_ext se = {0};
struct v3d_bin_job *bin = NULL;
- struct v3d_render_job *render = NULL;
- struct v3d_job *clean_job = NULL;
- struct v3d_job *last_job;
- struct ww_acquire_ctx acquire_ctx;
- int ret = 0;
+ struct v3d_render_job *render;
+ struct v3d_job *clean_job;
+ int ret;
- trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end);
+ trace_v3d_submit_cl_ioctl(dev, args->rcl_start, args->rcl_end);
if (args->pad)
return -EINVAL;
@@ -936,30 +1042,17 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
}
}
- ret = v3d_job_allocate(v3d, (void *)&render, sizeof(*render));
- if (ret)
- return ret;
-
- ret = v3d_job_init(v3d, file_priv, &render->base,
- v3d_render_job_free, args->in_sync_rcl, &se, V3D_RENDER);
- if (ret) {
- v3d_job_deallocate((void *)&render);
- goto fail;
+ /* If multisync is configured, give priority to it and ignore out_sync. */
+ if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) {
+ sync_out = drm_syncobj_find(file_priv, args->out_sync);
+ if (!sync_out)
+ return -ENOENT;
}
- render->start = args->rcl_start;
- render->end = args->rcl_end;
- INIT_LIST_HEAD(&render->unref_list);
-
if (args->bcl_start != args->bcl_end) {
- ret = v3d_job_allocate(v3d, (void *)&bin, sizeof(*bin));
- if (ret)
- goto fail;
-
- ret = v3d_job_init(v3d, file_priv, &bin->base,
- v3d_job_free, args->in_sync_bcl, &se, V3D_BIN);
- if (ret) {
- v3d_job_deallocate((void *)&bin);
+ bin = (struct v3d_bin_job *)v3d_submit_add_job(&submit, V3D_BIN);
+ if (IS_ERR(bin)) {
+ ret = PTR_ERR(bin);
goto fail;
}
@@ -968,101 +1061,60 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
bin->qma = args->qma;
bin->qms = args->qms;
bin->qts = args->qts;
- bin->render = render;
- }
- if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) {
- ret = v3d_job_allocate(v3d, (void *)&clean_job, sizeof(*clean_job));
+ ret = v3d_job_add_syncobjs(&bin->base, file_priv, args->in_sync_bcl,
+ &se);
if (ret)
goto fail;
-
- ret = v3d_job_init(v3d, file_priv, clean_job,
- v3d_job_free, 0, NULL, V3D_CACHE_CLEAN);
- if (ret) {
- v3d_job_deallocate((void *)&clean_job);
- goto fail;
- }
-
- last_job = clean_job;
- } else {
- last_job = &render->base;
}
- ret = v3d_lookup_bos(dev, file_priv, last_job,
- args->bo_handles, args->bo_handle_count);
- if (ret)
+ render = (struct v3d_render_job *)v3d_submit_add_job(&submit, V3D_RENDER);
+ if (IS_ERR(render)) {
+ ret = PTR_ERR(render);
goto fail;
+ }
- ret = v3d_lock_bo_reservations(last_job, &acquire_ctx);
- if (ret)
- goto fail;
+ INIT_LIST_HEAD(&render->unref_list);
+ render->start = args->rcl_start;
+ render->end = args->rcl_end;
- if (args->perfmon_id) {
- if (v3d->global_perfmon) {
- ret = -EAGAIN;
- goto fail_perfmon;
- }
+ if (bin)
+ bin->render = render;
- render->base.perfmon = v3d_perfmon_find(v3d_priv,
- args->perfmon_id);
+ ret = v3d_job_add_syncobjs(&render->base, file_priv, args->in_sync_rcl, &se);
+ if (ret)
+ goto fail;
- if (!render->base.perfmon) {
- ret = -ENOENT;
- goto fail_perfmon;
+ if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) {
+ clean_job = v3d_submit_add_job(&submit, V3D_CACHE_CLEAN);
+ if (IS_ERR(clean_job)) {
+ ret = PTR_ERR(clean_job);
+ goto fail;
}
}
- mutex_lock(&v3d->sched_lock);
- if (bin) {
- bin->base.perfmon = render->base.perfmon;
- v3d_perfmon_get(bin->base.perfmon);
- v3d_push_job(&bin->base);
-
- ret = drm_sched_job_add_dependency(&render->base.base,
- dma_fence_get(bin->base.done_fence));
- if (ret)
- goto fail_unreserve;
- }
-
- v3d_push_job(&render->base);
-
- if (clean_job) {
- struct dma_fence *render_fence =
- dma_fence_get(render->base.done_fence);
- ret = drm_sched_job_add_dependency(&clean_job->base,
- render_fence);
- if (ret)
- goto fail_unreserve;
- clean_job->perfmon = render->base.perfmon;
- v3d_perfmon_get(clean_job->perfmon);
- v3d_push_job(clean_job);
- }
+ ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id);
+ if (ret)
+ goto fail;
- mutex_unlock(&v3d->sched_lock);
+ ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
+ if (ret)
+ goto fail;
- v3d_attach_fences_and_unlock_reservation(file_priv,
- last_job,
- &acquire_ctx,
- args->out_sync,
- &se,
- last_job->done_fence);
+ ret = v3d_submit_lock_reservations(&submit);
+ if (ret)
+ goto fail;
- v3d_job_put(&bin->base);
- v3d_job_put(&render->base);
- v3d_job_put(clean_job);
+ ret = v3d_submit_jobs(&submit, sync_out, &se);
+ if (ret)
+ goto fail_submit;
return 0;
-fail_unreserve:
- mutex_unlock(&v3d->sched_lock);
-fail_perfmon:
- drm_gem_unlock_reservations(last_job->bo,
- last_job->bo_count, &acquire_ctx);
fail:
- v3d_job_cleanup((void *)bin);
- v3d_job_cleanup((void *)render);
- v3d_job_cleanup(clean_job);
- v3d_put_multisync_post_deps(&se);
+ v3d_submit_cleanup_jobs(&submit);
+fail_submit:
+ v3d_submit_put_post_deps(sync_out, &se);
return ret;
}
@@ -1080,14 +1132,14 @@ int
v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct v3d_dev *v3d = to_v3d_dev(dev);
+ struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv };
struct drm_v3d_submit_tfu *args = data;
+ struct drm_syncobj *sync_out = NULL;
struct v3d_submit_ext se = {0};
- struct v3d_tfu_job *job = NULL;
- struct ww_acquire_ctx acquire_ctx;
+ struct v3d_tfu_job *job;
int ret = 0;
- trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia);
+ trace_v3d_submit_tfu_ioctl(dev, args->iia);
if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) {
drm_dbg(dev, "invalid flags: %d\n", args->flags);
@@ -1102,17 +1154,23 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
}
}
- ret = v3d_job_allocate(v3d, (void *)&job, sizeof(*job));
- if (ret)
- return ret;
+ /* If multisync is configured, give priority to it and ignore out_sync. */
+ if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) {
+ sync_out = drm_syncobj_find(file_priv, args->out_sync);
+ if (!sync_out)
+ return -ENOENT;
+ }
- ret = v3d_job_init(v3d, file_priv, &job->base,
- v3d_job_free, args->in_sync, &se, V3D_TFU);
- if (ret) {
- v3d_job_deallocate((void *)&job);
+ job = (struct v3d_tfu_job *)v3d_submit_add_job(&submit, V3D_TFU);
+ if (IS_ERR(job)) {
+ ret = PTR_ERR(job);
goto fail;
}
+ ret = v3d_job_add_syncobjs(&job->base, file_priv, args->in_sync, &se);
+ if (ret)
+ goto fail;
+
job->base.bo = kzalloc_objs(*job->base.bo, ARRAY_SIZE(args->bo_handles));
if (!job->base.bo) {
ret = -ENOMEM;
@@ -1140,27 +1198,20 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
job->base.bo[job->base.bo_count] = bo;
}
- ret = v3d_lock_bo_reservations(&job->base, &acquire_ctx);
+ ret = v3d_submit_lock_reservations(&submit);
if (ret)
goto fail;
- mutex_lock(&v3d->sched_lock);
- v3d_push_job(&job->base);
- mutex_unlock(&v3d->sched_lock);
-
- v3d_attach_fences_and_unlock_reservation(file_priv,
- &job->base, &acquire_ctx,
- args->out_sync,
- &se,
- job->base.done_fence);
-
- v3d_job_put(&job->base);
+ ret = v3d_submit_jobs(&submit, sync_out, &se);
+ if (ret)
+ goto fail_submit;
return 0;
fail:
- v3d_job_cleanup((void *)job);
- v3d_put_multisync_post_deps(&se);
+ v3d_submit_cleanup_jobs(&submit);
+fail_submit:
+ v3d_submit_put_post_deps(sync_out, &se);
return ret;
}
@@ -1178,21 +1229,18 @@ int
v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct v3d_dev *v3d = to_v3d_dev(dev);
- struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
+ struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv };
struct drm_v3d_submit_csd *args = data;
+ struct drm_syncobj *sync_out = NULL;
struct v3d_submit_ext se = {0};
- struct v3d_csd_job *job = NULL;
- struct v3d_job *clean_job = NULL;
- struct ww_acquire_ctx acquire_ctx;
int ret;
- trace_v3d_submit_csd_ioctl(&v3d->drm, args->cfg[5], args->cfg[6]);
+ trace_v3d_submit_csd_ioctl(dev, args->cfg[5], args->cfg[6]);
if (args->pad)
return -EINVAL;
- if (!v3d_has_csd(v3d)) {
+ if (!v3d_has_csd(submit.v3d)) {
drm_warn(dev, "Attempting CSD submit on non-CSD hardware\n");
return -EINVAL;
}
@@ -1210,58 +1258,35 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
}
}
- ret = v3d_setup_csd_jobs_and_bos(file_priv, v3d, args,
- &job, &clean_job, &se,
- &acquire_ctx);
- if (ret)
- goto fail;
-
- if (args->perfmon_id) {
- if (v3d->global_perfmon) {
- ret = -EAGAIN;
- goto fail_perfmon;
- }
-
- job->base.perfmon = v3d_perfmon_find(v3d_priv,
- args->perfmon_id);
- if (!job->base.perfmon) {
- ret = -ENOENT;
- goto fail_perfmon;
- }
+ /* If multisync is configured, give priority to it and ignore out_sync. */
+ if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) {
+ sync_out = drm_syncobj_find(file_priv, args->out_sync);
+ if (!sync_out)
+ return -ENOENT;
}
- mutex_lock(&v3d->sched_lock);
- v3d_push_job(&job->base);
-
- ret = drm_sched_job_add_dependency(&clean_job->base,
- dma_fence_get(job->base.done_fence));
+ ret = v3d_setup_csd_jobs_and_bos(&submit, args, &se);
if (ret)
- goto fail_unreserve;
+ goto fail;
- v3d_push_job(clean_job);
- mutex_unlock(&v3d->sched_lock);
+ ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id);
+ if (ret)
+ goto fail;
- v3d_attach_fences_and_unlock_reservation(file_priv,
- clean_job,
- &acquire_ctx,
- args->out_sync,
- &se,
- clean_job->done_fence);
+ ret = v3d_submit_lock_reservations(&submit);
+ if (ret)
+ goto fail;
- v3d_job_put(&job->base);
- v3d_job_put(clean_job);
+ ret = v3d_submit_jobs(&submit, sync_out, &se);
+ if (ret)
+ goto fail_submit;
return 0;
-fail_unreserve:
- mutex_unlock(&v3d->sched_lock);
-fail_perfmon:
- drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count,
- &acquire_ctx);
fail:
- v3d_job_cleanup((void *)job);
- v3d_job_cleanup(clean_job);
- v3d_put_multisync_post_deps(&se);
+ v3d_submit_cleanup_jobs(&submit);
+fail_submit:
+ v3d_submit_put_post_deps(sync_out, &se);
return ret;
}
@@ -1288,14 +1313,10 @@ int
v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct v3d_dev *v3d = to_v3d_dev(dev);
+ struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv };
struct drm_v3d_submit_cpu *args = data;
struct v3d_submit_ext se = {0};
- struct v3d_submit_ext *out_se = NULL;
struct v3d_cpu_job *cpu_job = NULL;
- struct v3d_csd_job *csd_job = NULL;
- struct v3d_job *clean_job = NULL;
- struct ww_acquire_ctx acquire_ctx;
int ret;
if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) {
@@ -1303,9 +1324,9 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- ret = v3d_job_allocate(v3d, (void *)&cpu_job, sizeof(*cpu_job));
- if (ret)
- return ret;
+ cpu_job = (struct v3d_cpu_job *)v3d_submit_add_job(&submit, V3D_CPU);
+ if (IS_ERR(cpu_job))
+ return PTR_ERR(cpu_job);
if (args->flags & DRM_V3D_SUBMIT_EXTENSION) {
ret = v3d_get_extensions(file_priv, args->extensions, &se, cpu_job);
@@ -1328,92 +1349,51 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
goto fail;
}
- trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type);
+ trace_v3d_submit_cpu_ioctl(dev, cpu_job->job_type);
- ret = v3d_job_init(v3d, file_priv, &cpu_job->base,
- v3d_cpu_job_free, 0, &se, V3D_CPU);
- if (ret) {
- v3d_job_deallocate((void *)&cpu_job);
+ ret = v3d_job_add_syncobjs(&cpu_job->base, file_priv, 0, &se);
+ if (ret)
goto fail;
- }
-
- clean_job = cpu_job->indirect_csd.clean_job;
- csd_job = cpu_job->indirect_csd.job;
+ /* Look up the CPU jobs' BOs before v3d_setup_csd_jobs_and_bos() appends
+ * the CSD and clean jobs in the case of indirect CSD job.
+ */
if (args->bo_handle_count) {
- ret = v3d_lookup_bos(dev, file_priv, &cpu_job->base,
- args->bo_handles, args->bo_handle_count);
- if (ret)
- goto fail;
-
- ret = v3d_lock_bo_reservations(&cpu_job->base, &acquire_ctx);
+ ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;
}
- mutex_lock(&v3d->sched_lock);
- v3d_push_job(&cpu_job->base);
-
- switch (cpu_job->job_type) {
- case V3D_CPU_JOB_TYPE_INDIRECT_CSD:
- ret = drm_sched_job_add_dependency(&csd_job->base.base,
- dma_fence_get(cpu_job->base.done_fence));
- if (ret)
- goto fail_unreserve;
-
- v3d_push_job(&csd_job->base);
-
- ret = drm_sched_job_add_dependency(&clean_job->base,
- dma_fence_get(csd_job->base.done_fence));
+ if (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) {
+ ret = v3d_setup_csd_jobs_and_bos(&submit, &cpu_job->indirect_csd.args,
+ NULL);
if (ret)
- goto fail_unreserve;
+ goto fail;
- v3d_push_job(clean_job);
+ /* The CSD job was appended at jobs[1] */
+ if (WARN_ON(submit.jobs[1]->queue != V3D_CSD)) {
+ ret = -EINVAL;
+ goto fail;
+ }
- break;
- default:
- break;
+ cpu_job->indirect_csd.job = container_of(submit.jobs[1], struct v3d_csd_job,
+ base);
}
- mutex_unlock(&v3d->sched_lock);
- out_se = (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) ? NULL : &se;
-
- v3d_attach_fences_and_unlock_reservation(file_priv,
- &cpu_job->base,
- &acquire_ctx, 0,
- out_se, cpu_job->base.done_fence);
-
- switch (cpu_job->job_type) {
- case V3D_CPU_JOB_TYPE_INDIRECT_CSD:
- v3d_attach_fences_and_unlock_reservation(file_priv,
- clean_job,
- &cpu_job->indirect_csd.acquire_ctx,
- 0, &se, clean_job->done_fence);
- break;
- default:
- break;
- }
+ ret = v3d_submit_lock_reservations(&submit);
+ if (ret)
+ goto fail;
- v3d_job_put(&cpu_job->base);
- v3d_job_put(&csd_job->base);
- v3d_job_put(clean_job);
+ ret = v3d_submit_jobs(&submit, NULL, &se);
+ if (ret)
+ goto fail_submit;
return 0;
-fail_unreserve:
- mutex_unlock(&v3d->sched_lock);
-
- drm_gem_unlock_reservations(cpu_job->base.bo, cpu_job->base.bo_count,
- &acquire_ctx);
-
- drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count,
- &cpu_job->indirect_csd.acquire_ctx);
-
fail:
- v3d_job_cleanup((void *)cpu_job);
- v3d_job_cleanup((void *)csd_job);
- v3d_job_cleanup(clean_job);
- v3d_put_multisync_post_deps(&se);
+ v3d_submit_cleanup_jobs(&submit);
+fail_submit:
+ v3d_submit_put_post_deps(NULL, &se);
return ret;
}
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index fc76018b044c..0148f2befed1 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1411,7 +1411,7 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_pre_enable = vc4_dsi_bridge_pre_enable,
.atomic_enable = vc4_dsi_bridge_enable,
.atomic_disable = vc4_dsi_bridge_disable,
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index a161d3b00a25..74dce4be0c00 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -508,7 +508,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
static void vc4_hdmi_connector_reset(struct drm_connector *connector)
{
drm_atomic_helper_connector_reset(connector);
- __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
+ __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state);
drm_atomic_helper_connector_tv_margins_reset(connector);
}
diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c b/drivers/gpu/drm/verisilicon/vs_bridge.c
index 7a93049368db..dc7c85b07fe3 100644
--- a/drivers/gpu/drm/verisilicon/vs_bridge.c
+++ b/drivers/gpu/drm/verisilicon/vs_bridge.c
@@ -246,7 +246,7 @@ static const struct drm_bridge_funcs vs_dpi_bridge_funcs = {
.atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dpi,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static const struct drm_bridge_funcs vs_dp_bridge_funcs = {
@@ -258,7 +258,7 @@ static const struct drm_bridge_funcs vs_dp_bridge_funcs = {
.atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dp,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
};
static int vs_bridge_detect_output_interface(struct device_node *of_node,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 405097e388a5..2aaa7cb08085 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -119,10 +119,24 @@ err_free:
return ret;
}
+/*
+ * Release pending virtqueue waits so the drm_dev_enter/exit() critical
+ * sections complete before drm_dev_unplug() blocks on synchronize_srcu().
+ */
+static void virtio_gpu_release_vqs(struct drm_device *dev)
+{
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+
+ vgdev->vqs_released = true;
+ wake_up_all(&vgdev->ctrlq.ack_queue);
+ wake_up_all(&vgdev->cursorq.ack_queue);
+}
+
static void virtio_gpu_remove(struct virtio_device *vdev)
{
struct drm_device *dev = vdev->priv;
+ virtio_gpu_release_vqs(dev);
drm_dev_unplug(dev);
if (drm_core_check_feature(dev, DRIVER_ATOMIC))
@@ -136,6 +150,7 @@ static void virtio_gpu_shutdown(struct virtio_device *vdev)
{
struct drm_device *dev = vdev->priv;
+ virtio_gpu_release_vqs(dev);
/* stop talking to the device */
drm_dev_unplug(dev);
}
@@ -168,6 +183,86 @@ static unsigned int features[] = {
VIRTIO_GPU_F_CONTEXT_INIT,
VIRTIO_GPU_F_BLOB_ALIGNMENT,
};
+
+#ifdef CONFIG_PM_SLEEP
+static void virtgpu_hibernation_restore(struct virtio_gpu_device *vgdev)
+{
+ if (vgdev->hibernated) {
+ vgdev->hibernated = false;
+ virtio_gpu_object_restore_all(vgdev);
+ }
+}
+
+static int virtgpu_freeze(struct virtio_device *vdev)
+{
+ struct drm_device *dev = vdev->priv;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ int error;
+
+ error = drm_mode_config_helper_suspend(dev);
+ if (error) {
+ DRM_ERROR("suspend error: %d\n", error);
+ return error;
+ }
+
+ if (vgdev->hibernated)
+ virtio_gpu_object_unref_all(vgdev);
+
+ flush_work(&vgdev->obj_free_work);
+ flush_work(&vgdev->ctrlq.dequeue_work);
+ flush_work(&vgdev->cursorq.dequeue_work);
+ flush_work(&vgdev->config_changed_work);
+
+ error = virtio_gpu_wait_queue(&vgdev->ctrlq, vgdev->ctrlq.vq->num_max);
+ if (error) {
+ DRM_ERROR("ctrlq wait failed: %d\n", error);
+ goto err_resume;
+ }
+
+ error = virtio_gpu_wait_queue(&vgdev->cursorq, vgdev->cursorq.vq->num_max);
+ if (error) {
+ DRM_ERROR("cursorq wait failed: %d\n", error);
+ goto err_resume;
+ }
+
+ vdev->config->del_vqs(vdev);
+
+ return 0;
+
+err_resume:
+ virtgpu_hibernation_restore(vgdev);
+
+ drm_mode_config_helper_resume(dev);
+
+ return error;
+}
+
+static int virtgpu_restore(struct virtio_device *vdev)
+{
+ struct drm_device *dev = vdev->priv;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ int error;
+
+ error = virtio_gpu_find_vqs(vgdev);
+ if (error) {
+ DRM_ERROR("failed to find virt queues\n");
+ return error;
+ }
+
+ virtio_device_ready(vdev);
+
+ virtgpu_hibernation_restore(vgdev);
+
+ error = drm_mode_config_helper_resume(dev);
+ if (error) {
+ DRM_ERROR("resume error: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+#endif
+
static struct virtio_driver virtio_gpu_driver = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
@@ -176,7 +271,11 @@ static struct virtio_driver virtio_gpu_driver = {
.probe = virtio_gpu_probe,
.remove = virtio_gpu_remove,
.shutdown = virtio_gpu_shutdown,
- .config_changed = virtio_gpu_config_changed
+ .config_changed = virtio_gpu_config_changed,
+#ifdef CONFIG_PM_SLEEP
+ .freeze = virtgpu_freeze,
+ .restore = virtgpu_restore,
+#endif
};
static int __init virtio_gpu_driver_init(void)
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 7449907754a4..17a6a4d26516 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -99,6 +99,10 @@ struct virtio_gpu_object {
int uuid_state;
uuid_t uuid;
+
+ /* for restoration of objects after hibernation */
+ struct virtio_gpu_object_params params;
+ struct list_head restore_node;
};
#define gem_to_virtio_gpu_obj(gobj) \
container_of((gobj), struct virtio_gpu_object, base.base)
@@ -236,6 +240,7 @@ struct virtio_gpu_device {
struct virtio_gpu_queue ctrlq;
struct virtio_gpu_queue cursorq;
+ bool vqs_released;
struct kmem_cache *vbufs;
atomic_t pending_commands;
@@ -259,6 +264,7 @@ struct virtio_gpu_device {
bool has_host_visible;
bool has_context_init;
bool has_blob_alignment;
+ bool hibernated;
struct virtio_shm_region host_visible_region;
struct drm_mm host_visible_mm;
@@ -267,6 +273,8 @@ struct virtio_gpu_device {
struct work_struct obj_free_work;
spinlock_t obj_free_lock;
struct list_head obj_free_list;
+ struct mutex obj_restore_lock;
+ struct list_head obj_restore_list;
struct virtio_gpu_drv_capset *capsets;
uint32_t num_capsets;
@@ -274,6 +282,8 @@ struct virtio_gpu_device {
struct list_head cap_cache;
uint32_t blob_alignment;
+ struct notifier_block pm_nb;
+
/* protects uuid state when exporting */
spinlock_t resource_export_lock;
/* protects map state and host_visible_mm */
@@ -303,6 +313,7 @@ void virtio_gpu_deinit(struct drm_device *dev);
void virtio_gpu_release(struct drm_device *dev);
int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file);
void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file);
+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev);
/* virtgpu_gem.c */
int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
@@ -338,7 +349,8 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs,
struct virtio_gpu_fence *fence);
void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
- struct virtio_gpu_object *bo);
+ struct virtio_gpu_object *bo,
+ bool no_cb);
int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
uint64_t offset,
uint32_t width, uint32_t height,
@@ -424,6 +436,7 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
void virtio_gpu_panic_notify(struct virtio_gpu_device *vgdev);
void virtio_gpu_notify(struct virtio_gpu_device *vgdev);
+int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, unsigned int num_elem);
int
virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev,
@@ -470,6 +483,7 @@ void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
u64 fence_id);
/* virtgpu_object.c */
+void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo);
void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo);
struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
size_t size);
@@ -482,6 +496,14 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo);
int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
uint32_t *resid);
+
+void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo);
+
+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev);
+
+void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev);
+
/* virtgpu_prime.c */
int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo);
@@ -496,6 +518,8 @@ int virtgpu_dma_buf_import_sgt(struct virtio_gpu_mem_entry **ents,
unsigned int *nents,
struct virtio_gpu_object *bo,
struct dma_buf_attachment *attach);
+int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo);
/* virtgpu_debugfs.c */
void virtio_gpu_debugfs_init(struct drm_minor *minor);
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index cfde9f573df6..223d5d513b5a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -23,6 +23,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <linux/suspend.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ring.h>
@@ -115,15 +116,53 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
vgdev->num_capsets = num_capsets;
}
-int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev)
{
struct virtqueue_info vqs_info[] = {
{ "control", virtio_gpu_ctrl_ack },
{ "cursor", virtio_gpu_cursor_ack },
};
- struct virtio_gpu_device *vgdev;
- /* this will expand later */
struct virtqueue *vqs[2];
+ int ret;
+
+ ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL);
+ if (ret)
+ return ret;
+
+ vgdev->ctrlq.vq = vqs[0];
+ vgdev->cursorq.vq = vqs[1];
+
+ return 0;
+}
+
+static int virtio_gpu_pm_notifier(struct notifier_block *nb, unsigned long mode,
+ void *data)
+{
+ struct virtio_gpu_device *vgdev = container_of(nb,
+ struct virtio_gpu_device,
+ pm_nb);
+
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ if (vgdev->has_virgl_3d) {
+ DRM_ERROR("S4 not allowed when VIRGL is enabled\n");
+ return notifier_from_errno(-EPERM);
+ }
+
+ vgdev->hibernated = true;
+ break;
+
+ case PM_POST_HIBERNATION:
+ vgdev->hibernated = false;
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
+{
+ struct virtio_gpu_device *vgdev;
u32 num_scanouts, num_capsets, blob_alignment;
int ret = 0;
@@ -158,6 +197,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
virtio_gpu_array_put_free_work);
INIT_LIST_HEAD(&vgdev->obj_free_list);
spin_lock_init(&vgdev->obj_free_lock);
+ INIT_LIST_HEAD(&vgdev->obj_restore_list);
+ mutex_init(&vgdev->obj_restore_lock);
#ifdef __LITTLE_ENDIAN
if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL))
@@ -215,13 +256,11 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
vgdev->has_context_init ? '+' : '-',
vgdev->has_blob_alignment ? '+' : '-');
- ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL);
+ ret = virtio_gpu_find_vqs(vgdev);
if (ret) {
DRM_ERROR("failed to find virt queues\n");
goto err_vqs;
}
- vgdev->ctrlq.vq = vqs[0];
- vgdev->cursorq.vq = vqs[1];
ret = virtio_gpu_alloc_vbufs(vgdev);
if (ret) {
DRM_ERROR("failed to alloc vbufs\n");
@@ -262,11 +301,25 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
virtio_gpu_cmd_get_edids(vgdev);
virtio_gpu_cmd_get_display_info(vgdev);
virtio_gpu_notify(vgdev);
- wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
- 5 * HZ);
+ if (!wait_event_timeout(vgdev->resp_wq,
+ !vgdev->display_info_pending,
+ 5 * HZ)) {
+ DRM_ERROR("timed out waiting for display info\n");
+ ret = -ETIMEDOUT;
+ goto err_reset_device;
+ }
}
+
+ vgdev->pm_nb.notifier_call = virtio_gpu_pm_notifier;
+ ret = register_pm_notifier(&vgdev->pm_nb);
+ if (ret)
+ goto err_reset_device;
+
return 0;
+err_reset_device:
+ virtio_reset_device(vgdev->vdev);
+ virtio_gpu_modeset_fini(vgdev);
err_scanouts:
virtio_gpu_free_vbufs(vgdev);
err_vbufs:
@@ -290,12 +343,14 @@ void virtio_gpu_deinit(struct drm_device *dev)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
+ unregister_pm_notifier(&vgdev->pm_nb);
flush_work(&vgdev->obj_free_work);
flush_work(&vgdev->ctrlq.dequeue_work);
flush_work(&vgdev->cursorq.dequeue_work);
flush_work(&vgdev->config_changed_work);
virtio_reset_device(vgdev->vdev);
vgdev->vdev->config->del_vqs(vgdev->vdev);
+ mutex_destroy(&vgdev->obj_restore_lock);
}
void virtio_gpu_release(struct drm_device *dev)
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index ec9efacc6919..9bc0bd68c314 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -63,6 +63,15 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t
ida_free(&vgdev->resource_ida, id - 1);
}
+void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+
+ mutex_lock(&vgdev->obj_restore_lock);
+ list_del_init(&bo->restore_node);
+ mutex_unlock(&vgdev->obj_restore_lock);
+}
+
void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
{
struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
@@ -94,7 +103,8 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj)
struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
if (bo->created) {
- virtio_gpu_cmd_unref_resource(vgdev, bo);
+ virtio_gpu_remove_from_restore_list(bo);
+ virtio_gpu_cmd_unref_resource(vgdev, bo, false);
virtio_gpu_notify(vgdev);
/* completion handler calls virtio_gpu_cleanup_object() */
return;
@@ -220,6 +230,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
return PTR_ERR(shmem_obj);
bo = gem_to_virtio_gpu_obj(&shmem_obj->base);
+ INIT_LIST_HEAD(&bo->restore_node);
+
ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle);
if (ret < 0)
goto err_free_gem;
@@ -258,6 +270,12 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
virtio_gpu_object_attach(vgdev, bo, ents, nents);
}
+ if (!params->virgl) {
+ /* store non-virgl object with its param to the restore list */
+ bo->params = *params;
+ virtio_gpu_add_object_to_restore_list(vgdev, bo);
+ }
+
*bo_ptr = bo;
return 0;
@@ -271,3 +289,73 @@ err_free_gem:
drm_gem_shmem_free(shmem_obj);
return ret;
}
+
+void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo)
+{
+ mutex_lock(&vgdev->obj_restore_lock);
+ list_add_tail(&bo->restore_node, &vgdev->obj_restore_list);
+ mutex_unlock(&vgdev->obj_restore_lock);
+}
+
+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_object *bo, *tmp;
+ struct virtio_gpu_mem_entry *ents;
+ unsigned int nents;
+ int ret = 0;
+
+ mutex_lock(&vgdev->obj_restore_lock);
+ list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
+ restore_node) {
+ if (drm_gem_is_imported(&bo->base.base)) {
+ ret = virtgpu_dma_buf_obj_resubmit(vgdev, bo);
+ if (ret)
+ break;
+
+ continue;
+ }
+
+ if (bo->params.blob || bo->attached) {
+ ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents,
+ &nents);
+ if (ret)
+ break;
+ }
+
+ if (bo->params.blob) {
+ virtio_gpu_cmd_resource_create_blob(vgdev, bo,
+ &bo->params,
+ ents, nents);
+ } else {
+ virtio_gpu_cmd_create_resource(vgdev, bo, &bo->params,
+ NULL, NULL);
+ if (bo->attached) {
+ bo->attached = false;
+ virtio_gpu_object_attach(vgdev, bo, ents,
+ nents);
+ }
+ }
+ }
+ mutex_unlock(&vgdev->obj_restore_lock);
+
+ if (ret)
+ DRM_ERROR("failed to restore virtio-gpu objects: %d\n", ret);
+
+ return ret;
+}
+
+void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_object *bo, *tmp;
+
+ mutex_lock(&vgdev->obj_restore_lock);
+ list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
+ restore_node)
+ if (bo->created) {
+ virtio_gpu_cmd_unref_resource(vgdev, bo, true);
+ virtio_gpu_notify(vgdev);
+ }
+
+ mutex_unlock(&vgdev->obj_restore_lock);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c
index 70b3b836e1c9..216c77cd0d21 100644
--- a/drivers/gpu/drm/virtio/virtgpu_prime.c
+++ b/drivers/gpu/drm/virtio/virtgpu_prime.c
@@ -23,6 +23,7 @@
*/
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <linux/virtio_dma_buf.h>
#include "virtgpu_drv.h"
@@ -216,7 +217,8 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj)
}
if (bo->created) {
- virtio_gpu_cmd_unref_resource(vgdev, bo);
+ virtio_gpu_remove_from_restore_list(bo);
+ virtio_gpu_cmd_unref_resource(vgdev, bo, false);
virtio_gpu_notify(vgdev);
return;
}
@@ -262,6 +264,13 @@ static int virtgpu_dma_buf_init_obj(struct drm_device *dev,
dma_buf_unpin(attach);
dma_resv_unlock(resv);
+ /*
+ * Store the dmabuf imported object with its params to the
+ * restore list.
+ */
+ bo->params = params;
+ virtio_gpu_add_object_to_restore_list(vgdev, bo);
+
return 0;
err_import:
@@ -272,6 +281,38 @@ err_pin:
return ret;
}
+int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_mem_entry *ents;
+ struct scatterlist *sl;
+ int i;
+
+ if (!bo->sgt) {
+ DRM_ERROR("no sgt bound to virtio_gpu_object\n");
+ return -ENOMEM;
+ }
+
+ ents = kvmalloc_array(bo->sgt->nents,
+ sizeof(struct virtio_gpu_mem_entry),
+ GFP_KERNEL);
+ if (!ents) {
+ DRM_ERROR("failed to allocate ent list\n");
+ return -ENOMEM;
+ }
+
+ for_each_sgtable_dma_sg(bo->sgt, sl, i) {
+ ents[i].addr = cpu_to_le64(sg_dma_address(sl));
+ ents[i].length = cpu_to_le32(sg_dma_len(sl));
+ ents[i].padding = 0;
+ }
+
+ virtio_gpu_cmd_resource_create_blob(vgdev, bo, &bo->params,
+ ents, bo->sgt->nents);
+
+ return 0;
+}
+
static const struct drm_gem_object_funcs virtgpu_gem_dma_buf_funcs = {
.free = virtgpu_dma_buf_free_obj,
};
@@ -317,6 +358,8 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev,
if (!bo)
return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&bo->restore_node);
+
obj = &bo->base.base;
obj->resv = buf->resv;
obj->funcs = &virtgpu_gem_dma_buf_funcs;
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index c8b9475a7472..905b1f42cd98 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -369,6 +369,20 @@ static int virtio_gpu_panic_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
return 0;
}
+int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, unsigned int num_elem)
+{
+ int ret;
+
+ /* Wait up to 5 seconds for enough free slots to become available */
+ ret = wait_event_timeout(vgvq->ack_queue,
+ vgvq->vq->num_free >= num_elem,
+ 5 * HZ);
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf,
struct virtio_gpu_fence *fence,
@@ -396,7 +410,19 @@ again:
if (vq->num_free < elemcnt) {
spin_unlock(&vgdev->ctrlq.qlock);
virtio_gpu_notify(vgdev);
- wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= elemcnt);
+ wait_event(vgdev->ctrlq.ack_queue,
+ vq->num_free >= elemcnt || vgdev->vqs_released);
+ /*
+ * Set by virtio_gpu_release_vqs() to unblock
+ * synchronize_srcu() wait in drm_dev_unplug().
+ */
+ if (vgdev->vqs_released) {
+ if (fence && vbuf->objs)
+ virtio_gpu_array_unlock_resv(vbuf->objs);
+ free_vbuf(vgdev, vbuf);
+ drm_dev_exit(idx);
+ return -ENODEV;
+ }
goto again;
}
@@ -566,7 +592,14 @@ retry:
ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
spin_unlock(&vgdev->cursorq.qlock);
- wait_event(vgdev->cursorq.ack_queue, vq->num_free >= outcnt);
+ wait_event(vgdev->cursorq.ack_queue,
+ vq->num_free >= outcnt || vgdev->vqs_released);
+ /* See comment in virtio_gpu_queue_ctrl_sgs(). */
+ if (vgdev->vqs_released) {
+ free_vbuf(vgdev, vbuf);
+ drm_dev_exit(idx);
+ return;
+ }
spin_lock(&vgdev->cursorq.qlock);
goto retry;
} else {
@@ -626,14 +659,21 @@ static void virtio_gpu_cmd_unref_cb(struct virtio_gpu_device *vgdev,
}
void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
- struct virtio_gpu_object *bo)
+ struct virtio_gpu_object *bo,
+ bool no_cb)
{
struct virtio_gpu_resource_unref *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
int ret;
- cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
- virtio_gpu_cmd_unref_cb);
+ if (no_cb) {
+ cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
+ NULL);
+ } else {
+ cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
+ virtio_gpu_cmd_unref_cb);
+ }
+
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
diff --git a/drivers/gpu/drm/virtio/virtgpu_vram.c b/drivers/gpu/drm/virtio/virtgpu_vram.c
index 4ae3cbc35dd3..e2e799b42c61 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vram.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vram.c
@@ -20,7 +20,8 @@ static void virtio_gpu_vram_free(struct drm_gem_object *obj)
if (unmap)
virtio_gpu_cmd_unmap(vgdev, bo);
- virtio_gpu_cmd_unref_resource(vgdev, bo);
+ virtio_gpu_remove_from_restore_list(bo);
+ virtio_gpu_cmd_unref_resource(vgdev, bo, false);
virtio_gpu_notify(vgdev);
return;
}
@@ -207,6 +208,8 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
obj = &vram->base.base.base;
obj->funcs = &virtio_gpu_vram_funcs;
+ INIT_LIST_HEAD(&vram->base.restore_node);
+
params->size = PAGE_ALIGN(params->size);
drm_gem_private_object_init(vgdev->ddev, obj, params->size);
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 4c80bac67622..85e6d9a0f575 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -670,7 +670,7 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
dma_resv_iter_begin(&cursor, bo->ttm.base.resv,
DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence)
- dma_fence_enable_sw_signaling(fence);
+ dma_fence_enable_signaling(fence);
dma_resv_iter_end(&cursor);
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index abe25aedeead..4efd2bc8c02e 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -396,7 +396,7 @@ static const struct drm_driver regular_driver = {
XE_DISPLAY_DRIVER_FEATURES |
DRIVER_GEM |
DRIVER_RENDER | DRIVER_SYNCOBJ |
- DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA,
+ DRIVER_SYNCOBJ_TIMELINE,
.open = xe_file_open,
.postclose = xe_file_close,
@@ -427,7 +427,7 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = {
static const struct drm_driver admin_only_driver = {
.driver_features =
XE_DISPLAY_DRIVER_FEATURES |
- DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA,
+ DRIVER_GEM | DRIVER_RENDER,
.open = xe_file_open,
.postclose = xe_file_close,
.ioctls = xe_ioctls_admin_only,
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index ae5b38b2a884..a4fa00632a30 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -214,7 +214,7 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error)
trace_xe_sched_job_set_error(job);
- dma_fence_enable_sw_signaling(job->fence);
+ dma_fence_enable_signaling(job->fence);
xe_hw_fence_irq_run(job->q->fence_irq);
}
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index b1e1ac26c66d..e0f0c23d172d 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -1090,7 +1090,7 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL,
false, MAX_SCHEDULE_TIMEOUT);
else if (pre_migrate_fence)
- dma_fence_enable_sw_signaling(pre_migrate_fence);
+ dma_fence_enable_signaling(pre_migrate_fence);
}
drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
index 6f71bc66b14e..1d2ab678faf5 100644
--- a/drivers/gpu/drm/xe/xe_userptr.c
+++ b/drivers/gpu/drm/xe/xe_userptr.c
@@ -180,7 +180,7 @@ xe_vma_userptr_invalidate_pass1(struct xe_vm *vm, struct xe_userptr_vma *uvma)
dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
- dma_fence_enable_sw_signaling(fence);
+ dma_fence_enable_signaling(fence);
if (signaled && !dma_fence_is_signaled(fence))
signaled = false;
}
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 080c2fff0e95..73ac031ffb04 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -256,7 +256,7 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
*/
wait = __xe_vm_userptr_needs_repin(vm) || preempt_fences_waiting(vm);
if (wait)
- dma_fence_enable_sw_signaling(pfence);
+ dma_fence_enable_signaling(pfence);
xe_svm_notifier_unlock(vm);
@@ -287,7 +287,7 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
--vm->preempt.num_exec_queues;
}
if (q->lr.pfence) {
- dma_fence_enable_sw_signaling(q->lr.pfence);
+ dma_fence_enable_signaling(q->lr.pfence);
dma_fence_put(q->lr.pfence);
q->lr.pfence = NULL;
}
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 7fb11b0a44f0..b209582bc130 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -2280,7 +2280,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
.atomic_disable = zynqmp_dp_bridge_atomic_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_check = zynqmp_dp_bridge_atomic_check,
.detect = zynqmp_dp_bridge_detect,
.edid_read = zynqmp_dp_bridge_edid_read,