From c57e67b0c620d5ce7d17c503697e374ccf06fdeb Mon Sep 17 00:00:00 2001 From: Goodwill <646689853@qq.com> Date: Thu, 27 Aug 2026 01:24:36 +0800 Subject: [PATCH 1/2] drm/panel: waveshare: Add automatic panel detection support Add support for Waveshare automatic detection 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: Goodwill <646689853@qq.com> --- drivers/gpu/drm/panel/panel-waveshare-dsi.c | 154 +++++++++++++++++++- 1 file changed, 148 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..6ef696f0aa6320 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,23 @@ 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; + + /* For automatic detection panels, mode is NULL - detect via I2C */ + if (!_ws_panel_data->mode) { + 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 +686,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); @@ -570,10 +700,18 @@ static int ws_panel_probe(struct i2c_client *i2c) return -ENODEV; } +static void ws_panel_reset(struct drm_panel *panel) +{ + struct ws_panel *ts = panel_to_ts(panel); + + ws_panel_i2c_write(ts, 0xd2, 0x5a); +} + static void ws_panel_remove(struct i2c_client *i2c) { struct ws_panel *ts = i2c_get_clientdata(i2c); + ws_panel_reset(&ts->base); ws_panel_disable(&ts->base); drm_panel_remove(&ts->base); @@ -583,6 +721,7 @@ static void ws_panel_shutdown(struct i2c_client *i2c) { struct ws_panel *ts = i2c_get_clientdata(i2c); + ws_panel_reset(&ts->base); ws_panel_disable(&ts->base); } @@ -635,6 +774,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-detection-panel", + .data = &ws_panel_automatic_data, }, { /* sentinel */ } From d6d4ebeb0d730e7dc5316a79e901fe3495ad8726 Mon Sep 17 00:00:00 2001 From: Goodwill <646689853@qq.com> Date: Thu, 27 Aug 2026 01:24:45 +0800 Subject: [PATCH 2/2] ARM: dts: overlays: waveshare-dsi: Add auto_detect panel option Add the auto_detect overlay parameter to select the automatic detection panel compatible string (waveshare,automatic-detection-panel). This enables runtime panel detection via I2C for Waveshare DSI displays that support automatic identification. Signed-off-by: Goodwill <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..78733b78fc36ba 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -6069,6 +6069,8 @@ Params: 2_8_inch 2.8" 480x640 11_9_inch 11.9" 320x1480 13_3_inch_4lane 13.3" 1920x1080 4lane 13_3_inch_2lane 13.3" 1920x1080 2lane + auto_detect Automatic panel detection (1200x1920 or + 1920x1200 at 30fps/60fps) i2c1 Use i2c-1 with jumper wires from GPIOs 2&3 disable_touch Disable the touch controller rotation Set the panel orientation property 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..d41c9dfe810f4e 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_detect = <&panel>, "compatible=waveshare,automatic-detection-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";