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_encoder.h>
  18#include <drm/drm_modes.h>
  19#include <drm/drm_of.h>
  20
  21#include <uapi/drm/drm_mode.h>
  22
  23#include <linux/component.h>
  24#include <linux/ioport.h>
  25#include <linux/of_address.h>
  26#include <linux/of_device.h>
  27#include <linux/of_irq.h>
  28#include <linux/regmap.h>
  29#include <linux/reset.h>
  30
  31#include "sun4i_crtc.h"
  32#include "sun4i_dotclock.h"
  33#include "sun4i_drv.h"
  34#include "sun4i_lvds.h"
  35#include "sun4i_rgb.h"
  36#include "sun4i_tcon.h"
  37#include "sun6i_mipi_dsi.h"
  38#include "sunxi_engine.h"
  39
  40static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
  41{
  42        struct drm_connector *connector;
  43        struct drm_connector_list_iter iter;
  44
  45        drm_connector_list_iter_begin(encoder->dev, &iter);
  46        drm_for_each_connector_iter(connector, &iter)
  47                if (connector->encoder == encoder) {
  48                        drm_connector_list_iter_end(&iter);
  49                        return connector;
  50                }
  51        drm_connector_list_iter_end(&iter);
  52
  53        return NULL;
  54}
  55
  56static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder)
  57{
  58        struct drm_connector *connector;
  59        struct drm_display_info *info;
  60
  61        connector = sun4i_tcon_get_connector(encoder);
  62        if (!connector)
  63                return -EINVAL;
  64
  65        info = &connector->display_info;
  66        if (info->num_bus_formats != 1)
  67                return -EINVAL;
  68
  69        switch (info->bus_formats[0]) {
  70        case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
  71                return 18;
  72
  73        case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
  74        case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
  75                return 24;
  76        }
  77
  78        return -EINVAL;
  79}
  80
  81static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
  82                                          bool enabled)
  83{
  84        struct clk *clk;
  85
  86        switch (channel) {
  87        case 0:
  88                WARN_ON(!tcon->quirks->has_channel_0);
  89                regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
  90                                   SUN4I_TCON0_CTL_TCON_ENABLE,
  91                                   enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);
  92                clk = tcon->dclk;
  93                break;
  94        case 1:
  95                WARN_ON(!tcon->quirks->has_channel_1);
  96                regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
  97                                   SUN4I_TCON1_CTL_TCON_ENABLE,
  98                                   enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0);
  99                clk = tcon->sclk1;
 100                break;
 101        default:
 102                DRM_WARN("Unknown channel... doing nothing\n");
 103                return;
 104        }
 105
 106        if (enabled) {
 107                clk_prepare_enable(clk);
 108                clk_rate_exclusive_get(clk);
 109        } else {
 110                clk_rate_exclusive_put(clk);
 111                clk_disable_unprepare(clk);
 112        }
 113}
 114
 115static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
 116                                       const struct drm_encoder *encoder,
 117                                       bool enabled)
 118{
 119        if (enabled) {
 120                u8 val;
 121
 122                regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
 123                                   SUN4I_TCON0_LVDS_IF_EN,
 124                                   SUN4I_TCON0_LVDS_IF_EN);
 125
 126                /*
 127                 * As their name suggest, these values only apply to the A31
 128                 * and later SoCs. We'll have to rework this when merging
 129                 * support for the older SoCs.
 130                 */
 131                regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
 132                             SUN6I_TCON0_LVDS_ANA0_C(2) |
 133                             SUN6I_TCON0_LVDS_ANA0_V(3) |
 134                             SUN6I_TCON0_LVDS_ANA0_PD(2) |
 135                             SUN6I_TCON0_LVDS_ANA0_EN_LDO);
 136                udelay(2);
 137
 138                regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
 139                                   SUN6I_TCON0_LVDS_ANA0_EN_MB,
 140                                   SUN6I_TCON0_LVDS_ANA0_EN_MB);
 141                udelay(2);
 142
 143                regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
 144                                   SUN6I_TCON0_LVDS_ANA0_EN_DRVC,
 145                                   SUN6I_TCON0_LVDS_ANA0_EN_DRVC);
 146
 147                if (sun4i_tcon_get_pixel_depth(encoder) == 18)
 148                        val = 7;
 149                else
 150                        val = 0xf;
 151
 152                regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
 153                                  SUN6I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
 154                                  SUN6I_TCON0_LVDS_ANA0_EN_DRVD(val));
 155        } else {
 156                regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
 157                                   SUN4I_TCON0_LVDS_IF_EN, 0);
 158        }
 159}
 160
 161void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
 162                           const struct drm_encoder *encoder,
 163                           bool enabled)
 164{
 165        bool is_lvds = false;
 166        int channel;
 167
 168        switch (encoder->encoder_type) {
 169        case DRM_MODE_ENCODER_LVDS:
 170                is_lvds = true;
 171                /* Fallthrough */
 172        case DRM_MODE_ENCODER_DSI:
 173        case DRM_MODE_ENCODER_NONE:
 174                channel = 0;
 175                break;
 176        case DRM_MODE_ENCODER_TMDS:
 177        case DRM_MODE_ENCODER_TVDAC:
 178                channel = 1;
 179                break;
 180        default:
 181                DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
 182                return;
 183        }
 184
 185        if (is_lvds && !enabled)
 186                sun4i_tcon_lvds_set_status(tcon, encoder, false);
 187
 188        regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
 189                           SUN4I_TCON_GCTL_TCON_ENABLE,
 190                           enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
 191
 192        if (is_lvds && enabled)
 193                sun4i_tcon_lvds_set_status(tcon, encoder, true);
 194
 195        sun4i_tcon_channel_set_status(tcon, channel, enabled);
 196}
 197
 198void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
 199{
 200        u32 mask, val = 0;
 201
 202        DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");
 203
 204        mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |
 205                SUN4I_TCON_GINT0_VBLANK_ENABLE(1) |
 206                SUN4I_TCON_GINT0_TCON0_TRI_FINISH_ENABLE;
 207
 208        if (enable)
 209                val = mask;
 210
 211        regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val);
 212}
 213EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
 214
 215/*
 216 * This function is a helper for TCON output muxing. The TCON output
 217 * muxing control register in earlier SoCs (without the TCON TOP block)
 218 * are located in TCON0. This helper returns a pointer to TCON0's
 219 * sun4i_tcon structure, or NULL if not found.
 220 */
 221static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
 222{
 223        struct sun4i_drv *drv = drm->dev_private;
 224        struct sun4i_tcon *tcon;
 225
 226        list_for_each_entry(tcon, &drv->tcon_list, list)
 227                if (tcon->id == 0)
 228                        return tcon;
 229
 230        dev_warn(drm->dev,
 231                 "TCON0 not found, display output muxing may not work\n");
 232
 233        return NULL;
 234}
 235
 236void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
 237                        const struct drm_encoder *encoder)
 238{
 239        int ret = -ENOTSUPP;
 240
 241        if (tcon->quirks->set_mux)
 242                ret = tcon->quirks->set_mux(tcon, encoder);
 243
 244        DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",
 245                         encoder->name, encoder->crtc->name, ret);
 246}
 247
 248static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode,
 249                                    int channel)
 250{
 251        int delay = mode->vtotal - mode->vdisplay;
 252
 253        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 254                delay /= 2;
 255
 256        if (channel == 1)
 257                delay -= 2;
 258
 259        delay = min(delay, 30);
 260
 261        DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay);
 262
 263        return delay;
 264}
 265
 266static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
 267                                        const struct drm_display_mode *mode)
 268{
 269        /* Configure the dot clock */
 270        clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
 271
 272        /* Set the resolution */
 273        regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,
 274                     SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |
 275                     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
 276}
 277
 278static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
 279                                     struct mipi_dsi_device *device,
 280                                     const struct drm_display_mode *mode)
 281{
 282        u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format);
 283        u8 lanes = device->lanes;
 284        u32 block_space, start_delay;
 285        u32 tcon_div;
 286
 287        tcon->dclk_min_div = 4;
 288        tcon->dclk_max_div = 127;
 289
 290        sun4i_tcon0_mode_set_common(tcon, mode);
 291
 292        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
 293                           SUN4I_TCON0_CTL_IF_MASK,
 294                           SUN4I_TCON0_CTL_IF_8080);
 295
 296        regmap_write(tcon->regs, SUN4I_TCON_ECC_FIFO_REG,
 297                     SUN4I_TCON_ECC_FIFO_EN);
 298
 299        regmap_write(tcon->regs, SUN4I_TCON0_CPU_IF_REG,
 300                     SUN4I_TCON0_CPU_IF_MODE_DSI |
 301                     SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH |
 302                     SUN4I_TCON0_CPU_IF_TRI_FIFO_EN |
 303                     SUN4I_TCON0_CPU_IF_TRI_EN);
 304
 305        /*
 306         * This looks suspicious, but it works...
 307         *
 308         * The datasheet says that this should be set higher than 20 *
 309         * pixel cycle, but it's not clear what a pixel cycle is.
 310         */
 311        regmap_read(tcon->regs, SUN4I_TCON0_DCLK_REG, &tcon_div);
 312        tcon_div &= GENMASK(6, 0);
 313        block_space = mode->htotal * bpp / (tcon_div * lanes);
 314        block_space -= mode->hdisplay + 40;
 315
 316        regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI0_REG,
 317                     SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(block_space) |
 318                     SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(mode->hdisplay));
 319
 320        regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI1_REG,
 321                     SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(mode->vdisplay));
 322
 323        start_delay = (mode->crtc_vtotal - mode->crtc_vdisplay - 10 - 1);
 324        start_delay = start_delay * mode->crtc_htotal * 149;
 325        start_delay = start_delay / (mode->crtc_clock / 1000) / 8;
 326        regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI2_REG,
 327                     SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(10) |
 328                     SUN4I_TCON0_CPU_TRI2_START_DELAY(start_delay));
 329
 330        /*
 331         * The Allwinner BSP has a comment that the period should be
 332         * the display clock * 15, but uses an hardcoded 3000...
 333         */
 334        regmap_write(tcon->regs, SUN4I_TCON_SAFE_PERIOD_REG,
 335                     SUN4I_TCON_SAFE_PERIOD_NUM(3000) |
 336                     SUN4I_TCON_SAFE_PERIOD_MODE(3));
 337
 338        /* Enable the output on the pins */
 339        regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG,
 340                     0xe0000000);
 341}
 342
 343static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
 344                                      const struct drm_encoder *encoder,
 345                                      const struct drm_display_mode *mode)
 346{
 347        unsigned int bp;
 348        u8 clk_delay;
 349        u32 reg, val = 0;
 350
 351        WARN_ON(!tcon->quirks->has_channel_0);
 352
 353        tcon->dclk_min_div = 7;
 354        tcon->dclk_max_div = 7;
 355        sun4i_tcon0_mode_set_common(tcon, mode);
 356
 357        /* Adjust clock delay */
 358        clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
 359        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
 360                           SUN4I_TCON0_CTL_CLK_DELAY_MASK,
 361                           SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
 362
 363        /*
 364         * This is called a backporch in the register documentation,
 365         * but it really is the back porch + hsync
 366         */
 367        bp = mode->crtc_htotal - mode->crtc_hsync_start;
 368        DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
 369                         mode->crtc_htotal, bp);
 370
 371        /* Set horizontal display timings */
 372        regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
 373                     SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) |
 374                     SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
 375
 376        /*
 377         * This is called a backporch in the register documentation,
 378         * but it really is the back porch + hsync
 379         */
 380        bp = mode->crtc_vtotal - mode->crtc_vsync_start;
 381        DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
 382                         mode->crtc_vtotal, bp);
 383
 384        /* Set vertical display timings */
 385        regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
 386                     SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
 387                     SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
 388
 389        reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
 390                SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
 391                SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
 392        if (sun4i_tcon_get_pixel_depth(encoder) == 24)
 393                reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
 394        else
 395                reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS;
 396
 397        regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg);
 398
 399        /* Setup the polarity of the various signals */
 400        if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
 401                val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
 402
 403        if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
 404                val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
 405
 406        regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);
 407
 408        /* Map output pins to channel 0 */
 409        regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
 410                           SUN4I_TCON_GCTL_IOMAP_MASK,
 411                           SUN4I_TCON_GCTL_IOMAP_TCON0);
 412
 413        /* Enable the output on the pins */
 414        regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0xe0000000);
 415}
 416
 417static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
 418                                     const struct drm_display_mode *mode)
 419{
 420        unsigned int bp, hsync, vsync;
 421        u8 clk_delay;
 422        u32 val = 0;
 423
 424        WARN_ON(!tcon->quirks->has_channel_0);
 425
 426        tcon->dclk_min_div = 6;
 427        tcon->dclk_max_div = 127;
 428        sun4i_tcon0_mode_set_common(tcon, mode);
 429
 430        /* Adjust clock delay */
 431        clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
 432        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
 433                           SUN4I_TCON0_CTL_CLK_DELAY_MASK,
 434                           SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
 435
 436        /*
 437         * This is called a backporch in the register documentation,
 438         * but it really is the back porch + hsync
 439         */
 440        bp = mode->crtc_htotal - mode->crtc_hsync_start;
 441        DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
 442                         mode->crtc_htotal, bp);
 443
 444        /* Set horizontal display timings */
 445        regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
 446                     SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) |
 447                     SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
 448
 449        /*
 450         * This is called a backporch in the register documentation,
 451         * but it really is the back porch + hsync
 452         */
 453        bp = mode->crtc_vtotal - mode->crtc_vsync_start;
 454        DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
 455                         mode->crtc_vtotal, bp);
 456
 457        /* Set vertical display timings */
 458        regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
 459                     SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
 460                     SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
 461
 462        /* Set Hsync and Vsync length */
 463        hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
 464        vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
 465        DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
 466        regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG,
 467                     SUN4I_TCON0_BASIC3_V_SYNC(vsync) |
 468                     SUN4I_TCON0_BASIC3_H_SYNC(hsync));
 469
 470        /* Setup the polarity of the various signals */
 471        if (mode->flags & DRM_MODE_FLAG_PHSYNC)
 472                val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
 473
 474        if (mode->flags & DRM_MODE_FLAG_PVSYNC)
 475                val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
 476
 477        regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG,
 478                           SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE,
 479                           val);
 480
 481        /* Map output pins to channel 0 */
 482        regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
 483                           SUN4I_TCON_GCTL_IOMAP_MASK,
 484                           SUN4I_TCON_GCTL_IOMAP_TCON0);
 485
 486        /* Enable the output on the pins */
 487        regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0);
 488}
 489
 490static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
 491                                 const struct drm_display_mode *mode)
 492{
 493        unsigned int bp, hsync, vsync, vtotal;
 494        u8 clk_delay;
 495        u32 val;
 496
 497        WARN_ON(!tcon->quirks->has_channel_1);
 498
 499        /* Configure the dot clock */
 500        clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
 501
 502        /* Adjust clock delay */
 503        clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
 504        regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
 505                           SUN4I_TCON1_CTL_CLK_DELAY_MASK,
 506                           SUN4I_TCON1_CTL_CLK_DELAY(clk_delay));
 507
 508        /* Set interlaced mode */
 509        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 510                val = SUN4I_TCON1_CTL_INTERLACE_ENABLE;
 511        else
 512                val = 0;
 513        regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
 514                           SUN4I_TCON1_CTL_INTERLACE_ENABLE,
 515                           val);
 516
 517        /* Set the input resolution */
 518        regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,
 519                     SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |
 520                     SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));
 521
 522        /* Set the upscaling resolution */
 523        regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,
 524                     SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |
 525                     SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));
 526
 527        /* Set the output resolution */
 528        regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,
 529                     SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |
 530                     SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
 531
 532        /* Set horizontal display timings */
 533        bp = mode->crtc_htotal - mode->crtc_hsync_start;
 534        DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
 535                         mode->htotal, bp);
 536        regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
 537                     SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
 538                     SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
 539
 540        bp = mode->crtc_vtotal - mode->crtc_vsync_start;
 541        DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
 542                         mode->crtc_vtotal, bp);
 543
 544        /*
 545         * The vertical resolution needs to be doubled in all
 546         * cases. We could use crtc_vtotal and always multiply by two,
 547         * but that leads to a rounding error in interlace when vtotal
 548         * is odd.
 549         *
 550         * This happens with TV's PAL for example, where vtotal will
 551         * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be
 552         * 624, which apparently confuses the hardware.
 553         *
 554         * To work around this, we will always use vtotal, and
 555         * multiply by two only if we're not in interlace.
 556         */
 557        vtotal = mode->vtotal;
 558        if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
 559                vtotal = vtotal * 2;
 560
 561        /* Set vertical display timings */
 562        regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
 563                     SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) |
 564                     SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
 565
 566        /* Set Hsync and Vsync length */
 567        hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
 568        vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
 569        DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
 570        regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG,
 571                     SUN4I_TCON1_BASIC5_V_SYNC(vsync) |
 572                     SUN4I_TCON1_BASIC5_H_SYNC(hsync));
 573
 574        /* Map output pins to channel 1 */
 575        regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
 576                           SUN4I_TCON_GCTL_IOMAP_MASK,
 577                           SUN4I_TCON_GCTL_IOMAP_TCON1);
 578}
 579
 580void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
 581                         const struct drm_encoder *encoder,
 582                         const struct drm_display_mode *mode)
 583{
 584        struct sun6i_dsi *dsi;
 585
 586        switch (encoder->encoder_type) {
 587        case DRM_MODE_ENCODER_DSI:
 588                /*
 589                 * This is not really elegant, but it's the "cleaner"
 590                 * way I could think of...
 591                 */
 592                dsi = encoder_to_sun6i_dsi(encoder);
 593                sun4i_tcon0_mode_set_cpu(tcon, dsi->device, mode);
 594                break;
 595        case DRM_MODE_ENCODER_LVDS:
 596                sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
 597                break;
 598        case DRM_MODE_ENCODER_NONE:
 599                sun4i_tcon0_mode_set_rgb(tcon, mode);
 600                sun4i_tcon_set_mux(tcon, 0, encoder);
 601                break;
 602        case DRM_MODE_ENCODER_TVDAC:
 603        case DRM_MODE_ENCODER_TMDS:
 604                sun4i_tcon1_mode_set(tcon, mode);
 605                sun4i_tcon_set_mux(tcon, 1, encoder);
 606                break;
 607        default:
 608                DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
 609        }
 610}
 611EXPORT_SYMBOL(sun4i_tcon_mode_set);
 612
 613static void sun4i_tcon_finish_page_flip(struct drm_device *dev,
 614                                        struct sun4i_crtc *scrtc)
 615{
 616        unsigned long flags;
 617
 618        spin_lock_irqsave(&dev->event_lock, flags);
 619        if (scrtc->event) {
 620                drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event);
 621                drm_crtc_vblank_put(&scrtc->crtc);
 622                scrtc->event = NULL;
 623        }
 624        spin_unlock_irqrestore(&dev->event_lock, flags);
 625}
 626
 627static irqreturn_t sun4i_tcon_handler(int irq, void *private)
 628{
 629        struct sun4i_tcon *tcon = private;
 630        struct drm_device *drm = tcon->drm;
 631        struct sun4i_crtc *scrtc = tcon->crtc;
 632        struct sunxi_engine *engine = scrtc->engine;
 633        unsigned int status;
 634
 635        regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
 636
 637        if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
 638                        SUN4I_TCON_GINT0_VBLANK_INT(1) |
 639                        SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT)))
 640                return IRQ_NONE;
 641
 642        drm_crtc_handle_vblank(&scrtc->crtc);
 643        sun4i_tcon_finish_page_flip(drm, scrtc);
 644
 645        /* Acknowledge the interrupt */
 646        regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,
 647                           SUN4I_TCON_GINT0_VBLANK_INT(0) |
 648                           SUN4I_TCON_GINT0_VBLANK_INT(1) |
 649                           SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT,
 650                           0);
 651
 652        if (engine->ops->vblank_quirk)
 653                engine->ops->vblank_quirk(engine);
 654
 655        return IRQ_HANDLED;
 656}
 657
 658static int sun4i_tcon_init_clocks(struct device *dev,
 659                                  struct sun4i_tcon *tcon)
 660{
 661        tcon->clk = devm_clk_get(dev, "ahb");
 662        if (IS_ERR(tcon->clk)) {
 663                dev_err(dev, "Couldn't get the TCON bus clock\n");
 664                return PTR_ERR(tcon->clk);
 665        }
 666        clk_prepare_enable(tcon->clk);
 667
 668        if (tcon->quirks->has_channel_0) {
 669                tcon->sclk0 = devm_clk_get(dev, "tcon-ch0");
 670                if (IS_ERR(tcon->sclk0)) {
 671                        dev_err(dev, "Couldn't get the TCON channel 0 clock\n");
 672                        return PTR_ERR(tcon->sclk0);
 673                }
 674        }
 675
 676        if (tcon->quirks->has_channel_1) {
 677                tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
 678                if (IS_ERR(tcon->sclk1)) {
 679                        dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
 680                        return PTR_ERR(tcon->sclk1);
 681                }
 682        }
 683
 684        return 0;
 685}
 686
 687static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
 688{
 689        clk_disable_unprepare(tcon->clk);
 690}
 691
 692static int sun4i_tcon_init_irq(struct device *dev,
 693                               struct sun4i_tcon *tcon)
 694{
 695        struct platform_device *pdev = to_platform_device(dev);
 696        int irq, ret;
 697
 698        irq = platform_get_irq(pdev, 0);
 699        if (irq < 0) {
 700                dev_err(dev, "Couldn't retrieve the TCON interrupt\n");
 701                return irq;
 702        }
 703
 704        ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0,
 705                               dev_name(dev), tcon);
 706        if (ret) {
 707                dev_err(dev, "Couldn't request the IRQ\n");
 708                return ret;
 709        }
 710
 711        return 0;
 712}
 713
 714static struct regmap_config sun4i_tcon_regmap_config = {
 715        .reg_bits       = 32,
 716        .val_bits       = 32,
 717        .reg_stride     = 4,
 718        .max_register   = 0x800,
 719};
 720
 721static int sun4i_tcon_init_regmap(struct device *dev,
 722                                  struct sun4i_tcon *tcon)
 723{
 724        struct platform_device *pdev = to_platform_device(dev);
 725        struct resource *res;
 726        void __iomem *regs;
 727
 728        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 729        regs = devm_ioremap_resource(dev, res);
 730        if (IS_ERR(regs))
 731                return PTR_ERR(regs);
 732
 733        tcon->regs = devm_regmap_init_mmio(dev, regs,
 734                                           &sun4i_tcon_regmap_config);
 735        if (IS_ERR(tcon->regs)) {
 736                dev_err(dev, "Couldn't create the TCON regmap\n");
 737                return PTR_ERR(tcon->regs);
 738        }
 739
 740        /* Make sure the TCON is disabled and all IRQs are off */
 741        regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);
 742        regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
 743        regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0);
 744
 745        /* Disable IO lines and set them to tristate */
 746        regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);
 747        regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);
 748
 749        return 0;
 750}
 751
 752/*
 753 * On SoCs with the old display pipeline design (Display Engine 1.0),
 754 * the TCON is always tied to just one backend. Hence we can traverse
 755 * the of_graph upwards to find the backend our tcon is connected to,
 756 * and take its ID as our own.
 757 *
 758 * We can either identify backends from their compatible strings, which
 759 * means maintaining a large list of them. Or, since the backend is
 760 * registered and binded before the TCON, we can just go through the
 761 * list of registered backends and compare the device node.
 762 *
 763 * As the structures now store engines instead of backends, here this
 764 * function in fact searches the corresponding engine, and the ID is
 765 * requested via the get_id function of the engine.
 766 */
 767static struct sunxi_engine *
 768sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
 769                                struct device_node *node)
 770{
 771        struct device_node *port, *ep, *remote;
 772        struct sunxi_engine *engine = ERR_PTR(-EINVAL);
 773
 774        port = of_graph_get_port_by_id(node, 0);
 775        if (!port)
 776                return ERR_PTR(-EINVAL);
 777
 778        /*
 779         * This only works if there is only one path from the TCON
 780         * to any display engine. Otherwise the probe order of the
 781         * TCONs and display engines is not guaranteed. They may
 782         * either bind to the wrong one, or worse, bind to the same
 783         * one if additional checks are not done.
 784         *
 785         * Bail out if there are multiple input connections.
 786         */
 787        if (of_get_available_child_count(port) != 1)
 788                goto out_put_port;
 789
 790        /* Get the first connection without specifying an ID */
 791        ep = of_get_next_available_child(port, NULL);
 792        if (!ep)
 793                goto out_put_port;
 794
 795        remote = of_graph_get_remote_port_parent(ep);
 796        if (!remote)
 797                goto out_put_ep;
 798
 799        /* does this node match any registered engines? */
 800        list_for_each_entry(engine, &drv->engine_list, list)
 801                if (remote == engine->node)
 802                        goto out_put_remote;
 803
 804        /* keep looking through upstream ports */
 805        engine = sun4i_tcon_find_engine_traverse(drv, remote);
 806
 807out_put_remote:
 808        of_node_put(remote);
 809out_put_ep:
 810        of_node_put(ep);
 811out_put_port:
 812        of_node_put(port);
 813
 814        return engine;
 815}
 816
 817/*
 818 * The device tree binding says that the remote endpoint ID of any
 819 * connection between components, up to and including the TCON, of
 820 * the display pipeline should be equal to the actual ID of the local
 821 * component. Thus we can look at any one of the input connections of
 822 * the TCONs, and use that connection's remote endpoint ID as our own.
 823 *
 824 * Since the user of this function already finds the input port,
 825 * the port is passed in directly without further checks.
 826 */
 827static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
 828{
 829        struct device_node *ep;
 830        int ret = -EINVAL;
 831
 832        /* try finding an upstream endpoint */
 833        for_each_available_child_of_node(port, ep) {
 834                struct device_node *remote;
 835                u32 reg;
 836
 837                remote = of_graph_get_remote_endpoint(ep);
 838                if (!remote)
 839                        continue;
 840
 841                ret = of_property_read_u32(remote, "reg", &reg);
 842                if (ret)
 843                        continue;
 844
 845                ret = reg;
 846        }
 847
 848        return ret;
 849}
 850
 851/*
 852 * Once we know the TCON's id, we can look through the list of
 853 * engines to find a matching one. We assume all engines have
 854 * been probed and added to the list.
 855 */
 856static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,
 857                                                        int id)
 858{
 859        struct sunxi_engine *engine;
 860
 861        list_for_each_entry(engine, &drv->engine_list, list)
 862                if (engine->id == id)
 863                        return engine;
 864
 865        return ERR_PTR(-EINVAL);
 866}
 867
 868/*
 869 * On SoCs with the old display pipeline design (Display Engine 1.0),
 870 * we assumed the TCON was always tied to just one backend. However
 871 * this proved not to be the case. On the A31, the TCON can select
 872 * either backend as its source. On the A20 (and likely on the A10),
 873 * the backend can choose which TCON to output to.
 874 *
 875 * The device tree binding says that the remote endpoint ID of any
 876 * connection between components, up to and including the TCON, of
 877 * the display pipeline should be equal to the actual ID of the local
 878 * component. Thus we should be able to look at any one of the input
 879 * connections of the TCONs, and use that connection's remote endpoint
 880 * ID as our own.
 881 *
 882 * However  the connections between the backend and TCON were assumed
 883 * to be always singular, and their endpoit IDs were all incorrectly
 884 * set to 0. This means for these old device trees, we cannot just look
 885 * up the remote endpoint ID of a TCON input endpoint. TCON1 would be
 886 * incorrectly identified as TCON0.
 887 *
 888 * This function first checks if the TCON node has 2 input endpoints.
 889 * If so, then the device tree is a corrected version, and it will use
 890 * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above
 891 * to fetch the ID and engine directly. If not, then it is likely an
 892 * old device trees, where the endpoint IDs were incorrect, but did not
 893 * have endpoint connections between the backend and TCON across
 894 * different display pipelines. It will fall back to the old method of
 895 * traversing the  of_graph to try and find a matching engine by device
 896 * node.
 897 *
 898 * In the case of single display pipeline device trees, either method
 899 * works.
 900 */
 901static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
 902                                                   struct device_node *node)
 903{
 904        struct device_node *port;
 905        struct sunxi_engine *engine;
 906
 907        port = of_graph_get_port_by_id(node, 0);
 908        if (!port)
 909                return ERR_PTR(-EINVAL);
 910
 911        /*
 912         * Is this a corrected device tree with cross pipeline
 913         * connections between the backend and TCON?
 914         */
 915        if (of_get_child_count(port) > 1) {
 916                /* Get our ID directly from an upstream endpoint */
 917                int id = sun4i_tcon_of_get_id_from_port(port);
 918
 919                /* Get our engine by matching our ID */
 920                engine = sun4i_tcon_get_engine_by_id(drv, id);
 921
 922                of_node_put(port);
 923                return engine;
 924        }
 925
 926        /* Fallback to old method by traversing input endpoints */
 927        of_node_put(port);
 928        return sun4i_tcon_find_engine_traverse(drv, node);
 929}
 930
 931static int sun4i_tcon_bind(struct device *dev, struct device *master,
 932                           void *data)
 933{
 934        struct drm_device *drm = data;
 935        struct sun4i_drv *drv = drm->dev_private;
 936        struct sunxi_engine *engine;
 937        struct device_node *remote;
 938        struct sun4i_tcon *tcon;
 939        struct reset_control *edp_rstc;
 940        bool has_lvds_rst, has_lvds_alt, can_lvds;
 941        int ret;
 942
 943        engine = sun4i_tcon_find_engine(drv, dev->of_node);
 944        if (IS_ERR(engine)) {
 945                dev_err(dev, "Couldn't find matching engine\n");
 946                return -EPROBE_DEFER;
 947        }
 948
 949        tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
 950        if (!tcon)
 951                return -ENOMEM;
 952        dev_set_drvdata(dev, tcon);
 953        tcon->drm = drm;
 954        tcon->dev = dev;
 955        tcon->id = engine->id;
 956        tcon->quirks = of_device_get_match_data(dev);
 957
 958        tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
 959        if (IS_ERR(tcon->lcd_rst)) {
 960                dev_err(dev, "Couldn't get our reset line\n");
 961                return PTR_ERR(tcon->lcd_rst);
 962        }
 963
 964        if (tcon->quirks->needs_edp_reset) {
 965                edp_rstc = devm_reset_control_get_shared(dev, "edp");
 966                if (IS_ERR(edp_rstc)) {
 967                        dev_err(dev, "Couldn't get edp reset line\n");
 968                        return PTR_ERR(edp_rstc);
 969                }
 970
 971                ret = reset_control_deassert(edp_rstc);
 972                if (ret) {
 973                        dev_err(dev, "Couldn't deassert edp reset line\n");
 974                        return ret;
 975                }
 976        }
 977
 978        /* Make sure our TCON is reset */
 979        ret = reset_control_reset(tcon->lcd_rst);
 980        if (ret) {
 981                dev_err(dev, "Couldn't deassert our reset line\n");
 982                return ret;
 983        }
 984
 985        if (tcon->quirks->supports_lvds) {
 986                /*
 987                 * This can only be made optional since we've had DT
 988                 * nodes without the LVDS reset properties.
 989                 *
 990                 * If the property is missing, just disable LVDS, and
 991                 * print a warning.
 992                 */
 993                tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds");
 994                if (IS_ERR(tcon->lvds_rst)) {
 995                        dev_err(dev, "Couldn't get our reset line\n");
 996                        return PTR_ERR(tcon->lvds_rst);
 997                } else if (tcon->lvds_rst) {
 998                        has_lvds_rst = true;
 999                        reset_control_reset(tcon->lvds_rst);
1000                } else {
1001                        has_lvds_rst = false;
1002                }
1003
1004                /*
1005                 * This can only be made optional since we've had DT
1006                 * nodes without the LVDS reset properties.
1007                 *
1008                 * If the property is missing, just disable LVDS, and
1009                 * print a warning.
1010                 */
1011                if (tcon->quirks->has_lvds_alt) {
1012                        tcon->lvds_pll = devm_clk_get(dev, "lvds-alt");
1013                        if (IS_ERR(tcon->lvds_pll)) {
1014                                if (PTR_ERR(tcon->lvds_pll) == -ENOENT) {
1015                                        has_lvds_alt = false;
1016                                } else {
1017                                        dev_err(dev, "Couldn't get the LVDS PLL\n");
1018                                        return PTR_ERR(tcon->lvds_pll);
1019                                }
1020                        } else {
1021                                has_lvds_alt = true;
1022                        }
1023                }
1024
1025                if (!has_lvds_rst ||
1026                    (tcon->quirks->has_lvds_alt && !has_lvds_alt)) {
1027                        dev_warn(dev, "Missing LVDS properties, Please upgrade your DT\n");
1028                        dev_warn(dev, "LVDS output disabled\n");
1029                        can_lvds = false;
1030                } else {
1031                        can_lvds = true;
1032                }
1033        } else {
1034                can_lvds = false;
1035        }
1036
1037        ret = sun4i_tcon_init_clocks(dev, tcon);
1038        if (ret) {
1039                dev_err(dev, "Couldn't init our TCON clocks\n");
1040                goto err_assert_reset;
1041        }
1042
1043        ret = sun4i_tcon_init_regmap(dev, tcon);
1044        if (ret) {
1045                dev_err(dev, "Couldn't init our TCON regmap\n");
1046                goto err_free_clocks;
1047        }
1048
1049        if (tcon->quirks->has_channel_0) {
1050                ret = sun4i_dclk_create(dev, tcon);
1051                if (ret) {
1052                        dev_err(dev, "Couldn't create our TCON dot clock\n");
1053                        goto err_free_clocks;
1054                }
1055        }
1056
1057        ret = sun4i_tcon_init_irq(dev, tcon);
1058        if (ret) {
1059                dev_err(dev, "Couldn't init our TCON interrupts\n");
1060                goto err_free_dotclock;
1061        }
1062
1063        tcon->crtc = sun4i_crtc_init(drm, engine, tcon);
1064        if (IS_ERR(tcon->crtc)) {
1065                dev_err(dev, "Couldn't create our CRTC\n");
1066                ret = PTR_ERR(tcon->crtc);
1067                goto err_free_dotclock;
1068        }
1069
1070        /*
1071         * If we have an LVDS panel connected to the TCON, we should
1072         * just probe the LVDS connector. Otherwise, just probe RGB as
1073         * we used to.
1074         */
1075        remote = of_graph_get_remote_node(dev->of_node, 1, 0);
1076        if (of_device_is_compatible(remote, "panel-lvds"))
1077                if (can_lvds)
1078                        ret = sun4i_lvds_init(drm, tcon);
1079                else
1080                        ret = -EINVAL;
1081        else
1082                ret = sun4i_rgb_init(drm, tcon);
1083        of_node_put(remote);
1084
1085        if (ret < 0)
1086                goto err_free_dotclock;
1087
1088        if (tcon->quirks->needs_de_be_mux) {
1089                /*
1090                 * We assume there is no dynamic muxing of backends
1091                 * and TCONs, so we select the backend with same ID.
1092                 *
1093                 * While dynamic selection might be interesting, since
1094                 * the CRTC is tied to the TCON, while the layers are
1095                 * tied to the backends, this means, we will need to
1096                 * switch between groups of layers. There might not be
1097                 * a way to represent this constraint in DRM.
1098                 */
1099                regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
1100                                   SUN4I_TCON0_CTL_SRC_SEL_MASK,
1101                                   tcon->id);
1102                regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
1103                                   SUN4I_TCON1_CTL_SRC_SEL_MASK,
1104                                   tcon->id);
1105        }
1106
1107        list_add_tail(&tcon->list, &drv->tcon_list);
1108
1109        return 0;
1110
1111err_free_dotclock:
1112        if (tcon->quirks->has_channel_0)
1113                sun4i_dclk_free(tcon);
1114err_free_clocks:
1115        sun4i_tcon_free_clocks(tcon);
1116err_assert_reset:
1117        reset_control_assert(tcon->lcd_rst);
1118        return ret;
1119}
1120
1121static void sun4i_tcon_unbind(struct device *dev, struct device *master,
1122                              void *data)
1123{
1124        struct sun4i_tcon *tcon = dev_get_drvdata(dev);
1125
1126        list_del(&tcon->list);
1127        if (tcon->quirks->has_channel_0)
1128                sun4i_dclk_free(tcon);
1129        sun4i_tcon_free_clocks(tcon);
1130}
1131
1132static const struct component_ops sun4i_tcon_ops = {
1133        .bind   = sun4i_tcon_bind,
1134        .unbind = sun4i_tcon_unbind,
1135};
1136
1137static int sun4i_tcon_probe(struct platform_device *pdev)
1138{
1139        struct device_node *node = pdev->dev.of_node;
1140        struct drm_bridge *bridge;
1141        struct drm_panel *panel;
1142        int ret;
1143
1144        ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
1145        if (ret == -EPROBE_DEFER)
1146                return ret;
1147
1148        return component_add(&pdev->dev, &sun4i_tcon_ops);
1149}
1150
1151static int sun4i_tcon_remove(struct platform_device *pdev)
1152{
1153        component_del(&pdev->dev, &sun4i_tcon_ops);
1154
1155        return 0;
1156}
1157
1158/* platform specific TCON muxing callbacks */
1159static int sun4i_a10_tcon_set_mux(struct sun4i_tcon *tcon,
1160                                  const struct drm_encoder *encoder)
1161{
1162        struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
1163        u32 shift;
1164
1165        if (!tcon0)
1166                return -EINVAL;
1167
1168        switch (encoder->encoder_type) {
1169        case DRM_MODE_ENCODER_TMDS:
1170                /* HDMI */
1171                shift = 8;
1172                break;
1173        default:
1174                return -EINVAL;
1175        }
1176
1177        regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
1178                           0x3 << shift, tcon->id << shift);
1179
1180        return 0;
1181}
1182
1183static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,
1184                                  const struct drm_encoder *encoder)
1185{
1186        u32 val;
1187
1188        if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
1189                val = 1;
1190        else
1191                val = 0;
1192
1193        /*
1194         * FIXME: Undocumented bits
1195         */
1196        return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
1197}
1198
1199static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,
1200                              const struct drm_encoder *encoder)
1201{
1202        struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
1203        u32 shift;
1204
1205        if (!tcon0)
1206                return -EINVAL;
1207
1208        switch (encoder->encoder_type) {
1209        case DRM_MODE_ENCODER_TMDS:
1210                /* HDMI */
1211                shift = 8;
1212                break;
1213        default:
1214                /* TODO A31 has MIPI DSI but A31s does not */
1215                return -EINVAL;
1216        }
1217
1218        regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
1219                           0x3 << shift, tcon->id << shift);
1220
1221        return 0;
1222}
1223
1224static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
1225        .has_channel_0          = true,
1226        .has_channel_1          = true,
1227        .set_mux                = sun4i_a10_tcon_set_mux,
1228};
1229
1230static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
1231        .has_channel_0          = true,
1232        .has_channel_1          = true,
1233        .set_mux                = sun5i_a13_tcon_set_mux,
1234};
1235
1236static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
1237        .has_channel_0          = true,
1238        .has_channel_1          = true,
1239        .has_lvds_alt           = true,
1240        .needs_de_be_mux        = true,
1241        .set_mux                = sun6i_tcon_set_mux,
1242};
1243
1244static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
1245        .has_channel_0          = true,
1246        .has_channel_1          = true,
1247        .needs_de_be_mux        = true,
1248};
1249
1250static const struct sun4i_tcon_quirks sun7i_a20_quirks = {
1251        .has_channel_0          = true,
1252        .has_channel_1          = true,
1253        /* Same display pipeline structure as A10 */
1254        .set_mux                = sun4i_a10_tcon_set_mux,
1255};
1256
1257static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
1258        .has_channel_0          = true,
1259        .has_lvds_alt           = true,
1260};
1261
1262static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = {
1263        .supports_lvds          = true,
1264        .has_channel_0          = true,
1265};
1266
1267static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = {
1268        .has_channel_1          = true,
1269};
1270
1271static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
1272        .has_channel_0          = true,
1273};
1274
1275static const struct sun4i_tcon_quirks sun9i_a80_tcon_lcd_quirks = {
1276        .has_channel_0  = true,
1277        .needs_edp_reset = true,
1278};
1279
1280static const struct sun4i_tcon_quirks sun9i_a80_tcon_tv_quirks = {
1281        .has_channel_1  = true,
1282        .needs_edp_reset = true,
1283};
1284
1285/* sun4i_drv uses this list to check if a device node is a TCON */
1286const struct of_device_id sun4i_tcon_of_table[] = {
1287        { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks },
1288        { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
1289        { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
1290        { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
1291        { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks },
1292        { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
1293        { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks },
1294        { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks },
1295        { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
1296        { .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks },
1297        { .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks },
1298        { }
1299};
1300MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
1301EXPORT_SYMBOL(sun4i_tcon_of_table);
1302
1303static struct platform_driver sun4i_tcon_platform_driver = {
1304        .probe          = sun4i_tcon_probe,
1305        .remove         = sun4i_tcon_remove,
1306        .driver         = {
1307                .name           = "sun4i-tcon",
1308                .of_match_table = sun4i_tcon_of_table,
1309        },
1310};
1311module_platform_driver(sun4i_tcon_platform_driver);
1312
1313MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
1314MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");
1315MODULE_LICENSE("GPL");
1316