1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com> 4 * Copyright (c) 2023 Manuel Traut <manut@mecka.net> 5 * Copyright (c) 2023 Dang Huynh <danct12@riseup.net> 6 */ 7 8 #include <linux/delay.h> 9 #include <linux/gpio/consumer.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_device.h> 13 #include <linux/regulator/consumer.h> 14 15 #include <drm/drm_connector.h> 16 #include <drm/drm_mipi_dsi.h> 17 #include <drm/drm_modes.h> 18 #include <drm/drm_panel.h> 19 20 struct boe_th101mb31ig002 { 21 struct drm_panel panel; 22 23 struct mipi_dsi_device *dsi; 24 25 struct regulator *power; 26 struct gpio_desc *enable; 27 struct gpio_desc *reset; 28 29 enum drm_panel_orientation orientation; 30 }; 31 32 static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx) 33 { 34 gpiod_direction_output(ctx->reset, 0); 35 usleep_range(10, 100); 36 gpiod_direction_output(ctx->reset, 1); 37 usleep_range(10, 100); 38 gpiod_direction_output(ctx->reset, 0); 39 usleep_range(5000, 6000); 40 } 41 42 static int boe_th101mb31ig002_enable(struct drm_panel *panel) 43 { 44 struct boe_th101mb31ig002 *ctx = container_of(panel, 45 struct boe_th101mb31ig002, 46 panel); 47 struct mipi_dsi_device *dsi = ctx->dsi; 48 struct device *dev = &dsi->dev; 49 int ret; 50 51 mipi_dsi_dcs_write_seq(dsi, 0xE0, 0xAB, 0xBA); 52 mipi_dsi_dcs_write_seq(dsi, 0xE1, 0xBA, 0xAB); 53 mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x10, 0x01, 0x47, 0xFF); 54 mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x0C, 0x14, 0x04, 0x50, 0x50, 0x14); 55 mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x56, 0x53, 0x00); 56 mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x33, 0x30, 0x04); 57 mipi_dsi_dcs_write_seq(dsi, 0xB6, 0xB0, 0x00, 0x00, 0x10, 0x00, 0x10, 58 0x00); 59 mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00, 60 0x00); 61 mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x7C, 0x65, 0x55, 0x49, 0x46, 0x36, 62 0x3B, 0x24, 0x3D, 0x3C, 0x3D, 0x5C, 0x4C, 63 0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7C, 64 0x65, 0x55, 0x49, 0x46, 0x36, 0x3B, 0x24, 65 0x3D, 0x3C, 0x3D, 0x5C, 0x4C, 0x55, 0x47, 66 0x46, 0x39, 0x26, 0x06); 67 mipi_dsi_dcs_write_seq(dsi, 0x00, 0xFF, 0x87, 0x12, 0x34, 0x44, 0x44, 68 0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0F, 69 0x00, 0x00, 0xC1); 70 mipi_dsi_dcs_write_seq(dsi, 0xC1, 0x54, 0x94, 0x02, 0x85, 0x9F, 0x00, 71 0x7F, 0x00, 0x54, 0x00); 72 mipi_dsi_dcs_write_seq(dsi, 0xC2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11, 73 0x22, 0x20, 0x44, 0xFF, 0x18, 0x00); 74 mipi_dsi_dcs_write_seq(dsi, 0xC3, 0x86, 0x46, 0x05, 0x05, 0x1C, 0x1C, 75 0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E, 76 0x0F, 0x0F, 0x0D, 0x0D, 0x13, 0x13, 0x11, 77 0x11, 0x00); 78 mipi_dsi_dcs_write_seq(dsi, 0xC4, 0x07, 0x07, 0x04, 0x04, 0x1C, 0x1C, 79 0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E, 80 0x0E, 0x0E, 0x0C, 0x0C, 0x12, 0x12, 0x10, 81 0x10, 0x00); 82 mipi_dsi_dcs_write_seq(dsi, 0xC6, 0x2A, 0x2A); 83 mipi_dsi_dcs_write_seq(dsi, 0xC8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16); 84 mipi_dsi_dcs_write_seq(dsi, 0xCA, 0xCB, 0x43); 85 mipi_dsi_dcs_write_seq(dsi, 0xCD, 0x0E, 0x4B, 0x4B, 0x20, 0x19, 0x6B, 86 0x06, 0xB3); 87 mipi_dsi_dcs_write_seq(dsi, 0xD2, 0xE3, 0x2B, 0x38, 0x00); 88 mipi_dsi_dcs_write_seq(dsi, 0xD4, 0x00, 0x01, 0x00, 0x0E, 0x04, 0x44, 89 0x08, 0x10, 0x00, 0x00, 0x00); 90 mipi_dsi_dcs_write_seq(dsi, 0xE6, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 91 0xFF, 0xFF); 92 mipi_dsi_dcs_write_seq(dsi, 0xF0, 0x12, 0x03, 0x20, 0x00, 0xFF); 93 mipi_dsi_dcs_write_seq(dsi, 0xF3, 0x00); 94 95 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 96 if (ret < 0) { 97 dev_err(dev, "Failed to exit sleep mode: %d\n", ret); 98 return ret; 99 } 100 101 msleep(120); 102 103 ret = mipi_dsi_dcs_set_display_on(dsi); 104 if (ret < 0) { 105 dev_err(dev, "Failed to set panel on: %d\n", ret); 106 return ret; 107 } 108 109 return 0; 110 } 111 112 static int boe_th101mb31ig002_disable(struct drm_panel *panel) 113 { 114 struct boe_th101mb31ig002 *ctx = container_of(panel, 115 struct boe_th101mb31ig002, 116 panel); 117 struct mipi_dsi_device *dsi = ctx->dsi; 118 struct device *dev = &dsi->dev; 119 int ret; 120 121 ret = mipi_dsi_dcs_set_display_off(dsi); 122 if (ret < 0) 123 dev_err(dev, "Failed to set panel off: %d\n", ret); 124 125 msleep(120); 126 127 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 128 if (ret < 0) 129 dev_err(dev, "Failed to enter sleep mode: %d\n", ret); 130 131 return 0; 132 } 133 134 static int boe_th101mb31ig002_unprepare(struct drm_panel *panel) 135 { 136 struct boe_th101mb31ig002 *ctx = container_of(panel, 137 struct boe_th101mb31ig002, 138 panel); 139 140 gpiod_set_value_cansleep(ctx->reset, 1); 141 gpiod_set_value_cansleep(ctx->enable, 0); 142 regulator_disable(ctx->power); 143 144 return 0; 145 } 146 147 static int boe_th101mb31ig002_prepare(struct drm_panel *panel) 148 { 149 struct boe_th101mb31ig002 *ctx = container_of(panel, 150 struct boe_th101mb31ig002, 151 panel); 152 struct device *dev = &ctx->dsi->dev; 153 int ret; 154 155 ret = regulator_enable(ctx->power); 156 if (ret) { 157 dev_err(dev, "Failed to enable power supply: %d\n", ret); 158 return ret; 159 } 160 161 gpiod_set_value_cansleep(ctx->enable, 1); 162 msleep(50); 163 boe_th101mb31ig002_reset(ctx); 164 boe_th101mb31ig002_enable(panel); 165 166 return 0; 167 } 168 169 static const struct drm_display_mode boe_th101mb31ig002_default_mode = { 170 .clock = 73500, 171 .hdisplay = 800, 172 .hsync_start = 800 + 64, 173 .hsync_end = 800 + 64 + 16, 174 .htotal = 800 + 64 + 16 + 64, 175 .vdisplay = 1280, 176 .vsync_start = 1280 + 2, 177 .vsync_end = 1280 + 2 + 4, 178 .vtotal = 1280 + 2 + 4 + 12, 179 .width_mm = 135, 180 .height_mm = 216, 181 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 182 }; 183 184 static int boe_th101mb31ig002_get_modes(struct drm_panel *panel, 185 struct drm_connector *connector) 186 { 187 struct boe_th101mb31ig002 *ctx = container_of(panel, 188 struct boe_th101mb31ig002, 189 panel); 190 struct drm_display_mode *mode; 191 192 mode = drm_mode_duplicate(connector->dev, 193 &boe_th101mb31ig002_default_mode); 194 if (!mode) { 195 dev_err(panel->dev, "Failed to add mode %ux%u@%u\n", 196 boe_th101mb31ig002_default_mode.hdisplay, 197 boe_th101mb31ig002_default_mode.vdisplay, 198 drm_mode_vrefresh(&boe_th101mb31ig002_default_mode)); 199 return -ENOMEM; 200 } 201 202 drm_mode_set_name(mode); 203 204 connector->display_info.bpc = 8; 205 connector->display_info.width_mm = mode->width_mm; 206 connector->display_info.height_mm = mode->height_mm; 207 208 /* 209 * TODO: Remove once all drm drivers call 210 * drm_connector_set_orientation_from_panel() 211 */ 212 drm_connector_set_panel_orientation(connector, ctx->orientation); 213 214 drm_mode_probed_add(connector, mode); 215 216 return 1; 217 } 218 219 static enum drm_panel_orientation 220 boe_th101mb31ig002_get_orientation(struct drm_panel *panel) 221 { 222 struct boe_th101mb31ig002 *ctx = container_of(panel, 223 struct boe_th101mb31ig002, 224 panel); 225 226 return ctx->orientation; 227 } 228 229 static const struct drm_panel_funcs boe_th101mb31ig002_funcs = { 230 .prepare = boe_th101mb31ig002_prepare, 231 .unprepare = boe_th101mb31ig002_unprepare, 232 .disable = boe_th101mb31ig002_disable, 233 .get_modes = boe_th101mb31ig002_get_modes, 234 .get_orientation = boe_th101mb31ig002_get_orientation, 235 }; 236 237 static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi) 238 { 239 struct boe_th101mb31ig002 *ctx; 240 int ret; 241 242 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); 243 if (!ctx) 244 return -ENOMEM; 245 246 mipi_dsi_set_drvdata(dsi, ctx); 247 ctx->dsi = dsi; 248 249 dsi->lanes = 4; 250 dsi->format = MIPI_DSI_FMT_RGB888; 251 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 252 MIPI_DSI_MODE_NO_EOT_PACKET | 253 MIPI_DSI_MODE_LPM; 254 255 ctx->power = devm_regulator_get(&dsi->dev, "power"); 256 if (IS_ERR(ctx->power)) 257 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), 258 "Failed to get power regulator\n"); 259 260 ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW); 261 if (IS_ERR(ctx->enable)) 262 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable), 263 "Failed to get enable GPIO\n"); 264 265 ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH); 266 if (IS_ERR(ctx->reset)) 267 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), 268 "Failed to get reset GPIO\n"); 269 270 ret = of_drm_get_panel_orientation(dsi->dev.of_node, 271 &ctx->orientation); 272 if (ret) 273 return dev_err_probe(&dsi->dev, ret, 274 "Failed to get orientation\n"); 275 276 drm_panel_init(&ctx->panel, &dsi->dev, &boe_th101mb31ig002_funcs, 277 DRM_MODE_CONNECTOR_DSI); 278 279 ret = drm_panel_of_backlight(&ctx->panel); 280 if (ret) 281 return ret; 282 283 drm_panel_add(&ctx->panel); 284 285 ret = mipi_dsi_attach(dsi); 286 if (ret < 0) { 287 dev_err_probe(&dsi->dev, ret, 288 "Failed to attach panel to DSI host\n"); 289 drm_panel_remove(&ctx->panel); 290 return ret; 291 } 292 293 return 0; 294 } 295 296 static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi) 297 { 298 struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi); 299 300 mipi_dsi_detach(dsi); 301 drm_panel_remove(&ctx->panel); 302 } 303 304 static const struct of_device_id boe_th101mb31ig002_of_match[] = { 305 { .compatible = "boe,th101mb31ig002-28a", }, 306 { /* sentinel */ } 307 }; 308 MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match); 309 310 static struct mipi_dsi_driver boe_th101mb31ig002_driver = { 311 .driver = { 312 .name = "boe-th101mb31ig002-28a", 313 .of_match_table = boe_th101mb31ig002_of_match, 314 }, 315 .probe = boe_th101mb31ig002_dsi_probe, 316 .remove = boe_th101mb31ig002_dsi_remove, 317 }; 318 module_mipi_dsi_driver(boe_th101mb31ig002_driver); 319 320 MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>"); 321 MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel"); 322 MODULE_LICENSE("GPL"); 323