root/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c

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

DEFINITIONS

This source file includes following definitions.
  1. panel_to_ili9881c
  2. ili9881c_switch_page
  3. ili9881c_send_cmd_data
  4. ili9881c_prepare
  5. ili9881c_enable
  6. ili9881c_disable
  7. ili9881c_unprepare
  8. ili9881c_get_modes
  9. ili9881c_dsi_probe
  10. ili9881c_dsi_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2017-2018, Bootlin
   4  */
   5 
   6 #include <linux/backlight.h>
   7 #include <linux/delay.h>
   8 #include <linux/device.h>
   9 #include <linux/err.h>
  10 #include <linux/errno.h>
  11 #include <linux/fb.h>
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 
  15 #include <linux/gpio/consumer.h>
  16 #include <linux/regulator/consumer.h>
  17 
  18 #include <drm/drm_mipi_dsi.h>
  19 #include <drm/drm_modes.h>
  20 #include <drm/drm_panel.h>
  21 
  22 #include <video/mipi_display.h>
  23 
  24 struct ili9881c {
  25         struct drm_panel        panel;
  26         struct mipi_dsi_device  *dsi;
  27 
  28         struct backlight_device *backlight;
  29         struct regulator        *power;
  30         struct gpio_desc        *reset;
  31 };
  32 
  33 enum ili9881c_op {
  34         ILI9881C_SWITCH_PAGE,
  35         ILI9881C_COMMAND,
  36 };
  37 
  38 struct ili9881c_instr {
  39         enum ili9881c_op        op;
  40 
  41         union arg {
  42                 struct cmd {
  43                         u8      cmd;
  44                         u8      data;
  45                 } cmd;
  46                 u8      page;
  47         } arg;
  48 };
  49 
  50 #define ILI9881C_SWITCH_PAGE_INSTR(_page)       \
  51         {                                       \
  52                 .op = ILI9881C_SWITCH_PAGE,     \
  53                 .arg = {                        \
  54                         .page = (_page),        \
  55                 },                              \
  56         }
  57 
  58 #define ILI9881C_COMMAND_INSTR(_cmd, _data)             \
  59         {                                               \
  60                 .op = ILI9881C_COMMAND,         \
  61                 .arg = {                                \
  62                         .cmd = {                        \
  63                                 .cmd = (_cmd),          \
  64                                 .data = (_data),        \
  65                         },                              \
  66                 },                                      \
  67         }
  68 
  69 static const struct ili9881c_instr ili9881c_init[] = {
  70         ILI9881C_SWITCH_PAGE_INSTR(3),
  71         ILI9881C_COMMAND_INSTR(0x01, 0x00),
  72         ILI9881C_COMMAND_INSTR(0x02, 0x00),
  73         ILI9881C_COMMAND_INSTR(0x03, 0x73),
  74         ILI9881C_COMMAND_INSTR(0x04, 0x03),
  75         ILI9881C_COMMAND_INSTR(0x05, 0x00),
  76         ILI9881C_COMMAND_INSTR(0x06, 0x06),
  77         ILI9881C_COMMAND_INSTR(0x07, 0x06),
  78         ILI9881C_COMMAND_INSTR(0x08, 0x00),
  79         ILI9881C_COMMAND_INSTR(0x09, 0x18),
  80         ILI9881C_COMMAND_INSTR(0x0a, 0x04),
  81         ILI9881C_COMMAND_INSTR(0x0b, 0x00),
  82         ILI9881C_COMMAND_INSTR(0x0c, 0x02),
  83         ILI9881C_COMMAND_INSTR(0x0d, 0x03),
  84         ILI9881C_COMMAND_INSTR(0x0e, 0x00),
  85         ILI9881C_COMMAND_INSTR(0x0f, 0x25),
  86         ILI9881C_COMMAND_INSTR(0x10, 0x25),
  87         ILI9881C_COMMAND_INSTR(0x11, 0x00),
  88         ILI9881C_COMMAND_INSTR(0x12, 0x00),
  89         ILI9881C_COMMAND_INSTR(0x13, 0x00),
  90         ILI9881C_COMMAND_INSTR(0x14, 0x00),
  91         ILI9881C_COMMAND_INSTR(0x15, 0x00),
  92         ILI9881C_COMMAND_INSTR(0x16, 0x0C),
  93         ILI9881C_COMMAND_INSTR(0x17, 0x00),
  94         ILI9881C_COMMAND_INSTR(0x18, 0x00),
  95         ILI9881C_COMMAND_INSTR(0x19, 0x00),
  96         ILI9881C_COMMAND_INSTR(0x1a, 0x00),
  97         ILI9881C_COMMAND_INSTR(0x1b, 0x00),
  98         ILI9881C_COMMAND_INSTR(0x1c, 0x00),
  99         ILI9881C_COMMAND_INSTR(0x1d, 0x00),
 100         ILI9881C_COMMAND_INSTR(0x1e, 0xC0),
 101         ILI9881C_COMMAND_INSTR(0x1f, 0x80),
 102         ILI9881C_COMMAND_INSTR(0x20, 0x04),
 103         ILI9881C_COMMAND_INSTR(0x21, 0x01),
 104         ILI9881C_COMMAND_INSTR(0x22, 0x00),
 105         ILI9881C_COMMAND_INSTR(0x23, 0x00),
 106         ILI9881C_COMMAND_INSTR(0x24, 0x00),
 107         ILI9881C_COMMAND_INSTR(0x25, 0x00),
 108         ILI9881C_COMMAND_INSTR(0x26, 0x00),
 109         ILI9881C_COMMAND_INSTR(0x27, 0x00),
 110         ILI9881C_COMMAND_INSTR(0x28, 0x33),
 111         ILI9881C_COMMAND_INSTR(0x29, 0x03),
 112         ILI9881C_COMMAND_INSTR(0x2a, 0x00),
 113         ILI9881C_COMMAND_INSTR(0x2b, 0x00),
 114         ILI9881C_COMMAND_INSTR(0x2c, 0x00),
 115         ILI9881C_COMMAND_INSTR(0x2d, 0x00),
 116         ILI9881C_COMMAND_INSTR(0x2e, 0x00),
 117         ILI9881C_COMMAND_INSTR(0x2f, 0x00),
 118         ILI9881C_COMMAND_INSTR(0x30, 0x00),
 119         ILI9881C_COMMAND_INSTR(0x31, 0x00),
 120         ILI9881C_COMMAND_INSTR(0x32, 0x00),
 121         ILI9881C_COMMAND_INSTR(0x33, 0x00),
 122         ILI9881C_COMMAND_INSTR(0x34, 0x04),
 123         ILI9881C_COMMAND_INSTR(0x35, 0x00),
 124         ILI9881C_COMMAND_INSTR(0x36, 0x00),
 125         ILI9881C_COMMAND_INSTR(0x37, 0x00),
 126         ILI9881C_COMMAND_INSTR(0x38, 0x3C),
 127         ILI9881C_COMMAND_INSTR(0x39, 0x00),
 128         ILI9881C_COMMAND_INSTR(0x3a, 0x00),
 129         ILI9881C_COMMAND_INSTR(0x3b, 0x00),
 130         ILI9881C_COMMAND_INSTR(0x3c, 0x00),
 131         ILI9881C_COMMAND_INSTR(0x3d, 0x00),
 132         ILI9881C_COMMAND_INSTR(0x3e, 0x00),
 133         ILI9881C_COMMAND_INSTR(0x3f, 0x00),
 134         ILI9881C_COMMAND_INSTR(0x40, 0x00),
 135         ILI9881C_COMMAND_INSTR(0x41, 0x00),
 136         ILI9881C_COMMAND_INSTR(0x42, 0x00),
 137         ILI9881C_COMMAND_INSTR(0x43, 0x00),
 138         ILI9881C_COMMAND_INSTR(0x44, 0x00),
 139         ILI9881C_COMMAND_INSTR(0x50, 0x01),
 140         ILI9881C_COMMAND_INSTR(0x51, 0x23),
 141         ILI9881C_COMMAND_INSTR(0x52, 0x45),
 142         ILI9881C_COMMAND_INSTR(0x53, 0x67),
 143         ILI9881C_COMMAND_INSTR(0x54, 0x89),
 144         ILI9881C_COMMAND_INSTR(0x55, 0xab),
 145         ILI9881C_COMMAND_INSTR(0x56, 0x01),
 146         ILI9881C_COMMAND_INSTR(0x57, 0x23),
 147         ILI9881C_COMMAND_INSTR(0x58, 0x45),
 148         ILI9881C_COMMAND_INSTR(0x59, 0x67),
 149         ILI9881C_COMMAND_INSTR(0x5a, 0x89),
 150         ILI9881C_COMMAND_INSTR(0x5b, 0xab),
 151         ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
 152         ILI9881C_COMMAND_INSTR(0x5d, 0xef),
 153         ILI9881C_COMMAND_INSTR(0x5e, 0x11),
 154         ILI9881C_COMMAND_INSTR(0x5f, 0x02),
 155         ILI9881C_COMMAND_INSTR(0x60, 0x02),
 156         ILI9881C_COMMAND_INSTR(0x61, 0x02),
 157         ILI9881C_COMMAND_INSTR(0x62, 0x02),
 158         ILI9881C_COMMAND_INSTR(0x63, 0x02),
 159         ILI9881C_COMMAND_INSTR(0x64, 0x02),
 160         ILI9881C_COMMAND_INSTR(0x65, 0x02),
 161         ILI9881C_COMMAND_INSTR(0x66, 0x02),
 162         ILI9881C_COMMAND_INSTR(0x67, 0x02),
 163         ILI9881C_COMMAND_INSTR(0x68, 0x02),
 164         ILI9881C_COMMAND_INSTR(0x69, 0x02),
 165         ILI9881C_COMMAND_INSTR(0x6a, 0x0C),
 166         ILI9881C_COMMAND_INSTR(0x6b, 0x02),
 167         ILI9881C_COMMAND_INSTR(0x6c, 0x0F),
 168         ILI9881C_COMMAND_INSTR(0x6d, 0x0E),
 169         ILI9881C_COMMAND_INSTR(0x6e, 0x0D),
 170         ILI9881C_COMMAND_INSTR(0x6f, 0x06),
 171         ILI9881C_COMMAND_INSTR(0x70, 0x07),
 172         ILI9881C_COMMAND_INSTR(0x71, 0x02),
 173         ILI9881C_COMMAND_INSTR(0x72, 0x02),
 174         ILI9881C_COMMAND_INSTR(0x73, 0x02),
 175         ILI9881C_COMMAND_INSTR(0x74, 0x02),
 176         ILI9881C_COMMAND_INSTR(0x75, 0x02),
 177         ILI9881C_COMMAND_INSTR(0x76, 0x02),
 178         ILI9881C_COMMAND_INSTR(0x77, 0x02),
 179         ILI9881C_COMMAND_INSTR(0x78, 0x02),
 180         ILI9881C_COMMAND_INSTR(0x79, 0x02),
 181         ILI9881C_COMMAND_INSTR(0x7a, 0x02),
 182         ILI9881C_COMMAND_INSTR(0x7b, 0x02),
 183         ILI9881C_COMMAND_INSTR(0x7c, 0x02),
 184         ILI9881C_COMMAND_INSTR(0x7d, 0x02),
 185         ILI9881C_COMMAND_INSTR(0x7e, 0x02),
 186         ILI9881C_COMMAND_INSTR(0x7f, 0x02),
 187         ILI9881C_COMMAND_INSTR(0x80, 0x0C),
 188         ILI9881C_COMMAND_INSTR(0x81, 0x02),
 189         ILI9881C_COMMAND_INSTR(0x82, 0x0F),
 190         ILI9881C_COMMAND_INSTR(0x83, 0x0E),
 191         ILI9881C_COMMAND_INSTR(0x84, 0x0D),
 192         ILI9881C_COMMAND_INSTR(0x85, 0x06),
 193         ILI9881C_COMMAND_INSTR(0x86, 0x07),
 194         ILI9881C_COMMAND_INSTR(0x87, 0x02),
 195         ILI9881C_COMMAND_INSTR(0x88, 0x02),
 196         ILI9881C_COMMAND_INSTR(0x89, 0x02),
 197         ILI9881C_COMMAND_INSTR(0x8A, 0x02),
 198         ILI9881C_SWITCH_PAGE_INSTR(4),
 199         ILI9881C_COMMAND_INSTR(0x6C, 0x15),
 200         ILI9881C_COMMAND_INSTR(0x6E, 0x22),
 201         ILI9881C_COMMAND_INSTR(0x6F, 0x33),
 202         ILI9881C_COMMAND_INSTR(0x3A, 0xA4),
 203         ILI9881C_COMMAND_INSTR(0x8D, 0x0D),
 204         ILI9881C_COMMAND_INSTR(0x87, 0xBA),
 205         ILI9881C_COMMAND_INSTR(0x26, 0x76),
 206         ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
 207         ILI9881C_SWITCH_PAGE_INSTR(1),
 208         ILI9881C_COMMAND_INSTR(0x22, 0x0A),
 209         ILI9881C_COMMAND_INSTR(0x53, 0xDC),
 210         ILI9881C_COMMAND_INSTR(0x55, 0xA7),
 211         ILI9881C_COMMAND_INSTR(0x50, 0x78),
 212         ILI9881C_COMMAND_INSTR(0x51, 0x78),
 213         ILI9881C_COMMAND_INSTR(0x31, 0x02),
 214         ILI9881C_COMMAND_INSTR(0x60, 0x14),
 215         ILI9881C_COMMAND_INSTR(0xA0, 0x2A),
 216         ILI9881C_COMMAND_INSTR(0xA1, 0x39),
 217         ILI9881C_COMMAND_INSTR(0xA2, 0x46),
 218         ILI9881C_COMMAND_INSTR(0xA3, 0x0e),
 219         ILI9881C_COMMAND_INSTR(0xA4, 0x12),
 220         ILI9881C_COMMAND_INSTR(0xA5, 0x25),
 221         ILI9881C_COMMAND_INSTR(0xA6, 0x19),
 222         ILI9881C_COMMAND_INSTR(0xA7, 0x1d),
 223         ILI9881C_COMMAND_INSTR(0xA8, 0xa6),
 224         ILI9881C_COMMAND_INSTR(0xA9, 0x1C),
 225         ILI9881C_COMMAND_INSTR(0xAA, 0x29),
 226         ILI9881C_COMMAND_INSTR(0xAB, 0x85),
 227         ILI9881C_COMMAND_INSTR(0xAC, 0x1C),
 228         ILI9881C_COMMAND_INSTR(0xAD, 0x1B),
 229         ILI9881C_COMMAND_INSTR(0xAE, 0x51),
 230         ILI9881C_COMMAND_INSTR(0xAF, 0x22),
 231         ILI9881C_COMMAND_INSTR(0xB0, 0x2d),
 232         ILI9881C_COMMAND_INSTR(0xB1, 0x4f),
 233         ILI9881C_COMMAND_INSTR(0xB2, 0x59),
 234         ILI9881C_COMMAND_INSTR(0xB3, 0x3F),
 235         ILI9881C_COMMAND_INSTR(0xC0, 0x2A),
 236         ILI9881C_COMMAND_INSTR(0xC1, 0x3a),
 237         ILI9881C_COMMAND_INSTR(0xC2, 0x45),
 238         ILI9881C_COMMAND_INSTR(0xC3, 0x0e),
 239         ILI9881C_COMMAND_INSTR(0xC4, 0x11),
 240         ILI9881C_COMMAND_INSTR(0xC5, 0x24),
 241         ILI9881C_COMMAND_INSTR(0xC6, 0x1a),
 242         ILI9881C_COMMAND_INSTR(0xC7, 0x1c),
 243         ILI9881C_COMMAND_INSTR(0xC8, 0xaa),
 244         ILI9881C_COMMAND_INSTR(0xC9, 0x1C),
 245         ILI9881C_COMMAND_INSTR(0xCA, 0x29),
 246         ILI9881C_COMMAND_INSTR(0xCB, 0x96),
 247         ILI9881C_COMMAND_INSTR(0xCC, 0x1C),
 248         ILI9881C_COMMAND_INSTR(0xCD, 0x1B),
 249         ILI9881C_COMMAND_INSTR(0xCE, 0x51),
 250         ILI9881C_COMMAND_INSTR(0xCF, 0x22),
 251         ILI9881C_COMMAND_INSTR(0xD0, 0x2b),
 252         ILI9881C_COMMAND_INSTR(0xD1, 0x4b),
 253         ILI9881C_COMMAND_INSTR(0xD2, 0x59),
 254         ILI9881C_COMMAND_INSTR(0xD3, 0x3F),
 255 };
 256 
 257 static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel)
 258 {
 259         return container_of(panel, struct ili9881c, panel);
 260 }
 261 
 262 /*
 263  * The panel seems to accept some private DCS commands that map
 264  * directly to registers.
 265  *
 266  * It is organised by page, with each page having its own set of
 267  * registers, and the first page looks like it's holding the standard
 268  * DCS commands.
 269  *
 270  * So before any attempt at sending a command or data, we have to be
 271  * sure if we're in the right page or not.
 272  */
 273 static int ili9881c_switch_page(struct ili9881c *ctx, u8 page)
 274 {
 275         u8 buf[4] = { 0xff, 0x98, 0x81, page };
 276         int ret;
 277 
 278         ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
 279         if (ret < 0)
 280                 return ret;
 281 
 282         return 0;
 283 }
 284 
 285 static int ili9881c_send_cmd_data(struct ili9881c *ctx, u8 cmd, u8 data)
 286 {
 287         u8 buf[2] = { cmd, data };
 288         int ret;
 289 
 290         ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
 291         if (ret < 0)
 292                 return ret;
 293 
 294         return 0;
 295 }
 296 
 297 static int ili9881c_prepare(struct drm_panel *panel)
 298 {
 299         struct ili9881c *ctx = panel_to_ili9881c(panel);
 300         unsigned int i;
 301         int ret;
 302 
 303         /* Power the panel */
 304         ret = regulator_enable(ctx->power);
 305         if (ret)
 306                 return ret;
 307         msleep(5);
 308 
 309         /* And reset it */
 310         gpiod_set_value(ctx->reset, 1);
 311         msleep(20);
 312 
 313         gpiod_set_value(ctx->reset, 0);
 314         msleep(20);
 315 
 316         for (i = 0; i < ARRAY_SIZE(ili9881c_init); i++) {
 317                 const struct ili9881c_instr *instr = &ili9881c_init[i];
 318 
 319                 if (instr->op == ILI9881C_SWITCH_PAGE)
 320                         ret = ili9881c_switch_page(ctx, instr->arg.page);
 321                 else if (instr->op == ILI9881C_COMMAND)
 322                         ret = ili9881c_send_cmd_data(ctx, instr->arg.cmd.cmd,
 323                                                       instr->arg.cmd.data);
 324 
 325                 if (ret)
 326                         return ret;
 327         }
 328 
 329         ret = ili9881c_switch_page(ctx, 0);
 330         if (ret)
 331                 return ret;
 332 
 333         ret = mipi_dsi_dcs_set_tear_on(ctx->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
 334         if (ret)
 335                 return ret;
 336 
 337         ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
 338         if (ret)
 339                 return ret;
 340 
 341         return 0;
 342 }
 343 
 344 static int ili9881c_enable(struct drm_panel *panel)
 345 {
 346         struct ili9881c *ctx = panel_to_ili9881c(panel);
 347 
 348         msleep(120);
 349 
 350         mipi_dsi_dcs_set_display_on(ctx->dsi);
 351         backlight_enable(ctx->backlight);
 352 
 353         return 0;
 354 }
 355 
 356 static int ili9881c_disable(struct drm_panel *panel)
 357 {
 358         struct ili9881c *ctx = panel_to_ili9881c(panel);
 359 
 360         backlight_disable(ctx->backlight);
 361         return mipi_dsi_dcs_set_display_off(ctx->dsi);
 362 }
 363 
 364 static int ili9881c_unprepare(struct drm_panel *panel)
 365 {
 366         struct ili9881c *ctx = panel_to_ili9881c(panel);
 367 
 368         mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
 369         regulator_disable(ctx->power);
 370         gpiod_set_value(ctx->reset, 1);
 371 
 372         return 0;
 373 }
 374 
 375 static const struct drm_display_mode bananapi_default_mode = {
 376         .clock          = 62000,
 377         .vrefresh       = 60,
 378 
 379         .hdisplay       = 720,
 380         .hsync_start    = 720 + 10,
 381         .hsync_end      = 720 + 10 + 20,
 382         .htotal         = 720 + 10 + 20 + 30,
 383 
 384         .vdisplay       = 1280,
 385         .vsync_start    = 1280 + 10,
 386         .vsync_end      = 1280 + 10 + 10,
 387         .vtotal         = 1280 + 10 + 10 + 20,
 388 };
 389 
 390 static int ili9881c_get_modes(struct drm_panel *panel)
 391 {
 392         struct drm_connector *connector = panel->connector;
 393         struct ili9881c *ctx = panel_to_ili9881c(panel);
 394         struct drm_display_mode *mode;
 395 
 396         mode = drm_mode_duplicate(panel->drm, &bananapi_default_mode);
 397         if (!mode) {
 398                 dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
 399                         bananapi_default_mode.hdisplay,
 400                         bananapi_default_mode.vdisplay,
 401                         bananapi_default_mode.vrefresh);
 402                 return -ENOMEM;
 403         }
 404 
 405         drm_mode_set_name(mode);
 406 
 407         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 408         drm_mode_probed_add(connector, mode);
 409 
 410         panel->connector->display_info.width_mm = 62;
 411         panel->connector->display_info.height_mm = 110;
 412 
 413         return 1;
 414 }
 415 
 416 static const struct drm_panel_funcs ili9881c_funcs = {
 417         .prepare        = ili9881c_prepare,
 418         .unprepare      = ili9881c_unprepare,
 419         .enable         = ili9881c_enable,
 420         .disable        = ili9881c_disable,
 421         .get_modes      = ili9881c_get_modes,
 422 };
 423 
 424 static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
 425 {
 426         struct device_node *np;
 427         struct ili9881c *ctx;
 428         int ret;
 429 
 430         ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
 431         if (!ctx)
 432                 return -ENOMEM;
 433         mipi_dsi_set_drvdata(dsi, ctx);
 434         ctx->dsi = dsi;
 435 
 436         drm_panel_init(&ctx->panel);
 437         ctx->panel.dev = &dsi->dev;
 438         ctx->panel.funcs = &ili9881c_funcs;
 439 
 440         ctx->power = devm_regulator_get(&dsi->dev, "power");
 441         if (IS_ERR(ctx->power)) {
 442                 dev_err(&dsi->dev, "Couldn't get our power regulator\n");
 443                 return PTR_ERR(ctx->power);
 444         }
 445 
 446         ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
 447         if (IS_ERR(ctx->reset)) {
 448                 dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
 449                 return PTR_ERR(ctx->reset);
 450         }
 451 
 452         np = of_parse_phandle(dsi->dev.of_node, "backlight", 0);
 453         if (np) {
 454                 ctx->backlight = of_find_backlight_by_node(np);
 455                 of_node_put(np);
 456 
 457                 if (!ctx->backlight)
 458                         return -EPROBE_DEFER;
 459         }
 460 
 461         ret = drm_panel_add(&ctx->panel);
 462         if (ret < 0)
 463                 return ret;
 464 
 465         dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
 466         dsi->format = MIPI_DSI_FMT_RGB888;
 467         dsi->lanes = 4;
 468 
 469         return mipi_dsi_attach(dsi);
 470 }
 471 
 472 static int ili9881c_dsi_remove(struct mipi_dsi_device *dsi)
 473 {
 474         struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi);
 475 
 476         mipi_dsi_detach(dsi);
 477         drm_panel_remove(&ctx->panel);
 478 
 479         if (ctx->backlight)
 480                 put_device(&ctx->backlight->dev);
 481 
 482         return 0;
 483 }
 484 
 485 static const struct of_device_id ili9881c_of_match[] = {
 486         { .compatible = "bananapi,lhr050h41" },
 487         { }
 488 };
 489 MODULE_DEVICE_TABLE(of, ili9881c_of_match);
 490 
 491 static struct mipi_dsi_driver ili9881c_dsi_driver = {
 492         .probe          = ili9881c_dsi_probe,
 493         .remove         = ili9881c_dsi_remove,
 494         .driver = {
 495                 .name           = "ili9881c-dsi",
 496                 .of_match_table = ili9881c_of_match,
 497         },
 498 };
 499 module_mipi_dsi_driver(ili9881c_dsi_driver);
 500 
 501 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 502 MODULE_DESCRIPTION("Ilitek ILI9881C Controller Driver");
 503 MODULE_LICENSE("GPL v2");

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