1 /* 2 * Copyright (C) 2015 Free Electrons 3 * Copyright (C) 2015 NextThing Co 4 * 5 * Maxime Ripard <maxime.ripard@free-electrons.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 */ 12 13 #include <drm/drmP.h> 14 #include <drm/drm_atomic_helper.h> 15 #include <drm/drm_crtc.h> 16 #include <drm/drm_crtc_helper.h> 17 #include <drm/drm_modes.h> 18 #include <drm/drm_panel.h> 19 20 #include <linux/component.h> 21 #include <linux/ioport.h> 22 #include <linux/of_address.h> 23 #include <linux/of_device.h> 24 #include <linux/of_graph.h> 25 #include <linux/of_irq.h> 26 #include <linux/regmap.h> 27 #include <linux/reset.h> 28 29 #include "sun4i_crtc.h" 30 #include "sun4i_dotclock.h" 31 #include "sun4i_drv.h" 32 #include "sun4i_rgb.h" 33 #include "sun4i_tcon.h" 34 35 void sun4i_tcon_disable(struct sun4i_tcon *tcon) 36 { 37 DRM_DEBUG_DRIVER("Disabling TCON\n"); 38 39 /* Disable the TCON */ 40 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 41 SUN4I_TCON_GCTL_TCON_ENABLE, 0); 42 } 43 EXPORT_SYMBOL(sun4i_tcon_disable); 44 45 void sun4i_tcon_enable(struct sun4i_tcon *tcon) 46 { 47 DRM_DEBUG_DRIVER("Enabling TCON\n"); 48 49 /* Enable the TCON */ 50 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 51 SUN4I_TCON_GCTL_TCON_ENABLE, 52 SUN4I_TCON_GCTL_TCON_ENABLE); 53 } 54 EXPORT_SYMBOL(sun4i_tcon_enable); 55 56 void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) 57 { 58 /* Disable the TCON's channel */ 59 if (channel == 0) { 60 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 61 SUN4I_TCON0_CTL_TCON_ENABLE, 0); 62 clk_disable_unprepare(tcon->dclk); 63 return; 64 } 65 66 WARN_ON(!tcon->quirks->has_channel_1); 67 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 68 SUN4I_TCON1_CTL_TCON_ENABLE, 0); 69 clk_disable_unprepare(tcon->sclk1); 70 } 71 EXPORT_SYMBOL(sun4i_tcon_channel_disable); 72 73 void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) 74 { 75 /* Enable the TCON's channel */ 76 if (channel == 0) { 77 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 78 SUN4I_TCON0_CTL_TCON_ENABLE, 79 SUN4I_TCON0_CTL_TCON_ENABLE); 80 clk_prepare_enable(tcon->dclk); 81 return; 82 } 83 84 WARN_ON(!tcon->quirks->has_channel_1); 85 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 86 SUN4I_TCON1_CTL_TCON_ENABLE, 87 SUN4I_TCON1_CTL_TCON_ENABLE); 88 clk_prepare_enable(tcon->sclk1); 89 } 90 EXPORT_SYMBOL(sun4i_tcon_channel_enable); 91 92 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) 93 { 94 u32 mask, val = 0; 95 96 DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis"); 97 98 mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) | 99 SUN4I_TCON_GINT0_VBLANK_ENABLE(1); 100 101 if (enable) 102 val = mask; 103 104 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val); 105 } 106 EXPORT_SYMBOL(sun4i_tcon_enable_vblank); 107 108 static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, 109 int channel) 110 { 111 int delay = mode->vtotal - mode->vdisplay; 112 113 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 114 delay /= 2; 115 116 if (channel == 1) 117 delay -= 2; 118 119 delay = min(delay, 30); 120 121 DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay); 122 123 return delay; 124 } 125 126 void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, 127 struct drm_display_mode *mode) 128 { 129 unsigned int bp, hsync, vsync; 130 u8 clk_delay; 131 u32 val = 0; 132 133 /* Adjust clock delay */ 134 clk_delay = sun4i_tcon_get_clk_delay(mode, 0); 135 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 136 SUN4I_TCON0_CTL_CLK_DELAY_MASK, 137 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); 138 139 /* Set the resolution */ 140 regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, 141 SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | 142 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); 143 144 /* 145 * This is called a backporch in the register documentation, 146 * but it really is the front porch + hsync 147 */ 148 bp = mode->crtc_htotal - mode->crtc_hsync_start; 149 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 150 mode->crtc_htotal, bp); 151 152 /* Set horizontal display timings */ 153 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG, 154 SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) | 155 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp)); 156 157 /* 158 * This is called a backporch in the register documentation, 159 * but it really is the front porch + hsync 160 */ 161 bp = mode->crtc_vtotal - mode->crtc_vsync_start; 162 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 163 mode->crtc_vtotal, bp); 164 165 /* Set vertical display timings */ 166 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, 167 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) | 168 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); 169 170 /* Set Hsync and Vsync length */ 171 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 172 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 173 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 174 regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG, 175 SUN4I_TCON0_BASIC3_V_SYNC(vsync) | 176 SUN4I_TCON0_BASIC3_H_SYNC(hsync)); 177 178 /* Setup the polarity of the various signals */ 179 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) 180 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE; 181 182 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) 183 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; 184 185 regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, 186 SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, 187 val); 188 189 /* Map output pins to channel 0 */ 190 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 191 SUN4I_TCON_GCTL_IOMAP_MASK, 192 SUN4I_TCON_GCTL_IOMAP_TCON0); 193 194 /* Enable the output on the pins */ 195 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0); 196 } 197 EXPORT_SYMBOL(sun4i_tcon0_mode_set); 198 199 void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, 200 struct drm_display_mode *mode) 201 { 202 unsigned int bp, hsync, vsync; 203 u8 clk_delay; 204 u32 val; 205 206 WARN_ON(!tcon->quirks->has_channel_1); 207 208 /* Adjust clock delay */ 209 clk_delay = sun4i_tcon_get_clk_delay(mode, 1); 210 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 211 SUN4I_TCON1_CTL_CLK_DELAY_MASK, 212 SUN4I_TCON1_CTL_CLK_DELAY(clk_delay)); 213 214 /* Set interlaced mode */ 215 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 216 val = SUN4I_TCON1_CTL_INTERLACE_ENABLE; 217 else 218 val = 0; 219 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 220 SUN4I_TCON1_CTL_INTERLACE_ENABLE, 221 val); 222 223 /* Set the input resolution */ 224 regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG, 225 SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) | 226 SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay)); 227 228 /* Set the upscaling resolution */ 229 regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG, 230 SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) | 231 SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay)); 232 233 /* Set the output resolution */ 234 regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG, 235 SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) | 236 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); 237 238 /* Set horizontal display timings */ 239 bp = mode->crtc_htotal - mode->crtc_hsync_end; 240 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 241 mode->htotal, bp); 242 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, 243 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | 244 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); 245 246 /* Set vertical display timings */ 247 bp = mode->crtc_vtotal - mode->crtc_vsync_end; 248 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 249 mode->vtotal, bp); 250 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG, 251 SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) | 252 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp)); 253 254 /* Set Hsync and Vsync length */ 255 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start; 256 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start; 257 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync); 258 regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG, 259 SUN4I_TCON1_BASIC5_V_SYNC(vsync) | 260 SUN4I_TCON1_BASIC5_H_SYNC(hsync)); 261 262 /* Map output pins to channel 1 */ 263 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 264 SUN4I_TCON_GCTL_IOMAP_MASK, 265 SUN4I_TCON_GCTL_IOMAP_TCON1); 266 267 /* 268 * FIXME: Undocumented bits 269 */ 270 if (tcon->quirks->has_unknown_mux) 271 regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1); 272 } 273 EXPORT_SYMBOL(sun4i_tcon1_mode_set); 274 275 static void sun4i_tcon_finish_page_flip(struct drm_device *dev, 276 struct sun4i_crtc *scrtc) 277 { 278 unsigned long flags; 279 280 spin_lock_irqsave(&dev->event_lock, flags); 281 if (scrtc->event) { 282 drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event); 283 drm_crtc_vblank_put(&scrtc->crtc); 284 scrtc->event = NULL; 285 } 286 spin_unlock_irqrestore(&dev->event_lock, flags); 287 } 288 289 static irqreturn_t sun4i_tcon_handler(int irq, void *private) 290 { 291 struct sun4i_tcon *tcon = private; 292 struct drm_device *drm = tcon->drm; 293 struct sun4i_drv *drv = drm->dev_private; 294 struct sun4i_crtc *scrtc = drv->crtc; 295 unsigned int status; 296 297 regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status); 298 299 if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) | 300 SUN4I_TCON_GINT0_VBLANK_INT(1)))) 301 return IRQ_NONE; 302 303 drm_crtc_handle_vblank(&scrtc->crtc); 304 sun4i_tcon_finish_page_flip(drm, scrtc); 305 306 /* Acknowledge the interrupt */ 307 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, 308 SUN4I_TCON_GINT0_VBLANK_INT(0) | 309 SUN4I_TCON_GINT0_VBLANK_INT(1), 310 0); 311 312 return IRQ_HANDLED; 313 } 314 315 static int sun4i_tcon_init_clocks(struct device *dev, 316 struct sun4i_tcon *tcon) 317 { 318 tcon->clk = devm_clk_get(dev, "ahb"); 319 if (IS_ERR(tcon->clk)) { 320 dev_err(dev, "Couldn't get the TCON bus clock\n"); 321 return PTR_ERR(tcon->clk); 322 } 323 clk_prepare_enable(tcon->clk); 324 325 tcon->sclk0 = devm_clk_get(dev, "tcon-ch0"); 326 if (IS_ERR(tcon->sclk0)) { 327 dev_err(dev, "Couldn't get the TCON channel 0 clock\n"); 328 return PTR_ERR(tcon->sclk0); 329 } 330 331 if (tcon->quirks->has_channel_1) { 332 tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); 333 if (IS_ERR(tcon->sclk1)) { 334 dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); 335 return PTR_ERR(tcon->sclk1); 336 } 337 } 338 339 return sun4i_dclk_create(dev, tcon); 340 } 341 342 static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon) 343 { 344 sun4i_dclk_free(tcon); 345 clk_disable_unprepare(tcon->clk); 346 } 347 348 static int sun4i_tcon_init_irq(struct device *dev, 349 struct sun4i_tcon *tcon) 350 { 351 struct platform_device *pdev = to_platform_device(dev); 352 int irq, ret; 353 354 irq = platform_get_irq(pdev, 0); 355 if (irq < 0) { 356 dev_err(dev, "Couldn't retrieve the TCON interrupt\n"); 357 return irq; 358 } 359 360 ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0, 361 dev_name(dev), tcon); 362 if (ret) { 363 dev_err(dev, "Couldn't request the IRQ\n"); 364 return ret; 365 } 366 367 return 0; 368 } 369 370 static struct regmap_config sun4i_tcon_regmap_config = { 371 .reg_bits = 32, 372 .val_bits = 32, 373 .reg_stride = 4, 374 .max_register = 0x800, 375 }; 376 377 static int sun4i_tcon_init_regmap(struct device *dev, 378 struct sun4i_tcon *tcon) 379 { 380 struct platform_device *pdev = to_platform_device(dev); 381 struct resource *res; 382 void __iomem *regs; 383 384 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 385 regs = devm_ioremap_resource(dev, res); 386 if (IS_ERR(regs)) 387 return PTR_ERR(regs); 388 389 tcon->regs = devm_regmap_init_mmio(dev, regs, 390 &sun4i_tcon_regmap_config); 391 if (IS_ERR(tcon->regs)) { 392 dev_err(dev, "Couldn't create the TCON regmap\n"); 393 return PTR_ERR(tcon->regs); 394 } 395 396 /* Make sure the TCON is disabled and all IRQs are off */ 397 regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0); 398 regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0); 399 regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0); 400 401 /* Disable IO lines and set them to tristate */ 402 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0); 403 regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0); 404 405 return 0; 406 } 407 408 struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) 409 { 410 struct device_node *port, *remote, *child; 411 struct device_node *end_node = NULL; 412 413 /* Inputs are listed first, then outputs */ 414 port = of_graph_get_port_by_id(node, 1); 415 416 /* 417 * Our first output is the RGB interface where the panel will 418 * be connected. 419 */ 420 for_each_child_of_node(port, child) { 421 u32 reg; 422 423 of_property_read_u32(child, "reg", ®); 424 if (reg == 0) 425 end_node = child; 426 } 427 428 if (!end_node) { 429 DRM_DEBUG_DRIVER("Missing panel endpoint\n"); 430 return ERR_PTR(-ENODEV); 431 } 432 433 remote = of_graph_get_remote_port_parent(end_node); 434 if (!remote) { 435 DRM_DEBUG_DRIVER("Unable to parse remote node\n"); 436 return ERR_PTR(-EINVAL); 437 } 438 439 return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER); 440 } 441 442 struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node) 443 { 444 struct device_node *port, *remote, *child; 445 struct device_node *end_node = NULL; 446 447 /* Inputs are listed first, then outputs */ 448 port = of_graph_get_port_by_id(node, 1); 449 450 /* 451 * Our first output is the RGB interface where the panel will 452 * be connected. 453 */ 454 for_each_child_of_node(port, child) { 455 u32 reg; 456 457 of_property_read_u32(child, "reg", ®); 458 if (reg == 0) 459 end_node = child; 460 } 461 462 if (!end_node) { 463 DRM_DEBUG_DRIVER("Missing bridge endpoint\n"); 464 return ERR_PTR(-ENODEV); 465 } 466 467 remote = of_graph_get_remote_port_parent(end_node); 468 if (!remote) { 469 DRM_DEBUG_DRIVER("Enable to parse remote node\n"); 470 return ERR_PTR(-EINVAL); 471 } 472 473 return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER); 474 } 475 476 static int sun4i_tcon_bind(struct device *dev, struct device *master, 477 void *data) 478 { 479 struct drm_device *drm = data; 480 struct sun4i_drv *drv = drm->dev_private; 481 struct sun4i_tcon *tcon; 482 int ret; 483 484 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 485 if (!tcon) 486 return -ENOMEM; 487 dev_set_drvdata(dev, tcon); 488 drv->tcon = tcon; 489 tcon->drm = drm; 490 tcon->dev = dev; 491 tcon->quirks = of_device_get_match_data(dev); 492 493 tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); 494 if (IS_ERR(tcon->lcd_rst)) { 495 dev_err(dev, "Couldn't get our reset line\n"); 496 return PTR_ERR(tcon->lcd_rst); 497 } 498 499 /* Make sure our TCON is reset */ 500 if (!reset_control_status(tcon->lcd_rst)) 501 reset_control_assert(tcon->lcd_rst); 502 503 ret = reset_control_deassert(tcon->lcd_rst); 504 if (ret) { 505 dev_err(dev, "Couldn't deassert our reset line\n"); 506 return ret; 507 } 508 509 ret = sun4i_tcon_init_regmap(dev, tcon); 510 if (ret) { 511 dev_err(dev, "Couldn't init our TCON regmap\n"); 512 goto err_assert_reset; 513 } 514 515 ret = sun4i_tcon_init_clocks(dev, tcon); 516 if (ret) { 517 dev_err(dev, "Couldn't init our TCON clocks\n"); 518 goto err_assert_reset; 519 } 520 521 ret = sun4i_tcon_init_irq(dev, tcon); 522 if (ret) { 523 dev_err(dev, "Couldn't init our TCON interrupts\n"); 524 goto err_free_clocks; 525 } 526 527 ret = sun4i_rgb_init(drm); 528 if (ret < 0) 529 goto err_free_clocks; 530 531 return 0; 532 533 err_free_clocks: 534 sun4i_tcon_free_clocks(tcon); 535 err_assert_reset: 536 reset_control_assert(tcon->lcd_rst); 537 return ret; 538 } 539 540 static void sun4i_tcon_unbind(struct device *dev, struct device *master, 541 void *data) 542 { 543 struct sun4i_tcon *tcon = dev_get_drvdata(dev); 544 545 sun4i_tcon_free_clocks(tcon); 546 } 547 548 static struct component_ops sun4i_tcon_ops = { 549 .bind = sun4i_tcon_bind, 550 .unbind = sun4i_tcon_unbind, 551 }; 552 553 static int sun4i_tcon_probe(struct platform_device *pdev) 554 { 555 struct device_node *node = pdev->dev.of_node; 556 struct drm_bridge *bridge; 557 struct drm_panel *panel; 558 559 /* 560 * Neither the bridge or the panel is ready. 561 * Defer the probe. 562 */ 563 panel = sun4i_tcon_find_panel(node); 564 bridge = sun4i_tcon_find_bridge(node); 565 566 /* 567 * If we don't have a panel endpoint, just go on 568 */ 569 if ((PTR_ERR(panel) == -EPROBE_DEFER) && 570 (PTR_ERR(bridge) == -EPROBE_DEFER)) { 571 DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n"); 572 return -EPROBE_DEFER; 573 } 574 575 return component_add(&pdev->dev, &sun4i_tcon_ops); 576 } 577 578 static int sun4i_tcon_remove(struct platform_device *pdev) 579 { 580 component_del(&pdev->dev, &sun4i_tcon_ops); 581 582 return 0; 583 } 584 585 static const struct sun4i_tcon_quirks sun5i_a13_quirks = { 586 .has_unknown_mux = true, 587 .has_channel_1 = true, 588 }; 589 590 static const struct sun4i_tcon_quirks sun8i_a33_quirks = { 591 /* nothing is supported */ 592 }; 593 594 static const struct of_device_id sun4i_tcon_of_table[] = { 595 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, 596 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, 597 { } 598 }; 599 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); 600 601 static struct platform_driver sun4i_tcon_platform_driver = { 602 .probe = sun4i_tcon_probe, 603 .remove = sun4i_tcon_remove, 604 .driver = { 605 .name = "sun4i-tcon", 606 .of_match_table = sun4i_tcon_of_table, 607 }, 608 }; 609 module_platform_driver(sun4i_tcon_platform_driver); 610 611 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 612 MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver"); 613 MODULE_LICENSE("GPL"); 614