Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/panel: add BOE tv101wum-ll2 panel driver

Add support for the 1200x1920 BOE TV101WUM-LL2 DSI Display Panel found
in the Lenovo Smart Tab M10 tablet. The controller is unknown.

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20240828-topic-sdm450-upstream-tbx605f-panel-v3-2-b792f93e1d6b@linaro.org
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240828-topic-sdm450-upstream-tbx605f-panel-v3-2-b792f93e1d6b@linaro.org

+251
+9
drivers/gpu/drm/panel/Kconfig
··· 87 87 Say Y here if you want to support for BOE TV101WUM and AUO KD101N80 88 88 45NA WUXGA PANEL DSI Video Mode panel 89 89 90 + config DRM_PANEL_BOE_TV101WUM_LL2 91 + tristate "BOE TV101WUM LL2 1200x1920 panel" 92 + depends on OF 93 + depends on DRM_MIPI_DSI 94 + depends on BACKLIGHT_CLASS_DEVICE 95 + help 96 + Say Y here if you want to support for BOE TV101WUM-LL2 97 + WUXGA PANEL DSI Video Mode panel 98 + 90 99 config DRM_PANEL_EBBG_FT8719 91 100 tristate "EBBG FT8719 panel driver" 92 101 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 6 6 obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o 7 7 obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o 8 8 obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o 9 + obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o 9 10 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o 10 11 obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o 11 12 obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
+241
drivers/gpu/drm/panel/panel-boe-tv101wum-ll2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: 3 + // Copyright (c) 2013, The Linux Foundation. All rights reserved. 4 + // Copyright (c) 2024, Neil Armstrong <neil.armstrong@linaro.org> 5 + 6 + #include <linux/delay.h> 7 + #include <linux/gpio/consumer.h> 8 + #include <linux/module.h> 9 + #include <linux/of.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <drm/drm_mipi_dsi.h> 13 + #include <drm/drm_modes.h> 14 + #include <drm/drm_panel.h> 15 + #include <drm/drm_probe_helper.h> 16 + 17 + struct boe_tv101wum_ll2 { 18 + struct drm_panel panel; 19 + struct mipi_dsi_device *dsi; 20 + struct gpio_desc *reset_gpio; 21 + struct regulator_bulk_data *supplies; 22 + }; 23 + 24 + static const struct regulator_bulk_data boe_tv101wum_ll2_supplies[] = { 25 + { .supply = "vsp" }, 26 + { .supply = "vsn" }, 27 + }; 28 + 29 + static inline struct boe_tv101wum_ll2 *to_boe_tv101wum_ll2(struct drm_panel *panel) 30 + { 31 + return container_of(panel, struct boe_tv101wum_ll2, panel); 32 + } 33 + 34 + static void boe_tv101wum_ll2_reset(struct boe_tv101wum_ll2 *ctx) 35 + { 36 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 37 + usleep_range(5000, 6000); 38 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 39 + usleep_range(5000, 6000); 40 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 41 + 42 + msleep(120); 43 + } 44 + 45 + static int boe_tv101wum_ll2_on(struct boe_tv101wum_ll2 *ctx) 46 + { 47 + struct mipi_dsi_device *dsi = ctx->dsi; 48 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 49 + 50 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 51 + 52 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 53 + 54 + mipi_dsi_msleep(&dsi_ctx, 120); 55 + 56 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0e); 57 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xff, 0x81, 0x68, 0x6c, 0x22, 58 + 0x6d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00); 59 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x23); 60 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x00, 0x00); 61 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x94, 0x2c, 0x00); 62 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x19); 63 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa2, 0x38); 64 + 65 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x5a, 0x0c); 66 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x80, 0xfd); 67 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x50, 0x00); 68 + 69 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 70 + 71 + mipi_dsi_msleep(&dsi_ctx, 20); 72 + 73 + return dsi_ctx.accum_err; 74 + } 75 + 76 + static void boe_tv101wum_ll2_off(struct boe_tv101wum_ll2 *ctx) 77 + { 78 + struct mipi_dsi_device *dsi = ctx->dsi; 79 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 80 + 81 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 82 + 83 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 84 + 85 + mipi_dsi_msleep(&dsi_ctx, 70); 86 + 87 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 88 + 89 + mipi_dsi_msleep(&dsi_ctx, 20); 90 + 91 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5a); 92 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5a); 93 + 94 + mipi_dsi_msleep(&dsi_ctx, 150); 95 + } 96 + 97 + static int boe_tv101wum_ll2_prepare(struct drm_panel *panel) 98 + { 99 + struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel); 100 + int ret; 101 + 102 + ret = regulator_bulk_enable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), 103 + ctx->supplies); 104 + if (ret < 0) 105 + return ret; 106 + 107 + boe_tv101wum_ll2_reset(ctx); 108 + 109 + ret = boe_tv101wum_ll2_on(ctx); 110 + if (ret < 0) { 111 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 112 + regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), 113 + ctx->supplies); 114 + return ret; 115 + } 116 + 117 + return 0; 118 + } 119 + 120 + static int boe_tv101wum_ll2_unprepare(struct drm_panel *panel) 121 + { 122 + struct boe_tv101wum_ll2 *ctx = to_boe_tv101wum_ll2(panel); 123 + 124 + /* Ignore errors on failure, in any case set gpio and disable regulators */ 125 + boe_tv101wum_ll2_off(ctx); 126 + 127 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 128 + 129 + regulator_bulk_disable(ARRAY_SIZE(boe_tv101wum_ll2_supplies), 130 + ctx->supplies); 131 + 132 + return 0; 133 + } 134 + 135 + static const struct drm_display_mode boe_tv101wum_ll2_mode = { 136 + .clock = (1200 + 27 + 8 + 12) * (1920 + 155 + 8 + 32) * 60 / 1000, 137 + .hdisplay = 1200, 138 + .hsync_start = 1200 + 27, 139 + .hsync_end = 1200 + 27 + 8, 140 + .htotal = 1200 + 27 + 8 + 12, 141 + .vdisplay = 1920, 142 + .vsync_start = 1920 + 155, 143 + .vsync_end = 1920 + 155 + 8, 144 + .vtotal = 1920 + 155 + 8 + 32, 145 + .width_mm = 136, 146 + .height_mm = 217, 147 + .type = DRM_MODE_TYPE_DRIVER, 148 + }; 149 + 150 + static int boe_tv101wum_ll2_get_modes(struct drm_panel *panel, 151 + struct drm_connector *connector) 152 + { 153 + /* We do not set display_info.bpc since unset value is bpc=8 by default */ 154 + return drm_connector_helper_get_modes_fixed(connector, &boe_tv101wum_ll2_mode); 155 + } 156 + 157 + static const struct drm_panel_funcs boe_tv101wum_ll2_panel_funcs = { 158 + .prepare = boe_tv101wum_ll2_prepare, 159 + .unprepare = boe_tv101wum_ll2_unprepare, 160 + .get_modes = boe_tv101wum_ll2_get_modes, 161 + }; 162 + 163 + static int boe_tv101wum_ll2_probe(struct mipi_dsi_device *dsi) 164 + { 165 + struct device *dev = &dsi->dev; 166 + struct boe_tv101wum_ll2 *ctx; 167 + int ret; 168 + 169 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 170 + if (!ctx) 171 + return -ENOMEM; 172 + 173 + ret = devm_regulator_bulk_get_const(&dsi->dev, 174 + ARRAY_SIZE(boe_tv101wum_ll2_supplies), 175 + boe_tv101wum_ll2_supplies, 176 + &ctx->supplies); 177 + if (ret < 0) 178 + return ret; 179 + 180 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 181 + if (IS_ERR(ctx->reset_gpio)) 182 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 183 + "Failed to get reset-gpios\n"); 184 + 185 + ctx->dsi = dsi; 186 + mipi_dsi_set_drvdata(dsi, ctx); 187 + 188 + dsi->lanes = 4; 189 + dsi->format = MIPI_DSI_FMT_RGB888; 190 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 191 + MIPI_DSI_MODE_VIDEO_HSE; 192 + 193 + drm_panel_init(&ctx->panel, dev, &boe_tv101wum_ll2_panel_funcs, 194 + DRM_MODE_CONNECTOR_DSI); 195 + ctx->panel.prepare_prev_first = true; 196 + 197 + ret = drm_panel_of_backlight(&ctx->panel); 198 + if (ret) 199 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 200 + 201 + drm_panel_add(&ctx->panel); 202 + 203 + ret = mipi_dsi_attach(dsi); 204 + if (ret < 0) { 205 + drm_panel_remove(&ctx->panel); 206 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 207 + } 208 + 209 + return 0; 210 + } 211 + 212 + static void boe_tv101wum_ll2_remove(struct mipi_dsi_device *dsi) 213 + { 214 + struct boe_tv101wum_ll2 *ctx = mipi_dsi_get_drvdata(dsi); 215 + int ret; 216 + 217 + ret = mipi_dsi_detach(dsi); 218 + if (ret < 0) 219 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 220 + 221 + drm_panel_remove(&ctx->panel); 222 + } 223 + 224 + static const struct of_device_id boe_tv101wum_ll2_of_match[] = { 225 + { .compatible = "boe,tv101wum-ll2" }, 226 + { /* sentinel */ } 227 + }; 228 + MODULE_DEVICE_TABLE(of, boe_tv101wum_ll2_of_match); 229 + 230 + static struct mipi_dsi_driver boe_tv101wum_ll2_driver = { 231 + .probe = boe_tv101wum_ll2_probe, 232 + .remove = boe_tv101wum_ll2_remove, 233 + .driver = { 234 + .name = "panel-boe-tv101wum_ll2", 235 + .of_match_table = boe_tv101wum_ll2_of_match, 236 + }, 237 + }; 238 + module_mipi_dsi_driver(boe_tv101wum_ll2_driver); 239 + 240 + MODULE_DESCRIPTION("DRM driver for BOE TV101WUM-LL2 Panel"); 241 + MODULE_LICENSE("GPL");