uboot/drivers/video/sunxi/lcdc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Timing controller driver for Allwinner SoCs.
   4 *
   5 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
   6 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
   7 * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
   8 */
   9
  10#include <common.h>
  11
  12#include <asm/arch/clock.h>
  13#include <asm/arch/lcdc.h>
  14#include <asm/io.h>
  15
  16static int lcdc_get_clk_delay(const struct display_timing *mode, int tcon)
  17{
  18        int delay;
  19
  20        delay = mode->vfront_porch.typ + mode->vsync_len.typ +
  21                mode->vback_porch.typ;
  22        if (mode->flags & DISPLAY_FLAGS_INTERLACED)
  23                delay /= 2;
  24        if (tcon == 1)
  25                delay -= 2;
  26
  27        return (delay > 30) ? 30 : delay;
  28}
  29
  30void lcdc_init(struct sunxi_lcdc_reg * const lcdc)
  31{
  32        /* Init lcdc */
  33        writel(0, &lcdc->ctrl); /* Disable tcon */
  34        writel(0, &lcdc->int0); /* Disable all interrupts */
  35
  36        /* Disable tcon0 dot clock */
  37        clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
  38
  39        /* Set all io lines to tristate */
  40        writel(0xffffffff, &lcdc->tcon0_io_tristate);
  41        writel(0xffffffff, &lcdc->tcon1_io_tristate);
  42}
  43
  44void lcdc_enable(struct sunxi_lcdc_reg * const lcdc, int depth)
  45{
  46        setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
  47#ifdef CONFIG_VIDEO_LCD_IF_LVDS
  48        setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
  49        setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
  50#ifdef CONFIG_SUNXI_GEN_SUN6I
  51        udelay(2); /* delay at least 1200 ns */
  52        setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB);
  53        udelay(2); /* delay at least 1200 ns */
  54        setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC);
  55        if (depth == 18)
  56                setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
  57        else
  58                setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
  59#else
  60        setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
  61        udelay(2); /* delay at least 1200 ns */
  62        setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
  63        udelay(1); /* delay at least 120 ns */
  64        setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
  65        setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
  66#endif
  67#endif
  68}
  69
  70void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
  71                         const struct display_timing *mode,
  72                         int clk_div, bool for_ext_vga_dac,
  73                         int depth, int dclk_phase)
  74{
  75        int bp, clk_delay, total, val;
  76
  77#ifndef CONFIG_SUNXI_DE2
  78        /* Use tcon0 */
  79        clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
  80                        SUNXI_LCDC_CTRL_IO_MAP_TCON0);
  81#endif
  82
  83        clk_delay = lcdc_get_clk_delay(mode, 0);
  84        writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
  85               SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
  86
  87        writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
  88               SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
  89
  90        writel(SUNXI_LCDC_X(mode->hactive.typ) |
  91               SUNXI_LCDC_Y(mode->vactive.typ), &lcdc->tcon0_timing_active);
  92
  93        bp = mode->hsync_len.typ + mode->hback_porch.typ;
  94        total = mode->hactive.typ + mode->hfront_porch.typ + bp;
  95        writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
  96               SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
  97
  98        bp = mode->vsync_len.typ + mode->vback_porch.typ;
  99        total = mode->vactive.typ + mode->vfront_porch.typ + bp;
 100        writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
 101               SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
 102
 103#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_VIDEO_DE2)
 104        writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
 105               SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync);
 106
 107        writel(0, &lcdc->tcon0_hv_intf);
 108        writel(0, &lcdc->tcon0_cpu_intf);
 109#endif
 110#ifdef CONFIG_VIDEO_LCD_IF_LVDS
 111        val = (depth == 18) ? 1 : 0;
 112        writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val) |
 113               SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &lcdc->tcon0_lvds_intf);
 114#endif
 115
 116        if (depth == 18 || depth == 16) {
 117                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
 118                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
 119                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
 120                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
 121                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
 122                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
 123                writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
 124                writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
 125                writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
 126                writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
 127                writel(((depth == 18) ?
 128                        SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
 129                        SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
 130                       &lcdc->tcon0_frm_ctrl);
 131        }
 132
 133        val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(dclk_phase);
 134        if (mode->flags & DISPLAY_FLAGS_HSYNC_LOW)
 135                val |= SUNXI_LCDC_TCON_HSYNC_MASK;
 136        if (mode->flags & DISPLAY_FLAGS_VSYNC_LOW)
 137                val |= SUNXI_LCDC_TCON_VSYNC_MASK;
 138
 139#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
 140        if (for_ext_vga_dac)
 141                val = 0;
 142#endif
 143        writel(val, &lcdc->tcon0_io_polarity);
 144
 145        writel(0, &lcdc->tcon0_io_tristate);
 146}
 147
 148void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
 149                         const struct display_timing *mode,
 150                         bool ext_hvsync, bool is_composite)
 151{
 152        int bp, clk_delay, total, val, yres;
 153
 154#ifndef CONFIG_SUNXI_DE2
 155        /* Use tcon1 */
 156        clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
 157                        SUNXI_LCDC_CTRL_IO_MAP_TCON1);
 158#endif
 159
 160        clk_delay = lcdc_get_clk_delay(mode, 1);
 161        writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
 162               ((mode->flags & DISPLAY_FLAGS_INTERLACED) ?
 163                        SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
 164               SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
 165
 166        yres = mode->vactive.typ;
 167        if (mode->flags & DISPLAY_FLAGS_INTERLACED)
 168                yres /= 2;
 169        writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
 170               &lcdc->tcon1_timing_source);
 171        writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
 172               &lcdc->tcon1_timing_scale);
 173        writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
 174               &lcdc->tcon1_timing_out);
 175
 176        bp = mode->hsync_len.typ + mode->hback_porch.typ;
 177        total = mode->hactive.typ + mode->hfront_porch.typ + bp;
 178        writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
 179               SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
 180
 181        bp = mode->vsync_len.typ + mode->vback_porch.typ;
 182        total = mode->vactive.typ + mode->vfront_porch.typ + bp;
 183        if (!(mode->flags & DISPLAY_FLAGS_INTERLACED))
 184                total *= 2;
 185        writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
 186               SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
 187
 188        writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
 189               SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon1_timing_sync);
 190
 191        if (ext_hvsync) {
 192                val = 0;
 193                if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
 194                        val |= SUNXI_LCDC_TCON_HSYNC_MASK;
 195                if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
 196                        val |= SUNXI_LCDC_TCON_VSYNC_MASK;
 197                writel(val, &lcdc->tcon1_io_polarity);
 198
 199                clrbits_le32(&lcdc->tcon1_io_tristate,
 200                             SUNXI_LCDC_TCON_VSYNC_MASK |
 201                             SUNXI_LCDC_TCON_HSYNC_MASK);
 202        }
 203
 204#ifdef CONFIG_MACH_SUN5I
 205        if (is_composite)
 206                clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK,
 207                                SUNXI_LCDC_MUX_CTRL_SRC0(1));
 208#endif
 209}
 210
 211void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int dotclock,
 212                  int *clk_div, int *clk_double, bool is_composite)
 213{
 214        int value, n, m, min_m, max_m, diff, step;
 215        int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
 216        int best_double = 0;
 217        bool use_mipi_pll = false;
 218
 219#ifdef CONFIG_SUNXI_DE2
 220        step = 6000;
 221#else
 222        step = 3000;
 223#endif
 224
 225        if (tcon == 0) {
 226#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_SUNXI_DE2)
 227                min_m = 6;
 228                max_m = 127;
 229#endif
 230#ifdef CONFIG_VIDEO_LCD_IF_LVDS
 231                min_m = 7;
 232                max_m = 7;
 233#endif
 234        } else {
 235                min_m = 1;
 236                max_m = 15;
 237        }
 238
 239        /*
 240         * Find the lowest divider resulting in a matching clock, if there
 241         * is no match, pick the closest lower clock, as monitors tend to
 242         * not sync to higher frequencies.
 243         */
 244        for (m = min_m; m <= max_m; m++) {
 245#ifndef CONFIG_SUNXI_DE2
 246                n = (m * dotclock) / step;
 247
 248                if ((n >= 9) && (n <= 127)) {
 249                        value = (step * n) / m;
 250                        diff = dotclock - value;
 251                        if (diff < best_diff) {
 252                                best_diff = diff;
 253                                best_m = m;
 254                                best_n = n;
 255                                best_double = 0;
 256                        }
 257                }
 258
 259                /* These are just duplicates */
 260                if (!(m & 1))
 261                        continue;
 262#endif
 263
 264                /* No double clock on DE2 */
 265                n = (m * dotclock) / (step * 2);
 266                if ((n >= 9) && (n <= 127)) {
 267                        value = (step * 2 * n) / m;
 268                        diff = dotclock - value;
 269                        if (diff < best_diff) {
 270                                best_diff = diff;
 271                                best_m = m;
 272                                best_n = n;
 273                                best_double = 1;
 274                        }
 275                }
 276        }
 277
 278#ifdef CONFIG_MACH_SUN6I
 279        /*
 280         * Use the MIPI pll if we've been unable to find any matching setting
 281         * for PLL3, this happens with high dotclocks because of min_m = 6.
 282         */
 283        if (tcon == 0 && best_n == 0) {
 284                use_mipi_pll = true;
 285                best_m = 6;  /* Minimum m for tcon0 */
 286        }
 287
 288        if (use_mipi_pll) {
 289                clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */
 290                clock_set_mipi_pll(best_m * dotclock * 1000);
 291                debug("dotclock: %dkHz = %dkHz via mipi pll\n",
 292                      dotclock, clock_get_mipi_pll() / best_m / 1000);
 293        } else
 294#endif
 295        {
 296                clock_set_pll3(best_n * step * 1000);
 297                debug("dotclock: %dkHz = %dkHz: (%d * %dkHz * %d) / %d\n",
 298                      dotclock,
 299                      (best_double + 1) * clock_get_pll3() / best_m / 1000,
 300                      best_double + 1, step, best_n, best_m);
 301        }
 302
 303        if (tcon == 0) {
 304                u32 pll;
 305
 306                if (use_mipi_pll)
 307                        pll = CCM_LCD_CH0_CTRL_MIPI_PLL;
 308                else if (best_double)
 309                        pll = CCM_LCD_CH0_CTRL_PLL3_2X;
 310                else
 311                        pll = CCM_LCD_CH0_CTRL_PLL3;
 312#ifndef CONFIG_SUNXI_DE2
 313                writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll,
 314                       &ccm->lcd0_ch0_clk_cfg);
 315#else
 316                writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll,
 317                       &ccm->lcd0_clk_cfg);
 318#endif
 319        }
 320#ifndef CONFIG_SUNXI_DE2
 321        else {
 322                writel(CCM_LCD_CH1_CTRL_GATE |
 323                       (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
 324                                      CCM_LCD_CH1_CTRL_PLL3) |
 325                       CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
 326                if (is_composite)
 327                        setbits_le32(&ccm->lcd0_ch1_clk_cfg,
 328                                     CCM_LCD_CH1_CTRL_HALF_SCLK1);
 329        }
 330#endif
 331
 332        *clk_div = best_m;
 333        *clk_double = best_double;
 334}
 335