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