summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-08-08 14:13:33 +1000
committerDave Airlie <airlied@redhat.com>2026-08-08 14:13:58 +1000
commite4d41a34eedb808d423d73cae8d8601be32f307e (patch)
treeb81478f91e13b57bce5a3580fde622a4f710d7d2 /drivers/gpu
parent7dda2a8bfde23c8f787aa34e5ce40725a6be65b9 (diff)
parentbd4f284df04d76fd65e57141cb1e6e7a49e4c3cb (diff)
downloadlinux-e4d41a34eedb808d423d73cae8d8601be32f307e.tar.gz
linux-e4d41a34eedb808d423d73cae8d8601be32f307e.zip
Merge tag 'drm-misc-next-2026-08-06' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.3: UAPI Changes: - Remove the default udmabuf size limit of 64MB. Cross-subsystem Changes: - Add dmemcg support for eviction, and hook it up for amdgpu and xe. Core Changes: - Changes to TTM to be more aggressive when allocating below protection limit! - Improve dt binding documentation for renesas. - Add helper to convert physical address back to buddy block, add that to and improve its kunit test. Driver Changes: - Assorted small fixes to ti-sn65dsi86, panthor, imagination, omapdrm, bridge/synopsys, panel-edp, ssd130x, panel/tdo-tl070wsh30. - Add Sharp LQ120P1JX51 panel. - Add dmemcg support to nouveau. - Various updates and improvements to sun4i, among which YUV and 4k support. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/917d462a-8976-4a15-bec4-4513ec51c5c0@linux.intel.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/Kconfig3
-rw-r--r--drivers/gpu/buddy.c53
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c65
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h3
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c10
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx6345.c2
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c4
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c4
-rw-r--r--drivers/gpu/drm/bridge/chrontel-ch7033.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/Kconfig11
-rw-r--r--drivers/gpu/drm/bridge/imx/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/imx/imx93-pdfc.c222
-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/lontium-lt8713sx.c2
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8912b.c6
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9211.c4
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c4
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611uxc.c2
-rw-r--r--drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c8
-rw-r--r--drivers/gpu/drm/bridge/nxp-ptn3460.c4
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8622.c8
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c2
-rw-r--r--drivers/gpu/drm/bridge/sii9234.c2
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c1
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c3
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c1
-rw-r--r--drivers/gpu/drm/bridge/tc358762.c19
-rw-r--r--drivers/gpu/drm/bridge/tc358764.c19
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358768.c4
-rw-r--r--drivers/gpu/drm/bridge/tc358775.c4
-rw-r--r--drivers/gpu/drm/bridge/tda998x_drv.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-dlpc3433.c2
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c6
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c6
-rw-r--r--drivers/gpu/drm/bridge/ti-tfp410.c2
-rw-r--r--drivers/gpu/drm/drm_drv.c8
-rw-r--r--drivers/gpu/drm/imagination/pvr_ccb.c6
-rw-r--r--drivers/gpu/drm/imagination/pvr_check.h36
-rw-r--r--drivers/gpu/drm/imagination/pvr_device_info.h6
-rw-r--r--drivers/gpu/drm/imagination/pvr_gem.h12
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c3
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h26
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h9
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h9
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_heap_config.h10
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_mips_check.h71
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base.h6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base507c.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base827c.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base907c.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/curs507a.c3
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.c40
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.h11
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c8
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dsi.c7
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dsi.h2
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c47
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9805.c212
-rw-r--r--drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c249
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36536.c4
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c33
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7703.c96
-rw-r--r--drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c40
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c2
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c22
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c11
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c65
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c3
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_framebuffer.c19
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c22
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_tcon_top.c71
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_tcon_top.h6
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.c20
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.h1
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c320
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c3
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c105
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.c71
-rw-r--r--drivers/gpu/tests/.kunitconfig2
-rw-r--r--drivers/gpu/tests/gpu_buddy_test.c102
96 files changed, 1811 insertions, 528 deletions
diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig
index ebb2ad4b7ea0..3bcf08260d73 100644
--- a/drivers/gpu/Kconfig
+++ b/drivers/gpu/Kconfig
@@ -7,7 +7,8 @@ config GPU_BUDDY
config GPU_BUDDY_KUNIT_TEST
tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS
- depends on GPU_BUDDY && KUNIT
+ depends on KUNIT
+ select GPU_BUDDY
default KUNIT_ALL_TESTS
help
KUnit tests for the GPU buddy allocator.
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index dc81fe0301ce..4d5ac375a538 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -630,6 +630,59 @@ void gpu_buddy_free_block(struct gpu_buddy *mm,
}
EXPORT_SYMBOL(gpu_buddy_free_block);
+/**
+ * gpu_buddy_allocated_addr_to_block - given relative address find the allocated block
+ *
+ * @mm: GPU buddy manager
+ * @addr: Relative address
+ *
+ * Returns:
+ * gpu_buddy_block on success, NULL or error code on failure
+ */
+struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr)
+{
+ struct gpu_buddy_block *block;
+ LIST_HEAD(dfs);
+ u64 end;
+ int i;
+
+ gpu_buddy_driver_lock_held(mm);
+
+ end = addr + mm->chunk_size - 1;
+ for (i = 0; i < mm->n_roots; ++i)
+ list_add_tail(&mm->roots[i]->tmp_link, &dfs);
+
+ do {
+ u64 block_start;
+ u64 block_end;
+
+ block = list_first_entry_or_null(&dfs,
+ struct gpu_buddy_block,
+ tmp_link);
+ if (!block)
+ break;
+
+ list_del(&block->tmp_link);
+
+ block_start = gpu_buddy_block_offset(block);
+ block_end = block_start + gpu_buddy_block_size(mm, block) - 1;
+
+ if (!overlaps(addr, end, block_start, block_end))
+ continue;
+
+ if (gpu_buddy_block_is_allocated(block))
+ return block;
+ else if (gpu_buddy_block_is_free(block))
+ return NULL;
+
+ list_add(&block->right->tmp_link, &dfs);
+ list_add(&block->left->tmp_link, &dfs);
+ } while (1);
+
+ return ERR_PTR(-ENXIO);
+}
+EXPORT_SYMBOL(gpu_buddy_allocated_addr_to_block);
+
static void __gpu_buddy_free_list(struct gpu_buddy *mm,
struct list_head *objects,
bool mark_clear,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 5bcb6a966941..74c1521b8efc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1526,7 +1526,7 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
dma_resv_for_each_fence(&resv_cursor, bo->base.resv,
DMA_RESV_USAGE_BOOKKEEP, f) {
if (amdkfd_fence_check_mm(f, current->mm) &&
- !(place->flags & TTM_PL_FLAG_CONTIGUOUS))
+ !(place && (place->flags & TTM_PL_FLAG_CONTIGUOUS)))
return false;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index 2a241a5b12c4..249f07f30951 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -23,6 +23,8 @@
*/
#include <linux/dma-mapping.h>
+#include <linux/cgroup_dmem.h>
+#include <linux/pm_runtime.h>
#include <drm/ttm/ttm_range_manager.h>
#include <drm/drm_drv.h>
#include <drm/drm_buddy.h>
@@ -905,6 +907,38 @@ static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
.debug = amdgpu_vram_mgr_debug
};
+static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops;
+
+static int amdgpu_vram_mgr_dmem_reclaim(struct dmem_cgroup_pool_state *pool,
+ u64 target_bytes, void *priv)
+{
+ struct ttm_resource_manager *man = priv;
+ struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
+ int ret, idx;
+
+ if (!drm_dev_enter(adev_to_drm(adev), &idx))
+ return -ENODEV;
+
+ ret = pm_runtime_get_sync(adev_to_drm(adev)->dev);
+ if (ret < 0) {
+ pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
+ goto out;
+ }
+
+ ret = ttm_resource_manager_dmem_reclaim(pool, target_bytes, priv);
+
+ pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
+ pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
+
+out:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops = {
+ .reclaim = amdgpu_vram_mgr_dmem_reclaim,
+};
+
/**
* amdgpu_vram_mgr_init - init VRAM manager and DRM MM
*
@@ -916,11 +950,9 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
+ struct dmem_cgroup_region *cg;
int err;
- man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size);
- if (IS_ERR(man->cg))
- return PTR_ERR(man->cg);
ttm_resource_manager_init(man, &adev->mman.bdev,
adev->gmc.real_vram_size);
@@ -935,6 +967,20 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
if (err)
return err;
+ cg = dmem_cgroup_register_region(&(struct dmem_cgroup_init){
+ .size = adev->gmc.real_vram_size,
+ .ops = &amdgpu_vram_mgr_dmem_ops,
+ .reclaim_priv = man,
+ },
+ "drm/%s/vram", adev_to_drm(adev)->unique);
+ if (IS_ERR(cg)) {
+ gpu_buddy_fini(&mgr->mm);
+ return PTR_ERR(cg);
+ }
+
+ mgr->cg_region = cg;
+ ttm_resource_manager_set_dmem_region(man, cg);
+
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
ttm_resource_manager_set_used(man, true);
return 0;
@@ -958,6 +1004,19 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
ttm_resource_manager_set_used(man, false);
ret = ttm_resource_manager_evict_all(&adev->mman.bdev, man);
+
+ /*
+ * Unregister the dmem cgroup region regardless of the evict_all()
+ * result and before any further teardown. This drains in-flight
+ * reclaim callbacks and blocks new ones, so no reclaim can reference
+ * the manager once we start freeing it. It must run after evict_all()
+ * so that ttm_resource_free() can still uncharge via man->cg during
+ * eviction. Clear man->cg afterwards.
+ */
+ dmem_cgroup_unregister_region(mgr->cg_region);
+ mgr->cg_region = NULL;
+ ttm_resource_manager_set_dmem_region(man, NULL);
+
if (ret)
return;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
index 429a21a2e9b2..e581cbf52fcb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
@@ -26,6 +26,8 @@
#include <linux/gpu_buddy.h>
+struct dmem_cgroup_region;
+
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager;
struct gpu_buddy mm;
@@ -36,6 +38,7 @@ struct amdgpu_vram_mgr {
atomic64_t vis_usage;
u64 default_page_size;
struct list_head allocated_vres_list;
+ struct dmem_cgroup_region *cg_region;
};
struct amdgpu_vres_task {
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index db99e252aeeb..038b852405b5 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -1471,11 +1471,11 @@ static const struct adv7511_chip_info adv7535_chip_info = {
};
static const struct i2c_device_id adv7511_i2c_ids[] = {
- { "adv7511", (kernel_ulong_t)&adv7511_chip_info },
- { "adv7511w", (kernel_ulong_t)&adv7511_chip_info },
- { "adv7513", (kernel_ulong_t)&adv7511_chip_info },
- { "adv7533", (kernel_ulong_t)&adv7533_chip_info },
- { "adv7535", (kernel_ulong_t)&adv7535_chip_info },
+ { .name = "adv7511", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
+ { .name = "adv7511w", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
+ { .name = "adv7513", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
+ { .name = "adv7533", .driver_data = (kernel_ulong_t)&adv7533_chip_info },
+ { .name = "adv7535", .driver_data = (kernel_ulong_t)&adv7535_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
index d152c02419f5..407834a03b63 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
@@ -783,7 +783,7 @@ static void anx6345_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx6345_id[] = {
- { "anx6345" },
+ { .name = "anx6345" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, anx6345_id);
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index ebd34657908e..c3af88e14f8b 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -2993,8 +2993,8 @@ static void anx7625_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx7625_id[] = {
- { "anx7625" },
- {}
+ { .name = "anx7625" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, anx7625_id);
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index eb628a0ce6ec..0f6173c61502 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -784,8 +784,8 @@ static struct mipi_dsi_driver chipone_dsi_driver = {
};
static const struct i2c_device_id chipone_i2c_id[] = {
- { "chipone,icn6211" },
- {},
+ { .name = "chipone,icn6211" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, chipone_i2c_id);
diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c
index 92d464727e41..4bc64b2885f7 100644
--- a/drivers/gpu/drm/bridge/chrontel-ch7033.c
+++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c
@@ -601,7 +601,7 @@ static const struct of_device_id ch7033_dt_ids[] = {
MODULE_DEVICE_TABLE(of, ch7033_dt_ids);
static const struct i2c_device_id ch7033_ids[] = {
- { "ch7033" },
+ { .name = "ch7033" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ch7033_ids);
diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig
index 005a745e5c47..49fcecfee38e 100644
--- a/drivers/gpu/drm/bridge/imx/Kconfig
+++ b/drivers/gpu/drm/bridge/imx/Kconfig
@@ -107,4 +107,15 @@ config DRM_IMX93_MIPI_DSI
Choose this to enable MIPI DSI controller found in Freescale i.MX93
processor.
+config DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER
+ tristate "NXP i.MX91/i.MX93 parallel display format converter"
+ depends on OF
+ select DRM_KMS_HELPER
+ help
+ On i.MX93 and i.MX91 SoCs the parallel display format output is
+ controlled via the MEDIAMIX BLK-CTRL DISPLAY_MUX.
+
+ Say 'Y' or 'M' if you use the parallel display output path on a
+ i.MX93 or i.MX91 SoC.
+
endif # ARCH_MXC || COMPILE_TEST
diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile
index 94ac8c40ebe9..99f28cd013ae 100644
--- a/drivers/gpu/drm/bridge/imx/Makefile
+++ b/drivers/gpu/drm/bridge/imx/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER) += imx8qxp-pixel-combiner.o
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
obj-$(CONFIG_DRM_IMX93_MIPI_DSI) += imx93-mipi-dsi.o
+obj-$(CONFIG_DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER) += imx93-pdfc.o
diff --git a/drivers/gpu/drm/bridge/imx/imx93-pdfc.c b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c
new file mode 100644
index 000000000000..2d6ca95ed554
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2022-2025 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+
+#define IMX93_DISPLAY_MUX_REG 0x60
+#define PARALLEL_DISP_FORMAT GENMASK(10, 8)
+#define FORMAT_RGB888_TO_RGB888 FIELD_PREP(PARALLEL_DISP_FORMAT, 0)
+#define FORMAT_RGB888_TO_RGB666 FIELD_PREP(PARALLEL_DISP_FORMAT, 1)
+#define FORMAT_RGB565_TO_RGB565 FIELD_PREP(PARALLEL_DISP_FORMAT, 2)
+
+struct imx93_pdfc {
+ struct drm_bridge bridge;
+ struct device *dev;
+ struct regmap *regmap;
+ u32 phy_bus_width;
+};
+
+static struct imx93_pdfc *bridge_to_imx93_pdfc(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct imx93_pdfc, bridge);
+}
+
+static int
+imx93_pdfc_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ return drm_bridge_attach(bridge->encoder, bridge->next_bridge, bridge, flags);
+}
+
+static void imx93_pdfc_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_commit *state)
+{
+ struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
+ const struct drm_bridge_state *bridge_state;
+ unsigned int mask = PARALLEL_DISP_FORMAT;
+ unsigned int val;
+
+ bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+
+ switch (bridge_state->output_bus_cfg.format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_FIXED:
+ val = FORMAT_RGB888_TO_RGB888;
+ if (pdfc->phy_bus_width == 18) {
+ /*
+ * Can be valid if physical bus limitation exists,
+ * therefore use dev_dbg().
+ */
+ dev_dbg(pdfc->dev, "Truncate two LSBs from each color\n");
+ val = FORMAT_RGB888_TO_RGB666;
+ }
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ val = FORMAT_RGB888_TO_RGB666;
+ break;
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ val = FORMAT_RGB565_TO_RGB565;
+ break;
+ }
+
+ regmap_update_bits(pdfc->regmap, IMX93_DISPLAY_MUX_REG, mask, val);
+}
+
+/* TODO: Add YUV formats */
+static const u32 imx93_pdfc_bus_output_fmts[] = {
+ MEDIA_BUS_FMT_FIXED,
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_RGB666_1X18,
+ MEDIA_BUS_FMT_RGB565_1X16,
+};
+
+static bool imx93_pdfc_bus_output_fmt_supported(u32 fmt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx93_pdfc_bus_output_fmts); i++) {
+ if (imx93_pdfc_bus_output_fmts[i] == fmt)
+ return true;
+ }
+
+ return false;
+}
+
+static u32 *
+imx93_pdfc_bridge_atomic_get_input_bus_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 *input_fmts;
+
+ *num_input_fmts = 0;
+
+ if (!imx93_pdfc_bus_output_fmt_supported(output_fmt))
+ return NULL;
+
+ input_fmts = kmalloc_obj(*input_fmts);
+ if (!input_fmts)
+ return NULL;
+
+ *num_input_fmts = 1;
+
+ switch (output_fmt) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ input_fmts[0] = output_fmt;
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_FIXED:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ }
+
+ return input_fmts;
+}
+
+static int imx93_pdfc_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
+ u32 format = bridge_state->output_bus_cfg.format;
+
+ if (imx93_pdfc_bus_output_fmt_supported(format))
+ return 0;
+
+ dev_warn(pdfc->dev, "Unsupported output bus format: 0x%x\n", format);
+
+ return -EINVAL;
+}
+
+static const struct drm_bridge_funcs funcs = {
+ .attach = imx93_pdfc_bridge_attach,
+ .atomic_enable = imx93_pdfc_bridge_atomic_enable,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = imx93_pdfc_bridge_atomic_get_input_bus_fmts,
+ .atomic_check = imx93_pdfc_bridge_atomic_check,
+ .atomic_create_state = drm_atomic_helper_bridge_create_state,
+};
+
+static int imx93_pdfc_bridge_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct drm_bridge *next_bridge;
+ struct imx93_pdfc *pdfc;
+ struct device_node *ep;
+ int err;
+
+ pdfc = devm_drm_bridge_alloc(dev, struct imx93_pdfc, bridge, &funcs);
+ if (IS_ERR(pdfc))
+ return PTR_ERR(pdfc);
+
+ pdfc->regmap = syscon_node_to_regmap(dev->of_node->parent);
+ if (IS_ERR(pdfc->regmap))
+ return dev_err_probe(dev, PTR_ERR(pdfc->regmap),
+ "failed to get regmap\n");
+
+ /* No limits per default */
+ pdfc->phy_bus_width = 24;
+
+ /* Get output ep (port1/endpoint) */
+ ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
+ if (ep) {
+ err = of_property_read_u32(ep, "bus-width", &pdfc->phy_bus_width);
+ of_node_put(ep);
+
+ /* bus-width is optional but it must have valid data if present */
+ if (err && err != -EINVAL)
+ return dev_err_probe(dev, err,
+ "failed to query bus-width\n");
+ }
+
+ next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(next_bridge))
+ return dev_err_probe(dev, PTR_ERR(next_bridge),
+ "failed to get next bridge\n");
+ pdfc->dev = dev;
+ pdfc->bridge.of_node = dev->of_node;
+ pdfc->bridge.type = DRM_MODE_CONNECTOR_DPI;
+ pdfc->bridge.next_bridge = drm_bridge_get(next_bridge);
+
+ return devm_drm_bridge_add(dev, &pdfc->bridge);
+}
+
+static const struct of_device_id imx93_pdfc_dt_ids[] = {
+ { .compatible = "nxp,imx91-pdfc", },
+ { .compatible = "nxp,imx93-pdfc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx93_pdfc_dt_ids);
+
+static struct platform_driver imx93_pdfc_bridge_driver = {
+ .probe = imx93_pdfc_bridge_probe,
+ .driver = {
+ .of_match_table = imx93_pdfc_dt_ids,
+ .name = "imx93_pdfc",
+ },
+};
+module_platform_driver(imx93_pdfc_bridge_driver);
+
+MODULE_DESCRIPTION("NXP i.MX93 parallel display format configuration driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c
index 4c0b670806cc..21ac8e58b9d2 100644
--- a/drivers/gpu/drm/bridge/ite-it6263.c
+++ b/drivers/gpu/drm/bridge/ite-it6263.c
@@ -910,7 +910,7 @@ static const struct of_device_id it6263_of_match[] = {
MODULE_DEVICE_TABLE(of, it6263_of_match);
static const struct i2c_device_id it6263_i2c_ids[] = {
- { "it6263" },
+ { .name = "it6263" },
{ }
};
MODULE_DEVICE_TABLE(i2c, it6263_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 8ecb43611dba..025a19c1e530 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -3652,7 +3652,7 @@ static void it6505_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id it6505_id[] = {
- { "it6505" },
+ { .name = "it6505" },
{ }
};
diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
index 4a6420b876da..bfff28883280 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
@@ -572,7 +572,7 @@ static void lt8713sx_remove(struct i2c_client *client)
}
static struct i2c_device_id lt8713sx_id[] = {
- { "lontium,lt8713sx", 0 },
+ { .name = "lontium,lt8713sx" },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c
index df5d07e7644c..e347d04b7b2d 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8912b.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c
@@ -677,7 +677,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(lt8912_bridge_pm_ops, lt8912_bridge_suspend, lt8
static int lt8912_get_regulators(struct lt8912 *lt)
{
unsigned int i;
- const char * const supply_names[] = {
+ static const char * const supply_names[] = {
"vdd", "vccmipirx", "vccsysclk", "vcclvdstx",
"vcchdmitx", "vcclvdspll", "vcchdmipll"
};
@@ -820,8 +820,8 @@ static const struct of_device_id lt8912_dt_match[] = {
MODULE_DEVICE_TABLE(of, lt8912_dt_match);
static const struct i2c_device_id lt8912_id[] = {
- { "lt8912" },
- {}
+ { .name = "lt8912" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lt8912_id);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c
index 6d97ae5640c4..23682384daf4 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9211.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9211.c
@@ -773,8 +773,8 @@ static void lt9211_remove(struct i2c_client *client)
}
static const struct i2c_device_id lt9211_id[] = {
- { "lontium,lt9211" },
- {},
+ { .name = "lontium,lt9211" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lt9211_id);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index b6a368f71285..fb34f661ee0a 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -1229,8 +1229,8 @@ static void lt9611_remove(struct i2c_client *client)
}
static const struct i2c_device_id lt9611_id[] = {
- { "lontium,lt9611" },
- {}
+ { .name = "lontium,lt9611" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lt9611_id);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 6625ba23761c..42a6503cd0f9 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -899,7 +899,7 @@ static void lt9611uxc_remove(struct i2c_client *client)
}
static const struct i2c_device_id lt9611uxc_id[] = {
- { "lontium,lt9611uxc" },
+ { .name = "lontium,lt9611uxc" },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
index 25ff0a0acc93..4d244e6d4ed9 100644
--- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
@@ -317,8 +317,8 @@ static void stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c)
}
static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = {
- { "stdp4028_ge_fw" },
- {}
+ { .name = "stdp4028_ge_fw" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table);
@@ -364,8 +364,8 @@ static void stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c)
}
static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = {
- { "stdp2690_ge_fw" },
- {}
+ { .name = "stdp2690_ge_fw" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table);
diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
index a9b4ac2defc9..f9eff8bfceb7 100644
--- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
+++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
@@ -323,8 +323,8 @@ static void ptn3460_remove(struct i2c_client *client)
}
static const struct i2c_device_id ptn3460_i2c_table[] = {
- { "ptn3460" },
- {}
+ { .name = "ptn3460" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
index 33844db753aa..3a2ec482037d 100644
--- a/drivers/gpu/drm/bridge/parade-ps8622.c
+++ b/drivers/gpu/drm/bridge/parade-ps8622.c
@@ -534,10 +534,10 @@ static void ps8622_remove(struct i2c_client *client)
}
static const struct i2c_device_id ps8622_i2c_table[] = {
- /* Device type, max_lane_count */
- {"ps8622", 1},
- {"ps8625", 2},
- {},
+ /* Device type, driver_data holds the maximal lane_count */
+ { .name = "ps8622", .driver_data = 1 },
+ { .name = "ps8625", .driver_data = 2 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index f575b2dbe32b..9aac44ed5b67 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -1235,7 +1235,7 @@ static const struct of_device_id sii902x_dt_ids[] = {
MODULE_DEVICE_TABLE(of, sii902x_dt_ids);
static const struct i2c_device_id sii902x_i2c_ids[] = {
- { "sii9022" },
+ { .name = "sii9022" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c
index be67642ab7d3..9fef332db248 100644
--- a/drivers/gpu/drm/bridge/sii9234.c
+++ b/drivers/gpu/drm/bridge/sii9234.c
@@ -944,7 +944,7 @@ static const struct of_device_id sii9234_dt_match[] = {
MODULE_DEVICE_TABLE(of, sii9234_dt_match);
static const struct i2c_device_id sii9234_id[] = {
- { "SII9234" },
+ { .name = "SII9234" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sii9234_id);
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 22c81b38b41e..b452748ad9db 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2374,7 +2374,7 @@ static const struct of_device_id sii8620_dt_match[] = {
MODULE_DEVICE_TABLE(of, sii8620_dt_match);
static const struct i2c_device_id sii8620_id[] = {
- { "sii8620" },
+ { .name = "sii8620" },
{ }
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 3445c82e6f50..8b8529e1723c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1949,7 +1949,6 @@ static const struct regmap_config dw_dp_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .fast_io = true,
.max_register = DW_DP_MAX_REGISTER,
.rd_table = &dw_dp_readable_table,
};
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index a408dbd414bf..34c5f583e910 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3389,7 +3389,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
break;
default:
dev_err(dev, "reg-io-width must be 1 or 4\n");
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ goto err_res;
}
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
index 5f06cb03b4c0..7fd2f1e60654 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
@@ -907,7 +907,6 @@ static const struct regmap_config dw_mipi_dsi2_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .fast_io = true,
};
static struct dw_mipi_dsi2 *
diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c
index 6124d3123f57..76564d5e4f69 100644
--- a/drivers/gpu/drm/bridge/tc358762.c
+++ b/drivers/gpu/drm/bridge/tc358762.c
@@ -292,25 +292,17 @@ static int tc358762_probe(struct mipi_dsi_device *dsi)
ctx->bridge.of_node = dev->of_node;
ctx->bridge.pre_enable_prev_first = true;
- drm_bridge_add(&ctx->bridge);
+ ret = devm_drm_bridge_add(dev, &ctx->bridge);
+ if (ret < 0)
+ return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_bridge_remove(&ctx->bridge);
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
dev_err(dev, "failed to attach dsi\n");
- }
return ret;
}
-static void tc358762_remove(struct mipi_dsi_device *dsi)
-{
- struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(dsi);
- drm_bridge_remove(&ctx->bridge);
-}
-
static const struct of_device_id tc358762_of_match[] = {
{ .compatible = "toshiba,tc358762" },
{ }
@@ -319,7 +311,6 @@ MODULE_DEVICE_TABLE(of, tc358762_of_match);
static struct mipi_dsi_driver tc358762_driver = {
.probe = tc358762_probe,
- .remove = tc358762_remove,
.driver = {
.name = "tc358762",
.of_match_table = tc358762_of_match,
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c
index f55a56cbd88f..049215e65988 100644
--- a/drivers/gpu/drm/bridge/tc358764.c
+++ b/drivers/gpu/drm/bridge/tc358764.c
@@ -376,25 +376,17 @@ static int tc358764_probe(struct mipi_dsi_device *dsi)
ctx->bridge.of_node = dev->of_node;
ctx->bridge.pre_enable_prev_first = true;
- drm_bridge_add(&ctx->bridge);
+ ret = devm_drm_bridge_add(dev, &ctx->bridge);
+ if (ret < 0)
+ return ret;
- ret = mipi_dsi_attach(dsi);
- if (ret < 0) {
- drm_bridge_remove(&ctx->bridge);
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0)
dev_err(dev, "failed to attach dsi\n");
- }
return ret;
}
-static void tc358764_remove(struct mipi_dsi_device *dsi)
-{
- struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi);
-
- mipi_dsi_detach(dsi);
- drm_bridge_remove(&ctx->bridge);
-}
-
static const struct of_device_id tc358764_of_match[] = {
{ .compatible = "toshiba,tc358764" },
{ }
@@ -403,7 +395,6 @@ MODULE_DEVICE_TABLE(of, tc358764_of_match);
static struct mipi_dsi_driver tc358764_driver = {
.probe = tc358764_probe,
- .remove = tc358764_remove,
.driver = {
.name = "tc358764",
.of_match_table = tc358764_of_match,
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 8f1648f799a7..e3a57f8228da 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -2613,7 +2613,7 @@ static void tc_remove(struct i2c_client *client)
}
static const struct i2c_device_id tc358767_i2c_ids[] = {
- { "tc358767" },
+ { .name = "tc358767" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 112a421f99b5..d1fc6af37cc5 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -1362,8 +1362,8 @@ static const struct regmap_config tc358768_regmap_config = {
};
static const struct i2c_device_id tc358768_i2c_ids[] = {
- { "tc358768" },
- { "tc358778" },
+ { .name = "tc358768" },
+ { .name = "tc358778" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc358768_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c
index 728938f9dcb4..203dc738ecef 100644
--- a/drivers/gpu/drm/bridge/tc358775.c
+++ b/drivers/gpu/drm/bridge/tc358775.c
@@ -727,8 +727,8 @@ static void tc_remove(struct i2c_client *client)
}
static const struct i2c_device_id tc358775_i2c_ids[] = {
- { "tc358765", TC358765, },
- { "tc358775", TC358775, },
+ { .name = "tc358765", .driver_data = TC358765 },
+ { .name = "tc358775", .driver_data = TC358775 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids);
diff --git a/drivers/gpu/drm/bridge/tda998x_drv.c b/drivers/gpu/drm/bridge/tda998x_drv.c
index 16c745591175..0b966c9844e7 100644
--- a/drivers/gpu/drm/bridge/tda998x_drv.c
+++ b/drivers/gpu/drm/bridge/tda998x_drv.c
@@ -2064,7 +2064,7 @@ MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
#endif
static const struct i2c_device_id tda998x_ids[] = {
- { "tda998x" },
+ { .name = "tda998x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tda998x_ids);
diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c
index c16eb3a05cfa..c9f789e30687 100644
--- a/drivers/gpu/drm/bridge/ti-dlpc3433.c
+++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c
@@ -389,7 +389,7 @@ static void dlpc3433_remove(struct i2c_client *client)
}
static const struct i2c_device_id dlpc3433_id[] = {
- { "ti,dlpc3433" },
+ { .name = "ti,dlpc3433" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, dlpc3433_id);
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index e6dbe51d0dba..f9fdbf48c6b3 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -1072,9 +1072,9 @@ static void sn65dsi83_remove(struct i2c_client *client)
}
static const struct i2c_device_id sn65dsi83_id[] = {
- { "ti,sn65dsi83", MODEL_SN65DSI83 },
- { "ti,sn65dsi84", MODEL_SN65DSI84 },
- {},
+ { .name = "ti,sn65dsi83", .driver_data = MODEL_SN65DSI83 },
+ { .name = "ti,sn65dsi84", .driver_data = MODEL_SN65DSI84 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sn65dsi83_id);
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 1da242f17413..48b83df9aed6 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -2080,7 +2080,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
dev_name(pdata->dev), pdata);
if (ret)
- return dev_err_probe(dev, ret, "failed to request interrupt\n");
+ return ret;
}
/*
@@ -2116,8 +2116,8 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
}
static const struct i2c_device_id ti_sn65dsi86_id[] = {
- { "ti,sn65dsi86" },
- {}
+ { .name = "ti,sn65dsi86" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ti_sn65dsi86_id);
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index f0a14f52aaac..629ff7095e87 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -437,7 +437,7 @@ static void tfp410_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id tfp410_i2c_ids[] = {
- { "tfp410" },
+ { .name = "tfp410" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 1ff0bf7cba6a..3c570f9393b9 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -960,17 +960,19 @@ static void drmm_cg_unregister_region(struct drm_device *dev, void *arg)
* drmm_cgroup_register_region - Register a region of a DRM device to cgroups
* @dev: device for region
* @region_name: Region name for registering
- * @size: Size of region in bytes
+ * @init: Initialization parameters for the region.
*
* This decreases the ref-count of @dev by one. The device is destroyed if the
* ref-count drops to zero.
*/
-struct dmem_cgroup_region *drmm_cgroup_register_region(struct drm_device *dev, const char *region_name, u64 size)
+struct dmem_cgroup_region *
+drmm_cgroup_register_region(struct drm_device *dev, const char *region_name,
+ const struct dmem_cgroup_init *init)
{
struct dmem_cgroup_region *region;
int ret;
- region = dmem_cgroup_register_region(size, "drm/%s/%s", dev->unique, region_name);
+ region = dmem_cgroup_register_region(init, "drm/%s/%s", dev->unique, region_name);
if (IS_ERR_OR_NULL(region))
return region;
diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c
index 4accf18e2341..b702d122d791 100644
--- a/drivers/gpu/drm/imagination/pvr_ccb.c
+++ b/drivers/gpu/drm/imagination/pvr_ccb.c
@@ -255,7 +255,7 @@ pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev)
* pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref
* held and a slot pre-reserved
* @pvr_dev: Device pointer.
- * @cmd: Command to sent.
+ * @cmd: Command to send.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*/
void
@@ -361,7 +361,7 @@ static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev)
/**
* pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held
* @pvr_dev: Device pointer.
- * @cmd: Command to sent.
+ * @cmd: Command to send.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*
* Returns:
@@ -385,7 +385,7 @@ pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd
/**
* pvr_kccb_send_cmd() - Send command to the KCCB
* @pvr_dev: Device pointer.
- * @cmd: Command to sent.
+ * @cmd: Command to send.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*
* Returns:
diff --git a/drivers/gpu/drm/imagination/pvr_check.h b/drivers/gpu/drm/imagination/pvr_check.h
new file mode 100644
index 000000000000..94764ce9bbd1
--- /dev/null
+++ b/drivers/gpu/drm/imagination/pvr_check.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/* Copyright (c) 2026 Imagination Technologies Ltd. */
+
+#ifndef PVR_CHECK_H
+#define PVR_CHECK_H
+
+#include <linux/build_bug.h>
+#include <linux/overflow.h>
+#include <linux/stddef.h>
+
+#define OFFSET_CHECK(type, member, offset) \
+ static_assert(offsetof(type, member) == (offset), \
+ "offsetof(" #type ", " #member ") incorrect")
+
+#define SIZE_CHECK(type, size) \
+ static_assert(sizeof(type) == (size), #type " is incorrect size")
+
+#define ALIGN_CHECK(type, align) \
+ static_assert(__alignof__(type) <= (align), #type " has incorrect alignment")
+
+/*
+ * Where the last member of a struct is a flexible array member, using
+ * SIZE_CHECK() is pointless. If the structure is not already padded to
+ * alignment without the flexible array member, sizeof() will not match the
+ * offset of the flexible array member and the "correct" sizeof() value is
+ * completely meaningless.
+ *
+ * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final
+ * field is a flexible array member and that it behaves as expected.
+ */
+#define FLEX_ARRAY_CHECK(type, member) \
+ static_assert(flex_array_size((type *)NULL, member, 1) == \
+ sizeof_field(type, member[0]), \
+ #type "->" #member " is incorrect size")
+
+#endif /* PVR_CHECK_H */
diff --git a/drivers/gpu/drm/imagination/pvr_device_info.h b/drivers/gpu/drm/imagination/pvr_device_info.h
index f61fb988b553..f6c2845530da 100644
--- a/drivers/gpu/drm/imagination/pvr_device_info.h
+++ b/drivers/gpu/drm/imagination/pvr_device_info.h
@@ -22,12 +22,12 @@ struct pvr_device_features {
bool has_coreid_per_os;
bool has_dynamic_dust_power;
bool has_ecc_rams;
- bool has_fb_cdc_v4;
- bool has_fbc_max_default_descriptors;
- bool has_fbc_max_large_descriptors;
bool has_fbcdc;
bool has_fbcdc_algorithm;
bool has_fbcdc_architecture;
+ bool has_fbc_max_default_descriptors;
+ bool has_fbc_max_large_descriptors;
+ bool has_fb_cdc_v4;
bool has_gpu_multicore_support;
bool has_gpu_virtualisation;
bool has_gs_rta_support;
diff --git a/drivers/gpu/drm/imagination/pvr_gem.h b/drivers/gpu/drm/imagination/pvr_gem.h
index c99f30cc6208..4286e047ae24 100644
--- a/drivers/gpu/drm/imagination/pvr_gem.h
+++ b/drivers/gpu/drm/imagination/pvr_gem.h
@@ -84,8 +84,10 @@ struct pvr_gem_object {
/**
* @base: The underlying &struct drm_gem_shmem_object.
*
- * Do not access this member directly, instead call
- * shem_gem_from_pvr_gem().
+ * .. note::
+ *
+ * This member should not be accessed directly, but instead by
+ * calling shmem_gem_from_pvr_gem().
*/
struct drm_gem_shmem_object base;
@@ -97,12 +99,6 @@ struct pvr_gem_object {
* changed after creation.
*
* Must be a combination of DRM_PVR_BO_* and/or PVR_BO_* flags.
- *
- * .. note::
- *
- * This member is declared const to indicate that none of these
- * options may change or be changed throughout the object's
- * lifetime.
*/
u64 flags;
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index 54e88b4208d7..09993e858df8 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -17,6 +17,7 @@
#include "pvr_rogue_fwif_client.h"
#define MAX_DEADLINE_MS 30000
+#define SCHED_TIMEOUT_PERIOD (2 * MAX_DEADLINE_MS)
#define CTX_COMPUTE_CCCB_SIZE_LOG2 15
#define CTX_FRAG_CCCB_SIZE_LOG2 15
@@ -1283,7 +1284,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
.submit_wq = pvr_dev->sched_wq,
.credit_limit = 64 * 1024,
.hang_limit = 1,
- .timeout = msecs_to_jiffies(500),
+ .timeout = msecs_to_jiffies(SCHED_TIMEOUT_PERIOD),
.timeout_wq = pvr_dev->sched_wq,
.name = "pvr-queue",
.dev = pvr_dev->base.dev,
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
index e72f4064af18..5048866359b9 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
@@ -4,31 +4,7 @@
#ifndef PVR_ROGUE_FWIF_CHECK_H
#define PVR_ROGUE_FWIF_CHECK_H
-#include <linux/build_bug.h>
-#include <linux/overflow.h>
-#include <linux/stddef.h>
-
-#define OFFSET_CHECK(type, member, offset) \
- static_assert(offsetof(type, member) == (offset), \
- "offsetof(" #type ", " #member ") incorrect")
-
-#define SIZE_CHECK(type, size) \
- static_assert(sizeof(type) == (size), #type " is incorrect size")
-
-/*
- * Where the last member of a struct is a flexible array member, using
- * SIZE_CHECK() is pointless. If the structure is not already padded to
- * alignment without the flexible array member, sizeof() will not match the
- * offset of the flexible array member and the "correct" sizeof() value is
- * completely meaningless.
- *
- * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final
- * field is a flexible array member and that it behaves as expected.
- */
-#define FLEX_ARRAY_CHECK(type, member) \
- static_assert(flex_array_size((type *)NULL, member, 1) == \
- sizeof_field(type, member[0]), \
- #type "->" #member " is incorrect size")
+#include "pvr_check.h"
OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0);
OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200);
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h
index 54aa4474163e..467d1d2073a1 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h
@@ -4,14 +4,7 @@
#ifndef PVR_ROGUE_FWIF_CLIENT_CHECK_H
#define PVR_ROGUE_FWIF_CLIENT_CHECK_H
-#include <linux/build_bug.h>
-
-#define OFFSET_CHECK(type, member, offset) \
- static_assert(offsetof(type, member) == (offset), \
- "offsetof(" #type ", " #member ") incorrect")
-
-#define SIZE_CHECK(type, size) \
- static_assert(sizeof(type) == (size), #type " is incorrect size")
+#include "pvr_check.h"
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_ctrl_stream_base, 0);
OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu_border_colour_table, 8);
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h
index 597ed54bbd3a..59ef0a9aa395 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h
@@ -4,14 +4,7 @@
#ifndef PVR_ROGUE_FWIF_SHARED_CHECK_H
#define PVR_ROGUE_FWIF_SHARED_CHECK_H
-#include <linux/build_bug.h>
-
-#define OFFSET_CHECK(type, member, offset) \
- static_assert(offsetof(type, member) == (offset), \
- "offsetof(" #type ", " #member ") incorrect")
-
-#define SIZE_CHECK(type, size) \
- static_assert(sizeof(type) == (size), #type " is incorrect size")
+#include "pvr_check.h"
OFFSET_CHECK(struct rogue_fwif_dma_addr, dev_addr, 0);
OFFSET_CHECK(struct rogue_fwif_dma_addr, fw_addr, 8);
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h
index 684766006703..5c33b342dda5 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h
@@ -33,12 +33,12 @@
/* 0 MiB to 4 MiB, size of 4 MiB : RESERVED */
/* 0x00_0040_0000 - 0x7F_FFC0_0000 **/
-/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED **/
+/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED */
/* 0x80_0000_0000 ************************************************************/
/* 0x80_0000_0000 - 0x9F_FFFF_FFFF **/
-/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP **/
+/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP */
#define ROGUE_GENERAL_HEAP_BASE 0x8000000000ull
#define ROGUE_GENERAL_HEAP_SIZE SZ_128G
@@ -62,7 +62,7 @@
#define ROGUE_PDSCODEDATA_HEAP_SIZE SZ_4G
/* 0xDB_0000_0000 - 0xDB_FFFF_FFFF */
-/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN **/
+/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN */
/*
* The BRN63142 quirk workaround requires Region Header memory to be at the top
* of a 16GiB aligned range. This is so when masked with 0x03FFFFFFFF the
@@ -82,7 +82,7 @@
/* 0xE1_0000_0000 - 0xE1_BFFF_FFFF */
/* 900 GiB to 903 GiB, size of 3 GiB : RESERVED */
-/* 0xE1_C000_000 - 0xE1_FFFF_FFFF */
+/* 0xE1_C000_0000 - 0xE1_FFFF_FFFF */
/* 903 GiB to 904 GiB, reserved 1 GiB, : FIRMWARE_HEAP */
#define ROGUE_FW_HEAP_BASE 0xE1C0000000ull
@@ -90,7 +90,7 @@
/* 904 GiB to 912 GiB, size of 8 GiB : FREE */
/* 0xE4_0000_0000 - 0xE7_FFFF_FFFF */
-/* 912 GiB to 968 GiB, size of 16 GiB : TRANSFER_FRAG */
+/* 912 GiB to 928 GiB, size of 16 GiB : TRANSFER_FRAG */
#define ROGUE_TRANSFER_FRAG_HEAP_BASE 0xE400000000ull
#define ROGUE_TRANSFER_FRAG_HEAP_SIZE SZ_16G
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h
index 824b4bf33ac1..aebf231ce90f 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h
@@ -4,55 +4,32 @@
#ifndef PVR_ROGUE_MIPS_CHECK_H
#define PVR_ROGUE_MIPS_CHECK_H
-#include <linux/build_bug.h>
+#include "pvr_check.h"
-static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) == 0,
- "offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) incorrect");
-static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_hi) == 4,
- "offsetof(struct rogue_mips_tlb_entry, tlb_hi) incorrect");
-static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo0) == 8,
- "offsetof(struct rogue_mips_tlb_entry, tlb_lo0) incorrect");
-static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo1) == 12,
- "offsetof(struct rogue_mips_tlb_entry, tlb_lo1) incorrect");
-static_assert(sizeof(struct rogue_mips_tlb_entry) == 16,
- "struct rogue_mips_tlb_entry is incorrect size");
+OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_page_mask, 0);
+OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_hi, 4);
+OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo0, 8);
+OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo1, 12);
+SIZE_CHECK(struct rogue_mips_tlb_entry, 16);
-static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_in) == 0,
- "offsetof(struct rogue_mips_remap_entry, remap_addr_in) incorrect");
-static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_out) == 4,
- "offsetof(struct rogue_mips_remap_entry, remap_addr_out) incorrect");
-static_assert(offsetof(struct rogue_mips_remap_entry, remap_region_size) == 8,
- "offsetof(struct rogue_mips_remap_entry, remap_region_size) incorrect");
-static_assert(sizeof(struct rogue_mips_remap_entry) == 12,
- "struct rogue_mips_remap_entry is incorrect size");
+OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_in, 0);
+OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_out, 4);
+OFFSET_CHECK(struct rogue_mips_remap_entry, remap_region_size, 8);
+SIZE_CHECK(struct rogue_mips_remap_entry, 12);
-static_assert(offsetof(struct rogue_mips_state, error_state) == 0,
- "offsetof(struct rogue_mips_state, error_state) incorrect");
-static_assert(offsetof(struct rogue_mips_state, error_epc) == 4,
- "offsetof(struct rogue_mips_state, error_epc) incorrect");
-static_assert(offsetof(struct rogue_mips_state, status_register) == 8,
- "offsetof(struct rogue_mips_state, status_register) incorrect");
-static_assert(offsetof(struct rogue_mips_state, cause_register) == 12,
- "offsetof(struct rogue_mips_state, cause_register) incorrect");
-static_assert(offsetof(struct rogue_mips_state, bad_register) == 16,
- "offsetof(struct rogue_mips_state, bad_register) incorrect");
-static_assert(offsetof(struct rogue_mips_state, epc) == 20,
- "offsetof(struct rogue_mips_state, epc) incorrect");
-static_assert(offsetof(struct rogue_mips_state, sp) == 24,
- "offsetof(struct rogue_mips_state, sp) incorrect");
-static_assert(offsetof(struct rogue_mips_state, debug) == 28,
- "offsetof(struct rogue_mips_state, debug) incorrect");
-static_assert(offsetof(struct rogue_mips_state, depc) == 32,
- "offsetof(struct rogue_mips_state, depc) incorrect");
-static_assert(offsetof(struct rogue_mips_state, bad_instr) == 36,
- "offsetof(struct rogue_mips_state, bad_instr) incorrect");
-static_assert(offsetof(struct rogue_mips_state, unmapped_address) == 40,
- "offsetof(struct rogue_mips_state, unmapped_address) incorrect");
-static_assert(offsetof(struct rogue_mips_state, tlb) == 44,
- "offsetof(struct rogue_mips_state, tlb) incorrect");
-static_assert(offsetof(struct rogue_mips_state, remap) == 300,
- "offsetof(struct rogue_mips_state, remap) incorrect");
-static_assert(sizeof(struct rogue_mips_state) == 684,
- "struct rogue_mips_state is incorrect size");
+OFFSET_CHECK(struct rogue_mips_state, error_state, 0);
+OFFSET_CHECK(struct rogue_mips_state, error_epc, 4);
+OFFSET_CHECK(struct rogue_mips_state, status_register, 8);
+OFFSET_CHECK(struct rogue_mips_state, cause_register, 12);
+OFFSET_CHECK(struct rogue_mips_state, bad_register, 16);
+OFFSET_CHECK(struct rogue_mips_state, epc, 20);
+OFFSET_CHECK(struct rogue_mips_state, sp, 24);
+OFFSET_CHECK(struct rogue_mips_state, debug, 28);
+OFFSET_CHECK(struct rogue_mips_state, depc, 32);
+OFFSET_CHECK(struct rogue_mips_state, bad_instr, 36);
+OFFSET_CHECK(struct rogue_mips_state, unmapped_address, 40);
+OFFSET_CHECK(struct rogue_mips_state, tlb, 44);
+OFFSET_CHECK(struct rogue_mips_state, remap, 300);
+SIZE_CHECK(struct rogue_mips_state, 684);
#endif /* PVR_ROGUE_MIPS_CHECK_H */
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base.h b/drivers/gpu/drm/nouveau/dispnv50/base.h
index 085bd3aeb40b..fbfd62d8fc79 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/base.h
@@ -1,5 +1,7 @@
#ifndef __NV50_KMS_BASE_H__
#define __NV50_KMS_BASE_H__
+#include <drm/drm_blend.h>
+
#include "wndw.h"
int base507c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
@@ -24,4 +26,8 @@ extern const struct nv50_wndw_func base907c;
int base917c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
int nv50_base_new(struct nouveau_drm *, int head, struct nv50_wndw **);
+
+/* The only blend mode supported by base channels */
+#define BASE_BLEND_MODES BIT(DRM_MODE_BLEND_PIXEL_NONE)
+
#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base507c.c b/drivers/gpu/drm/nouveau/dispnv50/base507c.c
index a431f6c5f6fa..41b648ab3c5e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base507c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base507c.c
@@ -297,6 +297,7 @@ base507c = {
.image_set = base507c_image_set,
.image_clr = base507c_image_clr,
.update = base507c_update,
+ .blend_modes = BASE_BLEND_MODES,
};
int
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base827c.c b/drivers/gpu/drm/nouveau/dispnv50/base827c.c
index 4545cc5f3a14..6c290324b4f3 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base827c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base827c.c
@@ -93,6 +93,7 @@ base827c = {
.image_set = base827c_image_set,
.image_clr = base507c_image_clr,
.update = base507c_update,
+ .blend_modes = BASE_BLEND_MODES,
};
int
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base907c.c b/drivers/gpu/drm/nouveau/dispnv50/base907c.c
index 4a2d5a259e15..a94d8350224b 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base907c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base907c.c
@@ -205,6 +205,7 @@ base907c = {
.image_set = base907c_image_set,
.image_clr = base507c_image_clr,
.update = base507c_update,
+ .blend_modes = BASE_BLEND_MODES,
};
int
diff --git a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
index 1a889139cb05..5f2ff40388e7 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c
@@ -29,6 +29,7 @@
#include <nvhw/class/cl507a.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
bool
@@ -164,6 +165,8 @@ curs507a_wndw = {
.acquire = curs507a_acquire,
.release = curs507a_release,
.prepare = curs507a_prepare,
+ // TODO: Cursors also support premulti, but we haven't hooked it up anywhere yet.
+ .blend_modes = BIT(DRM_MODE_BLEND_COVERAGE),
};
int
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 364227f5456f..2c66e480b511 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -2768,6 +2768,7 @@ nv50_display_read_hw_state(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
struct drm_encoder *encoder;
+ struct drm_plane *plane;
struct drm_modeset_acquire_ctx ctx;
struct nv50_disp *disp = nv50_disp(dev);
int ret;
@@ -2781,6 +2782,9 @@ nv50_display_read_hw_state(struct nouveau_drm *drm)
nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder));
}
+ drm_for_each_plane(plane, dev)
+ nv50_wndw_default_state(nv50_wndw(plane));
+
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index 2635458d52ac..15a322422f4e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -848,6 +848,28 @@ static const u64 nv50_cursor_format_modifiers[] = {
DRM_FORMAT_MOD_INVALID,
};
+/*
+ * Setup defaults for the atomic wndw state
+ */
+void
+nv50_wndw_default_state(struct nv50_wndw *wndw)
+{
+ struct nv50_wndw_atom *armw = nv50_wndw_atom(wndw->plane.state);
+ const unsigned int blend_modes = wndw->func->blend_modes;
+
+ drm_modeset_lock_assert_held(&wndw->plane.mutex);
+
+ /* Ensure the plane's atomic state didn't default to a pixel_blend_mode we don't support */
+ if (blend_modes && (!(BIT(armw->state.pixel_blend_mode) & blend_modes))) {
+ if (blend_modes & BIT(DRM_MODE_BLEND_COVERAGE))
+ armw->state.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE;
+ else if (blend_modes & BIT(DRM_MODE_BLEND_PREMULTI))
+ armw->state.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
+ else if (blend_modes & BIT(DRM_MODE_BLEND_PIXEL_NONE))
+ armw->state.pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
+ }
+}
+
int
nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
enum drm_plane_type type, const char *name, int index,
@@ -908,13 +930,6 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
ret = drm_plane_create_alpha_property(&wndw->plane);
if (ret)
return ret;
-
- ret = drm_plane_create_blend_mode_property(&wndw->plane,
- BIT(DRM_MODE_BLEND_PIXEL_NONE) |
- BIT(DRM_MODE_BLEND_PREMULTI) |
- BIT(DRM_MODE_BLEND_COVERAGE));
- if (ret)
- return ret;
} else {
ret = drm_plane_create_zpos_immutable_property(&wndw->plane,
nv50_wndw_zpos_default(&wndw->plane));
@@ -922,6 +937,17 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
return ret;
}
+ /*
+ * DRM requires that we have a blend mode property for any type of plane that exposes color
+ * formats with an alpha channel. So do this, even if we don't actually have control for the
+ * blend property hooked up with blend_set.
+ */
+ if (func->blend_modes) {
+ ret = drm_plane_create_blend_mode_property(&wndw->plane, func->blend_modes);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.h b/drivers/gpu/drm/nouveau/dispnv50/wndw.h
index 90d100514bef..7bd8bcc199db 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.h
@@ -1,6 +1,8 @@
#ifndef __NV50_KMS_WNDW_H__
#define __NV50_KMS_WNDW_H__
#define nv50_wndw(p) container_of((p), struct nv50_wndw, plane)
+#include <drm/drm_blend.h>
+
#include "disp.h"
#include "atom.h"
#include "lut.h"
@@ -44,6 +46,7 @@ void nv50_wndw_flush_clr(struct nv50_wndw *, u32 *interlock, bool flush,
struct nv50_wndw_atom *);
void nv50_wndw_ntfy_enable(struct nv50_wndw *, struct nv50_wndw_atom *);
int nv50_wndw_wait_armed(struct nv50_wndw *, struct nv50_wndw_atom *);
+void nv50_wndw_default_state(struct nv50_wndw *wndw);
struct nv50_wndw_func {
int (*acquire)(struct nv50_wndw *, struct nv50_wndw_atom *asyw,
@@ -74,6 +77,7 @@ struct nv50_wndw_func {
int (*image_clr)(struct nv50_wndw *);
int (*scale_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
int (*blend_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
+ unsigned int blend_modes;
int (*update)(struct nv50_wndw *, u32 *interlock);
};
@@ -139,4 +143,11 @@ int wndwca7e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
int nv50_wndw_new(struct nouveau_drm *, enum drm_plane_type, int index,
struct nv50_wndw **);
+
+/* A set of blend modes supported by all wndws */
+#define WNDW_BLEND_MODES \
+ ( BIT(DRM_MODE_BLEND_COVERAGE) \
+ | BIT(DRM_MODE_BLEND_PREMULTI) \
+ | BIT(DRM_MODE_BLEND_PIXEL_NONE))
+
#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
index 554c4f91f8be..7ab4d532118e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
@@ -343,6 +343,7 @@ wndwc37e = {
.image_set = wndwc37e_image_set,
.image_clr = wndwc37e_image_clr,
.blend_set = wndwc37e_blend_set,
+ .blend_modes = WNDW_BLEND_MODES,
.update = wndwc37e_update,
};
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
index d1ca51aae58c..e220f43b5bca 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
@@ -231,6 +231,7 @@ wndwc57e = {
.image_set = wndwc57e_image_set,
.image_clr = wndwc37e_image_clr,
.blend_set = wndwc37e_blend_set,
+ .blend_modes = WNDW_BLEND_MODES,
.update = wndwc37e_update,
};
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c
index 52af293c98f4..580cf7abd819 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c
@@ -95,6 +95,7 @@ wndwc67e = {
.image_set = wndwc67e_image_set,
.image_clr = wndwc37e_image_clr,
.blend_set = wndwc37e_blend_set,
+ .blend_modes = WNDW_BLEND_MODES,
.update = wndwc37e_update,
};
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
index 2cec8cfbd546..d65ec438b434 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
@@ -231,6 +231,7 @@ wndwca7e = {
.image_set = wndwca7e_image_set,
.image_clr = wndwca7e_image_clr,
.blend_set = wndwc37e_blend_set,
+ .blend_modes = WNDW_BLEND_MODES,
.update = wndwc37e_update,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index 291203121f0c..4542d5f4ded8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -408,6 +408,9 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
case NOUVEAU_FIFO_ENGINE_CE:
engine = NV_DEVICE_HOST_RUNLIST_ENGINES_CE;
break;
+ case NOUVEAU_FIFO_ENGINE_NVDEC:
+ engine = NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC;
+ break;
default:
return nouveau_abi16_put(abi16, -ENOSYS);
}
@@ -485,10 +488,13 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
goto done;
break;
case NV_DEVICE_INFO_V0_TURING:
- ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar", 0, TURING_DMA_COPY_A,
- NULL, 0, &chan->ce);
- if (ret)
- goto done;
+ if (engine != NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC) {
+ ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar",
+ 0, TURING_DMA_COPY_A, NULL, 0,
+ &chan->ce);
+ if (ret)
+ goto done;
+ }
break;
default:
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 1c2523e2f92e..5fc75dc750ed 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -10,7 +10,7 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 4
-#define DRIVER_PATCHLEVEL 2
+#define DRIVER_PATCHLEVEL 3
/*
* 1.1.1:
@@ -39,6 +39,8 @@
* - add variable page sizes and compression for Turing+
* 1.4.2:
* - tell userspace LPTE/SPTE races are fixed.
+ * 1.4.3:
+ * - VDEC contexts can be created.
*/
#include <linux/notifier.h>
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index ad01f922aa86..cfc0868e2dd3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -23,6 +23,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+#include <linux/cgroup_dmem.h>
#include <linux/limits.h>
#include <drm/ttm/ttm_range_manager.h>
@@ -188,6 +189,13 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm)
man->func = &nouveau_vram_manager;
+ man->cg = drmm_cgroup_register_region(drm->dev, "vram",
+ &(struct dmem_cgroup_init){
+ .size = drm->gem.vram_available
+ });
+ if (IS_ERR(man->cg))
+ return PTR_ERR(man->cg);
+
ttm_resource_manager_init(man, &drm->ttm.bdev,
drm->gem.vram_available >> PAGE_SHIFT);
ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_VRAM, man);
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c
index 7aaee94f86e4..6da720e8732d 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.c
@@ -456,15 +456,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
timer_delete(&dsi->te_timer);
#endif
- /* make a copy and unlock, so that isrs can unregister
- * themselves */
- memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
- sizeof(dsi->isr_tables));
+ dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus);
spin_unlock(&dsi->irq_lock);
- dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
-
dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.h b/drivers/gpu/drm/omapdrm/dss/dsi.h
index 601707c0ecc4..2b25247ea893 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.h
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.h
@@ -379,8 +379,6 @@ struct dsi_data {
spinlock_t irq_lock;
struct dsi_isr_tables isr_tables;
- /* space for a copy used by the interrupt handler */
- struct dsi_isr_tables isr_tables_copy;
int update_vc;
#ifdef DSI_PERF_MEASURE
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index df7286679446..2b455369ed70 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -145,6 +145,16 @@ struct panel_delay {
unsigned int disable;
/**
+ * @pre_unprepare: Time for the end of video data to power off.
+ *
+ * The time (in milliseconds) that it needs to have passed between
+ * the end of valid video data from source and start powering off.
+ *
+ * This is T10-min on eDP timing diagrams. It is not common to set.
+ */
+ unsigned int pre_unprepare;
+
+ /**
* @unprepare: Time to power down completely.
*
* The time (in milliseconds) that it takes for the panel
@@ -413,8 +423,12 @@ static int panel_edp_suspend(struct device *dev)
static int panel_edp_unprepare(struct drm_panel *panel)
{
+ struct panel_edp *p = to_panel_edp(panel);
int ret;
+ if (p->desc->delay.pre_unprepare)
+ msleep(p->desc->delay.pre_unprepare);
+
ret = pm_runtime_put_sync_suspend(panel->dev);
if (ret < 0)
return ret;
@@ -832,6 +846,13 @@ exit:
return 0;
}
+static void panel_edp_put_adapter(void *_adap)
+{
+ struct i2c_adapter *adap = _adap;
+
+ i2c_put_adapter(adap);
+}
+
static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
struct drm_dp_aux *aux)
{
@@ -874,11 +895,16 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (ddc) {
- panel->ddc = of_find_i2c_adapter_by_node(ddc);
+ panel->ddc = of_get_i2c_adapter_by_node(ddc);
of_node_put(ddc);
if (!panel->ddc)
return -EPROBE_DEFER;
+
+ err = devm_add_action_or_reset(dev, panel_edp_put_adapter,
+ panel->ddc);
+ if (err)
+ return err;
} else if (aux) {
panel->ddc = &aux->ddc;
}
@@ -890,7 +916,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
err = drm_panel_of_backlight(&panel->base);
if (err)
- goto err_finished_ddc_init;
+ return err;
/*
* We use runtime PM for prepare / unprepare since those power the panel
@@ -937,9 +963,6 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
err_finished_pm_runtime:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
-err_finished_ddc_init:
- if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
- put_device(&panel->ddc->dev);
return err;
}
@@ -983,8 +1006,6 @@ static void panel_edp_remove(struct device *dev)
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
- if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
- put_device(&panel->ddc->dev);
drm_edid_free(panel->drm_edid);
panel->drm_edid = NULL;
@@ -1803,6 +1824,13 @@ static const struct panel_delay delay_80_500_e80_p2e200 = {
.prepare_to_enable = 200,
};
+static const struct panel_delay delay_200_500_e80_pu100 = {
+ .hpd_absent = 200,
+ .unprepare = 500,
+ .enable = 80,
+ .pre_unprepare = 100,
+};
+
static const struct panel_delay delay_100_500_e200 = {
.hpd_absent = 100,
.unprepare = 500,
@@ -2029,6 +2057,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a84, &delay_200_500_e50, "NV133WUM-T01"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ae8, &delay_200_500_e50_p2e80, "NV140WUM-N41"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0af2, &delay_200_500_e50_p2e80, "NE140QDM-NX2"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80_d50, "NV122WUM-N41"),
@@ -2076,6 +2105,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', 0x116c, &delay_200_500_e80_d50, "N116BCP-EA2"),
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"),
@@ -2165,6 +2195,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x158f, &delay_200_500_p2e100, "LQ134Z1"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1593, &delay_200_500_p2e100, "LQ134N1"),
+ EDP_PANEL_ENTRY('S', 'H', 'P', 0x15a7, &delay_200_500_e200, "LQ120P1JX51"),
EDP_PANEL_ENTRY('S', 'T', 'A', 0x0004, &delay_200_500_e200, "116KHD024006"),
EDP_PANEL_ENTRY('S', 'T', 'A', 0x0009, &delay_200_500_e250, "116QHD024002"),
@@ -2172,7 +2203,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"),
+ EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e80_pu100, "TM156VDXP25"),
{ /* sentinal */ }
};
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
index e6c483851f1f..5610a225f77b 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c
@@ -25,22 +25,8 @@
#define ILI9805_SETEXTC_PARAMETER2 (0x98)
#define ILI9805_SETEXTC_PARAMETER3 (0x05)
-#define ILI9805_INSTR(_delay, ...) { \
- .delay = (_delay), \
- .len = sizeof((u8[]) {__VA_ARGS__}), \
- .data = (u8[]){__VA_ARGS__} \
- }
-
-struct ili9805_instr {
- size_t len;
- const u8 *data;
- u32 delay;
-};
-
struct ili9805_desc {
- const char *name;
- const struct ili9805_instr *init;
- const size_t init_length;
+ void (*init)(struct mipi_dsi_multi_context *ctx);
const struct drm_display_mode *mode;
u32 width_mm;
u32 height_mm;
@@ -56,65 +42,80 @@ struct ili9805 {
struct gpio_desc *reset_gpio;
};
-static const struct ili9805_instr gpm1780a0_init[] = {
- ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
- ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
- ILI9805_INSTR(100, 0xFD, 0x0F, 0x10, 0x44, 0x00),
- ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,
- 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00),
- ILI9805_INSTR(0, 0xB8, 0x62),
- ILI9805_INSTR(0, 0xF1, 0x00),
- ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
- ILI9805_INSTR(0, 0xF3, 0x60, 0x83, 0x04),
- ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
- ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
- ILI9805_INSTR(0, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
- 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
- ILI9805_INSTR(0, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
- 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
- ILI9805_INSTR(10, 0xc1, 0x13, 0x39, 0x19, 0x06),
- ILI9805_INSTR(10, 0xc7, 0xe5),
- ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
- ILI9805_INSTR(10, 0xB4, 0x02),
- ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
- ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x08),
- ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
- ILI9805_INSTR(0, 0x20),
- ILI9805_INSTR(0, 0xB0, 0x01),
- ILI9805_INSTR(0, 0xB6, 0x31, 0x00, 0xef),
- ILI9805_INSTR(0, 0xDF, 0x23),
- ILI9805_INSTR(0, 0xB9, 0x02, 0x00),
-};
+static void gpm1780a0_init(struct mipi_dsi_multi_context *ctx)
+{
+ mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG,
+ ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2,
+ ILI9805_SETEXTC_PARAMETER3);
+ mipi_dsi_msleep(ctx, 100);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x10, 0x44, 0x00);
+ mipi_dsi_msleep(ctx, 100);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x62);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf3, 0x60, 0x83, 0x04);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08,
+ 0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08,
+ 0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x13, 0x39, 0x19, 0x06);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0xe5);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55);
+ mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x08);
+ mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x20);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x01);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x31, 0x00, 0xef);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xdf, 0x23);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb9, 0x02, 0x00);
+}
-static const struct ili9805_instr tm041xdhg01_init[] = {
- ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
- ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
- ILI9805_INSTR(100, 0xFD, 0x0F, 0x13, 0x44, 0x00),
- ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,
- 0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01),
- ILI9805_INSTR(0, 0xB8, 0x74),
- ILI9805_INSTR(0, 0xF1, 0x00),
- ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
- ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
- ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
- ILI9805_INSTR(0, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
- 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
- ILI9805_INSTR(0, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
- 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
- ILI9805_INSTR(10, 0xc1, 0x15, 0x03, 0x03, 0x31),
- ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
- ILI9805_INSTR(10, 0xB4, 0x02),
- ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
- ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x0a),
- ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
- ILI9805_INSTR(0, 0x20),
- ILI9805_INSTR(0, 0xB0, 0x00),
- ILI9805_INSTR(0, 0xB6, 0x01),
- ILI9805_INSTR(0, 0xc2, 0x11),
- ILI9805_INSTR(0, 0x51, 0xFF),
- ILI9805_INSTR(0, 0x53, 0x24),
- ILI9805_INSTR(0, 0x55, 0x00),
-};
+static void tm041xdhg01_init(struct mipi_dsi_multi_context *ctx)
+{
+ mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG,
+ ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2,
+ ILI9805_SETEXTC_PARAMETER3);
+ mipi_dsi_msleep(ctx, 100);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x13, 0x44, 0x00);
+ mipi_dsi_msleep(ctx, 100);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,
+ 0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x74);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08,
+ 0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08,
+ 0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x15, 0x03, 0x03, 0x31);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02);
+ mipi_dsi_msleep(ctx, 10);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55);
+ mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+ mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x20);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x00);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x01);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x11);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0xff);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x24);
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x55, 0x00);
+}
static inline struct ili9805 *panel_to_ili9805(struct drm_panel *panel)
{
@@ -148,47 +149,24 @@ static int ili9805_power_on(struct ili9805 *ctx)
return 0;
}
-static int ili9805_power_off(struct ili9805 *ctx)
+static void ili9805_power_off(struct ili9805 *ctx)
{
gpiod_set_value(ctx->reset_gpio, 0);
regulator_disable(ctx->dvdd);
regulator_disable(ctx->avdd);
-
- return 0;
}
static int ili9805_activate(struct ili9805 *ctx)
{
- struct mipi_dsi_device *dsi = ctx->dsi;
- struct device *dev = &dsi->dev;
- int i, ret;
-
- for (i = 0; i < ctx->desc->init_length; i++) {
- const struct ili9805_instr *instr = &ctx->desc->init[i];
-
- ret = mipi_dsi_dcs_write_buffer(ctx->dsi, instr->data, instr->len);
- if (ret < 0)
- return ret;
-
- if (instr->delay > 0)
- msleep(instr->delay);
- }
-
- ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
- if (ret) {
- dev_err(dev, "Failed to exit sleep mode (%d)\n", ret);
- return ret;
- }
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
- usleep_range(5000, 6000);
+ ctx->desc->init(&dsi_ctx);
- ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
- if (ret) {
- dev_err(dev, "Failed to set display ON (%d)\n", ret);
- return ret;
- }
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
- return 0;
+ return dsi_ctx.accum_err;
}
static int ili9805_prepare(struct drm_panel *panel)
@@ -209,27 +187,13 @@ static int ili9805_prepare(struct drm_panel *panel)
return 0;
}
-static int ili9805_deactivate(struct ili9805 *ctx)
+static void ili9805_deactivate(struct ili9805 *ctx)
{
- struct mipi_dsi_device *dsi = ctx->dsi;
- struct device *dev = &dsi->dev;
- int ret;
-
- ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to set display OFF (%d)\n", ret);
- return ret;
- }
-
- usleep_range(5000, 10000);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
- ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
- if (ret < 0) {
- dev_err(dev, "Failed to enter sleep mode (%d)\n", ret);
- return ret;
- }
-
- return 0;
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 5000, 10000);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
}
static int ili9805_unprepare(struct drm_panel *panel)
@@ -271,7 +235,7 @@ static const struct drm_display_mode tm041xdhg01_timing = {
};
static int ili9805_get_modes(struct drm_panel *panel,
- struct drm_connector *connector)
+ struct drm_connector *connector)
{
struct ili9805 *ctx = panel_to_ili9805(panel);
struct drm_display_mode *mode;
@@ -368,7 +332,6 @@ static void ili9805_dsi_remove(struct mipi_dsi_device *dsi)
static const struct ili9805_desc gpm1780a0_desc = {
.init = gpm1780a0_init,
- .init_length = ARRAY_SIZE(gpm1780a0_init),
.mode = &gpm1780a0_timing,
.width_mm = 65,
.height_mm = 65,
@@ -376,7 +339,6 @@ static const struct ili9805_desc gpm1780a0_desc = {
static const struct ili9805_desc tm041xdhg01_desc = {
.init = tm041xdhg01_init,
- .init_length = ARRAY_SIZE(tm041xdhg01_init),
.mode = &tm041xdhg01_timing,
.width_mm = 42,
.height_mm = 96,
diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
index 67ef9e0fea03..5202c1ffc6e1 100644
--- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
+++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
@@ -2931,6 +2931,248 @@ static const struct jadard_panel_desc waveshare_10_1_inch_b_desc = {
MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
};
+static int dcltek_dt300250_init_cmds(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x2B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x2B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xCF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xCF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xFE);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x5C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x7F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xC8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x66);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x8D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x29);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x1A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x7F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x65);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x3B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x3C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x3F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x5D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x3D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x65);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x3C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x3F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x5D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x4D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x3D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x4B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x4F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x51);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x4A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x5B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x59);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x4E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x5F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2C, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2D, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2E, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2F, 0x0C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x1B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0x0A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0x1E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x0D);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4A, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4C, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4D, 0x1A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4E, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4F, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x1C);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x0B);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x4A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x4F);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xF7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88);
+
+ jd9365da_switch_page(&dsi_ctx, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x4A);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0F);
+
+ jd9365da_switch_page(&dsi_ctx, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x76);
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ 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);
+
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct jadard_panel_desc dcltek_dt300250_desc = {
+ .mode_4ln = &(const struct drm_display_mode) {
+ .clock = (600 + 48 + 32 + 80) * (1600 + 18 + 6 + 23) * 60 / 1000,
+
+ .hdisplay = 600,
+ .hsync_start = 600 + 48,
+ .hsync_end = 600 + 48 + 32,
+ .htotal = 600 + 48 + 32 + 80,
+
+ .vdisplay = 1600,
+ .vsync_start = 1600 + 18,
+ .vsync_end = 1600 + 18 + 6,
+ .vtotal = 1600 + 18 + 6 + 23,
+
+ .width_mm = 83,
+ .height_mm = 221,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = dcltek_dt300250_init_cmds,
+};
+
static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
@@ -2950,7 +3192,8 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
else
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_NO_EOT_PACKET;
+ MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_MODE_LPM;
dsi->format = desc->format;
dsi->lanes = desc->lanes;
@@ -3037,6 +3280,10 @@ static const struct of_device_id jadard_of_match[] = {
.data = &cz101b4001_desc
},
{
+ .compatible = "dcltek,dt300250",
+ .data = &dcltek_dt300250_desc
+ },
+ {
.compatible = "kingdisplay,kd101ne3-40ti",
.data = &kingdisplay_kd101ne3_40ti_desc
},
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36536.c b/drivers/gpu/drm/panel/panel-novatek-nt36536.c
index 2a82b54880c3..8bd125650168 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt36536.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36536.c
@@ -429,11 +429,9 @@ static int novatek_probe(struct mipi_dsi_device *dsi)
ctx->dsi[i]->mode_flags = desc->mode_flags;
ctx->dsi[i]->dsc = &ctx->dsc;
ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
- if (ret < 0) {
- drm_panel_remove(&ctx->panel);
+ if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to attach to DSI host\n");
- }
}
if (desc->has_dcs_backlight) {
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 292212119fda..c95964ba25a9 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -912,6 +912,36 @@ static const struct panel_desc ampire_am_800480l1tmqw_t00h = {
.connector_type = DRM_MODE_CONNECTOR_DPI,
};
+static const struct display_timing ampire_am_800480n3tzqw_00h_timing = {
+ .pixelclock = {23000000, 25000000, 27000000},
+ .hactive = { 800, 800, 800 },
+ .hfront_porch = { 4, 8, 48 },
+ .hback_porch = { 4, 8, 48 },
+ .hsync_len = { 2, 8, 8 },
+ .vactive = { 480, 480, 480 },
+ .vfront_porch = { 4, 8, 12 },
+ .vback_porch = { 4, 8, 12 },
+ .vsync_len = { 2, 4, 8 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+ DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
+ DISPLAY_FLAGS_SYNC_POSEDGE,
+};
+
+static const struct panel_desc ampire_am_800480n3tzqw_00h = {
+ .timings = &ampire_am_800480n3tzqw_00h_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 108,
+ .height = 65,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
static const struct panel_desc ampire_am800480r3tmqwa1h = {
.modes = &ampire_am800480r3tmqwa1h_mode,
.num_modes = 1,
@@ -5649,6 +5679,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "ampire,am-800480l1tmqw-t00h",
.data = &ampire_am_800480l1tmqw_t00h,
}, {
+ .compatible = "ampire,am-800480n3tzqw-00h",
+ .data = &ampire_am_800480n3tzqw_00h,
+ }, {
.compatible = "ampire,am800480r3tmqwa1h",
.data = &ampire_am800480r3tmqwa1h,
}, {
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
index fcbb0b7d50e4..70f7282471e6 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
@@ -47,6 +47,7 @@
#define ST7703_CMD_SETCABC 0xC8
#define ST7703_CMD_SETPANEL 0xCC
#define ST7703_CMD_SETGAMMA 0xE0
+#define ST7703_CMD_UNKNOWN_E1 0xE1
#define ST7703_CMD_SETEQ 0xE3
#define ST7703_CMD_SETGIP1 0xE9
#define ST7703_CMD_SETGIP2 0xEA
@@ -673,6 +674,100 @@ static const struct st7703_panel_desc gameforcechi_desc = {
.init_sequence = gameforcechi_init_sequence,
};
+static void ayaneo_pocket_ds_lower_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ /* Init sequence extracted from the AYANEO Pocket DS BSP kernel. */
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12,
+ 0x87);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x00, 0x01,
+ 0x78);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10,
+ 0x28, 0x28, 0x03, 0xff, 0x00, 0x00, 0x00,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x0a, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x90, 0x90);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x76,
+ 0x22, 0xf0, 0x13);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81,
+ 0x05, 0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
+ 0x00, 0x91, 0x0a, 0x00, 0x00, 0x01, 0x4f,
+ 0x01, 0x00, 0x00, 0x37);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x47);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x10,
+ 0x00, 0x80, 0x04);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73,
+ 0x50, 0x50, 0x00, 0x00, 0x12, 0x73, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x55, 0x00,
+ 0x32, 0x32, 0x77, 0xf1, 0xf7, 0xf7, 0xcc,
+ 0xcc, 0x7f, 0x7f, 0x11, 0x11, 0x00, 0x00,
+ 0x32);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETIO, 0xb8, 0x00,
+ 0x0a, 0x10, 0x01, 0x09, 0x00, 0x00, 0xed,
+ 0xc5, 0x00, 0xa5);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCABC, 0x10, 0x40,
+ 0x1e, 0x03);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x00,
+ 0x00, 0x25, 0x31, 0x3f, 0x26, 0x29, 0x04,
+ 0x08, 0x0d, 0x0e, 0x12, 0x0f, 0x11, 0x12,
+ 0x18, 0x00, 0x00, 0x00, 0x25, 0x31, 0x3f,
+ 0x26, 0x29, 0x04, 0x08, 0x0d, 0x0e, 0x12,
+ 0x0f, 0x11, 0x12, 0x18);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_E1, 0x11, 0x11,
+ 0x91, 0x00, 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x84, 0xc0, 0x10);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10,
+ 0x04, 0x04, 0x02, 0x80, 0x81, 0x12, 0x31,
+ 0x23, 0x2f, 0x80, 0x80, 0x81, 0x27, 0x20,
+ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x44, 0x00,
+ 0x88, 0xff, 0x00, 0x22, 0x44, 0x66, 0x88,
+ 0x88, 0x88, 0x55, 0x11, 0x88, 0xff, 0x11,
+ 0x33, 0x55, 0x77, 0x88, 0x88, 0x88, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x11, 0x55, 0xff, 0x88,
+ 0x77, 0x55, 0x33, 0x11, 0x88, 0x88, 0x88,
+ 0x00, 0x44, 0xff, 0x88, 0x66, 0x44, 0x22,
+ 0x00, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00,
+ 0x00, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x80, 0x81, 0x00,
+ 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff,
+ 0x01);
+}
+
+static const struct drm_display_mode ayaneo_pocket_ds_lower_mode = {
+ .hdisplay = 768,
+ .hsync_start = 768 + 110,
+ .hsync_end = 768 + 110 + 45,
+ .htotal = 768 + 110 + 45 + 110,
+ .vdisplay = 1024,
+ .vsync_start = 1024 + 18,
+ .vsync_end = 1024 + 18 + 6,
+ .vtotal = 1024 + 18 + 6 + 16,
+ .clock = 65946,
+ .width_mm = 76,
+ .height_mm = 102,
+};
+
+static const struct st7703_panel_desc ayaneo_pocket_ds_lower_desc = {
+ .mode = &ayaneo_pocket_ds_lower_mode,
+ .lanes = 4,
+ .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,
+ .init_sequence = ayaneo_pocket_ds_lower_init_sequence,
+};
+
static int st7703_enable(struct drm_panel *panel)
{
struct st7703 *ctx = panel_to_st7703(panel);
@@ -916,6 +1011,7 @@ static void st7703_remove(struct mipi_dsi_device *dsi)
static const struct of_device_id st7703_of_match[] = {
{ .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },
+ { .compatible = "ayaneo,pocket-ds-lower-panel", .data = &ayaneo_pocket_ds_lower_desc },
{ .compatible = "gameforce,chi-panel", .data = &gameforcechi_desc },
{ .compatible = "powkiddy,rgb10max3-panel", .data = &rgb10max3_panel_desc },
{ .compatible = "powkiddy,rgb30-panel", .data = &rgb30panel_desc },
diff --git a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
index 13cfe252a838..fa961abdd2d7 100644
--- a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
+++ b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
@@ -35,6 +35,7 @@ struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
{
struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link };
int err;
err = regulator_enable(tdo_tl070wsh30->supply);
@@ -51,44 +52,27 @@ static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
msleep(200);
- err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
- if (err < 0) {
- dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
- regulator_disable(tdo_tl070wsh30->supply);
- return err;
- }
-
- msleep(200);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 200);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
- err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
- if (err < 0) {
- dev_err(panel->dev, "failed to set display on: %d\n", err);
+ if (dsi_ctx.accum_err)
regulator_disable(tdo_tl070wsh30->supply);
- return err;
- }
-
- msleep(20);
- return 0;
+ return dsi_ctx.accum_err;
}
static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
{
struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
- int err;
-
- err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
- if (err < 0)
- dev_err(panel->dev, "failed to set display off: %d\n", err);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link };
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ /* Reset error to continue power-down sequence even if display off failed */
+ dsi_ctx.accum_err = 0;
usleep_range(10000, 11000);
-
- err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
- if (err < 0) {
- dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
- return err;
- }
-
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
usleep_range(10000, 11000);
regulator_disable(tdo_tl070wsh30->supply);
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 106da676fd2e..46a3080b0b20 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1824,7 +1824,7 @@ static int panthor_probe(struct platform_device *pdev)
ptdev = devm_drm_dev_alloc(&pdev->dev, &panthor_drm_driver,
struct panthor_device, base);
if (IS_ERR(ptdev))
- return -ENOMEM;
+ return PTR_ERR(ptdev);
platform_set_drvdata(pdev, ptdev);
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index de8e6689a869..fc1a423e48a8 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -697,7 +697,8 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev,
return ret;
if (hdr.meta_start > fw->size ||
- hdr.meta_start + hdr.meta_size > fw->size) {
+ hdr.meta_size > fw->size - hdr.meta_start ||
+ hdr.meta_size <= header_len) {
drm_err(&ptdev->base, "Firmware build info corrupt\n");
/* We don't need the build info, so continue */
return 0;
@@ -877,14 +878,15 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev,
struct panthor_fw_csg_iface *csg_iface = panthor_fw_get_csg_iface(ptdev, csg_idx);
struct panthor_fw_cs_iface *cs_iface = &ptdev->fw->iface.streams[csg_idx][cs_idx];
u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
- u32 iface_offset = CSF_GROUP_CONTROL_OFFSET +
- (csg_idx * glb_iface->control->group_stride) +
+ u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
+ ((u64)csg_idx * glb_iface->control->group_stride) +
CSF_STREAM_CONTROL_OFFSET +
- (cs_idx * csg_iface->control->stream_stride);
+ ((u64)cs_idx * csg_iface->control->stream_stride);
struct panthor_fw_cs_iface *first_cs_iface =
panthor_fw_get_cs_iface(ptdev, 0, 0);
- if (iface_offset + sizeof(*cs_iface) >= shared_section_sz)
+ if (iface_offset > shared_section_sz ||
+ sizeof(*cs_iface->control) > shared_section_sz - iface_offset)
return -EINVAL;
spin_lock_init(&cs_iface->lock);
@@ -932,10 +934,12 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev,
struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
struct panthor_fw_csg_iface *csg_iface = &ptdev->fw->iface.groups[csg_idx];
u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
- u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + (csg_idx * glb_iface->control->group_stride);
+ u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
+ ((u64)csg_idx * glb_iface->control->group_stride);
unsigned int i;
- if (iface_offset + sizeof(*csg_iface) >= shared_section_sz)
+ if (iface_offset > shared_section_sz ||
+ sizeof(*csg_iface->control) > shared_section_sz - iface_offset)
return -EINVAL;
spin_lock_init(&csg_iface->lock);
@@ -985,11 +989,15 @@ static u32 panthor_get_instr_features(struct panthor_device *ptdev)
static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
{
struct panthor_fw_global_iface *glb_iface = &ptdev->fw->iface.global;
+ u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
unsigned int i;
if (!ptdev->fw->shared_section->mem->kmap)
return -EINVAL;
+ if (sizeof(*glb_iface->control) > shared_section_sz)
+ return -EINVAL;
+
spin_lock_init(&glb_iface->lock);
glb_iface->control = ptdev->fw->shared_section->mem->kmap;
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index f45ef5824ff2..0182b72f1932 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -306,10 +306,13 @@ struct panthor_vm {
/** @kernel_auto_va.start: Start of the automatic VA-range for kernel BOs. */
u64 start;
- /** @kernel_auto_va.size: Size of the automatic VA-range for kernel BOs. */
+ /** @kernel_auto_va.end: End of the automatic VA-range for kernel BOs. */
u64 end;
} kernel_auto_va;
+ /** @user_va_range: Upper boundary of VAs VM users can map objects against. */
+ u64 user_va_range;
+
/** @as: Address space related fields. */
struct {
/**
@@ -2892,6 +2895,8 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
va_range = full_va_range;
}
+ vm->user_va_range = kernel_va_start;
+
mutex_init(&vm->mm_lock);
drm_mm_init(&vm->mm, kernel_va_start, kernel_va_size);
vm->kernel_auto_va.start = auto_kernel_va_start;
@@ -2980,6 +2985,10 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file,
if (!IS_ALIGNED(op->va | op->size | op->bo_offset, vm_pgsz))
return -EINVAL;
+ /* We don't allow mappings that overlap with kbo's reserved range */
+ if (range_overflows(op->va, op->size, vm->user_va_range))
+ return -EINVAL;
+
switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) {
case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP:
if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) {
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 0940eae7a2e4..0b0fc6fe3df2 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -14,6 +14,7 @@
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/math.h>
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/regulator/consumer.h>
@@ -140,6 +141,11 @@
#define SSD133X_SET_PRECHARGE_VOLTAGE 0xbb
#define SSD133X_SET_VCOMH_VOLTAGE 0xbe
+/* ssd133x A/B/C channel contrast at full brightness (white balance) */
+#define SSD133X_DEFAULT_CONTRAST_A 0x91
+#define SSD133X_DEFAULT_CONTRAST_B 0x50
+#define SSD133X_DEFAULT_CONTRAST_C 0x7d
+
#define MAX_CONTRAST 255
const struct ssd130x_deviceinfo ssd130x_variants[] = {
@@ -582,12 +588,37 @@ static int ssd132x_init(struct ssd130x_device *ssd130x)
return ssd130x_run_cmd_seq(ssd130x, cmds);
}
+/* Scale a channel's white-balance calibration contrast by the requested brightness */
+static u8 ssd130x_scale_contrast(u8 calibration, u32 brightness)
+{
+ return DIV_ROUND_CLOSEST(calibration * brightness, MAX_CONTRAST);
+}
+
+/*
+ * The A/B/C contrast channels drive sub-pixels whose OLED materials differ
+ * in luminous efficiency, so the per-channel values are a white-balance
+ * calibration. Scale them by the requested brightness instead of
+ * overwriting them, to keep the white point while dimming.
+ */
+static int ssd133x_set_contrast(struct ssd130x_device *ssd130x, u32 brightness)
+{
+ const u8 cmds[] = {
+ 2, SSD133X_CONTRAST_A,
+ ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_A, brightness),
+ 2, SSD133X_CONTRAST_B,
+ ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_B, brightness),
+ 2, SSD133X_CONTRAST_C,
+ ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_C, brightness),
+ 0,
+ };
+
+ return ssd130x_run_cmd_seq(ssd130x, cmds);
+}
+
static int ssd133x_init(struct ssd130x_device *ssd130x)
{
+ int ret;
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,
@@ -614,6 +645,10 @@ static int ssd133x_init(struct ssd130x_device *ssd130x)
0,
};
+ ret = ssd133x_set_contrast(ssd130x, ssd130x->contrast);
+ if (ret < 0)
+ return ret;
+
return ssd130x_run_cmd_seq(ssd130x, cmds);
}
@@ -1676,8 +1711,25 @@ static int ssd130x_update_bl(struct backlight_device *bdev)
return 0;
}
-static const struct backlight_ops ssd130xfb_bl_ops = {
- .update_status = ssd130x_update_bl,
+static int ssd133x_update_bl(struct backlight_device *bdev)
+{
+ struct ssd130x_device *ssd130x = bl_get_data(bdev);
+
+ ssd130x->contrast = backlight_get_brightness(bdev);
+
+ return ssd133x_set_contrast(ssd130x, ssd130x->contrast);
+}
+
+static const struct backlight_ops ssd130xfb_bl_ops[] = {
+ [SSD130X_FAMILY] = {
+ .update_status = ssd130x_update_bl,
+ },
+ [SSD132X_FAMILY] = {
+ .update_status = ssd130x_update_bl,
+ },
+ [SSD133X_FAMILY] = {
+ .update_status = ssd133x_update_bl,
+ },
};
static void ssd130x_parse_properties(struct ssd130x_device *ssd130x)
@@ -1884,7 +1936,8 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)
return ERR_PTR(ret);
bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x,
- &ssd130xfb_bl_ops, NULL);
+ &ssd130xfb_bl_ops[ssd130x->device_info->family_id],
+ NULL);
if (IS_ERR(bl))
return ERR_PTR(dev_err_probe(dev, PTR_ERR(bl),
"Unable to register backlight device\n"));
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index c2f7f69baf75..3fa1f4fda0f0 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -208,7 +208,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
planes = sunxi_engine_layers_init(drm, engine);
if (IS_ERR(planes)) {
dev_err(drm->dev, "Couldn't create the planes\n");
- return NULL;
+ return ERR_CAST(planes);
}
/* find primary and cursor planes for drm_crtc_init_with_planes */
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 8a409eee1dca..e9baca360160 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -289,6 +289,8 @@ static void sun4i_drv_traverse_endpoints(struct endpoint_list *list,
kfifo_put(&list->fifo, remote);
}
+
+ of_node_put(port);
}
static int sun4i_drv_add_endpoints(struct device *dev,
@@ -394,6 +396,7 @@ static int sun4i_drv_probe(struct platform_device *pdev)
/* process this endpoint */
ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match,
endpoint);
+ of_node_put(endpoint);
/* sun4i_drv_add_endpoints can fail to allocate memory */
if (ret < 0)
diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c
index 9c8ef927d929..fc822bc2c62b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c
@@ -14,24 +14,8 @@
#include "sun4i_drv.h"
#include "sun4i_framebuffer.h"
-static int sun4i_de_atomic_check(struct drm_device *dev,
- struct drm_atomic_commit *state)
-{
- int ret;
-
- ret = drm_atomic_helper_check_modeset(dev, state);
- if (ret)
- return ret;
-
- ret = drm_atomic_normalize_zpos(dev, state);
- if (ret)
- return ret;
-
- return drm_atomic_helper_check_planes(dev, state);
-}
-
static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = {
- .atomic_check = sun4i_de_atomic_check,
+ .atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
.fb_create = drm_gem_fb_create,
};
@@ -46,6 +30,7 @@ void sun4i_framebuffer_init(struct drm_device *drm)
drm->mode_config.max_width = 8192;
drm->mode_config.max_height = 8192;
+ drm->mode_config.normalize_zpos = true;
drm->mode_config.funcs = &sun4i_de_mode_config_funcs;
drm->mode_config.helper_private = &sun4i_de_mode_config_helpers;
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index e99f52ebb26f..97253d631b2b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -160,7 +160,7 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
+ val = SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index d4c1723c5e3d..bf405a2aa324 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -717,9 +717,11 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
case DRM_MODE_ENCODER_DSI:
/* DSI is tied to special case of CPU interface */
sun4i_tcon0_mode_set_cpu(tcon, encoder, mode);
+ sun4i_tcon_set_mux(tcon, 0, encoder);
break;
case DRM_MODE_ENCODER_LVDS:
sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
+ sun4i_tcon_set_mux(tcon, 0, encoder);
break;
case DRM_MODE_ENCODER_NONE:
sun4i_tcon0_mode_set_rgb(tcon, encoder, mode);
@@ -970,6 +972,7 @@ static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
continue;
ret = of_property_read_u32(remote, "reg", &reg);
+ of_node_put(remote);
if (ret)
continue;
@@ -1409,7 +1412,7 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
{
struct device_node *port, *remote;
struct platform_device *pdev;
- int id, ret;
+ int id, ret = 0;
/* find TCON TOP platform device and TCON id */
@@ -1432,21 +1435,20 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
encoder->encoder_type == DRM_MODE_ENCODER_TMDS) {
ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id);
- if (ret) {
- put_device(&pdev->dev);
- return ret;
- }
+ if (ret)
+ goto out_put_device;
}
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) {
ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id);
- if (ret) {
- put_device(&pdev->dev);
- return ret;
- }
+ if (ret)
+ goto out_put_device;
}
- return 0;
+out_put_device:
+ put_device(&pdev->dev);
+
+ return ret;
}
static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
index 9694ad142a7d..bd3923ef5a41 100644
--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
+++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
@@ -86,6 +86,8 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
of_node_put(remote_port);
}
}
+
+ of_node_put(port);
} else {
crtcs = drm_of_find_possible_crtcs(drm, node);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 4fa69c463dc4..489ea94693ff 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -91,7 +91,7 @@ static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
},
}, {
594000000, {
- { 0x1a40, 0x0003 },
+ { 0x1a7c, 0x0003 },
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 4e4226295010..b6cd7352e76c 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -785,7 +785,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
.de_type = SUN8I_MIXER_DE2,
.vi_scaler_num = 2,
.scaler_mask = 0x3,
- .scanline_yuv = 2048,
+ .scanline_yuv = 1024,
},
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 150000000,
diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
index 8adda578c51b..9cbd655518b2 100644
--- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
+++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c
@@ -25,6 +25,49 @@ static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node)
return !!of_match_node(sun8i_tcon_top_of_table, node);
}
+static unsigned int sun8i_tcon_top_get_tcon_map(struct device_node *node)
+{
+ static const u32 out_ports[] = {
+ TCON_TOP_MIXER0_OUT_PORT,
+ TCON_TOP_MIXER1_OUT_PORT,
+ };
+ unsigned int i, map = 0;
+
+ for (i = 0; i < ARRAY_SIZE(out_ports); i++) {
+ struct device_node *port;
+
+ port = of_graph_get_port_by_id(node, out_ports[i]);
+ if (!port)
+ continue;
+
+ for_each_of_graph_port_endpoint(port, ep) {
+ struct of_endpoint endpoint;
+
+ if (of_graph_parse_endpoint(ep, &endpoint))
+ continue;
+
+ if (endpoint.id < TCON_TOP_PORT_TCON_NUM)
+ map |= BIT(endpoint.id);
+ }
+
+ of_node_put(port);
+ }
+
+ return map;
+}
+
+static unsigned int sun8i_tcon_top_park_index(struct sun8i_tcon_top *tcon_top,
+ int tcon)
+{
+ unsigned int candidates;
+
+ candidates = tcon_top->tcon_map & ~BIT(tcon);
+ if (!candidates)
+ candidates = GENMASK(TCON_TOP_PORT_TCON_NUM - 1, 0) & ~BIT(tcon);
+
+ return ffs(candidates) - 1;
+}
+
int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon)
{
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
@@ -57,6 +100,7 @@ EXPORT_SYMBOL(sun8i_tcon_top_set_hdmi_src);
int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon)
{
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
+ u32 mixer_msk, other_msk;
unsigned long flags;
u32 reg;
@@ -70,21 +114,27 @@ int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon)
return -EINVAL;
}
- if (tcon > 3) {
- dev_err(dev, "TCON index is too high!\n");
+ if (tcon < 0 || tcon >= TCON_TOP_PORT_TCON_NUM) {
+ dev_err(dev, "TCON index is invalid!\n");
return -EINVAL;
}
+ mixer_msk = mixer ? TCON_TOP_PORT_DE1_MSK : TCON_TOP_PORT_DE0_MSK;
+ other_msk = mixer ? TCON_TOP_PORT_DE0_MSK : TCON_TOP_PORT_DE1_MSK;
+
spin_lock_irqsave(&tcon_top->reg_lock, flags);
reg = readl(tcon_top->regs + TCON_TOP_PORT_SEL_REG);
- if (mixer == 0) {
- reg &= ~TCON_TOP_PORT_DE0_MSK;
- reg |= FIELD_PREP(TCON_TOP_PORT_DE0_MSK, tcon);
- } else {
- reg &= ~TCON_TOP_PORT_DE1_MSK;
- reg |= FIELD_PREP(TCON_TOP_PORT_DE1_MSK, tcon);
+
+ reg &= ~mixer_msk;
+ reg |= field_prep(mixer_msk, tcon);
+
+ if (field_get(other_msk, reg) == tcon) {
+ reg &= ~other_msk;
+ reg |= field_prep(other_msk,
+ sun8i_tcon_top_park_index(tcon_top, tcon));
}
+
writel(reg, tcon_top->regs + TCON_TOP_PORT_SEL_REG);
spin_unlock_irqrestore(&tcon_top->reg_lock, flags);
@@ -143,6 +193,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
return -ENOMEM;
clk_data->num = CLK_NUM;
tcon_top->clk_data = clk_data;
+ tcon_top->tcon_map = sun8i_tcon_top_get_tcon_map(dev->of_node);
spin_lock_init(&tcon_top->reg_lock);
@@ -175,10 +226,6 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
goto err_assert_reset;
}
- /*
- * At least on H6, some registers have some bits set by default
- * which may cause issues. Clear them here.
- */
writel(0, regs + TCON_TOP_PORT_SEL_REG);
writel(0, regs + TCON_TOP_GATE_SRC_REG);
diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.h b/drivers/gpu/drm/sun4i/sun8i_tcon_top.h
index 0390584a330e..2b887470a49f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.h
+++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.h
@@ -14,6 +14,10 @@
#define TCON_TOP_PORT_SEL_REG 0x1C
#define TCON_TOP_PORT_DE0_MSK GENMASK(1, 0)
#define TCON_TOP_PORT_DE1_MSK GENMASK(5, 4)
+#define TCON_TOP_PORT_TCON_NUM 4
+
+#define TCON_TOP_MIXER0_OUT_PORT 1
+#define TCON_TOP_MIXER1_OUT_PORT 3
#define TCON_TOP_GATE_SRC_REG 0x20
#define TCON_TOP_HDMI_SRC_MSK GENMASK(29, 28)
@@ -29,6 +33,8 @@ struct sun8i_tcon_top {
void __iomem *regs;
struct reset_control *rst;
+ unsigned int tcon_map;
+
/*
* spinlock is used to synchronize access to same
* register where multiple clock gates can be set.
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
index 3dec4eeb1ba2..a5cc99981006 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
@@ -894,20 +894,26 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
lan3coefftab32_left[offset + i]);
regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i),
lan3coefftab32_right[offset + i]);
+ }
+ offset = sun8i_vi_scaler_coef_index(vstep) *
+ SUN8I_VI_SCALER_COEFF_COUNT;
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
+ regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
+ lan2coefftab32[offset + i]);
+
+ offset = sun8i_vi_scaler_coef_index(hstep / format->hsub) *
+ SUN8I_VI_SCALER_COEFF_COUNT;
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i),
ch_left[offset + i]);
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i),
ch_right[offset + i]);
}
-
- offset = sun8i_vi_scaler_coef_index(hstep) *
+ offset = sun8i_vi_scaler_coef_index(vstep / format->vsub) *
SUN8I_VI_SCALER_COEFF_COUNT;
- for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
- regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
- lan2coefftab32[offset + i]);
+ for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i),
cy[offset + i]);
- }
}
void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable)
@@ -969,6 +975,8 @@ void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
regmap_write(layer->regs,
SUN50I_SCALER_VSU_SCALE_MODE(base), val);
+ regmap_write(layer->regs,
+ SUN50I_SCALER_VSU_GLB_ALPHA(base), 0xff);
}
regmap_write(layer->regs,
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
index 245fe2f431c3..d89c0cbe188d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
@@ -36,6 +36,7 @@
#define SUN50I_SCALER_VSU_EDSCL_CTRL(base) ((base) + 0x28)
#define SUN50I_SCALER_VSU_ANGLE_THR(base) ((base) + 0x2c)
#define SUN8I_SCALER_VSU_OUTSIZE(base) ((base) + 0x40)
+#define SUN50I_SCALER_VSU_GLB_ALPHA(base) ((base) + 0x44)
#define SUN8I_SCALER_VSU_YINSIZE(base) ((base) + 0x80)
#define SUN8I_SCALER_VSU_YHSTEP(base) ((base) + 0x88)
#define SUN8I_SCALER_VSU_YVSTEP(base) ((base) + 0x8c)
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 3980f376e3ba..ef56c18ded1b 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -488,6 +488,118 @@ out_no_ref:
return ret;
}
+struct ttm_bo_alloc_state {
+ /** @charge_pool: The memory pool the resource is charged to */
+ struct dmem_cgroup_pool_state *charge_pool;
+ /** @limit_pool: Which pool limit we should test against */
+ struct dmem_cgroup_pool_state *limit_pool;
+ /** @in_evict: Whether we are currently evicting buffers */
+ bool in_evict;
+ /** @may_try_low: If only unprotected BOs, i.e. BOs whose cgroup
+ * is exceeding its dmem low/min protection, should be considered for eviction
+ */
+ bool may_try_low;
+};
+
+/**
+ * ttm_bo_alloc_at_place - Attempt allocating a BO's backing store in a place
+ *
+ * @bo: The buffer to allocate the backing store of
+ * @place: The place to attempt allocation in
+ * @ctx: ttm_operation_ctx associated with this allocation
+ * @force_space: If we should evict buffers to force space
+ * @res: On allocation success, the resulting struct ttm_resource.
+ * @alloc_state: Object holding allocation state such as charged cgroups.
+ *
+ * Returns:
+ * -EBUSY: No space available, but allocation should be retried with ttm_bo_evict_alloc.
+ * -ENOSPC: No space available, allocation should not be retried.
+ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
+ *
+ */
+static int ttm_bo_alloc_at_place(struct ttm_buffer_object *bo,
+ const struct ttm_place *place,
+ bool force_space,
+ struct ttm_resource **res,
+ struct ttm_bo_alloc_state *alloc_state)
+{
+ bool may_evict;
+ int ret;
+
+ may_evict = !alloc_state->in_evict && force_space &&
+ place->mem_type != TTM_PL_SYSTEM;
+ if (!alloc_state->charge_pool) {
+ ret = ttm_resource_try_charge(bo, place, &alloc_state->charge_pool,
+ force_space ? &alloc_state->limit_pool
+ : NULL);
+ if (ret) {
+ /*
+ * -EAGAIN means the charge failed, which we treat
+ * like an allocation failure. Therefore, return an
+ * error code indicating the allocation failed -
+ * either -EBUSY if the allocation should be
+ * retried with eviction, or -ENOSPC if there should
+ * be no second attempt.
+ */
+ if (!alloc_state->in_evict)
+ alloc_state->may_try_low = may_evict;
+ if (ret == -EAGAIN)
+ ret = may_evict ? -EBUSY : -ENOSPC;
+ return ret;
+ }
+ }
+
+ /*
+ * cgroup protection plays a special role in eviction.
+ * Conceptually, protection of memory via the dmem cgroup controller
+ * entitles the protected cgroup to use a certain amount of memory.
+ * There are two types of protection - the 'low' limit is a
+ * "best-effort" protection, whereas the 'min' limit provides a hard
+ * guarantee that memory within the cgroup's allowance will not be
+ * evicted under any circumstance.
+ *
+ * To faithfully model this concept in TTM, we also need to take cgroup
+ * protection into account when allocating. When allocation in one
+ * place fails, TTM will default to trying other places first before
+ * evicting.
+ * If the allocation is covered by dmem cgroup protection, however,
+ * this prevents the allocation from using the memory it is "entitled"
+ * to. To make sure unprotected allocations cannot push new protected
+ * allocations out of places they are "entitled" to use, we should
+ * evict buffers not covered by any cgroup protection, if this
+ * allocation is covered by cgroup protection.
+ *
+ * Buffers covered by 'min' protection are a special case - the 'min'
+ * limit is a stronger guarantee than 'low', and thus buffers protected
+ * by 'low' but not 'min' should also be considered for eviction.
+ * Buffers protected by 'min' will never be considered for eviction
+ * anyway, so the regular eviction path should be triggered here.
+ * Buffers protected by 'low' but not 'min' will take a special
+ * eviction path that only evicts buffers covered by neither 'low' or
+ * 'min' protections.
+ */
+ if (!alloc_state->in_evict) {
+ may_evict |= dmem_cgroup_below_min(NULL, alloc_state->charge_pool);
+ alloc_state->may_try_low = may_evict;
+
+ may_evict |= dmem_cgroup_below_low(NULL, alloc_state->charge_pool);
+ }
+
+ ret = ttm_resource_alloc(bo, place, res, alloc_state->charge_pool);
+ if (ret) {
+ if (ret == -ENOSPC && may_evict)
+ ret = -EBUSY;
+ return ret;
+ }
+
+ /*
+ * Ownership of charge_pool has been transferred to the TTM resource,
+ * don't make the caller think we still hold a reference to it.
+ */
+ alloc_state->charge_pool = NULL;
+ return 0;
+}
+
/**
* struct ttm_bo_evict_walk - Parameters for the evict walk.
*/
@@ -503,24 +615,70 @@ struct ttm_bo_evict_walk {
/** @evicted: Number of successful evictions. */
unsigned long evicted;
- /** @limit_pool: Which pool limit we should test against */
- struct dmem_cgroup_pool_state *limit_pool;
/** @try_low: Whether we should attempt to evict BO's with low watermark threshold */
bool try_low;
/** @hit_low: If we cannot evict a bo when @try_low is false (first pass) */
bool hit_low;
+
+ /** @alloc_state: State associated with the allocation attempt. */
+ struct ttm_bo_alloc_state *alloc_state;
};
static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
{
struct ttm_bo_evict_walk *evict_walk =
container_of(walk, typeof(*evict_walk), walk);
+ struct dmem_cgroup_pool_state *limit_pool, *ancestor = NULL;
+ s64 bo_size = bo->base.size;
+ bool evict_valuable;
s64 lret;
- if (!dmem_cgroup_state_evict_valuable(evict_walk->limit_pool, bo->resource->css,
- evict_walk->try_low, &evict_walk->hit_low))
+ /*
+ * If may_try_low is not set, then we're trying to evict unprotected
+ * buffers in favor of a protected allocation for charge_pool. Explicitly skip
+ * buffers belonging to the same cgroup here - that cgroup is definitely protected,
+ * even though dmem_cgroup_state_evict_valuable would allow the eviction because a
+ * cgroup is always allowed to evict from itself even if it is protected.
+ */
+ if (!evict_walk->alloc_state->may_try_low &&
+ bo->resource->css == evict_walk->alloc_state->charge_pool)
return 0;
+ limit_pool = evict_walk->alloc_state->limit_pool;
+ /*
+ * If there is no explicit limit pool, find the root of the shared subtree between
+ * evictor and evictee. This is important so that recursive protection rules can
+ * apply properly: Recursive protection distributes cgroup protection afforded
+ * to a parent cgroup but not used explicitly by a child cgroup between all child
+ * cgroups (see docs of effective_protection in mm/page_counter.c). However, when
+ * direct siblings compete for memory, siblings that were explicitly protected
+ * should get prioritized over siblings that weren't. This only happens correctly
+ * when the root of the shared subtree is passed to
+ * dmem_cgroup_state_evict_valuable. Otherwise, the effective-protection
+ * calculation cannot distinguish direct siblings from unrelated subtrees and the
+ * calculated protection ends up wrong.
+ */
+ if (!limit_pool) {
+ ancestor = dmem_cgroup_get_common_ancestor(bo->resource->css,
+ evict_walk->alloc_state->charge_pool);
+ limit_pool = ancestor;
+ }
+
+ evict_valuable = dmem_cgroup_state_evict_valuable(limit_pool, bo->resource->css,
+ evict_walk->try_low,
+ &evict_walk->hit_low);
+ if (ancestor)
+ dmem_cgroup_pool_state_put(ancestor);
+
+ if (!evict_valuable)
+ return 0;
+
+ /*
+ * evict_walk->place is NULL in cgroup drain mode. Drivers'
+ * eviction_valuable() callbacks must handle a NULL place, treating it
+ * as "any placement": the TTM base implementation already does so via
+ * ttm_resource_intersects().
+ */
if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place))
return 0;
@@ -536,11 +694,17 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
goto out;
evict_walk->evicted++;
- if (evict_walk->res)
- lret = ttm_resource_alloc(evict_walk->evictor, evict_walk->place,
- evict_walk->res, NULL);
- if (lret == 0)
- return 1;
+ if (evict_walk->res) {
+ lret = ttm_bo_alloc_at_place(evict_walk->evictor,
+ evict_walk->place, false,
+ evict_walk->res,
+ evict_walk->alloc_state);
+ if (lret == 0)
+ return 1;
+ } else {
+ /* Cgroup drain: return bytes freed for byte-denominated progress. */
+ return bo_size;
+ }
out:
/* Errors that should terminate the walk. */
if (lret == -ENOSPC)
@@ -560,7 +724,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
struct ttm_operation_ctx *ctx,
struct ww_acquire_ctx *ticket,
struct ttm_resource **res,
- struct dmem_cgroup_pool_state *limit_pool)
+ struct ttm_bo_alloc_state *state)
{
struct ttm_bo_evict_walk evict_walk = {
.walk = {
@@ -573,15 +737,21 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
.place = place,
.evictor = evictor,
.res = res,
- .limit_pool = limit_pool,
+ .alloc_state = state,
};
s64 lret;
+ state->in_evict = true;
+
evict_walk.walk.arg.trylock_only = true;
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
- /* One more attempt if we hit low limit? */
- if (!lret && evict_walk.hit_low) {
+ /* If we failed to find enough BOs to evict, but we skipped over
+ * some BOs because they were covered by dmem low protection, retry
+ * evicting these protected BOs too, except if we're told not to
+ * consider protected BOs at all.
+ */
+ if (!lret && evict_walk.hit_low && state->may_try_low) {
evict_walk.try_low = true;
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
}
@@ -602,11 +772,13 @@ retry:
} while (!lret && evict_walk.evicted);
/* We hit the low limit? Try once more */
- if (!lret && evict_walk.hit_low && !evict_walk.try_low) {
+ if (!lret && evict_walk.hit_low && !evict_walk.try_low &&
+ state->may_try_low) {
evict_walk.try_low = true;
goto retry;
}
out:
+ state->in_evict = false;
if (lret < 0)
return lret;
if (lret == 0)
@@ -615,6 +787,86 @@ out:
}
/**
+ * ttm_bo_evict_cgroup() - Evict buffer objects charged to a specific cgroup.
+ * @bdev: The TTM device.
+ * @man: The resource manager whose LRU to walk.
+ * @limit_pool: The cgroup pool state whose members should be evicted.
+ * @target_bytes: Number of bytes to free.
+ * @ctx: The TTM operation context.
+ *
+ * Walk the LRU of @man and evict buffer objects that are charged to the
+ * cgroup identified by @limit_pool, until at least @target_bytes have been
+ * freed. Mirrors the two-pass (trylock -> sleeping-lock, low-watermark)
+ * strategy used by ttm_bo_evict_alloc().
+ *
+ * Return: >= @target_bytes on full success, 0..target_bytes-1 if partial,
+ * negative error code on fatal error.
+ */
+s64 ttm_bo_evict_cgroup(struct ttm_device *bdev,
+ struct ttm_resource_manager *man,
+ struct dmem_cgroup_pool_state *limit_pool,
+ s64 target_bytes,
+ struct ttm_operation_ctx *ctx)
+{
+ struct ttm_bo_evict_walk evict_walk = {
+ .walk = {
+ .ops = &ttm_evict_walk_ops,
+ .arg = { .ctx = ctx },
+ },
+ .alloc_state = &(struct ttm_bo_alloc_state) {
+ .limit_pool = limit_pool,
+ .in_evict = true,
+ },
+ /* place, evictor, res left NULL: selects cgroup drain mode */
+ };
+ s64 lret, pass;
+
+ evict_walk.walk.arg.trylock_only = true;
+ lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, target_bytes);
+ if (lret < 0 || lret >= target_bytes)
+ return lret;
+
+ /* Second pass: also evict BOs at the low watermark. */
+ if (evict_walk.hit_low) {
+ evict_walk.try_low = true;
+ pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man,
+ target_bytes - lret);
+ if (pass < 0)
+ return pass;
+ lret += pass;
+ if (lret >= target_bytes)
+ return lret;
+ }
+
+ /* Full sleeping-lock pass for remaining target. */
+ evict_walk.try_low = evict_walk.hit_low = false;
+ evict_walk.walk.arg.trylock_only = false;
+
+retry:
+ evict_walk.walk.arg.sleeping_lock = true;
+ do {
+ evict_walk.evicted = 0;
+ pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man,
+ target_bytes - lret);
+ if (pass < 0) {
+ lret = pass;
+ goto out;
+ }
+ lret += pass;
+ } while (lret < target_bytes && evict_walk.evicted);
+
+ /* One more attempt if we hit the low limit during sleeping-lock pass. */
+ if (lret < target_bytes && evict_walk.hit_low && !evict_walk.try_low) {
+ evict_walk.try_low = true;
+ goto retry;
+ }
+
+out:
+ return lret;
+}
+EXPORT_SYMBOL(ttm_bo_evict_cgroup);
+
+/**
* ttm_bo_pin - Pin the buffer object.
* @bo: The buffer object to pin
*
@@ -724,9 +976,8 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
for (i = 0; i < placement->num_placement; ++i) {
const struct ttm_place *place = &placement->placement[i];
- struct dmem_cgroup_pool_state *limit_pool = NULL;
+ struct ttm_bo_alloc_state alloc_state = {};
struct ttm_resource_manager *man;
- bool may_evict;
man = ttm_manager_type(bdev, place->mem_type);
if (!man || !ttm_resource_manager_used(man))
@@ -736,25 +987,30 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
TTM_PL_FLAG_FALLBACK))
continue;
- may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM);
- ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL);
- if (ret) {
- if (ret != -ENOSPC) {
- dmem_cgroup_pool_state_put(limit_pool);
- return ret;
- }
- if (!may_evict) {
- dmem_cgroup_pool_state_put(limit_pool);
- continue;
- }
+ ret = ttm_bo_alloc_at_place(bo, place, force_space, res,
+ &alloc_state);
+ if (ret == -ENOSPC) {
+ dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size);
+ dmem_cgroup_pool_state_put(alloc_state.limit_pool);
+ continue;
+ } else if (ret == -EBUSY) {
ret = ttm_bo_evict_alloc(bdev, man, place, bo, ctx,
- ticket, res, limit_pool);
- dmem_cgroup_pool_state_put(limit_pool);
- if (ret == -EBUSY)
- continue;
- if (ret)
+ ticket, res, &alloc_state);
+
+ dmem_cgroup_pool_state_put(alloc_state.limit_pool);
+
+ if (ret) {
+ dmem_cgroup_uncharge(alloc_state.charge_pool,
+ bo->base.size);
+ if (ret == -EBUSY)
+ continue;
return ret;
+ }
+ } else if (ret) {
+ dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size);
+ dmem_cgroup_pool_state_put(alloc_state.limit_pool);
+ return ret;
}
ret = ttm_bo_add_pipelined_eviction_fences(bo, man, ctx->no_wait_gpu);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 3e3c201a0222..bd0b23ac2cc4 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -999,7 +999,8 @@ __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
bo = res->bo;
if (ttm_lru_walk_trylock(curs, bo))
bo_locked = true;
- else if (!arg->ticket || arg->ctx->no_wait_gpu || arg->trylock_only)
+ else if ((!arg->ticket && !arg->sleeping_lock) || arg->ctx->no_wait_gpu ||
+ arg->trylock_only)
continue;
if (!ttm_bo_get_unless_zero(bo)) {
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 154d6739256f..4e6d3d658d85 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -386,33 +386,52 @@ void ttm_resource_fini(struct ttm_resource_manager *man,
}
EXPORT_SYMBOL(ttm_resource_fini);
+/**
+ * ttm_resource_try_charge - charge a resource manager's cgroup pool
+ * @bo: buffer for which an allocation should be charged
+ * @place: where the allocation is attempted to be placed
+ * @ret_pool: on charge success, the pool that was charged
+ * @ret_limit_pool: on charge failure, the pool responsible for the failure
+ *
+ * Should be used to charge cgroups before attempting resource allocation.
+ * When charging succeeds, the value of ret_pool should be passed to
+ * ttm_resource_alloc.
+ *
+ * Returns: 0 on charge success, negative errno on failure.
+ */
+int ttm_resource_try_charge(struct ttm_buffer_object *bo,
+ const struct ttm_place *place,
+ struct dmem_cgroup_pool_state **ret_pool,
+ struct dmem_cgroup_pool_state **ret_limit_pool)
+{
+ struct ttm_resource_manager *man =
+ ttm_manager_type(bo->bdev, place->mem_type);
+
+ if (!man->cg) {
+ *ret_pool = NULL;
+ if (ret_limit_pool)
+ *ret_limit_pool = NULL;
+ return 0;
+ }
+
+ return dmem_cgroup_try_charge(man->cg, bo->base.size, ret_pool,
+ ret_limit_pool);
+}
+
int ttm_resource_alloc(struct ttm_buffer_object *bo,
const struct ttm_place *place,
struct ttm_resource **res_ptr,
- struct dmem_cgroup_pool_state **ret_limit_pool)
+ struct dmem_cgroup_pool_state *charge_pool)
{
struct ttm_resource_manager *man =
ttm_manager_type(bo->bdev, place->mem_type);
- struct dmem_cgroup_pool_state *pool = NULL;
int ret;
- if (man->cg) {
- ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool);
- if (ret) {
- if (ret == -EAGAIN)
- ret = -ENOSPC;
- return ret;
- }
- }
-
ret = man->func->alloc(man, bo, place, res_ptr);
- if (ret) {
- if (pool)
- dmem_cgroup_uncharge(pool, bo->base.size);
+ if (ret)
return ret;
- }
- (*res_ptr)->css = pool;
+ (*res_ptr)->css = charge_pool;
spin_lock(&bo->bdev->lru_lock);
ttm_resource_add_bulk_move(*res_ptr, bo);
@@ -437,7 +456,7 @@ void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res)
man = ttm_manager_type(bo->bdev, (*res)->mem_type);
man->func->free(man, *res);
*res = NULL;
- if (man->cg)
+ if (pool)
dmem_cgroup_uncharge(pool, bo->base.size);
}
EXPORT_SYMBOL(ttm_resource_free);
@@ -953,3 +972,55 @@ void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man,
#endif
}
EXPORT_SYMBOL(ttm_resource_manager_create_debugfs);
+
+/**
+ * ttm_resource_manager_dmem_reclaim() - dmem cgroup reclaim callback for TTM
+ * resource managers.
+ * @pool: The dmem cgroup pool state for the cgroup being reclaimed.
+ * @target_bytes: Number of bytes to try to free.
+ * @priv: The &ttm_resource_manager pointer, passed as @init.reclaim_priv to
+ * dmem_cgroup_register_region().
+ *
+ * Drivers should use this as the @reclaim member of their own
+ * &struct dmem_cgroup_ops, with the &ttm_resource_manager pointer as
+ * @init.reclaim_priv.
+ *
+ * Return: 0 if some memory was freed, -ENOSPC if nothing was freed, or
+ * another negative error code on fatal failure.
+ */
+int ttm_resource_manager_dmem_reclaim(struct dmem_cgroup_pool_state *pool,
+ u64 target_bytes, void *priv)
+{
+ struct ttm_resource_manager *man = priv;
+ struct ttm_operation_ctx ctx = { .interruptible = true };
+ s64 freed;
+
+ freed = ttm_bo_evict_cgroup(man->bdev, man, pool, target_bytes, &ctx);
+ if (freed < 0)
+ return freed;
+
+ return freed > 0 ? 0 : -ENOSPC;
+}
+EXPORT_SYMBOL(ttm_resource_manager_dmem_reclaim);
+
+/**
+ * ttm_resource_manager_set_dmem_region() - Associate a dmem cgroup region with a
+ * resource manager.
+ * @man: The resource manager.
+ * @region: The dmem cgroup region to associate, may be NULL or IS_ERR().
+ *
+ * When @region is valid, stores it in @man->cg so that TTM can look up the
+ * associated pool during charging and eviction-target selection. When
+ * @region is %NULL, clears @man->cg to detach the region before teardown.
+ * An IS_ERR() @region is ignored, leaving @man->cg unchanged.
+ * The reclaim callback must be wired up using ttm_resource_manager_dmem_reclaim()
+ * in the driver's own &struct dmem_cgroup_ops, with the manager pointer as
+ * @init.reclaim_priv.
+ */
+void ttm_resource_manager_set_dmem_region(struct ttm_resource_manager *man,
+ struct dmem_cgroup_region *region)
+{
+ if (!IS_ERR(region))
+ man->cg = region;
+}
+EXPORT_SYMBOL(ttm_resource_manager_set_dmem_region);
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
index b518f7dec680..05911904c1f9 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
@@ -4,6 +4,8 @@
* Copyright (C) 2021-2022 Red Hat
*/
+#include <linux/cgroup_dmem.h>
+
#include <drm/drm_managed.h>
#include <drm/drm_drv.h>
#include <drm/drm_buddy.h>
@@ -13,6 +15,7 @@
#include "xe_bo.h"
#include "xe_device.h"
+#include "xe_pm.h"
#include "xe_res_cursor.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vram_types.h"
@@ -274,14 +277,47 @@ static const struct ttm_resource_manager_func xe_ttm_vram_mgr_func = {
.debug = xe_ttm_vram_mgr_debug
};
+static const struct dmem_cgroup_ops xe_ttm_vram_mgr_dmem_ops;
+
+static int xe_ttm_vram_mgr_dmem_reclaim(struct dmem_cgroup_pool_state *pool,
+ u64 target_bytes, void *priv)
+{
+ struct ttm_resource_manager *man = priv;
+ struct xe_device *xe = ttm_to_xe_device(man->bdev);
+ int ret, idx;
+
+ if (!drm_dev_enter(&xe->drm, &idx))
+ return -ENODEV;
+
+ {
+ ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
+
+ ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
+ if (ret >= 0)
+ ret = ttm_resource_manager_dmem_reclaim(pool, target_bytes, priv);
+ }
+
+ drm_dev_exit(idx);
+ return ret;
+}
+
+static const struct dmem_cgroup_ops xe_ttm_vram_mgr_dmem_ops = {
+ .reclaim = xe_ttm_vram_mgr_dmem_reclaim,
+};
+
+static void xe_ttm_vram_mgr_set_unused(struct drm_device *dev, void *arg)
+{
+ struct ttm_resource_manager *man = arg;
+
+ ttm_resource_manager_set_used(man, false);
+}
+
static void xe_ttm_vram_mgr_fini(struct drm_device *dev, void *arg)
{
struct xe_device *xe = to_xe_device(dev);
struct xe_ttm_vram_mgr *mgr = arg;
struct ttm_resource_manager *man = &mgr->manager;
- ttm_resource_manager_set_used(man, false);
-
if (ttm_resource_manager_evict_all(&xe->ttm, man))
return;
@@ -299,14 +335,10 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr,
u64 default_page_size)
{
struct ttm_resource_manager *man = &mgr->manager;
+ struct dmem_cgroup_region *cg;
const char *name;
int err;
- name = mem_type == XE_PL_VRAM0 ? "vram0" : "vram1";
- man->cg = drmm_cgroup_register_region(&xe->drm, name, size);
- if (IS_ERR(man->cg))
- return PTR_ERR(man->cg);
-
man->func = &xe_ttm_vram_mgr_func;
mgr->mem_type = mem_type;
err = drmm_mutex_init(&xe->drm, &mgr->lock);
@@ -323,9 +355,30 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr,
gpu_buddy_driver_set_lock(&mgr->mm, &mgr->lock);
ttm_set_driver_manager(&xe->ttm, mem_type, &mgr->manager);
- ttm_resource_manager_set_used(&mgr->manager, true);
- return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr);
+ /*
+ * Register the fini action before the cgroup region so that devres
+ * LIFO teardown runs unregister_region before manager teardown
+ * (draining any in-flight reclaim callbacks) and the manager fini second.
+ */
+ err = drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr);
+ if (err)
+ return err;
+
+ name = mem_type == XE_PL_VRAM0 ? "vram0" : "vram1";
+ cg = drmm_cgroup_register_region(&xe->drm, name,
+ &(struct dmem_cgroup_init){
+ .size = size,
+ .ops = &xe_ttm_vram_mgr_dmem_ops,
+ .reclaim_priv = man,
+ });
+ if (IS_ERR(cg))
+ return PTR_ERR(cg);
+
+ ttm_resource_manager_set_dmem_region(man, cg);
+ ttm_resource_manager_set_used(man, true);
+
+ return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_set_unused, man);
}
/**
diff --git a/drivers/gpu/tests/.kunitconfig b/drivers/gpu/tests/.kunitconfig
new file mode 100644
index 000000000000..fd611b3dfe09
--- /dev/null
+++ b/drivers/gpu/tests/.kunitconfig
@@ -0,0 +1,2 @@
+CONFIG_KUNIT=y
+CONFIG_GPU_BUDDY_KUNIT_TEST=y
diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c
index 89698563c61b..ed4c1c3acb3c 100644
--- a/drivers/gpu/tests/gpu_buddy_test.c
+++ b/drivers/gpu/tests/gpu_buddy_test.c
@@ -1004,42 +1004,51 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test)
gpu_buddy_fini(&mm);
/*
- * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an
- * even sequence and free them as cleared. All blocks should be marked as
- * dirty and the split blocks should be merged back to their original
- * size when the blocks clear reset function is called.
+ * Using a non-power-of-two mm size, allocate all 4KiB blocks and split
+ * them across two alternating lists, then free one list as cleared and
+ * the other as dirty. This interleaves cleared and dirty blocks so that
+ * neighbouring buddies cannot be merged, fragmenting the address space.
+ * After gpu_buddy_reset_clear(false) every block should be marked dirty
+ * and the split blocks should be merged back to their original size, so
+ * clear_avail must drop to 0.
*/
KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
- i = 0;
n_pages = mm_size / ps;
- do {
- if (i % 2 == 0)
- KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
- ps, ps, &allocated, 0),
- "buddy_alloc hit an error size=%lu\n", ps);
- } while (++i < n_pages);
+ for (i = 0; i < n_pages; i++) {
+ struct list_head *list = (i % 2) ? &clean : &dirty;
- gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
+ ps, ps, list, 0),
+ "buddy_alloc hit an error size=%lu\n", ps);
+ }
+
+ gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
+ gpu_buddy_free_list(&mm, &dirty, 0);
gpu_buddy_reset_clear(&mm, false);
KUNIT_EXPECT_EQ(test, mm.clear_avail, 0);
+ gpu_buddy_fini(&mm);
/*
- * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an
- * odd sequence and free them as cleared. All blocks should be marked as
- * cleared and the split blocks should be merged back to their original
- * size when the blocks clear reset function is called.
+ * Repeat the same fragmented setup, but this time call
+ * gpu_buddy_reset_clear(true). Every block should be marked cleared and
+ * the split blocks should be merged back to their original size, so the
+ * whole address space (clear_avail) must equal mm_size.
*/
- i = 0;
- do {
- if (i % 2 != 0)
- KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
- ps, ps, &allocated, 0),
- "buddy_alloc hit an error size=%lu\n", ps);
- } while (++i < n_pages);
+ KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
+ KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
- gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
+ for (i = 0; i < n_pages; i++) {
+ struct list_head *list = (i % 2) ? &clean : &dirty;
+
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
+ ps, ps, list, 0),
+ "buddy_alloc hit an error size=%lu\n", ps);
+ }
+
+ gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
+ gpu_buddy_free_list(&mm, &dirty, 0);
gpu_buddy_reset_clear(&mm, true);
KUNIT_EXPECT_EQ(test, mm.clear_avail, mm_size);
gpu_buddy_fini(&mm);
@@ -1422,6 +1431,50 @@ static void gpu_test_buddy_alloc_exceeds_max_order(struct kunit *test)
gpu_buddy_fini(&mm);
}
+static void gpu_test_buddy_addr_to_block(struct kunit *test)
+{
+ struct gpu_buddy_block *allocated_block, *found_block;
+ LIST_HEAD(allocated_list);
+ const u64 test_size = SZ_4M + SZ_2M;
+ const u64 alloc_start = SZ_4M;
+ const u64 alloc_size = SZ_4K;
+ const u64 chunk_size = SZ_4K;
+ struct gpu_buddy mm;
+
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, test_size, chunk_size),
+ "buddy_init failed\n");
+
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, alloc_start,
+ alloc_start + alloc_size,
+ alloc_size, chunk_size,
+ &allocated_list, 0),
+ "buddy_alloc failed\n");
+
+ allocated_block = list_first_entry(&allocated_list, struct gpu_buddy_block, link);
+ KUNIT_EXPECT_EQ(test, gpu_buddy_block_offset(allocated_block), alloc_start);
+ KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, allocated_block), alloc_size);
+
+ found_block = gpu_buddy_allocated_addr_to_block(&mm, alloc_start);
+ KUNIT_EXPECT_PTR_EQ(test, found_block, allocated_block);
+
+ /* Unaligned address inside the allocated block (should resolve to the same block) */
+ found_block = gpu_buddy_allocated_addr_to_block(&mm, alloc_start + 16);
+ KUNIT_EXPECT_PTR_EQ(test, found_block, allocated_block);
+
+ /* An unallocated address inside the manager should return NULL. */
+ found_block = gpu_buddy_allocated_addr_to_block(&mm,
+ alloc_start - chunk_size);
+ KUNIT_EXPECT_NULL(test, found_block);
+
+ /* An address outside the manager should return -ENXIO. */
+ found_block = gpu_buddy_allocated_addr_to_block(&mm, test_size);
+ KUNIT_EXPECT_EQ(test, PTR_ERR(found_block), -ENXIO);
+
+ /* 3. Standard inline cleanup flow */
+ gpu_buddy_free_list(&mm, &allocated_list, 0);
+ gpu_buddy_fini(&mm);
+}
+
static int gpu_buddy_suite_init(struct kunit_suite *suite)
{
while (!random_seed)
@@ -1446,6 +1499,7 @@ static struct kunit_case gpu_buddy_tests[] = {
KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order),
KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation),
KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress),
+ KUNIT_CASE(gpu_test_buddy_addr_to_block),
{}
};