summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2024-11-01 13:46:03 +1000
committerDave Airlie <airlied@redhat.com>2024-11-01 13:46:03 +1000
commit8a07b2623e7ff04856f8d4470d002675049b2065 (patch)
tree7389837e789808821463da9c5f859ade10340985 /drivers
parente7103f8785504dd5c6aad118fbc64fc49eda33af (diff)
parent2e0757012cf4f3c29a671f0981f9aa853a96def6 (diff)
Merge tag 'drm-misc-next-2024-10-31' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.13: All of the previous pull request, with MORE! Core Changes: - Update documentation for scheduler start/stop and job init. - Add dedede and sm8350-hdk hardware to ci runs. Driver Changes: - Small fixes and cleanups to panfrost, omap, nouveau, ivpu, zynqmp, v3d, panthor docs, and leadtek-ltk050h3146w. - Crashdump support for qaic. - Support DP compliance in zynqmp. - Add Samsung S6E88A0-AMS427AP24 panel. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/deeef745-f3fb-4e85-a9d0-e8d38d43c1cf@linux.intel.com
Diffstat (limited to 'drivers')
-rw-r--r--drivers/accel/ivpu/Kconfig9
-rw-r--r--drivers/accel/ivpu/Makefile2
-rw-r--r--drivers/accel/ivpu/ivpu_drv.c31
-rw-r--r--drivers/accel/ivpu/ivpu_drv.h16
-rw-r--r--drivers/accel/ivpu/ivpu_fw.c8
-rw-r--r--drivers/accel/ivpu/ivpu_hw.c10
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs.c12
-rw-r--r--drivers/accel/ivpu/ivpu_job.c148
-rw-r--r--drivers/accel/ivpu/ivpu_job.h2
-rw-r--r--drivers/accel/ivpu/ivpu_jsm_msg.c8
-rw-r--r--drivers/accel/ivpu/ivpu_mmu.c101
-rw-r--r--drivers/accel/ivpu/ivpu_mmu.h4
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.c158
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.h9
-rw-r--r--drivers/accel/ivpu/ivpu_pm.c2
-rw-r--r--drivers/accel/qaic/qaic_drv.c4
-rw-r--r--drivers/accel/qaic/sahara.c388
-rw-r--r--drivers/gpu/drm/Kconfig41
-rw-r--r--drivers/gpu/drm/Makefile20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c22
-rw-r--r--drivers/gpu/drm/bridge/aux-bridge.c4
-rw-r--r--drivers/gpu/drm/bridge/aux-hpd-bridge.c4
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c4
-rw-r--r--drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c1
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c2
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c24
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Kconfig8
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Makefile2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c647
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h834
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c3
-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/ti-dlpc3433.c2
-rw-r--r--drivers/gpu/drm/ci/arm64.config7
-rw-r--r--drivers/gpu/drm/ci/build.sh1
-rw-r--r--drivers/gpu/drm/ci/test.yml25
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt51
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt13
-rw-r--r--drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt20
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt15
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt6
-rw-r--r--drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt211
-rw-r--r--drivers/gpu/drm/display/Kconfig2
-rw-r--r--drivers/gpu/drm/display/drm_bridge_connector.c6
-rw-r--r--drivers/gpu/drm/drm_atomic.c2
-rw-r--r--drivers/gpu/drm/drm_client.c121
-rw-r--r--drivers/gpu/drm/drm_client_event.c197
-rw-r--r--drivers/gpu/drm/drm_client_setup.c3
-rw-r--r--drivers/gpu/drm/drm_debugfs.c1
-rw-r--r--drivers/gpu/drm/drm_drv.c2
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c2
-rw-r--r--drivers/gpu/drm/drm_fbdev_client.c30
-rw-r--r--drivers/gpu/drm/drm_file.c2
-rw-r--r--drivers/gpu/drm/drm_internal.h8
-rw-r--r--drivers/gpu/drm/drm_modeset_helper.c14
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c2
-rw-r--r--drivers/gpu/drm/fsl-dcu/Kconfig1
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c23
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.c2
-rw-r--r--drivers/gpu/drm/i915/Kconfig1
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.c2
-rw-r--r--drivers/gpu/drm/imx/dcss/Kconfig2
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-scaler.c4
-rw-r--r--drivers/gpu/drm/imx/ipuv3/Kconfig1
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig4
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp.c2
-rw-r--r--drivers/gpu/drm/meson/Kconfig2
-rw-r--r--drivers/gpu/drm/meson/meson_drv.c2
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c14
-rw-r--r--drivers/gpu/drm/msm/Kconfig1
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c4
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c10
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.h7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c1
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dispc.c146
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dss.h13
-rw-r--r--drivers/gpu/drm/panel/Kconfig10
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9322.c2
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c345
-rw-r--r--drivers/gpu/drm/panel/panel-newvision-nv3052c.c2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c766
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c28
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c19
-rw-r--r--drivers/gpu/drm/radeon/radeon_fbdev.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h3
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig9
-rw-r--r--drivers/gpu/drm/rockchip/Makefile1
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c424
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h1
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c20
-rw-r--r--drivers/gpu/drm/sprd/sprd_dsi.c2
-rw-r--r--drivers/gpu/drm/tiny/Kconfig21
-rw-r--r--drivers/gpu/drm/tiny/Makefile1
-rw-r--r--drivers/gpu/drm/tiny/sharp-memory.c671
-rw-r--r--drivers/gpu/drm/v3d/v3d_bo.c9
-rw-r--r--drivers/gpu/drm/xe/Kconfig1
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_disp.c3
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp.c843
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_kms.c6
108 files changed, 5778 insertions, 954 deletions
diff --git a/drivers/accel/ivpu/Kconfig b/drivers/accel/ivpu/Kconfig
index e4d418b44626..9e055b5ce03d 100644
--- a/drivers/accel/ivpu/Kconfig
+++ b/drivers/accel/ivpu/Kconfig
@@ -16,3 +16,12 @@ config DRM_ACCEL_IVPU
and Deep Learning applications.
If "M" is selected, the module will be called intel_vpu.
+
+config DRM_ACCEL_IVPU_DEBUG
+ bool "Intel NPU debug mode"
+ depends on DRM_ACCEL_IVPU
+ help
+ Choose this option to enable additional
+ debug features for the Intel NPU driver:
+ - Always print debug messages regardless of dyndbg config,
+ - Enable unsafe module params.
diff --git a/drivers/accel/ivpu/Makefile b/drivers/accel/ivpu/Makefile
index e73937c86d9a..1029e0bab061 100644
--- a/drivers/accel/ivpu/Makefile
+++ b/drivers/accel/ivpu/Makefile
@@ -24,4 +24,6 @@ intel_vpu-$(CONFIG_DEV_COREDUMP) += ivpu_coredump.o
obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o
+subdir-ccflags-$(CONFIG_DRM_ACCEL_IVPU_DEBUG) += -DDEBUG
+
CFLAGS_ivpu_trace_points.o = -I$(src)
diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c
index e7d8967c02f2..ca2bf47ce248 100644
--- a/drivers/accel/ivpu/ivpu_drv.c
+++ b/drivers/accel/ivpu/ivpu_drv.c
@@ -43,8 +43,10 @@ module_param_named(dbg_mask, ivpu_dbg_mask, int, 0644);
MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros.");
int ivpu_test_mode;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
module_param_named_unsafe(test_mode, ivpu_test_mode, int, 0644);
MODULE_PARM_DESC(test_mode, "Test mode mask. See IVPU_TEST_MODE_* macros.");
+#endif
u8 ivpu_pll_min_ratio;
module_param_named(pll_min_ratio, ivpu_pll_min_ratio, byte, 0644);
@@ -86,7 +88,7 @@ static void file_priv_unbind(struct ivpu_device *vdev, struct ivpu_file_priv *fi
ivpu_cmdq_release_all_locked(file_priv);
ivpu_bo_unbind_all_bos_from_context(vdev, &file_priv->ctx);
- ivpu_mmu_user_context_fini(vdev, &file_priv->ctx);
+ ivpu_mmu_context_fini(vdev, &file_priv->ctx);
file_priv->bound = false;
drm_WARN_ON(&vdev->drm, !xa_erase_irq(&vdev->context_xa, file_priv->ctx.id));
}
@@ -104,6 +106,8 @@ static void file_priv_release(struct kref *ref)
pm_runtime_get_sync(vdev->drm.dev);
mutex_lock(&vdev->context_list_lock);
file_priv_unbind(vdev, file_priv);
+ drm_WARN_ON(&vdev->drm, !xa_empty(&file_priv->cmdq_xa));
+ xa_destroy(&file_priv->cmdq_xa);
mutex_unlock(&vdev->context_list_lock);
pm_runtime_put_autosuspend(vdev->drm.dev);
@@ -254,14 +258,14 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
goto err_unlock;
}
- ret = ivpu_mmu_user_context_init(vdev, &file_priv->ctx, ctx_id);
- if (ret)
- goto err_xa_erase;
+ ivpu_mmu_context_init(vdev, &file_priv->ctx, ctx_id);
+
+ file_priv->job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1));
+ file_priv->job_limit.max = file_priv->job_limit.min | IVPU_JOB_ID_JOB_MASK;
- file_priv->default_job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK,
- (file_priv->ctx.id - 1));
- file_priv->default_job_limit.max = file_priv->default_job_limit.min | IVPU_JOB_ID_JOB_MASK;
- file_priv->job_limit = file_priv->default_job_limit;
+ xa_init_flags(&file_priv->cmdq_xa, XA_FLAGS_ALLOC1);
+ file_priv->cmdq_limit.min = IVPU_CMDQ_MIN_ID;
+ file_priv->cmdq_limit.max = IVPU_CMDQ_MAX_ID;
mutex_unlock(&vdev->context_list_lock);
drm_dev_exit(idx);
@@ -273,8 +277,6 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
return 0;
-err_xa_erase:
- xa_erase_irq(&vdev->context_xa, ctx_id);
err_unlock:
mutex_unlock(&vdev->context_list_lock);
mutex_destroy(&file_priv->ms_lock);
@@ -622,9 +624,8 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key);
INIT_LIST_HEAD(&vdev->bo_list);
- vdev->default_db_limit.min = IVPU_MIN_DB;
- vdev->default_db_limit.max = IVPU_MAX_DB;
- vdev->db_limit = vdev->default_db_limit;
+ vdev->db_limit.min = IVPU_MIN_DB;
+ vdev->db_limit.max = IVPU_MAX_DB;
ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock);
if (ret)
@@ -652,9 +653,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
if (ret)
goto err_shutdown;
- ret = ivpu_mmu_global_context_init(vdev);
- if (ret)
- goto err_shutdown;
+ ivpu_mmu_global_context_init(vdev);
ret = ivpu_mmu_init(vdev);
if (ret)
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
index f905021ac174..3fdff3f6cffd 100644
--- a/drivers/accel/ivpu/ivpu_drv.h
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -49,11 +49,11 @@
#define IVPU_JOB_ID_JOB_MASK GENMASK(7, 0)
#define IVPU_JOB_ID_CONTEXT_MASK GENMASK(31, 8)
-#define IVPU_NUM_ENGINES 2
#define IVPU_NUM_PRIORITIES 4
-#define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_ENGINES * IVPU_NUM_PRIORITIES)
+#define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_PRIORITIES)
-#define IVPU_CMDQ_INDEX(engine, priority) ((engine) * IVPU_NUM_PRIORITIES + (priority))
+#define IVPU_CMDQ_MIN_ID 1
+#define IVPU_CMDQ_MAX_ID 255
#define IVPU_PLATFORM_SILICON 0
#define IVPU_PLATFORM_SIMICS 2
@@ -140,7 +140,7 @@ struct ivpu_device {
struct xarray db_xa;
struct xa_limit db_limit;
- struct xa_limit default_db_limit;
+ u32 db_next;
struct mutex bo_list_lock; /* Protects bo_list */
struct list_head bo_list;
@@ -171,13 +171,15 @@ struct ivpu_file_priv {
struct kref ref;
struct ivpu_device *vdev;
struct mutex lock; /* Protects cmdq */
- struct ivpu_cmdq *cmdq[IVPU_NUM_CMDQS_PER_CTX];
+ struct xarray cmdq_xa;
struct ivpu_mmu_context ctx;
struct mutex ms_lock; /* Protects ms_instance_list, ms_info_bo */
struct list_head ms_instance_list;
struct ivpu_bo *ms_info_bo;
struct xa_limit job_limit;
- struct xa_limit default_job_limit;
+ u32 job_id_next;
+ struct xa_limit cmdq_limit;
+ u32 cmdq_id_next;
bool has_mmu_faults;
bool bound;
bool aborted;
@@ -195,7 +197,7 @@ extern bool ivpu_force_snoop;
#define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2)
#define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4)
#define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5)
-#define IVPU_TEST_MODE_PREEMPTION_DISABLE BIT(6)
+#define IVPU_TEST_MODE_MIP_DISABLE BIT(6)
#define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8)
#define IVPU_TEST_MODE_TURBO BIT(9)
extern int ivpu_test_mode;
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
index be367465e7df..6037ec0b3096 100644
--- a/drivers/accel/ivpu/ivpu_fw.c
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -46,8 +46,10 @@
#define IVPU_FOCUS_PRESENT_TIMER_MS 1000
static char *ivpu_firmware;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644);
MODULE_PARM_DESC(firmware, "NPU firmware binary in /lib/firmware/..");
+#endif
static struct {
int gen;
@@ -582,8 +584,10 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
boot_params->ipc_payload_area_start = ipc_mem_rx->vpu_addr + ivpu_bo_size(ipc_mem_rx) / 2;
boot_params->ipc_payload_area_size = ivpu_bo_size(ipc_mem_rx) / 2;
- boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start;
- boot_params->global_aliased_pio_size = ivpu_hw_range_size(&vdev->hw->ranges.user);
+ if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
+ boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start;
+ boot_params->global_aliased_pio_size = ivpu_hw_range_size(&vdev->hw->ranges.user);
+ }
/* Allow configuration for L2C_PAGE_TABLE with boot param value */
boot_params->autoconfig = 1;
diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c
index 1c259d717815..09ada8b500b9 100644
--- a/drivers/accel/ivpu/ivpu_hw.c
+++ b/drivers/accel/ivpu/ivpu_hw.c
@@ -114,14 +114,14 @@ static void memory_ranges_init(struct ivpu_device *vdev)
{
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
- ivpu_hw_range_init(&vdev->hw->ranges.user, 0xc0000000, 255 * SZ_1M);
+ ivpu_hw_range_init(&vdev->hw->ranges.user, 0x88000000, 511 * SZ_1M);
ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x180000000, SZ_2G);
- ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_8G);
+ ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_128G);
} else {
ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
- ivpu_hw_range_init(&vdev->hw->ranges.user, 0x80000000, SZ_256M);
- ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000 + SZ_256M, SZ_2G - SZ_256M);
- ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_8G);
+ ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000, SZ_2G);
+ ivpu_hw_range_init(&vdev->hw->ranges.user, 0x100000000, SZ_256G);
+ vdev->hw->ranges.dma = vdev->hw->ranges.user;
}
}
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c
index 6d5f1cc71143..3212c99f3682 100644
--- a/drivers/accel/ivpu/ivpu_hw_btrs.c
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.c
@@ -141,16 +141,10 @@ static int read_tile_config_fuse(struct ivpu_device *vdev, u32 *tile_fuse_config
}
config = REG_GET_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, CONFIG, fuse);
- if (!tile_disable_check(config)) {
- ivpu_err(vdev, "Fuse: Invalid tile disable config (0x%x)\n", config);
- return -EIO;
- }
+ if (!tile_disable_check(config))
+ ivpu_warn(vdev, "More than 1 tile disabled, tile fuse config mask: 0x%x\n", config);
- if (config)
- ivpu_dbg(vdev, MISC, "Fuse: %d tiles enabled. Tile number %d disabled\n",
- BTRS_LNL_TILE_MAX_NUM - 1, ffs(config) - 1);
- else
- ivpu_dbg(vdev, MISC, "Fuse: All %d tiles enabled\n", BTRS_LNL_TILE_MAX_NUM);
+ ivpu_dbg(vdev, MISC, "Tile disable config mask: 0x%x\n", config);
*tile_fuse_config = config;
return 0;
diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c
index 98e0b7b61407..7149312f16e1 100644
--- a/drivers/accel/ivpu/ivpu_job.c
+++ b/drivers/accel/ivpu/ivpu_job.c
@@ -35,7 +35,8 @@ static int ivpu_preemption_buffers_create(struct ivpu_device *vdev,
u64 primary_size = ALIGN(vdev->fw->primary_preempt_buf_size, PAGE_SIZE);
u64 secondary_size = ALIGN(vdev->fw->secondary_preempt_buf_size, PAGE_SIZE);
- if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW ||
+ ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
return 0;
cmdq->primary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.user,
@@ -45,7 +46,7 @@ static int ivpu_preemption_buffers_create(struct ivpu_device *vdev,
return -ENOMEM;
}
- cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.shave,
+ cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.dma,
secondary_size, DRM_IVPU_BO_WC);
if (!cmdq->secondary_preempt_buf) {
ivpu_err(vdev, "Failed to create secondary preemption buffer\n");
@@ -72,26 +73,6 @@ static void ivpu_preemption_buffers_free(struct ivpu_device *vdev,
ivpu_bo_free(cmdq->secondary_preempt_buf);
}
-static int ivpu_id_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit *limit,
- const struct xa_limit default_limit)
-{
- int ret;
-
- ret = __xa_alloc(xa, id, entry, *limit, GFP_KERNEL);
- if (ret) {
- limit->min = default_limit.min;
- ret = __xa_alloc(xa, id, entry, *limit, GFP_KERNEL);
- if (ret)
- return ret;
- }
-
- limit->min = *id + 1;
- if (limit->min > limit->max)
- limit->min = default_limit.min;
-
- return ret;
-}
-
static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
{
struct ivpu_device *vdev = file_priv->vdev;
@@ -102,18 +83,23 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
if (!cmdq)
return NULL;
- xa_lock(&vdev->db_xa); /* lock here to protect db_limit */
- ret = ivpu_id_alloc(&vdev->db_xa, &cmdq->db_id, NULL, &vdev->db_limit,
- vdev->default_db_limit);
- xa_unlock(&vdev->db_xa);
- if (ret) {
+ ret = xa_alloc_cyclic(&vdev->db_xa, &cmdq->db_id, NULL, vdev->db_limit, &vdev->db_next,
+ GFP_KERNEL);
+ if (ret < 0) {
ivpu_err(vdev, "Failed to allocate doorbell id: %d\n", ret);
goto err_free_cmdq;
}
+ ret = xa_alloc_cyclic(&file_priv->cmdq_xa, &cmdq->id, cmdq, file_priv->cmdq_limit,
+ &file_priv->cmdq_id_next, GFP_KERNEL);
+ if (ret < 0) {
+ ivpu_err(vdev, "Failed to allocate command queue id: %d\n", ret);
+ goto err_erase_db_xa;
+ }
+
cmdq->mem = ivpu_bo_create_global(vdev, SZ_4K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!cmdq->mem)
- goto err_erase_xa;
+ goto err_erase_cmdq_xa;
ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq);
if (ret)
@@ -121,7 +107,9 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
return cmdq;
-err_erase_xa:
+err_erase_cmdq_xa:
+ xa_erase(&file_priv->cmdq_xa, cmdq->id);
+err_erase_db_xa:
xa_erase(&vdev->db_xa, cmdq->db_id);
err_free_cmdq:
kfree(cmdq);
@@ -145,13 +133,13 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq
struct ivpu_device *vdev = file_priv->vdev;
int ret;
- ret = ivpu_jsm_hws_create_cmdq(vdev, file_priv->ctx.id, file_priv->ctx.id, cmdq->db_id,
+ ret = ivpu_jsm_hws_create_cmdq(vdev, file_priv->ctx.id, file_priv->ctx.id, cmdq->id,
task_pid_nr(current), engine,
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
if (ret)
return ret;
- ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->db_id,
+ ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id,
priority);
if (ret)
return ret;
@@ -165,20 +153,21 @@ static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *
int ret;
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
- ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->db_id, cmdq->db_id,
+ ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->id, cmdq->db_id,
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
else
ret = ivpu_jsm_register_db(vdev, file_priv->ctx.id, cmdq->db_id,
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
if (!ret)
- ivpu_dbg(vdev, JOB, "DB %d registered to ctx %d\n", cmdq->db_id, file_priv->ctx.id);
+ ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d\n",
+ cmdq->db_id, cmdq->id, file_priv->ctx.id);
return ret;
}
static int
-ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 engine, u8 priority)
+ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u8 priority)
{
struct ivpu_device *vdev = file_priv->vdev;
struct vpu_job_queue_header *jobq_header;
@@ -194,7 +183,7 @@ ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 eng
cmdq->jobq = (struct vpu_job_queue *)ivpu_bo_vaddr(cmdq->mem);
jobq_header = &cmdq->jobq->header;
- jobq_header->engine_idx = engine;
+ jobq_header->engine_idx = VPU_ENGINE_COMPUTE;
jobq_header->head = 0;
jobq_header->tail = 0;
if (ivpu_test_mode & IVPU_TEST_MODE_TURBO) {
@@ -205,7 +194,7 @@ ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 eng
wmb(); /* Flush WC buffer for jobq->header */
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
- ret = ivpu_hws_cmdq_init(file_priv, cmdq, engine, priority);
+ ret = ivpu_hws_cmdq_init(file_priv, cmdq, VPU_ENGINE_COMPUTE, priority);
if (ret)
return ret;
}
@@ -232,9 +221,9 @@ static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cm
cmdq->db_registered = false;
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
- ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->db_id);
+ ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id);
if (!ret)
- ivpu_dbg(vdev, JOB, "Command queue %d destroyed\n", cmdq->db_id);
+ ivpu_dbg(vdev, JOB, "Command queue %d destroyed\n", cmdq->id);
}
ret = ivpu_jsm_unregister_db(vdev, cmdq->db_id);
@@ -244,55 +233,46 @@ static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cm
return 0;
}
-static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 engine,
- u8 priority)
+static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u8 priority)
{
- int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority);
- struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx];
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
int ret;
lockdep_assert_held(&file_priv->lock);
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
+ if (cmdq->priority == priority)
+ break;
+
if (!cmdq) {
cmdq = ivpu_cmdq_alloc(file_priv);
if (!cmdq)
return NULL;
- file_priv->cmdq[cmdq_idx] = cmdq;
+ cmdq->priority = priority;
}
- ret = ivpu_cmdq_init(file_priv, cmdq, engine, priority);
+ ret = ivpu_cmdq_init(file_priv, cmdq, priority);
if (ret)
return NULL;
return cmdq;
}
-static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u16 engine, u8 priority)
+void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv)
{
- int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority);
- struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx];
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
lockdep_assert_held(&file_priv->lock);
- if (cmdq) {
- file_priv->cmdq[cmdq_idx] = NULL;
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) {
+ xa_erase(&file_priv->cmdq_xa, cmdq_id);
ivpu_cmdq_fini(file_priv, cmdq);
ivpu_cmdq_free(file_priv, cmdq);
}
}
-void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv)
-{
- u16 engine;
- u8 priority;
-
- lockdep_assert_held(&file_priv->lock);
-
- for (engine = 0; engine < IVPU_NUM_ENGINES; engine++)
- for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++)
- ivpu_cmdq_release_locked(file_priv, engine, priority);
-}
-
/*
* Mark the doorbell as unregistered
* This function needs to be called when the VPU hardware is restarted
@@ -301,20 +281,13 @@ void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv)
*/
static void ivpu_cmdq_reset(struct ivpu_file_priv *file_priv)
{
- u16 engine;
- u8 priority;
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
mutex_lock(&file_priv->lock);
- for (engine = 0; engine < IVPU_NUM_ENGINES; engine++) {
- for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) {
- int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority);
- struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx];
-
- if (cmdq)
- cmdq->db_registered = false;
- }
- }
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
+ cmdq->db_registered = false;
mutex_unlock(&file_priv->lock);
}
@@ -334,17 +307,11 @@ void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev)
static void ivpu_cmdq_fini_all(struct ivpu_file_priv *file_priv)
{
- u16 engine;
- u8 priority;
-
- for (engine = 0; engine < IVPU_NUM_ENGINES; engine++) {
- for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) {
- int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority);
+ struct ivpu_cmdq *cmdq;
+ unsigned long cmdq_id;
- if (file_priv->cmdq[cmdq_idx])
- ivpu_cmdq_fini(file_priv, file_priv->cmdq[cmdq_idx]);
- }
- }
+ xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
+ ivpu_cmdq_fini(file_priv, cmdq);
}
void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv)
@@ -369,8 +336,8 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
/* Check if there is space left in job queue */
if (next_entry == header->head) {
- ivpu_dbg(vdev, JOB, "Job queue full: ctx %d engine %d db %d head %d tail %d\n",
- job->file_priv->ctx.id, job->engine_idx, cmdq->db_id, header->head, tail);
+ ivpu_dbg(vdev, JOB, "Job queue full: ctx %d cmdq %d db %d head %d tail %d\n",
+ job->file_priv->ctx.id, cmdq->id, cmdq->db_id, header->head, tail);
return -EBUSY;
}
@@ -381,8 +348,7 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION))
entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK;
- if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW &&
- (unlikely(!(ivpu_test_mode & IVPU_TEST_MODE_PREEMPTION_DISABLE)))) {
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
if (cmdq->primary_preempt_buf) {
entry->primary_preempt_buf_addr = cmdq->primary_preempt_buf->vpu_addr;
entry->primary_preempt_buf_size = ivpu_bo_size(cmdq->primary_preempt_buf);
@@ -557,7 +523,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
mutex_lock(&file_priv->lock);
- cmdq = ivpu_cmdq_acquire(file_priv, job->engine_idx, priority);
+ cmdq = ivpu_cmdq_acquire(file_priv, priority);
if (!cmdq) {
ivpu_warn_ratelimited(vdev, "Failed to get job queue, ctx %d engine %d prio %d\n",
file_priv->ctx.id, job->engine_idx, priority);
@@ -567,9 +533,9 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
xa_lock(&vdev->submitted_jobs_xa);
is_first_job = xa_empty(&vdev->submitted_jobs_xa);
- ret = ivpu_id_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, &file_priv->job_limit,
- file_priv->default_job_limit);
- if (ret) {
+ ret = __xa_alloc_cyclic(&vdev->submitted_jobs_xa, &job->job_id, job, file_priv->job_limit,
+ &file_priv->job_id_next, GFP_KERNEL);
+ if (ret < 0) {
ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n",
file_priv->ctx.id);
ret = -EBUSY;
@@ -699,7 +665,7 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
int idx, ret;
u8 priority;
- if (params->engine > DRM_IVPU_ENGINE_COPY)
+ if (params->engine != DRM_IVPU_ENGINE_COMPUTE)
return -EINVAL;
if (params->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h
index 6accb94028c7..8b19e3f8b4cf 100644
--- a/drivers/accel/ivpu/ivpu_job.h
+++ b/drivers/accel/ivpu/ivpu_job.h
@@ -28,8 +28,10 @@ struct ivpu_cmdq {
struct ivpu_bo *secondary_preempt_buf;
struct ivpu_bo *mem;
u32 entry_count;
+ u32 id;
u32 db_id;
bool db_registered;
+ u8 priority;
};
/**
diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c
index 8f5d904ca1ed..30a40be76930 100644
--- a/drivers/accel/ivpu/ivpu_jsm_msg.c
+++ b/drivers/accel/ivpu/ivpu_jsm_msg.c
@@ -132,7 +132,7 @@ int ivpu_jsm_get_heartbeat(struct ivpu_device *vdev, u32 engine, u64 *heartbeat)
struct vpu_jsm_msg resp;
int ret;
- if (engine > VPU_ENGINE_COPY)
+ if (engine != VPU_ENGINE_COMPUTE)
return -EINVAL;
req.payload.query_engine_hb.engine_idx = engine;
@@ -155,7 +155,7 @@ int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine)
struct vpu_jsm_msg resp;
int ret;
- if (engine > VPU_ENGINE_COPY)
+ if (engine != VPU_ENGINE_COMPUTE)
return -EINVAL;
req.payload.engine_reset.engine_idx = engine;
@@ -174,7 +174,7 @@ int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id
struct vpu_jsm_msg resp;
int ret;
- if (engine > VPU_ENGINE_COPY)
+ if (engine != VPU_ENGINE_COMPUTE)
return -EINVAL;
req.payload.engine_preempt.engine_idx = engine;
@@ -346,7 +346,7 @@ int ivpu_jsm_hws_resume_engine(struct ivpu_device *vdev, u32 engine)
struct vpu_jsm_msg resp;
int ret;
- if (engine >= VPU_ENGINE_NB)
+ if (engine != VPU_ENGINE_COMPUTE)
return -EINVAL;
req.payload.hws_resume_engine.engine_idx = engine;
diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c
index c078e214b221..26ef52fbb93e 100644
--- a/drivers/accel/ivpu/ivpu_mmu.c
+++ b/drivers/accel/ivpu/ivpu_mmu.c
@@ -696,7 +696,7 @@ unlock:
return ret;
}
-static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma)
+static int ivpu_mmu_cdtab_entry_set(struct ivpu_device *vdev, u32 ssid, u64 cd_dma, bool valid)
{
struct ivpu_mmu_info *mmu = vdev->mmu;
struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
@@ -708,30 +708,29 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma)
return -EINVAL;
entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE);
-
- if (cd_dma != 0) {
- cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) |
- FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) |
- FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) |
- FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) |
- FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) |
- FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) |
- FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) |
- IVPU_MMU_CD_0_TCR_EPD1 |
- IVPU_MMU_CD_0_AA64 |
- IVPU_MMU_CD_0_R |
- IVPU_MMU_CD_0_ASET |
- IVPU_MMU_CD_0_V;
- cd[1] = cd_dma & IVPU_MMU_CD_1_TTB0_MASK;
- cd[2] = 0;
- cd[3] = 0x0000000000007444;
-
- /* For global context generate memory fault on VPU */
- if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID)
- cd[0] |= IVPU_MMU_CD_0_A;
- } else {
- memset(cd, 0, sizeof(cd));
- }
+ drm_WARN_ON(&vdev->drm, (entry[0] & IVPU_MMU_CD_0_V) == valid);
+
+ cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) |
+ FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) |
+ FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) |
+ IVPU_MMU_CD_0_TCR_EPD1 |
+ IVPU_MMU_CD_0_AA64 |
+ IVPU_MMU_CD_0_R |
+ IVPU_MMU_CD_0_ASET;
+ cd[1] = cd_dma & IVPU_MMU_CD_1_TTB0_MASK;
+ cd[2] = 0;
+ cd[3] = 0x0000000000007444;
+
+ /* For global context generate memory fault on VPU */
+ if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID)
+ cd[0] |= IVPU_MMU_CD_0_A;
+
+ if (valid)
+ cd[0] |= IVPU_MMU_CD_0_V;
WRITE_ONCE(entry[1], cd[1]);
WRITE_ONCE(entry[2], cd[2]);
@@ -741,8 +740,8 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma)
if (!ivpu_is_force_snoop_enabled(vdev))
clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
- ivpu_dbg(vdev, MMU, "CDTAB %s entry (SSID=%u, dma=%pad): 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
- cd_dma ? "write" : "clear", ssid, &cd_dma, cd[0], cd[1], cd[2], cd[3]);
+ ivpu_dbg(vdev, MMU, "CDTAB set %s entry (SSID=%u, dma=%pad): 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
+ valid ? "valid" : "invalid", ssid, &cd_dma, cd[0], cd[1], cd[2], cd[3]);
mutex_lock(&mmu->lock);
if (!mmu->on)
@@ -750,38 +749,18 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma)
ret = ivpu_mmu_cmdq_write_cfgi_all(vdev);
if (ret)
- goto unlock;
+ goto err_invalidate;
ret = ivpu_mmu_cmdq_sync(vdev);
+ if (ret)
+ goto err_invalidate;
unlock:
mutex_unlock(&mmu->lock);
- return ret;
-}
-
-static int ivpu_mmu_cd_add_gbl(struct ivpu_device *vdev)
-{
- int ret;
-
- ret = ivpu_mmu_cd_add(vdev, 0, vdev->gctx.pgtable.pgd_dma);
- if (ret)
- ivpu_err(vdev, "Failed to add global CD entry: %d\n", ret);
-
- return ret;
-}
-
-static int ivpu_mmu_cd_add_user(struct ivpu_device *vdev, u32 ssid, dma_addr_t cd_dma)
-{
- int ret;
-
- if (ssid == 0) {
- ivpu_err(vdev, "Invalid SSID: %u\n", ssid);
- return -EINVAL;
- }
-
- ret = ivpu_mmu_cd_add(vdev, ssid, cd_dma);
- if (ret)
- ivpu_err(vdev, "Failed to add CD entry SSID=%u: %d\n", ssid, ret);
+ return 0;
+err_invalidate:
+ WRITE_ONCE(entry[0], 0);
+ mutex_unlock(&mmu->lock);
return ret;
}
@@ -808,12 +787,6 @@ int ivpu_mmu_init(struct ivpu_device *vdev)
return ret;
}
- ret = ivpu_mmu_cd_add_gbl(vdev);
- if (ret) {
- ivpu_err(vdev, "Failed to initialize strtab: %d\n", ret);
- return ret;
- }
-
ret = ivpu_mmu_enable(vdev);
if (ret) {
ivpu_err(vdev, "Failed to resume MMU: %d\n", ret);
@@ -966,12 +939,12 @@ void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev)
REGV_WR32(IVPU_MMU_REG_GERRORN, gerror_val);
}
-int ivpu_mmu_set_pgtable(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable)
+int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable)
{
- return ivpu_mmu_cd_add_user(vdev, ssid, pgtable->pgd_dma);
+ return ivpu_mmu_cdtab_entry_set(vdev, ssid, pgtable->pgd_dma, true);
}
-void ivpu_mmu_clear_pgtable(struct ivpu_device *vdev, int ssid)
+void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid)
{
- ivpu_mmu_cd_add_user(vdev, ssid, 0); /* 0 will clear CD entry */
+ ivpu_mmu_cdtab_entry_set(vdev, ssid, 0, false);
}
diff --git a/drivers/accel/ivpu/ivpu_mmu.h b/drivers/accel/ivpu/ivpu_mmu.h
index 6fa35c240710..7afea9cd8731 100644
--- a/drivers/accel/ivpu/ivpu_mmu.h
+++ b/drivers/accel/ivpu/ivpu_mmu.h
@@ -40,8 +40,8 @@ struct ivpu_mmu_info {
int ivpu_mmu_init(struct ivpu_device *vdev);
void ivpu_mmu_disable(struct ivpu_device *vdev);
int ivpu_mmu_enable(struct ivpu_device *vdev);
-int ivpu_mmu_set_pgtable(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable);
-void ivpu_mmu_clear_pgtable(struct ivpu_device *vdev, int ssid);
+int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable);
+void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid);
int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid);
void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev);
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c
index bbe652a7019d..891967a95bc3 100644
--- a/drivers/accel/ivpu/ivpu_mmu_context.c
+++ b/drivers/accel/ivpu/ivpu_mmu_context.c
@@ -90,19 +90,6 @@ static void ivpu_pgtable_free_page(struct ivpu_device *vdev, u64 *cpu_addr, dma_
}
}
-static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
-{
- dma_addr_t pgd_dma;
-
- pgtable->pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma);
- if (!pgtable->pgd_dma_ptr)
- return -ENOMEM;
-
- pgtable->pgd_dma = pgd_dma;
-
- return 0;
-}
-
static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
{
int pgd_idx, pud_idx, pmd_idx;
@@ -140,6 +127,27 @@ static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgt
}
ivpu_pgtable_free_page(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma);
+ pgtable->pgd_dma_ptr = NULL;
+ pgtable->pgd_dma = 0;
+}
+
+static u64*
+ivpu_mmu_ensure_pgd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable)
+{
+ u64 *pgd_dma_ptr = pgtable->pgd_dma_ptr;
+ dma_addr_t pgd_dma;
+
+ if (pgd_dma_ptr)
+ return pgd_dma_ptr;
+
+ pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma);
+ if (!pgd_dma_ptr)
+ return NULL;
+
+ pgtable->pgd_dma_ptr = pgd_dma_ptr;
+ pgtable->pgd_dma = pgd_dma;
+
+ return pgd_dma_ptr;
}
static u64*
@@ -237,6 +245,12 @@ ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx
int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr);
int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr);
+ drm_WARN_ON(&vdev->drm, ctx->id == IVPU_RESERVED_CONTEXT_MMU_SSID);
+
+ /* Allocate PGD - first level page table if needed */
+ if (!ivpu_mmu_ensure_pgd(vdev, &ctx->pgtable))
+ return -ENOMEM;
+
/* Allocate PUD - second level page table if needed */
if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx))
return -ENOMEM;
@@ -418,6 +432,7 @@ int
ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
u64 vpu_addr, struct sg_table *sgt, bool llc_coherent)
{
+ size_t start_vpu_addr = vpu_addr;
struct scatterlist *sg;
int ret;
u64 prot;
@@ -448,20 +463,36 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot);
if (ret) {
ivpu_err(vdev, "Failed to map context pages\n");
- mutex_unlock(&ctx->lock);
- return ret;
+ goto err_unmap_pages;
}
vpu_addr += size;
}
+ if (!ctx->is_cd_valid) {
+ ret = ivpu_mmu_cd_set(vdev, ctx->id, &ctx->pgtable);
+ if (ret) {
+ ivpu_err(vdev, "Failed to set context descriptor for context %u: %d\n",
+ ctx->id, ret);
+ goto err_unmap_pages;
+ }
+ ctx->is_cd_valid = true;
+ }
+
/* Ensure page table modifications are flushed from wc buffers to memory */
wmb();
- mutex_unlock(&ctx->lock);
-
ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
- if (ret)
+ if (ret) {
ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
+ goto err_unmap_pages;
+ }
+
+ mutex_unlock(&ctx->lock);
+ return 0;
+
+err_unmap_pages:
+ ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, vpu_addr - start_vpu_addr);
+ mutex_unlock(&ctx->lock);
return ret;
}
@@ -530,65 +561,75 @@ ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *n
mutex_unlock(&ctx->lock);
}
-static int
-ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id)
+void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id)
{
u64 start, end;
- int ret;
mutex_init(&ctx->lock);
- ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable);
- if (ret) {
- ivpu_err(vdev, "Failed to initialize pgtable for ctx %u: %d\n", context_id, ret);
- return ret;
- }
-
if (!context_id) {
start = vdev->hw->ranges.global.start;
end = vdev->hw->ranges.shave.end;
} else {
- start = vdev->hw->ranges.user.start;
- end = vdev->hw->ranges.dma.end;
+ start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
+ end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
}
drm_mm_init(&ctx->mm, start, end - start);
ctx->id = context_id;
-
- return 0;
}
-static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
+void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
{
- if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr))
- return;
+ if (ctx->is_cd_valid) {
+ ivpu_mmu_cd_clear(vdev, ctx->id);
+ ctx->is_cd_valid = false;
+ }
mutex_destroy(&ctx->lock);
ivpu_mmu_pgtables_free(vdev, &ctx->pgtable);
drm_mm_takedown(&ctx->mm);
-
- ctx->pgtable.pgd_dma_ptr = NULL;
- ctx->pgtable.pgd_dma = 0;
}
-int ivpu_mmu_global_context_init(struct ivpu_device *vdev)
+void ivpu_mmu_global_context_init(struct ivpu_device *vdev)
{
- return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID);
+ ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID);
}
void ivpu_mmu_global_context_fini(struct ivpu_device *vdev)
{
- return ivpu_mmu_context_fini(vdev, &vdev->gctx);
+ ivpu_mmu_context_fini(vdev, &vdev->gctx);
}
int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev)
{
- return ivpu_mmu_user_context_init(vdev, &vdev->rctx, IVPU_RESERVED_CONTEXT_MMU_SSID);
+ int ret;
+
+ ivpu_mmu_context_init(vdev, &vdev->rctx, IVPU_RESERVED_CONTEXT_MMU_SSID);
+
+ mutex_lock(&vdev->rctx.lock);
+
+ if (!ivpu_mmu_ensure_pgd(vdev, &vdev->rctx.pgtable)) {
+ ivpu_err(vdev, "Failed to allocate root page table for reserved context\n");
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = ivpu_mmu_cd_set(vdev, vdev->rctx.id, &vdev->rctx.pgtable);
+ if (ret) {
+ ivpu_err(vdev, "Failed to set context descriptor for reserved context\n");
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&vdev->rctx.lock);
+ return ret;
}
void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev)
{
- return ivpu_mmu_user_context_fini(vdev, &vdev->rctx);
+ ivpu_mmu_cd_clear(vdev, vdev->rctx.id);
+ ivpu_mmu_context_fini(vdev, &vdev->rctx);
}
void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
@@ -603,36 +644,3 @@ void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
xa_unlock(&vdev->context_xa);
}
-
-int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id)
-{
- int ret;
-
- drm_WARN_ON(&vdev->drm, !ctx_id);
-
- ret = ivpu_mmu_context_init(vdev, ctx, ctx_id);
- if (ret) {
- ivpu_err(vdev, "Failed to initialize context %u: %d\n", ctx_id, ret);
- return ret;
- }
-
- ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable);
- if (ret) {
- ivpu_err(vdev, "Failed to set page table for context %u: %d\n", ctx_id, ret);
- goto err_context_fini;
- }
-
- return 0;
-
-err_context_fini:
- ivpu_mmu_context_fini(vdev, ctx);
- return ret;
-}
-
-void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx)
-{
- drm_WARN_ON(&vdev->drm, !ctx->id);
-
- ivpu_mmu_clear_pgtable(vdev, ctx->id);
- ivpu_mmu_context_fini(vdev, ctx);
-}
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h
index 7f9aaf3d10c2..8042fc067062 100644
--- a/drivers/accel/ivpu/ivpu_mmu_context.h
+++ b/drivers/accel/ivpu/ivpu_mmu_context.h
@@ -23,19 +23,20 @@ struct ivpu_mmu_pgtable {
};
struct ivpu_mmu_context {
- struct mutex lock; /* Protects: mm, pgtable */
+ struct mutex lock; /* Protects: mm, pgtable, is_cd_valid */
struct drm_mm mm;
struct ivpu_mmu_pgtable pgtable;
+ bool is_cd_valid;
u32 id;
};
-int ivpu_mmu_global_context_init(struct ivpu_device *vdev);
+void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id);
+void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
+void ivpu_mmu_global_context_init(struct ivpu_device *vdev);
void ivpu_mmu_global_context_fini(struct ivpu_device *vdev);
int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev);
void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev);
-int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id);
-void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid);
int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range,
diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c
index e567df79a612..dbc0711e28d1 100644
--- a/drivers/accel/ivpu/ivpu_pm.c
+++ b/drivers/accel/ivpu/ivpu_pm.c
@@ -24,8 +24,10 @@
#include "vpu_boot_api.h"
static bool ivpu_disable_recovery;
+#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG)
module_param_named_unsafe(disable_recovery, ivpu_disable_recovery, bool, 0644);
MODULE_PARM_DESC(disable_recovery, "Disables recovery when NPU hang is detected");
+#endif
static unsigned long ivpu_tdr_timeout_ms;
module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644);
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index f139c564eadf..3575e0c984d6 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -54,12 +54,12 @@ static void qaicm_wq_release(struct drm_device *dev, void *res)
destroy_workqueue(wq);
}
-static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *fmt)
+static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *name)
{
struct workqueue_struct *wq;
int ret;
- wq = alloc_workqueue(fmt, WQ_UNBOUND, 0);
+ wq = alloc_workqueue("%s", WQ_UNBOUND, 0, name);
if (!wq)
return ERR_PTR(-ENOMEM);
ret = drmm_add_action_or_reset(dev, qaicm_wq_release, wq);
diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c
index bf94bbab6be5..6d772143d612 100644
--- a/drivers/accel/qaic/sahara.c
+++ b/drivers/accel/qaic/sahara.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+#include <linux/devcoredump.h>
#include <linux/firmware.h>
#include <linux/limits.h>
#include <linux/mhi.h>
@@ -9,6 +10,7 @@
#include <linux/mod_devicetable.h>
#include <linux/overflow.h>
#include <linux/types.h>
+#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include "sahara.h"
@@ -36,12 +38,14 @@
#define SAHARA_PACKET_MAX_SIZE 0xffffU /* MHI_MAX_MTU */
#define SAHARA_TRANSFER_MAX_SIZE 0x80000
+#define SAHARA_READ_MAX_SIZE 0xfff0U /* Avoid unaligned requests */
#define SAHARA_NUM_TX_BUF DIV_ROUND_UP(SAHARA_TRANSFER_MAX_SIZE,\
SAHARA_PACKET_MAX_SIZE)
#define SAHARA_IMAGE_ID_NONE U32_MAX
#define SAHARA_VERSION 2
#define SAHARA_SUCCESS 0
+#define SAHARA_TABLE_ENTRY_STR_LEN 20
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
@@ -53,6 +57,8 @@
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
#define SAHARA_DONE_LENGTH 0x8
#define SAHARA_RESET_LENGTH 0x8
+#define SAHARA_MEM_DEBUG64_LENGTH 0x18
+#define SAHARA_MEM_READ64_LENGTH 0x18
struct sahara_packet {
__le32 cmd;
@@ -80,18 +86,95 @@ struct sahara_packet {
__le32 image;
__le32 status;
} end_of_image;
+ struct {
+ __le64 table_address;
+ __le64 table_length;
+ } memory_debug64;
+ struct {
+ __le64 memory_address;
+ __le64 memory_length;
+ } memory_read64;
};
};
+struct sahara_debug_table_entry64 {
+ __le64 type;
+ __le64 address;
+ __le64 length;
+ char description[SAHARA_TABLE_ENTRY_STR_LEN];
+ char filename[SAHARA_TABLE_ENTRY_STR_LEN];
+};
+
+struct sahara_dump_table_entry {
+ u64 type;
+ u64 address;
+ u64 length;
+ char description[SAHARA_TABLE_ENTRY_STR_LEN];
+ char filename[SAHARA_TABLE_ENTRY_STR_LEN];
+};
+
+#define SAHARA_DUMP_V1_MAGIC 0x1234567890abcdef
+#define SAHARA_DUMP_V1_VER 1
+struct sahara_memory_dump_meta_v1 {
+ u64 magic;
+ u64 version;
+ u64 dump_size;
+ u64 table_size;
+};
+
+/*
+ * Layout of crashdump provided to user via devcoredump
+ * +------------------------------------------+
+ * | Crashdump Meta structure |
+ * | type: struct sahara_memory_dump_meta_v1 |
+ * +------------------------------------------+
+ * | Crashdump Table |
+ * | type: array of struct |
+ * | sahara_dump_table_entry |
+ * | |
+ * | |
+ * +------------------------------------------+
+ * | Crashdump |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ *
+ * First is the metadata header. Userspace can use the magic number to verify
+ * the content type, and then check the version for the rest of the format.
+ * New versions should keep the magic number location/value, and version
+ * location, but increment the version value.
+ *
+ * For v1, the metadata lists the size of the entire dump (header + table +
+ * dump) and the size of the table. Then the dump image table, which describes
+ * the contents of the dump. Finally all the images are listed in order, with
+ * no deadspace in between. Userspace can use the sizes listed in the image
+ * table to reconstruct the individual images.
+ */
+
struct sahara_context {
struct sahara_packet *tx[SAHARA_NUM_TX_BUF];
struct sahara_packet *rx;
- struct work_struct work;
+ struct work_struct fw_work;
+ struct work_struct dump_work;
struct mhi_device *mhi_dev;
const char **image_table;
u32 table_size;
u32 active_image_id;
const struct firmware *firmware;
+ u64 dump_table_address;
+ u64 dump_table_length;
+ size_t rx_size;
+ size_t rx_size_requested;
+ void *mem_dump;
+ size_t mem_dump_sz;
+ struct sahara_dump_table_entry *dump_image;
+ u64 dump_image_offset;
+ void *mem_dump_freespace;
+ u64 dump_images_left;
+ bool is_mem_dump_mode;
};
static const char *aic100_image_table[] = {
@@ -153,6 +236,8 @@ static void sahara_send_reset(struct sahara_context *context)
{
int ret;
+ context->is_mem_dump_mode = false;
+
context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
@@ -186,7 +271,8 @@ static void sahara_hello(struct sahara_context *context)
}
if (le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_PENDING &&
- le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE) {
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE &&
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_MEMORY_DEBUG) {
dev_err(&context->mhi_dev->dev, "Unsupported hello packet - mode %d\n",
le32_to_cpu(context->rx->hello.mode));
return;
@@ -320,9 +406,70 @@ static void sahara_end_of_image(struct sahara_context *context)
dev_dbg(&context->mhi_dev->dev, "Unable to send done response %d\n", ret);
}
+static void sahara_memory_debug64(struct sahara_context *context)
+{
+ int ret;
+
+ dev_dbg(&context->mhi_dev->dev,
+ "MEMORY DEBUG64 cmd received. length:%d table_address:%#llx table_length:%#llx\n",
+ le32_to_cpu(context->rx->length),
+ le64_to_cpu(context->rx->memory_debug64.table_address),
+ le64_to_cpu(context->rx->memory_debug64.table_length));
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_MEM_DEBUG64_LENGTH) {
+ dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ context->dump_table_address = le64_to_cpu(context->rx->memory_debug64.table_address);
+ context->dump_table_length = le64_to_cpu(context->rx->memory_debug64.table_length);
+
+ if (context->dump_table_length % sizeof(struct sahara_debug_table_entry64) != 0 ||
+ !context->dump_table_length) {
+ dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - table length %lld\n",
+ context->dump_table_length);
+ return;
+ }
+
+ /*
+ * From this point, the protocol flips. We make memory_read requests to
+ * the device, and the device responds with the raw data. If the device
+ * has an error, it will send an End of Image command. First we need to
+ * request the memory dump table so that we know where all the pieces
+ * of the dump are that we can consume.
+ */
+
+ context->is_mem_dump_mode = true;
+
+ /*
+ * Assume that the table is smaller than our MTU so that we can read it
+ * in one shot. The spec does not put an upper limit on the table, but
+ * no known device will exceed this.
+ */
+ if (context->dump_table_length > SAHARA_PACKET_MAX_SIZE) {
+ dev_err(&context->mhi_dev->dev, "Memory dump table length %lld exceeds supported size. Discarding dump\n",
+ context->dump_table_length);
+ sahara_send_reset(context);
+ return;
+ }
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_table_address);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(context->dump_table_length);
+
+ context->rx_size_requested = context->dump_table_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send read for dump table %d\n", ret);
+}
+
static void sahara_processing(struct work_struct *work)
{
- struct sahara_context *context = container_of(work, struct sahara_context, work);
+ struct sahara_context *context = container_of(work, struct sahara_context, fw_work);
int ret;
switch (le32_to_cpu(context->rx->cmd)) {
@@ -338,6 +485,12 @@ static void sahara_processing(struct work_struct *work)
case SAHARA_DONE_RESP_CMD:
/* Intentional do nothing as we don't need to exit an app */
break;
+ case SAHARA_RESET_RESP_CMD:
+ /* Intentional do nothing as we don't need to exit an app */
+ break;
+ case SAHARA_MEM_DEBUG64_CMD:
+ sahara_memory_debug64(context);
+ break;
default:
dev_err(&context->mhi_dev->dev, "Unknown command %d\n",
le32_to_cpu(context->rx->cmd));
@@ -350,6 +503,217 @@ static void sahara_processing(struct work_struct *work)
dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret);
}
+static void sahara_parse_dump_table(struct sahara_context *context)
+{
+ struct sahara_dump_table_entry *image_out_table;
+ struct sahara_debug_table_entry64 *dev_table;
+ struct sahara_memory_dump_meta_v1 *dump_meta;
+ u64 table_nents;
+ u64 dump_length;
+ int ret;
+ u64 i;
+
+ table_nents = context->dump_table_length / sizeof(*dev_table);
+ context->dump_images_left = table_nents;
+ dump_length = 0;
+
+ dev_table = (struct sahara_debug_table_entry64 *)(context->rx);
+ for (i = 0; i < table_nents; ++i) {
+ /* Do not trust the device, ensure the strings are terminated */
+ dev_table[i].description[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
+ dev_table[i].filename[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
+
+ dump_length = size_add(dump_length, le64_to_cpu(dev_table[i].length));
+ if (dump_length == SIZE_MAX) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ dev_dbg(&context->mhi_dev->dev,
+ "Memory dump table entry %lld type: %lld address: %#llx length: %#llx description: \"%s\" filename \"%s\"\n",
+ i,
+ le64_to_cpu(dev_table[i].type),
+ le64_to_cpu(dev_table[i].address),
+ le64_to_cpu(dev_table[i].length),
+ dev_table[i].description,
+ dev_table[i].filename);
+ }
+
+ dump_length = size_add(dump_length, sizeof(*dump_meta));
+ if (dump_length == SIZE_MAX) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+ dump_length = size_add(dump_length, size_mul(sizeof(*image_out_table), table_nents));
+ if (dump_length == SIZE_MAX) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ context->mem_dump_sz = dump_length;
+ context->mem_dump = vzalloc(dump_length);
+ if (!context->mem_dump) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+
+ /* Populate the dump metadata and table for userspace */
+ dump_meta = context->mem_dump;
+ dump_meta->magic = SAHARA_DUMP_V1_MAGIC;
+ dump_meta->version = SAHARA_DUMP_V1_VER;
+ dump_meta->dump_size = dump_length;
+ dump_meta->table_size = context->dump_table_length;
+
+ image_out_table = context->mem_dump + sizeof(*dump_meta);
+ for (i = 0; i < table_nents; ++i) {
+ image_out_table[i].type = le64_to_cpu(dev_table[i].type);
+ image_out_table[i].address = le64_to_cpu(dev_table[i].address);
+ image_out_table[i].length = le64_to_cpu(dev_table[i].length);
+ strscpy(image_out_table[i].description, dev_table[i].description,
+ SAHARA_TABLE_ENTRY_STR_LEN);
+ strscpy(image_out_table[i].filename,
+ dev_table[i].filename,
+ SAHARA_TABLE_ENTRY_STR_LEN);
+ }
+
+ context->mem_dump_freespace = &image_out_table[i];
+
+ /* Done parsing the table, switch to image dump mode */
+ context->dump_table_length = 0;
+
+ /* Request the first chunk of the first image */
+ context->dump_image = &image_out_table[0];
+ dump_length = min(context->dump_image->length, SAHARA_READ_MAX_SIZE);
+ /* Avoid requesting EOI sized data so that we can identify errors */
+ if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
+ dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
+
+ context->dump_image_offset = dump_length;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_image->address);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length);
+
+ context->rx_size_requested = dump_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send read for dump content %d\n", ret);
+}
+
+static void sahara_parse_dump_image(struct sahara_context *context)
+{
+ u64 dump_length;
+ int ret;
+
+ memcpy(context->mem_dump_freespace, context->rx, context->rx_size);
+ context->mem_dump_freespace += context->rx_size;
+
+ if (context->dump_image_offset >= context->dump_image->length) {
+ /* Need to move to next image */
+ context->dump_image++;
+ context->dump_images_left--;
+ context->dump_image_offset = 0;
+
+ if (!context->dump_images_left) {
+ /* Dump done */
+ dev_coredumpv(context->mhi_dev->mhi_cntrl->cntrl_dev,
+ context->mem_dump,
+ context->mem_dump_sz,
+ GFP_KERNEL);
+ context->mem_dump = NULL;
+ sahara_send_reset(context);
+ return;
+ }
+ }
+
+ /* Get next image chunk */
+ dump_length = context->dump_image->length - context->dump_image_offset;
+ dump_length = min(dump_length, SAHARA_READ_MAX_SIZE);
+ /* Avoid requesting EOI sized data so that we can identify errors */
+ if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
+ dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH);
+ context->tx[0]->memory_read64.memory_address =
+ cpu_to_le64(context->dump_image->address + context->dump_image_offset);
+ context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length);
+
+ context->dump_image_offset += dump_length;
+ context->rx_size_requested = dump_length;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_MEM_READ64_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev,
+ "Unable to send read for dump content %d\n", ret);
+}
+
+static void sahara_dump_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, dump_work);
+ int ret;
+
+ /*
+ * We should get the expected raw data, but if the device has an error
+ * it is supposed to send EOI with an error code.
+ */
+ if (context->rx_size != context->rx_size_requested &&
+ context->rx_size != SAHARA_END_OF_IMAGE_LENGTH) {
+ dev_err(&context->mhi_dev->dev,
+ "Unexpected response to read_data. Expected size: %#zx got: %#zx\n",
+ context->rx_size_requested,
+ context->rx_size);
+ goto error;
+ }
+
+ if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH &&
+ le32_to_cpu(context->rx->cmd) == SAHARA_END_OF_IMAGE_CMD) {
+ dev_err(&context->mhi_dev->dev,
+ "Unexpected EOI response to read_data. Status: %d\n",
+ le32_to_cpu(context->rx->end_of_image.status));
+ goto error;
+ }
+
+ if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH &&
+ le32_to_cpu(context->rx->cmd) != SAHARA_END_OF_IMAGE_CMD) {
+ dev_err(&context->mhi_dev->dev,
+ "Invalid EOI response to read_data. CMD: %d\n",
+ le32_to_cpu(context->rx->cmd));
+ goto error;
+ }
+
+ /*
+ * Need to know if we received the dump table, or part of a dump image.
+ * Since we get raw data, we cannot tell from the data itself. Instead,
+ * we use the stored dump_table_length, which we zero after we read and
+ * process the entire table.
+ */
+ if (context->dump_table_length)
+ sahara_parse_dump_table(context);
+ else
+ sahara_parse_dump_image(context);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE, context->rx,
+ SAHARA_PACKET_MAX_SIZE, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret);
+
+ return;
+
+error:
+ vfree(context->mem_dump);
+ context->mem_dump = NULL;
+ sahara_send_reset(context);
+}
+
static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
struct sahara_context *context;
@@ -382,7 +746,8 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
}
context->mhi_dev = mhi_dev;
- INIT_WORK(&context->work, sahara_processing);
+ INIT_WORK(&context->fw_work, sahara_processing);
+ INIT_WORK(&context->dump_work, sahara_dump_processing);
context->image_table = aic100_image_table;
context->table_size = ARRAY_SIZE(aic100_image_table);
context->active_image_id = SAHARA_IMAGE_ID_NONE;
@@ -405,7 +770,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev)
{
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
- cancel_work_sync(&context->work);
+ cancel_work_sync(&context->fw_work);
+ cancel_work_sync(&context->dump_work);
+ if (context->mem_dump)
+ vfree(context->mem_dump);
sahara_release_image(context);
mhi_unprepare_from_transfer(mhi_dev);
}
@@ -418,8 +786,14 @@ static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result
{
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
- if (!mhi_result->transaction_status)
- schedule_work(&context->work);
+ if (!mhi_result->transaction_status) {
+ context->rx_size = mhi_result->bytes_xferd;
+ if (context->is_mem_dump_mode)
+ schedule_work(&context->dump_work);
+ else
+ schedule_work(&context->fw_work);
+ }
+
}
static const struct mhi_device_id sahara_mhi_match_table[] = {
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 45f624b088da..5504721007cc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -9,9 +9,6 @@ menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
select DRM_PANEL_ORIENTATION_QUIRKS
- select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
- select FB_CORE if DRM_FBDEV_EMULATION
- select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
select HDMI
select I2C
select DMA_SHARED_BUFFER
@@ -211,10 +208,28 @@ config DRM_DEBUG_MODESET_LOCK
If in doubt, say "N".
-config DRM_CLIENT_SELECTION
+config DRM_CLIENT
bool
depends on DRM
- select DRM_CLIENT_SETUP if DRM_FBDEV_EMULATION
+ help
+ Enables support for DRM clients. DRM drivers that need
+ struct drm_client_dev and its interfaces should select this
+ option. Drivers that support the default clients should
+ select DRM_CLIENT_SELECTION instead.
+
+config DRM_CLIENT_LIB
+ tristate
+ depends on DRM
+ select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
+ select FB_CORE if DRM_FBDEV_EMULATION
+ help
+ This option enables the DRM client library and selects all
+ modules and components according to the enabled clients.
+
+config DRM_CLIENT_SELECTION
+ tristate
+ depends on DRM
+ select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
help
Drivers that support in-kernel DRM clients have to select this
option.
@@ -222,10 +237,18 @@ config DRM_CLIENT_SELECTION
config DRM_CLIENT_SETUP
bool
depends on DRM_CLIENT_SELECTION
+ help
+ Enables the DRM client selection. DRM drivers that support the
+ default clients should select DRM_CLIENT_SELECTION instead.
+
+menu "Supported DRM clients"
+ depends on DRM_CLIENT_SELECTION
config DRM_FBDEV_EMULATION
bool "Enable legacy fbdev support for your modesetting driver"
- depends on DRM
+ depends on DRM_CLIENT_SELECTION
+ select DRM_CLIENT
+ select DRM_CLIENT_SETUP
select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE
default FB
help
@@ -264,6 +287,8 @@ config DRM_FBDEV_LEAK_PHYS_SMEM
If in doubt, say "N" or spread the word to your closed source
library vendor.
+endmenu
+
config DRM_LOAD_EDID_FIRMWARE
bool "Allow to specify an EDID data set instead of probing for it"
depends on DRM
@@ -333,19 +358,21 @@ config DRM_TTM_HELPER
tristate
depends on DRM
select DRM_TTM
+ select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help
Helpers for ttm-based gem objects
config DRM_GEM_DMA_HELPER
tristate
depends on DRM
- select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION
+ select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help
Choose this if you need the GEM DMA helper functions
config DRM_GEM_SHMEM_HELPER
tristate
depends on DRM && MMU
+ select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help
Choose this if you need the GEM shmem helper functions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 31d8bf60a2fd..463afad1b5ca 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -40,8 +40,6 @@ drm-y := \
drm_blend.o \
drm_bridge.o \
drm_cache.o \
- drm_client.o \
- drm_client_modeset.o \
drm_color_mgmt.o \
drm_connector.o \
drm_crtc.o \
@@ -75,6 +73,10 @@ drm-y := \
drm_vblank_work.o \
drm_vma_manager.o \
drm_writeback.o
+drm-$(CONFIG_DRM_CLIENT) += \
+ drm_client.o \
+ drm_client_event.o \
+ drm_client_modeset.o
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
@@ -142,15 +144,19 @@ drm_kms_helper-y := \
drm_probe_helper.o \
drm_self_refresh_helper.o \
drm_simple_kms_helper.o
-drm_kms_helper-$(CONFIG_DRM_CLIENT_SETUP) += \
- drm_client_setup.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
-drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += \
- drm_fbdev_client.o \
- drm_fb_helper.o
+drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
#
+# DRM clients
+#
+
+drm_client_lib-y := drm_client_setup.o
+drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o
+obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o
+
+#
# Drivers and the rest
#
diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig
index 680a94c361ba..41fa3377d9cf 100644
--- a/drivers/gpu/drm/amd/amdgpu/Kconfig
+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
@@ -5,6 +5,7 @@ config DRM_AMDGPU
depends on DRM && PCI && MMU
depends on !UML
select FW_LOADER
+ select DRM_CLIENT
select DRM_CLIENT_SELECTION
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index be5fb87ed5cd..f12fab13386a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -38,8 +38,8 @@
#include <linux/apple-gmux.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_crtc_helper.h>
-#include <drm/drm_fb_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/amdgpu_drm.h>
#include <linux/device.h>
@@ -4793,13 +4793,13 @@ unprepare:
* amdgpu_device_suspend - initiate device suspend
*
* @dev: drm dev pointer
- * @fbcon : notify the fbdev of suspend
+ * @notify_clients: notify in-kernel DRM clients
*
* Puts the hw in the suspend state (all asics).
* Returns 0 for success or an error on failure.
* Called at driver suspend.
*/
-int amdgpu_device_suspend(struct drm_device *dev, bool fbcon)
+int amdgpu_device_suspend(struct drm_device *dev, bool notify_clients)
{
struct amdgpu_device *adev = drm_to_adev(dev);
int r = 0;
@@ -4819,8 +4819,8 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon)
if (amdgpu_acpi_smart_shift_update(dev, AMDGPU_SS_DEV_D3))
DRM_WARN("smart shift update failed\n");
- if (fbcon)
- drm_fb_helper_set_suspend_unlocked(adev_to_drm(adev)->fb_helper, true);
+ if (notify_clients)
+ drm_client_dev_suspend(adev_to_drm(adev), false);
cancel_delayed_work_sync(&adev->delayed_init_work);
@@ -4855,13 +4855,13 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon)
* amdgpu_device_resume - initiate device resume
*
* @dev: drm dev pointer
- * @fbcon : notify the fbdev of resume
+ * @notify_clients: notify in-kernel DRM clients
*
* Bring the hw back to operating state (all asics).
* Returns 0 for success or an error on failure.
* Called at driver resume.
*/
-int amdgpu_device_resume(struct drm_device *dev, bool fbcon)
+int amdgpu_device_resume(struct drm_device *dev, bool notify_clients)
{
struct amdgpu_device *adev = drm_to_adev(dev);
int r = 0;
@@ -4917,8 +4917,8 @@ exit:
/* Make sure IB tests flushed */
flush_delayed_work(&adev->delayed_init_work);
- if (fbcon)
- drm_fb_helper_set_suspend_unlocked(adev_to_drm(adev)->fb_helper, false);
+ if (notify_clients)
+ drm_client_dev_resume(adev_to_drm(adev), false);
amdgpu_ras_resume(adev);
@@ -5482,7 +5482,7 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context)
if (r)
goto out;
- drm_fb_helper_set_suspend_unlocked(adev_to_drm(tmp_adev)->fb_helper, false);
+ drm_client_dev_resume(adev_to_drm(tmp_adev), false);
/*
* The GPU enters bad state once faulty pages
@@ -5842,7 +5842,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
*/
amdgpu_unregister_gpu_instance(tmp_adev);
- drm_fb_helper_set_suspend_unlocked(adev_to_drm(tmp_adev)->fb_helper, true);
+ drm_client_dev_suspend(adev_to_drm(tmp_adev), false);
/* disable ras on ALL IPs */
if (!need_emergency_restart &&
diff --git a/drivers/gpu/drm/bridge/aux-bridge.c b/drivers/gpu/drm/bridge/aux-bridge.c
index b29980f95379..e3e95ccec1ae 100644
--- a/drivers/gpu/drm/bridge/aux-bridge.c
+++ b/drivers/gpu/drm/bridge/aux-bridge.c
@@ -120,6 +120,10 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev,
data->bridge.funcs = &drm_aux_bridge_funcs;
data->bridge.of_node = data->dev->of_node;
+ /* passthrough data, allow everything */
+ data->bridge.interlace_allowed = true;
+ data->bridge.ycbcr_420_allowed = true;
+
return devm_drm_bridge_add(data->dev, &data->bridge);
}
diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c
index 6886db2d9e00..48f297c78ee6 100644
--- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c
+++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c
@@ -180,6 +180,10 @@ static int drm_aux_hpd_bridge_probe(struct auxiliary_device *auxdev,
data->bridge.ops = DRM_BRIDGE_OP_HPD;
data->bridge.type = id->driver_data;
+ /* passthrough data, allow everything */
+ data->bridge.interlace_allowed = true;
+ data->bridge.ycbcr_420_allowed = true;
+
auxiliary_set_drvdata(auxdev, data);
return devm_drm_bridge_add(data->dev, &data->bridge);
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index ab8e00baf3f1..aab9ce7be94c 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -270,6 +270,10 @@ static int display_connector_probe(struct platform_device *pdev)
/* All the supported connector types support interlaced modes. */
conn->bridge.interlace_allowed = true;
+ if (type == DRM_MODE_CONNECTOR_HDMIA ||
+ type == DRM_MODE_CONNECTOR_DisplayPort)
+ conn->bridge.ycbcr_420_allowed = true;
+
/* Get the optional connector label. */
of_property_read_string(pdev->dev.of_node, "label", &label);
diff --git a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
index 07a78a02a884..3ebf0b9866de 100644
--- a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
+++ b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
@@ -85,3 +85,4 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev,
EXPORT_SYMBOL_GPL(devm_imx_drm_legacy_bridge);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Freescale i.MX DRM bridge driver for legacy DT bindings");
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 925e42f46cd8..35ae3f0e8f51 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -770,8 +770,6 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
mutex_lock(&ctx->lock);
- hdmi_avi_infoframe_init(&ctx->hdmi_avi_infoframe);
-
ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector,
adjusted_mode);
if (ret) {
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index 7f91b0db161e..9be9cc5b9025 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -180,6 +180,8 @@ struct sii902x {
struct gpio_desc *reset_gpio;
struct i2c_mux_core *i2cmux;
bool sink_is_hdmi;
+ u32 bus_width;
+
/*
* Mutex protects audio and video functions from interfering
* each other, by keeping their i2c command sequences atomic.
@@ -477,6 +479,8 @@ static u32 *sii902x_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
u32 output_fmt,
unsigned int *num_input_fmts)
{
+
+ struct sii902x *sii902x = bridge_to_sii902x(bridge);
u32 *input_fmts;
*num_input_fmts = 0;
@@ -485,7 +489,20 @@ static u32 *sii902x_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
if (!input_fmts)
return NULL;
- input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ switch (sii902x->bus_width) {
+ case 16:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB565_1X16;
+ break;
+ case 18:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X18;
+ break;
+ case 24:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ default:
+ return NULL;
+ }
+
*num_input_fmts = 1;
return input_fmts;
@@ -1167,6 +1184,11 @@ static int sii902x_probe(struct i2c_client *client)
return PTR_ERR(sii902x->reset_gpio);
}
+ sii902x->bus_width = 24;
+ endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+ if (endpoint)
+ of_property_read_u32(endpoint, "bus-width", &sii902x->bus_width);
+
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
if (endpoint) {
struct device_node *remote = of_graph_get_remote_port_parent(endpoint);
diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig
index 15fc182d05ef..ca416dab156d 100644
--- a/drivers/gpu/drm/bridge/synopsys/Kconfig
+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig
@@ -46,6 +46,14 @@ config DRM_DW_HDMI_CEC
Support the CE interface which is part of the Synopsys
Designware HDMI block.
+config DRM_DW_HDMI_QP
+ tristate
+ select DRM_DISPLAY_HDMI_HELPER
+ select DRM_DISPLAY_HDMI_STATE_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_KMS_HELPER
+ select REGMAP_MMIO
+
config DRM_DW_MIPI_DSI
tristate
select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
index ce715562e9e5..9869d9651ed1 100644
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -5,4 +5,6 @@ obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
+obj-$(CONFIG_DRM_DW_HDMI_QP) += dw-hdmi-qp.o
+
obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
new file mode 100644
index 000000000000..181c5164b231
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2024 Collabora Ltd.
+ *
+ * Author: Algea Cao <algea.cao@rock-chips.com>
+ * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+ */
+#include <linux/completion.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+
+#include <drm/bridge/dw_hdmi_qp.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modes.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "dw-hdmi-qp.h"
+
+#define DDC_CI_ADDR 0x37
+#define DDC_SEGMENT_ADDR 0x30
+
+#define HDMI14_MAX_TMDSCLK 340000000
+
+#define SCRAMB_POLL_DELAY_MS 3000
+
+struct dw_hdmi_qp_i2c {
+ struct i2c_adapter adap;
+
+ struct mutex lock; /* used to serialize data transfers */
+ struct completion cmp;
+ u8 stat;
+
+ u8 slave_reg;
+ bool is_regaddr;
+ bool is_segment;
+};
+
+struct dw_hdmi_qp {
+ struct drm_bridge bridge;
+
+ struct device *dev;
+ struct dw_hdmi_qp_i2c *i2c;
+
+ struct {
+ const struct dw_hdmi_qp_phy_ops *ops;
+ void *data;
+ } phy;
+
+ struct regmap *regm;
+};
+
+static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val,
+ int offset)
+{
+ regmap_write(hdmi->regm, offset, val);
+}
+
+static unsigned int dw_hdmi_qp_read(struct dw_hdmi_qp *hdmi, int offset)
+{
+ unsigned int val = 0;
+
+ regmap_read(hdmi->regm, offset, &val);
+
+ return val;
+}
+
+static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data,
+ unsigned int mask, unsigned int reg)
+{
+ regmap_update_bits(hdmi->regm, reg, mask, data);
+}
+
+static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi,
+ unsigned char *buf, unsigned int length)
+{
+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
+ int stat;
+
+ if (!i2c->is_regaddr) {
+ dev_dbg(hdmi->dev, "set read register address to 0\n");
+ i2c->slave_reg = 0x00;
+ i2c->is_regaddr = true;
+ }
+
+ while (length--) {
+ reinit_completion(&i2c->cmp);
+
+ dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
+ I2CM_INTERFACE_CONTROL0);
+
+ if (i2c->is_segment)
+ dw_hdmi_qp_mod(hdmi, I2CM_EXT_READ, I2CM_WR_MASK,
+ I2CM_INTERFACE_CONTROL0);
+ else
+ dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK,
+ I2CM_INTERFACE_CONTROL0);
+
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat) {
+ dev_err(hdmi->dev, "i2c read timed out\n");
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
+ return -EAGAIN;
+ }
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
+ dev_err(hdmi->dev, "i2c read error\n");
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
+ return -EIO;
+ }
+
+ *buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff;
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
+ }
+
+ i2c->is_segment = false;
+
+ return 0;
+}
+
+static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi,
+ unsigned char *buf, unsigned int length)
+{
+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
+ int stat;
+
+ if (!i2c->is_regaddr) {
+ /* Use the first write byte as register address */
+ i2c->slave_reg = buf[0];
+ length--;
+ buf++;
+ i2c->is_regaddr = true;
+ }
+
+ while (length--) {
+ reinit_completion(&i2c->cmp);
+
+ dw_hdmi_qp_write(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3);
+ dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
+ I2CM_INTERFACE_CONTROL0);
+ dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK,
+ I2CM_INTERFACE_CONTROL0);
+
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+ if (!stat) {
+ dev_err(hdmi->dev, "i2c write time out!\n");
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
+ return -EAGAIN;
+ }
+
+ /* Check for error condition on the bus */
+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
+ dev_err(hdmi->dev, "i2c write nack!\n");
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
+ return -EIO;
+ }
+
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
+ }
+
+ return 0;
+}
+
+static int dw_hdmi_qp_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap);
+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
+ u8 addr = msgs[0].addr;
+ int i, ret = 0;
+
+ if (addr == DDC_CI_ADDR)
+ /*
+ * The internal I2C controller does not support the multi-byte
+ * read and write operations needed for DDC/CI.
+ * FIXME: Blacklist the DDC/CI address until we filter out
+ * unsupported I2C operations.
+ */
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].len == 0) {
+ dev_err(hdmi->dev,
+ "unsupported transfer %d/%d, no data\n",
+ i + 1, num);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ guard(mutex)(&i2c->lock);
+
+ /* Unmute DONE and ERROR interrupts */
+ dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
+ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
+ MAINUNIT_1_INT_MASK_N);
+
+ /* Set slave device address taken from the first I2C message */
+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
+ addr = DDC_ADDR;
+
+ dw_hdmi_qp_mod(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0);
+
+ /* Set slave device register address on transfer */
+ i2c->is_regaddr = false;
+
+ /* Set segment pointer for I2C extended read mode operation */
+ i2c->is_segment = false;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
+ i2c->is_segment = true;
+ dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR,
+ I2CM_INTERFACE_CONTROL1);
+ dw_hdmi_qp_mod(hdmi, *msgs[i].buf << 7, I2CM_SEG_PTR,
+ I2CM_INTERFACE_CONTROL1);
+ } else {
+ if (msgs[i].flags & I2C_M_RD)
+ ret = dw_hdmi_qp_i2c_read(hdmi, msgs[i].buf,
+ msgs[i].len);
+ else
+ ret = dw_hdmi_qp_i2c_write(hdmi, msgs[i].buf,
+ msgs[i].len);
+ }
+ if (ret < 0)
+ break;
+ }
+
+ if (!ret)
+ ret = num;
+
+ /* Mute DONE and ERROR interrupts */
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N,
+ MAINUNIT_1_INT_MASK_N);
+
+ return ret;
+}
+
+static u32 dw_hdmi_qp_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm dw_hdmi_qp_algorithm = {
+ .master_xfer = dw_hdmi_qp_i2c_xfer,
+ .functionality = dw_hdmi_qp_i2c_func,
+};
+
+static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi)
+{
+ struct dw_hdmi_qp_i2c *i2c;
+ struct i2c_adapter *adap;
+ int ret;
+
+ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&i2c->lock);
+ init_completion(&i2c->cmp);
+
+ adap = &i2c->adap;
+ adap->owner = THIS_MODULE;
+ adap->dev.parent = hdmi->dev;
+ adap->algo = &dw_hdmi_qp_algorithm;
+ strscpy(adap->name, "DesignWare HDMI QP", sizeof(adap->name));
+
+ i2c_set_adapdata(adap, hdmi);
+
+ ret = devm_i2c_add_adapter(hdmi->dev, adap);
+ if (ret) {
+ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+ devm_kfree(hdmi->dev, i2c);
+ return ERR_PTR(ret);
+ }
+
+ hdmi->i2c = i2c;
+ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+ return adap;
+}
+
+static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi,
+ const u8 *buffer, size_t len)
+{
+ u32 val, i, j;
+
+ if (len != HDMI_INFOFRAME_SIZE(AVI)) {
+ dev_err(hdmi->dev, "failed to configure avi infoframe\n");
+ return -EINVAL;
+ }
+
+ /*
+ * DW HDMI QP IP uses a different byte format from standard AVI info
+ * frames, though generally the bits are in the correct bytes.
+ */
+ val = buffer[1] << 8 | buffer[2] << 16;
+ dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ if (i * 4 + j >= 14)
+ break;
+ if (!j)
+ val = buffer[i * 4 + j + 3];
+ val |= buffer[i * 4 + j + 3] << (8 * j);
+ }
+
+ dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
+ }
+
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
+
+ dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
+ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi,
+ const u8 *buffer, size_t len)
+{
+ u32 val, i;
+
+ if (len != HDMI_INFOFRAME_SIZE(DRM)) {
+ dev_err(hdmi->dev, "failed to configure drm infoframe\n");
+ return -EINVAL;
+ }
+
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
+
+ val = buffer[1] << 8 | buffer[2] << 16;
+ dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0);
+
+ for (i = 0; i <= buffer[2]; i++) {
+ if (i % 4 == 0)
+ val = buffer[3 + i];
+ val |= buffer[3 + i] << ((i % 4) * 8);
+
+ if ((i % 4 == 3) || i == buffer[2])
+ dw_hdmi_qp_write(hdmi, val,
+ PKT_DRMI_CONTENTS1 + ((i / 4) * 4));
+ }
+
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
+ dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN,
+ PKTSCHED_PKT_EN);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_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 dw_hdmi_qp *hdmi = bridge->driver_private;
+ int ret;
+
+ ret = drm_atomic_helper_connector_hdmi_check(conn_state->connector,
+ conn_state->state);
+ if (ret)
+ dev_dbg(hdmi->dev, "%s failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ struct drm_atomic_state *state = old_state->base.state;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *connector;
+ unsigned int op_mode;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
+ return;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ if (connector->display_info.is_hdmi) {
+ dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n",
+ __func__, conn_state->hdmi.tmds_char_rate);
+ op_mode = 0;
+ } else {
+ dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
+ op_mode = OPMODE_DVI;
+ }
+
+ hdmi->phy.ops->init(hdmi, hdmi->phy.data);
+
+ dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
+ dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0);
+
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+}
+
+static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
+}
+
+static enum drm_connector_status
+dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+}
+
+static const struct drm_edid *
+dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ const struct drm_edid *drm_edid;
+
+ drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
+ if (!drm_edid)
+ dev_dbg(hdmi->dev, "failed to get edid\n");
+
+ return drm_edid;
+}
+
+static enum drm_mode_status
+dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ unsigned long long rate;
+
+ rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
+ if (rate > HDMI14_MAX_TMDSCLK) {
+ dev_dbg(hdmi->dev, "Unsupported mode clock: %d\n", mode->clock);
+ return MODE_CLOCK_HIGH;
+ }
+
+ return MODE_OK;
+}
+
+static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
+ PKTSCHED_PKT_EN);
+ break;
+
+ case HDMI_INFOFRAME_TYPE_DRM:
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
+ break;
+
+ default:
+ dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
+ }
+
+ return 0;
+}
+
+static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
+
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
+
+ case HDMI_INFOFRAME_TYPE_DRM:
+ return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
+
+ default:
+ dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
+ return 0;
+ }
+}
+
+static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_check = dw_hdmi_qp_bridge_atomic_check,
+ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
+ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
+ .detect = dw_hdmi_qp_bridge_detect,
+ .edid_read = dw_hdmi_qp_bridge_edid_read,
+ .mode_valid = dw_hdmi_qp_bridge_mode_valid,
+ .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
+ .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
+};
+
+static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
+{
+ struct dw_hdmi_qp *hdmi = dev_id;
+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
+ u32 stat;
+
+ stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS);
+
+ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ |
+ I2CM_NACK_RCVD_IRQ);
+
+ if (i2c->stat) {
+ dw_hdmi_qp_write(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR);
+ complete(&i2c->cmp);
+ }
+
+ if (stat)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+static const struct regmap_config dw_hdmi_qp_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = EARCRX_1_INT_FORCE,
+};
+
+static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
+{
+ dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
+ dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
+ dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
+
+ /* Software reset */
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
+
+ dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
+
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
+
+ /* Clear DONE and ERROR interrupts */
+ dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
+ MAINUNIT_1_INT_CLEAR);
+
+ if (hdmi->phy.ops->setup_hpd)
+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
+}
+
+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
+ struct drm_encoder *encoder,
+ const struct dw_hdmi_qp_plat_data *plat_data)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_hdmi_qp *hdmi;
+ void __iomem *regs;
+ int ret;
+
+ if (!plat_data->phy_ops || !plat_data->phy_ops->init ||
+ !plat_data->phy_ops->disable || !plat_data->phy_ops->read_hpd) {
+ dev_err(dev, "Missing platform PHY ops\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return ERR_PTR(-ENOMEM);
+
+ hdmi->dev = dev;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return ERR_CAST(regs);
+
+ hdmi->regm = devm_regmap_init_mmio(dev, regs, &dw_hdmi_qp_regmap_config);
+ if (IS_ERR(hdmi->regm)) {
+ dev_err(dev, "Failed to configure regmap\n");
+ return ERR_CAST(hdmi->regm);
+ }
+
+ hdmi->phy.ops = plat_data->phy_ops;
+ hdmi->phy.data = plat_data->phy_data;
+
+ dw_hdmi_qp_init_hw(hdmi);
+
+ ret = devm_request_threaded_irq(dev, plat_data->main_irq,
+ dw_hdmi_qp_main_hardirq, NULL,
+ IRQF_SHARED, dev_name(dev), hdmi);
+ if (ret)
+ return ERR_PTR(ret);
+
+ hdmi->bridge.driver_private = hdmi;
+ hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI |
+ DRM_BRIDGE_OP_HPD;
+ hdmi->bridge.of_node = pdev->dev.of_node;
+ hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ hdmi->bridge.vendor = "Synopsys";
+ hdmi->bridge.product = "DW HDMI QP TX";
+
+ hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi);
+ if (IS_ERR(hdmi->bridge.ddc))
+ return ERR_CAST(hdmi->bridge.ddc);
+
+ ret = devm_drm_bridge_add(dev, &hdmi->bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return hdmi;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
+
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi)
+{
+ dw_hdmi_qp_init_hw(hdmi);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume);
+
+MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>");
+MODULE_DESCRIPTION("DW HDMI QP transmitter library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
new file mode 100644
index 000000000000..2115b8ef0bd6
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
@@ -0,0 +1,834 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ * Algea Cao <algea.cao@rock-chips.com>
+ */
+#ifndef __DW_HDMI_QP_H__
+#define __DW_HDMI_QP_H__
+
+#include <linux/bits.h>
+
+/* Main Unit Registers */
+#define CORE_ID 0x0
+#define VER_NUMBER 0x4
+#define VER_TYPE 0x8
+#define CONFIG_REG 0xc
+#define CONFIG_CEC BIT(28)
+#define CONFIG_AUD_UD BIT(23)
+#define CORE_TIMESTAMP_HHMM 0x14
+#define CORE_TIMESTAMP_MMDD 0x18
+#define CORE_TIMESTAMP_YYYY 0x1c
+/* Reset Manager Registers */
+#define GLOBAL_SWRESET_REQUEST 0x40
+#define EARCRX_CMDC_SWINIT_P BIT(27)
+#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10)
+#define GLOBAL_SWDISABLE 0x44
+#define CEC_SWDISABLE BIT(17)
+#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10)
+#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6)
+#define RESET_MANAGER_CONFIG0 0x48
+#define RESET_MANAGER_STATUS0 0x50
+#define RESET_MANAGER_STATUS1 0x54
+#define RESET_MANAGER_STATUS2 0x58
+/* Timer Base Registers */
+#define TIMER_BASE_CONFIG0 0x80
+#define TIMER_BASE_STATUS0 0x84
+/* CMU Registers */
+#define CMU_CONFIG0 0xa0
+#define CMU_CONFIG1 0xa4
+#define CMU_CONFIG2 0xa8
+#define CMU_CONFIG3 0xac
+#define CMU_STATUS 0xb0
+#define DISPLAY_CLK_MONITOR 0x3f
+#define DISPLAY_CLK_LOCKED 0X15
+#define EARC_BPCLK_OFF BIT(9)
+#define AUDCLK_OFF BIT(7)
+#define LINKQPCLK_OFF BIT(5)
+#define VIDQPCLK_OFF BIT(3)
+#define IPI_CLK_OFF BIT(1)
+#define CMU_IPI_CLK_FREQ 0xb4
+#define CMU_VIDQPCLK_FREQ 0xb8
+#define CMU_LINKQPCLK_FREQ 0xbc
+#define CMU_AUDQPCLK_FREQ 0xc0
+#define CMU_EARC_BPCLK_FREQ 0xc4
+/* I2CM Registers */
+#define I2CM_SM_SCL_CONFIG0 0xe0
+#define I2CM_FM_SCL_CONFIG0 0xe4
+#define I2CM_CONFIG0 0xe8
+#define I2CM_CONTROL0 0xec
+#define I2CM_STATUS0 0xf0
+#define I2CM_INTERFACE_CONTROL0 0xf4
+#define I2CM_ADDR 0xff000
+#define I2CM_SLVADDR 0xfe0
+#define I2CM_WR_MASK 0x1e
+#define I2CM_EXT_READ BIT(4)
+#define I2CM_SHORT_READ BIT(3)
+#define I2CM_FM_READ BIT(2)
+#define I2CM_FM_WRITE BIT(1)
+#define I2CM_FM_EN BIT(0)
+#define I2CM_INTERFACE_CONTROL1 0xf8
+#define I2CM_SEG_PTR 0x7f80
+#define I2CM_SEG_ADDR 0x7f
+#define I2CM_INTERFACE_WRDATA_0_3 0xfc
+#define I2CM_INTERFACE_WRDATA_4_7 0x100
+#define I2CM_INTERFACE_WRDATA_8_11 0x104
+#define I2CM_INTERFACE_WRDATA_12_15 0x108
+#define I2CM_INTERFACE_RDDATA_0_3 0x10c
+#define I2CM_INTERFACE_RDDATA_4_7 0x110
+#define I2CM_INTERFACE_RDDATA_8_11 0x114
+#define I2CM_INTERFACE_RDDATA_12_15 0x118
+/* SCDC Registers */
+#define SCDC_CONFIG0 0x140
+#define SCDC_I2C_FM_EN BIT(12)
+#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6)
+#define SCDC_UPD_FLAGS_POLL_EN BIT(4)
+#define SCDC_CONTROL0 0x148
+#define SCDC_STATUS0 0x150
+#define STATUS_UPDATE BIT(0)
+#define FRL_START BIT(4)
+#define FLT_UPDATE BIT(5)
+/* FLT Registers */
+#define FLT_CONFIG0 0x160
+#define FLT_CONFIG1 0x164
+#define FLT_CONFIG2 0x168
+#define FLT_CONTROL0 0x170
+/* Main Unit 2 Registers */
+#define MAINUNIT_STATUS0 0x180
+/* Video Interface Registers */
+#define VIDEO_INTERFACE_CONFIG0 0x800
+#define VIDEO_INTERFACE_CONFIG1 0x804
+#define VIDEO_INTERFACE_CONFIG2 0x808
+#define VIDEO_INTERFACE_CONTROL0 0x80c
+#define VIDEO_INTERFACE_STATUS0 0x814
+/* Video Packing Registers */
+#define VIDEO_PACKING_CONFIG0 0x81c
+/* Audio Interface Registers */
+#define AUDIO_INTERFACE_CONFIG0 0x820
+#define AUD_IF_SEL_MSK 0x3
+#define AUD_IF_SPDIF 0x2
+#define AUD_IF_I2S 0x1
+#define AUD_IF_PAI 0x0
+#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2)
+#define AUD_FIFO_INIT_ON_OVF_EN BIT(2)
+#define I2S_LINES_EN_MSK GENMASK(7, 4)
+#define I2S_LINES_EN(x) BIT((x) + 4)
+#define I2S_BPCUV_RCV_MSK BIT(12)
+#define I2S_BPCUV_RCV_EN BIT(12)
+#define I2S_BPCUV_RCV_DIS 0
+#define SPDIF_LINES_EN GENMASK(19, 16)
+#define AUD_FORMAT_MSK GENMASK(26, 24)
+#define AUD_3DOBA (0x7 << 24)
+#define AUD_3DASP (0x6 << 24)
+#define AUD_MSOBA (0x5 << 24)
+#define AUD_MSASP (0x4 << 24)
+#define AUD_HBR (0x3 << 24)
+#define AUD_DST (0x2 << 24)
+#define AUD_OBA (0x1 << 24)
+#define AUD_ASP (0x0 << 24)
+#define AUDIO_INTERFACE_CONFIG1 0x824
+#define AUDIO_INTERFACE_CONTROL0 0x82c
+#define AUDIO_FIFO_CLR_P BIT(0)
+#define AUDIO_INTERFACE_STATUS0 0x834
+/* Frame Composer Registers */
+#define FRAME_COMPOSER_CONFIG0 0x840
+#define FRAME_COMPOSER_CONFIG1 0x844
+#define FRAME_COMPOSER_CONFIG2 0x848
+#define FRAME_COMPOSER_CONFIG3 0x84c
+#define FRAME_COMPOSER_CONFIG4 0x850
+#define FRAME_COMPOSER_CONFIG5 0x854
+#define FRAME_COMPOSER_CONFIG6 0x858
+#define FRAME_COMPOSER_CONFIG7 0x85c
+#define FRAME_COMPOSER_CONFIG8 0x860
+#define FRAME_COMPOSER_CONFIG9 0x864
+#define FRAME_COMPOSER_CONTROL0 0x86c
+/* Video Monitor Registers */
+#define VIDEO_MONITOR_CONFIG0 0x880
+#define VIDEO_MONITOR_STATUS0 0x884
+#define VIDEO_MONITOR_STATUS1 0x888
+#define VIDEO_MONITOR_STATUS2 0x88c
+#define VIDEO_MONITOR_STATUS3 0x890
+#define VIDEO_MONITOR_STATUS4 0x894
+#define VIDEO_MONITOR_STATUS5 0x898
+#define VIDEO_MONITOR_STATUS6 0x89c
+/* HDCP2 Logic Registers */
+#define HDCP2LOGIC_CONFIG0 0x8e0
+#define HDCP2_BYPASS BIT(0)
+#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4
+#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8
+/* HDCP14 Registers */
+#define HDCP14_CONFIG0 0x900
+#define HDCP14_CONFIG1 0x904
+#define HDCP14_CONFIG2 0x908
+#define HDCP14_CONFIG3 0x90c
+#define HDCP14_KEY_SEED 0x914
+#define HDCP14_KEY_H 0x918
+#define HDCP14_KEY_L 0x91c
+#define HDCP14_KEY_STATUS 0x920
+#define HDCP14_AKSV_H 0x924
+#define HDCP14_AKSV_L 0x928
+#define HDCP14_AN_H 0x92c
+#define HDCP14_AN_L 0x930
+#define HDCP14_STATUS0 0x934
+#define HDCP14_STATUS1 0x938
+/* Scrambler Registers */
+#define SCRAMB_CONFIG0 0x960
+/* Video Configuration Registers */
+#define LINK_CONFIG0 0x968
+#define OPMODE_FRL_4LANES BIT(8)
+#define OPMODE_DVI BIT(4)
+#define OPMODE_FRL BIT(0)
+/* TMDS FIFO Registers */
+#define TMDS_FIFO_CONFIG0 0x970
+#define TMDS_FIFO_CONTROL0 0x974
+/* FRL RSFEC Registers */
+#define FRL_RSFEC_CONFIG0 0xa20
+#define FRL_RSFEC_STATUS0 0xa30
+/* FRL Packetizer Registers */
+#define FRL_PKTZ_CONFIG0 0xa40
+#define FRL_PKTZ_CONTROL0 0xa44
+#define FRL_PKTZ_CONTROL1 0xa50
+#define FRL_PKTZ_STATUS1 0xa54
+/* Packet Scheduler Registers */
+#define PKTSCHED_CONFIG0 0xa80
+#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84
+#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88
+#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c
+#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90
+#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94
+#define PKTSCHED_PKT_CONFIG0 0xa98
+#define PKTSCHED_PKT_CONFIG1 0xa9c
+#define PKTSCHED_DRMI_FIELDRATE BIT(13)
+#define PKTSCHED_AVI_FIELDRATE BIT(12)
+#define PKTSCHED_PKT_CONFIG2 0xaa0
+#define PKTSCHED_PKT_CONFIG3 0xaa4
+#define PKTSCHED_PKT_EN 0xaa8
+#define PKTSCHED_DRMI_TX_EN BIT(17)
+#define PKTSCHED_AUDI_TX_EN BIT(15)
+#define PKTSCHED_AVI_TX_EN BIT(13)
+#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10)
+#define PKTSCHED_AMD_TX_EN BIT(8)
+#define PKTSCHED_GCP_TX_EN BIT(3)
+#define PKTSCHED_AUDS_TX_EN BIT(2)
+#define PKTSCHED_ACR_TX_EN BIT(1)
+#define PKTSCHED_NULL_TX_EN BIT(0)
+#define PKTSCHED_PKT_CONTROL0 0xaac
+#define PKTSCHED_PKT_SEND 0xab0
+#define PKTSCHED_PKT_STATUS0 0xab4
+#define PKTSCHED_PKT_STATUS1 0xab8
+#define PKT_NULL_CONTENTS0 0xb00
+#define PKT_NULL_CONTENTS1 0xb04
+#define PKT_NULL_CONTENTS2 0xb08
+#define PKT_NULL_CONTENTS3 0xb0c
+#define PKT_NULL_CONTENTS4 0xb10
+#define PKT_NULL_CONTENTS5 0xb14
+#define PKT_NULL_CONTENTS6 0xb18
+#define PKT_NULL_CONTENTS7 0xb1c
+#define PKT_ACP_CONTENTS0 0xb20
+#define PKT_ACP_CONTENTS1 0xb24
+#define PKT_ACP_CONTENTS2 0xb28
+#define PKT_ACP_CONTENTS3 0xb2c
+#define PKT_ACP_CONTENTS4 0xb30
+#define PKT_ACP_CONTENTS5 0xb34
+#define PKT_ACP_CONTENTS6 0xb38
+#define PKT_ACP_CONTENTS7 0xb3c
+#define PKT_ISRC1_CONTENTS0 0xb40
+#define PKT_ISRC1_CONTENTS1 0xb44
+#define PKT_ISRC1_CONTENTS2 0xb48
+#define PKT_ISRC1_CONTENTS3 0xb4c
+#define PKT_ISRC1_CONTENTS4 0xb50
+#define PKT_ISRC1_CONTENTS5 0xb54
+#define PKT_ISRC1_CONTENTS6 0xb58
+#define PKT_ISRC1_CONTENTS7 0xb5c
+#define PKT_ISRC2_CONTENTS0 0xb60
+#define PKT_ISRC2_CONTENTS1 0xb64
+#define PKT_ISRC2_CONTENTS2 0xb68
+#define PKT_ISRC2_CONTENTS3 0xb6c
+#define PKT_ISRC2_CONTENTS4 0xb70
+#define PKT_ISRC2_CONTENTS5 0xb74
+#define PKT_ISRC2_CONTENTS6 0xb78
+#define PKT_ISRC2_CONTENTS7 0xb7c
+#define PKT_GMD_CONTENTS0 0xb80
+#define PKT_GMD_CONTENTS1 0xb84
+#define PKT_GMD_CONTENTS2 0xb88
+#define PKT_GMD_CONTENTS3 0xb8c
+#define PKT_GMD_CONTENTS4 0xb90
+#define PKT_GMD_CONTENTS5 0xb94
+#define PKT_GMD_CONTENTS6 0xb98
+#define PKT_GMD_CONTENTS7 0xb9c
+#define PKT_AMD_CONTENTS0 0xba0
+#define PKT_AMD_CONTENTS1 0xba4
+#define PKT_AMD_CONTENTS2 0xba8
+#define PKT_AMD_CONTENTS3 0xbac
+#define PKT_AMD_CONTENTS4 0xbb0
+#define PKT_AMD_CONTENTS5 0xbb4
+#define PKT_AMD_CONTENTS6 0xbb8
+#define PKT_AMD_CONTENTS7 0xbbc
+#define PKT_VSI_CONTENTS0 0xbc0
+#define PKT_VSI_CONTENTS1 0xbc4
+#define PKT_VSI_CONTENTS2 0xbc8
+#define PKT_VSI_CONTENTS3 0xbcc
+#define PKT_VSI_CONTENTS4 0xbd0
+#define PKT_VSI_CONTENTS5 0xbd4
+#define PKT_VSI_CONTENTS6 0xbd8
+#define PKT_VSI_CONTENTS7 0xbdc
+#define PKT_AVI_CONTENTS0 0xbe0
+#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4)
+#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04
+#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08
+#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80
+#define PKT_AVI_CONTENTS1 0xbe4
+#define PKT_AVI_CONTENTS2 0xbe8
+#define PKT_AVI_CONTENTS3 0xbec
+#define PKT_AVI_CONTENTS4 0xbf0
+#define PKT_AVI_CONTENTS5 0xbf4
+#define PKT_AVI_CONTENTS6 0xbf8
+#define PKT_AVI_CONTENTS7 0xbfc
+#define PKT_SPDI_CONTENTS0 0xc00
+#define PKT_SPDI_CONTENTS1 0xc04
+#define PKT_SPDI_CONTENTS2 0xc08
+#define PKT_SPDI_CONTENTS3 0xc0c
+#define PKT_SPDI_CONTENTS4 0xc10
+#define PKT_SPDI_CONTENTS5 0xc14
+#define PKT_SPDI_CONTENTS6 0xc18
+#define PKT_SPDI_CONTENTS7 0xc1c
+#define PKT_AUDI_CONTENTS0 0xc20
+#define PKT_AUDI_CONTENTS1 0xc24
+#define PKT_AUDI_CONTENTS2 0xc28
+#define PKT_AUDI_CONTENTS3 0xc2c
+#define PKT_AUDI_CONTENTS4 0xc30
+#define PKT_AUDI_CONTENTS5 0xc34
+#define PKT_AUDI_CONTENTS6 0xc38
+#define PKT_AUDI_CONTENTS7 0xc3c
+#define PKT_NVI_CONTENTS0 0xc40
+#define PKT_NVI_CONTENTS1 0xc44
+#define PKT_NVI_CONTENTS2 0xc48
+#define PKT_NVI_CONTENTS3 0xc4c
+#define PKT_NVI_CONTENTS4 0xc50
+#define PKT_NVI_CONTENTS5 0xc54
+#define PKT_NVI_CONTENTS6 0xc58
+#define PKT_NVI_CONTENTS7 0xc5c
+#define PKT_DRMI_CONTENTS0 0xc60
+#define PKT_DRMI_CONTENTS1 0xc64
+#define PKT_DRMI_CONTENTS2 0xc68
+#define PKT_DRMI_CONTENTS3 0xc6c
+#define PKT_DRMI_CONTENTS4 0xc70
+#define PKT_DRMI_CONTENTS5 0xc74
+#define PKT_DRMI_CONTENTS6 0xc78
+#define PKT_DRMI_CONTENTS7 0xc7c
+#define PKT_GHDMI1_CONTENTS0 0xc80
+#define PKT_GHDMI1_CONTENTS1 0xc84
+#define PKT_GHDMI1_CONTENTS2 0xc88
+#define PKT_GHDMI1_CONTENTS3 0xc8c
+#define PKT_GHDMI1_CONTENTS4 0xc90
+#define PKT_GHDMI1_CONTENTS5 0xc94
+#define PKT_GHDMI1_CONTENTS6 0xc98
+#define PKT_GHDMI1_CONTENTS7 0xc9c
+#define PKT_GHDMI2_CONTENTS0 0xca0
+#define PKT_GHDMI2_CONTENTS1 0xca4
+#define PKT_GHDMI2_CONTENTS2 0xca8
+#define PKT_GHDMI2_CONTENTS3 0xcac
+#define PKT_GHDMI2_CONTENTS4 0xcb0
+#define PKT_GHDMI2_CONTENTS5 0xcb4
+#define PKT_GHDMI2_CONTENTS6 0xcb8
+#define PKT_GHDMI2_CONTENTS7 0xcbc
+/* EMP Packetizer Registers */
+#define PKT_EMP_CONFIG0 0xce0
+#define PKT_EMP_CONTROL0 0xcec
+#define PKT_EMP_CONTROL1 0xcf0
+#define PKT_EMP_CONTROL2 0xcf4
+#define PKT_EMP_VTEM_CONTENTS0 0xd00
+#define PKT_EMP_VTEM_CONTENTS1 0xd04
+#define PKT_EMP_VTEM_CONTENTS2 0xd08
+#define PKT_EMP_VTEM_CONTENTS3 0xd0c
+#define PKT_EMP_VTEM_CONTENTS4 0xd10
+#define PKT_EMP_VTEM_CONTENTS5 0xd14
+#define PKT_EMP_VTEM_CONTENTS6 0xd18
+#define PKT_EMP_VTEM_CONTENTS7 0xd1c
+#define PKT0_EMP_CVTEM_CONTENTS0 0xd20
+#define PKT0_EMP_CVTEM_CONTENTS1 0xd24
+#define PKT0_EMP_CVTEM_CONTENTS2 0xd28
+#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c
+#define PKT0_EMP_CVTEM_CONTENTS4 0xd30
+#define PKT0_EMP_CVTEM_CONTENTS5 0xd34
+#define PKT0_EMP_CVTEM_CONTENTS6 0xd38
+#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c
+#define PKT1_EMP_CVTEM_CONTENTS0 0xd40
+#define PKT1_EMP_CVTEM_CONTENTS1 0xd44
+#define PKT1_EMP_CVTEM_CONTENTS2 0xd48
+#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c
+#define PKT1_EMP_CVTEM_CONTENTS4 0xd50
+#define PKT1_EMP_CVTEM_CONTENTS5 0xd54
+#define PKT1_EMP_CVTEM_CONTENTS6 0xd58
+#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c
+#define PKT2_EMP_CVTEM_CONTENTS0 0xd60
+#define PKT2_EMP_CVTEM_CONTENTS1 0xd64
+#define PKT2_EMP_CVTEM_CONTENTS2 0xd68
+#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c
+#define PKT2_EMP_CVTEM_CONTENTS4 0xd70
+#define PKT2_EMP_CVTEM_CONTENTS5 0xd74
+#define PKT2_EMP_CVTEM_CONTENTS6 0xd78
+#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c
+#define PKT3_EMP_CVTEM_CONTENTS0 0xd80
+#define PKT3_EMP_CVTEM_CONTENTS1 0xd84
+#define PKT3_EMP_CVTEM_CONTENTS2 0xd88
+#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c
+#define PKT3_EMP_CVTEM_CONTENTS4 0xd90
+#define PKT3_EMP_CVTEM_CONTENTS5 0xd94
+#define PKT3_EMP_CVTEM_CONTENTS6 0xd98
+#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c
+#define PKT4_EMP_CVTEM_CONTENTS0 0xda0
+#define PKT4_EMP_CVTEM_CONTENTS1 0xda4
+#define PKT4_EMP_CVTEM_CONTENTS2 0xda8
+#define PKT4_EMP_CVTEM_CONTENTS3 0xdac
+#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0
+#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4
+#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8
+#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc
+#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0
+#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4
+#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8
+#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc
+#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0
+#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4
+#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8
+#define PKT5_EMP_CVTEM_CONTENTS7 0xddc
+/* Audio Packetizer Registers */
+#define AUDPKT_CONTROL0 0xe20
+#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12)
+#define AUDPKT_PBIT_FORCE_EN BIT(12)
+#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0)
+#define AUDPKT_CHSTATUS_OVR_EN BIT(0)
+#define AUDPKT_CONTROL1 0xe24
+#define AUDPKT_ACR_CONTROL0 0xe40
+#define AUDPKT_ACR_N_VALUE 0xfffff
+#define AUDPKT_ACR_CONTROL1 0xe44
+#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4)
+#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4)
+#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1)
+#define AUDPKT_ACR_CTS_OVR_EN BIT(1)
+#define AUDPKT_ACR_STATUS0 0xe4c
+#define AUDPKT_CHSTATUS_OVR0 0xe60
+#define AUDPKT_CHSTATUS_OVR1 0xe64
+/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */
+#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0)
+#define AUDPKT_CHSTATUS_SR_22050 0x4
+#define AUDPKT_CHSTATUS_SR_24000 0x6
+#define AUDPKT_CHSTATUS_SR_32000 0x3
+#define AUDPKT_CHSTATUS_SR_44100 0x0
+#define AUDPKT_CHSTATUS_SR_48000 0x2
+#define AUDPKT_CHSTATUS_SR_88200 0x8
+#define AUDPKT_CHSTATUS_SR_96000 0xa
+#define AUDPKT_CHSTATUS_SR_176400 0xc
+#define AUDPKT_CHSTATUS_SR_192000 0xe
+#define AUDPKT_CHSTATUS_SR_768000 0x9
+#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1
+/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */
+#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12)
+#define AUDPKT_CHSTATUS_OSR_8000 0x6
+#define AUDPKT_CHSTATUS_OSR_11025 0xa
+#define AUDPKT_CHSTATUS_OSR_12000 0x2
+#define AUDPKT_CHSTATUS_OSR_16000 0x8
+#define AUDPKT_CHSTATUS_OSR_22050 0xb
+#define AUDPKT_CHSTATUS_OSR_24000 0x9
+#define AUDPKT_CHSTATUS_OSR_32000 0xc
+#define AUDPKT_CHSTATUS_OSR_44100 0xf
+#define AUDPKT_CHSTATUS_OSR_48000 0xd
+#define AUDPKT_CHSTATUS_OSR_88200 0x7
+#define AUDPKT_CHSTATUS_OSR_96000 0x5
+#define AUDPKT_CHSTATUS_OSR_176400 0x3
+#define AUDPKT_CHSTATUS_OSR_192000 0x1
+#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0
+#define AUDPKT_CHSTATUS_OVR2 0xe68
+#define AUDPKT_CHSTATUS_OVR3 0xe6c
+#define AUDPKT_CHSTATUS_OVR4 0xe70
+#define AUDPKT_CHSTATUS_OVR5 0xe74
+#define AUDPKT_CHSTATUS_OVR6 0xe78
+#define AUDPKT_CHSTATUS_OVR7 0xe7c
+#define AUDPKT_CHSTATUS_OVR8 0xe80
+#define AUDPKT_CHSTATUS_OVR9 0xe84
+#define AUDPKT_CHSTATUS_OVR10 0xe88
+#define AUDPKT_CHSTATUS_OVR11 0xe8c
+#define AUDPKT_CHSTATUS_OVR12 0xe90
+#define AUDPKT_CHSTATUS_OVR13 0xe94
+#define AUDPKT_CHSTATUS_OVR14 0xe98
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20
+#define AUDPKT_VBIT_OVR0 0xf24
+/* CEC Registers */
+#define CEC_TX_CONTROL 0x1000
+#define CEC_STATUS 0x1004
+#define CEC_CONFIG 0x1008
+#define CEC_ADDR 0x100c
+#define CEC_TX_COUNT 0x1020
+#define CEC_TX_DATA3_0 0x1024
+#define CEC_TX_DATA7_4 0x1028
+#define CEC_TX_DATA11_8 0x102c
+#define CEC_TX_DATA15_12 0x1030
+#define CEC_RX_COUNT_STATUS 0x1040
+#define CEC_RX_DATA3_0 0x1044
+#define CEC_RX_DATA7_4 0x1048
+#define CEC_RX_DATA11_8 0x104c
+#define CEC_RX_DATA15_12 0x1050
+#define CEC_LOCK_CONTROL 0x1054
+#define CEC_RXQUAL_BITTIME_CONFIG 0x1060
+#define CEC_RX_BITTIME_CONFIG 0x1064
+#define CEC_TX_BITTIME_CONFIG 0x1068
+/* eARC RX CMDC Registers */
+#define EARCRX_CMDC_CONFIG0 0x1800
+#define EARCRX_XACTREAD_STOP_CFG BIT(26)
+#define EARCRX_XACTREAD_RETRY_CFG BIT(25)
+#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24)
+#define EARCRX_CMDC_XACT_RESTART_EN BIT(18)
+#define EARCRX_CMDC_CONFIG1 0x1804
+#define EARCRX_CMDC_CONTROL 0x1808
+#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4)
+#define EARCRX_CMDC_DISCOVERY_EN BIT(3)
+#define EARCRX_CONNECTOR_HPD BIT(1)
+#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c
+#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810
+#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814
+#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818
+#define EARCRX_CMDC_STATUS 0x181c
+#define EARCRX_CMDC_XACT_INFO 0x1820
+#define EARCRX_CMDC_XACT_ACTION 0x1824
+#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828
+#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c
+#define EARCRX_CMDC_XACT_WR0 0x1840
+#define EARCRX_CMDC_XACT_WR1 0x1844
+#define EARCRX_CMDC_XACT_WR2 0x1848
+#define EARCRX_CMDC_XACT_WR3 0x184c
+#define EARCRX_CMDC_XACT_WR4 0x1850
+#define EARCRX_CMDC_XACT_WR5 0x1854
+#define EARCRX_CMDC_XACT_WR6 0x1858
+#define EARCRX_CMDC_XACT_WR7 0x185c
+#define EARCRX_CMDC_XACT_WR8 0x1860
+#define EARCRX_CMDC_XACT_WR9 0x1864
+#define EARCRX_CMDC_XACT_WR10 0x1868
+#define EARCRX_CMDC_XACT_WR11 0x186c
+#define EARCRX_CMDC_XACT_WR12 0x1870
+#define EARCRX_CMDC_XACT_WR13 0x1874
+#define EARCRX_CMDC_XACT_WR14 0x1878
+#define EARCRX_CMDC_XACT_WR15 0x187c
+#define EARCRX_CMDC_XACT_WR16 0x1880
+#define EARCRX_CMDC_XACT_WR17 0x1884
+#define EARCRX_CMDC_XACT_WR18 0x1888
+#define EARCRX_CMDC_XACT_WR19 0x188c
+#define EARCRX_CMDC_XACT_WR20 0x1890
+#define EARCRX_CMDC_XACT_WR21 0x1894
+#define EARCRX_CMDC_XACT_WR22 0x1898
+#define EARCRX_CMDC_XACT_WR23 0x189c
+#define EARCRX_CMDC_XACT_WR24 0x18a0
+#define EARCRX_CMDC_XACT_WR25 0x18a4
+#define EARCRX_CMDC_XACT_WR26 0x18a8
+#define EARCRX_CMDC_XACT_WR27 0x18ac
+#define EARCRX_CMDC_XACT_WR28 0x18b0
+#define EARCRX_CMDC_XACT_WR29 0x18b4
+#define EARCRX_CMDC_XACT_WR30 0x18b8
+#define EARCRX_CMDC_XACT_WR31 0x18bc
+#define EARCRX_CMDC_XACT_WR32 0x18c0
+#define EARCRX_CMDC_XACT_WR33 0x18c4
+#define EARCRX_CMDC_XACT_WR34 0x18c8
+#define EARCRX_CMDC_XACT_WR35 0x18cc
+#define EARCRX_CMDC_XACT_WR36 0x18d0
+#define EARCRX_CMDC_XACT_WR37 0x18d4
+#define EARCRX_CMDC_XACT_WR38 0x18d8
+#define EARCRX_CMDC_XACT_WR39 0x18dc
+#define EARCRX_CMDC_XACT_WR40 0x18e0
+#define EARCRX_CMDC_XACT_WR41 0x18e4
+#define EARCRX_CMDC_XACT_WR42 0x18e8
+#define EARCRX_CMDC_XACT_WR43 0x18ec
+#define EARCRX_CMDC_XACT_WR44 0x18f0
+#define EARCRX_CMDC_XACT_WR45 0x18f4
+#define EARCRX_CMDC_XACT_WR46 0x18f8
+#define EARCRX_CMDC_XACT_WR47 0x18fc
+#define EARCRX_CMDC_XACT_WR48 0x1900
+#define EARCRX_CMDC_XACT_WR49 0x1904
+#define EARCRX_CMDC_XACT_WR50 0x1908
+#define EARCRX_CMDC_XACT_WR51 0x190c
+#define EARCRX_CMDC_XACT_WR52 0x1910
+#define EARCRX_CMDC_XACT_WR53 0x1914
+#define EARCRX_CMDC_XACT_WR54 0x1918
+#define EARCRX_CMDC_XACT_WR55 0x191c
+#define EARCRX_CMDC_XACT_WR56 0x1920
+#define EARCRX_CMDC_XACT_WR57 0x1924
+#define EARCRX_CMDC_XACT_WR58 0x1928
+#define EARCRX_CMDC_XACT_WR59 0x192c
+#define EARCRX_CMDC_XACT_WR60 0x1930
+#define EARCRX_CMDC_XACT_WR61 0x1934
+#define EARCRX_CMDC_XACT_WR62 0x1938
+#define EARCRX_CMDC_XACT_WR63 0x193c
+#define EARCRX_CMDC_XACT_WR64 0x1940
+#define EARCRX_CMDC_XACT_RD0 0x1960
+#define EARCRX_CMDC_XACT_RD1 0x1964
+#define EARCRX_CMDC_XACT_RD2 0x1968
+#define EARCRX_CMDC_XACT_RD3 0x196c
+#define EARCRX_CMDC_XACT_RD4 0x1970
+#define EARCRX_CMDC_XACT_RD5 0x1974
+#define EARCRX_CMDC_XACT_RD6 0x1978
+#define EARCRX_CMDC_XACT_RD7 0x197c
+#define EARCRX_CMDC_XACT_RD8 0x1980
+#define EARCRX_CMDC_XACT_RD9 0x1984
+#define EARCRX_CMDC_XACT_RD10 0x1988
+#define EARCRX_CMDC_XACT_RD11 0x198c
+#define EARCRX_CMDC_XACT_RD12 0x1990
+#define EARCRX_CMDC_XACT_RD13 0x1994
+#define EARCRX_CMDC_XACT_RD14 0x1998
+#define EARCRX_CMDC_XACT_RD15 0x199c
+#define EARCRX_CMDC_XACT_RD16 0x19a0
+#define EARCRX_CMDC_XACT_RD17 0x19a4
+#define EARCRX_CMDC_XACT_RD18 0x19a8
+#define EARCRX_CMDC_XACT_RD19 0x19ac
+#define EARCRX_CMDC_XACT_RD20 0x19b0
+#define EARCRX_CMDC_XACT_RD21 0x19b4
+#define EARCRX_CMDC_XACT_RD22 0x19b8
+#define EARCRX_CMDC_XACT_RD23 0x19bc
+#define EARCRX_CMDC_XACT_RD24 0x19c0
+#define EARCRX_CMDC_XACT_RD25 0x19c4
+#define EARCRX_CMDC_XACT_RD26 0x19c8
+#define EARCRX_CMDC_XACT_RD27 0x19cc
+#define EARCRX_CMDC_XACT_RD28 0x19d0
+#define EARCRX_CMDC_XACT_RD29 0x19d4
+#define EARCRX_CMDC_XACT_RD30 0x19d8
+#define EARCRX_CMDC_XACT_RD31 0x19dc
+#define EARCRX_CMDC_XACT_RD32 0x19e0
+#define EARCRX_CMDC_XACT_RD33 0x19e4
+#define EARCRX_CMDC_XACT_RD34 0x19e8
+#define EARCRX_CMDC_XACT_RD35 0x19ec
+#define EARCRX_CMDC_XACT_RD36 0x19f0
+#define EARCRX_CMDC_XACT_RD37 0x19f4
+#define EARCRX_CMDC_XACT_RD38 0x19f8
+#define EARCRX_CMDC_XACT_RD39 0x19fc
+#define EARCRX_CMDC_XACT_RD40 0x1a00
+#define EARCRX_CMDC_XACT_RD41 0x1a04
+#define EARCRX_CMDC_XACT_RD42 0x1a08
+#define EARCRX_CMDC_XACT_RD43 0x1a0c
+#define EARCRX_CMDC_XACT_RD44 0x1a10
+#define EARCRX_CMDC_XACT_RD45 0x1a14
+#define EARCRX_CMDC_XACT_RD46 0x1a18
+#define EARCRX_CMDC_XACT_RD47 0x1a1c
+#define EARCRX_CMDC_XACT_RD48 0x1a20
+#define EARCRX_CMDC_XACT_RD49 0x1a24
+#define EARCRX_CMDC_XACT_RD50 0x1a28
+#define EARCRX_CMDC_XACT_RD51 0x1a2c
+#define EARCRX_CMDC_XACT_RD52 0x1a30
+#define EARCRX_CMDC_XACT_RD53 0x1a34
+#define EARCRX_CMDC_XACT_RD54 0x1a38
+#define EARCRX_CMDC_XACT_RD55 0x1a3c
+#define EARCRX_CMDC_XACT_RD56 0x1a40
+#define EARCRX_CMDC_XACT_RD57 0x1a44
+#define EARCRX_CMDC_XACT_RD58 0x1a48
+#define EARCRX_CMDC_XACT_RD59 0x1a4c
+#define EARCRX_CMDC_XACT_RD60 0x1a50
+#define EARCRX_CMDC_XACT_RD61 0x1a54
+#define EARCRX_CMDC_XACT_RD62 0x1a58
+#define EARCRX_CMDC_XACT_RD63 0x1a5c
+#define EARCRX_CMDC_XACT_RD64 0x1a60
+#define EARCRX_CMDC_SYNC_CONFIG 0x1b00
+/* eARC RX DMAC Registers */
+#define EARCRX_DMAC_PHY_CONTROL 0x1c00
+#define EARCRX_DMAC_CONFIG 0x1c08
+#define EARCRX_DMAC_CONTROL0 0x1c0c
+#define EARCRX_DMAC_AUDIO_EN BIT(1)
+#define EARCRX_DMAC_EN BIT(0)
+#define EARCRX_DMAC_CONTROL1 0x1c10
+#define EARCRX_DMAC_STATUS 0x1c14
+#define EARCRX_DMAC_CHSTATUS0 0x1c18
+#define EARCRX_DMAC_CHSTATUS1 0x1c1c
+#define EARCRX_DMAC_CHSTATUS2 0x1c20
+#define EARCRX_DMAC_CHSTATUS3 0x1c24
+#define EARCRX_DMAC_CHSTATUS4 0x1c28
+#define EARCRX_DMAC_CHSTATUS5 0x1c2c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40
+#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44
+#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48
+#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c
+#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50
+#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54
+#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58
+#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c
+#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60
+#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64
+#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68
+#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c
+#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70
+#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74
+#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78
+#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c
+#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80
+/* Main Unit Interrupt Registers */
+#define MAIN_INTVEC_INDEX 0x3000
+#define MAINUNIT_0_INT_STATUS 0x3010
+#define MAINUNIT_0_INT_MASK_N 0x3014
+#define MAINUNIT_0_INT_CLEAR 0x3018
+#define MAINUNIT_0_INT_FORCE 0x301c
+#define MAINUNIT_1_INT_STATUS 0x3020
+#define FLT_EXIT_TO_LTSL_IRQ BIT(22)
+#define FLT_EXIT_TO_LTS4_IRQ BIT(21)
+#define FLT_EXIT_TO_LTSP_IRQ BIT(20)
+#define SCDC_NACK_RCVD_IRQ BIT(12)
+#define SCDC_RR_REPLY_STOP_IRQ BIT(11)
+#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10)
+#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9)
+#define SCDC_UPD_FLAGS_RD_IRQ BIT(8)
+#define I2CM_NACK_RCVD_IRQ BIT(2)
+#define I2CM_READ_REQUEST_IRQ BIT(1)
+#define I2CM_OP_DONE_IRQ BIT(0)
+#define MAINUNIT_1_INT_MASK_N 0x3024
+#define I2CM_NACK_RCVD_MASK_N BIT(2)
+#define I2CM_READ_REQUEST_MASK_N BIT(1)
+#define I2CM_OP_DONE_MASK_N BIT(0)
+#define MAINUNIT_1_INT_CLEAR 0x3028
+#define I2CM_NACK_RCVD_CLEAR BIT(2)
+#define I2CM_READ_REQUEST_CLEAR BIT(1)
+#define I2CM_OP_DONE_CLEAR BIT(0)
+#define MAINUNIT_1_INT_FORCE 0x302c
+/* AVPUNIT Interrupt Registers */
+#define AVP_INTVEC_INDEX 0x3800
+#define AVP_0_INT_STATUS 0x3810
+#define AVP_0_INT_MASK_N 0x3814
+#define AVP_0_INT_CLEAR 0x3818
+#define AVP_0_INT_FORCE 0x381c
+#define AVP_1_INT_STATUS 0x3820
+#define AVP_1_INT_MASK_N 0x3824
+#define HDCP14_AUTH_CHG_MASK_N BIT(6)
+#define AVP_1_INT_CLEAR 0x3828
+#define AVP_1_INT_FORCE 0x382c
+#define AVP_2_INT_STATUS 0x3830
+#define AVP_2_INT_MASK_N 0x3834
+#define AVP_2_INT_CLEAR 0x3838
+#define AVP_2_INT_FORCE 0x383c
+#define AVP_3_INT_STATUS 0x3840
+#define AVP_3_INT_MASK_N 0x3844
+#define AVP_3_INT_CLEAR 0x3848
+#define AVP_3_INT_FORCE 0x384c
+#define AVP_4_INT_STATUS 0x3850
+#define AVP_4_INT_MASK_N 0x3854
+#define AVP_4_INT_CLEAR 0x3858
+#define AVP_4_INT_FORCE 0x385c
+#define AVP_5_INT_STATUS 0x3860
+#define AVP_5_INT_MASK_N 0x3864
+#define AVP_5_INT_CLEAR 0x3868
+#define AVP_5_INT_FORCE 0x386c
+#define AVP_6_INT_STATUS 0x3870
+#define AVP_6_INT_MASK_N 0x3874
+#define AVP_6_INT_CLEAR 0x3878
+#define AVP_6_INT_FORCE 0x387c
+/* CEC Interrupt Registers */
+#define CEC_INT_STATUS 0x4000
+#define CEC_INT_MASK_N 0x4004
+#define CEC_INT_CLEAR 0x4008
+#define CEC_INT_FORCE 0x400c
+/* eARC RX Interrupt Registers */
+#define EARCRX_INTVEC_INDEX 0x4800
+#define EARCRX_0_INT_STATUS 0x4810
+#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9)
+#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8)
+#define EARCRX_0_INT_MASK_N 0x4814
+#define EARCRX_0_INT_CLEAR 0x4818
+#define EARCRX_0_INT_FORCE 0x481c
+#define EARCRX_1_INT_STATUS 0x4820
+#define EARCRX_1_INT_MASK_N 0x4824
+#define EARCRX_1_INT_CLEAR 0x4828
+#define EARCRX_1_INT_FORCE 0x482c
+
+#endif /* __DW_HDMI_QP_H__ */
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 0031f3c54882..996733ed2c00 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3503,6 +3503,9 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ if (hdmi->version >= 0x200a)
+ hdmi->bridge.ycbcr_420_allowed = plat_data->ycbcr_420_allowed;
+
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 0c48e747f5b0..d52d55e06be6 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -1707,7 +1707,7 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge,
{
struct tc_data *tc = bridge_to_tc(bridge);
- drm_mode_copy(&tc->mode, mode);
+ drm_mode_copy(&tc->mode, adj);
}
static const struct drm_edid *tc_edid_read(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 0e8813278a2f..fc96fa5aab54 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -443,7 +443,9 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
ret = -EINVAL;
ep = of_graph_get_endpoint_by_regs(host->dev->of_node, 0, 0);
if (ep) {
- ret = of_property_read_u32(ep, "data-lines", &priv->pd_lines);
+ ret = of_property_read_u32(ep, "bus-width", &priv->pd_lines);
+ if (ret)
+ ret = of_property_read_u32(ep, "data-lines", &priv->pd_lines);
of_node_put(ep);
}
diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c
index 6b559e071301..a0a1b5dd794e 100644
--- a/drivers/gpu/drm/bridge/ti-dlpc3433.c
+++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c
@@ -94,7 +94,7 @@ static const struct regmap_access_table dlpc_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges),
};
-static struct regmap_config dlpc_regmap_config = {
+static const struct regmap_config dlpc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = WR_DSI_PORT_EN,
diff --git a/drivers/gpu/drm/ci/arm64.config b/drivers/gpu/drm/ci/arm64.config
index 66e70ced796f..a8fca079921b 100644
--- a/drivers/gpu/drm/ci/arm64.config
+++ b/drivers/gpu/drm/ci/arm64.config
@@ -90,7 +90,12 @@ CONFIG_QCOM_GPI_DMA=y
CONFIG_USB_ONBOARD_DEV=y
CONFIG_NVMEM_QCOM_QFPROM=y
CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y
-
+CONFIG_REGULATOR_QCOM_REFGEN=y
+CONFIG_TYPEC_MUX_FSA4480=y
+CONFIG_QCOM_PMIC_GLINK=y
+CONFIG_UCSI_PMIC_GLINK=y
+CONFIG_QRTR=y
+CONFIG_QRTR_SMD=y
# db410c ethernet
CONFIG_USB_RTL8152=y
diff --git a/drivers/gpu/drm/ci/build.sh b/drivers/gpu/drm/ci/build.sh
index 5a3bdcffae32..139b81db6312 100644
--- a/drivers/gpu/drm/ci/build.sh
+++ b/drivers/gpu/drm/ci/build.sh
@@ -30,6 +30,7 @@ if [[ "$KERNEL_ARCH" = "arm64" ]]; then
DEVICE_TREES+=" arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dtb"
DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sc7180-trogdor-lazor-limozeen-nots-r5.dtb"
DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sc7180-trogdor-kingoftown.dtb"
+ DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sm8350-hdk.dtb"
elif [[ "$KERNEL_ARCH" = "arm" ]]; then
GCC_ARCH="arm-linux-gnueabihf"
DEBIAN_ARCH="armhf"
diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml
index 09d8447840e9..f0ef60c8f56d 100644
--- a/drivers/gpu/drm/ci/test.yml
+++ b/drivers/gpu/drm/ci/test.yml
@@ -162,6 +162,22 @@ msm:sdm845:
script:
- ./install/bare-metal/cros-servo.sh
+msm:sm8350-hdk:
+ extends:
+ - .lava-igt:arm64
+ stage: msm
+ parallel: 4
+ variables:
+ BOOT_METHOD: fastboot
+ DEVICE_TYPE: sm8350-hdk
+ DRIVER_NAME: msm
+ DTB: ${DEVICE_TYPE}
+ FARM: collabora
+ GPU_VERSION: ${DEVICE_TYPE}
+ KERNEL_IMAGE_NAME: "Image.gz"
+ KERNEL_IMAGE_TYPE: ""
+ RUNNER_TAG: mesa-ci-x86-64-lava-sm8350-hdk
+
.rockchip-device:
variables:
DTB: ${DEVICE_TYPE}
@@ -286,6 +302,15 @@ i915:tgl:
GPU_VERSION: tgl
RUNNER_TAG: mesa-ci-x86-64-lava-acer-cp514-2h-1130g7-volteer
+i915:jsl:
+ extends:
+ - .i915
+ parallel: 4
+ variables:
+ DEVICE_TYPE: acer-cb317-1h-c3z6-dedede
+ GPU_VERSION: jsl
+ RUNNER_TAG: mesa-ci-x86-64-lava-acer-cb317-1h-c3z6-dedede
+
.amdgpu:
extends:
- .lava-igt:x86_64
diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt
new file mode 100644
index 000000000000..ed9f7b576843
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt
@@ -0,0 +1,51 @@
+core_setmaster@master-drop-set-user,Fail
+i915_module_load@load,Fail
+i915_module_load@reload,Fail
+i915_module_load@reload-no-display,Fail
+i915_module_load@resize-bar,Fail
+i915_pm_rpm@gem-execbuf-stress,Timeout
+i915_pm_rpm@module-reload,Fail
+kms_flip@plain-flip-fb-recreate,Fail
+kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail
+kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail
+kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail
+kms_lease@lease-uevent,Fail
+kms_pm_rpm@legacy-planes,Timeout
+kms_pm_rpm@legacy-planes-dpms,Timeout
+kms_pm_rpm@modeset-stress-extra-wait,Timeout
+kms_pm_rpm@universal-planes,Timeout
+kms_pm_rpm@universal-planes-dpms,Timeout
+kms_rotation_crc@multiplane-rotation,Fail
+kms_rotation_crc@multiplane-rotation-cropping-bottom,Fail
+kms_rotation_crc@multiplane-rotation-cropping-top,Fail
+perf@i915-ref-count,Fail
+perf_pmu@busy-accuracy-50,Fail
+perf_pmu@module-unload,Fail
+perf_pmu@most-busy-idle-check-all,Fail
+perf_pmu@rc6,Crash
+sysfs_heartbeat_interval@long,Timeout
+sysfs_heartbeat_interval@off,Timeout
+sysfs_preempt_timeout@off,Timeout
+sysfs_timeslice_duration@off,Timeout
+xe_module_load@force-load,Fail
+xe_module_load@load,Fail
+xe_module_load@many-reload,Fail
+xe_module_load@reload,Fail
+xe_module_load@reload-no-display,Fail
diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt
new file mode 100644
index 000000000000..5c3ef4486b9d
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt
@@ -0,0 +1,13 @@
+# Board Name: acer-cb317-1h-c3z6-dedede
+# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12475
+# Failure Rate: 100
+# IGT Version: 1.28-ga73311079
+# Linux Version: 6.12.0-rc1
+kms_flip@flip-vs-panning-interruptible
+
+# Board Name: acer-cb317-1h-c3z6-dedede
+# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12476
+# Failure Rate: 100
+# IGT Version: 1.28-ga73311079
+# Linux Version: 6.12.0-rc1
+kms_universal_plane@cursor-fb-leak
diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt
new file mode 100644
index 000000000000..1a3d87c0ca6e
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt
@@ -0,0 +1,20 @@
+# Suspend to RAM seems to be broken on this machine
+.*suspend.*
+
+# Skip driver specific tests
+^amdgpu.*
+^msm.*
+nouveau_.*
+^panfrost.*
+^v3d.*
+^vc4.*
+^vmwgfx*
+
+# GEM tests takes ~1000 hours, so skip it
+gem_.*
+
+# trap_err
+i915_pm_rc6_residency.*
+
+# Hangs the machine and timeout occurs
+i915_pm_rpm@system-hibernate*
diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt
new file mode 100644
index 000000000000..4892c0c70a6d
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt
@@ -0,0 +1,15 @@
+kms_3d,Fail
+kms_cursor_legacy@forked-bo,Fail
+kms_cursor_legacy@forked-move,Fail
+kms_cursor_legacy@single-bo,Fail
+kms_cursor_legacy@single-move,Fail
+kms_cursor_legacy@torture-bo,Fail
+kms_cursor_legacy@torture-move,Fail
+kms_hdmi_inject@inject-4k,Fail
+kms_lease@lease-uevent,Fail
+kms_plane_alpha_blend@alpha-7efc,Fail
+kms_plane_alpha_blend@alpha-basic,Fail
+kms_plane_alpha_blend@alpha-opaque-fb,Fail
+kms_plane_alpha_blend@alpha-transparent-fb,Fail
+kms_plane_alpha_blend@constant-alpha-max,Fail
+msm/msm_recovery@gpu-fault-parallel,Fail
diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt
new file mode 100644
index 000000000000..c1859d9b165f
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt
@@ -0,0 +1,6 @@
+# Board Name: sm8350-hdk
+# Bug Report: https://gitlab.freedesktop.org/drm/msm/-/issues/65
+# Failure Rate: 100
+# IGT Version: 1.28-ga73311079
+# Linux Version: 6.12.0-rc1
+msm/msm_recovery@gpu-fault
diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt
new file mode 100644
index 000000000000..329770c520d9
--- /dev/null
+++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt
@@ -0,0 +1,211 @@
+# Skip driver specific tests
+^amdgpu.*
+nouveau_.*
+^panfrost.*
+^v3d.*
+^vc4.*
+^vmwgfx*
+
+# Skip intel specific tests
+gem_.*
+i915_.*
+tools_test.*
+
+# Currently fails and causes coverage loss for other tests
+# since core_getversion also fails.
+core_hotunplug.*
+
+# Kernel panic
+msm/msm_mapping@ring
+# DEBUG - Begin test msm/msm_mapping@ring
+# [ 200.874157] [IGT] msm_mapping: executing
+# [ 200.880236] [IGT] msm_mapping: starting subtest ring
+# [ 200.895243] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=PERMISSION source=CP (0,0,0,1)
+# [ 200.906885] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.917625] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.928353] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.939084] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.949815] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.950227] platform 3d6a000.gmu: [drm:a6xx_hfi_send_msg.constprop.0] *ERROR* Message HFI_H2F_MSG_GX_BW_PERF_VOTE id 25 timed out waiting for response
+# [ 200.960467] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.960500] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 200.995966] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 201.006702] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 204.213387] platform 3d6a000.gmu: GMU watchdog expired
+# [ 205.909103] adreno_fault_handler: 224274 callbacks suppressed
+# [ 205.909108] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.925794] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.936529] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.947263] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.957997] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.968731] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.979465] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 205.990199] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 206.000932] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 206.011666] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.925090] adreno_fault_handler: 224511 callbacks suppressed
+# [ 210.925096] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.941781] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.952517] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.963250] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.973985] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.984719] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 210.995452] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 211.006186] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 211.016921] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 211.027655] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.937100] adreno_fault_handler: 223760 callbacks suppressed
+# [ 215.937106] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.953824] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.964573] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.975321] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.986067] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 215.996815] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 216.007563] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 216.018310] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 216.029057] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 216.039805] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 220.945182] adreno_fault_handler: 222822 callbacks suppressed
+# [ 220.945188] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 220.961897] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 220.972645] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 220.983392] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 220.994140] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 221.004889] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 221.015636] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 221.026383] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 221.037130] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 221.047879] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 225.953179] adreno_fault_handler: 223373 callbacks suppressed
+# [ 225.953184] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 225.969883] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 225.980617] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 225.991350] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.002084] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.012818] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.023551] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.034285] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.045019] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 226.055753] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1)
+# [ 228.001087] rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
+# [ 228.007412] rcu: 0-....: (524 ticks this GP) idle=4ffc/1/0x4000000000000000 softirq=9367/9368 fqs=29
+# [ 228.017097] rcu: (detected by 1, t=6504 jiffies, g=29837, q=6 ncpus=8)
+# [ 228.023959] Sending NMI from CPU 1 to CPUs 0:
+# [ 228.161164] watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [gpu-worker:150]
+# [ 228.173169] Modules linked in:
+# [ 228.176361] irq event stamp: 2809595
+# [ 228.180083] hardirqs last enabled at (2809594): [<ffffd3bc52cb91ac>] exit_to_kernel_mode+0x38/0x130
+# [ 228.189547] hardirqs last disabled at (2809595): [<ffffd3bc52cb92c8>] el1_interrupt+0x24/0x64
+# [ 228.198377] softirqs last enabled at (1669060): [<ffffd3bc51936f98>] handle_softirqs+0x4a4/0x4bc
+# [ 228.207565] softirqs last disabled at (1669063): [<ffffd3bc518905a4>] __do_softirq+0x14/0x20
+# [ 228.216316] CPU: 0 UID: 0 PID: 150 Comm: gpu-worker Not tainted 6.12.0-rc1-g685d530dc83a #1
+# [ 228.224966] Hardware name: Qualcomm Technologies, Inc. SM8350 HDK (DT)
+# [ 228.231730] pstate: 00400005 (nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+# [ 228.238948] pc : tcp_fastretrans_alert+0x0/0x884
+# [ 228.243751] lr : tcp_ack+0x9d4/0x1238
+# [ 228.247562] sp : ffff8000800036d0
+# [ 228.251011] x29: ffff8000800036d0 x28: 000000000000000c x27: 0000000000000001
+# [ 228.258421] x26: ffff704683cd8000 x25: 0000000000000403 x24: ffff70468b7e7c00
+# [ 228.265829] x23: 0000000000000000 x22: 0000000000000004 x21: 000000000000140f
+# [ 228.273237] x20: 00000000f1de79f7 x19: 00000000f1de7a5f x18: 0000000000000001
+# [ 228.280644] x17: 00000000302d6762 x16: 632d6b64682d3035 x15: ffff704683c39000
+# [ 228.288051] x14: 00000000000e2000 x13: ffff704683df6000 x12: 0000000000000000
+# [ 228.295458] x11: 00000000000000a0 x10: 0000000000000000 x9 : ffffd3bc551a9a20
+# [ 228.302865] x8 : ffff800080003640 x7 : 0000000000040faa x6 : 00000000ffff9634
+# [ 228.310271] x5 : 00000000000005a8 x4 : ffff800080003788 x3 : ffff80008000377c
+# [ 228.317679] x2 : 0000000000000000 x1 : 00000000f1de79f7 x0 : ffff704683cd8000
+# [ 228.325087] Call trace:
+# [ 228.327640] tcp_fastretrans_alert+0x0/0x884
+# [ 228.332082] tcp_rcv_established+0x7c4/0x8bc
+# [ 228.336523] tcp_v4_do_rcv+0x244/0x31c
+# [ 228.340429] tcp_v4_rcv+0xcc4/0x1084
+# [ 228.344155] ip_protocol_deliver_rcu+0x64/0x218
+# [ 228.348862] ip_local_deliver_finish+0xb8/0x1ac
+# [ 228.353566] ip_local_deliver+0x84/0x254
+# [ 228.357651] ip_sublist_rcv_finish+0x84/0xb8
+# [ 228.362092] ip_sublist_rcv+0x11c/0x2f0
+# [ 228.366081] ip_list_rcv+0xfc/0x190
+# [ 228.369711] __netif_receive_skb_list_core+0x174/0x208
+# [ 228.375050] netif_receive_skb_list_internal+0x204/0x3ac
+# [ 228.380564] napi_complete_done+0x64/0x1d0
+# [ 228.384826] lan78xx_poll+0x71c/0x9cc
+# [ 228.388638] __napi_poll.constprop.0+0x3c/0x254
+# [ 228.393341] net_rx_action+0x164/0x2d4
+# [ 228.397244] handle_softirqs+0x128/0x4bc
+# [ 228.401329] __do_softirq+0x14/0x20
+# [ 228.404958] ____do_softirq+0x10/0x1c
+# [ 228.408769] call_on_irq_stack+0x24/0x4c
+# [ 228.412854] do_softirq_own_stack+0x1c/0x28
+# [ 228.417199] __irq_exit_rcu+0x124/0x164
+# [ 228.421188] irq_exit_rcu+0x10/0x38
+# [ 228.424819] el1_interrupt+0x38/0x64
+# [ 228.428546] el1h_64_irq_handler+0x18/0x24
+# [ 228.432807] el1h_64_irq+0x64/0x68
+# [ 228.436354] lock_acquire+0x214/0x32c
+# [ 228.440166] __mutex_lock+0x98/0x3d0
+# [ 228.443893] mutex_lock_nested+0x24/0x30
+# [ 228.447978] fault_worker+0x58/0x184
+# [ 228.451704] kthread_worker_fn+0xf4/0x320
+# [ 228.455873] kthread+0x114/0x118
+# [ 228.459243] ret_from_fork+0x10/0x20
+# [ 228.462970] Kernel panic - not syncing: softlockup: hung tasks
+# [ 228.469018] CPU: 0 UID: 0 PID: 150 Comm: gpu-worker Tainted: G L 6.12.0-rc1-g685d530dc83a #1
+# [ 228.479190] Tainted: [L]=SOFTLOCKUP
+# [ 228.482815] Hardware name: Qualcomm Technologies, Inc. SM8350 HDK (DT)
+# [ 228.489574] Call trace:
+# [ 228.492125] dump_backtrace+0x98/0xf0
+# [ 228.495931] show_stack+0x18/0x24
+# [ 228.499380] dump_stack_lvl+0x38/0xd0
+# [ 228.503189] dump_stack+0x18/0x24
+# [ 228.506639] panic+0x3bc/0x41c
+# [ 228.509826] watchdog_timer_fn+0x254/0x2e4
+# [ 228.514087] __hrtimer_run_queues+0x3b0/0x40c
+# [ 228.518612] hrtimer_interrupt+0xe8/0x248
+# [ 228.522777] arch_timer_handler_virt+0x2c/0x44
+# [ 228.527399] handle_percpu_devid_irq+0xa8/0x2c4
+# [ 228.532103] generic_handle_domain_irq+0x2c/0x44
+# [ 228.536902] gic_handle_irq+0x4c/0x11c
+# [ 228.540802] do_interrupt_handler+0x50/0x84
+# [ 228.545146] el1_interrupt+0x34/0x64
+# [ 228.548870] el1h_64_irq_handler+0x18/0x24
+# [ 228.553128] el1h_64_irq+0x64/0x68
+# [ 228.556672] tcp_fastretrans_alert+0x0/0x884
+# [ 228.561110] tcp_rcv_established+0x7c4/0x8bc
+# [ 228.565548] tcp_v4_do_rcv+0x244/0x31c
+# [ 228.569449] tcp_v4_rcv+0xcc4/0x1084
+# [ 228.573171] ip_protocol_deliver_rcu+0x64/0x218
+# [ 228.577873] ip_local_deliver_finish+0xb8/0x1ac
+# [ 228.582574] ip_local_deliver+0x84/0x254
+# [ 228.586655] ip_sublist_rcv_finish+0x84/0xb8
+# [ 228.591092] ip_sublist_rcv+0x11c/0x2f0
+# [ 228.595079] ip_list_rcv+0xfc/0x190
+# [ 228.598706] __netif_receive_skb_list_core+0x174/0x208
+# [ 228.604039] netif_receive_skb_list_internal+0x204/0x3ac
+# [ 228.609549] napi_complete_done+0x64/0x1d0
+# [ 228.613808] lan78xx_poll+0x71c/0x9cc
+# [ 228.617614] __napi_poll.constprop.0+0x3c/0x254
+# [ 228.622314] net_rx_action+0x164/0x2d4
+# [ 228.626214] handle_softirqs+0x128/0x4bc
+# [ 228.630297] __do_softirq+0x14/0x20
+# [ 228.633923] ____do_softirq+0x10/0x1c
+# [ 228.637729] call_on_irq_stack+0x24/0x4c
+# [ 228.641811] do_softirq_own_stack+0x1c/0x28
+# [ 228.646152] __irq_exit_rcu+0x124/0x164
+# [ 228.650139] irq_exit_rcu+0x10/0x38
+# [ 228.653768] el1_interrupt+0x38/0x64
+# [ 228.657491] el1h_64_irq_handler+0x18/0x24
+# [ 228.661750] el1h_64_irq+0x64/0x68
+# [ 228.665293] lock_acquire+0x214/0x32c
+# [ 228.669098] __mutex_lock+0x98/0x3d0
+# [ 228.672821] mutex_lock_nested+0x24/0x30
+# [ 228.676903] fault_worker+0x58/0x184
+# [ 228.680626] kthread_worker_fn+0xf4/0x320
+# [ 228.684790] kthread+0x114/0x118
+# [ 228.688156] ret_from_fork+0x10/0x20
+# [ 228.691882] SMP: stopping secondary CPUs
+# [ 229.736843] SMP: failed to stop secondary CPUs 1,4
+# [ 229.741827] Kernel Offset: 0x53bbd1880000 from 0xffff800080000000
+# [ 229.748159] PHYS_OFFSET: 0xfff08fba80000000
+# [ 229.752499] CPU features: 0x18,00000017,00200928,4200720b
+# [ 229.758095] Memory Limit: none
+# [ 229.761291] ---[ end Kernel panic - not syncing: softlockup: hung tasks ]---
diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig
index 0f07cf1483ff..6a4e892afcf8 100644
--- a/drivers/gpu/drm/display/Kconfig
+++ b/drivers/gpu/drm/display/Kconfig
@@ -3,7 +3,7 @@
config DRM_DISPLAY_DP_AUX_BUS
tristate
depends on DRM
- depends on OF || COMPILE_TEST
+ depends on OF
config DRM_DISPLAY_HELPER
tristate
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 3da5b8bf8259..320c297008aa 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -397,11 +397,11 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
bridge_connector->encoder = encoder;
/*
- * TODO: Handle doublescan_allowed, stereo_allowed and
- * ycbcr_420_allowed.
+ * TODO: Handle doublescan_allowed and stereo_allowed.
*/
connector = &bridge_connector->base;
connector->interlace_allowed = true;
+ connector->ycbcr_420_allowed = true;
/*
* Initialise connector status handling. First locate the furthest
@@ -414,6 +414,8 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
drm_for_each_bridge_in_chain(encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
+ if (!bridge->ycbcr_420_allowed)
+ connector->ycbcr_420_allowed = false;
if (bridge->ops & DRM_BRIDGE_OP_EDID)
bridge_connector->bridge_edid = bridge;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 0fc99da93afe..9ea2611770f4 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1132,6 +1132,8 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name);
drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)");
drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware);
+ drm_printf(p, "\tinterlace_allowed=%d\n", connector->interlace_allowed);
+ drm_printf(p, "\tycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed);
drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc);
drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace));
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index bfedcbf516db..549b28a5918c 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -10,7 +10,6 @@
#include <linux/slab.h>
#include <drm/drm_client.h>
-#include <drm/drm_debugfs.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
@@ -172,99 +171,6 @@ void drm_client_release(struct drm_client_dev *client)
}
EXPORT_SYMBOL(drm_client_release);
-/**
- * drm_client_dev_unregister - Unregister clients
- * @dev: DRM device
- *
- * This function releases all clients by calling each client's
- * &drm_client_funcs.unregister callback. The callback function
- * is responsibe for releaseing all resources including the client
- * itself.
- *
- * The helper drm_dev_unregister() calls this function. Drivers
- * that use it don't need to call this function themselves.
- */
-void drm_client_dev_unregister(struct drm_device *dev)
-{
- struct drm_client_dev *client, *tmp;
-
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return;
-
- mutex_lock(&dev->clientlist_mutex);
- list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
- list_del(&client->list);
- if (client->funcs && client->funcs->unregister) {
- client->funcs->unregister(client);
- } else {
- drm_client_release(client);
- kfree(client);
- }
- }
- mutex_unlock(&dev->clientlist_mutex);
-}
-EXPORT_SYMBOL(drm_client_dev_unregister);
-
-/**
- * drm_client_dev_hotplug - Send hotplug event to clients
- * @dev: DRM device
- *
- * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
- *
- * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
- * don't need to call this function themselves.
- */
-void drm_client_dev_hotplug(struct drm_device *dev)
-{
- struct drm_client_dev *client;
- int ret;
-
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return;
-
- if (!dev->mode_config.num_connector) {
- drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n");
- return;
- }
-
- mutex_lock(&dev->clientlist_mutex);
- list_for_each_entry(client, &dev->clientlist, list) {
- if (!client->funcs || !client->funcs->hotplug)
- continue;
-
- if (client->hotplug_failed)
- continue;
-
- ret = client->funcs->hotplug(client);
- drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
- if (ret)
- client->hotplug_failed = true;
- }
- mutex_unlock(&dev->clientlist_mutex);
-}
-EXPORT_SYMBOL(drm_client_dev_hotplug);
-
-void drm_client_dev_restore(struct drm_device *dev)
-{
- struct drm_client_dev *client;
- int ret;
-
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return;
-
- mutex_lock(&dev->clientlist_mutex);
- list_for_each_entry(client, &dev->clientlist, list) {
- if (!client->funcs || !client->funcs->restore)
- continue;
-
- ret = client->funcs->restore(client);
- drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
- if (!ret) /* The first one to return zero gets the privilege to restore */
- break;
- }
- mutex_unlock(&dev->clientlist_mutex);
-}
-
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
{
if (buffer->gem) {
@@ -584,30 +490,3 @@ int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_re
0, 0, NULL, 0);
}
EXPORT_SYMBOL(drm_client_framebuffer_flush);
-
-#ifdef CONFIG_DEBUG_FS
-static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
-{
- struct drm_debugfs_entry *entry = m->private;
- struct drm_device *dev = entry->dev;
- struct drm_printer p = drm_seq_file_printer(m);
- struct drm_client_dev *client;
-
- mutex_lock(&dev->clientlist_mutex);
- list_for_each_entry(client, &dev->clientlist, list)
- drm_printf(&p, "%s\n", client->name);
- mutex_unlock(&dev->clientlist_mutex);
-
- return 0;
-}
-
-static const struct drm_debugfs_info drm_client_debugfs_list[] = {
- { "internal_clients", drm_client_debugfs_internal_clients, 0 },
-};
-
-void drm_client_debugfs_init(struct drm_device *dev)
-{
- drm_debugfs_add_files(dev, drm_client_debugfs_list,
- ARRAY_SIZE(drm_client_debugfs_list));
-}
-#endif
diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c
new file mode 100644
index 000000000000..e303de564485
--- /dev/null
+++ b/drivers/gpu/drm/drm_client_event.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/*
+ * Copyright 2018 Noralf Trønnes
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
+
+#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+
+#include "drm_internal.h"
+
+/**
+ * drm_client_dev_unregister - Unregister clients
+ * @dev: DRM device
+ *
+ * This function releases all clients by calling each client's
+ * &drm_client_funcs.unregister callback. The callback function
+ * is responsibe for releaseing all resources including the client
+ * itself.
+ *
+ * The helper drm_dev_unregister() calls this function. Drivers
+ * that use it don't need to call this function themselves.
+ */
+void drm_client_dev_unregister(struct drm_device *dev)
+{
+ struct drm_client_dev *client, *tmp;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
+ list_del(&client->list);
+ if (client->funcs && client->funcs->unregister) {
+ client->funcs->unregister(client);
+ } else {
+ drm_client_release(client);
+ kfree(client);
+ }
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_unregister);
+
+/**
+ * drm_client_dev_hotplug - Send hotplug event to clients
+ * @dev: DRM device
+ *
+ * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
+ *
+ * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
+ * don't need to call this function themselves.
+ */
+void drm_client_dev_hotplug(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ if (!dev->mode_config.num_connector) {
+ drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n");
+ return;
+ }
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs || !client->funcs->hotplug)
+ continue;
+
+ if (client->hotplug_failed)
+ continue;
+
+ ret = client->funcs->hotplug(client);
+ drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
+ if (ret)
+ client->hotplug_failed = true;
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_hotplug);
+
+void drm_client_dev_restore(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs || !client->funcs->restore)
+ continue;
+
+ ret = client->funcs->restore(client);
+ drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
+ if (!ret) /* The first one to return zero gets the privilege to restore */
+ break;
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
+static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+{
+ struct drm_device *dev = client->dev;
+ int ret = 0;
+
+ if (drm_WARN_ON_ONCE(dev, client->suspended))
+ return 0;
+
+ if (client->funcs && client->funcs->suspend)
+ ret = client->funcs->suspend(client, holds_console_lock);
+ drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
+
+ client->suspended = true;
+
+ return ret;
+}
+
+void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
+{
+ struct drm_client_dev *client;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->suspended)
+ drm_client_suspend(client, holds_console_lock);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_suspend);
+
+static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+{
+ struct drm_device *dev = client->dev;
+ int ret = 0;
+
+ if (drm_WARN_ON_ONCE(dev, !client->suspended))
+ return 0;
+
+ if (client->funcs && client->funcs->resume)
+ ret = client->funcs->resume(client, holds_console_lock);
+ drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
+
+ client->suspended = false;
+
+ return ret;
+}
+
+void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
+{
+ struct drm_client_dev *client;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (client->suspended)
+ drm_client_resume(client, holds_console_lock);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_resume);
+
+#ifdef CONFIG_DEBUG_FS
+static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
+{
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *dev = entry->dev;
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_client_dev *client;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list)
+ drm_printf(&p, "%s\n", client->name);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ return 0;
+}
+
+static const struct drm_debugfs_info drm_client_debugfs_list[] = {
+ { "internal_clients", drm_client_debugfs_internal_clients, 0 },
+};
+
+void drm_client_debugfs_init(struct drm_device *dev)
+{
+ drm_debugfs_add_files(dev, drm_client_debugfs_list,
+ ARRAY_SIZE(drm_client_debugfs_list));
+}
+#endif
diff --git a/drivers/gpu/drm/drm_client_setup.c b/drivers/gpu/drm/drm_client_setup.c
index 5969c4ffe31b..c14221ca5a0d 100644
--- a/drivers/gpu/drm/drm_client_setup.c
+++ b/drivers/gpu/drm/drm_client_setup.c
@@ -64,3 +64,6 @@ void drm_client_setup_with_color_mode(struct drm_device *dev, unsigned int color
drm_client_setup_with_fourcc(dev, fourcc);
}
EXPORT_SYMBOL(drm_client_setup_with_color_mode);
+
+MODULE_DESCRIPTION("In-kernel DRM clients");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 52783eac078e..536409a35df4 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -32,7 +32,6 @@
#include <drm/drm_atomic.h>
#include <drm/drm_auth.h>
#include <drm/drm_bridge.h>
-#include <drm/drm_client.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index ac30b0ec9d93..c2c172eb25df 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -38,7 +38,7 @@
#include <drm/drm_accel.h>
#include <drm/drm_cache.h>
-#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d5e8994345bb..c9008113111b 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -697,6 +697,7 @@ void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u3
}
EXPORT_SYMBOL(drm_fb_helper_damage_area);
+#ifdef CONFIG_FB_DEFERRED_IO
/**
* drm_fb_helper_deferred_io() - fbdev deferred_io callback function
* @info: fb_info struct pointer
@@ -740,6 +741,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagerefli
}
}
EXPORT_SYMBOL(drm_fb_helper_deferred_io);
+#endif
/**
* drm_fb_helper_set_suspend - wrapper around fb_set_suspend
diff --git a/drivers/gpu/drm/drm_fbdev_client.c b/drivers/gpu/drm/drm_fbdev_client.c
index a09382afe2fb..246fb63ab250 100644
--- a/drivers/gpu/drm/drm_fbdev_client.c
+++ b/drivers/gpu/drm/drm_fbdev_client.c
@@ -61,11 +61,37 @@ err_drm_err:
return ret;
}
+static int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+{
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+ if (holds_console_lock)
+ drm_fb_helper_set_suspend(fb_helper, true);
+ else
+ drm_fb_helper_set_suspend_unlocked(fb_helper, true);
+
+ return 0;
+}
+
+static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+{
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+ if (holds_console_lock)
+ drm_fb_helper_set_suspend(fb_helper, false);
+ else
+ drm_fb_helper_set_suspend_unlocked(fb_helper, false);
+
+ return 0;
+}
+
static const struct drm_client_funcs drm_fbdev_client_funcs = {
.owner = THIS_MODULE,
.unregister = drm_fbdev_client_unregister,
.restore = drm_fbdev_client_restore,
.hotplug = drm_fbdev_client_hotplug,
+ .suspend = drm_fbdev_client_suspend,
+ .resume = drm_fbdev_client_resume,
};
/**
@@ -76,8 +102,8 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = {
*
* This function sets up fbdev emulation. Restore, hotplug events and
* teardown are all taken care of. Drivers that do suspend/resume need
- * to call drm_fb_helper_set_suspend_unlocked() themselves. Simple
- * drivers might use drm_mode_config_helper_suspend().
+ * to call drm_client_dev_suspend() and drm_client_dev_resume() by
+ * themselves. Simple drivers might use drm_mode_config_helper_suspend().
*
* This function is safe to call even when there are no connectors present.
* Setup will be retried on the next hotplug event.
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 2ee1c3233b0c..9011f8e16099 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -40,7 +40,7 @@
#include <linux/slab.h>
#include <linux/vga_switcheroo.h>
-#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 1705bfc90b1e..b2b6a8e49dda 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -48,6 +48,14 @@ struct drm_prime_file_private;
struct drm_printer;
struct drm_vblank_crtc;
+/* drm_client_event.c */
+#if defined(CONFIG_DRM_CLIENT)
+void drm_client_debugfs_init(struct drm_device *dev);
+#else
+static inline void drm_client_debugfs_init(struct drm_device *dev)
+{ }
+#endif
+
/* drm_file.c */
extern struct mutex drm_global_mutex;
bool drm_dev_needs_global_mutex(struct drm_device *dev);
diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c
index 2c582020cb42..5565464c1734 100644
--- a/drivers/gpu/drm/drm_modeset_helper.c
+++ b/drivers/gpu/drm/drm_modeset_helper.c
@@ -21,7 +21,7 @@
*/
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_fb_helper.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_modeset_helper.h>
@@ -185,7 +185,7 @@ EXPORT_SYMBOL(drm_crtc_init);
* Zero on success, negative error code on error.
*
* See also:
- * drm_kms_helper_poll_disable() and drm_fb_helper_set_suspend_unlocked().
+ * drm_kms_helper_poll_disable() and drm_client_dev_suspend().
*/
int drm_mode_config_helper_suspend(struct drm_device *dev)
{
@@ -199,10 +199,11 @@ int drm_mode_config_helper_suspend(struct drm_device *dev)
if (dev->mode_config.poll_enabled)
drm_kms_helper_poll_disable(dev);
- drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1);
+ drm_client_dev_suspend(dev, false);
state = drm_atomic_helper_suspend(dev);
if (IS_ERR(state)) {
- drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
+ drm_client_dev_resume(dev, false);
+
/*
* Don't enable polling if it was never initialized
*/
@@ -230,7 +231,7 @@ EXPORT_SYMBOL(drm_mode_config_helper_suspend);
* Zero on success, negative error code on error.
*
* See also:
- * drm_fb_helper_set_suspend_unlocked() and drm_kms_helper_poll_enable().
+ * drm_client_dev_resume() and drm_kms_helper_poll_enable().
*/
int drm_mode_config_helper_resume(struct drm_device *dev)
{
@@ -247,7 +248,8 @@ int drm_mode_config_helper_resume(struct drm_device *dev)
DRM_ERROR("Failed to resume (%d)\n", ret);
dev->mode_config.suspend_state = NULL;
- drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
+ drm_client_dev_resume(dev, false);
+
/*
* Don't enable polling if it is not initialized
*/
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 92f21764246f..96b266b37ba4 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -33,7 +33,7 @@
#include <linux/moduleparam.h>
#include <drm/drm_bridge.h>
-#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
diff --git a/drivers/gpu/drm/fsl-dcu/Kconfig b/drivers/gpu/drm/fsl-dcu/Kconfig
index 588ce2420af2..0e0f910ceb9f 100644
--- a/drivers/gpu/drm/fsl-dcu/Kconfig
+++ b/drivers/gpu/drm/fsl-dcu/Kconfig
@@ -9,6 +9,7 @@ config DRM_FSL_DCU
select DRM_PANEL
select REGMAP_MMIO
select VIDEOMODE_HELPERS
+ select MFD_SYSCON if SOC_LS1021A
help
Choose this option if you have an Freescale DCU chipset.
If M is selected the module will be called fsl-dcu-drm.
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
index 2b3f15f0dc56..91a48d774cf7 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -101,12 +101,25 @@ static void fsl_dcu_irq_uninstall(struct drm_device *dev)
static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
{
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ struct regmap *scfg;
int ret;
ret = fsl_dcu_drm_modeset_init(fsl_dev);
- if (ret < 0) {
- dev_err(dev->dev, "failed to initialize mode setting\n");
- return ret;
+ if (ret < 0)
+ return dev_err_probe(dev->dev, ret, "failed to initialize mode setting\n");
+
+ scfg = syscon_regmap_lookup_by_compatible("fsl,ls1021a-scfg");
+ if (PTR_ERR(scfg) != -ENODEV) {
+ /*
+ * For simplicity, enable the PIXCLK unconditionally,
+ * resulting in increased power consumption. Disabling
+ * the clock in PM or on unload could be implemented as
+ * a future improvement.
+ */
+ ret = regmap_update_bits(scfg, SCFG_PIXCLKCR, SCFG_PIXCLKCR_PXCEN,
+ SCFG_PIXCLKCR_PXCEN);
+ if (ret < 0)
+ return dev_err_probe(dev->dev, ret, "failed to enable pixclk\n");
}
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
@@ -274,10 +287,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
}
fsl_dev->irq = platform_get_irq(pdev, 0);
- if (fsl_dev->irq < 0) {
- dev_err(dev, "failed to get irq\n");
+ if (fsl_dev->irq < 0)
return fsl_dev->irq;
- }
fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
&fsl_dcu_regmap_config);
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
index e2049a0e8a92..566396013c04 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
@@ -160,6 +160,9 @@
#define FSL_DCU_ARGB4444 12
#define FSL_DCU_YUV422 14
+#define SCFG_PIXCLKCR 0x28
+#define SCFG_PIXCLKCR_PXCEN BIT(31)
+
#define VF610_LAYER_REG_NUM 9
#define LS1021A_LAYER_REG_NUM 10
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
index 9eb5abaf7d66..49bbd00c77ae 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
@@ -29,7 +29,7 @@ void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
FSL_TCON_CTRL1_TCON_BYPASS);
}
-static struct regmap_config fsl_tcon_regmap_config = {
+static const struct regmap_config fsl_tcon_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index db400aad88fa..1158a6b97f9a 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -10,6 +10,7 @@ config DRM_I915
# the shmem_readpage() which depends upon tmpfs
select SHMEM
select TMPFS
+ select DRM_CLIENT_SELECTION
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HDCP_HELPER
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index c106fb2dd20b..673f9b965494 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -11,7 +11,7 @@
#include <acpi/video.h>
#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_privacy_screen_consumer.h>
#include <drm/drm_probe_helper.h>
diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig
index 9d58f41bd00c..e014ed3ae66c 100644
--- a/drivers/gpu/drm/imx/dcss/Kconfig
+++ b/drivers/gpu/drm/imx/dcss/Kconfig
@@ -7,7 +7,7 @@ config DRM_IMX_DCSS
select DRM_BRIDGE_CONNECTOR
select DRM_GEM_DMA_HELPER
select VIDEOMODE_HELPERS
- depends on DRM && ARCH_MXC && ARM64
+ depends on DRM && ((ARCH_MXC && ARM64) || COMPILE_TEST)
help
Choose this if you have a NXP i.MX8MQ based system and want to use the
Display Controller Subsystem. This option enables DCSS support.
diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c
index 825728c356ff..32c3f46b21da 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c
@@ -136,7 +136,7 @@ static int div_q(int A, int B)
else
temp -= B / 2;
- result = (int)(temp / B);
+ result = div_s64(temp, B);
return result;
}
@@ -239,7 +239,7 @@ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
ll_temp = coef[phase][i];
ll_temp <<= PSC_COEFF_PRECISION;
ll_temp += sum >> 1;
- ll_temp /= sum;
+ ll_temp = div_s64(ll_temp, sum);
coef[phase][i] = (int)ll_temp;
}
}
diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig
index 0ea1feb84672..acaf25089001 100644
--- a/drivers/gpu/drm/imx/ipuv3/Kconfig
+++ b/drivers/gpu/drm/imx/ipuv3/Kconfig
@@ -15,6 +15,7 @@ config DRM_IMX_PARALLEL_DISPLAY
depends on DRM_IMX
select DRM_BRIDGE
select DRM_BRIDGE_CONNECTOR
+ select DRM_IMX_LEGACY_BRIDGE
select DRM_PANEL_BRIDGE
select VIDEOMODE_HELPERS
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 3d2436272ff2..2e0e7c4079b6 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -2,9 +2,9 @@
config DRM_MEDIATEK
tristate "DRM Support for Mediatek SoCs"
depends on DRM
- depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
+ depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
depends on COMMON_CLK
- depends on HAVE_ARM_SMCCC
+ depends on HAVE_ARM_SMCCC || COMPILE_TEST
depends on OF
depends on MTK_MMSYS
select DRM_CLIENT_SELECTION
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index d8796a904eca..f0f6f402994a 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -311,7 +311,7 @@ static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
},
};
-static struct regmap_config mtk_dp_regmap_config = {
+static const struct regmap_config mtk_dp_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig
index b410e0d8015a..417f79829cf8 100644
--- a/drivers/gpu/drm/meson/Kconfig
+++ b/drivers/gpu/drm/meson/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_MESON
tristate "DRM Support for Amlogic Meson Display Controller"
- depends on DRM && OF && (ARM || ARM64)
+ depends on DRM && OF && (ARM || ARM64 || COMPILE_TEST)
depends on ARCH_MESON || COMPILE_TEST
select DRM_CLIENT_SELECTION
select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index bbb662d2f184..7cace75a38af 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -128,7 +128,7 @@ static bool meson_vpu_has_available_connectors(struct device *dev)
return false;
}
-static struct regmap_config meson_regmap_config = {
+static const struct regmap_config meson_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 5565f7777529..b75db829b1da 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -272,20 +272,6 @@ static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
writeb(data, dw_hdmi->hdmitx + addr);
}
-/* Helper to change specific bits in controller registers */
-static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
- unsigned int addr,
- unsigned int mask,
- unsigned int val)
-{
- unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr);
-
- data &= ~mask;
- data |= val;
-
- dw_hdmi->data->dwc_write(dw_hdmi, addr, data);
-}
-
/* Bridge */
/* Setup PHY bandwidth modes */
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index f16d7c00e089..7ec833b6d829 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -6,6 +6,7 @@ config DRM_MSM
depends on ARCH_QCOM || SOC_IMX5 || COMPILE_TEST
depends on COMMON_CLK
depends on IOMMU_SUPPORT
+ depends on OF
depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
depends on QCOM_OCMEM || QCOM_OCMEM=n
depends on QCOM_LLCC || QCOM_LLCC=n
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index e1228fb093ee..0ddd8abca499 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1467,14 +1467,14 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
dp_priv = container_of(dp_display, struct dp_display_private, dp_display);
- ret = dp_bridge_init(dp_display, dev, encoder);
+ ret = dp_bridge_init(dp_display, dev, encoder, yuv_supported);
if (ret) {
DRM_DEV_ERROR(dev->dev,
"failed to create dp bridge: %d\n", ret);
return ret;
}
- dp_display->connector = dp_drm_connector_init(dp_display, encoder, yuv_supported);
+ dp_display->connector = dp_drm_connector_init(dp_display, encoder);
if (IS_ERR(dp_display->connector)) {
ret = PTR_ERR(dp_display->connector);
DRM_DEV_ERROR(dev->dev,
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 1b9be5bd97f1..7eb1621f9e7f 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -289,7 +289,7 @@ static const struct drm_bridge_funcs edp_bridge_ops = {
};
int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
- struct drm_encoder *encoder)
+ struct drm_encoder *encoder, bool yuv_supported)
{
int rc;
struct msm_dp_bridge *dp_bridge;
@@ -304,6 +304,7 @@ int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
bridge = &dp_bridge->bridge;
bridge->funcs = dp_display->is_edp ? &edp_bridge_ops : &dp_bridge_ops;
bridge->type = dp_display->connector_type;
+ bridge->ycbcr_420_allowed = yuv_supported;
/*
* Many ops only make sense for DP. Why?
@@ -351,8 +352,8 @@ int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
}
/* connector initialization */
-struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder,
- bool yuv_supported)
+struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display,
+ struct drm_encoder *encoder)
{
struct drm_connector *connector = NULL;
@@ -363,9 +364,6 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct dr
if (!dp_display->is_edp)
drm_connector_attach_dp_subconnector_property(connector);
- if (yuv_supported)
- connector->ycbcr_420_allowed = true;
-
drm_connector_attach_encoder(connector, encoder);
return connector;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 45e57ac25a4d..ae632fcc407c 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -19,10 +19,11 @@ struct msm_dp_bridge {
#define to_dp_bridge(x) container_of((x), struct msm_dp_bridge, bridge)
-struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder,
- bool yuv_supported);
+struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display,
+ struct drm_encoder *encoder);
int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
- struct drm_encoder *encoder);
+ struct drm_encoder *encoder,
+ bool yuv_supported);
void dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
struct drm_bridge_state *old_bridge_state);
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index e2fd561cd23f..619a3efbe8c8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -28,8 +28,8 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_crtc_helper.h>
-#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
@@ -804,8 +804,7 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
{
struct nouveau_display *disp = nouveau_display(dev);
- /* Disable console. */
- drm_fb_helper_set_suspend_unlocked(dev->fb_helper, true);
+ drm_client_dev_suspend(dev, false);
if (drm_drv_uses_atomic_modeset(dev)) {
if (!runtime) {
@@ -836,8 +835,7 @@ nouveau_display_resume(struct drm_device *dev, bool runtime)
}
}
- /* Enable console. */
- drm_fb_helper_set_suspend_unlocked(dev->fb_helper, false);
+ drm_client_dev_resume(dev, false);
}
int
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index ab4e11dc0b8a..a6c375a24154 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -2,7 +2,7 @@
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
-#include <drm/drm_fb_helper.h>
+#include <drm/drm_client_event.h>
#include "nouveau_drv.h"
#include "nouveau_acpi.h"
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 060c74a80eb1..3ea447f6a45b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -443,6 +443,7 @@ gf100_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch,
ret = gf100_grctx_generate(gr, chan, fifoch->inst);
if (ret) {
nvkm_error(&base->engine.subdev, "failed to construct context\n");
+ mutex_unlock(&gr->fecs.mutex);
return ret;
}
}
diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c
index 993691b3cc7e..9344855c4887 100644
--- a/drivers/gpu/drm/omapdrm/dss/dispc.c
+++ b/drivers/gpu/drm/omapdrm/dss/dispc.c
@@ -691,11 +691,6 @@ u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc,
return mgr_desc[channel].sync_lost_irq;
}
-u32 dispc_wb_get_framedone_irq(struct dispc_device *dispc)
-{
- return DISPC_IRQ_FRAMEDONEWB;
-}
-
void dispc_mgr_enable(struct dispc_device *dispc,
enum omap_channel channel, bool enable)
{
@@ -726,30 +721,6 @@ void dispc_mgr_go(struct dispc_device *dispc, enum omap_channel channel)
mgr_fld_write(dispc, channel, DISPC_MGR_FLD_GO, 1);
}
-bool dispc_wb_go_busy(struct dispc_device *dispc)
-{
- return REG_GET(dispc, DISPC_CONTROL2, 6, 6) == 1;
-}
-
-void dispc_wb_go(struct dispc_device *dispc)
-{
- enum omap_plane_id plane = OMAP_DSS_WB;
- bool enable, go;
-
- enable = REG_GET(dispc, DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1;
-
- if (!enable)
- return;
-
- go = REG_GET(dispc, DISPC_CONTROL2, 6, 6) == 1;
- if (go) {
- DSSERR("GO bit not down for WB\n");
- return;
- }
-
- REG_FLD_MOD(dispc, DISPC_CONTROL2, 1, 6, 6);
-}
-
static void dispc_ovl_write_firh_reg(struct dispc_device *dispc,
enum omap_plane_id plane, int reg,
u32 value)
@@ -1498,17 +1469,6 @@ void dispc_ovl_set_fifo_threshold(struct dispc_device *dispc,
min(high, 0xfffu));
}
-void dispc_enable_fifomerge(struct dispc_device *dispc, bool enable)
-{
- if (!dispc_has_feature(dispc, FEAT_FIFO_MERGE)) {
- WARN_ON(enable);
- return;
- }
-
- DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
- REG_FLD_MOD(dispc, DISPC_CONFIG, enable ? 1 : 0, 14, 14);
-}
-
void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc,
enum omap_plane_id plane,
u32 *fifo_low, u32 *fifo_high,
@@ -2814,95 +2774,6 @@ int dispc_ovl_setup(struct dispc_device *dispc,
return r;
}
-int dispc_wb_setup(struct dispc_device *dispc,
- const struct omap_dss_writeback_info *wi,
- bool mem_to_mem, const struct videomode *vm,
- enum dss_writeback_channel channel_in)
-{
- int r;
- u32 l;
- enum omap_plane_id plane = OMAP_DSS_WB;
- const int pos_x = 0, pos_y = 0;
- const u8 zorder = 0, global_alpha = 0;
- const bool replication = true;
- bool truncation;
- int in_width = vm->hactive;
- int in_height = vm->vactive;
- enum omap_overlay_caps caps =
- OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA;
-
- if (vm->flags & DISPLAY_FLAGS_INTERLACED)
- in_height /= 2;
-
- DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, "
- "rot %d\n", wi->paddr, wi->p_uv_addr, in_width,
- in_height, wi->width, wi->height, wi->fourcc, wi->rotation);
-
- r = dispc_ovl_setup_common(dispc, plane, caps, wi->paddr, wi->p_uv_addr,
- wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width,
- wi->height, wi->fourcc, wi->rotation, zorder,
- wi->pre_mult_alpha, global_alpha, wi->rotation_type,
- replication, vm, mem_to_mem, DRM_COLOR_YCBCR_BT601,
- DRM_COLOR_YCBCR_LIMITED_RANGE);
- if (r)
- return r;
-
- switch (wi->fourcc) {
- case DRM_FORMAT_RGB565:
- case DRM_FORMAT_RGB888:
- case DRM_FORMAT_ARGB4444:
- case DRM_FORMAT_RGBA4444:
- case DRM_FORMAT_RGBX4444:
- case DRM_FORMAT_ARGB1555:
- case DRM_FORMAT_XRGB1555:
- case DRM_FORMAT_XRGB4444:
- truncation = true;
- break;
- default:
- truncation = false;
- break;
- }
-
- /* setup extra DISPC_WB_ATTRIBUTES */
- l = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane));
- l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */
- l = FLD_MOD(l, channel_in, 18, 16); /* CHANNELIN */
- l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */
- if (mem_to_mem)
- l = FLD_MOD(l, 1, 26, 24); /* CAPTUREMODE */
- else
- l = FLD_MOD(l, 0, 26, 24); /* CAPTUREMODE */
- dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), l);
-
- if (mem_to_mem) {
- /* WBDELAYCOUNT */
- REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0);
- } else {
- u32 wbdelay;
-
- if (channel_in == DSS_WB_TV_MGR)
- wbdelay = vm->vsync_len + vm->vback_porch;
- else
- wbdelay = vm->vfront_porch + vm->vsync_len +
- vm->vback_porch;
-
- if (vm->flags & DISPLAY_FLAGS_INTERLACED)
- wbdelay /= 2;
-
- wbdelay = min(wbdelay, 255u);
-
- /* WBDELAYCOUNT */
- REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0);
- }
-
- return 0;
-}
-
-bool dispc_has_writeback(struct dispc_device *dispc)
-{
- return dispc->feat->has_writeback;
-}
-
int dispc_ovl_enable(struct dispc_device *dispc,
enum omap_plane_id plane, bool enable)
{
@@ -3742,23 +3613,6 @@ void dispc_mgr_set_clock_div(struct dispc_device *dispc,
cinfo->pck_div);
}
-int dispc_mgr_get_clock_div(struct dispc_device *dispc,
- enum omap_channel channel,
- struct dispc_clock_info *cinfo)
-{
- unsigned long fck;
-
- fck = dispc_fclk_rate(dispc);
-
- cinfo->lck_div = REG_GET(dispc, DISPC_DIVISORo(channel), 23, 16);
- cinfo->pck_div = REG_GET(dispc, DISPC_DIVISORo(channel), 7, 0);
-
- cinfo->lck = fck / cinfo->lck_div;
- cinfo->pck = cinfo->lck / cinfo->pck_div;
-
- return 0;
-}
-
u32 dispc_read_irqstatus(struct dispc_device *dispc)
{
return dispc_read_reg(dispc, DISPC_IRQSTATUS);
diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h
index 4ff02fbc0e71..a8b231ed4f4b 100644
--- a/drivers/gpu/drm/omapdrm/dss/dss.h
+++ b/drivers/gpu/drm/omapdrm/dss/dss.h
@@ -416,7 +416,6 @@ u32 dispc_mgr_get_framedone_irq(struct dispc_device *dispc,
enum omap_channel channel);
u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc,
enum omap_channel channel);
-u32 dispc_wb_get_framedone_irq(struct dispc_device *dispc);
u32 dispc_get_memory_bandwidth_limit(struct dispc_device *dispc);
@@ -458,20 +457,11 @@ int dispc_ovl_setup(struct dispc_device *dispc,
int dispc_ovl_enable(struct dispc_device *dispc,
enum omap_plane_id plane, bool enable);
-bool dispc_has_writeback(struct dispc_device *dispc);
-int dispc_wb_setup(struct dispc_device *dispc,
- const struct omap_dss_writeback_info *wi,
- bool mem_to_mem, const struct videomode *vm,
- enum dss_writeback_channel channel_in);
-bool dispc_wb_go_busy(struct dispc_device *dispc);
-void dispc_wb_go(struct dispc_device *dispc);
-
void dispc_enable_sidle(struct dispc_device *dispc);
void dispc_disable_sidle(struct dispc_device *dispc);
void dispc_lcd_enable_signal(struct dispc_device *dispc, bool enable);
void dispc_pck_free_enable(struct dispc_device *dispc, bool enable);
-void dispc_enable_fifomerge(struct dispc_device *dispc, bool enable);
typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck,
unsigned long pck, void *data);
@@ -494,9 +484,6 @@ void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc,
void dispc_mgr_set_clock_div(struct dispc_device *dispc,
enum omap_channel channel,
const struct dispc_clock_info *cinfo);
-int dispc_mgr_get_clock_div(struct dispc_device *dispc,
- enum omap_channel channel,
- struct dispc_clock_info *cinfo);
void dispc_set_tv_pclk(struct dispc_device *dispc, unsigned long pclk);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index bce4cb64184a..d7469c565d1d 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -632,6 +632,16 @@ config DRM_PANEL_SAMSUNG_AMS639RQ08
Say Y or M here if you want to enable support for the
Samsung AMS639RQ08 FHD Plus (2340x1080@60Hz) CMD mode panel.
+config DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24
+ tristate "Samsung AMS427AP24 panel with S6E88A0 controller"
+ depends on GPIOLIB && OF && REGULATOR
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for Samsung AMS427AP24 panel
+ with S6E88A0 controller (found in Samsung Galaxy S4 Mini Value Edition
+ GT-I9195I). To compile this driver as a module, choose M here.
+
config DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01
tristate "Samsung AMS452EF01 panel with S6E88A0 DSI video mode controller"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 4db02c2411ae..7dcf72646cac 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24) += panel-samsung-s6e88a0-ams427ap24.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
index 4a6dcfd781e8..94b7dfef3b5e 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
@@ -318,7 +318,7 @@ static int ili9322_regmap_spi_read(void *context, const void *reg,
return spi_write_then_read(spi, buf, 1, val, 1);
}
-static struct regmap_bus ili9322_regmap_bus = {
+static const struct regmap_bus ili9322_regmap_bus = {
.write = ili9322_regmap_spi_write,
.read = ili9322_regmap_spi_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
index 292aa26a456d..77f74e6c467e 100644
--- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c
@@ -26,7 +26,7 @@ struct ltk050h3146w;
struct ltk050h3146w_desc {
const unsigned long mode_flags;
const struct drm_display_mode *mode;
- int (*init)(struct ltk050h3146w *ctx);
+ void (*init)(struct mipi_dsi_multi_context *dsi_ctx);
};
struct ltk050h3146w {
@@ -243,67 +243,57 @@ struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
return container_of(panel, struct ltk050h3146w, panel);
}
-static int ltk050h3148w_init_sequence(struct ltk050h3146w *ctx)
+static void ltk050h3148w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
-
/*
* Init sequence was supplied by the panel vendor without much
* documentation.
*/
- mipi_dsi_dcs_write_seq(dsi, 0xb9, 0xff, 0x83, 0x94);
- mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
- 0x71, 0x31, 0x55, 0x2f);
- mipi_dsi_dcs_write_seq(dsi, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
- mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x88);
- mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
- 0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
- 0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
- mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
- 0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
- 0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
- 0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
- 0x17, 0x11, 0x40);
- mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
- 0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
- 0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
- mipi_dsi_dcs_write_seq(dsi, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
- 0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
- 0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
- mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
- 0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
- 0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
- 0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
- 0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
- 0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
- 0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
- 0x5d, 0x61, 0x65, 0x7f);
- mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x0b);
- mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x1f, 0x31);
- mipi_dsi_dcs_write_seq(dsi, 0xb6, 0xc4, 0xc4);
- mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xc6, 0xef);
- mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x02);
-
- ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
- return ret;
- }
-
- msleep(60);
-
- return 0;
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0xff, 0x83, 0x94);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
+ 0x71, 0x31, 0x55, 0x2f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x88);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
+ 0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
+ 0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
+ 0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
+ 0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
+ 0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
+ 0x17, 0x11, 0x40);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
+ 0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
+ 0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
+ 0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
+ 0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
+ 0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
+ 0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
+ 0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
+ 0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
+ 0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
+ 0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
+ 0x5d, 0x61, 0x65, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcc, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x1f, 0x31);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb6, 0xc4, 0xc4);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0xef);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x02);
+
+ mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
+ mipi_dsi_msleep(dsi_ctx, 60);
}
static const struct drm_display_mode ltk050h3148w_mode = {
@@ -327,74 +317,64 @@ static const struct ltk050h3146w_desc ltk050h3148w_data = {
MIPI_DSI_MODE_VIDEO_BURST,
};
-static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
+static void ltk050h3146w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
-
/*
* Init sequence was supplied by the panel vendor without much
* documentation.
*/
- mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
- mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
- 0x01);
- mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
- mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
- mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
-
- mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
- mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
- 0x28, 0x04, 0xcc, 0xcc, 0xcc);
- mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
- mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
- mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
- mipi_dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
- 0x80);
- mipi_dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
- 0x16, 0x00, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
- 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
- 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
- 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
- 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
- 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
- mipi_dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
- 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
- mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
- 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
- mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
- 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
- mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
- 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
- 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
- mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
- 0x21, 0x00, 0x60);
- mipi_dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
- mipi_dsi_dcs_write_seq(dsi, 0xde, 0x02);
- mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
- mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
- mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x11);
- mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
- mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
- mipi_dsi_dcs_write_seq(dsi, 0xde, 0x00);
-
- ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
- return ret;
- }
-
- msleep(60);
-
- return 0;
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdf, 0x93, 0x65, 0xf8);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
+ 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0xb5);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x00, 0xb5);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x00, 0xc4, 0x23, 0x07);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
+ 0x28, 0x04, 0xcc, 0xcc, 0xcc);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f, 0x04);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1e, 0xf2);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x26, 0x03);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x00, 0x12);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
+ 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
+ 0x16, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
+ 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
+ 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
+ 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
+ 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
+ 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
+ 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
+ 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
+ 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
+ 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
+ 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
+ 0x21, 0x00, 0x60);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x2c, 0xa3, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x02);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x32, 0x1c);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x3b, 0x70, 0x00, 0x04);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x11);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x20, 0x38, 0x1e, 0x84);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x00);
+
+ mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
+ mipi_dsi_msleep(dsi_ctx, 60);
}
static const struct drm_display_mode ltk050h3146w_mode = {
@@ -418,79 +398,42 @@ static const struct ltk050h3146w_desc ltk050h3146w_data = {
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
};
-static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
+static void ltk050h3146w_a2_select_page(struct mipi_dsi_multi_context *dsi_ctx, int page)
{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- u8 d[3] = { 0x98, 0x81, page };
+ u8 d[4] = { 0xff, 0x98, 0x81, page };
- return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
+ mipi_dsi_dcs_write_buffer_multi(dsi_ctx, d, ARRAY_SIZE(d));
}
-static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
+static void ltk050h3146w_a2_write_page(struct mipi_dsi_multi_context *dsi_ctx, int page,
const struct ltk050h3146w_cmd *cmds,
int num)
{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int i, ret;
+ ltk050h3146w_a2_select_page(dsi_ctx, page);
- ret = ltk050h3146w_a2_select_page(ctx, page);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
- return ret;
- }
-
- for (i = 0; i < num; i++) {
- ret = mipi_dsi_generic_write(dsi, &cmds[i],
+ for (int i = 0; i < num; i++)
+ mipi_dsi_generic_write_multi(dsi_ctx, &cmds[i],
sizeof(struct ltk050h3146w_cmd));
- if (ret < 0) {
- dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
- return ret;
- }
- }
-
- return 0;
}
-static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
+static void ltk050h3146w_a2_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
{
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
-
/*
* Init sequence was supplied by the panel vendor without much
* documentation.
*/
- ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
+ ltk050h3146w_a2_write_page(dsi_ctx, 3, page3_cmds,
ARRAY_SIZE(page3_cmds));
- if (ret < 0)
- return ret;
-
- ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
+ ltk050h3146w_a2_write_page(dsi_ctx, 4, page4_cmds,
ARRAY_SIZE(page4_cmds));
- if (ret < 0)
- return ret;
-
- ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
+ ltk050h3146w_a2_write_page(dsi_ctx, 1, page1_cmds,
ARRAY_SIZE(page1_cmds));
- if (ret < 0)
- return ret;
-
- ret = ltk050h3146w_a2_select_page(ctx, 0);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
- return ret;
- }
+ ltk050h3146w_a2_select_page(dsi_ctx, 0);
/* vendor code called this without param, where there should be one */
- ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
- return ret;
- }
-
- msleep(60);
+ mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 0);
- return 0;
+ mipi_dsi_msleep(dsi_ctx, 60);
}
static const struct drm_display_mode ltk050h3146w_a2_mode = {
@@ -518,19 +461,12 @@ static int ltk050h3146w_unprepare(struct drm_panel *panel)
{
struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- ret = mipi_dsi_dcs_set_display_off(dsi);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to set display off: %d\n", ret);
- return ret;
- }
-
- mipi_dsi_dcs_enter_sleep_mode(dsi);
- if (ret < 0) {
- dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
- return ret;
- }
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ if (dsi_ctx.accum_err)
+ return dsi_ctx.accum_err;
regulator_disable(ctx->iovcc);
regulator_disable(ctx->vci);
@@ -542,17 +478,17 @@ static int ltk050h3146w_prepare(struct drm_panel *panel)
{
struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dev_dbg(ctx->dev, "Resetting the panel\n");
- ret = regulator_enable(ctx->vci);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
- return ret;
+ dsi_ctx.accum_err = regulator_enable(ctx->vci);
+ if (dsi_ctx.accum_err) {
+ dev_err(ctx->dev, "Failed to enable vci supply: %d\n", dsi_ctx.accum_err);
+ return dsi_ctx.accum_err;
}
- ret = regulator_enable(ctx->iovcc);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
+ dsi_ctx.accum_err = regulator_enable(ctx->iovcc);
+ if (dsi_ctx.accum_err) {
+ dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", dsi_ctx.accum_err);
goto disable_vci;
}
@@ -561,28 +497,15 @@ static int ltk050h3146w_prepare(struct drm_panel *panel)
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(20);
- ret = ctx->panel_desc->init(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
- goto disable_iovcc;
- }
-
- ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
- goto disable_iovcc;
- }
-
+ ctx->panel_desc->init(&dsi_ctx);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
/* T9: 120ms */
- msleep(120);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 50);
- ret = mipi_dsi_dcs_set_display_on(dsi);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
+ if (dsi_ctx.accum_err)
goto disable_iovcc;
- }
-
- msleep(50);
return 0;
@@ -590,7 +513,7 @@ disable_iovcc:
regulator_disable(ctx->iovcc);
disable_vci:
regulator_disable(ctx->vci);
- return ret;
+ return dsi_ctx.accum_err;
}
static int ltk050h3146w_get_modes(struct drm_panel *panel,
diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
index d3baccfe6286..06e16a7c14a7 100644
--- a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
+++ b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
@@ -917,7 +917,7 @@ static const struct nv3052c_panel_info wl_355608_a8_panel_info = {
static const struct spi_device_id nv3052c_ids[] = {
{ "ltk035c5444t", },
{ "fs035vg158", },
- { "wl-355608-a8", },
+ { "rg35xx-plus-panel", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, nv3052c_ids);
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
index 76b5a6ea610d..64c6f7d45bed 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
@@ -24,7 +24,7 @@ struct s6e3ha8 {
struct regulator_bulk_data *supplies;
};
-const struct regulator_bulk_data s6e3ha8_supplies[] = {
+static const struct regulator_bulk_data s6e3ha8_supplies[] = {
{ .supply = "vdd3" },
{ .supply = "vci" },
{ .supply = "vddr" },
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c
new file mode 100644
index 000000000000..e92e95158d1f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Samsung AMS427AP24 panel with S6E88A0 controller
+ * Copyright (c) 2024 Jakob Hauser <jahau@rocketmail.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#define NUM_STEPS_CANDELA 54
+#define NUM_STEPS_AID 39
+#define NUM_STEPS_ELVSS 17
+
+/* length of the payload data, thereof fixed and variable */
+#define FIX_LEN_AID 4
+#define FIX_LEN_ELVSS 2
+#define FIX_LEN_GAMMA 1
+#define VAR_LEN_AID 2
+#define VAR_LEN_ELVSS 1
+#define VAR_LEN_GAMMA 33
+#define LEN_AID (FIX_LEN_AID + VAR_LEN_AID)
+#define LEN_ELVSS (FIX_LEN_ELVSS + VAR_LEN_ELVSS)
+#define LEN_GAMMA (FIX_LEN_GAMMA + VAR_LEN_GAMMA)
+
+struct s6e88a0_ams427ap24 {
+ struct drm_panel panel;
+ struct backlight_device *bl_dev;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+ bool flip_horizontal;
+};
+
+static const struct regulator_bulk_data s6e88a0_ams427ap24_supplies[] = {
+ { .supply = "vdd3" },
+ { .supply = "vci" },
+};
+
+static inline
+struct s6e88a0_ams427ap24 *to_s6e88a0_ams427ap24(struct drm_panel *panel)
+{
+ return container_of(panel, struct s6e88a0_ams427ap24, panel);
+}
+
+enum candela {
+ CANDELA_10CD, /* 0 */
+ CANDELA_11CD,
+ CANDELA_12CD,
+ CANDELA_13CD,
+ CANDELA_14CD,
+ CANDELA_15CD,
+ CANDELA_16CD,
+ CANDELA_17CD,
+ CANDELA_19CD,
+ CANDELA_20CD,
+ CANDELA_21CD,
+ CANDELA_22CD,
+ CANDELA_24CD,
+ CANDELA_25CD,
+ CANDELA_27CD,
+ CANDELA_29CD,
+ CANDELA_30CD,
+ CANDELA_32CD,
+ CANDELA_34CD,
+ CANDELA_37CD,
+ CANDELA_39CD,
+ CANDELA_41CD,
+ CANDELA_44CD,
+ CANDELA_47CD,
+ CANDELA_50CD,
+ CANDELA_53CD,
+ CANDELA_56CD,
+ CANDELA_60CD,
+ CANDELA_64CD,
+ CANDELA_68CD,
+ CANDELA_72CD,
+ CANDELA_77CD,
+ CANDELA_82CD,
+ CANDELA_87CD,
+ CANDELA_93CD,
+ CANDELA_98CD,
+ CANDELA_105CD,
+ CANDELA_111CD,
+ CANDELA_119CD,
+ CANDELA_126CD,
+ CANDELA_134CD,
+ CANDELA_143CD,
+ CANDELA_152CD,
+ CANDELA_162CD,
+ CANDELA_172CD,
+ CANDELA_183CD,
+ CANDELA_195CD,
+ CANDELA_207CD,
+ CANDELA_220CD,
+ CANDELA_234CD,
+ CANDELA_249CD,
+ CANDELA_265CD,
+ CANDELA_282CD,
+ CANDELA_300CD, /* 53 */
+};
+
+static const int s6e88a0_ams427ap24_br_to_cd[NUM_STEPS_CANDELA] = {
+ /* columns: brightness from, brightness till, candela */
+ /* 0 */ 10, /* 10CD */
+ /* 11 */ 11, /* 11CD */
+ /* 12 */ 12, /* 12CD */
+ /* 13 */ 13, /* 13CD */
+ /* 14 */ 14, /* 14CD */
+ /* 15 */ 15, /* 15CD */
+ /* 16 */ 16, /* 16CD */
+ /* 17 */ 17, /* 17CD */
+ /* 18 */ 18, /* 19CD */
+ /* 19 */ 19, /* 20CD */
+ /* 20 */ 20, /* 21CD */
+ /* 21 */ 21, /* 22CD */
+ /* 22 */ 22, /* 24CD */
+ /* 23 */ 23, /* 25CD */
+ /* 24 */ 24, /* 27CD */
+ /* 25 */ 25, /* 29CD */
+ /* 26 */ 26, /* 30CD */
+ /* 27 */ 27, /* 32CD */
+ /* 28 */ 28, /* 34CD */
+ /* 29 */ 29, /* 37CD */
+ /* 30 */ 30, /* 39CD */
+ /* 31 */ 32, /* 41CD */
+ /* 33 */ 34, /* 44CD */
+ /* 35 */ 36, /* 47CD */
+ /* 37 */ 38, /* 50CD */
+ /* 39 */ 40, /* 53CD */
+ /* 41 */ 43, /* 56CD */
+ /* 44 */ 46, /* 60CD */
+ /* 47 */ 49, /* 64CD */
+ /* 50 */ 52, /* 68CD */
+ /* 53 */ 56, /* 72CD */
+ /* 57 */ 59, /* 77CD */
+ /* 60 */ 63, /* 82CD */
+ /* 64 */ 67, /* 87CD */
+ /* 68 */ 71, /* 93CD */
+ /* 72 */ 76, /* 98CD */
+ /* 77 */ 80, /* 105CD */
+ /* 81 */ 86, /* 111CD */
+ /* 87 */ 91, /* 119CD */
+ /* 92 */ 97, /* 126CD */
+ /* 98 */ 104, /* 134CD */
+ /* 105 */ 110, /* 143CD */
+ /* 111 */ 118, /* 152CD */
+ /* 119 */ 125, /* 162CD */
+ /* 126 */ 133, /* 172CD */
+ /* 134 */ 142, /* 183CD */
+ /* 143 */ 150, /* 195CD */
+ /* 151 */ 160, /* 207CD */
+ /* 161 */ 170, /* 220CD */
+ /* 171 */ 181, /* 234CD */
+ /* 182 */ 205, /* 249CD */
+ /* 206 */ 234, /* 265CD */
+ /* 235 */ 254, /* 282CD */
+ /* 255 */ 255, /* 300CD */
+};
+
+static const u8 s6e88a0_ams427ap24_aid[NUM_STEPS_AID][VAR_LEN_AID] = {
+ { 0x03, 0x77 }, /* AOR 90.9%, 10CD */
+ { 0x03, 0x73 }, /* AOR 90.5%, 11CD */
+ { 0x03, 0x69 }, /* AOR 89.4%, 12CD */
+ { 0x03, 0x65 }, /* AOR 89.0%, 13CD */
+ { 0x03, 0x61 }, /* AOR 88.6%, 14CD */
+ { 0x03, 0x55 }, /* AOR 87.4%, 15CD */
+ { 0x03, 0x50 }, /* AOR 86.9%, 16CD */
+ { 0x03, 0x45 }, /* AOR 85.8%, 17CD */
+ { 0x03, 0x35 }, /* AOR 84.1%, 19CD */
+ { 0x03, 0x27 }, /* AOR 82.7%, 20CD */
+ { 0x03, 0x23 }, /* AOR 82.3%, 21CD */
+ { 0x03, 0x17 }, /* AOR 81.0%, 22CD */
+ { 0x03, 0x11 }, /* AOR 80.4%, 24CD */
+ { 0x03, 0x04 }, /* AOR 79.1%, 25CD */
+ { 0x02, 0xf4 }, /* AOR 77.5%, 27CD */
+ { 0x02, 0xe3 }, /* AOR 75.7%, 29CD */
+ { 0x02, 0xd7 }, /* AOR 74.5%, 30CD */
+ { 0x02, 0xc6 }, /* AOR 72.7%, 32CD */
+ { 0x02, 0xb7 }, /* AOR 71.2%, 34CD */
+ { 0x02, 0xa1 }, /* AOR 69.0%, 37CD */
+ { 0x02, 0x91 }, /* AOR 67.3%, 39CD */
+ { 0x02, 0x78 }, /* AOR 64.8%, 41CD */
+ { 0x02, 0x62 }, /* AOR 62.5%, 44CD */
+ { 0x02, 0x45 }, /* AOR 59.5%, 47CD */
+ { 0x02, 0x30 }, /* AOR 57.4%, 50CD */
+ { 0x02, 0x13 }, /* AOR 54.4%, 53CD */
+ { 0x01, 0xf5 }, /* AOR 51.3%, 56CD */
+ { 0x01, 0xd3 }, /* AOR 47.8%, 60CD */
+ { 0x01, 0xb1 }, /* AOR 44.4%, 64CD */
+ { 0x01, 0x87 }, /* AOR 40.1%, 68CD */
+ { 0x01, 0x63 }, /* AOR 36.6%, 72CD */
+ { 0x01, 0x35 }, /* AOR 31.7%, 77CD */
+ { 0x01, 0x05 }, /* AOR 26.9%, 82CD */
+ { 0x00, 0xd5 }, /* AOR 21.8%, 87CD */
+ { 0x00, 0xa1 }, /* AOR 16.5%, 93CD */
+ { 0x00, 0x6f }, /* AOR 11.4%, 98CD */
+ { 0x00, 0x31 }, /* AOR 5.0%, 105CD */
+ { 0x01, 0x86 }, /* AOR 40.0%, 111CD ~ 172CD */
+ { 0x00, 0x08 }, /* AOR 0.6%, 183CD ~ 300CD */
+};
+
+static const u8 s6e88a0_ams427ap24_elvss[NUM_STEPS_ELVSS][VAR_LEN_ELVSS] = {
+ { 0x14 }, /* 10CD ~ 111CD */
+ { 0x13 }, /* 119CD */
+ { 0x12 }, /* 126CD */
+ { 0x12 }, /* 134CD */
+ { 0x11 }, /* 143CD */
+ { 0x10 }, /* 152CD */
+ { 0x0f }, /* 162CD */
+ { 0x0e }, /* 172CD */
+ { 0x11 }, /* 183CD */
+ { 0x11 }, /* 195CD */
+ { 0x10 }, /* 207CD */
+ { 0x0f }, /* 220CD */
+ { 0x0f }, /* 234CD */
+ { 0x0e }, /* 249CD */
+ { 0x0d }, /* 265CD */
+ { 0x0c }, /* 282CD */
+ { 0x0b }, /* 300CD */
+};
+
+static const u8 s6e88a0_ams427ap24_gamma[NUM_STEPS_CANDELA][VAR_LEN_GAMMA] = {
+ /* 10CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x8a, 0x8c, 0x8b,
+ 0x8c, 0x87, 0x89, 0x89, 0x88, 0x87, 0x8c, 0x80, 0x82, 0x88, 0x7b,
+ 0x72, 0x8c, 0x60, 0x68, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 11CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x8a, 0x8c, 0x8b,
+ 0x8c, 0x87, 0x89, 0x89, 0x88, 0x87, 0x8c, 0x80, 0x82, 0x88, 0x7b,
+ 0x72, 0x8c, 0x60, 0x68, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 12CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8c, 0x88, 0x89, 0x8a, 0x88, 0x87, 0x8c, 0x81, 0x82, 0x87, 0x7a,
+ 0x72, 0x8b, 0x60, 0x68, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 13CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8c, 0x88, 0x89, 0x8a, 0x88, 0x87, 0x8c, 0x81, 0x82, 0x87, 0x7a,
+ 0x72, 0x8b, 0x61, 0x69, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 14CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8c, 0x8b,
+ 0x8c, 0x88, 0x89, 0x8a, 0x87, 0x86, 0x8a, 0x82, 0x82, 0x87, 0x79,
+ 0x71, 0x89, 0x63, 0x6c, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 15CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x8a, 0x8c, 0x8c,
+ 0x8c, 0x86, 0x87, 0x88, 0x85, 0x85, 0x8a, 0x83, 0x83, 0x88, 0x78,
+ 0x72, 0x89, 0x64, 0x6c, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 16CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8c, 0x8b,
+ 0x8c, 0x86, 0x88, 0x88, 0x86, 0x86, 0x8a, 0x84, 0x84, 0x88, 0x78,
+ 0x72, 0x89, 0x5d, 0x67, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 17CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x87, 0x89, 0x89, 0x86, 0x86, 0x8a, 0x84, 0x83, 0x87, 0x78,
+ 0x73, 0x89, 0x64, 0x6e, 0x8e, 0x38, 0x32, 0x24, 0x00, 0x00, 0x00 },
+ /* 19CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x87, 0x89, 0x89, 0x86, 0x86, 0x89, 0x84, 0x84, 0x87, 0x77,
+ 0x72, 0x88, 0x65, 0x6f, 0x8e, 0x38, 0x32, 0x24, 0x00, 0x00, 0x00 },
+ /* 20CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x88, 0x89, 0x89, 0x85, 0x85, 0x88, 0x82, 0x83, 0x85, 0x79,
+ 0x73, 0x88, 0x65, 0x6f, 0x8e, 0x38, 0x32, 0x24, 0x00, 0x00, 0x00 },
+ /* 21CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x88, 0x89, 0x89, 0x85, 0x85, 0x88, 0x82, 0x83, 0x85, 0x79,
+ 0x74, 0x88, 0x65, 0x6f, 0x8e, 0x38, 0x32, 0x24, 0x00, 0x00, 0x00 },
+ /* 22CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8c, 0x8b,
+ 0x8c, 0x86, 0x88, 0x87, 0x86, 0x86, 0x89, 0x82, 0x83, 0x85, 0x7c,
+ 0x75, 0x87, 0x65, 0x6f, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 24CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8c, 0x8b,
+ 0x8c, 0x86, 0x88, 0x87, 0x86, 0x86, 0x89, 0x82, 0x83, 0x85, 0x7c,
+ 0x76, 0x86, 0x66, 0x6f, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ /* 25CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x87, 0x87, 0x89, 0x82, 0x82, 0x84, 0x7f,
+ 0x7a, 0x89, 0x6b, 0x73, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 27CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x87, 0x87, 0x89, 0x82, 0x82, 0x84, 0x7f,
+ 0x7a, 0x89, 0x6b, 0x73, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 29CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x85, 0x84, 0x87, 0x84, 0x85, 0x86, 0x80,
+ 0x7b, 0x88, 0x6a, 0x73, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 30CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x85, 0x84, 0x87, 0x84, 0x85, 0x86, 0x80,
+ 0x7b, 0x88, 0x6a, 0x73, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 32CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x85, 0x84, 0x87, 0x84, 0x85, 0x86, 0x80,
+ 0x7b, 0x88, 0x6a, 0x73, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 34CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8c, 0x8a, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x89, 0x88, 0x85, 0x84, 0x87, 0x83, 0x84, 0x84, 0x7f,
+ 0x79, 0x86, 0x6c, 0x76, 0x91, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 37CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x88, 0x87, 0x86, 0x87, 0x83, 0x84, 0x84, 0x7f,
+ 0x79, 0x86, 0x6c, 0x76, 0x90, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 39CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x84, 0x84, 0x86, 0x83, 0x85, 0x85, 0x80,
+ 0x79, 0x85, 0x6c, 0x76, 0x90, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 41CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x84, 0x84, 0x86, 0x81, 0x84, 0x83, 0x7f,
+ 0x79, 0x84, 0x6e, 0x79, 0x93, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 44CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x84, 0x84, 0x86, 0x81, 0x84, 0x83, 0x7f,
+ 0x79, 0x84, 0x6e, 0x79, 0x92, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 47CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x84, 0x85, 0x86, 0x81, 0x84, 0x83, 0x7f,
+ 0x79, 0x83, 0x6f, 0x79, 0x91, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 50CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x84, 0x85, 0x86, 0x82, 0x84, 0x83, 0x7f,
+ 0x79, 0x83, 0x6f, 0x79, 0x90, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 53CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8b,
+ 0x8b, 0x86, 0x88, 0x87, 0x83, 0x83, 0x85, 0x84, 0x85, 0x85, 0x7f,
+ 0x79, 0x83, 0x70, 0x79, 0x8f, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 56CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8a,
+ 0x8a, 0x87, 0x89, 0x87, 0x83, 0x83, 0x85, 0x84, 0x85, 0x84, 0x7f,
+ 0x79, 0x82, 0x70, 0x7a, 0x8e, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 60CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8a,
+ 0x8a, 0x87, 0x89, 0x87, 0x83, 0x83, 0x85, 0x84, 0x85, 0x84, 0x7e,
+ 0x79, 0x82, 0x71, 0x7a, 0x8d, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 64CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8b, 0x89, 0x89, 0x8b, 0x8a,
+ 0x8a, 0x86, 0x88, 0x86, 0x84, 0x84, 0x86, 0x82, 0x83, 0x82, 0x80,
+ 0x7a, 0x84, 0x71, 0x7a, 0x8c, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 68CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8c, 0x8a,
+ 0x8a, 0x86, 0x88, 0x86, 0x84, 0x84, 0x86, 0x82, 0x84, 0x82, 0x81,
+ 0x7b, 0x83, 0x72, 0x7b, 0x8b, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 72CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8c, 0x8a,
+ 0x8a, 0x86, 0x88, 0x86, 0x85, 0x85, 0x86, 0x82, 0x84, 0x82, 0x81,
+ 0x7b, 0x83, 0x72, 0x7c, 0x8a, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 77CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8c, 0x8a,
+ 0x8a, 0x85, 0x87, 0x85, 0x85, 0x87, 0x87, 0x82, 0x84, 0x82, 0x81,
+ 0x7c, 0x82, 0x72, 0x7c, 0x89, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 82CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8c, 0x8a,
+ 0x8a, 0x85, 0x87, 0x85, 0x85, 0x87, 0x87, 0x82, 0x84, 0x82, 0x81,
+ 0x7c, 0x82, 0x73, 0x7c, 0x88, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 87CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8c, 0x8a,
+ 0x8a, 0x85, 0x87, 0x85, 0x84, 0x84, 0x86, 0x80, 0x84, 0x81, 0x80,
+ 0x7a, 0x82, 0x76, 0x7f, 0x89, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 93CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8b, 0x8a,
+ 0x8a, 0x86, 0x87, 0x85, 0x84, 0x85, 0x86, 0x80, 0x84, 0x80, 0x80,
+ 0x7a, 0x82, 0x76, 0x80, 0x88, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 98CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x8a, 0x89, 0x89, 0x8b, 0x8a,
+ 0x8a, 0x86, 0x87, 0x85, 0x85, 0x85, 0x86, 0x80, 0x84, 0x80, 0x80,
+ 0x7a, 0x82, 0x76, 0x80, 0x88, 0x33, 0x2f, 0x22, 0x00, 0x00, 0x00 },
+ /* 105CD */
+ { 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc5, 0x89, 0x88, 0x88, 0x8b, 0x8a,
+ 0x8a, 0x84, 0x87, 0x85, 0x85, 0x85, 0x85, 0x80, 0x84, 0x80, 0x7f,
+ 0x79, 0x81, 0x71, 0x7d, 0x87, 0x38, 0x32, 0x24, 0x00, 0x00, 0x00 },
+ /* 111CD */
+ { 0x00, 0xdf, 0x00, 0xde, 0x00, 0xde, 0x85, 0x85, 0x84, 0x87, 0x86,
+ 0x87, 0x85, 0x86, 0x85, 0x83, 0x83, 0x83, 0x81, 0x82, 0x82, 0x80,
+ 0x7d, 0x82, 0x75, 0x7f, 0x86, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 119CD */
+ { 0x00, 0xe3, 0x00, 0xe1, 0x00, 0xe2, 0x85, 0x85, 0x84, 0x86, 0x85,
+ 0x85, 0x84, 0x85, 0x84, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x7e,
+ 0x7b, 0x81, 0x75, 0x7f, 0x86, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 126CD */
+ { 0x00, 0xe6, 0x00, 0xe5, 0x00, 0xe5, 0x85, 0x84, 0x84, 0x85, 0x85,
+ 0x85, 0x84, 0x84, 0x84, 0x82, 0x83, 0x83, 0x80, 0x81, 0x81, 0x80,
+ 0x7f, 0x83, 0x73, 0x7c, 0x84, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 134CD */
+ { 0x00, 0xe9, 0x00, 0xe8, 0x00, 0xe8, 0x84, 0x84, 0x83, 0x85, 0x85,
+ 0x85, 0x84, 0x84, 0x83, 0x81, 0x82, 0x82, 0x81, 0x81, 0x81, 0x7f,
+ 0x7d, 0x81, 0x73, 0x7c, 0x83, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 143CD */
+ { 0x00, 0xed, 0x00, 0xec, 0x00, 0xec, 0x84, 0x83, 0x83, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x83, 0x82, 0x83, 0x83, 0x81, 0x80, 0x81, 0x7f,
+ 0x7e, 0x81, 0x70, 0x79, 0x81, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 152CD */
+ { 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x84, 0x84, 0x83, 0x81, 0x81, 0x81, 0x80, 0x80, 0x81, 0x80,
+ 0x80, 0x82, 0x6f, 0x78, 0x7f, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 162CD */
+ { 0x00, 0xf4, 0x00, 0xf3, 0x00, 0xf4, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x81, 0x80,
+ 0x7f, 0x82, 0x6f, 0x78, 0x7f, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 172CD */
+ { 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x81, 0x81, 0x80, 0x81, 0x80, 0x80, 0x80, 0x81, 0x81,
+ 0x80, 0x83, 0x6d, 0x76, 0x7d, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 183CD */
+ { 0x00, 0xe0, 0x00, 0xdf, 0x00, 0xdf, 0x84, 0x84, 0x83, 0x86, 0x86,
+ 0x86, 0x83, 0x84, 0x83, 0x82, 0x82, 0x82, 0x81, 0x83, 0x81, 0x81,
+ 0x7e, 0x81, 0x80, 0x82, 0x84, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 195CD */
+ { 0x00, 0xe4, 0x00, 0xe3, 0x00, 0xe3, 0x84, 0x83, 0x83, 0x85, 0x85,
+ 0x85, 0x83, 0x84, 0x83, 0x81, 0x82, 0x82, 0x82, 0x83, 0x81, 0x81,
+ 0x80, 0x82, 0x7d, 0x7f, 0x81, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 207CD */
+ { 0x00, 0xe7, 0x00, 0xe6, 0x00, 0xe6, 0x83, 0x82, 0x82, 0x85, 0x85,
+ 0x85, 0x82, 0x83, 0x83, 0x82, 0x82, 0x82, 0x80, 0x81, 0x80, 0x81,
+ 0x80, 0x82, 0x7d, 0x7f, 0x81, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 220CD */
+ { 0x00, 0xeb, 0x00, 0xea, 0x00, 0xea, 0x83, 0x83, 0x82, 0x84, 0x84,
+ 0x84, 0x82, 0x83, 0x82, 0x81, 0x81, 0x82, 0x81, 0x82, 0x81, 0x80,
+ 0x7e, 0x80, 0x7d, 0x7f, 0x81, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 234CD */
+ { 0x00, 0xef, 0x00, 0xee, 0x00, 0xee, 0x83, 0x82, 0x82, 0x83, 0x83,
+ 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x7b, 0x7c, 0x7f, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 249CD */
+ { 0x00, 0xf3, 0x00, 0xf2, 0x00, 0xf2, 0x82, 0x81, 0x81, 0x83, 0x83,
+ 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x80, 0x81, 0x80, 0x7f,
+ 0x7e, 0x7f, 0x7b, 0x7c, 0x7f, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 265CD */
+ { 0x00, 0xf7, 0x00, 0xf7, 0x00, 0xf7, 0x81, 0x81, 0x80, 0x82, 0x82,
+ 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x80, 0x7f,
+ 0x7e, 0x7f, 0x7b, 0x7c, 0x7f, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 282CD */
+ { 0x00, 0xfb, 0x00, 0xfb, 0x00, 0xfb, 0x80, 0x80, 0x80, 0x81, 0x81,
+ 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x78, 0x79, 0x7d, 0x85, 0x85, 0x82, 0x00, 0x00, 0x00 },
+ /* 300CD */
+ { 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00 },
+};
+
+static int s6e88a0_ams427ap24_set_brightness(struct backlight_device *bd)
+{
+ struct s6e88a0_ams427ap24 *ctx = bl_get_data(bd);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ struct device *dev = &dsi->dev;
+ int brightness = bd->props.brightness;
+ int candela_enum;
+ u8 b2[LEN_AID] = { 0xb2, 0x40, 0x08, 0x20, 0x00, 0x00 };
+ u8 b6[LEN_ELVSS] = { 0xb6, 0x28, 0x00 };
+ u8 ca[LEN_GAMMA];
+
+ /* get candela enum from brightness */
+ for (candela_enum = 0; candela_enum < NUM_STEPS_CANDELA; candela_enum++)
+ if (brightness <= s6e88a0_ams427ap24_br_to_cd[candela_enum])
+ break;
+
+ /* get aid */
+ switch (candela_enum) {
+ case CANDELA_10CD ... CANDELA_105CD:
+ memcpy(&b2[FIX_LEN_AID],
+ s6e88a0_ams427ap24_aid[candela_enum],
+ VAR_LEN_AID);
+ break;
+ case CANDELA_111CD ... CANDELA_172CD:
+ memcpy(&b2[FIX_LEN_AID],
+ s6e88a0_ams427ap24_aid[CANDELA_111CD],
+ VAR_LEN_AID);
+ break;
+ case CANDELA_183CD ... CANDELA_300CD:
+ memcpy(&b2[FIX_LEN_AID],
+ s6e88a0_ams427ap24_aid[CANDELA_111CD + 1],
+ VAR_LEN_AID);
+ break;
+ default:
+ dev_err(dev, "Failed to get aid data\n");
+ return -EINVAL;
+ }
+
+ /* get elvss */
+ if (candela_enum <= CANDELA_111CD) {
+ memcpy(&b6[FIX_LEN_ELVSS],
+ s6e88a0_ams427ap24_elvss[0],
+ VAR_LEN_ELVSS);
+ } else {
+ memcpy(&b6[FIX_LEN_ELVSS],
+ s6e88a0_ams427ap24_elvss[candela_enum - CANDELA_111CD],
+ VAR_LEN_ELVSS);
+ }
+
+ /* get gamma */
+ ca[0] = 0xca;
+ memcpy(&ca[FIX_LEN_GAMMA],
+ s6e88a0_ams427ap24_gamma[candela_enum],
+ VAR_LEN_GAMMA);
+
+ /* write data */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a); // level 1 key on
+ mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, b2, ARRAY_SIZE(b2)); // set aid
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x00); // acl off
+ mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, b6, ARRAY_SIZE(b6)); // set elvss
+ mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, ca, ARRAY_SIZE(ca)); // set gamma
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x03); // gamma update
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5); // level 1 key off
+
+ return dsi_ctx.accum_err;
+}
+
+static void s6e88a0_ams427ap24_reset(struct s6e88a0_ams427ap24 *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(18000, 19000);
+}
+
+static int s6e88a0_ams427ap24_on(struct s6e88a0_ams427ap24 *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a); // level 1 key on
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0x5a, 0x5a); // level 2 key on
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x11); // src latch set global 1
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x11); // src latch set 1
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x13); // src latch set global 2
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x18); // src latch set 2
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02); // avdd set 1
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x30); // avdd set 2
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0x5a, 0x5a); // level 3 key on
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcc, 0x4c); // pixel clock divider pol.
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x03, 0x0d); // unknown
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0xa5, 0xa5); // level 3 key off
+
+ if (ctx->flip_horizontal)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcb, 0x0e); // flip display
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5); // level 1 key off
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0xa5, 0xa5); // level 2 key off
+
+ ret = s6e88a0_ams427ap24_set_brightness(ctx->bl_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set brightness: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static int s6e88a0_ams427ap24_off(struct s6e88a0_ams427ap24 *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return dsi_ctx.accum_err;
+}
+
+static int s6e88a0_ams427ap24_prepare(struct drm_panel *panel)
+{
+ struct s6e88a0_ams427ap24 *ctx = to_s6e88a0_ams427ap24(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
+ ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ s6e88a0_ams427ap24_reset(ctx);
+
+ ret = s6e88a0_ams427ap24_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
+ ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int s6e88a0_ams427ap24_unprepare(struct drm_panel *panel)
+{
+ struct s6e88a0_ams427ap24 *ctx = to_s6e88a0_ams427ap24(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = s6e88a0_ams427ap24_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
+ ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode s6e88a0_ams427ap24_mode = {
+ .clock = (540 + 94 + 4 + 18) * (960 + 12 + 1 + 3) * 60 / 1000,
+ .hdisplay = 540,
+ .hsync_start = 540 + 94,
+ .hsync_end = 540 + 94 + 4,
+ .htotal = 540 + 94 + 4 + 18,
+ .vdisplay = 960,
+ .vsync_start = 960 + 12,
+ .vsync_end = 960 + 12 + 1,
+ .vtotal = 960 + 12 + 1 + 3,
+ .width_mm = 55,
+ .height_mm = 95,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int s6e88a0_ams427ap24_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector,
+ &s6e88a0_ams427ap24_mode);
+}
+
+static const struct drm_panel_funcs s6e88a0_ams427ap24_panel_funcs = {
+ .prepare = s6e88a0_ams427ap24_prepare,
+ .unprepare = s6e88a0_ams427ap24_unprepare,
+ .get_modes = s6e88a0_ams427ap24_get_modes,
+};
+
+static const struct backlight_ops s6e88a0_ams427ap24_bl_ops = {
+ .update_status = s6e88a0_ams427ap24_set_brightness,
+};
+
+static int s6e88a0_ams427ap24_register_backlight(struct s6e88a0_ams427ap24 *ctx)
+{
+ struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 180,
+ .max_brightness = 255,
+ };
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret = 0;
+
+ ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev), dev, ctx,
+ &s6e88a0_ams427ap24_bl_ops,
+ &props);
+ if (IS_ERR(ctx->bl_dev)) {
+ ret = PTR_ERR(ctx->bl_dev);
+ dev_err(dev, "error registering backlight device (%d)\n", ret);
+ }
+
+ return ret;
+}
+
+static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct s6e88a0_ams427ap24 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(s6e88a0_ams427ap24_supplies),
+ s6e88a0_ams427ap24_supplies,
+ &ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 2;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_NO_HFP;
+
+ drm_panel_init(&ctx->panel, dev, &s6e88a0_ams427ap24_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ ctx->panel.prepare_prev_first = true;
+
+ ctx->flip_horizontal = device_property_read_bool(dev, "flip-horizontal");
+
+ ret = s6e88a0_ams427ap24_register_backlight(ctx);
+ if (ret < 0)
+ return ret;
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void s6e88a0_ams427ap24_remove(struct mipi_dsi_device *dsi)
+{
+ struct s6e88a0_ams427ap24 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id s6e88a0_ams427ap24_of_match[] = {
+ { .compatible = "samsung,s6e88a0-ams427ap24" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s6e88a0_ams427ap24_of_match);
+
+static struct mipi_dsi_driver s6e88a0_ams427ap24_driver = {
+ .probe = s6e88a0_ams427ap24_probe,
+ .remove = s6e88a0_ams427ap24_remove,
+ .driver = {
+ .name = "panel-s6e88a0-ams427ap24",
+ .of_match_table = s6e88a0_ams427ap24_of_match,
+ },
+};
+module_mipi_dsi_driver(s6e88a0_ams427ap24_driver);
+
+MODULE_AUTHOR("Jakob Hauser <jahau@rocketmail.com>");
+MODULE_DESCRIPTION("Samsung AMS427AP24 panel with S6E88A0 controller");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 86735430462f..06381c628209 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -4565,6 +4565,31 @@ static const struct panel_desc yes_optoelectronics_ytc700tlag_05_201c = {
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
+static const struct drm_display_mode mchp_ac69t88a_mode = {
+ .clock = 25000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 88,
+ .hsync_end = 800 + 88 + 5,
+ .htotal = 800 + 88 + 5 + 40,
+ .vdisplay = 480,
+ .vsync_start = 480 + 23,
+ .vsync_end = 480 + 23 + 5,
+ .vtotal = 480 + 23 + 5 + 1,
+};
+
+static const struct panel_desc mchp_ac69t88a = {
+ .modes = &mchp_ac69t88a_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 108,
+ .height = 65,
+ },
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode arm_rtsm_mode[] = {
{
.clock = 65000,
@@ -5049,6 +5074,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "yes-optoelectronics,ytc700tlag-05-201c",
.data = &yes_optoelectronics_ytc700tlag_05_201c,
}, {
+ .compatible = "microchip,ac69t88a",
+ .data = &mchp_ac69t88a,
+ }, {
/* Must be the last entry */
.compatible = "panel-dpi",
.data = &panel_dpi,
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index f19f918e2330..f5abde3866fb 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -177,7 +177,6 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev)
struct panfrost_model {
const char *name;
u32 id;
- u32 id_mask;
u64 features;
u64 issues;
struct {
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 554b236c2328..6f071e61f764 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -35,6 +35,7 @@
#include <linux/vgaarb.h>
#include <drm/drm_cache.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>
@@ -1542,7 +1543,7 @@ void radeon_device_fini(struct radeon_device *rdev)
* Called at driver suspend.
*/
int radeon_suspend_kms(struct drm_device *dev, bool suspend,
- bool fbcon, bool freeze)
+ bool notify_clients, bool freeze)
{
struct radeon_device *rdev;
struct pci_dev *pdev;
@@ -1634,9 +1635,9 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend,
pci_set_power_state(pdev, PCI_D3hot);
}
- if (fbcon) {
+ if (notify_clients) {
console_lock();
- radeon_fbdev_set_suspend(rdev, 1);
+ drm_client_dev_suspend(dev, true);
console_unlock();
}
return 0;
@@ -1649,7 +1650,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend,
* Returns 0 for success or an error on failure.
* Called at driver resume.
*/
-int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
+int radeon_resume_kms(struct drm_device *dev, bool resume, bool notify_clients)
{
struct drm_connector *connector;
struct radeon_device *rdev = dev->dev_private;
@@ -1660,14 +1661,14 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
- if (fbcon) {
+ if (notify_clients) {
console_lock();
}
if (resume) {
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
if (pci_enable_device(pdev)) {
- if (fbcon)
+ if (notify_clients)
console_unlock();
return -1;
}
@@ -1730,7 +1731,7 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
/* reset hpd state */
radeon_hpd_init(rdev);
/* blat the mode back in */
- if (fbcon) {
+ if (notify_clients) {
drm_helper_resume_force_mode(dev);
/* turn on display hw */
drm_modeset_lock_all(dev);
@@ -1746,8 +1747,8 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled)
radeon_pm_compute_clocks(rdev);
- if (fbcon) {
- radeon_fbdev_set_suspend(rdev, 0);
+ if (notify_clients) {
+ drm_client_dev_resume(dev, true);
console_unlock();
}
diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c
index 0aa20c8df546..d4a58bd679db 100644
--- a/drivers/gpu/drm/radeon/radeon_fbdev.c
+++ b/drivers/gpu/drm/radeon/radeon_fbdev.c
@@ -288,12 +288,6 @@ err_radeon_fbdev_destroy_pinned_object:
return ret;
}
-void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
-{
- if (rdev_to_drm(rdev)->fb_helper)
- drm_fb_helper_set_suspend(rdev_to_drm(rdev)->fb_helper, state);
-}
-
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
{
struct drm_fb_helper *fb_helper = rdev_to_drm(rdev)->fb_helper;
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 12a1d99a1815..4063d3801e81 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -942,13 +942,10 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes);
#define RADEON_FBDEV_DRIVER_OPS \
.fbdev_probe = radeon_fbdev_driver_fbdev_probe
-void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
#else
#define RADEON_FBDEV_DRIVER_OPS \
.fbdev_probe = NULL
-static inline void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
-{ }
static inline bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
{
return false;
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 258dd38e1a8e..3ac579615749 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -9,6 +9,7 @@ config DRM_ROCKCHIP
select VIDEOMODE_HELPERS
select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
+ select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP
select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
@@ -64,6 +65,14 @@ config ROCKCHIP_DW_HDMI
enable HDMI on RK3288 or RK3399 based SoC, you should select
this option.
+config ROCKCHIP_DW_HDMI_QP
+ bool "Rockchip specific extensions for Synopsys DW HDMI QP"
+ select DRM_BRIDGE_CONNECTOR
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare HDMI QP driver. If you want to
+ enable HDMI on RK3588 based SoC, you should select this option.
+
config ROCKCHIP_DW_MIPI_DSI
bool "Rockchip specific extensions for Synopsys DW MIPI DSI"
select GENERIC_PHY_MIPI_DPHY
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 3ff7b21c0414..3eab662a5a1d 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -11,6 +11,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
new file mode 100644
index 000000000000..9c796ee4c303
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2024 Collabora Ltd.
+ *
+ * Author: Algea Cao <algea.cao@rock-chips.com>
+ * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+
+#include <drm/bridge/dw_hdmi_qp.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "rockchip_drm_drv.h"
+
+#define RK3588_GRF_SOC_CON2 0x0308
+#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
+#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
+#define RK3588_GRF_SOC_CON7 0x031c
+#define RK3588_SET_HPD_PATH_MASK GENMASK(13, 12)
+#define RK3588_GRF_SOC_STATUS1 0x0384
+#define RK3588_HDMI0_LEVEL_INT BIT(16)
+#define RK3588_GRF_VO1_CON3 0x000c
+#define RK3588_SCLIN_MASK BIT(9)
+#define RK3588_SDAIN_MASK BIT(10)
+#define RK3588_MODE_MASK BIT(11)
+#define RK3588_I2S_SEL_MASK BIT(13)
+#define RK3588_GRF_VO1_CON9 0x0024
+#define RK3588_HDMI0_GRANT_SEL BIT(10)
+
+#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
+#define HOTPLUG_DEBOUNCE_MS 150
+
+struct rockchip_hdmi_qp {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap *vo_regmap;
+ struct rockchip_encoder encoder;
+ struct clk *ref_clk;
+ struct dw_hdmi_qp *hdmi;
+ struct phy *phy;
+ struct gpio_desc *enable_gpio;
+ struct delayed_work hpd_work;
+};
+
+static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
+{
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
+}
+
+static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
+{
+ struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
+ unsigned long long rate;
+
+ /* Unconditionally switch to TMDS as FRL is not yet supported */
+ gpiod_set_value(hdmi->enable_gpio, 1);
+
+ if (crtc && crtc->state) {
+ rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
+ 8, HDMI_COLORSPACE_RGB);
+ clk_set_rate(hdmi->ref_clk, rate);
+ /*
+ * FIXME: Temporary workaround to pass pixel clock rate
+ * to the PHY driver until phy_configure_opts_hdmi
+ * becomes available in the PHY API. See also the related
+ * comment in rk_hdptx_phy_power_on() from
+ * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+ */
+ phy_set_bus_width(hdmi->phy, rate / 100);
+ }
+}
+
+static int
+dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+
+ return 0;
+}
+
+static const struct
+drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
+ .enable = dw_hdmi_qp_rockchip_encoder_enable,
+ .atomic_check = dw_hdmi_qp_rockchip_encoder_atomic_check,
+};
+
+static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ return phy_power_on(hdmi->phy);
+}
+
+static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
+ void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ phy_power_off(hdmi->phy);
+}
+
+static enum drm_connector_status
+dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+ u32 val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
+
+ return val & RK3588_HDMI0_LEVEL_INT ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ regmap_write(hdmi->regmap,
+ RK3588_GRF_SOC_CON2,
+ HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR |
+ RK3588_HDMI0_HPD_INT_MSK));
+}
+
+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
+ .init = dw_hdmi_qp_rk3588_phy_init,
+ .disable = dw_hdmi_qp_rk3588_phy_disable,
+ .read_hpd = dw_hdmi_qp_rk3588_read_hpd,
+ .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd,
+};
+
+static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
+{
+ struct rockchip_hdmi_qp *hdmi = container_of(work,
+ struct rockchip_hdmi_qp,
+ hpd_work.work);
+ struct drm_device *drm = hdmi->encoder.encoder.dev;
+ bool changed;
+
+ if (drm) {
+ changed = drm_helper_hpd_irq_event(drm);
+ if (changed)
+ drm_dbg(hdmi, "connector status changed\n");
+ }
+}
+
+static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_id;
+ u32 intr_stat, val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+ if (intr_stat) {
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
+ RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_id;
+ u32 intr_stat, val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+ if (!intr_stat)
+ return IRQ_NONE;
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ mod_delayed_work(system_wq, &hdmi->hpd_work,
+ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+ val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
+ { .compatible = "rockchip,rk3588-dw-hdmi-qp",
+ .data = &rk3588_hdmi_phy_ops },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
+
+static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ static const char * const clk_names[] = {
+ "pclk", "earc", "aud", "hdp", "hclk_vo1",
+ "ref" /* keep "ref" last */
+ };
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi_qp_plat_data plat_data;
+ struct drm_device *drm = data;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct rockchip_hdmi_qp *hdmi;
+ struct clk *clk;
+ int ret, irq, i;
+ u32 val;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ plat_data.phy_ops = of_device_get_match_data(dev);
+ if (!plat_data.phy_ops)
+ return -ENODEV;
+
+ plat_data.phy_data = hdmi;
+ hdmi->dev = &pdev->dev;
+
+ encoder = &hdmi->encoder.encoder;
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
+ dev->of_node, 0, 0);
+ /*
+ * If we failed to find the CRTC(s) which this encoder is
+ * supposed to be connected to, it's because the CRTC has
+ * not been registered yet. Defer probing, and hope that
+ * the required CRTC is added later.
+ */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmi->regmap)) {
+ drm_err(hdmi, "Unable to get rockchip,grf\n");
+ return PTR_ERR(hdmi->regmap);
+ }
+
+ hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,vo-grf");
+ if (IS_ERR(hdmi->vo_regmap)) {
+ drm_err(hdmi, "Unable to get rockchip,vo-grf\n");
+ return PTR_ERR(hdmi->vo_regmap);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
+ clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]);
+
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ if (ret != -EPROBE_DEFER)
+ drm_err(hdmi, "Failed to get %s clock: %d\n",
+ clk_names[i], ret);
+ return ret;
+ }
+ }
+ hdmi->ref_clk = clk;
+
+ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(hdmi->enable_gpio)) {
+ ret = PTR_ERR(hdmi->enable_gpio);
+ drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret);
+ return ret;
+ }
+
+ hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+ if (IS_ERR(hdmi->phy)) {
+ ret = PTR_ERR(hdmi->phy);
+ if (ret != -EPROBE_DEFER)
+ drm_err(hdmi, "failed to get phy: %d\n", ret);
+ return ret;
+ }
+
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
+
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
+
+ plat_data.main_irq = platform_get_irq_byname(pdev, "main");
+ if (plat_data.main_irq < 0)
+ return plat_data.main_irq;
+
+ irq = platform_get_irq_byname(pdev, "hpd");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(hdmi->dev, irq,
+ dw_hdmi_qp_rk3588_hardirq,
+ dw_hdmi_qp_rk3588_irq,
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
+ hdmi);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+ platform_set_drvdata(pdev, hdmi);
+
+ hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
+ if (IS_ERR(hdmi->hdmi)) {
+ ret = PTR_ERR(hdmi->hdmi);
+ drm_encoder_cleanup(encoder);
+ return ret;
+ }
+
+ connector = drm_bridge_connector_init(drm, encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ drm_err(hdmi, "failed to init bridge connector: %d\n", ret);
+ return ret;
+ }
+
+ return drm_connector_attach_encoder(connector, encoder);
+}
+
+static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&hdmi->hpd_work);
+
+ drm_encoder_cleanup(&hdmi->encoder.encoder);
+}
+
+static const struct component_ops dw_hdmi_qp_rockchip_ops = {
+ .bind = dw_hdmi_qp_rockchip_bind,
+ .unbind = dw_hdmi_qp_rockchip_unbind,
+};
+
+static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
+}
+
+static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
+}
+
+static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+ u32 val;
+
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
+
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
+
+ dw_hdmi_qp_resume(dev, hdmi->hdmi);
+
+ if (hdmi->encoder.encoder.dev)
+ drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume)
+};
+
+struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
+ .probe = dw_hdmi_qp_rockchip_probe,
+ .remove = dw_hdmi_qp_rockchip_remove,
+ .driver = {
+ .name = "dwhdmiqp-rockchip",
+ .pm = &dw_hdmi_qp_rockchip_pm,
+ .of_match_table = dw_hdmi_qp_rockchip_dt_ids,
+ },
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 2560b0eec9db..585355de696b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -532,6 +532,8 @@ static int __init rockchip_drm_init(void)
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
CONFIG_ROCKCHIP_DW_HDMI);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
+ CONFIG_ROCKCHIP_DW_HDMI_QP);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 8d566fcd80a2..24b4ce5ceaf1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -88,6 +88,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
+extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver;
extern struct platform_driver dw_mipi_dsi_rockchip_driver;
extern struct platform_driver inno_hdmi_driver;
extern struct platform_driver rockchip_dp_driver;
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index dab8cca79eb7..8451f3884a46 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -593,6 +593,9 @@ static void drm_sched_job_timedout(struct work_struct *work)
* callers responsibility to release it manually if it's not part of the
* pending list any more.
*
+ * This function is typically used for reset recovery (see the docu of
+ * drm_sched_backend_ops.timedout_job() for details). Do not call it for
+ * scheduler teardown, i.e., before calling drm_sched_fini().
*/
void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
{
@@ -665,7 +668,6 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
*/
cancel_delayed_work(&sched->work_tdr);
}
-
EXPORT_SYMBOL(drm_sched_stop);
/**
@@ -674,6 +676,10 @@ EXPORT_SYMBOL(drm_sched_stop);
* @sched: scheduler instance
* @errno: error to set on the pending fences
*
+ * This function is typically used for reset recovery (see the docu of
+ * drm_sched_backend_ops.timedout_job() for details). Do not call it for
+ * scheduler startup. The scheduler itself is fully operational after
+ * drm_sched_init() succeeded.
*/
void drm_sched_start(struct drm_gpu_scheduler *sched, int errno)
{
@@ -771,6 +777,10 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs);
* Drivers must make sure drm_sched_job_cleanup() if this function returns
* successfully, even when @job is aborted before drm_sched_job_arm() is called.
*
+ * Note that this function does not assign a valid value to each struct member
+ * of struct drm_sched_job. Take a look at that struct's documentation to see
+ * who sets which struct member with what lifetime.
+ *
* WARNING: amdgpu abuses &drm_sched.ready to signal when the hardware
* has died, which can mean that there's no valid runqueue for a @entity.
* This function returns -ENOENT in this case (which probably should be -EIO as
@@ -796,6 +806,14 @@ int drm_sched_job_init(struct drm_sched_job *job,
return -EINVAL;
}
+ /*
+ * We don't know for sure how the user has allocated. Thus, zero the
+ * struct so that unallowed (i.e., too early) usage of pointers that
+ * this function does not set is guaranteed to lead to a NULL pointer
+ * exception instead of UB.
+ */
+ memset(job, 0, sizeof(*job));
+
job->entity = entity;
job->credits = credits;
job->s_fence = drm_sched_fence_alloc(entity, owner);
diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c
index 0b69c140eab3..44a7a579660f 100644
--- a/drivers/gpu/drm/sprd/sprd_dsi.c
+++ b/drivers/gpu/drm/sprd/sprd_dsi.c
@@ -209,7 +209,7 @@ static int regmap_tst_io_read(void *context, u32 reg, u32 *val)
return 0;
}
-static struct regmap_bus regmap_tst_io = {
+static const struct regmap_bus regmap_tst_io = {
.reg_write = regmap_tst_io_write,
.reg_read = regmap_tst_io_read,
};
diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
index f542aae334d5..94cbdb1337c0 100644
--- a/drivers/gpu/drm/tiny/Kconfig
+++ b/drivers/gpu/drm/tiny/Kconfig
@@ -198,6 +198,27 @@ config TINYDRM_REPAPER
If M is selected the module will be called repaper.
+config TINYDRM_SHARP_MEMORY
+ tristate "DRM support for Sharp Memory LCD panels"
+ depends on DRM && SPI
+ select DRM_CLIENT_SELECTION
+ select DRM_GEM_DMA_HELPER
+ select DRM_KMS_HELPER
+ help
+ DRM Driver for the following Sharp Memory Panels:
+ * 1.00" Sharp Memory LCD (LS010B7DH04)
+ * 1.10" Sharp Memory LCD (LS011B7DH03)
+ * 1.20" Sharp Memory LCD (LS012B7DD01)
+ * 1.28" Sharp Memory LCD (LS013B7DH03)
+ * 1.26" Sharp Memory LCD (LS013B7DH05)
+ * 1.80" Sharp Memory LCD (LS018B7DH02)
+ * 2.70" Sharp Memory LCD (LS027B7DH01)
+ * 2.70" Sharp Memory LCD (LS027B7DH01A)
+ * 3.20" Sharp Memory LCD (LS032B7DD02)
+ * 4.40" Sharp Memory LCD (LS044Q7DH01)
+
+ If M is selected the module will be called sharp_memory.
+
config TINYDRM_ST7586
tristate "DRM support for Sitronix ST7586 display panels"
depends on DRM && SPI
diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile
index 76dde89a044b..4aaf56f8707d 100644
--- a/drivers/gpu/drm/tiny/Makefile
+++ b/drivers/gpu/drm/tiny/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o
obj-$(CONFIG_TINYDRM_ILI9486) += ili9486.o
obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o
+obj-$(CONFIG_TINYDRM_SHARP_MEMORY) += sharp-memory.o
obj-$(CONFIG_TINYDRM_ST7586) += st7586.o
obj-$(CONFIG_TINYDRM_ST7735R) += st7735r.o
diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c
new file mode 100644
index 000000000000..2d2315bd6aef
--- /dev/null
+++ b/drivers/gpu/drm/tiny/sharp-memory.c
@@ -0,0 +1,671 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_client_setup.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_rect.h>
+#include <linux/bitrev.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kthread.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pwm.h>
+#include <linux/spi/spi.h>
+
+#define SHARP_MODE_PERIOD 8
+#define SHARP_ADDR_PERIOD 8
+#define SHARP_DUMMY_PERIOD 8
+
+#define SHARP_MEMORY_DISPLAY_MAINTAIN_MODE 0
+#define SHARP_MEMORY_DISPLAY_UPDATE_MODE 1
+#define SHARP_MEMORY_DISPLAY_CLEAR_MODE 4
+
+enum sharp_memory_model {
+ LS010B7DH04,
+ LS011B7DH03,
+ LS012B7DD01,
+ LS013B7DH03,
+ LS013B7DH05,
+ LS018B7DH02,
+ LS027B7DH01,
+ LS027B7DH01A,
+ LS032B7DD02,
+ LS044Q7DH01,
+};
+
+enum sharp_memory_vcom_mode {
+ SHARP_MEMORY_SOFTWARE_VCOM,
+ SHARP_MEMORY_EXTERNAL_VCOM,
+ SHARP_MEMORY_PWM_VCOM
+};
+
+struct sharp_memory_device {
+ struct drm_device drm;
+ struct spi_device *spi;
+
+ const struct drm_display_mode *mode;
+
+ struct drm_crtc crtc;
+ struct drm_plane plane;
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct gpio_desc *enable_gpio;
+
+ struct task_struct *sw_vcom_signal;
+ struct pwm_device *pwm_vcom_signal;
+
+ enum sharp_memory_vcom_mode vcom_mode;
+ u8 vcom;
+
+ u32 pitch;
+ u32 tx_buffer_size;
+ u8 *tx_buffer;
+
+ /* When vcom_mode == "software" a kthread is used to periodically send a
+ * 'maintain display' message over spi. This mutex ensures tx_buffer access
+ * and spi bus usage is synchronized in this case.
+ */
+ struct mutex tx_mutex;
+};
+
+static inline int sharp_memory_spi_write(struct spi_device *spi, void *buf, size_t len)
+{
+ /* Reverse the bit order */
+ for (u8 *b = buf; b < ((u8 *)buf) + len; ++b)
+ *b = bitrev8(*b);
+
+ return spi_write(spi, buf, len);
+}
+
+static inline struct sharp_memory_device *drm_to_sharp_memory_device(struct drm_device *drm)
+{
+ return container_of(drm, struct sharp_memory_device, drm);
+}
+
+DEFINE_DRM_GEM_DMA_FOPS(sharp_memory_fops);
+
+static const struct drm_driver sharp_memory_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .fops = &sharp_memory_fops,
+ DRM_GEM_DMA_DRIVER_OPS_VMAP,
+ DRM_FBDEV_DMA_DRIVER_OPS,
+ .name = "sharp_memory_display",
+ .desc = "Sharp Display Memory LCD",
+ .date = "20231129",
+ .major = 1,
+ .minor = 0,
+};
+
+static inline void sharp_memory_set_tx_buffer_mode(u8 *buffer, u8 mode, u8 vcom)
+{
+ *buffer = mode | (vcom << 1);
+}
+
+static inline void sharp_memory_set_tx_buffer_addresses(u8 *buffer,
+ struct drm_rect clip,
+ u32 pitch)
+{
+ for (u32 line = 0; line < clip.y2; ++line)
+ buffer[line * pitch] = line + 1;
+}
+
+static void sharp_memory_set_tx_buffer_data(u8 *buffer,
+ struct drm_framebuffer *fb,
+ struct drm_rect clip,
+ u32 pitch,
+ struct drm_format_conv_state *fmtcnv_state)
+{
+ int ret;
+ struct iosys_map dst, vmap;
+ struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
+
+ ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
+ if (ret)
+ return;
+
+ iosys_map_set_vaddr(&dst, buffer);
+ iosys_map_set_vaddr(&vmap, dma_obj->vaddr);
+
+ drm_fb_xrgb8888_to_mono(&dst, &pitch, &vmap, fb, &clip, fmtcnv_state);
+
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+}
+
+static int sharp_memory_update_display(struct sharp_memory_device *smd,
+ struct drm_framebuffer *fb,
+ struct drm_rect clip,
+ struct drm_format_conv_state *fmtcnv_state)
+{
+ int ret;
+ u32 pitch = smd->pitch;
+ u8 vcom = smd->vcom;
+ u8 *tx_buffer = smd->tx_buffer;
+ u32 tx_buffer_size = smd->tx_buffer_size;
+
+ mutex_lock(&smd->tx_mutex);
+
+ /* Populate the transmit buffer with frame data */
+ sharp_memory_set_tx_buffer_mode(&tx_buffer[0],
+ SHARP_MEMORY_DISPLAY_UPDATE_MODE, vcom);
+ sharp_memory_set_tx_buffer_addresses(&tx_buffer[1], clip, pitch);
+ sharp_memory_set_tx_buffer_data(&tx_buffer[2], fb, clip, pitch, fmtcnv_state);
+
+ ret = sharp_memory_spi_write(smd->spi, tx_buffer, tx_buffer_size);
+
+ mutex_unlock(&smd->tx_mutex);
+
+ return ret;
+}
+
+static int sharp_memory_maintain_display(struct sharp_memory_device *smd)
+{
+ int ret;
+ u8 vcom = smd->vcom;
+ u8 *tx_buffer = smd->tx_buffer;
+
+ mutex_lock(&smd->tx_mutex);
+
+ sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_MAINTAIN_MODE, vcom);
+ tx_buffer[1] = 0; /* Write dummy data */
+ ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2);
+
+ mutex_unlock(&smd->tx_mutex);
+
+ return ret;
+}
+
+static int sharp_memory_clear_display(struct sharp_memory_device *smd)
+{
+ int ret;
+ u8 vcom = smd->vcom;
+ u8 *tx_buffer = smd->tx_buffer;
+
+ mutex_lock(&smd->tx_mutex);
+
+ sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_CLEAR_MODE, vcom);
+ tx_buffer[1] = 0; /* write dummy data */
+ ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2);
+
+ mutex_unlock(&smd->tx_mutex);
+
+ return ret;
+}
+
+static void sharp_memory_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect,
+ struct drm_format_conv_state *fmtconv_state)
+{
+ struct drm_rect clip;
+ struct sharp_memory_device *smd = drm_to_sharp_memory_device(fb->dev);
+
+ /* Always update a full line regardless of what is dirty */
+ clip.x1 = 0;
+ clip.x2 = fb->width;
+ clip.y1 = rect->y1;
+ clip.y2 = rect->y2;
+
+ sharp_memory_update_display(smd, fb, clip, fmtconv_state);
+}
+
+static int sharp_memory_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct sharp_memory_device *smd;
+ struct drm_crtc_state *crtc_state;
+
+ smd = container_of(plane, struct sharp_memory_device, plane);
+ crtc_state = drm_atomic_get_new_crtc_state(state, &smd->crtc);
+
+ return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+}
+
+static void sharp_memory_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+ struct drm_plane_state *plane_state = plane->state;
+ struct drm_format_conv_state fmtcnv_state = DRM_FORMAT_CONV_STATE_INIT;
+ struct sharp_memory_device *smd;
+ struct drm_rect rect;
+
+ smd = container_of(plane, struct sharp_memory_device, plane);
+ if (!smd->crtc.state->active)
+ return;
+
+ if (drm_atomic_helper_damage_merged(old_state, plane_state, &rect))
+ sharp_memory_fb_dirty(plane_state->fb, &rect, &fmtcnv_state);
+
+ drm_format_conv_state_release(&fmtcnv_state);
+}
+
+static const struct drm_plane_helper_funcs sharp_memory_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = sharp_memory_plane_atomic_check,
+ .atomic_update = sharp_memory_plane_atomic_update,
+};
+
+static bool sharp_memory_format_mod_supported(struct drm_plane *plane,
+ u32 format,
+ u64 modifier)
+{
+ return modifier == DRM_FORMAT_MOD_LINEAR;
+}
+
+static const struct drm_plane_funcs sharp_memory_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .format_mod_supported = sharp_memory_format_mod_supported,
+};
+
+static enum drm_mode_status sharp_memory_crtc_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
+
+ return drm_crtc_helper_mode_valid_fixed(crtc, mode, smd->mode);
+}
+
+static int sharp_memory_crtc_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static int sharp_memory_sw_vcom_signal_thread(void *data)
+{
+ struct sharp_memory_device *smd = data;
+
+ while (!kthread_should_stop()) {
+ smd->vcom ^= 1; /* Toggle vcom */
+ sharp_memory_maintain_display(smd);
+ msleep(1000);
+ }
+
+ return 0;
+}
+
+static void sharp_memory_crtc_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
+
+ sharp_memory_clear_display(smd);
+
+ if (smd->enable_gpio)
+ gpiod_set_value(smd->enable_gpio, 1);
+}
+
+static void sharp_memory_crtc_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
+
+ sharp_memory_clear_display(smd);
+
+ if (smd->enable_gpio)
+ gpiod_set_value(smd->enable_gpio, 0);
+}
+
+static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = {
+ .mode_valid = sharp_memory_crtc_mode_valid,
+ .atomic_check = sharp_memory_crtc_check,
+ .atomic_enable = sharp_memory_crtc_enable,
+ .atomic_disable = sharp_memory_crtc_disable,
+};
+
+static const struct drm_crtc_funcs sharp_memory_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs sharp_memory_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int sharp_memory_connector_get_modes(struct drm_connector *connector)
+{
+ struct sharp_memory_device *smd = drm_to_sharp_memory_device(connector->dev);
+
+ return drm_connector_helper_get_modes_fixed(connector, smd->mode);
+}
+
+static const struct drm_connector_helper_funcs sharp_memory_connector_hfuncs = {
+ .get_modes = sharp_memory_connector_get_modes,
+};
+
+static const struct drm_connector_funcs sharp_memory_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+
+};
+
+static const struct drm_mode_config_funcs sharp_memory_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create_with_dirty,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static const struct drm_display_mode sharp_memory_ls010b7dh04_mode = {
+ DRM_SIMPLE_MODE(128, 128, 18, 18),
+};
+
+static const struct drm_display_mode sharp_memory_ls011b7dh03_mode = {
+ DRM_SIMPLE_MODE(160, 68, 25, 10),
+};
+
+static const struct drm_display_mode sharp_memory_ls012b7dd01_mode = {
+ DRM_SIMPLE_MODE(184, 38, 29, 6),
+};
+
+static const struct drm_display_mode sharp_memory_ls013b7dh03_mode = {
+ DRM_SIMPLE_MODE(128, 128, 23, 23),
+};
+
+static const struct drm_display_mode sharp_memory_ls013b7dh05_mode = {
+ DRM_SIMPLE_MODE(144, 168, 20, 24),
+};
+
+static const struct drm_display_mode sharp_memory_ls018b7dh02_mode = {
+ DRM_SIMPLE_MODE(230, 303, 27, 36),
+};
+
+static const struct drm_display_mode sharp_memory_ls027b7dh01_mode = {
+ DRM_SIMPLE_MODE(400, 240, 58, 35),
+};
+
+static const struct drm_display_mode sharp_memory_ls032b7dd02_mode = {
+ DRM_SIMPLE_MODE(336, 536, 42, 68),
+};
+
+static const struct drm_display_mode sharp_memory_ls044q7dh01_mode = {
+ DRM_SIMPLE_MODE(320, 240, 89, 67),
+};
+
+static const struct spi_device_id sharp_memory_ids[] = {
+ {"ls010b7dh04", (kernel_ulong_t)&sharp_memory_ls010b7dh04_mode},
+ {"ls011b7dh03", (kernel_ulong_t)&sharp_memory_ls011b7dh03_mode},
+ {"ls012b7dd01", (kernel_ulong_t)&sharp_memory_ls012b7dd01_mode},
+ {"ls013b7dh03", (kernel_ulong_t)&sharp_memory_ls013b7dh03_mode},
+ {"ls013b7dh05", (kernel_ulong_t)&sharp_memory_ls013b7dh05_mode},
+ {"ls018b7dh02", (kernel_ulong_t)&sharp_memory_ls018b7dh02_mode},
+ {"ls027b7dh01", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},
+ {"ls027b7dh01a", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},
+ {"ls032b7dd02", (kernel_ulong_t)&sharp_memory_ls032b7dd02_mode},
+ {"ls044q7dh01", (kernel_ulong_t)&sharp_memory_ls044q7dh01_mode},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, sharp_memory_ids);
+
+static const struct of_device_id sharp_memory_of_match[] = {
+ {.compatible = "sharp,ls010b7dh04", &sharp_memory_ls010b7dh04_mode},
+ {.compatible = "sharp,ls011b7dh03", &sharp_memory_ls011b7dh03_mode},
+ {.compatible = "sharp,ls012b7dd01", &sharp_memory_ls012b7dd01_mode},
+ {.compatible = "sharp,ls013b7dh03", &sharp_memory_ls013b7dh03_mode},
+ {.compatible = "sharp,ls013b7dh05", &sharp_memory_ls013b7dh05_mode},
+ {.compatible = "sharp,ls018b7dh02", &sharp_memory_ls018b7dh02_mode},
+ {.compatible = "sharp,ls027b7dh01", &sharp_memory_ls027b7dh01_mode},
+ {.compatible = "sharp,ls027b7dh01a", &sharp_memory_ls027b7dh01_mode},
+ {.compatible = "sharp,ls032b7dd02", &sharp_memory_ls032b7dd02_mode},
+ {.compatible = "sharp,ls044q7dh01", &sharp_memory_ls044q7dh01_mode},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sharp_memory_of_match);
+
+static const u32 sharp_memory_formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
+static int sharp_memory_pipe_init(struct drm_device *dev,
+ struct sharp_memory_device *smd,
+ const u32 *formats, unsigned int format_count,
+ const u64 *format_modifiers)
+{
+ int ret;
+ struct drm_encoder *encoder = &smd->encoder;
+ struct drm_plane *plane = &smd->plane;
+ struct drm_crtc *crtc = &smd->crtc;
+ struct drm_connector *connector = &smd->connector;
+
+ drm_plane_helper_add(plane, &sharp_memory_plane_helper_funcs);
+ ret = drm_universal_plane_init(dev, plane, 0,
+ &sharp_memory_plane_funcs,
+ formats, format_count,
+ format_modifiers,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(crtc, &sharp_memory_crtc_helper_funcs);
+ ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+ &sharp_memory_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+ ret = drm_encoder_init(dev, encoder, &sharp_memory_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_init(&smd->drm, &smd->connector,
+ &sharp_memory_connector_funcs,
+ DRM_MODE_CONNECTOR_SPI);
+ if (ret)
+ return ret;
+
+ drm_connector_helper_add(&smd->connector,
+ &sharp_memory_connector_hfuncs);
+
+ return drm_connector_attach_encoder(connector, encoder);
+}
+
+static int sharp_memory_init_pwm_vcom_signal(struct sharp_memory_device *smd)
+{
+ int ret;
+ struct device *dev = &smd->spi->dev;
+ struct pwm_state pwm_state;
+
+ smd->pwm_vcom_signal = devm_pwm_get(dev, NULL);
+ if (IS_ERR(smd->pwm_vcom_signal))
+ return dev_err_probe(dev, PTR_ERR(smd->pwm_vcom_signal),
+ "Could not get pwm device\n");
+
+ pwm_init_state(smd->pwm_vcom_signal, &pwm_state);
+ pwm_set_relative_duty_cycle(&pwm_state, 1, 10);
+ pwm_state.enabled = true;
+ ret = pwm_apply_might_sleep(smd->pwm_vcom_signal, &pwm_state);
+ if (ret)
+ return dev_err_probe(dev, -EINVAL, "Could not apply pwm state\n");
+
+ return 0;
+}
+
+static int sharp_memory_probe(struct spi_device *spi)
+{
+ int ret;
+ struct device *dev;
+ struct sharp_memory_device *smd;
+ struct drm_device *drm;
+ const char *vcom_mode_str;
+
+ dev = &spi->dev;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to setup spi device\n");
+
+ if (!dev->coherent_dma_mask) {
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set dma mask\n");
+ }
+
+ smd = devm_drm_dev_alloc(dev, &sharp_memory_drm_driver,
+ struct sharp_memory_device, drm);
+ if (!smd)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, smd);
+
+ smd->spi = spi;
+ drm = &smd->drm;
+ ret = drmm_mode_config_init(drm);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize drm config\n");
+
+ smd->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (!smd->enable_gpio)
+ dev_warn(dev, "Enable gpio not defined\n");
+
+ drm->mode_config.funcs = &sharp_memory_mode_config_funcs;
+ smd->mode = spi_get_device_match_data(spi);
+
+ smd->pitch = (SHARP_ADDR_PERIOD + smd->mode->hdisplay + SHARP_DUMMY_PERIOD) / 8;
+ smd->tx_buffer_size = (SHARP_MODE_PERIOD +
+ (SHARP_ADDR_PERIOD + (smd->mode->hdisplay) + SHARP_DUMMY_PERIOD) *
+ smd->mode->vdisplay) / 8;
+
+ smd->tx_buffer = devm_kzalloc(dev, smd->tx_buffer_size, GFP_KERNEL);
+ if (!smd->tx_buffer)
+ return -ENOMEM;
+
+ mutex_init(&smd->tx_mutex);
+
+ /*
+ * VCOM is a signal that prevents DC bias from being built up in
+ * the panel resulting in pixels being forever stuck in one state.
+ *
+ * This driver supports three different methods to generate this
+ * signal depending on EXTMODE pin:
+ *
+ * software (EXTMODE = L) - This mode uses a kthread to
+ * periodically send a "maintain display" message to the display,
+ * toggling the vcom bit on and off with each message
+ *
+ * external (EXTMODE = H) - This mode relies on an external
+ * clock to generate the signal on the EXTCOMM pin
+ *
+ * pwm (EXTMODE = H) - This mode uses a pwm device to generate
+ * the signal on the EXTCOMM pin
+ *
+ */
+ if (device_property_read_string(dev, "sharp,vcom-mode", &vcom_mode_str))
+ return dev_err_probe(dev, -EINVAL,
+ "Unable to find sharp,vcom-mode node in device tree\n");
+
+ if (!strcmp("software", vcom_mode_str)) {
+ smd->vcom_mode = SHARP_MEMORY_SOFTWARE_VCOM;
+ smd->sw_vcom_signal = kthread_run(sharp_memory_sw_vcom_signal_thread,
+ smd, "sw_vcom_signal");
+
+ } else if (!strcmp("external", vcom_mode_str)) {
+ smd->vcom_mode = SHARP_MEMORY_EXTERNAL_VCOM;
+
+ } else if (!strcmp("pwm", vcom_mode_str)) {
+ smd->vcom_mode = SHARP_MEMORY_PWM_VCOM;
+ ret = sharp_memory_init_pwm_vcom_signal(smd);
+ if (ret)
+ return ret;
+ } else {
+ return dev_err_probe(dev, -EINVAL, "Invalid value set for vcom-mode\n");
+ }
+
+ drm->mode_config.min_width = smd->mode->hdisplay;
+ drm->mode_config.max_width = smd->mode->hdisplay;
+ drm->mode_config.min_height = smd->mode->vdisplay;
+ drm->mode_config.max_height = smd->mode->vdisplay;
+
+ ret = sharp_memory_pipe_init(drm, smd, sharp_memory_formats,
+ ARRAY_SIZE(sharp_memory_formats),
+ NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize display pipeline.\n");
+
+ drm_plane_enable_fb_damage_clips(&smd->plane);
+ drm_mode_config_reset(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register drm device.\n");
+
+ drm_client_setup(drm, NULL);
+
+ return 0;
+}
+
+static void sharp_memory_remove(struct spi_device *spi)
+{
+ struct sharp_memory_device *smd = spi_get_drvdata(spi);
+
+ drm_dev_unplug(&smd->drm);
+ drm_atomic_helper_shutdown(&smd->drm);
+
+ switch (smd->vcom_mode) {
+ case SHARP_MEMORY_SOFTWARE_VCOM:
+ kthread_stop(smd->sw_vcom_signal);
+ break;
+
+ case SHARP_MEMORY_EXTERNAL_VCOM:
+ break;
+
+ case SHARP_MEMORY_PWM_VCOM:
+ pwm_disable(smd->pwm_vcom_signal);
+ break;
+ }
+}
+
+static struct spi_driver sharp_memory_spi_driver = {
+ .driver = {
+ .name = "sharp_memory",
+ .of_match_table = sharp_memory_of_match,
+ },
+ .probe = sharp_memory_probe,
+ .remove = sharp_memory_remove,
+ .id_table = sharp_memory_ids,
+};
+module_spi_driver(sharp_memory_spi_driver);
+
+MODULE_AUTHOR("Alex Lanzano <lanzano.alex@gmail.com>");
+MODULE_DESCRIPTION("SPI Protocol driver for the sharp_memory display");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index 7055f7c7bcfe..73ab7dd31b17 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -157,13 +157,8 @@ struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
struct v3d_bo *bo;
int ret;
- /* Let the user opt out of allocating the BOs with THP */
- if (v3d->gemfs)
- shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size,
- v3d->gemfs);
- else
- shmem_obj = drm_gem_shmem_create(dev, unaligned_size);
-
+ shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size,
+ v3d->gemfs);
if (IS_ERR(shmem_obj))
return ERR_CAST(shmem_obj);
bo = to_v3d_bo(&shmem_obj->base);
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index ebd0879e04d4..bac96c0dd66e 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -8,6 +8,7 @@ config DRM_XE
select SHMEM
select TMPFS
select DRM_BUDDY
+ select DRM_CLIENT_SELECTION
select DRM_EXEC
select DRM_KMS_HELPER
select DRM_KUNIT_TEST_HELPERS if DRM_XE_KUNIT_TEST != n
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 9368acf56eaf..e4e0e299e8a7 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1200,6 +1200,9 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp,
{
unsigned int i;
+ if (!layer->info)
+ return;
+
for (i = 0; i < layer->info->num_channels; i++) {
struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 129beac4c073..25c5dc61ee88 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -18,7 +18,9 @@
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
@@ -51,6 +53,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)");
#define ZYNQMP_DP_LANE_COUNT_SET 0x4
#define ZYNQMP_DP_ENHANCED_FRAME_EN 0x8
#define ZYNQMP_DP_TRAINING_PATTERN_SET 0xc
+#define ZYNQMP_DP_LINK_QUAL_PATTERN_SET 0x10
#define ZYNQMP_DP_SCRAMBLING_DISABLE 0x14
#define ZYNQMP_DP_DOWNSPREAD_CTL 0x18
#define ZYNQMP_DP_SOFTWARE_RESET 0x1c
@@ -64,6 +67,9 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)");
ZYNQMP_DP_SOFTWARE_RESET_STREAM3 | \
ZYNQMP_DP_SOFTWARE_RESET_STREAM4 | \
ZYNQMP_DP_SOFTWARE_RESET_AUX)
+#define ZYNQMP_DP_COMP_PATTERN_80BIT_1 0x20
+#define ZYNQMP_DP_COMP_PATTERN_80BIT_2 0x24
+#define ZYNQMP_DP_COMP_PATTERN_80BIT_3 0x28
/* Core enable registers */
#define ZYNQMP_DP_TRANSMITTER_ENABLE 0x80
@@ -207,6 +213,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)");
#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_2 BIT(2)
#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_3 BIT(3)
#define ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL 0xf
+#define ZYNQMP_DP_TRANSMIT_PRBS7 0x230
#define ZYNQMP_DP_PHY_PRECURSOR_LANE_0 0x23c
#define ZYNQMP_DP_PHY_PRECURSOR_LANE_1 0x240
#define ZYNQMP_DP_PHY_PRECURSOR_LANE_2 0x244
@@ -275,30 +282,108 @@ struct zynqmp_dp_config {
};
/**
+ * enum test_pattern - Test patterns for test testing
+ * @TEST_VIDEO: Use regular video input
+ * @TEST_SYMBOL_ERROR: Symbol error measurement pattern
+ * @TEST_PRBS7: Output of the PRBS7 (x^7 + x^6 + 1) polynomial
+ * @TEST_80BIT_CUSTOM: A custom 80-bit pattern
+ * @TEST_CP2520: HBR2 compliance eye pattern
+ * @TEST_TPS1: Link training symbol pattern TPS1 (/D10.2/)
+ * @TEST_TPS2: Link training symbol pattern TPS2
+ * @TEST_TPS3: Link training symbol pattern TPS3 (for HBR2)
+ */
+enum test_pattern {
+ TEST_VIDEO,
+ TEST_TPS1,
+ TEST_TPS2,
+ TEST_TPS3,
+ TEST_SYMBOL_ERROR,
+ TEST_PRBS7,
+ TEST_80BIT_CUSTOM,
+ TEST_CP2520,
+};
+
+static const char *const test_pattern_str[] = {
+ [TEST_VIDEO] = "video",
+ [TEST_TPS1] = "tps1",
+ [TEST_TPS2] = "tps2",
+ [TEST_TPS3] = "tps3",
+ [TEST_SYMBOL_ERROR] = "symbol-error",
+ [TEST_PRBS7] = "prbs7",
+ [TEST_80BIT_CUSTOM] = "80bit-custom",
+ [TEST_CP2520] = "cp2520",
+};
+
+/**
+ * struct zynqmp_dp_test - Configuration for test mode
+ * @pattern: The test pattern
+ * @enhanced: Use enhanced framing
+ * @downspread: Use SSC
+ * @active: Whether test mode is active
+ * @custom: Custom pattern for %TEST_80BIT_CUSTOM
+ * @train_set: Voltage/preemphasis settings
+ * @bw_code: Bandwidth code for the link
+ * @link_cnt: Number of lanes
+ */
+struct zynqmp_dp_test {
+ enum test_pattern pattern;
+ bool enhanced, downspread, active;
+ u8 custom[10];
+ u8 train_set[ZYNQMP_DP_MAX_LANES];
+ u8 bw_code;
+ u8 link_cnt;
+};
+
+/**
+ * struct zynqmp_dp_train_set_priv - Private data for train_set debugfs files
+ * @dp: DisplayPort IP core structure
+ * @lane: The lane for this file
+ */
+struct zynqmp_dp_train_set_priv {
+ struct zynqmp_dp *dp;
+ int lane;
+};
+
+/**
* struct zynqmp_dp - Xilinx DisplayPort core
* @dev: device structure
* @dpsub: Display subsystem
* @iomem: device I/O memory for register access
* @reset: reset controller
+ * @lock: Mutex protecting this struct and register access (but not AUX)
* @irq: irq
* @bridge: DRM bridge for the DP encoder
* @next_bridge: The downstream bridge
+ * @test: Configuration for test mode
* @config: IP core configuration from DTS
* @aux: aux channel
+ * @aux_done: Completed when we get an AUX reply or timeout
+ * @ignore_aux_errors: If set, AUX errors are suppressed
* @phy: PHY handles for DP lanes
* @num_lanes: number of enabled phy lanes
* @hpd_work: hot plug detection worker
+ * @hpd_irq_work: hot plug detection IRQ worker
+ * @ignore_hpd: If set, HPD events and IRQs are ignored
* @status: connection status
* @enabled: flag to indicate if the device is enabled
* @dpcd: DP configuration data from currently connected sink device
* @link_config: common link configuration between IP core and sink device
* @mode: current mode between IP core and sink device
* @train_set: set of training data
+ * @debugfs_train_set: Debugfs private data for @train_set
+ *
+ * @lock covers the link configuration in this struct and the device's
+ * registers. It does not cover @aux or @ignore_aux_errors. It is not strictly
+ * required for any of the members which are only modified at probe/remove time
+ * (e.g. @dev).
*/
struct zynqmp_dp {
struct drm_dp_aux aux;
struct drm_bridge bridge;
struct work_struct hpd_work;
+ struct work_struct hpd_irq_work;
+ struct completion aux_done;
+ struct mutex lock;
struct drm_bridge *next_bridge;
struct device *dev;
@@ -310,9 +395,13 @@ struct zynqmp_dp {
enum drm_connector_status status;
int irq;
bool enabled;
+ bool ignore_aux_errors;
+ bool ignore_hpd;
+ struct zynqmp_dp_train_set_priv debugfs_train_set[ZYNQMP_DP_MAX_LANES];
struct zynqmp_dp_mode mode;
struct zynqmp_dp_link_config link_config;
+ struct zynqmp_dp_test test;
struct zynqmp_dp_config config;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
u8 train_set[ZYNQMP_DP_MAX_LANES];
@@ -626,6 +715,7 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp,
/**
* zynqmp_dp_update_vs_emph - Update the training values
* @dp: DisplayPort IP core structure
+ * @train_set: A set of training values
*
* Update the training values based on the request from sink. The mapped values
* are predefined, and values(vs, pe, pc) are from the device manual.
@@ -633,12 +723,12 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp,
* Return: 0 if vs and emph are updated successfully, or the error code returned
* by drm_dp_dpcd_write().
*/
-static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp)
+static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp, u8 *train_set)
{
unsigned int i;
int ret;
- ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->train_set,
+ ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set,
dp->mode.lane_cnt);
if (ret < 0)
return ret;
@@ -646,7 +736,7 @@ static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp)
for (i = 0; i < dp->mode.lane_cnt; i++) {
u32 reg = ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4;
union phy_configure_opts opts = { 0 };
- u8 train = dp->train_set[i];
+ u8 train = train_set[i];
opts.dp.voltage[0] = (train & DP_TRAIN_VOLTAGE_SWING_MASK)
>> DP_TRAIN_VOLTAGE_SWING_SHIFT;
@@ -690,7 +780,7 @@ static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp)
* So, This loop should exit before 512 iterations
*/
for (max_tries = 0; max_tries < 512; max_tries++) {
- ret = zynqmp_dp_update_vs_emph(dp);
+ ret = zynqmp_dp_update_vs_emph(dp, dp->train_set);
if (ret)
return ret;
@@ -755,7 +845,7 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp)
return ret;
for (tries = 0; tries < DP_MAX_TRAINING_TRIES; tries++) {
- ret = zynqmp_dp_update_vs_emph(dp);
+ ret = zynqmp_dp_update_vs_emph(dp, dp->train_set);
if (ret)
return ret;
@@ -778,28 +868,29 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp)
}
/**
- * zynqmp_dp_train - Train the link
+ * zynqmp_dp_setup() - Set up major link parameters
* @dp: DisplayPort IP core structure
+ * @bw_code: The link bandwidth as a multiple of 270 MHz
+ * @lane_cnt: The number of lanes to use
+ * @enhanced: Use enhanced framing
+ * @downspread: Enable spread-spectrum clocking
*
- * Return: 0 if all trains are done successfully, or corresponding error code.
+ * Return: 0 on success, or -errno on failure
*/
-static int zynqmp_dp_train(struct zynqmp_dp *dp)
+static int zynqmp_dp_setup(struct zynqmp_dp *dp, u8 bw_code, u8 lane_cnt,
+ bool enhanced, bool downspread)
{
u32 reg;
- u8 bw_code = dp->mode.bw_code;
- u8 lane_cnt = dp->mode.lane_cnt;
u8 aux_lane_cnt = lane_cnt;
- bool enhanced;
int ret;
zynqmp_dp_write(dp, ZYNQMP_DP_LANE_COUNT_SET, lane_cnt);
- enhanced = drm_dp_enhanced_frame_cap(dp->dpcd);
if (enhanced) {
zynqmp_dp_write(dp, ZYNQMP_DP_ENHANCED_FRAME_EN, 1);
aux_lane_cnt |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
}
- if (dp->dpcd[3] & 0x1) {
+ if (downspread) {
zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 1);
drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL,
DP_SPREAD_AMP_0_5);
@@ -842,8 +933,24 @@ static int zynqmp_dp_train(struct zynqmp_dp *dp)
}
zynqmp_dp_write(dp, ZYNQMP_DP_PHY_CLOCK_SELECT, reg);
- ret = zynqmp_dp_phy_ready(dp);
- if (ret < 0)
+ return zynqmp_dp_phy_ready(dp);
+}
+
+/**
+ * zynqmp_dp_train - Train the link
+ * @dp: DisplayPort IP core structure
+ *
+ * Return: 0 if all trains are done successfully, or corresponding error code.
+ */
+static int zynqmp_dp_train(struct zynqmp_dp *dp)
+{
+ int ret;
+
+ ret = zynqmp_dp_setup(dp, dp->mode.bw_code, dp->mode.lane_cnt,
+ drm_dp_enhanced_frame_cap(dp->dpcd),
+ dp->dpcd[DP_MAX_DOWNSPREAD] &
+ DP_MAX_DOWNSPREAD_0_5);
+ if (ret)
return ret;
zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 1);
@@ -934,12 +1041,15 @@ static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr,
u8 *buf, u8 bytes, u8 *reply)
{
bool is_read = (cmd & AUX_READ_BIT) ? true : false;
+ unsigned long time_left;
u32 reg, i;
reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST)
return -EBUSY;
+ reinit_completion(&dp->aux_done);
+
zynqmp_dp_write(dp, ZYNQMP_DP_AUX_ADDRESS, addr);
if (!is_read)
for (i = 0; i < bytes; i++)
@@ -954,17 +1064,14 @@ static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr,
zynqmp_dp_write(dp, ZYNQMP_DP_AUX_COMMAND, reg);
/* Wait for reply to be delivered upto 2ms */
- for (i = 0; ; i++) {
- reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
- if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY)
- break;
-
- if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT ||
- i == 2)
- return -ETIMEDOUT;
+ time_left = wait_for_completion_timeout(&dp->aux_done,
+ msecs_to_jiffies(2));
+ if (!time_left)
+ return -ETIMEDOUT;
- usleep_range(1000, 1100);
- }
+ reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
+ if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT)
+ return -ETIMEDOUT;
reg = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_CODE);
if (reply)
@@ -1006,6 +1113,8 @@ zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
if (dp->status == connector_status_disconnected) {
dev_dbg(dp->dev, "no connected aux device\n");
+ if (dp->ignore_aux_errors)
+ goto fake_response;
return -ENODEV;
}
@@ -1014,7 +1123,13 @@ zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
dev_dbg(dp->dev, "failed to do aux transfer (%d)\n", ret);
- return ret;
+ if (!dp->ignore_aux_errors)
+ return ret;
+
+fake_response:
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ memset(msg->buffer, 0, msg->size);
+ return msg->size;
}
/**
@@ -1048,6 +1163,9 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp)
(w << ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT) |
(rate / (1000 * 1000)));
+ zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_REPLY_RECEIVED |
+ ZYNQMP_DP_INT_REPLY_TIMEOUT);
+
dp->aux.name = "ZynqMP DP AUX";
dp->aux.dev = dp->dev;
dp->aux.drm_dev = dp->bridge.dev;
@@ -1065,6 +1183,9 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp)
static void zynqmp_dp_aux_cleanup(struct zynqmp_dp *dp)
{
drm_dp_aux_unregister(&dp->aux);
+
+ zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_REPLY_RECEIVED |
+ ZYNQMP_DP_INT_REPLY_TIMEOUT);
}
/* -----------------------------------------------------------------------------
@@ -1386,8 +1507,10 @@ zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge,
}
/* Check with link rate and lane count */
+ mutex_lock(&dp->lock);
rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
dp->link_config.max_lanes, dp->config.bpp);
+ mutex_unlock(&dp->lock);
if (mode->clock > rate) {
dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n",
mode->name);
@@ -1414,6 +1537,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
pm_runtime_get_sync(dp->dev);
+ mutex_lock(&dp->lock);
zynqmp_dp_disp_enable(dp, old_bridge_state);
/*
@@ -1474,6 +1598,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET,
ZYNQMP_DP_SOFTWARE_RESET_ALL);
zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1);
+ mutex_unlock(&dp->lock);
}
static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
@@ -1481,6 +1606,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
{
struct zynqmp_dp *dp = bridge_to_dp(bridge);
+ mutex_lock(&dp->lock);
dp->enabled = false;
cancel_work(&dp->hpd_work);
zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0);
@@ -1491,6 +1617,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
zynqmp_dp_disp_disable(dp, old_bridge_state);
+ mutex_unlock(&dp->lock);
pm_runtime_put_sync(dp->dev);
}
@@ -1526,13 +1653,14 @@ static int zynqmp_dp_bridge_atomic_check(struct drm_bridge *bridge,
return 0;
}
-static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge)
+static enum drm_connector_status __zynqmp_dp_bridge_detect(struct zynqmp_dp *dp)
{
- struct zynqmp_dp *dp = bridge_to_dp(bridge);
struct zynqmp_dp_link_config *link_config = &dp->link_config;
u32 state, i;
int ret;
+ lockdep_assert_held(&dp->lock);
+
/*
* This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to
* get the HPD signal with some monitors.
@@ -1568,6 +1696,18 @@ disconnected:
return connector_status_disconnected;
}
+static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge)
+{
+ struct zynqmp_dp *dp = bridge_to_dp(bridge);
+ enum drm_connector_status ret;
+
+ mutex_lock(&dp->lock);
+ ret = __zynqmp_dp_bridge_detect(dp);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
static const struct drm_edid *zynqmp_dp_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -1605,6 +1745,582 @@ zynqmp_dp_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
return zynqmp_dp_bridge_default_bus_fmts(num_input_fmts);
}
+/* -----------------------------------------------------------------------------
+ * debugfs
+ */
+
+/**
+ * zynqmp_dp_set_test_pattern() - Configure the link for a test pattern
+ * @dp: DisplayPort IP core structure
+ * @pattern: The test pattern to configure
+ * @custom: The custom pattern to use if @pattern is %TEST_80BIT_CUSTOM
+ *
+ * Return: 0 on success, or negative errno on (DPCD) failure
+ */
+static int zynqmp_dp_set_test_pattern(struct zynqmp_dp *dp,
+ enum test_pattern pattern,
+ u8 *const custom)
+{
+ bool scramble = false;
+ u32 train_pattern = 0;
+ u32 link_pattern = 0;
+ u8 dpcd_train = 0;
+ u8 dpcd_link = 0;
+ int ret;
+
+ switch (pattern) {
+ case TEST_TPS1:
+ train_pattern = 1;
+ break;
+ case TEST_TPS2:
+ train_pattern = 2;
+ break;
+ case TEST_TPS3:
+ train_pattern = 3;
+ break;
+ case TEST_SYMBOL_ERROR:
+ scramble = true;
+ link_pattern = DP_PHY_TEST_PATTERN_ERROR_COUNT;
+ break;
+ case TEST_PRBS7:
+ /* We use a dedicated register to enable PRBS7 */
+ dpcd_link = DP_LINK_QUAL_PATTERN_ERROR_RATE;
+ break;
+ case TEST_80BIT_CUSTOM: {
+ const u8 *p = custom;
+
+ link_pattern = DP_LINK_QUAL_PATTERN_80BIT_CUSTOM;
+
+ zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_1,
+ (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+ zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_2,
+ (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]);
+ zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_3,
+ (p[9] << 8) | p[8]);
+ break;
+ }
+ case TEST_CP2520:
+ link_pattern = DP_LINK_QUAL_PATTERN_CP2520_PAT_1;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ fallthrough;
+ case TEST_VIDEO:
+ scramble = true;
+ }
+
+ zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, !scramble);
+ zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, train_pattern);
+ zynqmp_dp_write(dp, ZYNQMP_DP_LINK_QUAL_PATTERN_SET, link_pattern);
+ zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMIT_PRBS7, pattern == TEST_PRBS7);
+
+ dpcd_link = dpcd_link ?: link_pattern;
+ dpcd_train = train_pattern;
+ if (!scramble)
+ dpcd_train |= DP_LINK_SCRAMBLING_DISABLE;
+
+ if (dp->dpcd[DP_DPCD_REV] < 0x12) {
+ if (pattern == TEST_CP2520)
+ dev_warn(dp->dev,
+ "can't set sink link quality pattern to CP2520 for DPCD < r1.2; error counters will be invalid\n");
+ else
+ dpcd_train |= FIELD_PREP(DP_LINK_QUAL_PATTERN_11_MASK,
+ dpcd_link);
+ } else {
+ u8 dpcd_link_lane[ZYNQMP_DP_MAX_LANES];
+
+ memset(dpcd_link_lane, dpcd_link, ZYNQMP_DP_MAX_LANES);
+ ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_QUAL_LANE0_SET,
+ dpcd_link_lane, ZYNQMP_DP_MAX_LANES);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, dpcd_train);
+ return ret < 0 ? ret : 0;
+}
+
+static int zynqmp_dp_test_setup(struct zynqmp_dp *dp)
+{
+ return zynqmp_dp_setup(dp, dp->test.bw_code, dp->test.link_cnt,
+ dp->test.enhanced, dp->test.downspread);
+}
+
+static ssize_t zynqmp_dp_pattern_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct zynqmp_dp *dp = file->private_data;
+ char buf[16];
+ ssize_t ret;
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+
+ mutex_lock(&dp->lock);
+ ret = snprintf(buf, sizeof(buf), "%s\n",
+ test_pattern_str[dp->test.pattern]);
+ mutex_unlock(&dp->lock);
+
+ debugfs_file_put(dentry);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+static ssize_t zynqmp_dp_pattern_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct zynqmp_dp *dp = file->private_data;
+ char buf[16];
+ ssize_t ret;
+ int pattern;
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf,
+ count);
+ if (ret < 0)
+ goto out;
+ buf[ret] = '\0';
+
+ pattern = sysfs_match_string(test_pattern_str, buf);
+ if (pattern < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&dp->lock);
+ dp->test.pattern = pattern;
+ if (dp->test.active)
+ ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern,
+ dp->test.custom) ?: ret;
+ mutex_unlock(&dp->lock);
+
+out:
+ debugfs_file_put(dentry);
+ return ret;
+}
+
+static const struct file_operations fops_zynqmp_dp_pattern = {
+ .read = zynqmp_dp_pattern_read,
+ .write = zynqmp_dp_pattern_write,
+ .open = simple_open,
+ .llseek = noop_llseek,
+};
+
+static int zynqmp_dp_enhanced_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = dp->test.enhanced;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_enhanced_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+ int ret = 0;
+
+ mutex_lock(&dp->lock);
+ dp->test.enhanced = val;
+ if (dp->test.active)
+ ret = zynqmp_dp_test_setup(dp);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_enhanced, zynqmp_dp_enhanced_get,
+ zynqmp_dp_enhanced_set, "%llu\n");
+
+static int zynqmp_dp_downspread_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = dp->test.downspread;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_downspread_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+ int ret = 0;
+
+ mutex_lock(&dp->lock);
+ dp->test.downspread = val;
+ if (dp->test.active)
+ ret = zynqmp_dp_test_setup(dp);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_downspread, zynqmp_dp_downspread_get,
+ zynqmp_dp_downspread_set, "%llu\n");
+
+static int zynqmp_dp_active_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = dp->test.active;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_active_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+ int ret = 0;
+
+ mutex_lock(&dp->lock);
+ if (val) {
+ if (val < 2) {
+ ret = zynqmp_dp_test_setup(dp);
+ if (ret)
+ goto out;
+ }
+
+ ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern,
+ dp->test.custom);
+ if (ret)
+ goto out;
+
+ ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set);
+ if (ret)
+ goto out;
+
+ dp->test.active = true;
+ } else {
+ int err;
+
+ dp->test.active = false;
+ err = zynqmp_dp_set_test_pattern(dp, TEST_VIDEO, NULL);
+ if (err)
+ dev_warn(dp->dev, "could not clear test pattern: %d\n",
+ err);
+ zynqmp_dp_train_loop(dp);
+ }
+out:
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_active, zynqmp_dp_active_get,
+ zynqmp_dp_active_set, "%llu\n");
+
+static ssize_t zynqmp_dp_custom_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct zynqmp_dp *dp = file->private_data;
+ ssize_t ret;
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+
+ mutex_lock(&dp->lock);
+ ret = simple_read_from_buffer(user_buf, count, ppos, &dp->test.custom,
+ sizeof(dp->test.custom));
+ mutex_unlock(&dp->lock);
+
+ debugfs_file_put(dentry);
+ return ret;
+}
+
+static ssize_t zynqmp_dp_custom_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct zynqmp_dp *dp = file->private_data;
+ ssize_t ret;
+ char buf[sizeof(dp->test.custom)];
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
+ if (ret < 0)
+ goto out;
+
+ mutex_lock(&dp->lock);
+ memcpy(dp->test.custom, buf, ret);
+ if (dp->test.active)
+ ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern,
+ dp->test.custom) ?: ret;
+ mutex_unlock(&dp->lock);
+
+out:
+ debugfs_file_put(dentry);
+ return ret;
+}
+
+static const struct file_operations fops_zynqmp_dp_custom = {
+ .read = zynqmp_dp_custom_read,
+ .write = zynqmp_dp_custom_write,
+ .open = simple_open,
+ .llseek = noop_llseek,
+};
+
+static int zynqmp_dp_swing_get(void *data, u64 *val)
+{
+ struct zynqmp_dp_train_set_priv *priv = data;
+ struct zynqmp_dp *dp = priv->dp;
+
+ mutex_lock(&dp->lock);
+ *val = dp->test.train_set[priv->lane] & DP_TRAIN_VOLTAGE_SWING_MASK;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_swing_set(void *data, u64 val)
+{
+ struct zynqmp_dp_train_set_priv *priv = data;
+ struct zynqmp_dp *dp = priv->dp;
+ u8 *train_set = &dp->test.train_set[priv->lane];
+ int ret = 0;
+
+ if (val > 3)
+ return -EINVAL;
+
+ mutex_lock(&dp->lock);
+ *train_set &= ~(DP_TRAIN_MAX_SWING_REACHED |
+ DP_TRAIN_VOLTAGE_SWING_MASK);
+ *train_set |= val;
+ if (val == 3)
+ *train_set |= DP_TRAIN_MAX_SWING_REACHED;
+
+ if (dp->test.active)
+ ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_swing, zynqmp_dp_swing_get,
+ zynqmp_dp_swing_set, "%llu\n");
+
+static int zynqmp_dp_preemphasis_get(void *data, u64 *val)
+{
+ struct zynqmp_dp_train_set_priv *priv = data;
+ struct zynqmp_dp *dp = priv->dp;
+
+ mutex_lock(&dp->lock);
+ *val = FIELD_GET(DP_TRAIN_PRE_EMPHASIS_MASK,
+ dp->test.train_set[priv->lane]);
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_preemphasis_set(void *data, u64 val)
+{
+ struct zynqmp_dp_train_set_priv *priv = data;
+ struct zynqmp_dp *dp = priv->dp;
+ u8 *train_set = &dp->test.train_set[priv->lane];
+ int ret = 0;
+
+ if (val > 2)
+ return -EINVAL;
+
+ mutex_lock(&dp->lock);
+ *train_set &= ~(DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ *train_set |= val;
+ if (val == 2)
+ *train_set |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ if (dp->test.active)
+ ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_preemphasis, zynqmp_dp_preemphasis_get,
+ zynqmp_dp_preemphasis_set, "%llu\n");
+
+static int zynqmp_dp_lanes_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = dp->test.link_cnt;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_lanes_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+ int ret = 0;
+
+ if (val > ZYNQMP_DP_MAX_LANES)
+ return -EINVAL;
+
+ mutex_lock(&dp->lock);
+ if (val > dp->num_lanes) {
+ ret = -EINVAL;
+ } else {
+ dp->test.link_cnt = val;
+ if (dp->test.active)
+ ret = zynqmp_dp_test_setup(dp);
+ }
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_lanes, zynqmp_dp_lanes_get,
+ zynqmp_dp_lanes_set, "%llu\n");
+
+static int zynqmp_dp_rate_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = drm_dp_bw_code_to_link_rate(dp->test.bw_code) * 10000;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_rate_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+ int link_rate;
+ int ret = 0;
+ u8 bw_code;
+
+ if (do_div(val, 10000))
+ return -EINVAL;
+
+ bw_code = drm_dp_link_rate_to_bw_code(val);
+ link_rate = drm_dp_bw_code_to_link_rate(bw_code);
+ if (val != link_rate)
+ return -EINVAL;
+
+ if (bw_code != DP_LINK_BW_1_62 && bw_code != DP_LINK_BW_2_7 &&
+ bw_code != DP_LINK_BW_5_4)
+ return -EINVAL;
+
+ mutex_lock(&dp->lock);
+ dp->test.bw_code = bw_code;
+ if (dp->test.active)
+ ret = zynqmp_dp_test_setup(dp);
+ mutex_unlock(&dp->lock);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_rate, zynqmp_dp_rate_get,
+ zynqmp_dp_rate_set, "%llu\n");
+
+static int zynqmp_dp_ignore_aux_errors_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->aux.hw_mutex);
+ *val = dp->ignore_aux_errors;
+ mutex_unlock(&dp->aux.hw_mutex);
+ return 0;
+}
+
+static int zynqmp_dp_ignore_aux_errors_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+
+ if (val != !!val)
+ return -EINVAL;
+
+ mutex_lock(&dp->aux.hw_mutex);
+ dp->ignore_aux_errors = val;
+ mutex_unlock(&dp->aux.hw_mutex);
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_ignore_aux_errors,
+ zynqmp_dp_ignore_aux_errors_get,
+ zynqmp_dp_ignore_aux_errors_set, "%llu\n");
+
+static int zynqmp_dp_ignore_hpd_get(void *data, u64 *val)
+{
+ struct zynqmp_dp *dp = data;
+
+ mutex_lock(&dp->lock);
+ *val = dp->ignore_hpd;
+ mutex_unlock(&dp->lock);
+ return 0;
+}
+
+static int zynqmp_dp_ignore_hpd_set(void *data, u64 val)
+{
+ struct zynqmp_dp *dp = data;
+
+ if (val != !!val)
+ return -EINVAL;
+
+ mutex_lock(&dp->lock);
+ dp->ignore_hpd = val;
+ mutex_lock(&dp->lock);
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_ignore_hpd, zynqmp_dp_ignore_hpd_get,
+ zynqmp_dp_ignore_hpd_set, "%llu\n");
+
+static void zynqmp_dp_bridge_debugfs_init(struct drm_bridge *bridge,
+ struct dentry *root)
+{
+ struct zynqmp_dp *dp = bridge_to_dp(bridge);
+ struct dentry *test;
+ int i;
+
+ dp->test.bw_code = DP_LINK_BW_5_4;
+ dp->test.link_cnt = dp->num_lanes;
+
+ test = debugfs_create_dir("test", root);
+#define CREATE_FILE(name) \
+ debugfs_create_file(#name, 0600, test, dp, &fops_zynqmp_dp_##name)
+ CREATE_FILE(pattern);
+ CREATE_FILE(enhanced);
+ CREATE_FILE(downspread);
+ CREATE_FILE(active);
+ CREATE_FILE(custom);
+ CREATE_FILE(rate);
+ CREATE_FILE(lanes);
+ CREATE_FILE(ignore_aux_errors);
+ CREATE_FILE(ignore_hpd);
+
+ for (i = 0; i < dp->num_lanes; i++) {
+ static const char fmt[] = "lane%d_preemphasis";
+ char name[sizeof(fmt)];
+
+ dp->debugfs_train_set[i].dp = dp;
+ dp->debugfs_train_set[i].lane = i;
+
+ snprintf(name, sizeof(name), fmt, i);
+ debugfs_create_file(name, 0600, test,
+ &dp->debugfs_train_set[i],
+ &fops_zynqmp_dp_preemphasis);
+
+ snprintf(name, sizeof(name), "lane%d_swing", i);
+ debugfs_create_file(name, 0600, test,
+ &dp->debugfs_train_set[i],
+ &fops_zynqmp_dp_swing);
+ }
+}
+
static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
.attach = zynqmp_dp_bridge_attach,
.detach = zynqmp_dp_bridge_detach,
@@ -1618,6 +2334,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
.detect = zynqmp_dp_bridge_detect,
.edid_read = zynqmp_dp_bridge_edid_read,
.atomic_get_input_bus_fmts = zynqmp_dp_bridge_get_input_bus_fmts,
+ .debugfs_init = zynqmp_dp_bridge_debugfs_init,
};
/* -----------------------------------------------------------------------------
@@ -1651,10 +2368,46 @@ static void zynqmp_dp_hpd_work_func(struct work_struct *work)
struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp, hpd_work);
enum drm_connector_status status;
- status = zynqmp_dp_bridge_detect(&dp->bridge);
+ mutex_lock(&dp->lock);
+ if (dp->ignore_hpd) {
+ mutex_unlock(&dp->lock);
+ return;
+ }
+
+ status = __zynqmp_dp_bridge_detect(dp);
+ mutex_unlock(&dp->lock);
+
drm_bridge_hpd_notify(&dp->bridge, status);
}
+static void zynqmp_dp_hpd_irq_work_func(struct work_struct *work)
+{
+ struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp,
+ hpd_irq_work);
+ u8 status[DP_LINK_STATUS_SIZE + 2];
+ int err;
+
+ mutex_lock(&dp->lock);
+ if (dp->ignore_hpd) {
+ mutex_unlock(&dp->lock);
+ return;
+ }
+
+ err = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status,
+ DP_LINK_STATUS_SIZE + 2);
+ if (err < 0) {
+ dev_dbg_ratelimited(dp->dev,
+ "could not read sink status: %d\n", err);
+ } else {
+ if (status[4] & DP_LINK_STATUS_UPDATED ||
+ !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) ||
+ !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) {
+ zynqmp_dp_train_loop(dp);
+ }
+ }
+ mutex_unlock(&dp->lock);
+}
+
static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
{
struct zynqmp_dp *dp = (struct zynqmp_dp *)data;
@@ -1686,23 +2439,15 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
if (status & ZYNQMP_DP_INT_HPD_EVENT)
schedule_work(&dp->hpd_work);
- if (status & ZYNQMP_DP_INT_HPD_IRQ) {
- int ret;
- u8 status[DP_LINK_STATUS_SIZE + 2];
+ if (status & ZYNQMP_DP_INT_HPD_IRQ)
+ schedule_work(&dp->hpd_irq_work);
- ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status,
- DP_LINK_STATUS_SIZE + 2);
- if (ret < 0)
- goto handled;
+ if (status & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY)
+ complete(&dp->aux_done);
- if (status[4] & DP_LINK_STATUS_UPDATED ||
- !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) ||
- !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) {
- zynqmp_dp_train_loop(dp);
- }
- }
+ if (status & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT)
+ complete(&dp->aux_done);
-handled:
return IRQ_HANDLED;
}
@@ -1725,8 +2470,11 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub)
dp->dev = &pdev->dev;
dp->dpsub = dpsub;
dp->status = connector_status_disconnected;
+ mutex_init(&dp->lock);
+ init_completion(&dp->aux_done);
INIT_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func);
+ INIT_WORK(&dp->hpd_irq_work, zynqmp_dp_hpd_irq_work_func);
/* Acquire all resources (IOMEM, IRQ and PHYs). */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp");
@@ -1802,9 +2550,8 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub)
* Now that the hardware is initialized and won't generate spurious
* interrupts, request the IRQ.
*/
- ret = devm_request_threaded_irq(dp->dev, dp->irq, NULL,
- zynqmp_dp_irq_handler, IRQF_ONESHOT,
- dev_name(dp->dev), dp);
+ ret = devm_request_irq(dp->dev, dp->irq, zynqmp_dp_irq_handler,
+ IRQF_SHARED, dev_name(dp->dev), dp);
if (ret < 0)
goto err_phy_exit;
@@ -1829,8 +2576,9 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub)
struct zynqmp_dp *dp = dpsub->dp;
zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL);
- disable_irq(dp->irq);
+ devm_free_irq(dp->dev, dp->irq, dp);
+ cancel_work_sync(&dp->hpd_irq_work);
cancel_work_sync(&dp->hpd_work);
zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0);
@@ -1838,4 +2586,5 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub)
zynqmp_dp_phy_exit(dp);
zynqmp_dp_reset(dp, true);
+ mutex_destroy(&dp->lock);
}
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 2452c2f09161..fc81983d9e5e 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -511,12 +511,12 @@ int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
if (ret)
return ret;
- drm_kms_helper_poll_init(drm);
-
ret = zynqmp_dpsub_kms_init(dpsub);
if (ret < 0)
goto err_poll_fini;
+ drm_kms_helper_poll_init(drm);
+
/* Reset all components and register the DRM device. */
drm_mode_config_reset(drm);
@@ -538,7 +538,7 @@ void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub)
{
struct drm_device *drm = &dpsub->drm->dev;
- drm_dev_unregister(drm);
+ drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
drm_encoder_cleanup(&dpsub->drm->encoder);
drm_kms_helper_poll_fini(drm);