diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 3823fdf118cbb..5657c90005ea0 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_rec Automatic panel recognition (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 7af0f3763fc40..3f167c7f24a1d 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"; diff --git a/drivers/gpu/drm/panel/panel-waveshare-dsi.c b/drivers/gpu/drm/panel/panel-waveshare-dsi.c index 02e1bd9fa15a2..636d19a12c6e1 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 */ }