uboot/drivers/video/tegra.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2011 The Chromium OS Authors.
   3 * SPDX-License-Identifier:     GPL-2.0+
   4 */
   5
   6#include <common.h>
   7#include <dm.h>
   8#include <fdtdec.h>
   9#include <panel.h>
  10#include <pwm.h>
  11#include <video.h>
  12#include <asm/system.h>
  13#include <asm/gpio.h>
  14#include <asm/io.h>
  15
  16#include <asm/arch/clock.h>
  17#include <asm/arch/funcmux.h>
  18#include <asm/arch/pinmux.h>
  19#include <asm/arch/pwm.h>
  20#include <asm/arch/display.h>
  21#include <asm/arch-tegra/timer.h>
  22
  23DECLARE_GLOBAL_DATA_PTR;
  24
  25/* Information about the display controller */
  26struct tegra_lcd_priv {
  27        int width;                      /* width in pixels */
  28        int height;                     /* height in pixels */
  29        enum video_log2_bpp log2_bpp;   /* colour depth */
  30        struct display_timing timing;
  31        struct udevice *panel;
  32        struct disp_ctlr *disp;         /* Display controller to use */
  33        fdt_addr_t frame_buffer;        /* Address of frame buffer */
  34        unsigned pixel_clock;           /* Pixel clock in Hz */
  35};
  36
  37enum {
  38        /* Maximum LCD size we support */
  39        LCD_MAX_WIDTH           = 1366,
  40        LCD_MAX_HEIGHT          = 768,
  41        LCD_MAX_LOG2_BPP        = VIDEO_BPP16,
  42};
  43
  44static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
  45{
  46        unsigned h_dda, v_dda;
  47        unsigned long val;
  48
  49        val = readl(&dc->cmd.disp_win_header);
  50        val |= WINDOW_A_SELECT;
  51        writel(val, &dc->cmd.disp_win_header);
  52
  53        writel(win->fmt, &dc->win.color_depth);
  54
  55        clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
  56                        BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
  57
  58        val = win->out_x << H_POSITION_SHIFT;
  59        val |= win->out_y << V_POSITION_SHIFT;
  60        writel(val, &dc->win.pos);
  61
  62        val = win->out_w << H_SIZE_SHIFT;
  63        val |= win->out_h << V_SIZE_SHIFT;
  64        writel(val, &dc->win.size);
  65
  66        val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
  67        val |= win->h << V_PRESCALED_SIZE_SHIFT;
  68        writel(val, &dc->win.prescaled_size);
  69
  70        writel(0, &dc->win.h_initial_dda);
  71        writel(0, &dc->win.v_initial_dda);
  72
  73        h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U);
  74        v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U);
  75
  76        val = h_dda << H_DDA_INC_SHIFT;
  77        val |= v_dda << V_DDA_INC_SHIFT;
  78        writel(val, &dc->win.dda_increment);
  79
  80        writel(win->stride, &dc->win.line_stride);
  81        writel(0, &dc->win.buf_stride);
  82
  83        val = WIN_ENABLE;
  84        if (win->bpp < 24)
  85                val |= COLOR_EXPAND;
  86        writel(val, &dc->win.win_opt);
  87
  88        writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
  89        writel(win->x, &dc->winbuf.addr_h_offset);
  90        writel(win->y, &dc->winbuf.addr_v_offset);
  91
  92        writel(0xff00, &dc->win.blend_nokey);
  93        writel(0xff00, &dc->win.blend_1win);
  94
  95        val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
  96        val |= GENERAL_UPDATE | WIN_A_UPDATE;
  97        writel(val, &dc->cmd.state_ctrl);
  98}
  99
 100static int update_display_mode(struct dc_disp_reg *disp,
 101                               struct tegra_lcd_priv *priv)
 102{
 103        struct display_timing *dt = &priv->timing;
 104        unsigned long val;
 105        unsigned long rate;
 106        unsigned long div;
 107
 108        writel(0x0, &disp->disp_timing_opt);
 109
 110        writel(1 | 1 << 16, &disp->ref_to_sync);
 111        writel(dt->hsync_len.typ | dt->vsync_len.typ << 16, &disp->sync_width);
 112        writel(dt->hback_porch.typ | dt->vback_porch.typ << 16,
 113               &disp->back_porch);
 114        writel((dt->hfront_porch.typ - 1) | (dt->vfront_porch.typ - 1) << 16,
 115               &disp->front_porch);
 116        writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active);
 117
 118        val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
 119        val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
 120        writel(val, &disp->data_enable_opt);
 121
 122        val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
 123        val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
 124        val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
 125        writel(val, &disp->disp_interface_ctrl);
 126
 127        /*
 128         * The pixel clock divider is in 7.1 format (where the bottom bit
 129         * represents 0.5). Here we calculate the divider needed to get from
 130         * the display clock (typically 600MHz) to the pixel clock. We round
 131         * up or down as requried.
 132         */
 133        rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
 134        div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
 135        debug("Display clock %lu, divider %lu\n", rate, div);
 136
 137        writel(0x00010001, &disp->shift_clk_opt);
 138
 139        val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
 140        val |= div << SHIFT_CLK_DIVIDER_SHIFT;
 141        writel(val, &disp->disp_clk_ctrl);
 142
 143        return 0;
 144}
 145
 146/* Start up the display and turn on power to PWMs */
 147static void basic_init(struct dc_cmd_reg *cmd)
 148{
 149        u32 val;
 150
 151        writel(0x00000100, &cmd->gen_incr_syncpt_ctrl);
 152        writel(0x0000011a, &cmd->cont_syncpt_vsync);
 153        writel(0x00000000, &cmd->int_type);
 154        writel(0x00000000, &cmd->int_polarity);
 155        writel(0x00000000, &cmd->int_mask);
 156        writel(0x00000000, &cmd->int_enb);
 157
 158        val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
 159        val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
 160        val |= PM1_ENABLE;
 161        writel(val, &cmd->disp_pow_ctrl);
 162
 163        val = readl(&cmd->disp_cmd);
 164        val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
 165        writel(val, &cmd->disp_cmd);
 166}
 167
 168static void basic_init_timer(struct dc_disp_reg *disp)
 169{
 170        writel(0x00000020, &disp->mem_high_pri);
 171        writel(0x00000001, &disp->mem_high_pri_timer);
 172}
 173
 174static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
 175        0x00000000,
 176        0x00000000,
 177        0x00000000,
 178        0x00000000,
 179};
 180
 181static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
 182        0x00000000,
 183        0x01000000,
 184        0x00000000,
 185        0x00000000,
 186};
 187
 188static const u32 rgb_data_tab[PIN_REG_COUNT] = {
 189        0x00000000,
 190        0x00000000,
 191        0x00000000,
 192        0x00000000,
 193};
 194
 195static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
 196        0x00000000,
 197        0x00000000,
 198        0x00000000,
 199        0x00000000,
 200        0x00210222,
 201        0x00002200,
 202        0x00020000,
 203};
 204
 205static void rgb_enable(struct dc_com_reg *com)
 206{
 207        int i;
 208
 209        for (i = 0; i < PIN_REG_COUNT; i++) {
 210                writel(rgb_enb_tab[i], &com->pin_output_enb[i]);
 211                writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]);
 212                writel(rgb_data_tab[i], &com->pin_output_data[i]);
 213        }
 214
 215        for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
 216                writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
 217}
 218
 219static int setup_window(struct disp_ctl_win *win,
 220                        struct tegra_lcd_priv *priv)
 221{
 222        win->x = 0;
 223        win->y = 0;
 224        win->w = priv->width;
 225        win->h = priv->height;
 226        win->out_x = 0;
 227        win->out_y = 0;
 228        win->out_w = priv->width;
 229        win->out_h = priv->height;
 230        win->phys_addr = priv->frame_buffer;
 231        win->stride = priv->width * (1 << priv->log2_bpp) / 8;
 232        debug("%s: depth = %d\n", __func__, priv->log2_bpp);
 233        switch (priv->log2_bpp) {
 234        case VIDEO_BPP32:
 235                win->fmt = COLOR_DEPTH_R8G8B8A8;
 236                win->bpp = 32;
 237                break;
 238        case VIDEO_BPP16:
 239                win->fmt = COLOR_DEPTH_B5G6R5;
 240                win->bpp = 16;
 241                break;
 242
 243        default:
 244                debug("Unsupported LCD bit depth");
 245                return -1;
 246        }
 247
 248        return 0;
 249}
 250
 251/**
 252 * Register a new display based on device tree configuration.
 253 *
 254 * The frame buffer can be positioned by U-Boot or overridden by the fdt.
 255 * You should pass in the U-Boot address here, and check the contents of
 256 * struct tegra_lcd_priv to see what was actually chosen.
 257 *
 258 * @param blob                  Device tree blob
 259 * @param priv                  Driver's private data
 260 * @param default_lcd_base      Default address of LCD frame buffer
 261 * @return 0 if ok, -1 on error (unsupported bits per pixel)
 262 */
 263static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
 264                               void *default_lcd_base)
 265{
 266        struct disp_ctl_win window;
 267        struct dc_ctlr *dc;
 268
 269        priv->frame_buffer = (u32)default_lcd_base;
 270
 271        dc = (struct dc_ctlr *)priv->disp;
 272
 273        /*
 274         * A header file for clock constants was NAKed upstream.
 275         * TODO: Put this into the FDT and fdt_lcd struct when we have clock
 276         * support there
 277         */
 278        clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
 279                               144 * 1000000);
 280        clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
 281                               600 * 1000000);
 282        basic_init(&dc->cmd);
 283        basic_init_timer(&dc->disp);
 284        rgb_enable(&dc->com);
 285
 286        if (priv->pixel_clock)
 287                update_display_mode(&dc->disp, priv);
 288
 289        if (setup_window(&window, priv))
 290                return -1;
 291
 292        update_window(dc, &window);
 293
 294        return 0;
 295}
 296
 297static int tegra_lcd_probe(struct udevice *dev)
 298{
 299        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 300        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 301        struct tegra_lcd_priv *priv = dev_get_priv(dev);
 302        const void *blob = gd->fdt_blob;
 303        int ret;
 304
 305        /* Initialize the Tegra display controller */
 306        funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
 307        if (tegra_display_probe(blob, priv, (void *)plat->base)) {
 308                printf("%s: Failed to probe display driver\n", __func__);
 309                return -1;
 310        }
 311
 312        pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
 313        pinmux_tristate_disable(PMUX_PINGRP_GPU);
 314
 315        ret = panel_enable_backlight(priv->panel);
 316        if (ret) {
 317                debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret);
 318                return ret;
 319        }
 320
 321        mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size,
 322                                        DCACHE_WRITETHROUGH);
 323
 324        /* Enable flushing after LCD writes if requested */
 325        video_set_flush_dcache(dev, true);
 326
 327        uc_priv->xsize = priv->width;
 328        uc_priv->ysize = priv->height;
 329        uc_priv->bpix = priv->log2_bpp;
 330        debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer,
 331              plat->size);
 332
 333        return 0;
 334}
 335
 336static int tegra_lcd_ofdata_to_platdata(struct udevice *dev)
 337{
 338        struct tegra_lcd_priv *priv = dev_get_priv(dev);
 339        const void *blob = gd->fdt_blob;
 340        struct display_timing *timing;
 341        int node = dev->of_offset;
 342        int panel_node;
 343        int rgb;
 344        int ret;
 345
 346        priv->disp = (struct disp_ctlr *)dev_get_addr(dev);
 347        if (!priv->disp) {
 348                debug("%s: No display controller address\n", __func__);
 349                return -EINVAL;
 350        }
 351
 352        rgb = fdt_subnode_offset(blob, node, "rgb");
 353        if (rgb < 0) {
 354                debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
 355                      __func__, dev->name, rgb);
 356                return -EINVAL;
 357        }
 358
 359        ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
 360        if (ret) {
 361                debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
 362                      __func__, dev->name, ret);
 363                return -EINVAL;
 364        }
 365        timing = &priv->timing;
 366        priv->width = timing->hactive.typ;
 367        priv->height = timing->vactive.typ;
 368        priv->pixel_clock = timing->pixelclock.typ;
 369        priv->log2_bpp = VIDEO_BPP16;
 370
 371        /*
 372         * Sadly the panel phandle is in an rgb subnode so we cannot use
 373         * uclass_get_device_by_phandle().
 374         */
 375        panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
 376        if (panel_node < 0) {
 377                debug("%s: Cannot find panel information\n", __func__);
 378                return -EINVAL;
 379        }
 380        ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node,
 381                                             &priv->panel);
 382        if (ret) {
 383                debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
 384                      dev->name, ret);
 385                return ret;
 386        }
 387
 388        return 0;
 389}
 390
 391static int tegra_lcd_bind(struct udevice *dev)
 392{
 393        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 394        const void *blob = gd->fdt_blob;
 395        int node = dev->of_offset;
 396        int rgb;
 397
 398        rgb = fdt_subnode_offset(blob, node, "rgb");
 399        if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb))
 400                return -ENODEV;
 401
 402        plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
 403                (1 << LCD_MAX_LOG2_BPP) / 8;
 404
 405        return 0;
 406}
 407
 408static const struct video_ops tegra_lcd_ops = {
 409};
 410
 411static const struct udevice_id tegra_lcd_ids[] = {
 412        { .compatible = "nvidia,tegra20-dc" },
 413        { }
 414};
 415
 416U_BOOT_DRIVER(tegra_lcd) = {
 417        .name   = "tegra_lcd",
 418        .id     = UCLASS_VIDEO,
 419        .of_match = tegra_lcd_ids,
 420        .ops    = &tegra_lcd_ops,
 421        .bind   = tegra_lcd_bind,
 422        .probe  = tegra_lcd_probe,
 423        .ofdata_to_platdata     = tegra_lcd_ofdata_to_platdata,
 424        .priv_auto_alloc_size   = sizeof(struct tegra_lcd_priv),
 425};
 426