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

Configure Feed

Select the types of activity you want to include in your feed.

drivers: gpu: drm: panel: Add BOE TD4320

Add driver for BOE TD4320 DSI panel, used in Xiaomi Redmi Note 7
mobile phone.

Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250430-lavender-panel-v3-2-7625e62d62b2@mainlining.org
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250430-lavender-panel-v3-2-7625e62d62b2@mainlining.org

authored by

Barnabás Czémán and committed by
Neil Armstrong
7220a310 af28dfc3

+257
+9
drivers/gpu/drm/panel/Kconfig
··· 67 67 24 bit RGB per pixel. It provides a MIPI DSI interface to 68 68 the host and has a built-in LED backlight. 69 69 70 + config DRM_PANEL_BOE_TD4320 71 + tristate "BOE TD4320 DSI panel" 72 + depends on OF 73 + depends on DRM_MIPI_DSI 74 + depends on BACKLIGHT_CLASS_DEVICE 75 + help 76 + Say Y here if you want to enable support for BOE TD4320 1080x2340 77 + video mode panel found in Xiaomi Redmi Note 7 smartphones. 78 + 70 79 config DRM_PANEL_BOE_TH101MB31UIG002_28A 71 80 tristate "Boe TH101MB31UIG002-28A panel" 72 81 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 5 5 obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o 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 + obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o 8 9 obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o 9 10 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o 10 11 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
+247
drivers/gpu/drm/panel/panel-boe-td4320.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // Copyright (c) 2024 Barnabas Czeman <barnabas.czeman@mainlining.org> 3 + // Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: 4 + // Copyright (c) 2013, The Linux Foundation. All rights reserved. 5 + 6 + #include <linux/delay.h> 7 + #include <linux/gpio/consumer.h> 8 + #include <linux/mod_devicetable.h> 9 + #include <linux/module.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <video/mipi_display.h> 13 + 14 + #include <drm/drm_mipi_dsi.h> 15 + #include <drm/drm_modes.h> 16 + #include <drm/drm_panel.h> 17 + #include <drm/drm_probe_helper.h> 18 + 19 + struct boe_td4320 { 20 + struct drm_panel panel; 21 + struct mipi_dsi_device *dsi; 22 + struct regulator_bulk_data *supplies; 23 + struct gpio_desc *reset_gpio; 24 + }; 25 + 26 + static const struct regulator_bulk_data boe_td4320_supplies[] = { 27 + { .supply = "iovcc" }, 28 + { .supply = "vsn" }, 29 + { .supply = "vsp" }, 30 + }; 31 + 32 + static inline struct boe_td4320 *to_boe_td4320(struct drm_panel *panel) 33 + { 34 + return container_of(panel, struct boe_td4320, panel); 35 + } 36 + 37 + static void boe_td4320_reset(struct boe_td4320 *ctx) 38 + { 39 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 40 + usleep_range(1000, 2000); 41 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 42 + usleep_range(5000, 6000); 43 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 44 + msleep(30); 45 + } 46 + 47 + static int boe_td4320_on(struct boe_td4320 *ctx) 48 + { 49 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 50 + 51 + ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM; 52 + 53 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x04); 54 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0x00); 55 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb8, 56 + 0x19, 0x55, 0x00, 0xbe, 0x00, 0x00, 57 + 0x00); 58 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9, 59 + 0x4d, 0x55, 0x05, 0xe6, 0x00, 0x02, 60 + 0x03); 61 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xba, 62 + 0x9b, 0x5b, 0x07, 0xe6, 0x00, 0x13, 63 + 0x00); 64 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 65 + 0x44, 0x3f, 0x00, 0x8d, 0xbf); 66 + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 67 + 0x5d, 0x00, 0x0f, 0x1f, 0x2f, 0x3f, 68 + 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 69 + 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, 70 + 0x04, 0x00, 0x02, 0x02, 0x42, 0x01, 71 + 0x69, 0x5a, 0x40, 0x40, 0x00, 0x00, 72 + 0x04, 0xfa, 0x00); 73 + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x00b8); 74 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 75 + 0x2c); 76 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 77 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x03); 78 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x00); 79 + mipi_dsi_msleep(&dsi_ctx, 96); 80 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x00); 81 + mipi_dsi_msleep(&dsi_ctx, 20); 82 + 83 + return dsi_ctx.accum_err; 84 + } 85 + 86 + static int boe_td4320_off(struct boe_td4320 *ctx) 87 + { 88 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 89 + 90 + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 91 + 92 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 93 + mipi_dsi_msleep(&dsi_ctx, 20); 94 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 95 + mipi_dsi_msleep(&dsi_ctx, 120); 96 + 97 + return dsi_ctx.accum_err; 98 + } 99 + 100 + static int boe_td4320_prepare(struct drm_panel *panel) 101 + { 102 + struct boe_td4320 *ctx = to_boe_td4320(panel); 103 + struct device *dev = &ctx->dsi->dev; 104 + int ret; 105 + 106 + ret = regulator_bulk_enable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies); 107 + if (ret < 0) { 108 + dev_err(dev, "Failed to enable regulators: %d\n", ret); 109 + return ret; 110 + } 111 + 112 + boe_td4320_reset(ctx); 113 + 114 + ret = boe_td4320_on(ctx); 115 + if (ret < 0) { 116 + dev_err(dev, "Failed to initialize panel: %d\n", ret); 117 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 118 + regulator_bulk_disable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies); 119 + return ret; 120 + } 121 + 122 + return 0; 123 + } 124 + 125 + static int boe_td4320_unprepare(struct drm_panel *panel) 126 + { 127 + struct boe_td4320 *ctx = to_boe_td4320(panel); 128 + struct device *dev = &ctx->dsi->dev; 129 + int ret; 130 + 131 + ret = boe_td4320_off(ctx); 132 + if (ret < 0) 133 + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); 134 + 135 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 136 + regulator_bulk_disable(ARRAY_SIZE(boe_td4320_supplies), ctx->supplies); 137 + 138 + return 0; 139 + } 140 + 141 + static const struct drm_display_mode boe_td4320_mode = { 142 + .clock = (1080 + 86 + 2 + 100) * (2340 + 4 + 4 + 60) * 60 / 1000, 143 + .hdisplay = 1080, 144 + .hsync_start = 1080 + 86, 145 + .hsync_end = 1080 + 86 + 2, 146 + .htotal = 1080 + 86 + 2 + 100, 147 + .vdisplay = 2340, 148 + .vsync_start = 2340 + 4, 149 + .vsync_end = 2340 + 4 + 4, 150 + .vtotal = 2340 + 4 + 4 + 60, 151 + .width_mm = 67, 152 + .height_mm = 145, 153 + .type = DRM_MODE_TYPE_DRIVER, 154 + }; 155 + 156 + static int boe_td4320_get_modes(struct drm_panel *panel, 157 + struct drm_connector *connector) 158 + { 159 + return drm_connector_helper_get_modes_fixed(connector, &boe_td4320_mode); 160 + } 161 + 162 + static const struct drm_panel_funcs boe_td4320_panel_funcs = { 163 + .prepare = boe_td4320_prepare, 164 + .unprepare = boe_td4320_unprepare, 165 + .get_modes = boe_td4320_get_modes, 166 + }; 167 + 168 + static int boe_td4320_probe(struct mipi_dsi_device *dsi) 169 + { 170 + struct device *dev = &dsi->dev; 171 + struct boe_td4320 *ctx; 172 + int ret; 173 + 174 + ctx = devm_drm_panel_alloc(dev, struct boe_td4320, panel, 175 + &boe_td4320_panel_funcs, 176 + DRM_MODE_CONNECTOR_DSI); 177 + if (IS_ERR(ctx)) 178 + return PTR_ERR(ctx); 179 + 180 + ret = devm_regulator_bulk_get_const(dev, 181 + ARRAY_SIZE(boe_td4320_supplies), 182 + boe_td4320_supplies, 183 + &ctx->supplies); 184 + if (ret < 0) 185 + return ret; 186 + 187 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 188 + if (IS_ERR(ctx->reset_gpio)) 189 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 190 + "Failed to get reset-gpios\n"); 191 + 192 + ctx->dsi = dsi; 193 + mipi_dsi_set_drvdata(dsi, ctx); 194 + 195 + dsi->lanes = 4; 196 + dsi->format = MIPI_DSI_FMT_RGB888; 197 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 198 + MIPI_DSI_CLOCK_NON_CONTINUOUS; 199 + 200 + ctx->panel.prepare_prev_first = true; 201 + 202 + ret = drm_panel_of_backlight(&ctx->panel); 203 + if (ret) 204 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 205 + 206 + drm_panel_add(&ctx->panel); 207 + 208 + ret = mipi_dsi_attach(dsi); 209 + if (ret < 0) { 210 + drm_panel_remove(&ctx->panel); 211 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 212 + } 213 + 214 + return 0; 215 + } 216 + 217 + static void boe_td4320_remove(struct mipi_dsi_device *dsi) 218 + { 219 + struct boe_td4320 *ctx = mipi_dsi_get_drvdata(dsi); 220 + int ret; 221 + 222 + ret = mipi_dsi_detach(dsi); 223 + if (ret < 0) 224 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 225 + 226 + drm_panel_remove(&ctx->panel); 227 + } 228 + 229 + static const struct of_device_id boe_td4320_of_match[] = { 230 + { .compatible = "boe,td4320" }, 231 + { /* sentinel */ } 232 + }; 233 + MODULE_DEVICE_TABLE(of, boe_td4320_of_match); 234 + 235 + static struct mipi_dsi_driver boe_td4320_driver = { 236 + .probe = boe_td4320_probe, 237 + .remove = boe_td4320_remove, 238 + .driver = { 239 + .name = "panel-boe-td4320", 240 + .of_match_table = boe_td4320_of_match, 241 + }, 242 + }; 243 + module_mipi_dsi_driver(boe_td4320_driver); 244 + 245 + MODULE_AUTHOR("Barnabas Czeman <barnabas.czeman@mainlining.org>"); 246 + MODULE_DESCRIPTION("DRM driver for boe td4320 fhdplus video mode dsi panel"); 247 + MODULE_LICENSE("GPL");