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_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
  35void 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}
  43EXPORT_SYMBOL(sun4i_tcon_disable);
  44
  45void 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}
  54EXPORT_SYMBOL(sun4i_tcon_enable);
  55
  56void 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}
  71EXPORT_SYMBOL(sun4i_tcon_channel_disable);
  72
  73void 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}
  90EXPORT_SYMBOL(sun4i_tcon_channel_enable);
  91
  92void 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}
 106EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
 107
 108static 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
 126void 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}
 197EXPORT_SYMBOL(sun4i_tcon0_mode_set);
 198
 199void 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}
 273EXPORT_SYMBOL(sun4i_tcon1_mode_set);
 274
 275static 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
 289static 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
 315static 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
 342static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
 343{
 344        sun4i_dclk_free(tcon);
 345        clk_disable_unprepare(tcon->clk);
 346}
 347
 348static 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
 370static 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
 377static 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
 408struct 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", &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
 442struct 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", &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
 476static 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
 533err_free_clocks:
 534        sun4i_tcon_free_clocks(tcon);
 535err_assert_reset:
 536        reset_control_assert(tcon->lcd_rst);
 537        return ret;
 538}
 539
 540static 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
 548static const struct component_ops sun4i_tcon_ops = {
 549        .bind   = sun4i_tcon_bind,
 550        .unbind = sun4i_tcon_unbind,
 551};
 552
 553static 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
 578static int sun4i_tcon_remove(struct platform_device *pdev)
 579{
 580        component_del(&pdev->dev, &sun4i_tcon_ops);
 581
 582        return 0;
 583}
 584
 585static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
 586        .has_unknown_mux = true,
 587        .has_channel_1  = true,
 588};
 589
 590static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
 591        .has_channel_1  = true,
 592};
 593
 594static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
 595        .has_channel_1  = true,
 596};
 597
 598static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
 599        /* nothing is supported */
 600};
 601
 602static const struct of_device_id sun4i_tcon_of_table[] = {
 603        { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
 604        { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
 605        { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
 606        { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
 607        { }
 608};
 609MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
 610
 611static struct platform_driver sun4i_tcon_platform_driver = {
 612        .probe          = sun4i_tcon_probe,
 613        .remove         = sun4i_tcon_remove,
 614        .driver         = {
 615                .name           = "sun4i-tcon",
 616                .of_match_table = sun4i_tcon_of_table,
 617        },
 618};
 619module_platform_driver(sun4i_tcon_platform_driver);
 620
 621MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 622MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");
 623MODULE_LICENSE("GPL");
 624