From 0c8d3b4a0342c5feb6887d18efd1273d80530674 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 16 Dec 2024 17:21:34 +0100 Subject: [PATCH 01/16] dt-bindings: display: simple: Add Tianma TM070JDHG34-00 panel Add the Tianma Micro-electronics TM070JDHG34-00 7.0" LVDS LCD TFT panel. Signed-off-by: Luca Ceresoli Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20241216-tianma_tm070jdhg34-v2-1-0b319a0bac39@bootlin.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241216-tianma_tm070jdhg34-v2-1-0b319a0bac39@bootlin.com --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index ee2314857aae..1b24773b7cac 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -282,6 +282,8 @@ properties: - team-source-display,tst043015cmhx # Tianma Micro-electronics TM070JDHG30 7.0" WXGA TFT LCD panel - tianma,tm070jdhg30 + # Tianma Micro-electronics TM070JDHG34-00 7.0" WXGA (1280x800) LVDS TFT LCD panel + - tianma,tm070jdhg34-00 # Tianma Micro-electronics TM070JVHG33 7.0" WXGA TFT LCD panel - tianma,tm070jvhg33 # Tianma Micro-electronics TM070RVHG71 7.0" WXGA TFT LCD panel -- 2.51.0 From bf6daaa281f7d11fcd91a68935142916afc51c53 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 16 Dec 2024 17:21:35 +0100 Subject: [PATCH 02/16] drm/panel: simple: Add Tianma TM070JDHG34-00 panel support Add Tianma TM070JDHG34-00 7.0" 1280x800 LVDS RGB TFT LCD panel. Panel info and datasheet: https://fortec.us/products/tm070jdhg34-00/ Reviewed-by: Neil Armstrong Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20241216-tianma_tm070jdhg34-v2-2-0b319a0bac39@bootlin.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241216-tianma_tm070jdhg34-v2-2-0b319a0bac39@bootlin.com --- drivers/gpu/drm/panel/panel-simple.c | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index d5bb850df9d2..a41f0d95fcb5 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4307,6 +4307,45 @@ static const struct panel_desc tianma_tm070jvhg33 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH, }; +/* + * The datasheet computes total blanking as back porch + front porch, not + * including sync pulse width. This is for both H and V. To make the total + * blanking and period correct, subtract the pulse width from the front + * porch. + * + * This works well for the Min and Typ values, but for Max values the sync + * pulse width is higher than back porch + front porch, so work around that + * by reducing the Max sync length value to 1 and then treating the Max + * porches as in the Min and Typ cases. + * + * Exact datasheet values are added as a comment where they differ from the + * ones implemented for the above reason. + */ +static const struct display_timing tianma_tm070jdhg34_00_timing = { + .pixelclock = { 68400000, 71900000, 78100000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 130, 138, 158 }, /* 131, 139, 159 */ + .hback_porch = { 5, 5, 5 }, + .hsync_len = { 1, 1, 1 }, /* 1, 1, 256 */ + .vactive = { 800, 800, 800 }, + .vfront_porch = { 2, 39, 98 }, /* 3, 40, 99 */ + .vback_porch = { 2, 2, 2 }, + .vsync_len = { 1, 1, 1 }, /* 1, 1, 128 */ + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc tianma_tm070jdhg34_00 = { + .timings = &tianma_tm070jdhg34_00_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 150, /* 149.76 */ + .height = 94, /* 93.60 */ + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct display_timing tianma_tm070rvhg71_timing = { .pixelclock = { 27700000, 29200000, 39600000 }, .hactive = { 800, 800, 800 }, @@ -5052,6 +5091,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "tianma,tm070jdhg30", .data = &tianma_tm070jdhg30, + }, { + .compatible = "tianma,tm070jdhg34-00", + .data = &tianma_tm070jdhg34_00, }, { .compatible = "tianma,tm070jvhg33", .data = &tianma_tm070jvhg33, -- 2.51.0 From 523092f6891d8652ab26331a9f35dc8329322896 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2024 13:26:28 +0100 Subject: [PATCH 03/16] dt-bindings: display: simple: Document Multi-Inno Technology MI1010Z1T-1CP11 panel Add Multi-Inno Technology MI1010Z1T-1CP11 10.1" 1024x600 LVDS panel compatible string. Signed-off-by: Marek Vasut Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241212122701.25305-1-marex@denx.de Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241212122701.25305-1-marex@denx.de --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 1b24773b7cac..7cdd69ba6db0 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -214,6 +214,8 @@ properties: - multi-inno,mi0800ft-9 # Multi-Inno Technology Co.,Ltd MI1010AIT-1CP 10.1" 1280x800 LVDS IPS Cap Touch Mod. - multi-inno,mi1010ait-1cp + # Multi-Inno Technology Co.,Ltd MI1010Z1T-1CP11 10.1" 1024x600 TFT Resistive Touch Module + - multi-inno,mi1010z1t-1cp11 # NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel - nec,nl12880bc20-05 # NEC LCD Technologies,Ltd. WQVGA TFT LCD panel -- 2.51.0 From 958473e7ed69bb397eed816b88be28986f7951ad Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2024 13:26:29 +0100 Subject: [PATCH 04/16] drm/panel: simple: add Multi-Inno Technology MI1010Z1T-1CP11 Add Multi-Inno Technology MI1010Z1T-1CP11 10.1" 1024x600 LVDS panel support. Signed-off-by: Marek Vasut Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241212122701.25305-2-marex@denx.de Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241212122701.25305-2-marex@denx.de --- drivers/gpu/drm/panel/panel-simple.c | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index a41f0d95fcb5..dbad12a354b6 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -3340,6 +3340,33 @@ static const struct panel_desc multi_inno_mi1010ait_1cp = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct display_timing multi_inno_mi1010z1t_1cp11_timing = { + .pixelclock = { 40800000, 51200000, 67200000 }, + .hactive = { 1024, 1024, 1024 }, + .hfront_porch = { 30, 110, 130 }, + .hback_porch = { 30, 110, 130 }, + .hsync_len = { 30, 100, 116 }, + .vactive = { 600, 600, 600 }, + .vfront_porch = { 4, 13, 80 }, + .vback_porch = { 4, 13, 80 }, + .vsync_len = { 2, 9, 40 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc multi_inno_mi1010z1t_1cp11 = { + .timings = &multi_inno_mi1010z1t_1cp11_timing, + .num_timings = 1, + .bpc = 6, + .size = { + .width = 260, + .height = 162, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct display_timing nec_nl12880bc20_05_timing = { .pixelclock = { 67000000, 71000000, 75000000 }, .hactive = { 1280, 1280, 1280 }, @@ -4983,6 +5010,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "multi-inno,mi1010ait-1cp", .data = &multi_inno_mi1010ait_1cp, + }, { + .compatible = "multi-inno,mi1010z1t-1cp11", + .data = &multi_inno_mi1010z1t_1cp11, }, { .compatible = "nec,nl12880bc20-05", .data = &nec_nl12880bc20_05, -- 2.51.0 From 221e29e197981e8e5882cdcf230d063598298e2f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 Dec 2024 09:58:57 +0100 Subject: [PATCH 05/16] accel/amdxdna: include linux/slab.h This driver fails to build in random configurations: drivers/accel/amdxdna/aie2_solver.c: In function 'remove_partition_node': drivers/accel/amdxdna/aie2_solver.c:121:9: error: implicit declaration of function 'kfree' [-Wimplicit-function-declaration] 121 | kfree(pt_node); | ^~~~~ drivers/accel/amdxdna/aie2_solver.c: In function 'get_free_partition': drivers/accel/amdxdna/aie2_solver.c:153:19: error: implicit declaration of function 'kzalloc' [-Wimplicit-function-declaration] 153 | pt_node = kzalloc(sizeof(*pt_node), GFP_KERNEL); Add the missing include. Fixes: c88d3325ae69 ("accel/amdxdna: Add hardware resource solver") Signed-off-by: Arnd Bergmann Reviewed-by: Mario Limonciello Signed-off-by: Mario Limonciello Link: https://patchwork.freedesktop.org/patch/msgid/20241218085902.2684002-1-arnd@kernel.org --- drivers/accel/amdxdna/aie2_solver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/aie2_solver.c b/drivers/accel/amdxdna/aie2_solver.c index 1939625d6027..2013d1f13aae 100644 --- a/drivers/accel/amdxdna/aie2_solver.c +++ b/drivers/accel/amdxdna/aie2_solver.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "aie2_solver.h" -- 2.51.0 From 7b256880fdb2d7f23393b87bb557090f049e686a Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Sat, 14 Dec 2024 16:17:02 +0800 Subject: [PATCH 06/16] drm/rockchip: vop2: Set AXI id for rk3588 There are two AXI bus in vop2, windows attached on the same bus must have a unique channel YUV and RGB channel ID. The default IDs will conflict with each other on the rk3588, so they need to be reassigned. Fixes: 5a028e8f062f ("drm/rockchip: vop2: Add support for rk3588") Signed-off-by: Andy Yan Tested-by: Derek Foreman Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20241214081719.3330518-4-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 14 +++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 9 +++++++ drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 26 +++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 68e32f100e7c..e60a7a2f26e6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1426,6 +1426,12 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, &fb->format->format, afbc_en ? "AFBC" : "", &yrgb_mst); + if (vop2->data->soc_id > 3568) { + 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); + } + if (vop2_cluster_window(win)) vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en); @@ -3353,6 +3359,10 @@ static struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = { [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8), [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9), [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11), + [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 3), + [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 8), + /* RK3588 only, reserved bit on rk3568*/ + [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13), /* Scale */ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15), @@ -3445,6 +3455,10 @@ static struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = { [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31), [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29), [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31), + [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8), + [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16), + /* RK3588 only, reserved register on rk3568 */ + [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1), /* Scale */ [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15), diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index 2cb7b6b40c77..f4724a402ac2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -78,6 +78,9 @@ enum vop2_win_regs { VOP2_WIN_COLOR_KEY, VOP2_WIN_COLOR_KEY_EN, VOP2_WIN_DITHER_UP, + VOP2_WIN_AXI_BUS_ID, + VOP2_WIN_AXI_YRGB_R_ID, + VOP2_WIN_AXI_UV_R_ID, /* scale regs */ VOP2_WIN_SCALE_YRGB_X, @@ -149,6 +152,10 @@ struct vop2_win_data { unsigned int layer_sel_id; uint64_t feature; + uint8_t axi_bus_id; + uint8_t axi_yrgb_r_id; + uint8_t axi_uv_r_id; + unsigned int max_upscale_factor; unsigned int max_downscale_factor; const u8 dly[VOP2_DLY_MODE_MAX]; @@ -319,6 +326,7 @@ enum dst_factor_mode { #define RK3568_CLUSTER_WIN_CTRL0 0x00 #define RK3568_CLUSTER_WIN_CTRL1 0x04 +#define RK3568_CLUSTER_WIN_CTRL2 0x08 #define RK3568_CLUSTER_WIN_YRGB_MST 0x10 #define RK3568_CLUSTER_WIN_CBR_MST 0x14 #define RK3568_CLUSTER_WIN_VIR 0x18 @@ -341,6 +349,7 @@ enum dst_factor_mode { /* (E)smart register definition, offset relative to window base */ #define RK3568_SMART_CTRL0 0x00 #define RK3568_SMART_CTRL1 0x04 +#define RK3588_SMART_AXI_CTRL 0x08 #define RK3568_SMART_REGION0_CTRL 0x10 #define RK3568_SMART_REGION0_YRGB_MST 0x14 #define RK3568_SMART_REGION0_CBR_MST 0x18 diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 9247f94343f2..65a88f489693 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -395,7 +395,7 @@ static const struct vop2_video_port_data rk3588_vop_video_ports[] = { * AXI1 is a read only bus. * * Every window on a AXI bus must assigned two unique - * read id(yrgb_id/uv_id, valid id are 0x1~0xe). + * read id(yrgb_r_id/uv_r_id, valid id are 0x1~0xe). * * AXI0: * Cluster0/1, Esmart0/1, WriteBack @@ -415,6 +415,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .layer_sel_id = 0, .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, + .axi_bus_id = 0, + .axi_yrgb_r_id = 2, + .axi_uv_r_id = 3, .max_upscale_factor = 4, .max_downscale_factor = 4, .dly = { 4, 26, 29 }, @@ -431,6 +434,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, + .axi_bus_id = 0, + .axi_yrgb_r_id = 6, + .axi_uv_r_id = 7, .max_upscale_factor = 4, .max_downscale_factor = 4, .dly = { 4, 26, 29 }, @@ -446,6 +452,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, + .axi_bus_id = 1, + .axi_yrgb_r_id = 2, + .axi_uv_r_id = 3, .max_upscale_factor = 4, .max_downscale_factor = 4, .dly = { 4, 26, 29 }, @@ -461,6 +470,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_PRIMARY, + .axi_bus_id = 1, + .axi_yrgb_r_id = 6, + .axi_uv_r_id = 7, .max_upscale_factor = 4, .max_downscale_factor = 4, .dly = { 4, 26, 29 }, @@ -475,6 +487,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .layer_sel_id = 2, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, + .axi_bus_id = 0, + .axi_yrgb_r_id = 0x0a, + .axi_uv_r_id = 0x0b, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 23, 45, 48 }, @@ -488,6 +503,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .layer_sel_id = 3, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, + .axi_bus_id = 0, + .axi_yrgb_r_id = 0x0c, + .axi_uv_r_id = 0x01, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 23, 45, 48 }, @@ -501,6 +519,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .layer_sel_id = 6, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, + .axi_bus_id = 1, + .axi_yrgb_r_id = 0x0a, + .axi_uv_r_id = 0x0b, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 23, 45, 48 }, @@ -514,6 +535,9 @@ static const struct vop2_win_data rk3588_vop_win_data[] = { .layer_sel_id = 7, .supported_rotations = DRM_MODE_REFLECT_Y, .type = DRM_PLANE_TYPE_OVERLAY, + .axi_bus_id = 1, + .axi_yrgb_r_id = 0x0c, + .axi_uv_r_id = 0x0d, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 23, 45, 48 }, -- 2.51.0 From c766998ba6df126ab6934d32ff2ff080316ec630 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Sat, 14 Dec 2024 16:17:03 +0800 Subject: [PATCH 07/16] drm/rockchip: vop2: Setup delay cycle for Esmart2/3 Each layer needs to set the correct delay cycle to display properly without unexpected offset on screen. Fixes: 5a028e8f062f ("drm/rockchip: vop2: Add support for rk3588") Signed-off-by: Andy Yan Tested-by: Derek Foreman Tested-by: Detlev Casanova Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20241214081719.3330518-5-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index e60a7a2f26e6..4e7b31eec36d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2704,9 +2704,11 @@ static void vop2_setup_dly_for_windows(struct vop2 *vop2) sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly); break; case ROCKCHIP_VOP2_SMART0: + case ROCKCHIP_VOP2_ESMART2: sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly); break; case ROCKCHIP_VOP2_SMART1: + case ROCKCHIP_VOP2_ESMART3: sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly); break; } -- 2.51.0 From df063c0b8ffbdca486ab2f802e716973985d8f86 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Sat, 14 Dec 2024 16:17:04 +0800 Subject: [PATCH 08/16] drm/rockchip: vop2: Check linear format for Cluster windows on rk3566/8 The Cluster windows on rk3566/8 only support afbc mode. Fixes: 604be85547ce ("drm/rockchip: Add VOP2 driver") Signed-off-by: Andy Yan Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20241214081719.3330518-6-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 4e7b31eec36d..a6281f1cd3fa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -593,6 +593,16 @@ 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_cluster_window(win)) { + if (modifier == DRM_FORMAT_MOD_LINEAR) { + drm_dbg_kms(vop2->drm, + "Cluster window only supports format with afbc\n"); + return false; + } + } + } + if (modifier == DRM_FORMAT_MOD_LINEAR) return true; -- 2.51.0 From 7e8a56c703c67bfa8d3f71a0c1c297bb1252b897 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Sat, 14 Dec 2024 16:17:05 +0800 Subject: [PATCH 09/16] drm/rockchip: vop2: Add check for 32 bpp format for rk3588 RK3588 only support DRM_FORMAT_XRGB2101010/XBGR2101010 in afbc mode. Fixes: 5a028e8f062f ("drm/rockchip: vop2: Add support for rk3588") Signed-off-by: Andy Yan Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20241214081719.3330518-7-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index a6281f1cd3fa..612a3b7f0482 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -603,6 +603,15 @@ 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 (!rockchip_afbc(plane, modifier)) { + drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); + return false; + } + } + } + if (modifier == DRM_FORMAT_MOD_LINEAR) return true; -- 2.51.0 From 77b1ccb2a27c7b3b118a03bf1730def92070d31b Mon Sep 17 00:00:00 2001 From: Min-Hua Chen Date: Sat, 14 Dec 2024 16:17:06 +0800 Subject: [PATCH 10/16] drm/rockchip: vop2: include rockchip_drm_drv.h Move rockchip_drm_drv.h in rockchip_drm_vop2.h to fix the follow sparse warning: ARCH=arm64 LLVM=1 make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' mrproper defconfig all -j12 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c:502:24: sparse: warning: symbol 'vop2_platform_driver' was not declared. Should it be static? It is also beneficial for the upcoming support for rk3576. Fixes: 604be85547ce ("drm/rockchip: Add VOP2 driver") Signed-off-by: Min-Hua Chen Signed-off-by: Andy Yan Reviewed-by: Min-Hua Chen Tested-by: Detlev Casanova Tested-by: Michael Riesch # on RK3568 Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20241214081719.3330518-8-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 1 - drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 612a3b7f0482..3ca6a688f5f5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -35,7 +35,6 @@ #include #include -#include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" #include "rockchip_drm_vop2.h" #include "rockchip_rgb.h" diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index f4724a402ac2..29cc7fb8f6d8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -9,6 +9,7 @@ #include #include +#include "rockchip_drm_drv.h" #include "rockchip_drm_vop.h" #define VOP2_VP_FEATURE_OUTPUT_10BIT BIT(0) -- 2.51.0 From 9367ab5d7ff75230534ee1d8d7e5a8365c36b014 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 23 Oct 2024 14:52:41 +0300 Subject: [PATCH 11/16] dt-bindings: display/xlnx/zynqmp-dpsub: Add audio DMAs The DP subsystem for ZynqMP supports audio via two channels, and the DP DMA has dma-engines for those channels. For some reason the DT binding has not specified those channels, even if the picture included in xlnx,zynqmp-dpsub.yaml shows "2 x aud" DMAs. This hasn't caused any issues as the drivers have not supported audio, and has thus gone unnoticed. To make it possible to add the audio support to the driver, add the two audio DMAs to the binding. While strictly speaking this is an ABI break, there should be no regressions caused by this as we're adding new entries at the end of the dmas list, and, after the audio support has been added in "arm64: dts: zynqmp: Add DMA for DP audio", the driver will treat the audio DMAs as optional to also support the old bindings. Reviewed-by: Rob Herring (Arm) Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241023-xilinx-dp-audio-v4-1-5128881457be@ideasonboard.com --- .../bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml index 554f9d5809d4..6b754d4f260e 100644 --- a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml +++ b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml @@ -100,12 +100,16 @@ properties: - description: Video layer, plane 1 (U/V or U) - description: Video layer, plane 2 (V) - description: Graphics layer + - description: Audio channel 0 + - description: Audio channel 1 dma-names: items: - const: vid0 - const: vid1 - const: vid2 - const: gfx0 + - const: aud0 + - const: aud1 phys: description: PHYs for the DP data lanes @@ -194,11 +198,13 @@ examples: power-domains = <&pd_dp>; resets = <&reset ZYNQMP_RESET_DP>; - dma-names = "vid0", "vid1", "vid2", "gfx0"; + dma-names = "vid0", "vid1", "vid2", "gfx0", "aud0", "aud1"; dmas = <&xlnx_dpdma 0>, <&xlnx_dpdma 1>, <&xlnx_dpdma 2>, - <&xlnx_dpdma 3>; + <&xlnx_dpdma 3>, + <&xlnx_dpdma 4>, + <&xlnx_dpdma 5>; phys = <&psgtr 1 PHY_TYPE_DP 0 3>, <&psgtr 0 PHY_TYPE_DP 1 3>; -- 2.51.0 From 0e0ab2462fd3a4c44f03617a9e107ef8c754e05e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 23 Oct 2024 14:52:42 +0300 Subject: [PATCH 12/16] arm64: dts: zynqmp: Add DMA for DP audio Add the two DMA channels used for the DisplayPort audio to the zynqmp_dpsub node. Acked-by: Michal Simek Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241023-xilinx-dp-audio-v4-2-5128881457be@ideasonboard.com --- arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi index 467f084c6469..e11d282462bd 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi @@ -1306,11 +1306,14 @@ "dp_vtc_pixel_clk_in"; power-domains = <&zynqmp_firmware PD_DP>; resets = <&zynqmp_reset ZYNQMP_RESET_DP>; - dma-names = "vid0", "vid1", "vid2", "gfx0"; + dma-names = "vid0", "vid1", "vid2", "gfx0", + "aud0", "aud1"; dmas = <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO0>, <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO1>, <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO2>, - <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>; + <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>, + <&zynqmp_dpdma ZYNQMP_DPDMA_AUDIO0>, + <&zynqmp_dpdma ZYNQMP_DPDMA_AUDIO1>; ports { #address-cells = <1>; -- 2.51.0 From 3ec5c15793051c9fe102ed0674c7925a56205385 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 23 Oct 2024 14:52:43 +0300 Subject: [PATCH 13/16] drm: xlnx: zynqmp_dpsub: Add DP audio support Add basic DisplayPort audio support. Support non-live audio playback from two PCMs (DMA channels), and the volume control in the audio mixer. As older dtb files may not have the audio DMA channels defined, the driver will just mark the audio support as disabled if the audio DMA is missing, and will continue with only display support. Note: Reset doesn't seem to work (ZYNQMP_DISP_AUD_SOFT_RESET). If we do a reset, audio playback won't start again even if, afaics, we do set up all the necessary registers. So, at the moment, resetting the audio block in dp_dai_hw_free() is commented out. Tested-by: Anatoliy Klymenko Reviewed-by: Vishal Sagar Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241023-xilinx-dp-audio-v4-3-5128881457be@ideasonboard.com --- drivers/gpu/drm/xlnx/Kconfig | 9 + drivers/gpu/drm/xlnx/Makefile | 1 + drivers/gpu/drm/xlnx/zynqmp_disp.c | 48 --- drivers/gpu/drm/xlnx/zynqmp_disp_regs.h | 7 +- drivers/gpu/drm/xlnx/zynqmp_dp.c | 54 ++- drivers/gpu/drm/xlnx/zynqmp_dp.h | 7 + drivers/gpu/drm/xlnx/zynqmp_dp_audio.c | 447 ++++++++++++++++++++++++ drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 39 +-- drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 15 +- 9 files changed, 526 insertions(+), 101 deletions(-) create mode 100644 drivers/gpu/drm/xlnx/zynqmp_dp_audio.c diff --git a/drivers/gpu/drm/xlnx/Kconfig b/drivers/gpu/drm/xlnx/Kconfig index 4197f44e202f..dbecca9bdd54 100644 --- a/drivers/gpu/drm/xlnx/Kconfig +++ b/drivers/gpu/drm/xlnx/Kconfig @@ -17,3 +17,12 @@ config DRM_ZYNQMP_DPSUB This is a DRM/KMS driver for ZynqMP DisplayPort controller. Choose this option if you have a Xilinx ZynqMP SoC with DisplayPort subsystem. + +config DRM_ZYNQMP_DPSUB_AUDIO + bool "ZynqMP DisplayPort Audio Support" + depends on DRM_ZYNQMP_DPSUB + depends on SND && SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Choose this option to enable DisplayPort audio support in the ZynqMP + DisplayPort driver. diff --git a/drivers/gpu/drm/xlnx/Makefile b/drivers/gpu/drm/xlnx/Makefile index ea1422a39502..ab6e2ffd7e8d 100644 --- a/drivers/gpu/drm/xlnx/Makefile +++ b/drivers/gpu/drm/xlnx/Makefile @@ -1,2 +1,3 @@ zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o zynqmp_kms.o +zynqmp-dpsub-$(CONFIG_DRM_ZYNQMP_DPSUB_AUDIO) += zynqmp_dp_audio.o obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index e4e0e299e8a7..80d1e499a18d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -143,7 +143,6 @@ struct zynqmp_disp_layer { * @dpsub: Display subsystem * @blend: Register I/O base address for the blender * @avbuf: Register I/O base address for the audio/video buffer manager - * @audio: Registers I/O base address for the audio mixer * @layers: Layers (planes) */ struct zynqmp_disp { @@ -152,7 +151,6 @@ struct zynqmp_disp { void __iomem *blend; void __iomem *avbuf; - void __iomem *audio; struct zynqmp_disp_layer layers[ZYNQMP_DPSUB_NUM_LAYERS]; }; @@ -865,42 +863,6 @@ static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp *disp, csc_zero_offsets); } -/* ----------------------------------------------------------------------------- - * Audio Mixer - */ - -static void zynqmp_disp_audio_write(struct zynqmp_disp *disp, int reg, u32 val) -{ - writel(val, disp->audio + reg); -} - -/** - * zynqmp_disp_audio_enable - Enable the audio mixer - * @disp: Display controller - * - * Enable the audio mixer by de-asserting the soft reset. The audio state is set to - * default values by the reset, set the default mixer volume explicitly. - */ -static void zynqmp_disp_audio_enable(struct zynqmp_disp *disp) -{ - /* Clear the audio soft reset register as it's an non-reset flop. */ - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 0); - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_MIXER_VOLUME, - ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); -} - -/** - * zynqmp_disp_audio_disable - Disable the audio mixer - * @disp: Display controller - * - * Disable the audio mixer by asserting its soft reset. - */ -static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) -{ - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, - ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); -} - /* ----------------------------------------------------------------------------- * ZynqMP Display Layer & DRM Plane */ @@ -1341,8 +1303,6 @@ void zynqmp_disp_enable(struct zynqmp_disp *disp) disp->dpsub->vid_clk_from_ps); zynqmp_disp_avbuf_enable_channels(disp); zynqmp_disp_avbuf_enable_audio(disp); - - zynqmp_disp_audio_enable(disp); } /** @@ -1351,8 +1311,6 @@ void zynqmp_disp_enable(struct zynqmp_disp *disp) */ void zynqmp_disp_disable(struct zynqmp_disp *disp) { - zynqmp_disp_audio_disable(disp); - zynqmp_disp_avbuf_disable_audio(disp); zynqmp_disp_avbuf_disable_channels(disp); zynqmp_disp_avbuf_disable(disp); @@ -1421,12 +1379,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub) goto error; } - disp->audio = devm_platform_ioremap_resource_byname(pdev, "aud"); - if (IS_ERR(disp->audio)) { - ret = PTR_ERR(disp->audio); - goto error; - } - ret = zynqmp_disp_create_layers(disp); if (ret) goto error; diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h b/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h index fa3935384834..9a4ff094e276 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h +++ b/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h @@ -177,12 +177,7 @@ #define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 #define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000 #define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 -#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 -#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc -#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 -#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 -#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 -#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c +#define ZYNQMP_DISP_AUD_CH_STATUS(x) (0x8 + ((x) * 4)) #define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 #define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 #define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28 diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 56a261a40ea3..0b63fd48ea92 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1342,7 +1342,6 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, { u8 lane_cnt = dp->mode.lane_cnt; u32 reg, wpl; - unsigned int rate; zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HTOTAL, mode->htotal); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VTOTAL, mode->vtotal); @@ -1367,18 +1366,8 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_M_VID, mode->clock); - rate = zynqmp_dpsub_get_audio_clk_rate(dp->dpsub); - if (rate) { - dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512); - zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, reg); - zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000); - } } - /* Only 2 channel audio is supported now */ - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, 1); - zynqmp_dp_write(dp, ZYNQMP_DP_USER_PIX_WIDTH, 1); /* Translate to the native 16 bit datapath based on IP core spec */ @@ -1387,6 +1376,44 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg); } +/* ----------------------------------------------------------------------------- + * Audio + */ + +void zynqmp_dp_audio_set_channels(struct zynqmp_dp *dp, + unsigned int num_channels) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, num_channels - 1); +} + +void zynqmp_dp_audio_enable(struct zynqmp_dp *dp) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); +} + +void zynqmp_dp_audio_disable(struct zynqmp_dp *dp) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); +} + +void zynqmp_dp_audio_write_n_m(struct zynqmp_dp *dp) +{ + unsigned int rate; + u32 link_rate; + + if (!(dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK)) + return; + + link_rate = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); + + rate = clk_get_rate(dp->dpsub->aud_clk); + + dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512); + + zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, link_rate); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000); +} + /* ----------------------------------------------------------------------------- * DISP Configuration */ @@ -1577,8 +1604,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, /* Enable the encoder */ dp->enabled = true; zynqmp_dp_update_misc(dp); - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0); if (dp->status == connector_status_connected) { for (i = 0; i < 3; i++) { @@ -1613,8 +1639,6 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); zynqmp_dp_disp_disable(dp, old_bridge_state); mutex_unlock(&dp->lock); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.h b/drivers/gpu/drm/xlnx/zynqmp_dp.h index f077d7fbd0ad..a3257793e23a 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.h @@ -22,4 +22,11 @@ void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp); int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub); void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub); +void zynqmp_dp_audio_set_channels(struct zynqmp_dp *dp, + unsigned int num_channels); +void zynqmp_dp_audio_enable(struct zynqmp_dp *dp); +void zynqmp_dp_audio_disable(struct zynqmp_dp *dp); + +void zynqmp_dp_audio_write_n_m(struct zynqmp_dp *dp); + #endif /* _ZYNQMP_DP_H_ */ diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp_audio.c b/drivers/gpu/drm/xlnx/zynqmp_dp_audio.c new file mode 100644 index 000000000000..fa5f0ace6084 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_dp_audio.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP DisplayPort Subsystem Driver - Audio support + * + * Copyright (C) 2015 - 2024 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon + * - Tomi Valkeinen + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "zynqmp_disp_regs.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" + +#define ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK 512 +#define ZYNQMP_NUM_PCMS 2 + +struct zynqmp_dpsub_audio { + void __iomem *base; + + struct snd_soc_card card; + + const char *dai_name; + const char *link_names[ZYNQMP_NUM_PCMS]; + const char *pcm_names[ZYNQMP_NUM_PCMS]; + + struct snd_soc_dai_driver dai_driver; + struct snd_dmaengine_pcm_config pcm_configs[2]; + + struct snd_soc_dai_link links[ZYNQMP_NUM_PCMS]; + + struct { + struct snd_soc_dai_link_component cpu; + struct snd_soc_dai_link_component codec; + struct snd_soc_dai_link_component platform; + } components[ZYNQMP_NUM_PCMS]; + + /* + * Protects: + * - enabled_streams + * - volumes + * - current_rate + */ + struct mutex enable_lock; + + u32 enabled_streams; + u32 current_rate; + + u16 volumes[2]; +}; + +static const struct snd_pcm_hardware zynqmp_dp_pcm_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 1024 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +static int zynqmp_dp_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + 256); + + return 0; +} + +static const struct snd_soc_ops zynqmp_dp_ops = { + .startup = zynqmp_dp_startup, +}; + +static void zynqmp_dp_audio_write(struct zynqmp_dpsub_audio *audio, int reg, + u32 val) +{ + writel(val, audio->base + reg); +} + +static int dp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct zynqmp_dpsub *dpsub = + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); + struct zynqmp_dpsub_audio *audio = dpsub->audio; + int ret; + u32 sample_rate; + struct snd_aes_iec958 iec = { 0 }; + unsigned long rate; + + sample_rate = params_rate(params); + + if (sample_rate != 48000 && sample_rate != 44100) + return -EINVAL; + + guard(mutex)(&audio->enable_lock); + + if (audio->enabled_streams && audio->current_rate != sample_rate) { + dev_err(dpsub->dev, + "Can't change rate while playback enabled\n"); + return -EINVAL; + } + + if (audio->enabled_streams > 0) { + /* Nothing to do */ + audio->enabled_streams++; + return 0; + } + + audio->current_rate = sample_rate; + + /* Note: clock rate can only be changed if the clock is disabled */ + ret = clk_set_rate(dpsub->aud_clk, + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK); + if (ret) { + dev_err(dpsub->dev, "can't set aud_clk to %u err:%d\n", + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK, ret); + return ret; + } + + clk_prepare_enable(dpsub->aud_clk); + + rate = clk_get_rate(dpsub->aud_clk); + + /* Ignore some offset +- 10 */ + if (abs(sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK - rate) > 10) { + dev_err(dpsub->dev, "aud_clk offset is higher: %ld\n", + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK - rate); + clk_disable_unprepare(dpsub->aud_clk); + return -EINVAL; + } + + pm_runtime_get_sync(dpsub->dev); + + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, + audio->volumes[0] | (audio->volumes[1] << 16)); + + /* Clear the audio soft reset register as it's an non-reset flop. */ + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, 0); + + /* Only 2 channel audio is supported now */ + zynqmp_dp_audio_set_channels(dpsub->dp, 2); + + zynqmp_dp_audio_write_n_m(dpsub->dp); + + /* Channel status */ + + if (sample_rate == 48000) + iec.status[3] = IEC958_AES3_CON_FS_48000; + else + iec.status[3] = IEC958_AES3_CON_FS_44100; + + for (unsigned int i = 0; i < AES_IEC958_STATUS_SIZE / 4; ++i) { + u32 v; + + v = (iec.status[(i * 4) + 0] << 0) | + (iec.status[(i * 4) + 1] << 8) | + (iec.status[(i * 4) + 2] << 16) | + (iec.status[(i * 4) + 3] << 24); + + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_CH_STATUS(i), v); + } + + zynqmp_dp_audio_enable(dpsub->dp); + + audio->enabled_streams++; + + return 0; +} + +static int dp_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct zynqmp_dpsub *dpsub = + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); + struct zynqmp_dpsub_audio *audio = dpsub->audio; + + guard(mutex)(&audio->enable_lock); + + /* Nothing to do */ + if (audio->enabled_streams > 1) { + audio->enabled_streams--; + return 0; + } + + pm_runtime_put(dpsub->dev); + + zynqmp_dp_audio_disable(dpsub->dp); + + /* + * Reset doesn't work. If we assert reset between audio stop and start, + * the audio won't start anymore. Probably we are missing writing + * some audio related registers. A/B buf? + */ + /* + zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); + */ + + clk_disable_unprepare(dpsub->aud_clk); + + audio->current_rate = 0; + audio->enabled_streams--; + + return 0; +} + +static const struct snd_soc_dai_ops zynqmp_dp_dai_ops = { + .hw_params = dp_dai_hw_params, + .hw_free = dp_dai_hw_free, +}; + +/* + * Min = 10 * log10(0x1 / 0x2000) = -39.13 + * Max = 10 * log10(0xffffff / 0x2000) = 9.03 + */ +static const DECLARE_TLV_DB_RANGE(zynqmp_dp_tlv, + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, -3913, 1), + 0x1, 0x2000, TLV_DB_LINEAR_ITEM(-3913, 0), + 0x2000, 0xffff, TLV_DB_LINEAR_ITEM(0, 903), +); + +static const struct snd_kcontrol_new zynqmp_dp_snd_controls[] = { + SOC_SINGLE_TLV("Input0 Playback Volume", 0, + 0, 0xffff, 0, zynqmp_dp_tlv), + SOC_SINGLE_TLV("Input1 Playback Volume", 1, + 0, 0xffff, 0, zynqmp_dp_tlv), +}; + +/* + * Note: these read & write functions only support two "registers", 0 and 1, + * for volume 0 and 1. In other words, these are not real register read/write + * functions. + * + * This is done to support caching the volume value for the case where the + * hardware is not enabled, and also to support locking as volumes 0 and 1 + * are in the same register. + */ +static unsigned int zynqmp_dp_dai_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(component->dev); + struct zynqmp_dpsub_audio *audio = dpsub->audio; + + return audio->volumes[reg]; +} + +static int zynqmp_dp_dai_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(component->dev); + struct zynqmp_dpsub_audio *audio = dpsub->audio; + + guard(mutex)(&audio->enable_lock); + + audio->volumes[reg] = val; + + if (audio->enabled_streams) + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, + audio->volumes[0] | + (audio->volumes[1] << 16)); + + return 0; +} + +static const struct snd_soc_component_driver zynqmp_dp_component_driver = { + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .controls = zynqmp_dp_snd_controls, + .num_controls = ARRAY_SIZE(zynqmp_dp_snd_controls), + .read = zynqmp_dp_dai_read, + .write = zynqmp_dp_dai_write, +}; + +int zynqmp_audio_init(struct zynqmp_dpsub *dpsub) +{ + struct platform_device *pdev = to_platform_device(dpsub->dev); + struct device *dev = dpsub->dev; + struct zynqmp_dpsub_audio *audio; + struct snd_soc_card *card; + void *dev_data; + int ret; + + if (!dpsub->aud_clk) + return 0; + + audio = devm_kzalloc(dev, sizeof(*audio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + dpsub->audio = audio; + + mutex_init(&audio->enable_lock); + + /* 0x2000 is the zero level, no change */ + audio->volumes[0] = 0x2000; + audio->volumes[1] = 0x2000; + + audio->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "%s-dai", dev_name(dev)); + + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { + audio->link_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "%s-dp-%u", dev_name(dev), i); + audio->pcm_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "%s-pcm-%u", dev_name(dev), i); + } + + audio->base = devm_platform_ioremap_resource_byname(pdev, "aud"); + if (IS_ERR(audio->base)) + return PTR_ERR(audio->base); + + /* Create CPU DAI */ + + audio->dai_driver = (struct snd_soc_dai_driver) { + .name = audio->dai_name, + .ops = &zynqmp_dp_dai_ops, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }; + + ret = devm_snd_soc_register_component(dev, &zynqmp_dp_component_driver, + &audio->dai_driver, 1); + if (ret) { + dev_err(dev, "Failed to register CPU DAI\n"); + return ret; + } + + /* Create PCMs */ + + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { + struct snd_dmaengine_pcm_config *pcm_config = + &audio->pcm_configs[i]; + + *pcm_config = (struct snd_dmaengine_pcm_config){ + .name = audio->pcm_names[i], + .pcm_hardware = &zynqmp_dp_pcm_hw, + .prealloc_buffer_size = 64 * 1024, + .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = + i == 0 ? "aud0" : "aud1", + }; + + ret = devm_snd_dmaengine_pcm_register(dev, pcm_config, 0); + if (ret) { + dev_err(dev, "Failed to register PCM %u\n", i); + return ret; + } + } + + /* Create card */ + + card = &audio->card; + card->name = "DisplayPort"; + card->long_name = "DisplayPort Monitor"; + card->driver_name = "zynqmp_dpsub"; + card->dev = dev; + card->owner = THIS_MODULE; + card->num_links = ZYNQMP_NUM_PCMS; + card->dai_link = audio->links; + + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { + struct snd_soc_dai_link *link = &card->dai_link[i]; + + link->ops = &zynqmp_dp_ops; + + link->name = audio->link_names[i]; + link->stream_name = audio->link_names[i]; + + link->cpus = &audio->components[i].cpu; + link->num_cpus = 1; + link->cpus[0].dai_name = audio->dai_name; + + link->codecs = &audio->components[i].codec; + link->num_codecs = 1; + link->codecs[0].name = "snd-soc-dummy"; + link->codecs[0].dai_name = "snd-soc-dummy-dai"; + + link->platforms = &audio->components[i].platform; + link->num_platforms = 1; + link->platforms[0].name = audio->pcm_names[i]; + } + + /* + * HACK: devm_snd_soc_register_card() overwrites current drvdata + * so we need to hack it back. + */ + dev_data = dev_get_drvdata(dev); + ret = devm_snd_soc_register_card(dev, card); + dev_set_drvdata(dev, dev_data); + if (ret) { + /* + * As older dtbs may not have the audio channel dmas defined, + * instead of returning an error here we'll continue and just + * mark the audio as disabled. + */ + dev_err(dev, "Failed to register sound card, disabling audio support\n"); + + devm_kfree(dev, audio); + dpsub->audio = NULL; + + return 0; + } + + return 0; +} + +void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub) +{ + struct zynqmp_dpsub_audio *audio = dpsub->audio; + + if (!audio) + return; + + if (!dpsub->aud_clk) + return; + + mutex_destroy(&audio->enable_lock); +} diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 07c4d184e7a1..f953ca48a930 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -56,36 +56,6 @@ static const struct dev_pm_ops zynqmp_dpsub_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume) }; -/* ----------------------------------------------------------------------------- - * DPSUB Configuration - */ - -/** - * zynqmp_dpsub_audio_enabled - If the audio is enabled - * @dpsub: DisplayPort subsystem - * - * Return if the audio is enabled depending on the audio clock. - * - * Return: true if audio is enabled, or false. - */ -bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub) -{ - return !!dpsub->aud_clk; -} - -/** - * zynqmp_dpsub_get_audio_clk_rate - Get the current audio clock rate - * @dpsub: DisplayPort subsystem - * - * Return: the current audio clock rate. - */ -unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub) -{ - if (zynqmp_dpsub_audio_enabled(dpsub)) - return 0; - return clk_get_rate(dpsub->aud_clk); -} - /* ----------------------------------------------------------------------------- * Probe & Remove */ @@ -264,10 +234,17 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev) goto err_disp; } + ret = zynqmp_audio_init(dpsub); + if (ret) + goto err_drm_cleanup; + dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed"); return 0; +err_drm_cleanup: + if (dpsub->drm) + zynqmp_dpsub_drm_cleanup(dpsub); err_disp: drm_bridge_remove(dpsub->bridge); zynqmp_disp_remove(dpsub); @@ -287,6 +264,8 @@ static void zynqmp_dpsub_remove(struct platform_device *pdev) { struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); + zynqmp_audio_uninit(dpsub); + if (dpsub->drm) zynqmp_dpsub_drm_cleanup(dpsub); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h index b18554467e9c..49875529c2a4 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -12,6 +12,8 @@ #ifndef _ZYNQMP_DPSUB_H_ #define _ZYNQMP_DPSUB_H_ +#include + struct clk; struct device; struct drm_bridge; @@ -39,6 +41,8 @@ enum zynqmp_dpsub_format { ZYNQMP_DPSUB_FORMAT_YONLY, }; +struct zynqmp_dpsub_audio; + /** * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem * @dev: The physical device @@ -77,10 +81,17 @@ struct zynqmp_dpsub { struct zynqmp_dp *dp; unsigned int dma_align; + + struct zynqmp_dpsub_audio *audio; }; -bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub); -unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub); +#ifdef CONFIG_DRM_ZYNQMP_DPSUB_AUDIO +int zynqmp_audio_init(struct zynqmp_dpsub *dpsub); +void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub); +#else +static inline int zynqmp_audio_init(struct zynqmp_dpsub *dpsub) { return 0; } +static inline void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub) { } +#endif void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub); -- 2.51.0 From 63f4e7dfef8c1162e22cd25c9a23b125ba40dfc4 Mon Sep 17 00:00:00 2001 From: Andrej Picej Date: Mon, 16 Dec 2024 09:54:08 +0100 Subject: [PATCH 14/16] dt-bindings: drm/bridge: ti-sn65dsi83: Add properties for ti,lvds-vod-swing Add properties which can be used to specify LVDS differential output voltage. Since this also depends on near-end signal termination also include property which sets this. LVDS differential output voltage is specified with an array (min, max), which should match the one from connected device. Signed-off-by: Andrej Picej Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241216085410.1968634-2-andrej.picej@norik.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241216085410.1968634-2-andrej.picej@norik.com --- .../bindings/display/bridge/ti,sn65dsi83.yaml | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml index 48a97bb3e2e0..bad6f5c81b06 100644 --- a/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml @@ -80,12 +80,12 @@ properties: - const: 4 port@2: - $ref: /schemas/graph.yaml#/properties/port description: Video port for LVDS Channel-A output (panel or bridge). + $ref: '#/$defs/lvds-port' port@3: - $ref: /schemas/graph.yaml#/properties/port description: Video port for LVDS Channel-B output (panel or bridge). + $ref: '#/$defs/lvds-port' required: - port@0 @@ -96,6 +96,36 @@ required: - reg - ports +$defs: + lvds-port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + ti,lvds-termination-ohms: + description: The value of near end differential termination in ohms. + enum: [100, 200] + default: 200 + + ti,lvds-vod-swing-clock-microvolt: + description: LVDS diferential output voltage for clock + lanes in microvolts. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + + ti,lvds-vod-swing-data-microvolt: + description: LVDS diferential output voltage for data + lanes in microvolts. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + allOf: - if: properties: -- 2.51.0 From d2b8c6d5495706eee2347483ea89b5c13f256ff2 Mon Sep 17 00:00:00 2001 From: Andrej Picej Date: Mon, 16 Dec 2024 09:54:09 +0100 Subject: [PATCH 15/16] drm/bridge: ti-sn65dsi83: Add ti,lvds-vod-swing optional properties Add a optional properties to change LVDS output voltage. This should not be static as this depends mainly on the connected display voltage requirement. We have three properties: - "ti,lvds-termination-ohms", which sets near end termination, - "ti,lvds-vod-swing-data-microvolt" and - "ti,lvds-vod-swing-clock-microvolt" which both set LVDS differential output voltage for data and clock lanes. They are defined as an array with min and max values. The appropriate bitfield will be set if selected constraints can be met. If "ti,lvds-termination-ohms" is not defined the default of 200 Ohm near end termination will be used. Selecting only one: "ti,lvds-vod-swing-data-microvolt" or "ti,lvds-vod-swing-clock-microvolt" can be done, but the output voltage constraint for only data/clock lanes will be met. Setting both is recommended. Signed-off-by: Andrej Picej Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20241216085410.1968634-3-andrej.picej@norik.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241216085410.1968634-3-andrej.picej@norik.com --- drivers/gpu/drm/bridge/ti-sn65dsi83.c | 145 +++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index e4a8e6472341..336380114eea 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -132,6 +132,16 @@ #define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2) #define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0) +enum sn65dsi83_channel { + CHANNEL_A, + CHANNEL_B +}; + +enum sn65dsi83_lvds_term { + OHM_100, + OHM_200 +}; + enum sn65dsi83_model { MODEL_SN65DSI83, MODEL_SN65DSI84, @@ -147,6 +157,8 @@ struct sn65dsi83 { struct regulator *vcc; bool lvds_dual_link; bool lvds_dual_link_even_odd_swap; + int lvds_vod_swing_conf[2]; + int lvds_term_conf[2]; }; static const struct regmap_range sn65dsi83_readable_ranges[] = { @@ -237,6 +249,36 @@ static const struct regmap_config sn65dsi83_regmap_config = { .max_register = REG_IRQ_STAT, }; +static const int lvds_vod_swing_data_table[2][4][2] = { + { /* 100 Ohm */ + { 180000, 313000 }, + { 215000, 372000 }, + { 250000, 430000 }, + { 290000, 488000 }, + }, + { /* 200 Ohm */ + { 150000, 261000 }, + { 200000, 346000 }, + { 250000, 428000 }, + { 300000, 511000 }, + }, +}; + +static const int lvds_vod_swing_clock_table[2][4][2] = { + { /* 100 Ohm */ + { 140000, 244000 }, + { 168000, 290000 }, + { 195000, 335000 }, + { 226000, 381000 }, + }, + { /* 200 Ohm */ + { 117000, 204000 }, + { 156000, 270000 }, + { 195000, 334000 }, + { 234000, 399000 }, + }, +}; + static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge) { return container_of(bridge, struct sn65dsi83, bridge); @@ -435,12 +477,16 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, val |= REG_LVDS_FMT_LVDS_LINK_CFG; regmap_write(ctx->regmap, REG_LVDS_FMT, val); - regmap_write(ctx->regmap, REG_LVDS_VCOM, 0x05); + regmap_write(ctx->regmap, REG_LVDS_VCOM, + REG_LVDS_VCOM_CHA_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_A]) | + REG_LVDS_VCOM_CHB_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_B])); regmap_write(ctx->regmap, REG_LVDS_LANE, (ctx->lvds_dual_link_even_odd_swap ? REG_LVDS_LANE_EVEN_ODD_SWAP : 0) | - REG_LVDS_LANE_CHA_LVDS_TERM | - REG_LVDS_LANE_CHB_LVDS_TERM); + (ctx->lvds_term_conf[CHANNEL_A] ? + REG_LVDS_LANE_CHA_LVDS_TERM : 0) | + (ctx->lvds_term_conf[CHANNEL_B] ? + REG_LVDS_LANE_CHB_LVDS_TERM : 0)); regmap_write(ctx->regmap, REG_LVDS_CM, 0x00); le16val = cpu_to_le16(mode->hdisplay); @@ -576,10 +622,103 @@ static const struct drm_bridge_funcs sn65dsi83_funcs = { .atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts, }; +static int sn65dsi83_select_lvds_vod_swing(struct device *dev, + u32 lvds_vod_swing_data[2], u32 lvds_vod_swing_clk[2], u8 lvds_term) +{ + int i; + + for (i = 0; i <= 3; i++) { + if (lvds_vod_swing_data_table[lvds_term][i][0] >= lvds_vod_swing_data[0] && + lvds_vod_swing_data_table[lvds_term][i][1] <= lvds_vod_swing_data[1] && + lvds_vod_swing_clock_table[lvds_term][i][0] >= lvds_vod_swing_clk[0] && + lvds_vod_swing_clock_table[lvds_term][i][1] <= lvds_vod_swing_clk[1]) + return i; + } + + dev_err(dev, "failed to find appropriate LVDS_VOD_SWING configuration\n"); + return -EINVAL; +} + +static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel) +{ + struct device *dev = ctx->dev; + struct device_node *endpoint; + int endpoint_reg; + /* Set so the property can be freely selected if not defined */ + u32 lvds_vod_swing_data[2] = { 0, 1000000 }; + u32 lvds_vod_swing_clk[2] = { 0, 1000000 }; + /* Set default near end terminataion to 200 Ohm */ + u32 lvds_term = 200; + int lvds_vod_swing_conf; + int ret = 0; + int ret_data; + int ret_clock; + + if (channel == CHANNEL_A) + endpoint_reg = 2; + else + endpoint_reg = 3; + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1); + + of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term); + if (lvds_term == 100) + ctx->lvds_term_conf[channel] = OHM_100; + else if (lvds_term == 200) + ctx->lvds_term_conf[channel] = OHM_200; + else { + ret = -EINVAL; + goto exit; + } + + ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt", + lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data)); + if (ret_data != 0 && ret_data != -EINVAL) { + ret = ret_data; + goto exit; + } + + ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt", + lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk)); + if (ret_clock != 0 && ret_clock != -EINVAL) { + ret = ret_clock; + goto exit; + } + + /* Use default value if both properties are NOT defined. */ + if (ret_data == -EINVAL && ret_clock == -EINVAL) + lvds_vod_swing_conf = 0x1; + + /* Use lookup table if any of the two properties is defined. */ + if (!ret_data || !ret_clock) { + lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data, + lvds_vod_swing_clk, ctx->lvds_term_conf[channel]); + if (lvds_vod_swing_conf < 0) { + ret = lvds_vod_swing_conf; + goto exit; + } + } + + ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf; + ret = 0; +exit: + of_node_put(endpoint); + return ret; +} + static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) { struct drm_bridge *panel_bridge; struct device *dev = ctx->dev; + int ret; + + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_A); + if (ret < 0) + return ret; + + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_B); + if (ret < 0) + return ret; ctx->lvds_dual_link = false; ctx->lvds_dual_link_even_odd_swap = false; -- 2.51.0 From 1e93f594285faef57651a0c89f61a7d976db7def Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 16 Dec 2024 18:10:03 +0800 Subject: [PATCH 16/16] drm/bridge: synopsys: Fix Copyright Writing Style of dw-hdmi-qp The standard writing style should be: Co., Ltd. This fix the mail server warning: DBL_SPAM(6.50)[co.ltd:url]; Signed-off-by: Andy Yan Reviewed-by: Cristian Ciocaltea Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241216101015.3726517-1-andyshrk@163.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241216101015.3726517-1-andyshrk@163.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h index 2115b8ef0bd6..72987e6c4689 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: * Algea Cao */ -- 2.51.0