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