From 145c9b36892a07bf5e2525b4938e1a6cc9b41b7a Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Tue, 18 Feb 2025 19:27:31 +0800 Subject: [PATCH 01/16] drm/rockchip: vop2: Merge vop2_cluster/esmart_init function Now these two function share the same logic, they can be merged as one. Signed-off-by: Andy Yan Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250218112744.34433-5-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 42 +++++--------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index efd6b4f91156..1f13b942064e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2434,18 +2434,18 @@ static int vop2_find_rgb_encoder(struct vop2 *vop2) return -ENOENT; } -static int vop2_cluster_init(struct vop2_win *win) +static int vop2_regmap_init(struct vop2_win *win, const struct reg_field *regs, + int nr_regs) { struct vop2 *vop2 = win->vop2; int i; - for (i = 0; i < vop2->data->nr_cluster_regs; i++) { + for (i = 0; i < nr_regs; i++) { const struct reg_field field = { - .reg = (vop2->data->cluster_reg[i].reg != 0xffffffff) ? - vop2->data->cluster_reg[i].reg + win->offset : - vop2->data->cluster_reg[i].reg, - .lsb = vop2->data->cluster_reg[i].lsb, - .msb = vop2->data->cluster_reg[i].msb + .reg = (regs[i].reg != 0xffffffff) ? + regs[i].reg + win->offset : regs[i].reg, + .lsb = regs[i].lsb, + .msb = regs[i].msb }; win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field); @@ -2456,28 +2456,6 @@ static int vop2_cluster_init(struct vop2_win *win) return 0; }; -static int vop2_esmart_init(struct vop2_win *win) -{ - struct vop2 *vop2 = win->vop2; - int i; - - for (i = 0; i < vop2->data->nr_smart_regs; i++) { - const struct reg_field field = { - .reg = (vop2->data->smart_reg[i].reg != 0xffffffff) ? - vop2->data->smart_reg[i].reg + win->offset : - vop2->data->smart_reg[i].reg, - .lsb = vop2->data->smart_reg[i].lsb, - .msb = vop2->data->smart_reg[i].msb - }; - - win->reg[i] = devm_regmap_field_alloc(vop2->dev, vop2->map, field); - if (IS_ERR(win->reg[i])) - return PTR_ERR(win->reg[i]); - } - - return 0; -} - static int vop2_win_init(struct vop2 *vop2) { const struct vop2_data *vop2_data = vop2->data; @@ -2494,9 +2472,11 @@ static int vop2_win_init(struct vop2 *vop2) win->win_id = i; win->vop2 = vop2; if (vop2_cluster_window(win)) - ret = vop2_cluster_init(win); + ret = vop2_regmap_init(win, vop2->data->cluster_reg, + vop2->data->nr_cluster_regs); else - ret = vop2_esmart_init(win); + ret = vop2_regmap_init(win, vop2->data->smart_reg, + vop2->data->nr_smart_regs); if (ret) return ret; } -- 2.51.0 From 5439c4f3cb0ec11a3f3cb70be2b019770f6d183c Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Tue, 18 Feb 2025 19:27:32 +0800 Subject: [PATCH 02/16] drm/rockchip: vop2: Support for different layer select configuration between VPs In the upcoming VOP for rk3576, every VP has it's own LAYER_SEL register, and the configuration value of each VP for the same window maybe different, so extend the layer_sel_id to array, let it can descption the layer select configuration value for different VP. Signed-off-by: Andy Yan Tested-by: Michael Riesch # on RK3568 Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250218112744.34433-6-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 4 +-- drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 38 ++++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index 904ce2008870..cae211a558bd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -166,9 +166,9 @@ struct vop2_win_data { const unsigned int supported_rotations; /** - * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2 + * @layer_sel_id: defined by register OVERLAY_LAYER_SEL or PORTn_LAYER_SEL */ - unsigned int layer_sel_id; + unsigned int layer_sel_id[ROCKCHIP_MAX_CRTC]; uint64_t feature; uint8_t axi_bus_id; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 56d72f35b57f..31edde7ae600 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -350,7 +350,8 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .formats = formats_smart, .nformats = ARRAY_SIZE(formats_smart), .format_modifiers = format_modifiers, - .layer_sel_id = 3, + /* 0xf means this layer can't attached to this VP */ + .layer_sel_id = { 3, 3, 3, 0xf }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, .max_upscale_factor = 8, @@ -363,7 +364,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_smart), .format_modifiers = format_modifiers, .base = 0x1e00, - .layer_sel_id = 7, + .layer_sel_id = { 7, 7, 7, 0xf }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, .max_upscale_factor = 8, @@ -376,7 +377,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_rk356x_esmart), .format_modifiers = format_modifiers, .base = 0x1a00, - .layer_sel_id = 6, + .layer_sel_id = { 6, 6, 6, 0xf }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, .max_upscale_factor = 8, @@ -389,7 +390,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_rk356x_esmart), .format_modifiers = format_modifiers, .base = 0x1800, - .layer_sel_id = 2, + .layer_sel_id = { 2, 2, 2, 0xf }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, .max_upscale_factor = 8, @@ -402,7 +403,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 0, + .layer_sel_id = { 0, 0, 0, 0xf }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .max_upscale_factor = 4, @@ -417,7 +418,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 1, + .layer_sel_id = { 1, 1, 1, 0xf }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, @@ -582,7 +583,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 0, + .layer_sel_id = { 0, 0, 0, 0 }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .axi_bus_id = 0, @@ -600,7 +601,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 1, + .layer_sel_id = { 1, 1, 1, 1 }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, @@ -618,7 +619,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 4, + .layer_sel_id = { 4, 4, 4, 4 }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, @@ -636,7 +637,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, - .layer_sel_id = 5, + .layer_sel_id = { 5, 5, 5, 5 }, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, @@ -654,7 +655,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, .base = 0x1800, - .layer_sel_id = 2, + .layer_sel_id = { 2, 2, 2, 2 }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, .axi_bus_id = 0, @@ -670,7 +671,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, .base = 0x1a00, - .layer_sel_id = 3, + .layer_sel_id = { 3, 3, 3, 3 }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, .axi_bus_id = 0, @@ -686,7 +687,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .formats = formats_esmart, .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, - .layer_sel_id = 6, + .layer_sel_id = { 6, 6, 6, 6 }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, .axi_bus_id = 1, @@ -702,7 +703,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, .base = 0x1e00, - .layer_sel_id = 7, + .layer_sel_id = { 7, 7, 7, 7 }, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, .axi_bus_id = 1, @@ -1454,7 +1455,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) */ for (old_layer_id = 0; old_layer_id < vop2->data->win_size; old_layer_id++) { layer_sel_id = (layer_sel >> (4 * old_layer_id)) & 0xf; - if (layer_sel_id == win->data->layer_sel_id) + if (layer_sel_id == win->data->layer_sel_id[vp->id]) break; } @@ -1464,7 +1465,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) for (i = 0; i < vop2->data->win_size; i++) { old_win = &vop2->win[i]; layer_sel_id = (layer_sel >> (4 * layer_id)) & 0xf; - if (layer_sel_id == old_win->data->layer_sel_id) + if (layer_sel_id == old_win->data->layer_sel_id[vp->id]) break; } @@ -1512,13 +1513,14 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) } layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(layer_id, 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id); + layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id[vp->id]); /* * When we bind a window from layerM to layerN, we also need to move the old * window on layerN to layerM to avoid one window selected by two or more layers. */ layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, 0x7); - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, old_win->data->layer_sel_id); + layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, + old_win->data->layer_sel_id[vp->id]); } vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); -- 2.51.0 From 301618ed1d8ab7cfaec39b107eded9f263da2299 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Tue, 18 Feb 2025 19:27:33 +0800 Subject: [PATCH 03/16] drm/rockchip: vop2: Introduce vop hardware version There is a version number hardcoded in the VOP VERSION_INFO register, and the version number increments sequentially based on the production order of the SoC. So using this version number to distinguish different VOP features will simplify the code. Signed-off-by: Andy Yan Tested-by: Michael Riesch # on RK3568 Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250218112744.34433-7-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 26 ++++++++++++++------ drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 11 +++++++++ drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 3 +++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 1f13b942064e..bebe5bd70b90 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -355,7 +355,7 @@ static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode) static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format) { - if (vop2->data->soc_id == 3588) { + if (vop2->version == VOP_VERSION_RK3588) { if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 || bus_format == MEDIA_BUS_FMT_YUV10_1X30) return true; @@ -408,7 +408,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, if (modifier == DRM_FORMAT_MOD_INVALID) return false; - if (vop2->data->soc_id == 3568 || vop2->data->soc_id == 3566) { + if (vop2->version == VOP_VERSION_RK3568) { if (vop2_cluster_window(win)) { if (modifier == DRM_FORMAT_MOD_LINEAR) { drm_dbg_kms(vop2->drm, @@ -419,7 +419,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, } if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) { - if (vop2->data->soc_id == 3588) { + if (vop2->version == VOP_VERSION_RK3588) { if (!rockchip_afbc(plane, modifier)) { drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); return false; @@ -818,6 +818,7 @@ static void rk3588_vop2_power_domain_enable_all(struct vop2 *vop2) static void vop2_enable(struct vop2 *vop2) { int ret; + u32 version; ret = pm_runtime_resume_and_get(vop2->dev); if (ret < 0) { @@ -837,10 +838,20 @@ static void vop2_enable(struct vop2 *vop2) return; } + version = vop2_readl(vop2, RK3568_VERSION_INFO); + if (version != vop2->version) { + drm_err(vop2->drm, "Hardware version(0x%08x) mismatch\n", version); + return; + } + + /* + * rk3566 share the same vop version with rk3568, so + * we need to use soc_id for identification here. + */ if (vop2->data->soc_id == 3566) vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); - if (vop2->data->soc_id == 3588) + if (vop2->version == VOP_VERSION_RK3588) rk3588_vop2_power_domain_enable_all(vop2); vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); @@ -921,7 +932,7 @@ static void vop2_vp_dsp_lut_update_enable(struct vop2_video_port *vp) static inline bool vop2_supports_seamless_gamma_lut_update(struct vop2 *vop2) { - return (vop2->data->soc_id != 3566 && vop2->data->soc_id != 3568); + return vop2->version != VOP_VERSION_RK3568; } static bool vop2_gamma_lut_in_use(struct vop2 *vop2, struct vop2_video_port *vp) @@ -1263,7 +1274,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, &fb->format->format, afbc_en ? "AFBC" : "", &yrgb_mst); - if (vop2->data->soc_id > 3568) { + if (vop2->version > VOP_VERSION_RK3568) { vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id); vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id); vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id); @@ -1323,7 +1334,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, * this bit is gating disable, we should write 1 to * disable gating when enable afbc. */ - if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) + if (vop2->version == VOP_VERSION_RK3568) vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); else vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1); @@ -2534,6 +2545,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) vop2->dev = dev; vop2->data = vop2_data; vop2->ops = vop2_data->ops; + vop2->version = vop2_data->version; vop2->drm = drm; dev_set_drvdata(dev, vop2); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index cae211a558bd..a309042aa8e6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -13,6 +13,15 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" +#define VOP2_VERSION(major, minor, build) ((major) << 24 | (minor) << 16 | (build)) + +/* The VOP version of new SoC is bigger than the old */ +#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023) +#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786) +#define VOP_VERSION_RK3528 VOP2_VERSION(0x50, 0x17, 0x1263) +#define VOP_VERSION_RK3562 VOP2_VERSION(0x50, 0x17, 0x4350) +#define VOP_VERSION_RK3576 VOP2_VERSION(0x50, 0x19, 0x9765) + #define VOP2_VP_FEATURE_OUTPUT_10BIT BIT(0) #define VOP2_FEATURE_HAS_SYS_GRF BIT(0) @@ -243,6 +252,7 @@ struct vop2_ops { struct vop2_data { u8 nr_vps; u64 feature; + u32 version; const struct vop2_ops *ops; const struct vop2_win_data *win; const struct vop2_video_port_data *vp; @@ -260,6 +270,7 @@ struct vop2_data { }; struct vop2 { + u32 version; struct device *dev; struct drm_device *drm; struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 31edde7ae600..0afef24db144 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -1627,6 +1627,7 @@ static const struct vop2_ops rk3588_vop_ops = { }; static const struct vop2_data rk3566_vop = { + .version = VOP_VERSION_RK3568, .feature = VOP2_FEATURE_HAS_SYS_GRF, .nr_vps = 3, .max_input = { 4096, 2304 }, @@ -1645,6 +1646,7 @@ static const struct vop2_data rk3566_vop = { }; static const struct vop2_data rk3568_vop = { + .version = VOP_VERSION_RK3568, .feature = VOP2_FEATURE_HAS_SYS_GRF, .nr_vps = 3, .max_input = { 4096, 2304 }, @@ -1663,6 +1665,7 @@ static const struct vop2_data rk3568_vop = { }; static const struct vop2_data rk3588_vop = { + .version = VOP_VERSION_RK3588, .feature = VOP2_FEATURE_HAS_SYS_GRF | VOP2_FEATURE_HAS_VO1_GRF | VOP2_FEATURE_HAS_VOP_GRF | VOP2_FEATURE_HAS_SYS_PMU, .nr_vps = 4, -- 2.51.0 From db505ff68c122734ed0e944f9f8430eb89947fdb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 28 Feb 2025 08:32:47 +0000 Subject: [PATCH 04/16] drm/bridge: Fix spelling mistake "gettin" -> "getting" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Fixes: ff5781634c41 ("drm/bridge: sii902x: Implement HDMI audio support") Reviewed-by: Dmitry Baryshkov Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20250228083248.676473-1-colin.i.king@gmail.com --- drivers/gpu/drm/bridge/sii902x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 2100a687096e..914a2609a685 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -887,7 +887,7 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x, lanes[0] = 0; } else if (num_lanes < 0) { dev_err(dev, - "%s: Error gettin \"sil,i2s-data-lanes\": %d\n", + "%s: Error getting \"sil,i2s-data-lanes\": %d\n", __func__, num_lanes); return num_lanes; } -- 2.51.0 From 30188df0c387d9837562bbcc38ede98f6a1f0d46 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 20 Feb 2025 14:25:37 +0100 Subject: [PATCH 05/16] drm/tests: Drop drm_kunit_helper_acquire_ctx_alloc() lockdep complains when a lock is released in a separate thread the lock is taken in, and it turns out that kunit does run its actions in a separate thread than the test ran in. This means that drm_kunit_helper_acquire_ctx_alloc() just cannot work as it's supposed to, so let's just get rid of it. Suggested-by: Simona Vetter Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20250220132537.2834168-1-mripard@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tests/drm_atomic_state_test.c | 24 +- .../drm/tests/drm_hdmi_state_helper_test.c | 254 +++++++++++------- drivers/gpu/drm/tests/drm_kunit_helpers.c | 41 --- .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 46 ++-- include/drm/drm_kunit_helpers.h | 2 - 5 files changed, 191 insertions(+), 176 deletions(-) diff --git a/drivers/gpu/drm/tests/drm_atomic_state_test.c b/drivers/gpu/drm/tests/drm_atomic_state_test.c index 5945c3298901..2f6ac7a09f44 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_test.c @@ -189,7 +189,7 @@ static int set_up_atomic_state(struct kunit *test, static void drm_test_check_connector_changed_modeset(struct kunit *test) { struct drm_atomic_test_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector *old_conn, *new_conn; struct drm_atomic_state *state; struct drm_device *drm; @@ -203,14 +203,13 @@ static void drm_test_check_connector_changed_modeset(struct kunit *test) old_conn = &priv->connectors[0]; new_conn = &priv->connectors[1]; - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); // first modeset to enable - ret = set_up_atomic_state(test, priv, old_conn, ctx); + ret = set_up_atomic_state(test, priv, old_conn, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); new_conn_state = drm_atomic_get_connector_state(state, new_conn); @@ -231,6 +230,9 @@ static void drm_test_check_connector_changed_modeset(struct kunit *test) ret = drm_atomic_commit(state); KUNIT_ASSERT_EQ(test, ret, 0); KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -263,7 +265,7 @@ static void drm_test_check_valid_clones(struct kunit *test) int ret; const struct drm_clone_mode_test *param = test->param_value; struct drm_atomic_test_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_device *drm; struct drm_atomic_state *state; struct drm_crtc_state *crtc_state; @@ -273,13 +275,12 @@ static void drm_test_check_valid_clones(struct kunit *test) drm = &priv->drm; - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = set_up_atomic_state(test, priv, NULL, ctx); + ret = set_up_atomic_state(test, priv, NULL, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); @@ -292,6 +293,9 @@ static void drm_test_check_valid_clones(struct kunit *test) ret = drm_atomic_helper_check_modeset(drm, state); KUNIT_ASSERT_EQ(test, ret, param->expected_result); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t, diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 23ecc00accb2..e97efd3af9ed 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -273,7 +273,7 @@ drm_kunit_helper_connector_hdmi_init(struct kunit *test, static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *old_conn_state; struct drm_connector_state *new_conn_state; struct drm_crtc_state *crtc_state; @@ -296,13 +296,12 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test) preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); new_conn_state = drm_atomic_get_connector_state(state, conn); @@ -327,6 +326,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test) crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -337,7 +339,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test) static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *old_conn_state; struct drm_connector_state *new_conn_state; struct drm_crtc_state *crtc_state; @@ -360,13 +362,12 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); new_conn_state = drm_atomic_get_connector_state(state, conn); @@ -393,6 +394,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -403,7 +407,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *preferred; @@ -426,13 +430,12 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -449,6 +452,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -459,7 +465,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *mode; @@ -477,17 +483,16 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) conn = &priv->connector; KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); crtc = priv->crtc; - ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + ret = light_up_connector(test, drm, crtc, conn, mode, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -504,6 +509,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -514,7 +522,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *preferred; @@ -537,13 +545,12 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -562,6 +569,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) DRM_HDMI_BROADCAST_RGB_FULL); KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -572,7 +582,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *mode; @@ -590,17 +600,16 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) conn = &priv->connector; KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); crtc = priv->crtc; - ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + ret = light_up_connector(test, drm, crtc, conn, mode, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -619,6 +628,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) DRM_HDMI_BROADCAST_RGB_FULL); KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -629,7 +641,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *preferred; @@ -652,13 +664,12 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -677,6 +688,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) DRM_HDMI_BROADCAST_RGB_LIMITED); KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -687,7 +701,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_atomic_state *state; struct drm_display_mode *mode; @@ -705,17 +719,16 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te conn = &priv->connector; KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); crtc = priv->crtc; - ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + ret = light_up_connector(test, drm, crtc, conn, mode, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, conn); @@ -734,6 +747,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te DRM_HDMI_BROADCAST_RGB_LIMITED); KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -744,7 +760,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *old_conn_state; struct drm_connector_state *new_conn_state; struct drm_crtc_state *crtc_state; @@ -771,13 +787,12 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); new_conn_state = drm_atomic_get_connector_state(state, conn); @@ -808,6 +823,9 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -818,7 +836,7 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *old_conn_state; struct drm_connector_state *new_conn_state; struct drm_crtc_state *crtc_state; @@ -845,13 +863,12 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); new_conn_state = drm_atomic_get_connector_state(state, conn); @@ -880,6 +897,9 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -889,7 +909,7 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) static void drm_test_check_output_bpc_dvi(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -919,10 +939,9 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test) preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); conn_state = conn->state; @@ -930,6 +949,9 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -939,7 +961,7 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test) static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_mode *preferred; struct drm_connector *conn; @@ -964,10 +986,9 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); conn_state = conn->state; @@ -976,6 +997,9 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -986,7 +1010,7 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_mode *preferred; struct drm_connector *conn; @@ -1011,10 +1035,9 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); conn_state = conn->state; @@ -1023,6 +1046,9 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 10); KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1033,7 +1059,7 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_mode *preferred; struct drm_connector *conn; @@ -1058,10 +1084,9 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); conn_state = conn->state; @@ -1070,6 +1095,9 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 12); KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1083,7 +1111,7 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; struct drm_display_mode *preferred; struct drm_crtc_state *crtc_state; @@ -1104,16 +1132,15 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); /* You shouldn't be doing that at home. */ conn->hdmi.funcs = &reject_connector_hdmi_funcs; - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); crtc_state = drm_atomic_get_crtc_state(state, crtc); @@ -1123,6 +1150,9 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_EXPECT_LT(test, ret, 0); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1139,7 +1169,7 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1176,10 +1206,9 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1188,6 +1217,9 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1206,7 +1238,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1248,10 +1280,9 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1259,6 +1290,9 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1269,7 +1303,7 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *mode; @@ -1310,11 +1344,10 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) rate = mode->clock * 1500; KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); crtc = priv->crtc; - ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + ret = light_up_connector(test, drm, crtc, conn, mode, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1322,6 +1355,9 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1331,7 +1367,7 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1376,10 +1412,9 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1387,6 +1422,9 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1396,7 +1434,7 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1443,10 +1481,9 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1454,6 +1491,9 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1464,7 +1504,7 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1501,10 +1541,9 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1512,6 +1551,9 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -1522,7 +1564,7 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_display_info *info; struct drm_display_mode *preferred; @@ -1561,10 +1603,9 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_EXPECT_EQ(test, ret, 0); conn_state = conn->state; @@ -1572,13 +1613,16 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* Test that atomic check succeeds when disabling a connector. */ static void drm_test_check_disable_connector(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; struct drm_atomic_state *state; @@ -1593,8 +1637,7 @@ static void drm_test_check_disable_connector(struct kunit *test) 8); KUNIT_ASSERT_NOT_NULL(test, priv); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); conn = &priv->connector; preferred = find_preferred_mode(conn); @@ -1602,10 +1645,10 @@ static void drm_test_check_disable_connector(struct kunit *test) drm = &priv->drm; crtc = priv->crtc; - ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); crtc_state = drm_atomic_get_crtc_state(state, crtc); @@ -1623,6 +1666,9 @@ static void drm_test_check_disable_connector(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 3c0b7824c0be..a4eb68f0decc 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -80,47 +80,6 @@ __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, } EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); -static void action_drm_release_context(void *ptr) -{ - struct drm_modeset_acquire_ctx *ctx = ptr; - - drm_modeset_drop_locks(ctx); - drm_modeset_acquire_fini(ctx); -} - -/** - * drm_kunit_helper_acquire_ctx_alloc - Allocates an acquire context - * @test: The test context object - * - * Allocates and initializes a modeset acquire context. - * - * The context is tied to the kunit test context, so we must not call - * drm_modeset_acquire_fini() on it, it will be done so automatically. - * - * Returns: - * An ERR_PTR on error, a pointer to the newly allocated context otherwise - */ -struct drm_modeset_acquire_ctx * -drm_kunit_helper_acquire_ctx_alloc(struct kunit *test) -{ - struct drm_modeset_acquire_ctx *ctx; - int ret; - - ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, ctx); - - drm_modeset_acquire_init(ctx, 0); - - ret = kunit_add_action_or_reset(test, - action_drm_release_context, - ctx); - if (ret) - return ERR_PTR(ret); - - return ctx; -} -EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc); - static void kunit_action_drm_atomic_state_put(void *ptr) { struct drm_atomic_state *state = ptr; diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c index 40a05869a50e..992e8f5c5c6e 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -724,7 +724,7 @@ static void drm_vc4_test_pv_muxing_invalid(struct kunit *test) static int vc4_pv_muxing_test_init(struct kunit *test) { const struct pv_muxing_param *params = test->param_value; - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct pv_muxing_priv *priv; struct drm_device *drm; struct vc4_dev *vc4; @@ -737,13 +737,15 @@ static int vc4_pv_muxing_test_init(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); priv->vc4 = vc4; - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); drm = &vc4->base; - priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return 0; } @@ -782,7 +784,7 @@ static struct kunit_suite vc5_pv_muxing_test_suite = { */ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test) { - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct vc4_hvs_state *new_hvs_state; @@ -795,11 +797,10 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); drm = &vc4->base; - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); @@ -822,7 +823,7 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); @@ -843,6 +844,9 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use); KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -854,7 +858,7 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes */ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) { - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct vc4_hvs_state *new_hvs_state; @@ -867,11 +871,10 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); drm = &vc4->base; - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); @@ -905,7 +908,7 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0); @@ -929,6 +932,9 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel); } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } /* @@ -949,7 +955,7 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test) { - struct drm_modeset_acquire_ctx *ctx; + struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct drm_device *drm; @@ -959,11 +965,10 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - ctx = drm_kunit_helper_acquire_ctx_alloc(test); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + drm_modeset_acquire_init(&ctx, 0); drm = &vc4->base; - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); @@ -975,7 +980,7 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); @@ -987,6 +992,9 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_EXPECT_NULL(test, new_vc4_crtc_state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); } static struct kunit_case vc5_pv_muxing_bugs_tests[] = { diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index afdd46ef04f7..11d59ce0bac0 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -95,8 +95,6 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, sizeof(_type), \ offsetof(_type, _member), \ _feat)) -struct drm_modeset_acquire_ctx * -drm_kunit_helper_acquire_ctx_alloc(struct kunit *test); struct drm_atomic_state * drm_kunit_helper_atomic_state_alloc(struct kunit *test, -- 2.51.0 From 16e57a72780931c3c70dbc928aeee4a0518075de Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Feb 2025 12:38:17 +0300 Subject: [PATCH 06/16] drm/vc4: hdmi: Fix some NULL vs IS_ERR() bugs The devm_platform_ioremap_resource_byname() function doesn't return NULL, it returns error pointers. Update the checking to match. Fixes: b93f07cf090a ("drm/vc4: move to devm_platform_ioremap_resource() usage") Signed-off-by: Dan Carpenter Link: https://patchwork.freedesktop.org/patch/msgid/a952e2b4-d4b8-49ac-abd9-9967c50f4a80@stanley.mountain Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index d20e5c53ba75..37238a12baa5 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2928,8 +2928,8 @@ static int vc5_hdmi_init_resources(struct drm_device *drm, vc4_hdmi->hdmicore_regs = devm_platform_ioremap_resource_byname(pdev, "hdmi"); - if (!vc4_hdmi->hdmicore_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->hdmicore_regs)) + return PTR_ERR(vc4_hdmi->hdmicore_regs); /* This is shared between both HDMI controllers. Cannot * claim for both instances. Lets not convert to using @@ -2946,33 +2946,33 @@ static int vc5_hdmi_init_resources(struct drm_device *drm, vc4_hdmi->cec_regs = devm_platform_ioremap_resource_byname(pdev, "cec"); - if (!vc4_hdmi->cec_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->cec_regs)) + return PTR_ERR(vc4_hdmi->cec_regs); vc4_hdmi->csc_regs = devm_platform_ioremap_resource_byname(pdev, "csc"); - if (!vc4_hdmi->csc_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->csc_regs)) + return PTR_ERR(vc4_hdmi->csc_regs); vc4_hdmi->dvp_regs = devm_platform_ioremap_resource_byname(pdev, "dvp"); - if (!vc4_hdmi->dvp_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->dvp_regs)) + return PTR_ERR(vc4_hdmi->dvp_regs); vc4_hdmi->phy_regs = devm_platform_ioremap_resource_byname(pdev, "phy"); - if (!vc4_hdmi->phy_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->phy_regs)) + return PTR_ERR(vc4_hdmi->phy_regs); vc4_hdmi->ram_regs = devm_platform_ioremap_resource_byname(pdev, "packet"); - if (!vc4_hdmi->ram_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->ram_regs)) + return PTR_ERR(vc4_hdmi->ram_regs); vc4_hdmi->rm_regs = devm_platform_ioremap_resource_byname(pdev, "rm"); - if (!vc4_hdmi->rm_regs) - return -ENOMEM; + if (IS_ERR(vc4_hdmi->rm_regs)) + return PTR_ERR(vc4_hdmi->rm_regs); vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); if (IS_ERR(vc4_hdmi->hsm_clock)) { -- 2.51.0 From c9043706cb11b8005e145debe0a3211acd08e2c1 Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Wed, 26 Feb 2025 16:03:47 +0000 Subject: [PATCH 07/16] drm/format-helper: Add conversion from XRGB8888 to BGR888 Add XRGB8888 emulation helper for devices that only support BGR888. Signed-off-by: Kerem Karabay Signed-off-by: Aditya Garg Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/9A67EA95-9BC7-4D56-8F87-05EAC1C166AD@live.com --- drivers/gpu/drm/drm_format_helper.c | 54 +++++++++++++ .../gpu/drm/tests/drm_format_helper_test.c | 81 +++++++++++++++++++ include/drm/drm_format_helper.h | 3 + 3 files changed, 138 insertions(+) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index ecb278b63e8c..01d3ab307ac3 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -702,6 +702,57 @@ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pi } EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); +static void drm_fb_xrgb8888_to_bgr888_line(void *dbuf, const void *sbuf, unsigned int pixels) +{ + u8 *dbuf8 = dbuf; + const __le32 *sbuf32 = sbuf; + unsigned int x; + u32 pix; + + for (x = 0; x < pixels; x++) { + pix = le32_to_cpu(sbuf32[x]); + /* write red-green-blue to output in little endianness */ + *dbuf8++ = (pix & 0x00ff0000) >> 16; + *dbuf8++ = (pix & 0x0000ff00) >> 8; + *dbuf8++ = (pix & 0x000000ff) >> 0; + } +} + +/** + * drm_fb_xrgb8888_to_bgr888 - Convert XRGB8888 to BGR888 clip buffer + * @dst: Array of BGR888 destination buffers + * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines + * within @dst; can be NULL if scanlines are stored next to each other. + * @src: Array of XRGB8888 source buffers + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + * @state: Transform and conversion state + * + * This function copies parts of a framebuffer to display memory and converts the + * color format during the process. Destination and framebuffer formats must match. The + * parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at + * least as many entries as there are planes in @fb's format. Each entry stores the + * value for the format's respective color plane at the same index. + * + * This function does not apply clipping on @dst (i.e. the destination is at the + * top-left corner). + * + * Drivers can use this function for BGR888 devices that don't natively + * support XRGB8888. + */ +void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state) +{ + static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { + 3, + }; + + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, + drm_fb_xrgb8888_to_bgr888_line); +} +EXPORT_SYMBOL(drm_fb_xrgb8888_to_bgr888); + static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { __le32 *dbuf32 = dbuf; @@ -1104,6 +1155,9 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d } else if (dst_format == DRM_FORMAT_RGB888) { drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip, state); return 0; + } else if (dst_format == DRM_FORMAT_BGR888) { + drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip, state); + return 0; } else if (dst_format == DRM_FORMAT_ARGB8888) { drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip, state); return 0; diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c index 08992636ec05..35cd3405d045 100644 --- a/drivers/gpu/drm/tests/drm_format_helper_test.c +++ b/drivers/gpu/drm/tests/drm_format_helper_test.c @@ -60,6 +60,11 @@ struct convert_to_rgb888_result { const u8 expected[TEST_BUF_SIZE]; }; +struct convert_to_bgr888_result { + unsigned int dst_pitch; + const u8 expected[TEST_BUF_SIZE]; +}; + struct convert_to_argb8888_result { unsigned int dst_pitch; const u32 expected[TEST_BUF_SIZE]; @@ -107,6 +112,7 @@ struct convert_xrgb8888_case { struct convert_to_argb1555_result argb1555_result; struct convert_to_rgba5551_result rgba5551_result; struct convert_to_rgb888_result rgb888_result; + struct convert_to_bgr888_result bgr888_result; struct convert_to_argb8888_result argb8888_result; struct convert_to_xrgb2101010_result xrgb2101010_result; struct convert_to_argb2101010_result argb2101010_result; @@ -151,6 +157,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x00, 0x00, 0xFF }, }, + .bgr888_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { 0xFF, 0x00, 0x00 }, + }, .argb8888_result = { .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF0000 }, @@ -217,6 +227,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x00, 0x00, 0xFF }, }, + .bgr888_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { 0xFF, 0x00, 0x00 }, + }, .argb8888_result = { .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF0000 }, @@ -330,6 +344,15 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, }, }, + .bgr888_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + }, + }, .argb8888_result = { .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { @@ -468,6 +491,17 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, + .bgr888_result = { + .dst_pitch = 15, + .expected = { + 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, 0xA8, 0xF3, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA8, 0x03, 0x03, 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, .argb8888_result = { .dst_pitch = 20, .expected = { @@ -914,6 +948,52 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } +static void drm_test_fb_xrgb8888_to_bgr888(struct kunit *test) +{ + const struct convert_xrgb8888_case *params = test->param_value; + const struct convert_to_bgr888_result *result = ¶ms->bgr888_result; + size_t dst_size; + u8 *buf = NULL; + __le32 *xrgb8888 = NULL; + struct iosys_map dst, src; + + struct drm_framebuffer fb = { + .format = drm_format_info(DRM_FORMAT_XRGB8888), + .pitches = { params->pitch, 0, 0 }, + }; + + dst_size = conversion_buf_size(DRM_FORMAT_BGR888, result->dst_pitch, + ¶ms->clip, 0); + KUNIT_ASSERT_GT(test, dst_size, 0); + + buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + iosys_map_set_vaddr(&dst, buf); + + xrgb8888 = cpubuf_to_le32(test, params->xrgb8888, TEST_BUF_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); + iosys_map_set_vaddr(&src, xrgb8888); + + /* + * BGR888 expected results are already in little-endian + * order, so there's no need to convert the test output. + */ + drm_fb_xrgb8888_to_bgr888(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip, + &fmtcnv_state); + KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); + + buf = dst.vaddr; /* restore original value of buf */ + memset(buf, 0, dst_size); + + int blit_result = 0; + + blit_result = drm_fb_blit(&dst, &result->dst_pitch, DRM_FORMAT_BGR888, &src, &fb, ¶ms->clip, + &fmtcnv_state); + + KUNIT_EXPECT_FALSE(test, blit_result); + KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); +} + static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) { const struct convert_xrgb8888_case *params = test->param_value; @@ -1851,6 +1931,7 @@ static struct kunit_case drm_format_helper_test_cases[] = { KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb1555, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgba5551, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb888, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_bgr888, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb8888, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb2101010, convert_xrgb8888_gen_params), diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index a1347e47e9d5..d8539174ca11 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -96,6 +96,9 @@ void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, const struct drm_rect *clip, struct drm_format_conv_state *state); +void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, const struct drm_rect *clip, struct drm_format_conv_state *state); -- 2.51.0 From 0670c2f56e45b3f4541985a9ebe06d04308e43b0 Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Wed, 26 Feb 2025 16:04:36 +0000 Subject: [PATCH 08/16] drm/tiny: add driver for Apple Touch Bars in x86 Macs The Touch Bars found on x86 Macs support two USB configurations: one where the device presents itself as a HID keyboard and can display predefined sets of keys, and one where the operating system has full control over what is displayed. This commit adds support for the display functionality of the second configuration. Functionality for the first configuration has been merged in the HID tree. Note that this driver has only been tested on T2 Macs, and only includes the USB device ID for these devices. Testing on T1 Macs would be appreciated. Credit goes to Ben (Bingxing) Wang on GitHub for reverse engineering most of the protocol. Also, as requested by Andy, I would like to clarify the use of __packed structs in this driver: - All the packed structs are aligned except for appletbdrm_msg_information. - We have to pack appletbdrm_msg_information since it is requirement of the protocol. - We compared binaries compiled by keeping the rest structs __packed and not __packed using bloat-o-meter, and __packed was not affecting code generation. - To maintain consistency, rest structs have been kept __packed. I would also like to point out that since the driver was reverse-engineered the actual data types of the protocol might be different, including, but not limited to, endianness. Link: https://github.com/imbushuo/DFRDisplayKm Signed-off-by: Kerem Karabay Co-developed-by: Atharva Tiwari Signed-off-by: Atharva Tiwari Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Aun-Ali Zaidi Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/FCAC702C-F84A-47F9-8C78-BBBB34D08500@live.com --- MAINTAINERS | 8 + drivers/gpu/drm/tiny/Kconfig | 12 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/appletbdrm.c | 841 ++++++++++++++++++++++++++++++ 4 files changed, 862 insertions(+) create mode 100644 drivers/gpu/drm/tiny/appletbdrm.c diff --git a/MAINTAINERS b/MAINTAINERS index 1d8a7937bb1d..2b20daaf7077 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7149,6 +7149,14 @@ S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/sun4i/sun8i* +DRM DRIVER FOR APPLE TOUCH BARS +M: Aun-Ali Zaidi +M: Aditya Garg +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/tiny/appletbdrm.c + DRM DRIVER FOR ARM PL111 CLCD M: Linus Walleij S: Maintained diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 94cbdb1337c0..54c84c9801c1 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -1,5 +1,17 @@ # SPDX-License-Identifier: GPL-2.0-only +config DRM_APPLETBDRM + tristate "DRM support for Apple Touch Bars" + depends on DRM && USB && MMU + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + Say Y here if you want support for the display of Touch Bars on x86 + MacBook Pros. + + To compile this driver as a module, choose M here: the + module will be called appletbdrm. + config DRM_ARCPGU tristate "ARC PGU" depends on DRM && OF diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 60816d2eb4ff..0a3a7837a58b 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DRM_APPLETBDRM) += appletbdrm.o obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o obj-$(CONFIG_DRM_BOCHS) += bochs.o obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus-qemu.o diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c new file mode 100644 index 000000000000..f5d177e234e4 --- /dev/null +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -0,0 +1,841 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple Touch Bar DRM Driver + * + * Copyright (c) 2023 Kerem Karabay + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLETBDRM_PIXEL_FORMAT cpu_to_le32(0x52474241) /* RGBA, the actual format is BGR888 */ +#define APPLETBDRM_BITS_PER_PIXEL 24 + +#define APPLETBDRM_MSG_CLEAR_DISPLAY cpu_to_le32(0x434c5244) /* CLRD */ +#define APPLETBDRM_MSG_GET_INFORMATION cpu_to_le32(0x47494e46) /* GINF */ +#define APPLETBDRM_MSG_UPDATE_COMPLETE cpu_to_le32(0x5544434c) /* UDCL */ +#define APPLETBDRM_MSG_SIGNAL_READINESS cpu_to_le32(0x52454459) /* REDY */ + +#define APPLETBDRM_BULK_MSG_TIMEOUT 1000 + +#define drm_to_adev(_drm) container_of(_drm, struct appletbdrm_device, drm) +#define adev_to_udev(adev) interface_to_usbdev(to_usb_interface(adev->dmadev)) + +struct appletbdrm_msg_request_header { + __le16 unk_00; + __le16 unk_02; + __le32 unk_04; + __le32 unk_08; + __le32 size; +} __packed; + +struct appletbdrm_msg_response_header { + u8 unk_00[16]; + __le32 msg; +} __packed; + +struct appletbdrm_msg_simple_request { + struct appletbdrm_msg_request_header header; + __le32 msg; + u8 unk_14[8]; + __le32 size; +} __packed; + +struct appletbdrm_msg_information { + struct appletbdrm_msg_response_header header; + u8 unk_14[12]; + __le32 width; + __le32 height; + u8 bits_per_pixel; + __le32 bytes_per_row; + __le32 orientation; + __le32 bitmap_info; + __le32 pixel_format; + __le32 width_inches; /* floating point */ + __le32 height_inches; /* floating point */ +} __packed; + +struct appletbdrm_frame { + __le16 begin_x; + __le16 begin_y; + __le16 width; + __le16 height; + __le32 buf_size; + u8 buf[]; +} __packed; + +struct appletbdrm_fb_request_footer { + u8 unk_00[12]; + __le32 unk_0c; + u8 unk_10[12]; + __le32 unk_1c; + __le64 timestamp; + u8 unk_28[12]; + __le32 unk_34; + u8 unk_38[20]; + __le32 unk_4c; +} __packed; + +struct appletbdrm_fb_request { + struct appletbdrm_msg_request_header header; + __le16 unk_10; + u8 msg_id; + u8 unk_13[29]; + /* + * Contents of `data`: + * - struct appletbdrm_frame frames[]; + * - struct appletbdrm_fb_request_footer footer; + * - padding to make the total size a multiple of 16 + */ + u8 data[]; +} __packed; + +struct appletbdrm_fb_request_response { + struct appletbdrm_msg_response_header header; + u8 unk_14[12]; + __le64 timestamp; +} __packed; + +struct appletbdrm_device { + struct device *dmadev; + + unsigned int in_ep; + unsigned int out_ep; + + unsigned int width; + unsigned int height; + + struct drm_device drm; + struct drm_display_mode mode; + struct drm_connector connector; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; +}; + +struct appletbdrm_plane_state { + struct drm_shadow_plane_state base; + struct appletbdrm_fb_request *request; + struct appletbdrm_fb_request_response *response; + size_t request_size; + size_t frames_size; +}; + +static inline struct appletbdrm_plane_state *to_appletbdrm_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct appletbdrm_plane_state, base.base); +} + +static int appletbdrm_send_request(struct appletbdrm_device *adev, + struct appletbdrm_msg_request_header *request, size_t size) +{ + struct usb_device *udev = adev_to_udev(adev); + struct drm_device *drm = &adev->drm; + int ret, actual_size; + + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, adev->out_ep), + request, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT); + if (ret) { + drm_err(drm, "Failed to send message (%d)\n", ret); + return ret; + } + + if (actual_size != size) { + drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n", + actual_size, size); + return -EIO; + } + + return 0; +} + +static int appletbdrm_read_response(struct appletbdrm_device *adev, + struct appletbdrm_msg_response_header *response, + size_t size, __le32 expected_response) +{ + struct usb_device *udev = adev_to_udev(adev); + struct drm_device *drm = &adev->drm; + int ret, actual_size; + bool readiness_signal_received = false; + +retry: + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, adev->in_ep), + response, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT); + if (ret) { + drm_err(drm, "Failed to read response (%d)\n", ret); + return ret; + } + + /* + * The device responds to the first request sent in a particular + * timeframe after the USB device configuration is set with a readiness + * signal, in which case the response should be read again + */ + if (response->msg == APPLETBDRM_MSG_SIGNAL_READINESS) { + if (!readiness_signal_received) { + readiness_signal_received = true; + goto retry; + } + + drm_err(drm, "Encountered unexpected readiness signal\n"); + return -EINTR; + } + + if (actual_size != size) { + drm_err(drm, "Actual size (%d) doesn't match expected size (%lu)\n", + actual_size, size); + return -EBADMSG; + } + + if (response->msg != expected_response) { + drm_err(drm, "Unexpected response from device (expected %p4cc found %p4cc)\n", + &expected_response, &response->msg); + return -EIO; + } + + return 0; +} + +static int appletbdrm_send_msg(struct appletbdrm_device *adev, __le32 msg) +{ + struct appletbdrm_msg_simple_request *request; + int ret; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->header.unk_00 = cpu_to_le16(2); + request->header.unk_02 = cpu_to_le16(0x1512); + request->header.size = cpu_to_le32(sizeof(*request) - sizeof(request->header)); + request->msg = msg; + request->size = request->header.size; + + ret = appletbdrm_send_request(adev, &request->header, sizeof(*request)); + + kfree(request); + + return ret; +} + +static int appletbdrm_clear_display(struct appletbdrm_device *adev) +{ + return appletbdrm_send_msg(adev, APPLETBDRM_MSG_CLEAR_DISPLAY); +} + +static int appletbdrm_signal_readiness(struct appletbdrm_device *adev) +{ + return appletbdrm_send_msg(adev, APPLETBDRM_MSG_SIGNAL_READINESS); +} + +static int appletbdrm_get_information(struct appletbdrm_device *adev) +{ + struct appletbdrm_msg_information *info; + struct drm_device *drm = &adev->drm; + u8 bits_per_pixel; + __le32 pixel_format; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = appletbdrm_send_msg(adev, APPLETBDRM_MSG_GET_INFORMATION); + if (ret) + return ret; + + ret = appletbdrm_read_response(adev, &info->header, sizeof(*info), + APPLETBDRM_MSG_GET_INFORMATION); + if (ret) + goto free_info; + + bits_per_pixel = info->bits_per_pixel; + pixel_format = get_unaligned(&info->pixel_format); + + adev->width = get_unaligned_le32(&info->width); + adev->height = get_unaligned_le32(&info->height); + + if (bits_per_pixel != APPLETBDRM_BITS_PER_PIXEL) { + drm_err(drm, "Encountered unexpected bits per pixel value (%d)\n", bits_per_pixel); + ret = -EINVAL; + goto free_info; + } + + if (pixel_format != APPLETBDRM_PIXEL_FORMAT) { + drm_err(drm, "Encountered unknown pixel format (%p4cc)\n", &pixel_format); + ret = -EINVAL; + goto free_info; + } + +free_info: + kfree(info); + + return ret; +} + +static u32 rect_size(struct drm_rect *rect) +{ + return drm_rect_width(rect) * drm_rect_height(rect) * + (BITS_TO_BYTES(APPLETBDRM_BITS_PER_PIXEL)); +} + +static int appletbdrm_connector_helper_get_modes(struct drm_connector *connector) +{ + struct appletbdrm_device *adev = drm_to_adev(connector->dev); + + return drm_connector_helper_get_modes_fixed(connector, &adev->mode); +} + +static const u32 appletbdrm_primary_plane_formats[] = { + DRM_FORMAT_BGR888, + DRM_FORMAT_XRGB8888, /* emulated */ +}; + +static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + size_t frames_size = 0; + size_t request_size; + int ret; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage)); + } + + if (!frames_size) + return 0; + + request_size = ALIGN(sizeof(struct appletbdrm_fb_request) + + frames_size + + sizeof(struct appletbdrm_fb_request_footer), 16); + + appletbdrm_state->request = kzalloc(request_size, GFP_KERNEL); + + if (!appletbdrm_state->request) + return -ENOMEM; + + appletbdrm_state->response = kzalloc(sizeof(*appletbdrm_state->response), GFP_KERNEL); + + if (!appletbdrm_state->response) + return -ENOMEM; + + appletbdrm_state->request_size = request_size; + appletbdrm_state->frames_size = frames_size; + + return 0; +} + +static int appletbdrm_flush_damage(struct appletbdrm_device *adev, + struct drm_plane_state *old_state, + struct drm_plane_state *state) +{ + struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); + struct appletbdrm_fb_request_response *response = appletbdrm_state->response; + struct appletbdrm_fb_request_footer *footer; + struct drm_atomic_helper_damage_iter iter; + struct drm_framebuffer *fb = state->fb; + struct appletbdrm_fb_request *request = appletbdrm_state->request; + struct drm_device *drm = &adev->drm; + struct appletbdrm_frame *frame; + u64 timestamp = ktime_get_ns(); + struct drm_rect damage; + size_t frames_size = appletbdrm_state->frames_size; + size_t request_size = appletbdrm_state->request_size; + int ret; + + if (!frames_size) + return 0; + + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) { + drm_err(drm, "Failed to start CPU framebuffer access (%d)\n", ret); + goto end_fb_cpu_access; + } + + request->header.unk_00 = cpu_to_le16(2); + request->header.unk_02 = cpu_to_le16(0x12); + request->header.unk_04 = cpu_to_le32(9); + request->header.size = cpu_to_le32(request_size - sizeof(request->header)); + request->unk_10 = cpu_to_le16(1); + request->msg_id = timestamp; + + frame = (struct appletbdrm_frame *)request->data; + + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct drm_rect dst_clip = state->dst; + struct iosys_map dst = IOSYS_MAP_INIT_VADDR(frame->buf); + u32 buf_size = rect_size(&damage); + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + /* + * The coordinates need to be translated to the coordinate + * system the device expects, see the comment in + * appletbdrm_setup_mode_config + */ + frame->begin_x = cpu_to_le16(damage.y1); + frame->begin_y = cpu_to_le16(adev->height - damage.x2); + frame->width = cpu_to_le16(drm_rect_height(&damage)); + frame->height = cpu_to_le16(drm_rect_width(&damage)); + frame->buf_size = cpu_to_le32(buf_size); + + switch (fb->format->format) { + case DRM_FORMAT_XRGB8888: + drm_fb_xrgb8888_to_bgr888(&dst, NULL, &shadow_plane_state->data[0], fb, &damage, &shadow_plane_state->fmtcnv_state); + break; + default: + drm_fb_memcpy(&dst, NULL, &shadow_plane_state->data[0], fb, &damage); + break; + } + + frame = (void *)frame + struct_size(frame, buf, buf_size); + } + + footer = (struct appletbdrm_fb_request_footer *)&request->data[frames_size]; + + footer->unk_0c = cpu_to_le32(0xfffe); + footer->unk_1c = cpu_to_le32(0x80001); + footer->unk_34 = cpu_to_le32(0x80002); + footer->unk_4c = cpu_to_le32(0xffff); + footer->timestamp = cpu_to_le64(timestamp); + + ret = appletbdrm_send_request(adev, &request->header, request_size); + if (ret) + goto end_fb_cpu_access; + + ret = appletbdrm_read_response(adev, &response->header, sizeof(*response), + APPLETBDRM_MSG_UPDATE_COMPLETE); + if (ret) + goto end_fb_cpu_access; + + if (response->timestamp != footer->timestamp) { + drm_err(drm, "Response timestamp (%llu) doesn't match request timestamp (%llu)\n", + le64_to_cpu(response->timestamp), timestamp); + goto end_fb_cpu_access; + } + +end_fb_cpu_access: + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + + return ret; +} + +static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct appletbdrm_device *adev = drm_to_adev(plane->dev); + struct drm_device *drm = plane->dev; + struct drm_plane_state *plane_state = plane->state; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + + appletbdrm_flush_damage(adev, old_plane_state, plane_state); + + drm_dev_exit(idx); +} + +static void appletbdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev = plane->dev; + struct appletbdrm_device *adev = drm_to_adev(dev); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; + + appletbdrm_clear_display(adev); + + drm_dev_exit(idx); +} + +static void appletbdrm_primary_plane_reset(struct drm_plane *plane) +{ + struct appletbdrm_plane_state *appletbdrm_state; + + WARN_ON(plane->state); + + appletbdrm_state = kzalloc(sizeof(*appletbdrm_state), GFP_KERNEL); + if (!appletbdrm_state) + return; + + __drm_gem_reset_shadow_plane(plane, &appletbdrm_state->base); +} + +static struct drm_plane_state *appletbdrm_primary_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_shadow_plane_state *new_shadow_plane_state; + struct appletbdrm_plane_state *appletbdrm_state; + + if (WARN_ON(!plane->state)) + return NULL; + + appletbdrm_state = kzalloc(sizeof(*appletbdrm_state), GFP_KERNEL); + if (!appletbdrm_state) + return NULL; + + /* Request and response are not duplicated and are allocated in .atomic_check */ + appletbdrm_state->request = NULL; + appletbdrm_state->response = NULL; + + appletbdrm_state->request_size = 0; + appletbdrm_state->frames_size = 0; + + new_shadow_plane_state = &appletbdrm_state->base; + + __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); + + return &new_shadow_plane_state->base; +} + +static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state); + + kfree(appletbdrm_state->request); + kfree(appletbdrm_state->response); + + __drm_gem_destroy_shadow_plane_state(&appletbdrm_state->base); + + kfree(appletbdrm_state); +} + +static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = appletbdrm_primary_plane_helper_atomic_check, + .atomic_update = appletbdrm_primary_plane_helper_atomic_update, + .atomic_disable = appletbdrm_primary_plane_helper_atomic_disable, +}; + +static const struct drm_plane_funcs appletbdrm_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = appletbdrm_primary_plane_reset, + .atomic_duplicate_state = appletbdrm_primary_plane_duplicate_state, + .atomic_destroy_state = appletbdrm_primary_plane_destroy_state, + .destroy = drm_plane_cleanup, +}; + +static enum drm_mode_status appletbdrm_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct appletbdrm_device *adev = drm_to_adev(crtc->dev); + + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &adev->mode); +} + +static const struct drm_mode_config_funcs appletbdrm_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_connector_funcs appletbdrm_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .destroy = drm_connector_cleanup, + .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, +}; + +static const struct drm_connector_helper_funcs appletbdrm_connector_helper_funcs = { + .get_modes = appletbdrm_connector_helper_get_modes, +}; + +static const struct drm_crtc_helper_funcs appletbdrm_crtc_helper_funcs = { + .mode_valid = appletbdrm_crtc_helper_mode_valid, +}; + +static const struct drm_crtc_funcs appletbdrm_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 appletbdrm_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static struct drm_gem_object *appletbdrm_driver_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct appletbdrm_device *adev = drm_to_adev(dev); + + if (!adev->dmadev) + return ERR_PTR(-ENODEV); + + return drm_gem_prime_import_dev(dev, dma_buf, adev->dmadev); +} + +DEFINE_DRM_GEM_FOPS(appletbdrm_drm_fops); + +static const struct drm_driver appletbdrm_drm_driver = { + DRM_GEM_SHMEM_DRIVER_OPS, + .gem_prime_import = appletbdrm_driver_gem_prime_import, + .name = "appletbdrm", + .desc = "Apple Touch Bar DRM Driver", + .major = 1, + .minor = 0, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .fops = &appletbdrm_drm_fops, +}; + +static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev) +{ + struct drm_connector *connector = &adev->connector; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_device *drm = &adev->drm; + int ret; + + ret = drmm_mode_config_init(drm); + if (ret) { + drm_err(drm, "Failed to initialize mode configuration\n"); + return ret; + } + + primary_plane = &adev->primary_plane; + ret = drm_universal_plane_init(drm, primary_plane, 0, + &appletbdrm_primary_plane_funcs, + appletbdrm_primary_plane_formats, + ARRAY_SIZE(appletbdrm_primary_plane_formats), + NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + drm_err(drm, "Failed to initialize universal plane object\n"); + return ret; + } + + drm_plane_helper_add(primary_plane, &appletbdrm_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + crtc = &adev->crtc; + ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &appletbdrm_crtc_funcs, NULL); + if (ret) { + drm_err(drm, "Failed to initialize CRTC object\n"); + return ret; + } + + drm_crtc_helper_add(crtc, &appletbdrm_crtc_helper_funcs); + + encoder = &adev->encoder; + ret = drm_encoder_init(drm, encoder, &appletbdrm_encoder_funcs, + DRM_MODE_ENCODER_DAC, NULL); + if (ret) { + drm_err(drm, "Failed to initialize encoder\n"); + return ret; + } + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* + * The coordinate system used by the device is different from the + * coordinate system of the framebuffer in that the x and y axes are + * swapped, and that the y axis is inverted; so what the device reports + * as the height is actually the width of the framebuffer and vice + * versa. + */ + drm->mode_config.max_width = max(adev->height, DRM_SHADOW_PLANE_MAX_WIDTH); + drm->mode_config.max_height = max(adev->width, DRM_SHADOW_PLANE_MAX_HEIGHT); + drm->mode_config.preferred_depth = APPLETBDRM_BITS_PER_PIXEL; + drm->mode_config.funcs = &appletbdrm_mode_config_funcs; + + adev->mode = (struct drm_display_mode) { + DRM_MODE_INIT(60, adev->height, adev->width, + DRM_MODE_RES_MM(adev->height, 218), + DRM_MODE_RES_MM(adev->width, 218)) + }; + + ret = drm_connector_init(drm, connector, + &appletbdrm_connector_funcs, DRM_MODE_CONNECTOR_USB); + if (ret) { + drm_err(drm, "Failed to initialize connector\n"); + return ret; + } + + drm_connector_helper_add(connector, &appletbdrm_connector_helper_funcs); + + ret = drm_connector_set_panel_orientation(connector, + DRM_MODE_PANEL_ORIENTATION_RIGHT_UP); + if (ret) { + drm_err(drm, "Failed to set panel orientation\n"); + return ret; + } + + connector->display_info.non_desktop = true; + ret = drm_object_property_set_value(&connector->base, + drm->mode_config.non_desktop_property, true); + if (ret) { + drm_err(drm, "Failed to set non-desktop property\n"); + return ret; + } + + ret = drm_connector_attach_encoder(connector, encoder); + + if (ret) { + drm_err(drm, "Failed to initialize simple display pipe\n"); + return ret; + } + + drm_mode_config_reset(drm); + + return 0; +} + +static int appletbdrm_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct device *dev = &intf->dev; + struct appletbdrm_device *adev; + struct drm_device *drm = NULL; + int ret; + + ret = usb_find_common_endpoints(intf->cur_altsetting, &bulk_in, &bulk_out, NULL, NULL); + if (ret) { + drm_err(drm, "appletbdrm: Failed to find bulk endpoints\n"); + return ret; + } + + adev = devm_drm_dev_alloc(dev, &appletbdrm_drm_driver, struct appletbdrm_device, drm); + if (IS_ERR(adev)) + return PTR_ERR(adev); + + adev->in_ep = bulk_in->bEndpointAddress; + adev->out_ep = bulk_out->bEndpointAddress; + adev->dmadev = dev; + + drm = &adev->drm; + + usb_set_intfdata(intf, adev); + + ret = appletbdrm_get_information(adev); + if (ret) { + drm_err(drm, "Failed to get display information\n"); + return ret; + } + + ret = appletbdrm_signal_readiness(adev); + if (ret) { + drm_err(drm, "Failed to signal readiness\n"); + return ret; + } + + ret = appletbdrm_setup_mode_config(adev); + if (ret) { + drm_err(drm, "Failed to setup mode config\n"); + return ret; + } + + ret = drm_dev_register(drm, 0); + if (ret) { + drm_err(drm, "Failed to register DRM device\n"); + return ret; + } + + ret = appletbdrm_clear_display(adev); + if (ret) { + drm_err(drm, "Failed to clear display\n"); + return ret; + } + + return 0; +} + +static void appletbdrm_disconnect(struct usb_interface *intf) +{ + struct appletbdrm_device *adev = usb_get_intfdata(intf); + struct drm_device *drm = &adev->drm; + + put_device(adev->dmadev); + drm_dev_unplug(drm); + drm_atomic_helper_shutdown(drm); +} + +static void appletbdrm_shutdown(struct usb_interface *intf) +{ + struct appletbdrm_device *adev = usb_get_intfdata(intf); + + /* + * The framebuffer needs to be cleared on shutdown since its content + * persists across boots + */ + drm_atomic_helper_shutdown(&adev->drm); +} + +static const struct usb_device_id appletbdrm_usb_id_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) }, + {} +}; +MODULE_DEVICE_TABLE(usb, appletbdrm_usb_id_table); + +static struct usb_driver appletbdrm_usb_driver = { + .name = "appletbdrm", + .probe = appletbdrm_probe, + .disconnect = appletbdrm_disconnect, + .shutdown = appletbdrm_shutdown, + .id_table = appletbdrm_usb_id_table, +}; +module_usb_driver(appletbdrm_usb_driver); + +MODULE_AUTHOR("Kerem Karabay "); +MODULE_DESCRIPTION("Apple Touch Bar DRM Driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 7a108b930a84e71be71c3370eef6dd96fbb8f618 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Mon, 24 Feb 2025 12:02:16 +0100 Subject: [PATCH 09/16] dt-bindings: display: Add Apple pre-DCP display controller Add bindings for a secondary display controller present on certain Apple laptops. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Neal Gompa Signed-off-by: Sasha Finkelstein Link: https://patchwork.freedesktop.org/patch/msgid/20250224-adpdrm-v8-1-cccf96710f0f@gmail.com Signed-off-by: Alyssa Rosenzweig --- .../display/apple,h7-display-pipe-mipi.yaml | 83 +++++++++++++++++ .../display/apple,h7-display-pipe.yaml | 88 +++++++++++++++++++ .../bindings/display/panel/apple,summit.yaml | 58 ++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml create mode 100644 Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml create mode 100644 Documentation/devicetree/bindings/display/panel/apple,summit.yaml diff --git a/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml b/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml new file mode 100644 index 000000000000..5e6da66499a5 --- /dev/null +++ b/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/apple,h7-display-pipe-mipi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple pre-DCP display controller MIPI interface + +maintainers: + - Sasha Finkelstein + +description: + The MIPI controller part of the pre-DCP Apple display controller + +allOf: + - $ref: dsi-controller.yaml# + +properties: + compatible: + items: + - enum: + - apple,t8112-display-pipe-mipi + - apple,t8103-display-pipe-mipi + - const: apple,h7-display-pipe-mipi + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Input port. Always connected to the primary controller + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Output MIPI DSI port to the panel + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - ports + +unevaluatedProperties: false + +examples: + - | + dsi@28200000 { + compatible = "apple,t8103-display-pipe-mipi", "apple,h7-display-pipe-mipi"; + reg = <0x28200000 0xc000>; + power-domains = <&ps_dispdfr_mipi>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dfr_adp_out_mipi: endpoint { + remote-endpoint = <&dfr_adp_out_mipi>; + }; + }; + + port@1 { + reg = <1>; + + dfr_panel_in: endpoint { + remote-endpoint = <&dfr_mipi_out_panel>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml b/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml new file mode 100644 index 000000000000..102fb1804c0c --- /dev/null +++ b/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/apple,h7-display-pipe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple pre-DCP display controller + +maintainers: + - Sasha Finkelstein + +description: + A secondary display controller used to drive the "touchbar" on + certain Apple laptops. + +properties: + compatible: + items: + - enum: + - apple,t8112-display-pipe + - apple,t8103-display-pipe + - const: apple,h7-display-pipe + + reg: + items: + - description: Primary register block, controls planes and blending + - description: + Contains other configuration registers like interrupt + and FIFO control + + reg-names: + items: + - const: be + - const: fe + + power-domains: + description: + Phandles to pmgr entries that are needed for this controller to turn on. + Aside from that, their specific functions are unknown + maxItems: 2 + + interrupts: + items: + - description: Unknown function + - description: Primary interrupt. Vsync events are reported via it + + interrupt-names: + items: + - const: be + - const: fe + + iommus: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/properties/port + description: Output port. Always connected to apple,h7-display-pipe-mipi + +required: + - compatible + - reg + - interrupts + - port + +additionalProperties: false + +examples: + - | + #include + display-pipe@28200000 { + compatible = "apple,t8103-display-pipe", "apple,h7-display-pipe"; + reg = <0x28200000 0xc000>, + <0x28400000 0x4000>; + reg-names = "be", "fe"; + power-domains = <&ps_dispdfr_fe>, <&ps_dispdfr_be>; + interrupt-parent = <&aic>; + interrupts = , + ; + interrupt-names = "be", "fe"; + iommus = <&displaydfr_dart 0>; + + port { + dfr_adp_out_mipi: endpoint { + remote-endpoint = <&dfr_mipi_in_adp>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/apple,summit.yaml b/Documentation/devicetree/bindings/display/panel/apple,summit.yaml new file mode 100644 index 000000000000..f081755325e9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/apple,summit.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/apple,summit.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple "Summit" display panel + +maintainers: + - Sasha Finkelstein + +description: + An OLED panel used as a touchbar on certain Apple laptops. + Contains a backlight device, which controls brightness of the panel itself. + The backlight common properties are included for this reason + +allOf: + - $ref: panel-common.yaml# + - $ref: /schemas/leds/backlight/common.yaml# + +properties: + compatible: + items: + - enum: + - apple,j293-summit + - apple,j493-summit + - const: apple,summit + + reg: + maxItems: 1 + +required: + - compatible + - reg + - max-brightness + - port + +unevaluatedProperties: false + +examples: + - | + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "apple,j293-summit", "apple,summit"; + reg = <0>; + max-brightness = <255>; + + port { + endpoint { + remote-endpoint = <&dfr_bridge_out>; + }; + }; + }; + }; +... -- 2.51.0 From 332122eba628d537a1b7b96b976079753fd03039 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Mon, 24 Feb 2025 12:02:17 +0100 Subject: [PATCH 10/16] drm: adp: Add Apple Display Pipe driver This display controller is present on M-series chips and is used to drive the touchbar display. Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau Reviewed-by: Dmitry Baryshkov Reviewed-by: Neal Gompa Signed-off-by: Sasha Finkelstein Link: https://patchwork.freedesktop.org/patch/msgid/20250224-adpdrm-v8-2-cccf96710f0f@gmail.com Signed-off-by: Alyssa Rosenzweig --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/adp/Kconfig | 17 + drivers/gpu/drm/adp/Makefile | 5 + drivers/gpu/drm/adp/adp-mipi.c | 276 +++++++++++++++ drivers/gpu/drm/adp/adp_drv.c | 616 +++++++++++++++++++++++++++++++++ 6 files changed, 917 insertions(+) create mode 100644 drivers/gpu/drm/adp/Kconfig create mode 100644 drivers/gpu/drm/adp/Makefile create mode 100644 drivers/gpu/drm/adp/adp-mipi.c create mode 100644 drivers/gpu/drm/adp/adp_drv.c diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d9986fd52194..e5b59de28216 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -439,6 +439,8 @@ source "drivers/gpu/drm/mcde/Kconfig" source "drivers/gpu/drm/tidss/Kconfig" +source "drivers/gpu/drm/adp/Kconfig" + source "drivers/gpu/drm/xlnx/Kconfig" source "drivers/gpu/drm/gud/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 50604b49d1ac..4cd054188faf 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -206,6 +206,7 @@ obj-y += mxsfb/ obj-y += tiny/ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ +obj-$(CONFIG_DRM_ADP) += adp/ obj-$(CONFIG_DRM_XEN) += xen/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ obj-$(CONFIG_DRM_LIMA) += lima/ diff --git a/drivers/gpu/drm/adp/Kconfig b/drivers/gpu/drm/adp/Kconfig new file mode 100644 index 000000000000..9fcc27eb200d --- /dev/null +++ b/drivers/gpu/drm/adp/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +config DRM_ADP + tristate "DRM Support for pre-DCP Apple display controllers" + depends on DRM && OF && ARM64 + depends on ARCH_APPLE || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_BRIDGE_CONNECTOR + select DRM_DISPLAY_HELPER + select DRM_KMS_DMA_HELPER + select DRM_GEM_DMA_HELPER + select DRM_PANEL_BRIDGE + select VIDEOMODE_HELPERS + select DRM_MIPI_DSI + help + Chose this option if you have an Apple Arm laptop with a touchbar. + + If M is selected, this module will be called adpdrm. diff --git a/drivers/gpu/drm/adp/Makefile b/drivers/gpu/drm/adp/Makefile new file mode 100644 index 000000000000..8e7b618edd35 --- /dev/null +++ b/drivers/gpu/drm/adp/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT + +adpdrm-y := adp_drv.o +adpdrm-mipi-y := adp-mipi.o +obj-$(CONFIG_DRM_ADP) += adpdrm.o adpdrm-mipi.o diff --git a/drivers/gpu/drm/adp/adp-mipi.c b/drivers/gpu/drm/adp/adp-mipi.c new file mode 100644 index 000000000000..ad80542b60ed --- /dev/null +++ b/drivers/gpu/drm/adp/adp-mipi.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +#include +#include + +#define DSI_GEN_HDR 0x6c +#define DSI_GEN_PLD_DATA 0x70 + +#define DSI_CMD_PKT_STATUS 0x74 + +#define GEN_PLD_R_EMPTY BIT(4) +#define GEN_PLD_W_FULL BIT(3) +#define GEN_PLD_W_EMPTY BIT(2) +#define GEN_CMD_FULL BIT(1) +#define GEN_CMD_EMPTY BIT(0) +#define GEN_RD_CMD_BUSY BIT(6) +#define CMD_PKT_STATUS_TIMEOUT_US 20000 + +struct adp_mipi_drv_private { + struct mipi_dsi_host dsi; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + void __iomem *mipi; +}; + +#define mipi_to_adp(x) container_of(x, struct adp_mipi_drv_private, dsi) + +static int adp_dsi_gen_pkt_hdr_write(struct adp_mipi_drv_private *adp, u32 hdr_val) +{ + int ret; + u32 val, mask; + + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_CMD_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->dsi.dev, "failed to get available command FIFO\n"); + return ret; + } + + writel(hdr_val, adp->mipi + DSI_GEN_HDR); + + mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY; + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, (val & mask) == mask, + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->dsi.dev, "failed to write command FIFO\n"); + return ret; + } + + return 0; +} + +static int adp_dsi_write(struct adp_mipi_drv_private *adp, + const struct mipi_dsi_packet *packet) +{ + const u8 *tx_buf = packet->payload; + int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret; + __le32 word; + u32 val; + + while (len) { + if (len < pld_data_bytes) { + word = 0; + memcpy(&word, tx_buf, len); + writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA); + len = 0; + } else { + memcpy(&word, tx_buf, pld_data_bytes); + writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA); + tx_buf += pld_data_bytes; + len -= pld_data_bytes; + } + + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_PLD_W_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->dsi.dev, + "failed to get available write payload FIFO\n"); + return ret; + } + } + + word = 0; + memcpy(&word, packet->header, sizeof(packet->header)); + return adp_dsi_gen_pkt_hdr_write(adp, le32_to_cpu(word)); +} + +static int adp_dsi_read(struct adp_mipi_drv_private *adp, + const struct mipi_dsi_msg *msg) +{ + int i, j, ret, len = msg->rx_len; + u8 *buf = msg->rx_buf; + u32 val; + + /* Wait end of the read operation */ + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_RD_CMD_BUSY), + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->dsi.dev, "Timeout during read operation\n"); + return ret; + } + + for (i = 0; i < len; i += 4) { + /* Read fifo must not be empty before all bytes are read */ + ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS, + val, !(val & GEN_PLD_R_EMPTY), + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret) { + dev_err(adp->dsi.dev, "Read payload FIFO is empty\n"); + return ret; + } + + val = readl(adp->mipi + DSI_GEN_PLD_DATA); + for (j = 0; j < 4 && j + i < len; j++) + buf[i + j] = val >> (8 * j); + } + + return ret; +} + +static ssize_t adp_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct adp_mipi_drv_private *adp = mipi_to_adp(host); + struct mipi_dsi_packet packet; + int ret, nb_bytes; + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret) { + dev_err(adp->dsi.dev, "failed to create packet: %d\n", ret); + return ret; + } + + ret = adp_dsi_write(adp, &packet); + if (ret) + return ret; + + if (msg->rx_buf && msg->rx_len) { + ret = adp_dsi_read(adp, msg); + if (ret) + return ret; + nb_bytes = msg->rx_len; + } else { + nb_bytes = packet.size; + } + + return nb_bytes; +} + +static int adp_dsi_bind(struct device *dev, struct device *master, void *data) +{ + return 0; +} + +static void adp_dsi_unbind(struct device *dev, struct device *master, void *data) +{ +} + +static const struct component_ops adp_dsi_component_ops = { + .bind = adp_dsi_bind, + .unbind = adp_dsi_unbind, +}; + +static int adp_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + struct adp_mipi_drv_private *adp = mipi_to_adp(host); + struct drm_bridge *next; + int ret; + + next = devm_drm_of_get_bridge(adp->dsi.dev, adp->dsi.dev->of_node, 1, 0); + if (IS_ERR(next)) + return PTR_ERR(next); + + adp->next_bridge = next; + + drm_bridge_add(&adp->bridge); + + ret = component_add(host->dev, &adp_dsi_component_ops); + if (ret) { + pr_err("failed to add dsi_host component: %d\n", ret); + drm_bridge_remove(&adp->bridge); + return ret; + } + + return 0; +} + +static int adp_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *dev) +{ + struct adp_mipi_drv_private *adp = mipi_to_adp(host); + + component_del(host->dev, &adp_dsi_component_ops); + drm_bridge_remove(&adp->bridge); + return 0; +} + +static const struct mipi_dsi_host_ops adp_dsi_host_ops = { + .transfer = adp_dsi_host_transfer, + .attach = adp_dsi_host_attach, + .detach = adp_dsi_host_detach, +}; + +static int adp_dsi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct adp_mipi_drv_private *adp = + container_of(bridge, struct adp_mipi_drv_private, bridge); + + return drm_bridge_attach(bridge->encoder, adp->next_bridge, bridge, flags); +} + +static const struct drm_bridge_funcs adp_dsi_bridge_funcs = { + .attach = adp_dsi_bridge_attach, +}; + +static int adp_mipi_probe(struct platform_device *pdev) +{ + struct adp_mipi_drv_private *adp; + + adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL); + if (!adp) + return -ENOMEM; + + adp->mipi = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adp->mipi)) { + dev_err(&pdev->dev, "failed to map mipi mmio"); + return PTR_ERR(adp->mipi); + } + + adp->dsi.dev = &pdev->dev; + adp->dsi.ops = &adp_dsi_host_ops; + adp->bridge.funcs = &adp_dsi_bridge_funcs; + adp->bridge.of_node = pdev->dev.of_node; + adp->bridge.type = DRM_MODE_CONNECTOR_DSI; + dev_set_drvdata(&pdev->dev, adp); + return mipi_dsi_host_register(&adp->dsi); +} + +static void adp_mipi_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adp_mipi_drv_private *adp = dev_get_drvdata(dev); + + mipi_dsi_host_unregister(&adp->dsi); +} + +static const struct of_device_id adp_mipi_of_match[] = { + { .compatible = "apple,h7-display-pipe-mipi", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adp_mipi_of_match); + +static struct platform_driver adp_mipi_platform_driver = { + .driver = { + .name = "adp-mipi", + .of_match_table = adp_mipi_of_match, + }, + .probe = adp_mipi_probe, + .remove = adp_mipi_remove, +}; + +module_platform_driver(adp_mipi_platform_driver); + +MODULE_DESCRIPTION("Apple Display Pipe MIPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c new file mode 100644 index 000000000000..0a39abdc9238 --- /dev/null +++ b/drivers/gpu/drm/adp/adp_drv.c @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADP_INT_STATUS 0x34 +#define ADP_INT_STATUS_INT_MASK 0x7 +#define ADP_INT_STATUS_VBLANK 0x1 +#define ADP_CTRL 0x100 +#define ADP_CTRL_VBLANK_ON 0x12 +#define ADP_CTRL_FIFO_ON 0x601 +#define ADP_SCREEN_SIZE 0x0c +#define ADP_SCREEN_HSIZE GENMASK(15, 0) +#define ADP_SCREEN_VSIZE GENMASK(31, 16) + +#define ADBE_FIFO 0x10c0 +#define ADBE_FIFO_SYNC 0xc0000000 + +#define ADBE_BLEND_BYPASS 0x2020 +#define ADBE_BLEND_EN1 0x2028 +#define ADBE_BLEND_EN2 0x2074 +#define ADBE_BLEND_EN3 0x202c +#define ADBE_BLEND_EN4 0x2034 +#define ADBE_MASK_BUF 0x2200 + +#define ADBE_SRC_START 0x4040 +#define ADBE_SRC_SIZE 0x4048 +#define ADBE_DST_START 0x4050 +#define ADBE_DST_SIZE 0x4054 +#define ADBE_STRIDE 0x4038 +#define ADBE_FB_BASE 0x4030 + +#define ADBE_LAYER_EN1 0x4020 +#define ADBE_LAYER_EN2 0x4068 +#define ADBE_LAYER_EN3 0x40b4 +#define ADBE_LAYER_EN4 0x40f4 +#define ADBE_SCALE_CTL 0x40ac +#define ADBE_SCALE_CTL_BYPASS 0x100000 + +#define ADBE_LAYER_CTL 0x1038 +#define ADBE_LAYER_CTL_ENABLE 0x10000 + +#define ADBE_PIX_FMT 0x402c +#define ADBE_PIX_FMT_XRGB32 0x53e4001 + +static int adp_open(struct inode *inode, struct file *filp) +{ + /* + * The modesetting driver does not check the non-desktop connector + * property and keeps the device open and locked. If the touchbar daemon + * opens the device first, modesetting breaks the whole X session. + * Simply refuse to open the device for X11 server processes as + * workaround. + */ + if (current->comm[0] == 'X') + return -EBUSY; + + return drm_open(inode, filp); +} + +static const struct file_operations adp_fops = { + .owner = THIS_MODULE, + .open = adp_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, + .poll = drm_poll, + .read = drm_read, + .llseek = noop_llseek, + .mmap = drm_gem_mmap, + .fop_flags = FOP_UNSIGNED_OFFSET, + DRM_GEM_DMA_UNMAPPED_AREA_FOPS +}; + +static int adp_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + args->height = ALIGN(args->height, 64); + args->size = args->pitch * args->height; + + return drm_gem_dma_dumb_create_internal(file_priv, drm, args); +} + +static const struct drm_driver adp_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &adp_fops, + DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(adp_drm_gem_dumb_create), + .name = "adp", + .desc = "Apple Display Pipe DRM Driver", + .major = 0, + .minor = 1, +}; + +struct adp_drv_private { + struct drm_device drm; + struct drm_crtc crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + struct drm_bridge *next_bridge; + void __iomem *be; + void __iomem *fe; + u32 *mask_buf; + u64 mask_buf_size; + dma_addr_t mask_iova; + int be_irq; + int fe_irq; + spinlock_t irq_lock; + struct drm_pending_vblank_event *event; +}; + +#define to_adp(x) container_of(x, struct adp_drv_private, drm) +#define crtc_to_adp(x) container_of(x, struct adp_drv_private, crtc) + +static int adp_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state; + struct drm_crtc_state *crtc_state; + + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + + if (!new_plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +} + +static void adp_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp; + struct drm_rect src_rect; + struct drm_gem_dma_object *obj; + struct drm_framebuffer *fb; + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + u32 src_pos, src_size, dst_pos, dst_size; + + if (!plane || !new_state) + return; + + fb = new_state->fb; + if (!fb) + return; + adp = to_adp(plane->dev); + + drm_rect_fp_to_int(&src_rect, &new_state->src); + src_pos = src_rect.x1 << 16 | src_rect.y1; + dst_pos = new_state->dst.x1 << 16 | new_state->dst.y1; + src_size = drm_rect_width(&src_rect) << 16 | drm_rect_height(&src_rect); + dst_size = drm_rect_width(&new_state->dst) << 16 | + drm_rect_height(&new_state->dst); + writel(src_pos, adp->be + ADBE_SRC_START); + writel(src_size, adp->be + ADBE_SRC_SIZE); + writel(dst_pos, adp->be + ADBE_DST_START); + writel(dst_size, adp->be + ADBE_DST_SIZE); + writel(fb->pitches[0], adp->be + ADBE_STRIDE); + obj = drm_fb_dma_get_gem_obj(fb, 0); + if (obj) + writel(obj->dma_addr + fb->offsets[0], adp->be + ADBE_FB_BASE); + + writel(BIT(0), adp->be + ADBE_LAYER_EN1); + writel(BIT(0), adp->be + ADBE_LAYER_EN2); + writel(BIT(0), adp->be + ADBE_LAYER_EN3); + writel(BIT(0), adp->be + ADBE_LAYER_EN4); + writel(ADBE_SCALE_CTL_BYPASS, adp->be + ADBE_SCALE_CTL); + writel(ADBE_LAYER_CTL_ENABLE | BIT(0), adp->be + ADBE_LAYER_CTL); + writel(ADBE_PIX_FMT_XRGB32, adp->be + ADBE_PIX_FMT); +} + +static void adp_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = to_adp(plane->dev); + + writel(0x0, adp->be + ADBE_LAYER_EN1); + writel(0x0, adp->be + ADBE_LAYER_EN2); + writel(0x0, adp->be + ADBE_LAYER_EN3); + writel(0x0, adp->be + ADBE_LAYER_EN4); + writel(ADBE_LAYER_CTL_ENABLE, adp->be + ADBE_LAYER_CTL); +} + +static const struct drm_plane_helper_funcs adp_plane_helper_funcs = { + .atomic_check = adp_plane_atomic_check, + .atomic_update = adp_plane_atomic_update, + .atomic_disable = adp_plane_atomic_disable, + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS +}; + +static const struct drm_plane_funcs adp_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + DRM_GEM_SHADOW_PLANE_FUNCS +}; + +static const u32 plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +#define ALL_CRTCS 1 + +static struct drm_plane *adp_plane_new(struct adp_drv_private *adp) +{ + struct drm_device *drm = &adp->drm; + struct drm_plane *plane; + + plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0, + ALL_CRTCS, &adp_plane_funcs, + plane_formats, ARRAY_SIZE(plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, "plane"); + if (!plane) { + drm_err(drm, "failed to allocate plane"); + return ERR_PTR(-ENOMEM); + } + + drm_plane_helper_add(plane, &adp_plane_helper_funcs); + return plane; +} + +static void adp_enable_vblank(struct adp_drv_private *adp) +{ + u32 cur_ctrl; + + writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS); + + cur_ctrl = readl(adp->fe + ADP_CTRL); + writel(cur_ctrl | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); +} + +static int adp_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct adp_drv_private *adp = to_adp(dev); + + adp_enable_vblank(adp); + + return 0; +} + +static void adp_disable_vblank(struct adp_drv_private *adp) +{ + u32 cur_ctrl; + + cur_ctrl = readl(adp->fe + ADP_CTRL); + writel(cur_ctrl & ~ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); + writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS); +} + +static void adp_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct adp_drv_private *adp = to_adp(dev); + + adp_disable_vblank(adp); +} + +static void adp_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = crtc_to_adp(crtc); + + writel(BIT(0), adp->be + ADBE_BLEND_EN2); + writel(BIT(4), adp->be + ADBE_BLEND_EN1); + writel(BIT(0), adp->be + ADBE_BLEND_EN3); + writel(BIT(0), adp->be + ADBE_BLEND_BYPASS); + writel(BIT(0), adp->be + ADBE_BLEND_EN4); +} + +static void adp_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct adp_drv_private *adp = crtc_to_adp(crtc); + struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); + + drm_atomic_helper_disable_planes_on_crtc(old_state, false); + + writel(0x0, adp->be + ADBE_BLEND_EN2); + writel(0x0, adp->be + ADBE_BLEND_EN1); + writel(0x0, adp->be + ADBE_BLEND_EN3); + writel(0x0, adp->be + ADBE_BLEND_BYPASS); + writel(0x0, adp->be + ADBE_BLEND_EN4); + drm_crtc_vblank_off(crtc); +} + +static void adp_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + u32 frame_num = 1; + struct adp_drv_private *adp = crtc_to_adp(crtc); + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc); + u64 new_size = ALIGN(new_state->mode.hdisplay * + new_state->mode.vdisplay * 4, PAGE_SIZE); + + if (new_size != adp->mask_buf_size) { + if (adp->mask_buf) + dma_free_coherent(crtc->dev->dev, adp->mask_buf_size, + adp->mask_buf, adp->mask_iova); + adp->mask_buf = NULL; + if (new_size != 0) { + adp->mask_buf = dma_alloc_coherent(crtc->dev->dev, new_size, + &adp->mask_iova, GFP_KERNEL); + memset(adp->mask_buf, 0xFF, new_size); + writel(adp->mask_iova, adp->be + ADBE_MASK_BUF); + } + adp->mask_buf_size = new_size; + } + writel(ADBE_FIFO_SYNC | frame_num, adp->be + ADBE_FIFO); + //FIXME: use adbe flush interrupt + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_vblank_get(crtc); + adp->event = crtc->state->event; + } + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); +} + +static const struct drm_crtc_funcs adp_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = adp_crtc_enable_vblank, + .disable_vblank = adp_crtc_disable_vblank, +}; + + +static const struct drm_crtc_helper_funcs adp_crtc_helper_funcs = { + .atomic_enable = adp_crtc_atomic_enable, + .atomic_disable = adp_crtc_atomic_disable, + .atomic_flush = adp_crtc_atomic_flush, +}; + +static int adp_setup_crtc(struct adp_drv_private *adp) +{ + struct drm_device *drm = &adp->drm; + struct drm_plane *primary; + int ret; + + primary = adp_plane_new(adp); + if (IS_ERR(primary)) + return PTR_ERR(primary); + + ret = drm_crtc_init_with_planes(drm, &adp->crtc, primary, + NULL, &adp_crtc_funcs, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(&adp->crtc, &adp_crtc_helper_funcs); + return 0; +} + +static const struct drm_mode_config_funcs adp_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int adp_setup_mode_config(struct adp_drv_private *adp) +{ + struct drm_device *drm = &adp->drm; + int ret; + u32 size; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + /* + * Query screen size restrict the frame buffer size to the screen size + * aligned to the next multiple of 64. This is not necessary but can be + * used as simple check for non-desktop devices. + * Xorg's modesetting driver does not care about the connector + * "non-desktop" property. The max frame buffer width or height can be + * easily checked and a device can be reject if the max width/height is + * smaller than 120 for example. + * Any touchbar daemon is not limited by this small framebuffer size. + */ + size = readl(adp->fe + ADP_SCREEN_SIZE); + + drm->mode_config.min_width = 32; + drm->mode_config.min_height = 32; + drm->mode_config.max_width = ALIGN(FIELD_GET(ADP_SCREEN_HSIZE, size), 64); + drm->mode_config.max_height = ALIGN(FIELD_GET(ADP_SCREEN_VSIZE, size), 64); + drm->mode_config.preferred_depth = 24; + drm->mode_config.prefer_shadow = 0; + drm->mode_config.funcs = &adp_mode_config_funcs; + + ret = adp_setup_crtc(adp); + if (ret) { + drm_err(drm, "failed to create crtc"); + return ret; + } + + adp->encoder = drmm_plain_encoder_alloc(drm, NULL, DRM_MODE_ENCODER_DSI, NULL); + if (IS_ERR(adp->encoder)) { + drm_err(drm, "failed to init encoder"); + return PTR_ERR(adp->encoder); + } + adp->encoder->possible_crtcs = ALL_CRTCS; + + ret = drm_bridge_attach(adp->encoder, adp->next_bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + drm_err(drm, "failed to init bridge chain"); + return ret; + } + + adp->connector = drm_bridge_connector_init(drm, adp->encoder); + if (IS_ERR(adp->connector)) + return PTR_ERR(adp->connector); + + drm_connector_attach_encoder(adp->connector, adp->encoder); + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) { + drm_err(drm, "failed to initialize vblank"); + return ret; + } + + drm_mode_config_reset(drm); + + return 0; +} + +static int adp_parse_of(struct platform_device *pdev, struct adp_drv_private *adp) +{ + struct device *dev = &pdev->dev; + + adp->be = devm_platform_ioremap_resource_byname(pdev, "be"); + if (IS_ERR(adp->be)) { + dev_err(dev, "failed to map display backend mmio"); + return PTR_ERR(adp->be); + } + + adp->fe = devm_platform_ioremap_resource_byname(pdev, "fe"); + if (IS_ERR(adp->fe)) { + dev_err(dev, "failed to map display pipe mmio"); + return PTR_ERR(adp->fe); + } + + adp->be_irq = platform_get_irq_byname(pdev, "be"); + if (adp->be_irq < 0) { + dev_err(dev, "failed to find be irq"); + return adp->be_irq; + } + + adp->fe_irq = platform_get_irq_byname(pdev, "fe"); + if (adp->fe_irq < 0) { + dev_err(dev, "failed to find fe irq"); + return adp->fe_irq; + } + + return 0; +} + +static irqreturn_t adp_fe_irq(int irq, void *arg) +{ + struct adp_drv_private *adp = (struct adp_drv_private *)arg; + u32 int_status; + u32 int_ctl; + + spin_lock(&adp->irq_lock); + + int_status = readl(adp->fe + ADP_INT_STATUS); + if (int_status & ADP_INT_STATUS_VBLANK) { + drm_crtc_handle_vblank(&adp->crtc); + spin_lock(&adp->crtc.dev->event_lock); + if (adp->event) { + int_ctl = readl(adp->fe + ADP_CTRL); + if ((int_ctl & 0xF00) == 0x600) { + drm_crtc_send_vblank_event(&adp->crtc, adp->event); + adp->event = NULL; + drm_crtc_vblank_put(&adp->crtc); + } + } + spin_unlock(&adp->crtc.dev->event_lock); + } + + writel(int_status, adp->fe + ADP_INT_STATUS); + + spin_unlock(&adp->irq_lock); + + return IRQ_HANDLED; +} + +static int adp_drm_bind(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct adp_drv_private *adp = to_adp(drm); + int err; + + adp_disable_vblank(adp); + writel(ADP_CTRL_FIFO_ON | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL); + + adp->next_bridge = drmm_of_get_bridge(&adp->drm, dev->of_node, 0, 0); + if (IS_ERR(adp->next_bridge)) { + dev_err(dev, "failed to find next bridge"); + return PTR_ERR(adp->next_bridge); + } + + err = adp_setup_mode_config(adp); + if (err < 0) + return err; + + err = request_irq(adp->fe_irq, adp_fe_irq, 0, "adp-fe", adp); + if (err) + return err; + + err = drm_dev_register(&adp->drm, 0); + if (err) + return err; + + return 0; +} + +static void adp_drm_unbind(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct adp_drv_private *adp = to_adp(drm); + + drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); + free_irq(adp->fe_irq, adp); +} + +static const struct component_master_ops adp_master_ops = { + .bind = adp_drm_bind, + .unbind = adp_drm_unbind, +}; + +static int compare_dev(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int adp_probe(struct platform_device *pdev) +{ + struct device_node *port; + struct component_match *match = NULL; + struct adp_drv_private *adp; + int err; + + adp = devm_drm_dev_alloc(&pdev->dev, &adp_driver, struct adp_drv_private, drm); + if (IS_ERR(adp)) + return PTR_ERR(adp); + + spin_lock_init(&adp->irq_lock); + + dev_set_drvdata(&pdev->dev, &adp->drm); + + err = adp_parse_of(pdev, adp); + if (err < 0) + return err; + + port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); + if (!port) + return -ENODEV; + + drm_of_component_match_add(&pdev->dev, &match, compare_dev, port); + of_node_put(port); + + return component_master_add_with_match(&pdev->dev, &adp_master_ops, match); +} + +static void adp_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &adp_master_ops); + dev_set_drvdata(&pdev->dev, NULL); +} + +static const struct of_device_id adp_of_match[] = { + { .compatible = "apple,h7-display-pipe", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adp_of_match); + +static struct platform_driver adp_platform_driver = { + .driver = { + .name = "adp", + .of_match_table = adp_of_match, + }, + .probe = adp_probe, + .remove = adp_remove, +}; + +module_platform_driver(adp_platform_driver); + +MODULE_DESCRIPTION("Apple Display Pipe DRM driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 4d2a877cc0efefa815648f1ed5f5b2b796f55bab Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Mon, 24 Feb 2025 12:02:20 +0100 Subject: [PATCH 11/16] MAINTAINERS: Add entries for touchbar display driver Add the MAINTAINERS entries for the driver Acked-by: Sven Peter Reviewed-by: Neil Armstrong Reviewed-by: Neal Gompa Signed-off-by: Sasha Finkelstein Link: https://patchwork.freedesktop.org/patch/msgid/20250224-adpdrm-v8-5-cccf96710f0f@gmail.com Signed-off-by: Alyssa Rosenzweig --- MAINTAINERS | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2b20daaf7077..d1ac30eae9fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7835,6 +7835,22 @@ F: drivers/gpu/host1x/ F: include/linux/host1x.h F: include/uapi/drm/tegra_drm.h +DRM DRIVERS FOR PRE-DCP APPLE DISPLAY OUTPUT +M: Sasha Finkelstein +R: Janne Grunau +L: dri-devel@lists.freedesktop.org +L: asahi@lists.linux.dev +S: Maintained +W: https://asahilinux.org +B: https://github.com/AsahiLinux/linux/issues +C: irc://irc.oftc.net/asahi-dev +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml +F: Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml +F: Documentation/devicetree/bindings/display/panel/apple,summit.yaml +F: drivers/gpu/drm/adp/ +F: drivers/gpu/drm/panel/panel-summit.c + DRM DRIVERS FOR RENESAS R-CAR M: Laurent Pinchart M: Tomi Valkeinen -- 2.51.0 From 6fd4f8a26a21dd2075cfcc7eae3b9d440d886571 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 3 Mar 2025 11:44:15 +0800 Subject: [PATCH 12/16] drm/rockchip: vop2: Register the primary plane and overlay plane separately In the upcoming VOP of rk3576, a Window cannot attach to all Video Ports, so make sure all VP find it's suitable primary plane, then register the remain windows as overlay plane will make code easier. Signed-off-by: Andy Yan Tested-by: Michael Riesch # on RK3568 Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250303034436.192400-2-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 99 ++++++++++++-------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index bebe5bd70b90..0af5059ff7d8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2264,22 +2264,29 @@ static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, return 0; } -static struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2) +/* + * On RK3566 these windows don't have an independent + * framebuffer. They can only share/mirror the framebuffer + * with smart0, esmart0 and cluster0 respectively. + * And RK3566 share the same vop version with Rk3568, so we + * need to use soc_id for identification here. + */ +static bool vop2_is_mirror_win(struct vop2_win *win) { - int i; - - for (i = 0; i < vop2->data->nr_vps; i++) { - struct vop2_video_port *vp = &vop2->vps[i]; - - if (!vp->crtc.port) - continue; - if (vp->primary_plane) - continue; + struct vop2 *vop2 = win->vop2; - return vp; + if (vop2->data->soc_id == 3566) { + switch (win->data->phys_id) { + case ROCKCHIP_VOP2_SMART1: + case ROCKCHIP_VOP2_ESMART1: + case ROCKCHIP_VOP2_CLUSTER1: + return true; + default: + return false; + } + } else { + return false; } - - return NULL; } static int vop2_create_crtcs(struct vop2 *vop2) @@ -2290,7 +2297,9 @@ static int vop2_create_crtcs(struct vop2 *vop2) struct drm_plane *plane; struct device_node *port; struct vop2_video_port *vp; - int i, nvp, nvps = 0; + struct vop2_win *win; + u32 possible_crtcs; + int i, j, nvp, nvps = 0; int ret; for (i = 0; i < vop2_data->nr_vps; i++) { @@ -2326,42 +2335,54 @@ static int vop2_create_crtcs(struct vop2 *vop2) } nvp = 0; - for (i = 0; i < vop2->registered_num_wins; i++) { - struct vop2_win *win = &vop2->win[i]; - u32 possible_crtcs = 0; - - if (vop2->data->soc_id == 3566) { - /* - * On RK3566 these windows don't have an independent - * framebuffer. They share the framebuffer with smart0, - * esmart0 and cluster0 respectively. - */ - switch (win->data->phys_id) { - case ROCKCHIP_VOP2_SMART1: - case ROCKCHIP_VOP2_ESMART1: - case ROCKCHIP_VOP2_CLUSTER1: + /* Register a primary plane for every crtc */ + for (i = 0; i < vop2_data->nr_vps; i++) { + vp = &vop2->vps[i]; + + if (!vp->crtc.port) + continue; + + for (j = 0; j < vop2->registered_num_wins; j++) { + win = &vop2->win[j]; + + /* Aready registered as primary plane */ + if (win->base.type == DRM_PLANE_TYPE_PRIMARY) + continue; + + if (vop2_is_mirror_win(win)) continue; - } - } - if (win->type == DRM_PLANE_TYPE_PRIMARY) { - vp = find_vp_without_primary(vop2); - if (vp) { + if (win->type == DRM_PLANE_TYPE_PRIMARY) { possible_crtcs = BIT(nvp); vp->primary_plane = win; + ret = vop2_plane_init(vop2, win, possible_crtcs); + if (ret) + return dev_err_probe(drm->dev, ret, + "failed to init primary plane %s\n", + win->data->name); nvp++; - } else { - /* change the unused primary window to overlay window */ - win->type = DRM_PLANE_TYPE_OVERLAY; + break; } } + } + + /* Register all unused window as overlay plane */ + for (i = 0; i < vop2->registered_num_wins; i++) { + win = &vop2->win[i]; + + /* Aready registered as primary plane */ + if (win->base.type == DRM_PLANE_TYPE_PRIMARY) + continue; + + if (vop2_is_mirror_win(win)) + continue; - if (win->type == DRM_PLANE_TYPE_OVERLAY) - possible_crtcs = (1 << nvps) - 1; + win->type = DRM_PLANE_TYPE_OVERLAY; + possible_crtcs = (1 << nvps) - 1; ret = vop2_plane_init(vop2, win, possible_crtcs); if (ret) - return dev_err_probe(drm->dev, ret, "failed to init plane %s\n", + return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n", win->data->name); } -- 2.51.0 From b90fa71a11600276c993e620abea8ca9f2045401 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 3 Mar 2025 11:44:16 +0800 Subject: [PATCH 13/16] drm/rockchip: vop2: Set plane possible crtcs by possible vp mask In the upcoming VOP of rk3576, a window cannot attach to all Video Ports, we introduce a possible_vp_mask for every window to indicate which Video Ports this window can attach to. Signed-off-by: Andy Yan Tested-by: Michael Riesch # on RK3568 Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250303034436.192400-3-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 18 +++++++++++++++++- drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 1 + drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 0af5059ff7d8..1280c5f07557 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2349,6 +2349,10 @@ static int vop2_create_crtcs(struct vop2 *vop2) if (win->base.type == DRM_PLANE_TYPE_PRIMARY) continue; + /* If this win can not attached to this VP */ + if (!(win->data->possible_vp_mask & BIT(vp->id))) + continue; + if (vop2_is_mirror_win(win)) continue; @@ -2379,7 +2383,19 @@ static int vop2_create_crtcs(struct vop2 *vop2) win->type = DRM_PLANE_TYPE_OVERLAY; - possible_crtcs = (1 << nvps) - 1; + possible_crtcs = 0; + nvp = 0; + for (j = 0; j < vop2_data->nr_vps; j++) { + vp = &vop2->vps[j]; + + if (!vp->crtc.port) + continue; + + if (win->data->possible_vp_mask & BIT(vp->id)) + possible_crtcs |= BIT(nvp); + nvp++; + } + ret = vop2_plane_init(vop2, win, possible_crtcs); if (ret) return dev_err_probe(drm->dev, ret, "failed to init overlay plane %s\n", diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index a309042aa8e6..46d37c61279e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -167,6 +167,7 @@ struct vop2_win_data { unsigned int phys_id; u32 base; + u32 possible_vp_mask; enum drm_plane_type type; u32 nformats; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 0afef24db144..97df9d479f11 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -347,6 +347,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .name = "Smart0-win0", .phys_id = ROCKCHIP_VOP2_SMART0, .base = 0x1c00, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_smart, .nformats = ARRAY_SIZE(formats_smart), .format_modifiers = format_modifiers, @@ -360,6 +361,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { }, { .name = "Smart1-win0", .phys_id = ROCKCHIP_VOP2_SMART1, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_smart, .nformats = ARRAY_SIZE(formats_smart), .format_modifiers = format_modifiers, @@ -373,6 +375,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { }, { .name = "Esmart1-win0", .phys_id = ROCKCHIP_VOP2_ESMART1, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_rk356x_esmart, .nformats = ARRAY_SIZE(formats_rk356x_esmart), .format_modifiers = format_modifiers, @@ -386,6 +389,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { }, { .name = "Esmart0-win0", .phys_id = ROCKCHIP_VOP2_ESMART0, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_rk356x_esmart, .nformats = ARRAY_SIZE(formats_rk356x_esmart), .format_modifiers = format_modifiers, @@ -400,6 +404,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .name = "Cluster0-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER0, .base = 0x1000, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -415,6 +420,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .name = "Cluster1-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER1, .base = 0x1200, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -580,6 +586,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .name = "Cluster0-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER0, .base = 0x1000, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -598,6 +605,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .name = "Cluster1-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER1, .base = 0x1200, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -616,6 +624,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .name = "Cluster2-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER2, .base = 0x1400, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -634,6 +643,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .name = "Cluster3-win0", .phys_id = ROCKCHIP_VOP2_CLUSTER3, .base = 0x1600, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_cluster, .nformats = ARRAY_SIZE(formats_cluster), .format_modifiers = format_modifiers_afbc, @@ -651,6 +661,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { }, { .name = "Esmart0-win0", .phys_id = ROCKCHIP_VOP2_ESMART0, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_esmart, .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, @@ -667,6 +678,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { }, { .name = "Esmart1-win0", .phys_id = ROCKCHIP_VOP2_ESMART1, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_esmart, .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, @@ -684,6 +696,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .name = "Esmart2-win0", .phys_id = ROCKCHIP_VOP2_ESMART2, .base = 0x1c00, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_esmart, .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, @@ -699,6 +712,7 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { }, { .name = "Esmart3-win0", .phys_id = ROCKCHIP_VOP2_ESMART3, + .possible_vp_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), .formats = formats_esmart, .nformats = ARRAY_SIZE(formats_esmart), .format_modifiers = format_modifiers, -- 2.51.0 From e7aae9f6d762139f8d2b86db03793ae0ab3dd802 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 3 Mar 2025 11:44:17 +0800 Subject: [PATCH 14/16] drm/rockchip: vop2: Add uv swap for cluster window The Cluster windows of upcoming VOP on rk3576 also support linear YUV support, we need to set uv swap bit for it. As the VOP2_WIN_UV_SWA register defined on rk3568/rk3588 is 0xffffffff, so this register will not be touched on these two platforms. Signed-off-by: Andy Yan Tested-by: Michael Riesch # on RK3568 Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250303034436.192400-4-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 1280c5f07557..338cfb69e1cf 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1377,10 +1377,8 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, rb_swap = vop2_win_rb_swap(fb->format->format); vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap); - if (!vop2_cluster_window(win)) { - uv_swap = vop2_win_uv_swap(fb->format->format); - vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); - } + uv_swap = vop2_win_uv_swap(fb->format->format); + vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); if (fb->format->is_yuv) { vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4)); -- 2.51.0 From 1803bfb59656d41c4f9f7e5f23188248b649e1a4 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 3 Mar 2025 11:44:18 +0800 Subject: [PATCH 15/16] dt-bindings: display: vop2: describe constraint SoC by SoC As more SoCs variants are introduced, each SoC brings its own unique set of constraints, describe this constraints SoC by SoC will make things easier. Signed-off-by: Andy Yan Reviewed-by: Krzysztof Kozlowski Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250303034436.192400-5-andyshrk@163.com --- .../display/rockchip/rockchip-vop2.yaml | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml index 46d956e63338..a5771edd83b5 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml @@ -14,6 +14,7 @@ description: maintainers: - Sandy Huang - Heiko Stuebner + - Andy Yan properties: compatible: @@ -124,43 +125,54 @@ allOf: properties: compatible: contains: - const: rockchip,rk3588-vop + enum: + - rockchip,rk3566-vop + - rockchip,rk3568-vop then: properties: clocks: - minItems: 7 + maxItems: 5 + clock-names: - minItems: 7 + maxItems: 5 ports: required: - port@0 - port@1 - port@2 - - port@3 - required: - - rockchip,grf - - rockchip,vo1-grf - - rockchip,vop-grf - - rockchip,pmu - - else: - properties: rockchip,vo1-grf: false rockchip,vop-grf: false rockchip,pmu: false + - if: + properties: + compatible: + contains: + const: rockchip,rk3588-vop + then: + properties: clocks: - maxItems: 5 + minItems: 7 + maxItems: 9 + clock-names: - maxItems: 5 + minItems: 7 + maxItems: 9 ports: required: - port@0 - port@1 - port@2 + - port@3 + + required: + - rockchip,grf + - rockchip,vo1-grf + - rockchip,vop-grf + - rockchip,pmu additionalProperties: false -- 2.51.0 From 47d31e6598e4fda1433e747ef031cb972c57c5ae Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 3 Mar 2025 11:44:19 +0800 Subject: [PATCH 16/16] dt-bindings: display: vop2: Add missing rockchip,grf property for rk3566/8 The clock polarity of RGB signal output is controlled by GRF, this property is already being used in the current device tree, but forgot to describe it as a required property in the binding file. Signed-off-by: Andy Yan Acked-by: Krzysztof Kozlowski Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20250303034436.192400-6-andyshrk@163.com --- .../devicetree/bindings/display/rockchip/rockchip-vop2.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml index a5771edd83b5..083eadcf0588 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml @@ -146,6 +146,9 @@ allOf: rockchip,vop-grf: false rockchip,pmu: false + required: + - rockchip,grf + - if: properties: compatible: @@ -200,6 +203,7 @@ examples: "dclk_vp1", "dclk_vp2"; power-domains = <&power RK3568_PD_VO>; + rockchip,grf = <&grf>; iommus = <&vop_mmu>; vop_out: ports { #address-cells = <1>; -- 2.51.0