10be8f203SSimon Glass /* 20be8f203SSimon Glass * Copyright (c) 2011 The Chromium OS Authors. 31a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 40be8f203SSimon Glass */ 5f20b2c06SSimon Glass 60be8f203SSimon Glass #include <common.h> 79e6866d3SSimon Glass #include <dm.h> 80be8f203SSimon Glass #include <fdtdec.h> 99e6866d3SSimon Glass #include <video.h> 100be8f203SSimon Glass #include <asm/system.h> 110be8f203SSimon Glass #include <asm/gpio.h> 1271cafc3fSSimon Glass #include <asm/io.h> 130be8f203SSimon Glass 140be8f203SSimon Glass #include <asm/arch/clock.h> 150be8f203SSimon Glass #include <asm/arch/funcmux.h> 160be8f203SSimon Glass #include <asm/arch/pinmux.h> 170be8f203SSimon Glass #include <asm/arch/pwm.h> 180be8f203SSimon Glass #include <asm/arch/display.h> 190be8f203SSimon Glass #include <asm/arch-tegra/timer.h> 200be8f203SSimon Glass 210be8f203SSimon Glass DECLARE_GLOBAL_DATA_PTR; 220be8f203SSimon Glass 230be8f203SSimon Glass /* These are the stages we go throuh in enabling the LCD */ 240be8f203SSimon Glass enum stage_t { 250be8f203SSimon Glass STAGE_START, 260be8f203SSimon Glass STAGE_PANEL_VDD, 270be8f203SSimon Glass STAGE_LVDS, 280be8f203SSimon Glass STAGE_BACKLIGHT_VDD, 290be8f203SSimon Glass STAGE_PWM, 300be8f203SSimon Glass STAGE_BACKLIGHT_EN, 310be8f203SSimon Glass STAGE_DONE, 320be8f203SSimon Glass }; 330be8f203SSimon Glass 34*bfda0377SSimon Glass #define FDT_LCD_TIMINGS 4 35*bfda0377SSimon Glass 36*bfda0377SSimon Glass enum { 37*bfda0377SSimon Glass FDT_LCD_TIMING_REF_TO_SYNC, 38*bfda0377SSimon Glass FDT_LCD_TIMING_SYNC_WIDTH, 39*bfda0377SSimon Glass FDT_LCD_TIMING_BACK_PORCH, 40*bfda0377SSimon Glass FDT_LCD_TIMING_FRONT_PORCH, 41*bfda0377SSimon Glass 42*bfda0377SSimon Glass FDT_LCD_TIMING_COUNT, 43*bfda0377SSimon Glass }; 44*bfda0377SSimon Glass 45*bfda0377SSimon Glass enum lcd_cache_t { 46*bfda0377SSimon Glass FDT_LCD_CACHE_OFF = 0, 47*bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_THROUGH = 1 << 0, 48*bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_BACK = 1 << 1, 49*bfda0377SSimon Glass FDT_LCD_CACHE_FLUSH = 1 << 2, 50*bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH = FDT_LCD_CACHE_WRITE_BACK | 51*bfda0377SSimon Glass FDT_LCD_CACHE_FLUSH, 52*bfda0377SSimon Glass }; 53*bfda0377SSimon Glass 54ce0c474aSSimon Glass /* Information about the display controller */ 55ce0c474aSSimon Glass struct tegra_lcd_priv { 56f20b2c06SSimon Glass enum stage_t stage; /* Current stage we are at */ 57f20b2c06SSimon Glass unsigned long timer_next; /* Time we can move onto next stage */ 58ce0c474aSSimon Glass int width; /* width in pixels */ 59ce0c474aSSimon Glass int height; /* height in pixels */ 60ce0c474aSSimon Glass int bpp; /* number of bits per pixel */ 61ce0c474aSSimon Glass 62ce0c474aSSimon Glass /* 63ce0c474aSSimon Glass * log2 of number of bpp, in general, unless it bpp is 24 in which 64ce0c474aSSimon Glass * case this field holds 24 also! This is a U-Boot thing. 65ce0c474aSSimon Glass */ 66ce0c474aSSimon Glass int log2_bpp; 67ce0c474aSSimon Glass struct disp_ctlr *disp; /* Display controller to use */ 68ce0c474aSSimon Glass fdt_addr_t frame_buffer; /* Address of frame buffer */ 69ce0c474aSSimon Glass unsigned pixel_clock; /* Pixel clock in Hz */ 70ce0c474aSSimon Glass uint horiz_timing[FDT_LCD_TIMING_COUNT]; /* Horizontal timing */ 71ce0c474aSSimon Glass uint vert_timing[FDT_LCD_TIMING_COUNT]; /* Vertical timing */ 72ce0c474aSSimon Glass int panel_node; /* node offset of panel information */ 73ce0c474aSSimon Glass int pwm_channel; /* PWM channel to use for backlight */ 74ce0c474aSSimon Glass enum lcd_cache_t cache_type; 75ce0c474aSSimon Glass 76ce0c474aSSimon Glass struct gpio_desc backlight_en; /* GPIO for backlight enable */ 77ce0c474aSSimon Glass struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */ 78ce0c474aSSimon Glass struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */ 79ce0c474aSSimon Glass struct gpio_desc panel_vdd; /* GPIO for panel vdd */ 80ce0c474aSSimon Glass /* 81ce0c474aSSimon Glass * Panel required timings 82ce0c474aSSimon Glass * Timing 1: delay between panel_vdd-rise and data-rise 83ce0c474aSSimon Glass * Timing 2: delay between data-rise and backlight_vdd-rise 84ce0c474aSSimon Glass * Timing 3: delay between backlight_vdd and pwm-rise 85ce0c474aSSimon Glass * Timing 4: delay between pwm-rise and backlight_en-rise 86ce0c474aSSimon Glass */ 87ce0c474aSSimon Glass uint panel_timings[FDT_LCD_TIMINGS]; 88ce0c474aSSimon Glass }; 89ce0c474aSSimon Glass 900be8f203SSimon Glass enum { 910be8f203SSimon Glass /* Maximum LCD size we support */ 920be8f203SSimon Glass LCD_MAX_WIDTH = 1366, 930be8f203SSimon Glass LCD_MAX_HEIGHT = 768, 949e6866d3SSimon Glass LCD_MAX_LOG2_BPP = VIDEO_BPP16, 950be8f203SSimon Glass }; 960be8f203SSimon Glass 9771cafc3fSSimon Glass static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) 9871cafc3fSSimon Glass { 9971cafc3fSSimon Glass unsigned h_dda, v_dda; 10071cafc3fSSimon Glass unsigned long val; 10171cafc3fSSimon Glass 10271cafc3fSSimon Glass val = readl(&dc->cmd.disp_win_header); 10371cafc3fSSimon Glass val |= WINDOW_A_SELECT; 10471cafc3fSSimon Glass writel(val, &dc->cmd.disp_win_header); 10571cafc3fSSimon Glass 10671cafc3fSSimon Glass writel(win->fmt, &dc->win.color_depth); 10771cafc3fSSimon Glass 10871cafc3fSSimon Glass clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, 10971cafc3fSSimon Glass BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); 11071cafc3fSSimon Glass 11171cafc3fSSimon Glass val = win->out_x << H_POSITION_SHIFT; 11271cafc3fSSimon Glass val |= win->out_y << V_POSITION_SHIFT; 11371cafc3fSSimon Glass writel(val, &dc->win.pos); 11471cafc3fSSimon Glass 11571cafc3fSSimon Glass val = win->out_w << H_SIZE_SHIFT; 11671cafc3fSSimon Glass val |= win->out_h << V_SIZE_SHIFT; 11771cafc3fSSimon Glass writel(val, &dc->win.size); 11871cafc3fSSimon Glass 11971cafc3fSSimon Glass val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; 12071cafc3fSSimon Glass val |= win->h << V_PRESCALED_SIZE_SHIFT; 12171cafc3fSSimon Glass writel(val, &dc->win.prescaled_size); 12271cafc3fSSimon Glass 12371cafc3fSSimon Glass writel(0, &dc->win.h_initial_dda); 12471cafc3fSSimon Glass writel(0, &dc->win.v_initial_dda); 12571cafc3fSSimon Glass 12671cafc3fSSimon Glass h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); 12771cafc3fSSimon Glass v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); 12871cafc3fSSimon Glass 12971cafc3fSSimon Glass val = h_dda << H_DDA_INC_SHIFT; 13071cafc3fSSimon Glass val |= v_dda << V_DDA_INC_SHIFT; 13171cafc3fSSimon Glass writel(val, &dc->win.dda_increment); 13271cafc3fSSimon Glass 13371cafc3fSSimon Glass writel(win->stride, &dc->win.line_stride); 13471cafc3fSSimon Glass writel(0, &dc->win.buf_stride); 13571cafc3fSSimon Glass 13671cafc3fSSimon Glass val = WIN_ENABLE; 13771cafc3fSSimon Glass if (win->bpp < 24) 13871cafc3fSSimon Glass val |= COLOR_EXPAND; 13971cafc3fSSimon Glass writel(val, &dc->win.win_opt); 14071cafc3fSSimon Glass 14171cafc3fSSimon Glass writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); 14271cafc3fSSimon Glass writel(win->x, &dc->winbuf.addr_h_offset); 14371cafc3fSSimon Glass writel(win->y, &dc->winbuf.addr_v_offset); 14471cafc3fSSimon Glass 14571cafc3fSSimon Glass writel(0xff00, &dc->win.blend_nokey); 14671cafc3fSSimon Glass writel(0xff00, &dc->win.blend_1win); 14771cafc3fSSimon Glass 14871cafc3fSSimon Glass val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; 14971cafc3fSSimon Glass val |= GENERAL_UPDATE | WIN_A_UPDATE; 15071cafc3fSSimon Glass writel(val, &dc->cmd.state_ctrl); 15171cafc3fSSimon Glass } 15271cafc3fSSimon Glass 1539e6866d3SSimon Glass static void write_pair(struct tegra_lcd_priv *priv, int item, u32 *reg) 15471cafc3fSSimon Glass { 1559e6866d3SSimon Glass writel(priv->horiz_timing[item] | 1569e6866d3SSimon Glass (priv->vert_timing[item] << 16), reg); 15771cafc3fSSimon Glass } 15871cafc3fSSimon Glass 15971cafc3fSSimon Glass static int update_display_mode(struct dc_disp_reg *disp, 1609e6866d3SSimon Glass struct tegra_lcd_priv *priv) 16171cafc3fSSimon Glass { 16271cafc3fSSimon Glass unsigned long val; 16371cafc3fSSimon Glass unsigned long rate; 16471cafc3fSSimon Glass unsigned long div; 16571cafc3fSSimon Glass 16671cafc3fSSimon Glass writel(0x0, &disp->disp_timing_opt); 1679e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); 1689e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); 1699e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); 1709e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); 17171cafc3fSSimon Glass 1729e6866d3SSimon Glass writel(priv->width | (priv->height << 16), &disp->disp_active); 17371cafc3fSSimon Glass 17471cafc3fSSimon Glass val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; 17571cafc3fSSimon Glass val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; 17671cafc3fSSimon Glass writel(val, &disp->data_enable_opt); 17771cafc3fSSimon Glass 17871cafc3fSSimon Glass val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; 17971cafc3fSSimon Glass val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; 18071cafc3fSSimon Glass val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; 18171cafc3fSSimon Glass writel(val, &disp->disp_interface_ctrl); 18271cafc3fSSimon Glass 18371cafc3fSSimon Glass /* 18471cafc3fSSimon Glass * The pixel clock divider is in 7.1 format (where the bottom bit 18571cafc3fSSimon Glass * represents 0.5). Here we calculate the divider needed to get from 18671cafc3fSSimon Glass * the display clock (typically 600MHz) to the pixel clock. We round 18771cafc3fSSimon Glass * up or down as requried. 18871cafc3fSSimon Glass */ 18971cafc3fSSimon Glass rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); 1909e6866d3SSimon Glass div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; 19171cafc3fSSimon Glass debug("Display clock %lu, divider %lu\n", rate, div); 19271cafc3fSSimon Glass 19371cafc3fSSimon Glass writel(0x00010001, &disp->shift_clk_opt); 19471cafc3fSSimon Glass 19571cafc3fSSimon Glass val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; 19671cafc3fSSimon Glass val |= div << SHIFT_CLK_DIVIDER_SHIFT; 19771cafc3fSSimon Glass writel(val, &disp->disp_clk_ctrl); 19871cafc3fSSimon Glass 19971cafc3fSSimon Glass return 0; 20071cafc3fSSimon Glass } 20171cafc3fSSimon Glass 20271cafc3fSSimon Glass /* Start up the display and turn on power to PWMs */ 20371cafc3fSSimon Glass static void basic_init(struct dc_cmd_reg *cmd) 20471cafc3fSSimon Glass { 20571cafc3fSSimon Glass u32 val; 20671cafc3fSSimon Glass 20771cafc3fSSimon Glass writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); 20871cafc3fSSimon Glass writel(0x0000011a, &cmd->cont_syncpt_vsync); 20971cafc3fSSimon Glass writel(0x00000000, &cmd->int_type); 21071cafc3fSSimon Glass writel(0x00000000, &cmd->int_polarity); 21171cafc3fSSimon Glass writel(0x00000000, &cmd->int_mask); 21271cafc3fSSimon Glass writel(0x00000000, &cmd->int_enb); 21371cafc3fSSimon Glass 21471cafc3fSSimon Glass val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; 21571cafc3fSSimon Glass val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; 21671cafc3fSSimon Glass val |= PM1_ENABLE; 21771cafc3fSSimon Glass writel(val, &cmd->disp_pow_ctrl); 21871cafc3fSSimon Glass 21971cafc3fSSimon Glass val = readl(&cmd->disp_cmd); 22071cafc3fSSimon Glass val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; 22171cafc3fSSimon Glass writel(val, &cmd->disp_cmd); 22271cafc3fSSimon Glass } 22371cafc3fSSimon Glass 22471cafc3fSSimon Glass static void basic_init_timer(struct dc_disp_reg *disp) 22571cafc3fSSimon Glass { 22671cafc3fSSimon Glass writel(0x00000020, &disp->mem_high_pri); 22771cafc3fSSimon Glass writel(0x00000001, &disp->mem_high_pri_timer); 22871cafc3fSSimon Glass } 22971cafc3fSSimon Glass 23071cafc3fSSimon Glass static const u32 rgb_enb_tab[PIN_REG_COUNT] = { 23171cafc3fSSimon Glass 0x00000000, 23271cafc3fSSimon Glass 0x00000000, 23371cafc3fSSimon Glass 0x00000000, 23471cafc3fSSimon Glass 0x00000000, 23571cafc3fSSimon Glass }; 23671cafc3fSSimon Glass 23771cafc3fSSimon Glass static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { 23871cafc3fSSimon Glass 0x00000000, 23971cafc3fSSimon Glass 0x01000000, 24071cafc3fSSimon Glass 0x00000000, 24171cafc3fSSimon Glass 0x00000000, 24271cafc3fSSimon Glass }; 24371cafc3fSSimon Glass 24471cafc3fSSimon Glass static const u32 rgb_data_tab[PIN_REG_COUNT] = { 24571cafc3fSSimon Glass 0x00000000, 24671cafc3fSSimon Glass 0x00000000, 24771cafc3fSSimon Glass 0x00000000, 24871cafc3fSSimon Glass 0x00000000, 24971cafc3fSSimon Glass }; 25071cafc3fSSimon Glass 25171cafc3fSSimon Glass static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { 25271cafc3fSSimon Glass 0x00000000, 25371cafc3fSSimon Glass 0x00000000, 25471cafc3fSSimon Glass 0x00000000, 25571cafc3fSSimon Glass 0x00000000, 25671cafc3fSSimon Glass 0x00210222, 25771cafc3fSSimon Glass 0x00002200, 25871cafc3fSSimon Glass 0x00020000, 25971cafc3fSSimon Glass }; 26071cafc3fSSimon Glass 26171cafc3fSSimon Glass static void rgb_enable(struct dc_com_reg *com) 26271cafc3fSSimon Glass { 26371cafc3fSSimon Glass int i; 26471cafc3fSSimon Glass 26571cafc3fSSimon Glass for (i = 0; i < PIN_REG_COUNT; i++) { 26671cafc3fSSimon Glass writel(rgb_enb_tab[i], &com->pin_output_enb[i]); 26771cafc3fSSimon Glass writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); 26871cafc3fSSimon Glass writel(rgb_data_tab[i], &com->pin_output_data[i]); 26971cafc3fSSimon Glass } 27071cafc3fSSimon Glass 27171cafc3fSSimon Glass for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) 27271cafc3fSSimon Glass writel(rgb_sel_tab[i], &com->pin_output_sel[i]); 27371cafc3fSSimon Glass } 27471cafc3fSSimon Glass 27571cafc3fSSimon Glass static int setup_window(struct disp_ctl_win *win, 2769e6866d3SSimon Glass struct tegra_lcd_priv *priv) 27771cafc3fSSimon Glass { 27871cafc3fSSimon Glass win->x = 0; 27971cafc3fSSimon Glass win->y = 0; 2809e6866d3SSimon Glass win->w = priv->width; 2819e6866d3SSimon Glass win->h = priv->height; 28271cafc3fSSimon Glass win->out_x = 0; 28371cafc3fSSimon Glass win->out_y = 0; 2849e6866d3SSimon Glass win->out_w = priv->width; 2859e6866d3SSimon Glass win->out_h = priv->height; 2869e6866d3SSimon Glass win->phys_addr = priv->frame_buffer; 2879e6866d3SSimon Glass win->stride = priv->width * (1 << priv->log2_bpp) / 8; 2889e6866d3SSimon Glass debug("%s: depth = %d\n", __func__, priv->log2_bpp); 2899e6866d3SSimon Glass switch (priv->log2_bpp) { 29071cafc3fSSimon Glass case 5: 29171cafc3fSSimon Glass case 24: 29271cafc3fSSimon Glass win->fmt = COLOR_DEPTH_R8G8B8A8; 29371cafc3fSSimon Glass win->bpp = 32; 29471cafc3fSSimon Glass break; 29571cafc3fSSimon Glass case 4: 29671cafc3fSSimon Glass win->fmt = COLOR_DEPTH_B5G6R5; 29771cafc3fSSimon Glass win->bpp = 16; 29871cafc3fSSimon Glass break; 29971cafc3fSSimon Glass 30071cafc3fSSimon Glass default: 30171cafc3fSSimon Glass debug("Unsupported LCD bit depth"); 30271cafc3fSSimon Glass return -1; 30371cafc3fSSimon Glass } 30471cafc3fSSimon Glass 30571cafc3fSSimon Glass return 0; 30671cafc3fSSimon Glass } 30771cafc3fSSimon Glass 30871cafc3fSSimon Glass static void debug_timing(const char *name, unsigned int timing[]) 30971cafc3fSSimon Glass { 31071cafc3fSSimon Glass #ifdef DEBUG 31171cafc3fSSimon Glass int i; 31271cafc3fSSimon Glass 31371cafc3fSSimon Glass debug("%s timing: ", name); 31471cafc3fSSimon Glass for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) 31571cafc3fSSimon Glass debug("%d ", timing[i]); 31671cafc3fSSimon Glass debug("\n"); 31771cafc3fSSimon Glass #endif 31871cafc3fSSimon Glass } 31971cafc3fSSimon Glass 32071cafc3fSSimon Glass /** 32171cafc3fSSimon Glass * Decode panel information from the fdt, according to a standard binding 32271cafc3fSSimon Glass * 32371cafc3fSSimon Glass * @param blob fdt blob 32471cafc3fSSimon Glass * @param node offset of fdt node to read from 3259e6866d3SSimon Glass * @param priv structure to store fdt config into 32671cafc3fSSimon Glass * @return 0 if ok, -ve on error 32771cafc3fSSimon Glass */ 32871cafc3fSSimon Glass static int tegra_decode_panel(const void *blob, int node, 3299e6866d3SSimon Glass struct tegra_lcd_priv *priv) 33071cafc3fSSimon Glass { 33171cafc3fSSimon Glass int front, back, ref; 33271cafc3fSSimon Glass 3339e6866d3SSimon Glass priv->width = fdtdec_get_int(blob, node, "xres", -1); 3349e6866d3SSimon Glass priv->height = fdtdec_get_int(blob, node, "yres", -1); 3359e6866d3SSimon Glass priv->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); 3369e6866d3SSimon Glass if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) { 33771cafc3fSSimon Glass debug("%s: Pixel parameters missing\n", __func__); 33871cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 33971cafc3fSSimon Glass } 34071cafc3fSSimon Glass 34171cafc3fSSimon Glass back = fdtdec_get_int(blob, node, "left-margin", -1); 34271cafc3fSSimon Glass front = fdtdec_get_int(blob, node, "right-margin", -1); 34371cafc3fSSimon Glass ref = fdtdec_get_int(blob, node, "hsync-len", -1); 34471cafc3fSSimon Glass if ((back | front | ref) == -1) { 34571cafc3fSSimon Glass debug("%s: Horizontal parameters missing\n", __func__); 34671cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 34771cafc3fSSimon Glass } 34871cafc3fSSimon Glass 34971cafc3fSSimon Glass /* Use a ref-to-sync of 1 always, and take this from the front porch */ 3509e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 3519e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 3529e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 3539e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 3549e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 3559e6866d3SSimon Glass debug_timing("horiz", priv->horiz_timing); 35671cafc3fSSimon Glass 35771cafc3fSSimon Glass back = fdtdec_get_int(blob, node, "upper-margin", -1); 35871cafc3fSSimon Glass front = fdtdec_get_int(blob, node, "lower-margin", -1); 35971cafc3fSSimon Glass ref = fdtdec_get_int(blob, node, "vsync-len", -1); 36071cafc3fSSimon Glass if ((back | front | ref) == -1) { 36171cafc3fSSimon Glass debug("%s: Vertical parameters missing\n", __func__); 36271cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 36371cafc3fSSimon Glass } 36471cafc3fSSimon Glass 3659e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 3669e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 3679e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 3689e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 3699e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 3709e6866d3SSimon Glass debug_timing("vert", priv->vert_timing); 37171cafc3fSSimon Glass 37271cafc3fSSimon Glass return 0; 37371cafc3fSSimon Glass } 37471cafc3fSSimon Glass 37571cafc3fSSimon Glass /** 37671cafc3fSSimon Glass * Decode the display controller information from the fdt. 37771cafc3fSSimon Glass * 37871cafc3fSSimon Glass * @param blob fdt blob 3799e6866d3SSimon Glass * @param priv structure to store fdt priv into 38071cafc3fSSimon Glass * @return 0 if ok, -ve on error 38171cafc3fSSimon Glass */ 3829e6866d3SSimon Glass static int tegra_display_decode_config(const void *blob, int node, 3839e6866d3SSimon Glass struct tegra_lcd_priv *priv) 38471cafc3fSSimon Glass { 3859e6866d3SSimon Glass int rgb; 38671cafc3fSSimon Glass int bpp, bit; 38771cafc3fSSimon Glass 3889e6866d3SSimon Glass priv->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); 3899e6866d3SSimon Glass if (!priv->disp) { 39071cafc3fSSimon Glass debug("%s: No display controller address\n", __func__); 39171cafc3fSSimon Glass return -1; 39271cafc3fSSimon Glass } 39371cafc3fSSimon Glass 39471cafc3fSSimon Glass rgb = fdt_subnode_offset(blob, node, "rgb"); 39571cafc3fSSimon Glass 3969e6866d3SSimon Glass priv->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); 3979e6866d3SSimon Glass if (priv->panel_node < 0) { 39871cafc3fSSimon Glass debug("%s: Cannot find panel information\n", __func__); 39971cafc3fSSimon Glass return -1; 40071cafc3fSSimon Glass } 40171cafc3fSSimon Glass 4029e6866d3SSimon Glass if (tegra_decode_panel(blob, priv->panel_node, priv)) { 40371cafc3fSSimon Glass debug("%s: Failed to decode panel information\n", __func__); 40471cafc3fSSimon Glass return -1; 40571cafc3fSSimon Glass } 40671cafc3fSSimon Glass 4079e6866d3SSimon Glass bpp = fdtdec_get_int(blob, priv->panel_node, "nvidia,bits-per-pixel", 40871cafc3fSSimon Glass -1); 40971cafc3fSSimon Glass bit = ffs(bpp) - 1; 41071cafc3fSSimon Glass if (bpp == (1 << bit)) 4119e6866d3SSimon Glass priv->log2_bpp = bit; 41271cafc3fSSimon Glass else 4139e6866d3SSimon Glass priv->log2_bpp = bpp; 41471cafc3fSSimon Glass if (bpp == -1) { 41571cafc3fSSimon Glass debug("%s: Pixel bpp parameters missing\n", __func__); 41671cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 41771cafc3fSSimon Glass } 4189e6866d3SSimon Glass priv->bpp = bpp; 41971cafc3fSSimon Glass 42071cafc3fSSimon Glass return 0; 42171cafc3fSSimon Glass } 42271cafc3fSSimon Glass 42371cafc3fSSimon Glass /** 42471cafc3fSSimon Glass * Register a new display based on device tree configuration. 42571cafc3fSSimon Glass * 42671cafc3fSSimon Glass * The frame buffer can be positioned by U-Boot or overriden by the fdt. 42771cafc3fSSimon Glass * You should pass in the U-Boot address here, and check the contents of 428ce0c474aSSimon Glass * struct tegra_lcd_priv to see what was actually chosen. 42971cafc3fSSimon Glass * 43071cafc3fSSimon Glass * @param blob Device tree blob 4319e6866d3SSimon Glass * @param priv Driver's private data 43271cafc3fSSimon Glass * @param default_lcd_base Default address of LCD frame buffer 43371cafc3fSSimon Glass * @return 0 if ok, -1 on error (unsupported bits per pixel) 43471cafc3fSSimon Glass */ 4359e6866d3SSimon Glass static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, 4369e6866d3SSimon Glass void *default_lcd_base) 43771cafc3fSSimon Glass { 43871cafc3fSSimon Glass struct disp_ctl_win window; 43971cafc3fSSimon Glass struct dc_ctlr *dc; 44071cafc3fSSimon Glass 4419e6866d3SSimon Glass priv->frame_buffer = (u32)default_lcd_base; 44271cafc3fSSimon Glass 4439e6866d3SSimon Glass dc = (struct dc_ctlr *)priv->disp; 44471cafc3fSSimon Glass 44571cafc3fSSimon Glass /* 44671cafc3fSSimon Glass * A header file for clock constants was NAKed upstream. 44771cafc3fSSimon Glass * TODO: Put this into the FDT and fdt_lcd struct when we have clock 44871cafc3fSSimon Glass * support there 44971cafc3fSSimon Glass */ 45071cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 45171cafc3fSSimon Glass 144 * 1000000); 45271cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, 45371cafc3fSSimon Glass 600 * 1000000); 45471cafc3fSSimon Glass basic_init(&dc->cmd); 45571cafc3fSSimon Glass basic_init_timer(&dc->disp); 45671cafc3fSSimon Glass rgb_enable(&dc->com); 45771cafc3fSSimon Glass 4589e6866d3SSimon Glass if (priv->pixel_clock) 4599e6866d3SSimon Glass update_display_mode(&dc->disp, priv); 46071cafc3fSSimon Glass 4619e6866d3SSimon Glass if (setup_window(&window, priv)) 46271cafc3fSSimon Glass return -1; 46371cafc3fSSimon Glass 46471cafc3fSSimon Glass update_window(dc, &window); 46571cafc3fSSimon Glass 46671cafc3fSSimon Glass return 0; 46771cafc3fSSimon Glass } 46871cafc3fSSimon Glass 4690be8f203SSimon Glass /** 4700be8f203SSimon Glass * Decode the panel information from the fdt. 4710be8f203SSimon Glass * 4720be8f203SSimon Glass * @param blob fdt blob 4739e6866d3SSimon Glass * @param priv structure to store fdt config into 4740be8f203SSimon Glass * @return 0 if ok, -ve on error 4750be8f203SSimon Glass */ 4769e6866d3SSimon Glass static int fdt_decode_lcd(const void *blob, struct tegra_lcd_priv *priv) 4770be8f203SSimon Glass { 4780be8f203SSimon Glass int display_node; 4790be8f203SSimon Glass 4809e6866d3SSimon Glass display_node = priv->panel_node; 4810be8f203SSimon Glass if (display_node < 0) { 4820be8f203SSimon Glass debug("%s: No panel configuration available\n", __func__); 4830be8f203SSimon Glass return -1; 4840be8f203SSimon Glass } 4850be8f203SSimon Glass 4869e6866d3SSimon Glass priv->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); 4879e6866d3SSimon Glass if (priv->pwm_channel < 0) { 4880be8f203SSimon Glass debug("%s: Unable to request PWM channel\n", __func__); 4890be8f203SSimon Glass return -1; 4900be8f203SSimon Glass } 4910be8f203SSimon Glass 4929e6866d3SSimon Glass priv->cache_type = fdtdec_get_int(blob, display_node, 4930be8f203SSimon Glass "nvidia,cache-type", 4940be8f203SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH); 4950be8f203SSimon Glass 4960be8f203SSimon Glass /* These GPIOs are all optional */ 49704072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 49804072cbaSSimon Glass "nvidia,backlight-enable-gpios", 0, 4999e6866d3SSimon Glass &priv->backlight_en, GPIOD_IS_OUT); 50004072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 50104072cbaSSimon Glass "nvidia,lvds-shutdown-gpios", 0, 5029e6866d3SSimon Glass &priv->lvds_shutdown, GPIOD_IS_OUT); 50304072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 50404072cbaSSimon Glass "nvidia,backlight-vdd-gpios", 0, 5059e6866d3SSimon Glass &priv->backlight_vdd, GPIOD_IS_OUT); 50604072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 50704072cbaSSimon Glass "nvidia,panel-vdd-gpios", 0, 5089e6866d3SSimon Glass &priv->panel_vdd, GPIOD_IS_OUT); 5090be8f203SSimon Glass 5100be8f203SSimon Glass return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", 5119e6866d3SSimon Glass priv->panel_timings, FDT_LCD_TIMINGS); 5120be8f203SSimon Glass } 5130be8f203SSimon Glass 5140be8f203SSimon Glass /** 5150be8f203SSimon Glass * Handle the next stage of device init 5160be8f203SSimon Glass */ 5179e6866d3SSimon Glass static int handle_stage(const void *blob, struct tegra_lcd_priv *priv) 5180be8f203SSimon Glass { 519f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 5200be8f203SSimon Glass 5210be8f203SSimon Glass /* do the things for this stage */ 522f20b2c06SSimon Glass switch (priv->stage) { 5230be8f203SSimon Glass case STAGE_START: 5240be8f203SSimon Glass /* 5250be8f203SSimon Glass * It is possible that the FDT has requested that the LCD be 5260be8f203SSimon Glass * disabled. We currently don't support this. It would require 5270be8f203SSimon Glass * changes to U-Boot LCD subsystem to have LCD support 5280be8f203SSimon Glass * compiled in but not used. An easier option might be to 5290be8f203SSimon Glass * still have a frame buffer, but leave the backlight off and 5300be8f203SSimon Glass * remove all mention of lcd in the stdout environment 5310be8f203SSimon Glass * variable. 5320be8f203SSimon Glass */ 5330be8f203SSimon Glass 5340be8f203SSimon Glass funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 5350be8f203SSimon Glass break; 5360be8f203SSimon Glass case STAGE_PANEL_VDD: 5379e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->panel_vdd)) 5389e6866d3SSimon Glass dm_gpio_set_value(&priv->panel_vdd, 1); 5390be8f203SSimon Glass break; 5400be8f203SSimon Glass case STAGE_LVDS: 5419e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->lvds_shutdown)) 5429e6866d3SSimon Glass dm_gpio_set_value(&priv->lvds_shutdown, 1); 5430be8f203SSimon Glass break; 5440be8f203SSimon Glass case STAGE_BACKLIGHT_VDD: 5459e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_vdd)) 5469e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_vdd, 1); 5470be8f203SSimon Glass break; 5480be8f203SSimon Glass case STAGE_PWM: 5490be8f203SSimon Glass /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 55070ad375eSStephen Warren pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); 55170ad375eSStephen Warren pinmux_tristate_disable(PMUX_PINGRP_GPU); 5520be8f203SSimon Glass 5539e6866d3SSimon Glass pwm_enable(priv->pwm_channel, 32768, 0xdf, 1); 5540be8f203SSimon Glass break; 5550be8f203SSimon Glass case STAGE_BACKLIGHT_EN: 5569e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_en)) 5579e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_en, 1); 5580be8f203SSimon Glass break; 5590be8f203SSimon Glass case STAGE_DONE: 5600be8f203SSimon Glass break; 5610be8f203SSimon Glass } 5620be8f203SSimon Glass 5630be8f203SSimon Glass /* set up timer for next stage */ 564f20b2c06SSimon Glass priv->timer_next = timer_get_us(); 565f20b2c06SSimon Glass if (priv->stage < FDT_LCD_TIMINGS) 566f20b2c06SSimon Glass priv->timer_next += priv->panel_timings[priv->stage] * 1000; 5670be8f203SSimon Glass 5680be8f203SSimon Glass /* move to next stage */ 569f20b2c06SSimon Glass priv->stage++; 5700be8f203SSimon Glass return 0; 5710be8f203SSimon Glass } 5720be8f203SSimon Glass 5739e6866d3SSimon Glass /** 5749e6866d3SSimon Glass * Perform the next stage of the LCD init if it is time to do so. 5759e6866d3SSimon Glass * 5769e6866d3SSimon Glass * LCD init can be time-consuming because of the number of delays we need 5779e6866d3SSimon Glass * while waiting for the backlight power supply, etc. This function can 5789e6866d3SSimon Glass * be called at various times during U-Boot operation to advance the 5799e6866d3SSimon Glass * initialization of the LCD to the next stage if sufficient time has 5809e6866d3SSimon Glass * passed since the last stage. It keeps track of what stage it is up to 5819e6866d3SSimon Glass * and the time that it is permitted to move to the next stage. 5829e6866d3SSimon Glass * 5839e6866d3SSimon Glass * The final call should have wait=1 to complete the init. 5849e6866d3SSimon Glass * 5859e6866d3SSimon Glass * @param blob fdt blob containing LCD information 5869e6866d3SSimon Glass * @param wait 1 to wait until all init is complete, and then return 5879e6866d3SSimon Glass * 0 to return immediately, potentially doing nothing if it is 5889e6866d3SSimon Glass * not yet time for the next init. 5899e6866d3SSimon Glass */ 5909e6866d3SSimon Glass static int tegra_lcd_check_next_stage(const void *blob, 5919e6866d3SSimon Glass struct tegra_lcd_priv *priv, int wait) 5920be8f203SSimon Glass { 593f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 5940be8f203SSimon Glass return 0; 5950be8f203SSimon Glass 5960be8f203SSimon Glass do { 5970be8f203SSimon Glass /* wait if we need to */ 598f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 599f20b2c06SSimon Glass if (priv->stage != STAGE_START) { 600f20b2c06SSimon Glass int delay = priv->timer_next - timer_get_us(); 6010be8f203SSimon Glass 6020be8f203SSimon Glass if (delay > 0) { 6030be8f203SSimon Glass if (wait) 6040be8f203SSimon Glass udelay(delay); 6050be8f203SSimon Glass else 6060be8f203SSimon Glass return 0; 6070be8f203SSimon Glass } 6080be8f203SSimon Glass } 6090be8f203SSimon Glass 6109e6866d3SSimon Glass if (handle_stage(blob, priv)) 6110be8f203SSimon Glass return -1; 612f20b2c06SSimon Glass } while (wait && priv->stage != STAGE_DONE); 613f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 6140be8f203SSimon Glass debug("%s: LCD init complete\n", __func__); 6150be8f203SSimon Glass 6160be8f203SSimon Glass return 0; 6170be8f203SSimon Glass } 6180be8f203SSimon Glass 6199e6866d3SSimon Glass static int tegra_lcd_probe(struct udevice *dev) 6200be8f203SSimon Glass { 6219e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 6229e6866d3SSimon Glass struct video_priv *uc_priv = dev_get_uclass_priv(dev); 6239e6866d3SSimon Glass struct tegra_lcd_priv *priv = dev_get_priv(dev); 6249e6866d3SSimon Glass const void *blob = gd->fdt_blob; 6259e6866d3SSimon Glass int type = DCACHE_OFF; 6269e6866d3SSimon Glass 6279e6866d3SSimon Glass if (tegra_display_decode_config(blob, dev->of_offset, priv)) 6289e6866d3SSimon Glass return -1; 6299e6866d3SSimon Glass 6309e6866d3SSimon Glass /* get panel details */ 6319e6866d3SSimon Glass if (fdt_decode_lcd(blob, priv)) { 6329e6866d3SSimon Glass printf("No valid LCD information in device tree\n"); 6339e6866d3SSimon Glass return -1; 6340be8f203SSimon Glass } 6359e6866d3SSimon Glass 6369e6866d3SSimon Glass /* Initialize the Tegra display controller */ 6379e6866d3SSimon Glass if (tegra_display_probe(blob, priv, (void *)plat->base)) { 6389e6866d3SSimon Glass printf("%s: Failed to probe display driver\n", __func__); 6399e6866d3SSimon Glass return -1; 6400be8f203SSimon Glass } 6419e6866d3SSimon Glass 6429e6866d3SSimon Glass tegra_lcd_check_next_stage(blob, priv, 1); 6439e6866d3SSimon Glass 6449e6866d3SSimon Glass /* Set up the LCD caching as requested */ 6459e6866d3SSimon Glass if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 6469e6866d3SSimon Glass type = DCACHE_WRITETHROUGH; 6479e6866d3SSimon Glass else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK) 6489e6866d3SSimon Glass type = DCACHE_WRITEBACK; 6499e6866d3SSimon Glass mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type); 6509e6866d3SSimon Glass 6519e6866d3SSimon Glass /* Enable flushing after LCD writes if requested */ 6529e6866d3SSimon Glass video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH); 6539e6866d3SSimon Glass 6549e6866d3SSimon Glass uc_priv->xsize = priv->width; 6559e6866d3SSimon Glass uc_priv->ysize = priv->height; 6569e6866d3SSimon Glass uc_priv->bpix = priv->log2_bpp; 6579e6866d3SSimon Glass debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer, 6589e6866d3SSimon Glass plat->size); 6599e6866d3SSimon Glass 6609e6866d3SSimon Glass return 0; 6619e6866d3SSimon Glass } 6629e6866d3SSimon Glass 6639e6866d3SSimon Glass static int tegra_lcd_bind(struct udevice *dev) 6649e6866d3SSimon Glass { 6659e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 6669e6866d3SSimon Glass 6679e6866d3SSimon Glass plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * 6689e6866d3SSimon Glass (1 << LCD_MAX_LOG2_BPP) / 8; 6699e6866d3SSimon Glass 6709e6866d3SSimon Glass return 0; 6719e6866d3SSimon Glass } 6729e6866d3SSimon Glass 6739e6866d3SSimon Glass static const struct video_ops tegra_lcd_ops = { 6749e6866d3SSimon Glass }; 6759e6866d3SSimon Glass 6769e6866d3SSimon Glass static const struct udevice_id tegra_lcd_ids[] = { 6779e6866d3SSimon Glass { .compatible = "nvidia,tegra20-dc" }, 6789e6866d3SSimon Glass { } 6799e6866d3SSimon Glass }; 6809e6866d3SSimon Glass 6819e6866d3SSimon Glass U_BOOT_DRIVER(tegra_lcd) = { 6829e6866d3SSimon Glass .name = "tegra_lcd", 6839e6866d3SSimon Glass .id = UCLASS_VIDEO, 6849e6866d3SSimon Glass .of_match = tegra_lcd_ids, 6859e6866d3SSimon Glass .ops = &tegra_lcd_ops, 6869e6866d3SSimon Glass .bind = tegra_lcd_bind, 6879e6866d3SSimon Glass .probe = tegra_lcd_probe, 6889e6866d3SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_lcd_priv), 6899e6866d3SSimon Glass }; 690