root/drivers/gpu/drm/panel/panel-innolux-p079zca.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_innolux_panel
  2. innolux_panel_disable
  3. innolux_panel_unprepare
  4. innolux_panel_prepare
  5. innolux_panel_enable
  6. innolux_panel_get_modes
  7. innolux_panel_add
  8. innolux_panel_del
  9. innolux_panel_probe
  10. innolux_panel_remove
  11. innolux_panel_shutdown

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
   4  */
   5 
   6 #include <linux/backlight.h>
   7 #include <linux/delay.h>
   8 #include <linux/gpio/consumer.h>
   9 #include <linux/module.h>
  10 #include <linux/of.h>
  11 #include <linux/of_device.h>
  12 #include <linux/regulator/consumer.h>
  13 
  14 #include <video/mipi_display.h>
  15 
  16 #include <drm/drm_crtc.h>
  17 #include <drm/drm_device.h>
  18 #include <drm/drm_mipi_dsi.h>
  19 #include <drm/drm_modes.h>
  20 #include <drm/drm_panel.h>
  21 #include <drm/drm_print.h>
  22 
  23 struct panel_init_cmd {
  24         size_t len;
  25         const char *data;
  26 };
  27 
  28 #define _INIT_CMD(...) { \
  29         .len = sizeof((char[]){__VA_ARGS__}), \
  30         .data = (char[]){__VA_ARGS__} }
  31 
  32 struct panel_desc {
  33         const struct drm_display_mode *mode;
  34         unsigned int bpc;
  35         struct {
  36                 unsigned int width;
  37                 unsigned int height;
  38         } size;
  39 
  40         unsigned long flags;
  41         enum mipi_dsi_pixel_format format;
  42         const struct panel_init_cmd *init_cmds;
  43         unsigned int lanes;
  44         const char * const *supply_names;
  45         unsigned int num_supplies;
  46         unsigned int sleep_mode_delay;
  47         unsigned int power_down_delay;
  48 };
  49 
  50 struct innolux_panel {
  51         struct drm_panel base;
  52         struct mipi_dsi_device *link;
  53         const struct panel_desc *desc;
  54 
  55         struct backlight_device *backlight;
  56         struct regulator_bulk_data *supplies;
  57         struct gpio_desc *enable_gpio;
  58 
  59         bool prepared;
  60         bool enabled;
  61 };
  62 
  63 static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
  64 {
  65         return container_of(panel, struct innolux_panel, base);
  66 }
  67 
  68 static int innolux_panel_disable(struct drm_panel *panel)
  69 {
  70         struct innolux_panel *innolux = to_innolux_panel(panel);
  71 
  72         if (!innolux->enabled)
  73                 return 0;
  74 
  75         backlight_disable(innolux->backlight);
  76 
  77         innolux->enabled = false;
  78 
  79         return 0;
  80 }
  81 
  82 static int innolux_panel_unprepare(struct drm_panel *panel)
  83 {
  84         struct innolux_panel *innolux = to_innolux_panel(panel);
  85         int err;
  86 
  87         if (!innolux->prepared)
  88                 return 0;
  89 
  90         err = mipi_dsi_dcs_set_display_off(innolux->link);
  91         if (err < 0)
  92                 DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
  93                               err);
  94 
  95         err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
  96         if (err < 0) {
  97                 DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
  98                               err);
  99                 return err;
 100         }
 101 
 102         if (innolux->desc->sleep_mode_delay)
 103                 msleep(innolux->desc->sleep_mode_delay);
 104 
 105         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
 106 
 107         if (innolux->desc->power_down_delay)
 108                 msleep(innolux->desc->power_down_delay);
 109 
 110         err = regulator_bulk_disable(innolux->desc->num_supplies,
 111                                      innolux->supplies);
 112         if (err < 0)
 113                 return err;
 114 
 115         innolux->prepared = false;
 116 
 117         return 0;
 118 }
 119 
 120 static int innolux_panel_prepare(struct drm_panel *panel)
 121 {
 122         struct innolux_panel *innolux = to_innolux_panel(panel);
 123         int err;
 124 
 125         if (innolux->prepared)
 126                 return 0;
 127 
 128         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
 129 
 130         err = regulator_bulk_enable(innolux->desc->num_supplies,
 131                                     innolux->supplies);
 132         if (err < 0)
 133                 return err;
 134 
 135         /* p079zca: t2 (20ms), p097pfg: t4 (15ms) */
 136         usleep_range(20000, 21000);
 137 
 138         gpiod_set_value_cansleep(innolux->enable_gpio, 1);
 139 
 140         /* p079zca: t4, p097pfg: t5 */
 141         usleep_range(20000, 21000);
 142 
 143         if (innolux->desc->init_cmds) {
 144                 const struct panel_init_cmd *cmds =
 145                                         innolux->desc->init_cmds;
 146                 unsigned int i;
 147 
 148                 for (i = 0; cmds[i].len != 0; i++) {
 149                         const struct panel_init_cmd *cmd = &cmds[i];
 150 
 151                         err = mipi_dsi_generic_write(innolux->link, cmd->data,
 152                                                      cmd->len);
 153                         if (err < 0) {
 154                                 dev_err(panel->dev,
 155                                         "failed to write command %u\n", i);
 156                                 goto poweroff;
 157                         }
 158 
 159                         /*
 160                          * Included by random guessing, because without this
 161                          * (or at least, some delay), the panel sometimes
 162                          * didn't appear to pick up the command sequence.
 163                          */
 164                         err = mipi_dsi_dcs_nop(innolux->link);
 165                         if (err < 0) {
 166                                 dev_err(panel->dev,
 167                                         "failed to send DCS nop: %d\n", err);
 168                                 goto poweroff;
 169                         }
 170                 }
 171         }
 172 
 173         err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
 174         if (err < 0) {
 175                 DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
 176                               err);
 177                 goto poweroff;
 178         }
 179 
 180         /* T6: 120ms - 1000ms*/
 181         msleep(120);
 182 
 183         err = mipi_dsi_dcs_set_display_on(innolux->link);
 184         if (err < 0) {
 185                 DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
 186                               err);
 187                 goto poweroff;
 188         }
 189 
 190         /* T7: 5ms */
 191         usleep_range(5000, 6000);
 192 
 193         innolux->prepared = true;
 194 
 195         return 0;
 196 
 197 poweroff:
 198         gpiod_set_value_cansleep(innolux->enable_gpio, 0);
 199         regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
 200 
 201         return err;
 202 }
 203 
 204 static int innolux_panel_enable(struct drm_panel *panel)
 205 {
 206         struct innolux_panel *innolux = to_innolux_panel(panel);
 207         int ret;
 208 
 209         if (innolux->enabled)
 210                 return 0;
 211 
 212         ret = backlight_enable(innolux->backlight);
 213         if (ret) {
 214                 DRM_DEV_ERROR(panel->drm->dev,
 215                               "Failed to enable backlight %d\n", ret);
 216                 return ret;
 217         }
 218 
 219         innolux->enabled = true;
 220 
 221         return 0;
 222 }
 223 
 224 static const char * const innolux_p079zca_supply_names[] = {
 225         "power",
 226 };
 227 
 228 static const struct drm_display_mode innolux_p079zca_mode = {
 229         .clock = 56900,
 230         .hdisplay = 768,
 231         .hsync_start = 768 + 40,
 232         .hsync_end = 768 + 40 + 40,
 233         .htotal = 768 + 40 + 40 + 40,
 234         .vdisplay = 1024,
 235         .vsync_start = 1024 + 20,
 236         .vsync_end = 1024 + 20 + 4,
 237         .vtotal = 1024 + 20 + 4 + 20,
 238         .vrefresh = 60,
 239 };
 240 
 241 static const struct panel_desc innolux_p079zca_panel_desc = {
 242         .mode = &innolux_p079zca_mode,
 243         .bpc = 8,
 244         .size = {
 245                 .width = 120,
 246                 .height = 160,
 247         },
 248         .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
 249                  MIPI_DSI_MODE_LPM,
 250         .format = MIPI_DSI_FMT_RGB888,
 251         .lanes = 4,
 252         .supply_names = innolux_p079zca_supply_names,
 253         .num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names),
 254         .power_down_delay = 80, /* T8: 80ms - 1000ms */
 255 };
 256 
 257 static const char * const innolux_p097pfg_supply_names[] = {
 258         "avdd",
 259         "avee",
 260 };
 261 
 262 static const struct drm_display_mode innolux_p097pfg_mode = {
 263         .clock = 229000,
 264         .hdisplay = 1536,
 265         .hsync_start = 1536 + 100,
 266         .hsync_end = 1536 + 100 + 24,
 267         .htotal = 1536 + 100 + 24 + 100,
 268         .vdisplay = 2048,
 269         .vsync_start = 2048 + 100,
 270         .vsync_end = 2048 + 100 + 2,
 271         .vtotal = 2048 + 100 + 2 + 18,
 272         .vrefresh = 60,
 273 };
 274 
 275 /*
 276  * Display manufacturer failed to provide init sequencing according to
 277  * https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/892065/
 278  * so the init sequence stems from a register dump of a working panel.
 279  */
 280 static const struct panel_init_cmd innolux_p097pfg_init_cmds[] = {
 281         /* page 0 */
 282         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00),
 283         _INIT_CMD(0xB1, 0xE8, 0x11),
 284         _INIT_CMD(0xB2, 0x25, 0x02),
 285         _INIT_CMD(0xB5, 0x08, 0x00),
 286         _INIT_CMD(0xBC, 0x0F, 0x00),
 287         _INIT_CMD(0xB8, 0x03, 0x06, 0x00, 0x00),
 288         _INIT_CMD(0xBD, 0x01, 0x90, 0x14, 0x14),
 289         _INIT_CMD(0x6F, 0x01),
 290         _INIT_CMD(0xC0, 0x03),
 291         _INIT_CMD(0x6F, 0x02),
 292         _INIT_CMD(0xC1, 0x0D),
 293         _INIT_CMD(0xD9, 0x01, 0x09, 0x70),
 294         _INIT_CMD(0xC5, 0x12, 0x21, 0x00),
 295         _INIT_CMD(0xBB, 0x93, 0x93),
 296 
 297         /* page 1 */
 298         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01),
 299         _INIT_CMD(0xB3, 0x3C, 0x3C),
 300         _INIT_CMD(0xB4, 0x0F, 0x0F),
 301         _INIT_CMD(0xB9, 0x45, 0x45),
 302         _INIT_CMD(0xBA, 0x14, 0x14),
 303         _INIT_CMD(0xCA, 0x02),
 304         _INIT_CMD(0xCE, 0x04),
 305         _INIT_CMD(0xC3, 0x9B, 0x9B),
 306         _INIT_CMD(0xD8, 0xC0, 0x03),
 307         _INIT_CMD(0xBC, 0x82, 0x01),
 308         _INIT_CMD(0xBD, 0x9E, 0x01),
 309 
 310         /* page 2 */
 311         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x02),
 312         _INIT_CMD(0xB0, 0x82),
 313         _INIT_CMD(0xD1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x82, 0x00, 0xA5,
 314                   0x00, 0xC1, 0x00, 0xEA, 0x01, 0x0D, 0x01, 0x40),
 315         _INIT_CMD(0xD2, 0x01, 0x6A, 0x01, 0xA8, 0x01, 0xDC, 0x02, 0x29,
 316                   0x02, 0x67, 0x02, 0x68, 0x02, 0xA8, 0x02, 0xF0),
 317         _INIT_CMD(0xD3, 0x03, 0x19, 0x03, 0x49, 0x03, 0x67, 0x03, 0x8C,
 318                   0x03, 0xA6, 0x03, 0xC7, 0x03, 0xDE, 0x03, 0xEC),
 319         _INIT_CMD(0xD4, 0x03, 0xFF, 0x03, 0xFF),
 320         _INIT_CMD(0xE0, 0x00, 0x00, 0x00, 0x86, 0x00, 0xC5, 0x00, 0xE5,
 321                   0x00, 0xFF, 0x01, 0x26, 0x01, 0x45, 0x01, 0x75),
 322         _INIT_CMD(0xE1, 0x01, 0x9C, 0x01, 0xD5, 0x02, 0x05, 0x02, 0x4D,
 323                   0x02, 0x86, 0x02, 0x87, 0x02, 0xC3, 0x03, 0x03),
 324         _INIT_CMD(0xE2, 0x03, 0x2A, 0x03, 0x56, 0x03, 0x72, 0x03, 0x94,
 325                   0x03, 0xAC, 0x03, 0xCB, 0x03, 0xE0, 0x03, 0xED),
 326         _INIT_CMD(0xE3, 0x03, 0xFF, 0x03, 0xFF),
 327 
 328         /* page 3 */
 329         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x03),
 330         _INIT_CMD(0xB0, 0x00, 0x00, 0x00, 0x00),
 331         _INIT_CMD(0xB1, 0x00, 0x00, 0x00, 0x00),
 332         _INIT_CMD(0xB2, 0x00, 0x00, 0x06, 0x04, 0x01, 0x40, 0x85),
 333         _INIT_CMD(0xB3, 0x10, 0x07, 0xFC, 0x04, 0x01, 0x40, 0x80),
 334         _INIT_CMD(0xB6, 0xF0, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
 335                   0x40, 0x80),
 336         _INIT_CMD(0xBA, 0xC5, 0x07, 0x00, 0x04, 0x11, 0x25, 0x8C),
 337         _INIT_CMD(0xBB, 0xC5, 0x07, 0x00, 0x03, 0x11, 0x25, 0x8C),
 338         _INIT_CMD(0xC0, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80),
 339         _INIT_CMD(0xC1, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80),
 340         _INIT_CMD(0xC4, 0x00, 0x00),
 341         _INIT_CMD(0xEF, 0x41),
 342 
 343         /* page 4 */
 344         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x04),
 345         _INIT_CMD(0xEC, 0x4C),
 346 
 347         /* page 5 */
 348         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x05),
 349         _INIT_CMD(0xB0, 0x13, 0x03, 0x03, 0x01),
 350         _INIT_CMD(0xB1, 0x30, 0x00),
 351         _INIT_CMD(0xB2, 0x02, 0x02, 0x00),
 352         _INIT_CMD(0xB3, 0x82, 0x23, 0x82, 0x9D),
 353         _INIT_CMD(0xB4, 0xC5, 0x75, 0x24, 0x57),
 354         _INIT_CMD(0xB5, 0x00, 0xD4, 0x72, 0x11, 0x11, 0xAB, 0x0A),
 355         _INIT_CMD(0xB6, 0x00, 0x00, 0xD5, 0x72, 0x24, 0x56),
 356         _INIT_CMD(0xB7, 0x5C, 0xDC, 0x5C, 0x5C),
 357         _INIT_CMD(0xB9, 0x0C, 0x00, 0x00, 0x01, 0x00),
 358         _INIT_CMD(0xC0, 0x75, 0x11, 0x11, 0x54, 0x05),
 359         _INIT_CMD(0xC6, 0x00, 0x00, 0x00, 0x00),
 360         _INIT_CMD(0xD0, 0x00, 0x48, 0x08, 0x00, 0x00),
 361         _INIT_CMD(0xD1, 0x00, 0x48, 0x09, 0x00, 0x00),
 362 
 363         /* page 6 */
 364         _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x06),
 365         _INIT_CMD(0xB0, 0x02, 0x32, 0x32, 0x08, 0x2F),
 366         _INIT_CMD(0xB1, 0x2E, 0x15, 0x14, 0x13, 0x12),
 367         _INIT_CMD(0xB2, 0x11, 0x10, 0x00, 0x3D, 0x3D),
 368         _INIT_CMD(0xB3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
 369         _INIT_CMD(0xB4, 0x3D, 0x32),
 370         _INIT_CMD(0xB5, 0x03, 0x32, 0x32, 0x09, 0x2F),
 371         _INIT_CMD(0xB6, 0x2E, 0x1B, 0x1A, 0x19, 0x18),
 372         _INIT_CMD(0xB7, 0x17, 0x16, 0x01, 0x3D, 0x3D),
 373         _INIT_CMD(0xB8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
 374         _INIT_CMD(0xB9, 0x3D, 0x32),
 375         _INIT_CMD(0xC0, 0x01, 0x32, 0x32, 0x09, 0x2F),
 376         _INIT_CMD(0xC1, 0x2E, 0x1A, 0x1B, 0x16, 0x17),
 377         _INIT_CMD(0xC2, 0x18, 0x19, 0x03, 0x3D, 0x3D),
 378         _INIT_CMD(0xC3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
 379         _INIT_CMD(0xC4, 0x3D, 0x32),
 380         _INIT_CMD(0xC5, 0x00, 0x32, 0x32, 0x08, 0x2F),
 381         _INIT_CMD(0xC6, 0x2E, 0x14, 0x15, 0x10, 0x11),
 382         _INIT_CMD(0xC7, 0x12, 0x13, 0x02, 0x3D, 0x3D),
 383         _INIT_CMD(0xC8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D),
 384         _INIT_CMD(0xC9, 0x3D, 0x32),
 385 
 386         {},
 387 };
 388 
 389 static const struct panel_desc innolux_p097pfg_panel_desc = {
 390         .mode = &innolux_p097pfg_mode,
 391         .bpc = 8,
 392         .size = {
 393                 .width = 147,
 394                 .height = 196,
 395         },
 396         .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
 397                  MIPI_DSI_MODE_LPM,
 398         .format = MIPI_DSI_FMT_RGB888,
 399         .init_cmds = innolux_p097pfg_init_cmds,
 400         .lanes = 4,
 401         .supply_names = innolux_p097pfg_supply_names,
 402         .num_supplies = ARRAY_SIZE(innolux_p097pfg_supply_names),
 403         .sleep_mode_delay = 100, /* T15 */
 404 };
 405 
 406 static int innolux_panel_get_modes(struct drm_panel *panel)
 407 {
 408         struct innolux_panel *innolux = to_innolux_panel(panel);
 409         const struct drm_display_mode *m = innolux->desc->mode;
 410         struct drm_display_mode *mode;
 411 
 412         mode = drm_mode_duplicate(panel->drm, m);
 413         if (!mode) {
 414                 DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
 415                               m->hdisplay, m->vdisplay, m->vrefresh);
 416                 return -ENOMEM;
 417         }
 418 
 419         drm_mode_set_name(mode);
 420 
 421         drm_mode_probed_add(panel->connector, mode);
 422 
 423         panel->connector->display_info.width_mm =
 424                         innolux->desc->size.width;
 425         panel->connector->display_info.height_mm =
 426                         innolux->desc->size.height;
 427         panel->connector->display_info.bpc = innolux->desc->bpc;
 428 
 429         return 1;
 430 }
 431 
 432 static const struct drm_panel_funcs innolux_panel_funcs = {
 433         .disable = innolux_panel_disable,
 434         .unprepare = innolux_panel_unprepare,
 435         .prepare = innolux_panel_prepare,
 436         .enable = innolux_panel_enable,
 437         .get_modes = innolux_panel_get_modes,
 438 };
 439 
 440 static const struct of_device_id innolux_of_match[] = {
 441         { .compatible = "innolux,p079zca",
 442           .data = &innolux_p079zca_panel_desc
 443         },
 444         { .compatible = "innolux,p097pfg",
 445           .data = &innolux_p097pfg_panel_desc
 446         },
 447         { }
 448 };
 449 MODULE_DEVICE_TABLE(of, innolux_of_match);
 450 
 451 static int innolux_panel_add(struct mipi_dsi_device *dsi,
 452                              const struct panel_desc *desc)
 453 {
 454         struct innolux_panel *innolux;
 455         struct device *dev = &dsi->dev;
 456         int err, i;
 457 
 458         innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL);
 459         if (!innolux)
 460                 return -ENOMEM;
 461 
 462         innolux->desc = desc;
 463 
 464         innolux->supplies = devm_kcalloc(dev, desc->num_supplies,
 465                                          sizeof(*innolux->supplies),
 466                                          GFP_KERNEL);
 467         if (!innolux->supplies)
 468                 return -ENOMEM;
 469 
 470         for (i = 0; i < desc->num_supplies; i++)
 471                 innolux->supplies[i].supply = desc->supply_names[i];
 472 
 473         err = devm_regulator_bulk_get(dev, desc->num_supplies,
 474                                       innolux->supplies);
 475         if (err < 0)
 476                 return err;
 477 
 478         innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
 479                                                        GPIOD_OUT_HIGH);
 480         if (IS_ERR(innolux->enable_gpio)) {
 481                 err = PTR_ERR(innolux->enable_gpio);
 482                 dev_dbg(dev, "failed to get enable gpio: %d\n", err);
 483                 innolux->enable_gpio = NULL;
 484         }
 485 
 486         innolux->backlight = devm_of_find_backlight(dev);
 487         if (IS_ERR(innolux->backlight))
 488                 return PTR_ERR(innolux->backlight);
 489 
 490         drm_panel_init(&innolux->base);
 491         innolux->base.funcs = &innolux_panel_funcs;
 492         innolux->base.dev = dev;
 493 
 494         err = drm_panel_add(&innolux->base);
 495         if (err < 0)
 496                 return err;
 497 
 498         mipi_dsi_set_drvdata(dsi, innolux);
 499         innolux->link = dsi;
 500 
 501         return 0;
 502 }
 503 
 504 static void innolux_panel_del(struct innolux_panel *innolux)
 505 {
 506         drm_panel_remove(&innolux->base);
 507 }
 508 
 509 static int innolux_panel_probe(struct mipi_dsi_device *dsi)
 510 {
 511         const struct panel_desc *desc;
 512         int err;
 513 
 514         desc = of_device_get_match_data(&dsi->dev);
 515         dsi->mode_flags = desc->flags;
 516         dsi->format = desc->format;
 517         dsi->lanes = desc->lanes;
 518 
 519         err = innolux_panel_add(dsi, desc);
 520         if (err < 0)
 521                 return err;
 522 
 523         return mipi_dsi_attach(dsi);
 524 }
 525 
 526 static int innolux_panel_remove(struct mipi_dsi_device *dsi)
 527 {
 528         struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
 529         int err;
 530 
 531         err = innolux_panel_unprepare(&innolux->base);
 532         if (err < 0)
 533                 DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
 534                               err);
 535 
 536         err = innolux_panel_disable(&innolux->base);
 537         if (err < 0)
 538                 DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
 539 
 540         err = mipi_dsi_detach(dsi);
 541         if (err < 0)
 542                 DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
 543                               err);
 544 
 545         innolux_panel_del(innolux);
 546 
 547         return 0;
 548 }
 549 
 550 static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
 551 {
 552         struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
 553 
 554         innolux_panel_unprepare(&innolux->base);
 555         innolux_panel_disable(&innolux->base);
 556 }
 557 
 558 static struct mipi_dsi_driver innolux_panel_driver = {
 559         .driver = {
 560                 .name = "panel-innolux-p079zca",
 561                 .of_match_table = innolux_of_match,
 562         },
 563         .probe = innolux_panel_probe,
 564         .remove = innolux_panel_remove,
 565         .shutdown = innolux_panel_shutdown,
 566 };
 567 module_mipi_dsi_driver(innolux_panel_driver);
 568 
 569 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 570 MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
 571 MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
 572 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */