1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * DRM driver for Ilitek ILI9225 panels 4 * 5 * Copyright 2017 David Lechner <david@lechnology.com> 6 * 7 * Some code copied from mipi-dbi.c 8 * Copyright 2016 Noralf Trønnes 9 */ 10 11 #include <linux/delay.h> 12 #include <linux/dma-buf.h> 13 #include <linux/gpio/consumer.h> 14 #include <linux/module.h> 15 #include <linux/property.h> 16 #include <linux/spi/spi.h> 17 #include <video/mipi_display.h> 18 19 #include <drm/clients/drm_client_setup.h> 20 #include <drm/drm_atomic.h> 21 #include <drm/drm_atomic_helper.h> 22 #include <drm/drm_damage_helper.h> 23 #include <drm/drm_drv.h> 24 #include <drm/drm_fb_dma_helper.h> 25 #include <drm/drm_fbdev_dma.h> 26 #include <drm/drm_fourcc.h> 27 #include <drm/drm_framebuffer.h> 28 #include <drm/drm_gem_dma_helper.h> 29 #include <drm/drm_managed.h> 30 #include <drm/drm_mipi_dbi.h> 31 #include <drm/drm_print.h> 32 #include <drm/drm_rect.h> 33 34 #define ILI9225_DRIVER_READ_CODE 0x00 35 #define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 36 #define ILI9225_LCD_AC_DRIVING_CONTROL 0x02 37 #define ILI9225_ENTRY_MODE 0x03 38 #define ILI9225_DISPLAY_CONTROL_1 0x07 39 #define ILI9225_BLANK_PERIOD_CONTROL_1 0x08 40 #define ILI9225_FRAME_CYCLE_CONTROL 0x0b 41 #define ILI9225_INTERFACE_CONTROL 0x0c 42 #define ILI9225_OSCILLATION_CONTROL 0x0f 43 #define ILI9225_POWER_CONTROL_1 0x10 44 #define ILI9225_POWER_CONTROL_2 0x11 45 #define ILI9225_POWER_CONTROL_3 0x12 46 #define ILI9225_POWER_CONTROL_4 0x13 47 #define ILI9225_POWER_CONTROL_5 0x14 48 #define ILI9225_VCI_RECYCLING 0x15 49 #define ILI9225_RAM_ADDRESS_SET_1 0x20 50 #define ILI9225_RAM_ADDRESS_SET_2 0x21 51 #define ILI9225_WRITE_DATA_TO_GRAM 0x22 52 #define ILI9225_SOFTWARE_RESET 0x28 53 #define ILI9225_GATE_SCAN_CONTROL 0x30 54 #define ILI9225_VERTICAL_SCROLL_1 0x31 55 #define ILI9225_VERTICAL_SCROLL_2 0x32 56 #define ILI9225_VERTICAL_SCROLL_3 0x33 57 #define ILI9225_PARTIAL_DRIVING_POS_1 0x34 58 #define ILI9225_PARTIAL_DRIVING_POS_2 0x35 59 #define ILI9225_HORIZ_WINDOW_ADDR_1 0x36 60 #define ILI9225_HORIZ_WINDOW_ADDR_2 0x37 61 #define ILI9225_VERT_WINDOW_ADDR_1 0x38 62 #define ILI9225_VERT_WINDOW_ADDR_2 0x39 63 #define ILI9225_GAMMA_CONTROL_1 0x50 64 #define ILI9225_GAMMA_CONTROL_2 0x51 65 #define ILI9225_GAMMA_CONTROL_3 0x52 66 #define ILI9225_GAMMA_CONTROL_4 0x53 67 #define ILI9225_GAMMA_CONTROL_5 0x54 68 #define ILI9225_GAMMA_CONTROL_6 0x55 69 #define ILI9225_GAMMA_CONTROL_7 0x56 70 #define ILI9225_GAMMA_CONTROL_8 0x57 71 #define ILI9225_GAMMA_CONTROL_9 0x58 72 #define ILI9225_GAMMA_CONTROL_10 0x59 73 74 struct ili9225_device { 75 struct mipi_dbi_dev dbidev; 76 77 struct drm_plane plane; 78 struct drm_crtc crtc; 79 struct drm_encoder encoder; 80 struct drm_connector connector; 81 }; 82 83 static struct ili9225_device *to_ili9225_device(struct drm_device *dev) 84 { 85 return container_of(drm_to_mipi_dbi_dev(dev), struct ili9225_device, dbidev); 86 } 87 88 static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) 89 { 90 u8 par[2] = { data >> 8, data & 0xff }; 91 92 return mipi_dbi_command_buf(dbi, cmd, par, 2); 93 } 94 95 static void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, 96 struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) 97 { 98 struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); 99 unsigned int height = rect->y2 - rect->y1; 100 unsigned int width = rect->x2 - rect->x1; 101 struct mipi_dbi *dbi = &dbidev->dbi; 102 bool swap = dbi->swap_bytes; 103 u16 x_start, y_start; 104 u16 x1, x2, y1, y2; 105 int ret = 0; 106 bool full; 107 void *tr; 108 109 full = width == fb->width && height == fb->height; 110 111 DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); 112 113 if (!dbi->dc || !full || swap || 114 fb->format->format == DRM_FORMAT_XRGB8888) { 115 tr = dbidev->tx_buf; 116 ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap, fmtcnv_state); 117 if (ret) 118 goto err_msg; 119 } else { 120 tr = src->vaddr; /* TODO: Use mapping abstraction properly */ 121 } 122 123 switch (dbidev->rotation) { 124 default: 125 x1 = rect->x1; 126 x2 = rect->x2 - 1; 127 y1 = rect->y1; 128 y2 = rect->y2 - 1; 129 x_start = x1; 130 y_start = y1; 131 break; 132 case 90: 133 x1 = rect->y1; 134 x2 = rect->y2 - 1; 135 y1 = fb->width - rect->x2; 136 y2 = fb->width - rect->x1 - 1; 137 x_start = x1; 138 y_start = y2; 139 break; 140 case 180: 141 x1 = fb->width - rect->x2; 142 x2 = fb->width - rect->x1 - 1; 143 y1 = fb->height - rect->y2; 144 y2 = fb->height - rect->y1 - 1; 145 x_start = x2; 146 y_start = y2; 147 break; 148 case 270: 149 x1 = fb->height - rect->y2; 150 x2 = fb->height - rect->y1 - 1; 151 y1 = rect->x1; 152 y2 = rect->x2 - 1; 153 x_start = x2; 154 y_start = y1; 155 break; 156 } 157 158 ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2); 159 ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1); 160 ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2); 161 ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1); 162 163 ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start); 164 ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start); 165 166 ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr, 167 width * height * 2); 168 err_msg: 169 if (ret) 170 dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); 171 } 172 173 static const u32 ili9225_plane_formats[] = { 174 DRM_MIPI_DBI_PLANE_FORMATS, 175 }; 176 177 static const u64 ili9225_plane_format_modifiers[] = { 178 DRM_MIPI_DBI_PLANE_FORMAT_MODIFIERS, 179 }; 180 181 static void ili9225_plane_helper_atomic_update(struct drm_plane *plane, 182 struct drm_atomic_state *state) 183 { 184 struct drm_device *drm = plane->dev; 185 struct drm_plane_state *plane_state = plane->state; 186 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 187 struct drm_framebuffer *fb = plane_state->fb; 188 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 189 struct drm_rect rect; 190 int idx; 191 192 if (!plane_state->fb) 193 return; 194 195 if (!drm_dev_enter(drm, &idx)) 196 return; 197 198 if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &rect)) 199 ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect, 200 &shadow_plane_state->fmtcnv_state); 201 202 drm_dev_exit(idx); 203 } 204 205 static const struct drm_plane_helper_funcs ili9225_plane_helper_funcs = { 206 DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 207 .atomic_check = drm_mipi_dbi_plane_helper_atomic_check, 208 .atomic_update = ili9225_plane_helper_atomic_update, 209 }; 210 211 static const struct drm_plane_funcs ili9225_plane_funcs = { 212 DRM_MIPI_DBI_PLANE_FUNCS, 213 .destroy = drm_plane_cleanup, 214 }; 215 216 static void ili9225_crtc_helper_atomic_enable(struct drm_crtc *crtc, 217 struct drm_atomic_state *state) 218 { 219 struct drm_device *drm = crtc->dev; 220 struct ili9225_device *ili9225 = to_ili9225_device(drm); 221 struct mipi_dbi_dev *dbidev = &ili9225->dbidev; 222 struct device *dev = drm->dev; 223 struct mipi_dbi *dbi = &dbidev->dbi; 224 int ret, idx; 225 u8 am_id; 226 227 if (!drm_dev_enter(drm, &idx)) 228 return; 229 230 DRM_DEBUG_KMS("\n"); 231 232 mipi_dbi_hw_reset(dbi); 233 234 /* 235 * There don't seem to be two example init sequences that match, so 236 * using the one from the popular Arduino library for this display. 237 * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp 238 */ 239 240 ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000); 241 if (ret) { 242 DRM_DEV_ERROR(dev, "Error sending command %d\n", ret); 243 goto out_exit; 244 } 245 ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000); 246 ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000); 247 ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000); 248 ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000); 249 250 msleep(40); 251 252 ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018); 253 ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121); 254 ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f); 255 ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f); 256 ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800); 257 258 msleep(10); 259 260 ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b); 261 262 msleep(50); 263 264 switch (dbidev->rotation) { 265 default: 266 am_id = 0x30; 267 break; 268 case 90: 269 am_id = 0x18; 270 break; 271 case 180: 272 am_id = 0x00; 273 break; 274 case 270: 275 am_id = 0x28; 276 break; 277 } 278 ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c); 279 ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100); 280 ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id); 281 ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 282 ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808); 283 ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100); 284 ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000); 285 ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01); 286 ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020); 287 ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000); 288 ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000); 289 290 ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000); 291 ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db); 292 ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000); 293 ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000); 294 ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db); 295 ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000); 296 297 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000); 298 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808); 299 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a); 300 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a); 301 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08); 302 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808); 303 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000); 304 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00); 305 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710); 306 ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710); 307 308 ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012); 309 310 msleep(50); 311 312 ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); 313 314 out_exit: 315 drm_dev_exit(idx); 316 } 317 318 static void ili9225_crtc_helper_atomic_disable(struct drm_crtc *crtc, 319 struct drm_atomic_state *state) 320 { 321 struct drm_device *drm = crtc->dev; 322 struct ili9225_device *ili9225 = to_ili9225_device(drm); 323 struct mipi_dbi_dev *dbidev = &ili9225->dbidev; 324 struct mipi_dbi *dbi = &dbidev->dbi; 325 326 DRM_DEBUG_KMS("\n"); 327 328 /* 329 * This callback is not protected by drm_dev_enter/exit since we want to 330 * turn off the display on regular driver unload. It's highly unlikely 331 * that the underlying SPI controller is gone should this be called after 332 * unplug. 333 */ 334 335 ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 336 msleep(50); 337 ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007); 338 msleep(50); 339 ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02); 340 } 341 342 static const struct drm_crtc_helper_funcs ili9225_crtc_helper_funcs = { 343 .mode_valid = drm_mipi_dbi_crtc_helper_mode_valid, 344 .atomic_check = drm_mipi_dbi_crtc_helper_atomic_check, 345 .atomic_enable = ili9225_crtc_helper_atomic_enable, 346 .atomic_disable = ili9225_crtc_helper_atomic_disable, 347 }; 348 349 static const struct drm_crtc_funcs ili9225_crtc_funcs = { 350 DRM_MIPI_DBI_CRTC_FUNCS, 351 .destroy = drm_crtc_cleanup, 352 }; 353 354 static const struct drm_encoder_funcs ili9225_encoder_funcs = { 355 .destroy = drm_encoder_cleanup, 356 }; 357 358 static const struct drm_connector_helper_funcs ili9225_connector_helper_funcs = { 359 DRM_MIPI_DBI_CONNECTOR_HELPER_FUNCS, 360 }; 361 362 static const struct drm_connector_funcs ili9225_connector_funcs = { 363 DRM_MIPI_DBI_CONNECTOR_FUNCS, 364 .destroy = drm_connector_cleanup, 365 }; 366 367 static const struct drm_mode_config_helper_funcs ili9225_mode_config_helper_funcs = { 368 DRM_MIPI_DBI_MODE_CONFIG_HELPER_FUNCS, 369 }; 370 371 static const struct drm_mode_config_funcs ili9225_mode_config_funcs = { 372 DRM_MIPI_DBI_MODE_CONFIG_FUNCS, 373 }; 374 375 static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, 376 size_t num) 377 { 378 struct spi_device *spi = dbi->spi; 379 unsigned int bpw = 8; 380 u32 speed_hz; 381 int ret; 382 383 spi_bus_lock(spi->controller); 384 gpiod_set_value_cansleep(dbi->dc, 0); 385 speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); 386 ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); 387 spi_bus_unlock(spi->controller); 388 if (ret || !num) 389 return ret; 390 391 if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes) 392 bpw = 16; 393 394 spi_bus_lock(spi->controller); 395 gpiod_set_value_cansleep(dbi->dc, 1); 396 speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); 397 ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); 398 spi_bus_unlock(spi->controller); 399 400 return ret; 401 } 402 403 static const struct drm_display_mode ili9225_mode = { 404 DRM_SIMPLE_MODE(176, 220, 35, 44), 405 }; 406 407 DEFINE_DRM_GEM_DMA_FOPS(ili9225_fops); 408 409 static const struct drm_driver ili9225_driver = { 410 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 411 .fops = &ili9225_fops, 412 DRM_GEM_DMA_DRIVER_OPS_VMAP, 413 DRM_FBDEV_DMA_DRIVER_OPS, 414 .name = "ili9225", 415 .desc = "Ilitek ILI9225", 416 .major = 1, 417 .minor = 0, 418 }; 419 420 static const struct of_device_id ili9225_of_match[] = { 421 { .compatible = "vot,v220hf01a-t" }, 422 {}, 423 }; 424 MODULE_DEVICE_TABLE(of, ili9225_of_match); 425 426 static const struct spi_device_id ili9225_id[] = { 427 { "v220hf01a-t", 0 }, 428 { }, 429 }; 430 MODULE_DEVICE_TABLE(spi, ili9225_id); 431 432 static int ili9225_probe(struct spi_device *spi) 433 { 434 struct device *dev = &spi->dev; 435 struct ili9225_device *ili9225; 436 struct mipi_dbi_dev *dbidev; 437 struct drm_device *drm; 438 struct mipi_dbi *dbi; 439 struct gpio_desc *rs; 440 struct drm_plane *plane; 441 struct drm_crtc *crtc; 442 struct drm_encoder *encoder; 443 struct drm_connector *connector; 444 u32 rotation = 0; 445 int ret; 446 447 ili9225 = devm_drm_dev_alloc(dev, &ili9225_driver, struct ili9225_device, dbidev.drm); 448 if (IS_ERR(ili9225)) 449 return PTR_ERR(ili9225); 450 dbidev = &ili9225->dbidev; 451 dbi = &dbidev->dbi; 452 drm = &dbidev->drm; 453 454 dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 455 if (IS_ERR(dbi->reset)) 456 return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n"); 457 458 rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW); 459 if (IS_ERR(rs)) 460 return dev_err_probe(dev, PTR_ERR(rs), "Failed to get GPIO 'rs'\n"); 461 462 device_property_read_u32(dev, "rotation", &rotation); 463 464 ret = mipi_dbi_spi_init(spi, dbi, rs); 465 if (ret) 466 return ret; 467 468 /* override the command function set in mipi_dbi_spi_init() */ 469 dbi->command = ili9225_dbi_command; 470 471 ret = drm_mipi_dbi_dev_init(dbidev, &ili9225_mode, ili9225_plane_formats[0], 472 rotation, 0); 473 if (ret) 474 return ret; 475 476 ret = drmm_mode_config_init(drm); 477 if (ret) 478 return ret; 479 480 drm->mode_config.min_width = dbidev->mode.hdisplay; 481 drm->mode_config.max_width = dbidev->mode.hdisplay; 482 drm->mode_config.min_height = dbidev->mode.vdisplay; 483 drm->mode_config.max_height = dbidev->mode.vdisplay; 484 drm->mode_config.funcs = &ili9225_mode_config_funcs; 485 drm->mode_config.preferred_depth = 16; 486 drm->mode_config.helper_private = &ili9225_mode_config_helper_funcs; 487 488 plane = &ili9225->plane; 489 ret = drm_universal_plane_init(drm, plane, 0, &ili9225_plane_funcs, 490 ili9225_plane_formats, ARRAY_SIZE(ili9225_plane_formats), 491 ili9225_plane_format_modifiers, 492 DRM_PLANE_TYPE_PRIMARY, NULL); 493 if (ret) 494 return ret; 495 drm_plane_helper_add(plane, &ili9225_plane_helper_funcs); 496 drm_plane_enable_fb_damage_clips(plane); 497 498 crtc = &ili9225->crtc; 499 ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL, &ili9225_crtc_funcs, NULL); 500 if (ret) 501 return ret; 502 drm_crtc_helper_add(crtc, &ili9225_crtc_helper_funcs); 503 504 encoder = &ili9225->encoder; 505 ret = drm_encoder_init(drm, encoder, &ili9225_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); 506 if (ret) 507 return ret; 508 encoder->possible_crtcs = drm_crtc_mask(crtc); 509 510 connector = &ili9225->connector; 511 ret = drm_connector_init(drm, connector, &ili9225_connector_funcs, 512 DRM_MODE_CONNECTOR_SPI); 513 if (ret) 514 return ret; 515 drm_connector_helper_add(connector, &ili9225_connector_helper_funcs); 516 517 ret = drm_connector_attach_encoder(connector, encoder); 518 if (ret) 519 return ret; 520 521 drm_mode_config_reset(drm); 522 523 ret = drm_dev_register(drm, 0); 524 if (ret) 525 return ret; 526 527 spi_set_drvdata(spi, drm); 528 529 drm_client_setup(drm, NULL); 530 531 return 0; 532 } 533 534 static void ili9225_remove(struct spi_device *spi) 535 { 536 struct drm_device *drm = spi_get_drvdata(spi); 537 538 drm_dev_unplug(drm); 539 drm_atomic_helper_shutdown(drm); 540 } 541 542 static void ili9225_shutdown(struct spi_device *spi) 543 { 544 drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 545 } 546 547 static struct spi_driver ili9225_spi_driver = { 548 .driver = { 549 .name = "ili9225", 550 .of_match_table = ili9225_of_match, 551 }, 552 .id_table = ili9225_id, 553 .probe = ili9225_probe, 554 .remove = ili9225_remove, 555 .shutdown = ili9225_shutdown, 556 }; 557 module_spi_driver(ili9225_spi_driver); 558 559 MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver"); 560 MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 561 MODULE_LICENSE("GPL"); 562