From 47827d7dadb54ff36faa237e451e8aa482ee9c08 Mon Sep 17 00:00:00 2001 From: Engineer_Will <646689853@qq.com> Date: Thu, 13 Aug 2026 09:43:55 +0800 Subject: [PATCH 1/2] drm/panel: waveshare: Add automatic panel recognition support Add support for Waveshare automatic recognition DSI panels which identify themselves at runtime via I2C registers. The driver reads screen type, rotation and refresh rate from registers 0xd0-0xd3 to select the appropriate display mode and DSI lane configuration. Supported configurations include 1200x1920 and 1920x1200 panels at both 30fps (2 lanes) and 60fps (4 lanes) refresh rates. Signed-off-by: Engineer_Will <646689853@qq.com> --- drivers/gpu/drm/panel/panel-waveshare-dsi.c | 147 +++++++++++++++++++- 1 file changed, 141 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-waveshare-dsi.c b/drivers/gpu/drm/panel/panel-waveshare-dsi.c index 02e1bd9fa15a2e..636d19a12c6e16 100644 --- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c +++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c @@ -30,6 +30,7 @@ struct ws_panel { struct i2c_client *i2c; const struct drm_display_mode *mode; enum drm_panel_orientation orientation; + int lanes; }; struct ws_panel_data { @@ -345,6 +346,63 @@ static const struct ws_panel_data ws_panel_7_0_h_data = { .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, }; +/* 10.1inch DSI LCD (E) + * https://www.waveshare.com/10.1inch-dsi-lcd-e.htm + */ +static const struct drm_display_mode ws_panel_1200x1920_60fps_mode = { + .clock = 166666, + .hdisplay = 1200, + .hsync_start = 1200 + 28, + .hsync_end = 1200 + 28 + 10, + .htotal = 1200 + 28 + 10 + 30, + .vdisplay = 1920, + .vsync_start = 1920 + 238, + .vsync_end = 1920 + 238 + 4, + .vtotal = 1920 + 238 + 4 + 28, +}; + +static const struct drm_display_mode ws_panel_1200x1920_30fps_mode = { + .clock = 83333, + .hdisplay = 1200, + .hsync_start = 1200 + 28, + .hsync_end = 1200 + 28 + 10, + .htotal = 1200 + 28 + 10 + 30, + .vdisplay = 1920, + .vsync_start = 1920 + 238, + .vsync_end = 1920 + 238 + 4, + .vtotal = 1920 + 238 + 4 + 28, +}; + +static const struct drm_display_mode ws_panel_1920x1200_60fps_mode = { + .clock = 166666, + .hdisplay = 1920, + .hsync_start = 1920 + 238, + .hsync_end = 1920 + 238 + 4, + .htotal = 1920 + 238 + 4 + 28, + .vdisplay = 1200, + .vsync_start = 1200 + 28, + .vsync_end = 1200 + 28 + 10, + .vtotal = 1200 + 28 + 10 + 30, +}; + +static const struct drm_display_mode ws_panel_1920x1200_30fps_mode = { + .clock = 83333, + .hdisplay = 1920, + .hsync_start = 1920 + 238, + .hsync_end = 1920 + 238 + 4, + .htotal = 1920 + 238 + 4 + 28, + .vdisplay = 1200, + .vsync_start = 1200 + 28, + .vsync_end = 1200 + 28 + 10, + .vtotal = 1200 + 28 + 10 + 30, +}; + +static const struct ws_panel_data ws_panel_automatic_data = { + .mode = NULL, + .lanes = 0, + .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, +}; + static struct ws_panel *panel_to_ts(struct drm_panel *panel) { return container_of(panel, struct ws_panel, base); @@ -359,6 +417,68 @@ static void ws_panel_i2c_write(struct ws_panel *ts, u8 reg, u8 val) dev_err(&ts->i2c->dev, "I2C write failed: %d\n", ret); } +static int ws_panel_i2c_read(struct ws_panel *ts, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(ts->i2c, reg); + if (ret < 0) + dev_err(&ts->i2c->dev, "read reg 0x%02x failed %d\n", reg, ret); + return ret; +} + +static int ws_panel_auto_detect(struct ws_panel *ts) +{ + int screen_type; + int rotate; + int fps; + + fps = ws_panel_i2c_read(ts, 0xd0); + if (fps < 0) + return fps; + + rotate = ws_panel_i2c_read(ts, 0xd1); + if (rotate < 0) + return rotate; + + screen_type = ws_panel_i2c_read(ts, 0xd3); + if (screen_type < 0) + return screen_type; + + dev_info(&ts->i2c->dev, + "automatic panel detect screen_type=0x%x rotate=0x%x fps=0x%x\n", + screen_type, rotate, fps); + + if (screen_type == 0x01) { + switch (rotate) { + case 0: + case 2: + dev_info(&ts->i2c->dev, "select 1200x1920 panel\n"); + ts->mode = fps == 60 ? + &ws_panel_1200x1920_60fps_mode : + &ws_panel_1200x1920_30fps_mode; + break; + case 1: + case 3: + dev_info(&ts->i2c->dev, "select 1920x1200 panel\n"); + ts->mode = fps == 60 ? + &ws_panel_1920x1200_60fps_mode : + &ws_panel_1920x1200_30fps_mode; + break; + default: + dev_err(&ts->i2c->dev, "unknown rotate value %d\n", rotate); + return -EINVAL; + } + if (fps == 60) + ts->lanes = 4; + else + ts->lanes = 2; + return 0; + } + dev_err(&ts->i2c->dev, "unsupported panel D3=0x%x\n", screen_type); + return -EINVAL; +} + static int ws_panel_disable(struct drm_panel *panel) { struct ws_panel *ts = panel_to_ts(panel); @@ -492,13 +612,24 @@ static int ws_panel_probe(struct i2c_client *i2c) if (!_ws_panel_data) return -EINVAL; - ts->mode = _ws_panel_data->mode; - if (!ts->mode) - return -EINVAL; - + ts->i2c = i2c; i2c_set_clientdata(i2c, ts); + ts->lanes = _ws_panel_data->lanes; + /* + * automatic recognition panel + */ + if (of_device_is_compatible(dev->of_node, "waveshare,automatic-recognition-panel")) { + ret = ws_panel_auto_detect(ts); + if (ret) { + dev_err(dev, "panel auto detect failed\n"); + return ret; + } + } else { + ts->mode = _ws_panel_data->mode; + } - ts->i2c = i2c; + if (!ts->mode) + return -EINVAL; ws_panel_i2c_write(ts, 0xc0, 0x01); ws_panel_i2c_write(ts, 0xc2, 0x01); @@ -556,7 +687,7 @@ static int ws_panel_probe(struct i2c_client *i2c) ts->dsi->mode_flags = _ws_panel_data->mode_flags; ts->dsi->format = MIPI_DSI_FMT_RGB888; - ts->dsi->lanes = _ws_panel_data->lanes; + ts->dsi->lanes = ts->lanes; ret = devm_mipi_dsi_attach(dev, ts->dsi); @@ -583,6 +714,7 @@ static void ws_panel_shutdown(struct i2c_client *i2c) { struct ws_panel *ts = i2c_get_clientdata(i2c); + ws_panel_i2c_write(ts, 0xd2, 0x5a); ws_panel_disable(&ts->base); } @@ -635,6 +767,9 @@ static const struct of_device_id ws_panel_of_ids[] = { }, { .compatible = "waveshare,7.0inch-h-panel", .data = &ws_panel_7_0_h_data, + }, { + .compatible = "waveshare,automatic-recognition-panel", + .data = &ws_panel_automatic_data, }, { /* sentinel */ } From 765af0747b680ea91d2cd77e6076f1b399a110b4 Mon Sep 17 00:00:00 2001 From: Engineer_Will <646689853@qq.com> Date: Thu, 13 Aug 2026 09:44:00 +0800 Subject: [PATCH 2/2] ARM: dts: overlays: waveshare-dsi: Add auto_rec panel option Add the auto_rec overlay parameter to select the automatic recognition panel compatible string (waveshare,automatic-recognition-panel). This enables runtime panel detection via I2C for Waveshare DSI displays that support automatic identification. Use lowercase with underscores for the parameter name to follow kernel naming conventions. Signed-off-by: Engineer_Will <646689853@qq.com> --- arch/arm/boot/dts/overlays/README | 2 ++ .../boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 3823fdf118cbbc..e3048b6468bf7a 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -6063,6 +6063,8 @@ Params: 2_8_inch 2.8" 480x640 8_8_inch 8.8" 480x1920 7_0_inchC 7.0" C 1024x600 7_0_inchH 7.0" H 1280x720 + auto_rec Automatic panel recognition (1200x1920 or + 1920x1200 at 30fps/60fps) 7_9_inch 7.9" 400x1280 8_0_inch 8.0" 1280x800 10_1_inch 10.1" 1280x800 diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts index 7af0f3763fc404..3f167c7f24a1db 100644 --- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts @@ -136,6 +136,9 @@ 7_0_inchH = <&panel>, "compatible=waveshare,7.0inch-h-panel", <&touch>, "touchscreen-swapped-x-y?", <&touch>, "touchscreen-inverted-x?"; + auto_rec = <&panel>, "compatible=waveshare,automatic-recognition-panel", + <&touch>, "touchscreen-swapped-x-y?", + <&touch>, "touchscreen-inverted-x?"; i2c1 = <&i2c_frag>, "target:0=",<&i2c1>, <0>, "-3-4+5"; disable_touch = <&touch>, "status=disabled";