uboot/drivers/video/sunxi/sunxi_dw_hdmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Allwinner DW HDMI bridge
   4 *
   5 * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
   6 */
   7
   8#include <common.h>
   9#include <display.h>
  10#include <dm.h>
  11#include <dw_hdmi.h>
  12#include <edid.h>
  13#include <asm/io.h>
  14#include <asm/arch/clock.h>
  15#include <asm/arch/lcdc.h>
  16
  17struct sunxi_dw_hdmi_priv {
  18        struct dw_hdmi hdmi;
  19        int mux;
  20};
  21
  22struct sunxi_hdmi_phy {
  23        u32 pol;
  24        u32 res1[3];
  25        u32 read_en;
  26        u32 unscramble;
  27        u32 res2[2];
  28        u32 ctrl;
  29        u32 unk1;
  30        u32 unk2;
  31        u32 pll;
  32        u32 clk;
  33        u32 unk3;
  34        u32 status;
  35};
  36
  37#define HDMI_PHY_OFFS 0x10000
  38
  39static int sunxi_dw_hdmi_get_divider(uint clock)
  40{
  41        /*
  42         * Due to missing documentaion of HDMI PHY, we know correct
  43         * settings only for following four PHY dividers. Select one
  44         * based on clock speed.
  45         */
  46        if (clock <= 27000000)
  47                return 11;
  48        else if (clock <= 74250000)
  49                return 4;
  50        else if (clock <= 148500000)
  51                return 2;
  52        else
  53                return 1;
  54}
  55
  56static void sunxi_dw_hdmi_phy_init(void)
  57{
  58        struct sunxi_hdmi_phy * const phy =
  59                (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
  60        unsigned long tmo;
  61        u32 tmp;
  62
  63        /*
  64         * HDMI PHY settings are taken as-is from Allwinner BSP code.
  65         * There is no documentation.
  66         */
  67        writel(0, &phy->ctrl);
  68        setbits_le32(&phy->ctrl, BIT(0));
  69        udelay(5);
  70        setbits_le32(&phy->ctrl, BIT(16));
  71        setbits_le32(&phy->ctrl, BIT(1));
  72        udelay(10);
  73        setbits_le32(&phy->ctrl, BIT(2));
  74        udelay(5);
  75        setbits_le32(&phy->ctrl, BIT(3));
  76        udelay(40);
  77        setbits_le32(&phy->ctrl, BIT(19));
  78        udelay(100);
  79        setbits_le32(&phy->ctrl, BIT(18));
  80        setbits_le32(&phy->ctrl, 7 << 4);
  81
  82        /* Note that Allwinner code doesn't fail in case of timeout */
  83        tmo = timer_get_us() + 2000;
  84        while ((readl(&phy->status) & 0x80) == 0) {
  85                if (timer_get_us() > tmo) {
  86                        printf("Warning: HDMI PHY init timeout!\n");
  87                        break;
  88                }
  89        }
  90
  91        setbits_le32(&phy->ctrl, 0xf << 8);
  92        setbits_le32(&phy->ctrl, BIT(7));
  93
  94        writel(0x39dc5040, &phy->pll);
  95        writel(0x80084343, &phy->clk);
  96        udelay(10000);
  97        writel(1, &phy->unk3);
  98        setbits_le32(&phy->pll, BIT(25));
  99        udelay(100000);
 100        tmp = (readl(&phy->status) & 0x1f800) >> 11;
 101        setbits_le32(&phy->pll, BIT(31) | BIT(30));
 102        setbits_le32(&phy->pll, tmp);
 103        writel(0x01FF0F7F, &phy->ctrl);
 104        writel(0x80639000, &phy->unk1);
 105        writel(0x0F81C405, &phy->unk2);
 106
 107        /* enable read access to HDMI controller */
 108        writel(0x54524545, &phy->read_en);
 109        /* descramble register offsets */
 110        writel(0x42494E47, &phy->unscramble);
 111}
 112
 113static int sunxi_dw_hdmi_get_plug_in_status(void)
 114{
 115        struct sunxi_hdmi_phy * const phy =
 116                (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
 117
 118        return !!(readl(&phy->status) & (1 << 19));
 119}
 120
 121static int sunxi_dw_hdmi_wait_for_hpd(void)
 122{
 123        ulong start;
 124
 125        start = get_timer(0);
 126        do {
 127                if (sunxi_dw_hdmi_get_plug_in_status())
 128                        return 0;
 129                udelay(100);
 130        } while (get_timer(start) < 300);
 131
 132        return -1;
 133}
 134
 135static void sunxi_dw_hdmi_phy_set(uint clock)
 136{
 137        struct sunxi_hdmi_phy * const phy =
 138                (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
 139        int div = sunxi_dw_hdmi_get_divider(clock);
 140        u32 tmp;
 141
 142        /*
 143         * Unfortunately, we don't know much about those magic
 144         * numbers. They are taken from Allwinner BSP driver.
 145         */
 146        switch (div) {
 147        case 1:
 148                writel(0x30dc5fc0, &phy->pll);
 149                writel(0x800863C0, &phy->clk);
 150                mdelay(10);
 151                writel(0x00000001, &phy->unk3);
 152                setbits_le32(&phy->pll, BIT(25));
 153                mdelay(200);
 154                tmp = (readl(&phy->status) & 0x1f800) >> 11;
 155                setbits_le32(&phy->pll, BIT(31) | BIT(30));
 156                if (tmp < 0x3d)
 157                        setbits_le32(&phy->pll, tmp + 2);
 158                else
 159                        setbits_le32(&phy->pll, 0x3f);
 160                mdelay(100);
 161                writel(0x01FFFF7F, &phy->ctrl);
 162                writel(0x8063b000, &phy->unk1);
 163                writel(0x0F8246B5, &phy->unk2);
 164                break;
 165        case 2:
 166                writel(0x39dc5040, &phy->pll);
 167                writel(0x80084381, &phy->clk);
 168                mdelay(10);
 169                writel(0x00000001, &phy->unk3);
 170                setbits_le32(&phy->pll, BIT(25));
 171                mdelay(100);
 172                tmp = (readl(&phy->status) & 0x1f800) >> 11;
 173                setbits_le32(&phy->pll, BIT(31) | BIT(30));
 174                setbits_le32(&phy->pll, tmp);
 175                writel(0x01FFFF7F, &phy->ctrl);
 176                writel(0x8063a800, &phy->unk1);
 177                writel(0x0F81C485, &phy->unk2);
 178                break;
 179        case 4:
 180                writel(0x39dc5040, &phy->pll);
 181                writel(0x80084343, &phy->clk);
 182                mdelay(10);
 183                writel(0x00000001, &phy->unk3);
 184                setbits_le32(&phy->pll, BIT(25));
 185                mdelay(100);
 186                tmp = (readl(&phy->status) & 0x1f800) >> 11;
 187                setbits_le32(&phy->pll, BIT(31) | BIT(30));
 188                setbits_le32(&phy->pll, tmp);
 189                writel(0x01FFFF7F, &phy->ctrl);
 190                writel(0x8063b000, &phy->unk1);
 191                writel(0x0F81C405, &phy->unk2);
 192                break;
 193        case 11:
 194                writel(0x39dc5040, &phy->pll);
 195                writel(0x8008430a, &phy->clk);
 196                mdelay(10);
 197                writel(0x00000001, &phy->unk3);
 198                setbits_le32(&phy->pll, BIT(25));
 199                mdelay(100);
 200                tmp = (readl(&phy->status) & 0x1f800) >> 11;
 201                setbits_le32(&phy->pll, BIT(31) | BIT(30));
 202                setbits_le32(&phy->pll, tmp);
 203                writel(0x01FFFF7F, &phy->ctrl);
 204                writel(0x8063b000, &phy->unk1);
 205                writel(0x0F81C405, &phy->unk2);
 206                break;
 207        }
 208}
 209
 210static void sunxi_dw_hdmi_pll_set(uint clk_khz)
 211{
 212        int value, n, m, div = 0, diff;
 213        int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
 214
 215        div = sunxi_dw_hdmi_get_divider(clk_khz * 1000);
 216
 217        /*
 218         * Find the lowest divider resulting in a matching clock. If there
 219         * is no match, pick the closest lower clock, as monitors tend to
 220         * not sync to higher frequencies.
 221         */
 222        for (m = 1; m <= 16; m++) {
 223                n = (m * div * clk_khz) / 24000;
 224
 225                if ((n >= 1) && (n <= 128)) {
 226                        value = (24000 * n) / m / div;
 227                        diff = clk_khz - value;
 228                        if (diff < best_diff) {
 229                                best_diff = diff;
 230                                best_m = m;
 231                                best_n = n;
 232                        }
 233                }
 234        }
 235
 236        clock_set_pll3_factors(best_m, best_n);
 237        debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
 238              clk_khz, (clock_get_pll3() / 1000) / div,
 239              best_n, best_m, div);
 240}
 241
 242static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
 243                                    int bpp)
 244{
 245        struct sunxi_ccm_reg * const ccm =
 246                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 247        int div = sunxi_dw_hdmi_get_divider(edid->pixelclock.typ);
 248        struct sunxi_lcdc_reg *lcdc;
 249
 250        if (mux == 0) {
 251                lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
 252
 253                /* Reset off */
 254                setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
 255
 256                /* Clock on */
 257                setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
 258                writel(CCM_LCD0_CTRL_GATE | CCM_LCD0_CTRL_M(div),
 259                       &ccm->lcd0_clk_cfg);
 260        } else {
 261                lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
 262
 263                /* Reset off */
 264                setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD1);
 265
 266                /* Clock on */
 267                setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD1);
 268                writel(CCM_LCD1_CTRL_GATE | CCM_LCD1_CTRL_M(div),
 269                       &ccm->lcd1_clk_cfg);
 270        }
 271
 272        lcdc_init(lcdc);
 273        lcdc_tcon1_mode_set(lcdc, edid, false, false);
 274        lcdc_enable(lcdc, bpp);
 275}
 276
 277static int sunxi_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
 278{
 279        sunxi_dw_hdmi_pll_set(mpixelclock/1000);
 280        sunxi_dw_hdmi_phy_set(mpixelclock);
 281
 282        return 0;
 283}
 284
 285static int sunxi_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
 286{
 287        struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
 288
 289        return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
 290}
 291
 292static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
 293                                const struct display_timing *edid)
 294{
 295        struct sunxi_hdmi_phy * const phy =
 296                (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
 297        struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
 298        int ret;
 299
 300        ret = dw_hdmi_enable(&priv->hdmi, edid);
 301        if (ret)
 302                return ret;
 303
 304        sunxi_dw_hdmi_lcdc_init(priv->mux, edid, panel_bpp);
 305
 306        if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW)
 307                setbits_le32(&phy->pol, 0x200);
 308
 309        if (edid->flags & DISPLAY_FLAGS_HSYNC_LOW)
 310                setbits_le32(&phy->pol, 0x100);
 311
 312        setbits_le32(&phy->ctrl, 0xf << 12);
 313
 314        /*
 315         * This is last hdmi access before boot, so scramble addresses
 316         * again or othwerwise BSP driver won't work. Dummy read is
 317         * needed or otherwise last write doesn't get written correctly.
 318         */
 319        (void)readb(SUNXI_HDMI_BASE);
 320        writel(0, &phy->unscramble);
 321
 322        return 0;
 323}
 324
 325static int sunxi_dw_hdmi_probe(struct udevice *dev)
 326{
 327        struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
 328        struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
 329        struct sunxi_ccm_reg * const ccm =
 330                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 331        int ret;
 332
 333        /* Set pll3 to 297 MHz */
 334        clock_set_pll3(297000000);
 335
 336        /* Set hdmi parent to pll3 */
 337        clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
 338                        CCM_HDMI_CTRL_PLL3);
 339
 340        /* Set ahb gating to pass */
 341        setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
 342        setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
 343        setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
 344        setbits_le32(&ccm->hdmi_slow_clk_cfg, CCM_HDMI_SLOW_CTRL_DDC_GATE);
 345
 346        /* Clock on */
 347        setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
 348
 349        sunxi_dw_hdmi_phy_init();
 350
 351        ret = sunxi_dw_hdmi_wait_for_hpd();
 352        if (ret < 0) {
 353                debug("hdmi can not get hpd signal\n");
 354                return -1;
 355        }
 356
 357        priv->hdmi.ioaddr = SUNXI_HDMI_BASE;
 358        priv->hdmi.i2c_clk_high = 0xd8;
 359        priv->hdmi.i2c_clk_low = 0xfe;
 360        priv->hdmi.reg_io_width = 1;
 361        priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg;
 362        priv->mux = uc_plat->source_id;
 363
 364        dw_hdmi_init(&priv->hdmi);
 365
 366        return 0;
 367}
 368
 369static const struct dm_display_ops sunxi_dw_hdmi_ops = {
 370        .read_edid = sunxi_dw_hdmi_read_edid,
 371        .enable = sunxi_dw_hdmi_enable,
 372};
 373
 374U_BOOT_DRIVER(sunxi_dw_hdmi) = {
 375        .name   = "sunxi_dw_hdmi",
 376        .id     = UCLASS_DISPLAY,
 377        .ops    = &sunxi_dw_hdmi_ops,
 378        .probe  = sunxi_dw_hdmi_probe,
 379        .priv_auto_alloc_size = sizeof(struct sunxi_dw_hdmi_priv),
 380};
 381
 382U_BOOT_DEVICE(sunxi_dw_hdmi) = {
 383        .name = "sunxi_dw_hdmi"
 384};
 385