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.

drm/panel: Add support for OTM8009A panel driver

This patch adds Orise Tech OTM8009A 3.97" 480x800 TFT LCD
panel driver (MIPI-DSI video mode). The panel backlight is
managed through the DSI link. This panel driver is used in
several STM32 boards.

Signed-off-by: Philippe CORNU <philippe.cornu@st.com>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1500297593-30633-4-git-send-email-philippe.cornu@st.com

authored by

Philippe CORNU and committed by
Thierry Reding
f0a5bb98 59ef38f5

+501
+9
drivers/gpu/drm/panel/Kconfig
··· 63 63 Say Y here if you want to enable support for LG4573 RGB panel. 64 64 To compile this driver as a module, choose M here. 65 65 66 + config DRM_PANEL_ORISETECH_OTM8009A 67 + tristate "Orise Technology otm8009a 480x800 dsi 2dl panel" 68 + depends on OF 69 + depends on DRM_MIPI_DSI 70 + depends on BACKLIGHT_CLASS_DEVICE 71 + help 72 + Say Y here if you want to enable support for Orise Technology 73 + otm8009a 480x800 dsi 2dl panel. 74 + 66 75 config DRM_PANEL_PANASONIC_VVX10F034N00 67 76 tristate "Panasonic VVX10F034N00 1920x1200 video mode panel" 68 77 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 3 3 obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o 4 4 obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o 5 5 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o 6 + obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o 6 7 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o 7 8 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o 8 9 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
+491
drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
··· 1 + /* 2 + * Copyright (C) STMicroelectronics SA 2017 3 + * 4 + * Authors: Philippe Cornu <philippe.cornu@st.com> 5 + * Yannick Fertre <yannick.fertre@st.com> 6 + * 7 + * License terms: GNU General Public License (GPL), version 2 8 + */ 9 + #include <drm/drmP.h> 10 + #include <drm/drm_mipi_dsi.h> 11 + #include <drm/drm_panel.h> 12 + #include <linux/backlight.h> 13 + #include <linux/gpio/consumer.h> 14 + #include <video/mipi_display.h> 15 + 16 + #define DRV_NAME "orisetech_otm8009a" 17 + 18 + #define OTM8009A_BACKLIGHT_DEFAULT 240 19 + #define OTM8009A_BACKLIGHT_MAX 255 20 + 21 + /* Manufacturer Command Set */ 22 + #define MCS_ADRSFT 0x0000 /* Address Shift Function */ 23 + #define MCS_PANSET 0xB3A6 /* Panel Type Setting */ 24 + #define MCS_SD_CTRL 0xC0A2 /* Source Driver Timing Setting */ 25 + #define MCS_P_DRV_M 0xC0B4 /* Panel Driving Mode */ 26 + #define MCS_OSC_ADJ 0xC181 /* Oscillator Adjustment for Idle/Normal mode */ 27 + #define MCS_RGB_VID_SET 0xC1A1 /* RGB Video Mode Setting */ 28 + #define MCS_SD_PCH_CTRL 0xC480 /* Source Driver Precharge Control */ 29 + #define MCS_NO_DOC1 0xC48A /* Command not documented */ 30 + #define MCS_PWR_CTRL1 0xC580 /* Power Control Setting 1 */ 31 + #define MCS_PWR_CTRL2 0xC590 /* Power Control Setting 2 for Normal Mode */ 32 + #define MCS_PWR_CTRL4 0xC5B0 /* Power Control Setting 4 for DC Voltage */ 33 + #define MCS_PANCTRLSET1 0xCB80 /* Panel Control Setting 1 */ 34 + #define MCS_PANCTRLSET2 0xCB90 /* Panel Control Setting 2 */ 35 + #define MCS_PANCTRLSET3 0xCBA0 /* Panel Control Setting 3 */ 36 + #define MCS_PANCTRLSET4 0xCBB0 /* Panel Control Setting 4 */ 37 + #define MCS_PANCTRLSET5 0xCBC0 /* Panel Control Setting 5 */ 38 + #define MCS_PANCTRLSET6 0xCBD0 /* Panel Control Setting 6 */ 39 + #define MCS_PANCTRLSET7 0xCBE0 /* Panel Control Setting 7 */ 40 + #define MCS_PANCTRLSET8 0xCBF0 /* Panel Control Setting 8 */ 41 + #define MCS_PANU2D1 0xCC80 /* Panel U2D Setting 1 */ 42 + #define MCS_PANU2D2 0xCC90 /* Panel U2D Setting 2 */ 43 + #define MCS_PANU2D3 0xCCA0 /* Panel U2D Setting 3 */ 44 + #define MCS_PAND2U1 0xCCB0 /* Panel D2U Setting 1 */ 45 + #define MCS_PAND2U2 0xCCC0 /* Panel D2U Setting 2 */ 46 + #define MCS_PAND2U3 0xCCD0 /* Panel D2U Setting 3 */ 47 + #define MCS_GOAVST 0xCE80 /* GOA VST Setting */ 48 + #define MCS_GOACLKA1 0xCEA0 /* GOA CLKA1 Setting */ 49 + #define MCS_GOACLKA3 0xCEB0 /* GOA CLKA3 Setting */ 50 + #define MCS_GOAECLK 0xCFC0 /* GOA ECLK Setting */ 51 + #define MCS_NO_DOC2 0xCFD0 /* Command not documented */ 52 + #define MCS_GVDDSET 0xD800 /* GVDD/NGVDD */ 53 + #define MCS_VCOMDC 0xD900 /* VCOM Voltage Setting */ 54 + #define MCS_GMCT2_2P 0xE100 /* Gamma Correction 2.2+ Setting */ 55 + #define MCS_GMCT2_2N 0xE200 /* Gamma Correction 2.2- Setting */ 56 + #define MCS_NO_DOC3 0xF5B6 /* Command not documented */ 57 + #define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ 58 + #define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ 59 + 60 + struct otm8009a { 61 + struct device *dev; 62 + struct drm_panel panel; 63 + struct backlight_device *bl_dev; 64 + struct gpio_desc *reset_gpio; 65 + bool prepared; 66 + bool enabled; 67 + }; 68 + 69 + static const struct drm_display_mode default_mode = { 70 + .clock = 32729, 71 + .hdisplay = 480, 72 + .hsync_start = 480 + 120, 73 + .hsync_end = 480 + 120 + 63, 74 + .htotal = 480 + 120 + 63 + 120, 75 + .vdisplay = 800, 76 + .vsync_start = 800 + 12, 77 + .vsync_end = 800 + 12 + 12, 78 + .vtotal = 800 + 12 + 12 + 12, 79 + .vrefresh = 50, 80 + .flags = 0, 81 + .width_mm = 52, 82 + .height_mm = 86, 83 + }; 84 + 85 + static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel) 86 + { 87 + return container_of(panel, struct otm8009a, panel); 88 + } 89 + 90 + static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data, 91 + size_t len) 92 + { 93 + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 94 + 95 + if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0) 96 + DRM_WARN("mipi dsi dcs write buffer failed\n"); 97 + } 98 + 99 + #define dcs_write_seq(ctx, seq...) \ 100 + ({ \ 101 + static const u8 d[] = { seq }; \ 102 + otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \ 103 + }) 104 + 105 + #define dcs_write_cmd_at(ctx, cmd, seq...) \ 106 + ({ \ 107 + dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \ 108 + dcs_write_seq(ctx, (cmd) >> 8, seq); \ 109 + }) 110 + 111 + static int otm8009a_init_sequence(struct otm8009a *ctx) 112 + { 113 + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 114 + int ret; 115 + 116 + /* Enter CMD2 */ 117 + dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01); 118 + 119 + /* Enter Orise Command2 */ 120 + dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09); 121 + 122 + dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30); 123 + mdelay(10); 124 + 125 + dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40); 126 + mdelay(10); 127 + 128 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9); 129 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34); 130 + dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50); 131 + dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E); 132 + dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */ 133 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01); 134 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34); 135 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33); 136 + dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79); 137 + dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B); 138 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83); 139 + dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83); 140 + dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E); 141 + dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01); 142 + 143 + dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); 144 + dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 145 + 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); 146 + dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 147 + 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); 148 + dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 149 + 0x01, 0x02, 0x00, 0x00); 150 + 151 + dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00); 152 + 153 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 154 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155 + 0, 0, 0, 0, 0); 156 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157 + 0, 0, 0, 0, 0); 158 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 159 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 160 + 0, 0, 0, 0, 0); 161 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 162 + 4, 0, 0, 0, 0); 163 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 164 + dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 165 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); 166 + 167 + dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 168 + 0x00, 0x00, 0x00, 0x00); 169 + dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02); 171 + dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 172 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 173 + dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 174 + 0x00, 0x00, 0x00, 0x00); 175 + dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01); 177 + dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 178 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 179 + 180 + dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66); 181 + 182 + dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06); 183 + 184 + dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 185 + 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 186 + 0x01); 187 + dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 188 + 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 189 + 0x01); 190 + 191 + /* Exit CMD2 */ 192 + dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF); 193 + 194 + ret = mipi_dsi_dcs_nop(dsi); 195 + if (ret) 196 + return ret; 197 + 198 + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 199 + if (ret) 200 + return ret; 201 + 202 + /* Wait for sleep out exit */ 203 + mdelay(120); 204 + 205 + /* Default portrait 480x800 rgb24 */ 206 + dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 207 + 208 + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 209 + default_mode.hdisplay - 1); 210 + if (ret) 211 + return ret; 212 + 213 + ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); 214 + if (ret) 215 + return ret; 216 + 217 + /* See otm8009a driver documentation for pixel format descriptions */ 218 + ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT | 219 + MIPI_DCS_PIXEL_FMT_24BIT << 4); 220 + if (ret) 221 + return ret; 222 + 223 + /* Disable CABC feature */ 224 + dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 225 + 226 + ret = mipi_dsi_dcs_set_display_on(dsi); 227 + if (ret) 228 + return ret; 229 + 230 + ret = mipi_dsi_dcs_nop(dsi); 231 + if (ret) 232 + return ret; 233 + 234 + /* Send Command GRAM memory write (no parameters) */ 235 + dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START); 236 + 237 + return 0; 238 + } 239 + 240 + static int otm8009a_disable(struct drm_panel *panel) 241 + { 242 + struct otm8009a *ctx = panel_to_otm8009a(panel); 243 + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 244 + int ret; 245 + 246 + if (!ctx->enabled) 247 + return 0; /* This is not an issue so we return 0 here */ 248 + 249 + /* Power off the backlight. Note: end-user still controls brightness */ 250 + ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 251 + ret = backlight_update_status(ctx->bl_dev); 252 + if (ret) 253 + return ret; 254 + 255 + ret = mipi_dsi_dcs_set_display_off(dsi); 256 + if (ret) 257 + return ret; 258 + 259 + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 260 + if (ret) 261 + return ret; 262 + 263 + msleep(120); 264 + 265 + ctx->enabled = false; 266 + 267 + return 0; 268 + } 269 + 270 + static int otm8009a_unprepare(struct drm_panel *panel) 271 + { 272 + struct otm8009a *ctx = panel_to_otm8009a(panel); 273 + 274 + if (!ctx->prepared) 275 + return 0; 276 + 277 + if (ctx->reset_gpio) { 278 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 279 + msleep(20); 280 + } 281 + 282 + ctx->prepared = false; 283 + 284 + return 0; 285 + } 286 + 287 + static int otm8009a_prepare(struct drm_panel *panel) 288 + { 289 + struct otm8009a *ctx = panel_to_otm8009a(panel); 290 + int ret; 291 + 292 + if (ctx->prepared) 293 + return 0; 294 + 295 + if (ctx->reset_gpio) { 296 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 297 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 298 + msleep(20); 299 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 300 + msleep(100); 301 + } 302 + 303 + ret = otm8009a_init_sequence(ctx); 304 + if (ret) 305 + return ret; 306 + 307 + ctx->prepared = true; 308 + 309 + /* 310 + * Power on the backlight. Note: end-user still controls brightness 311 + * Note: ctx->prepared must be true before updating the backlight. 312 + */ 313 + ctx->bl_dev->props.power = FB_BLANK_UNBLANK; 314 + backlight_update_status(ctx->bl_dev); 315 + 316 + return 0; 317 + } 318 + 319 + static int otm8009a_enable(struct drm_panel *panel) 320 + { 321 + struct otm8009a *ctx = panel_to_otm8009a(panel); 322 + 323 + ctx->enabled = true; 324 + 325 + return 0; 326 + } 327 + 328 + static int otm8009a_get_modes(struct drm_panel *panel) 329 + { 330 + struct drm_display_mode *mode; 331 + 332 + mode = drm_mode_duplicate(panel->drm, &default_mode); 333 + if (!mode) { 334 + DRM_ERROR("failed to add mode %ux%ux@%u\n", 335 + default_mode.hdisplay, default_mode.vdisplay, 336 + default_mode.vrefresh); 337 + return -ENOMEM; 338 + } 339 + 340 + drm_mode_set_name(mode); 341 + 342 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 343 + drm_mode_probed_add(panel->connector, mode); 344 + 345 + panel->connector->display_info.width_mm = mode->width_mm; 346 + panel->connector->display_info.height_mm = mode->height_mm; 347 + 348 + return 1; 349 + } 350 + 351 + static const struct drm_panel_funcs otm8009a_drm_funcs = { 352 + .disable = otm8009a_disable, 353 + .unprepare = otm8009a_unprepare, 354 + .prepare = otm8009a_prepare, 355 + .enable = otm8009a_enable, 356 + .get_modes = otm8009a_get_modes, 357 + }; 358 + 359 + /* 360 + * DSI-BASED BACKLIGHT 361 + */ 362 + 363 + static int otm8009a_backlight_update_status(struct backlight_device *bd) 364 + { 365 + struct otm8009a *ctx = bl_get_data(bd); 366 + u8 data[2]; 367 + 368 + if (!ctx->prepared) { 369 + DRM_DEBUG("lcd not ready yet for setting its backlight!\n"); 370 + return -ENXIO; 371 + } 372 + 373 + if (bd->props.power <= FB_BLANK_NORMAL) { 374 + /* Power on the backlight with the requested brightness 375 + * Note We can not use mipi_dsi_dcs_set_display_brightness() 376 + * as otm8009a driver support only 8-bit brightness (1 param). 377 + */ 378 + data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS; 379 + data[1] = bd->props.brightness; 380 + otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 381 + 382 + /* set Brightness Control & Backlight on */ 383 + data[1] = 0x24; 384 + 385 + } else { 386 + /* Power off the backlight: set Brightness Control & Bl off */ 387 + data[1] = 0; 388 + } 389 + 390 + /* Update Brightness Control & Backlight */ 391 + data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY; 392 + otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 393 + 394 + return 0; 395 + } 396 + 397 + static const struct backlight_ops otm8009a_backlight_ops = { 398 + .update_status = otm8009a_backlight_update_status, 399 + }; 400 + 401 + static int otm8009a_probe(struct mipi_dsi_device *dsi) 402 + { 403 + struct device *dev = &dsi->dev; 404 + struct otm8009a *ctx; 405 + int ret; 406 + 407 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 408 + if (!ctx) 409 + return -ENOMEM; 410 + 411 + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 412 + if (IS_ERR(ctx->reset_gpio)) { 413 + dev_err(dev, "cannot get reset-gpio\n"); 414 + return PTR_ERR(ctx->reset_gpio); 415 + } 416 + 417 + mipi_dsi_set_drvdata(dsi, ctx); 418 + 419 + ctx->dev = dev; 420 + 421 + dsi->lanes = 2; 422 + dsi->format = MIPI_DSI_FMT_RGB888; 423 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 424 + MIPI_DSI_MODE_LPM; 425 + 426 + drm_panel_init(&ctx->panel); 427 + ctx->panel.dev = dev; 428 + ctx->panel.funcs = &otm8009a_drm_funcs; 429 + 430 + ctx->bl_dev = backlight_device_register(DRV_NAME "_backlight", dev, ctx, 431 + &otm8009a_backlight_ops, NULL); 432 + if (IS_ERR(ctx->bl_dev)) { 433 + dev_err(dev, "failed to register backlight device\n"); 434 + return PTR_ERR(ctx->bl_dev); 435 + } 436 + 437 + ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX; 438 + ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT; 439 + ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 440 + ctx->bl_dev->props.type = BACKLIGHT_RAW; 441 + 442 + drm_panel_add(&ctx->panel); 443 + 444 + ret = mipi_dsi_attach(dsi); 445 + if (ret < 0) { 446 + dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n"); 447 + drm_panel_remove(&ctx->panel); 448 + backlight_device_unregister(ctx->bl_dev); 449 + return ret; 450 + } 451 + 452 + DRM_INFO(DRV_NAME "_panel %ux%u@%u %ubpp dsi %udl - ready\n", 453 + default_mode.hdisplay, default_mode.vdisplay, 454 + default_mode.vrefresh, 455 + mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); 456 + 457 + return 0; 458 + } 459 + 460 + static int otm8009a_remove(struct mipi_dsi_device *dsi) 461 + { 462 + struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi); 463 + 464 + mipi_dsi_detach(dsi); 465 + drm_panel_remove(&ctx->panel); 466 + 467 + backlight_device_unregister(ctx->bl_dev); 468 + 469 + return 0; 470 + } 471 + 472 + static const struct of_device_id orisetech_otm8009a_of_match[] = { 473 + { .compatible = "orisetech,otm8009a" }, 474 + { } 475 + }; 476 + MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match); 477 + 478 + static struct mipi_dsi_driver orisetech_otm8009a_driver = { 479 + .probe = otm8009a_probe, 480 + .remove = otm8009a_remove, 481 + .driver = { 482 + .name = DRV_NAME "_panel", 483 + .of_match_table = orisetech_otm8009a_of_match, 484 + }, 485 + }; 486 + module_mipi_dsi_driver(orisetech_otm8009a_driver); 487 + 488 + MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); 489 + MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 490 + MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel"); 491 + MODULE_LICENSE("GPL v2");