10be8f203SSimon Glass /* 20be8f203SSimon Glass * Copyright (c) 2011 The Chromium OS Authors. 31a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 40be8f203SSimon Glass */ 5*f20b2c06SSimon 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 34ce0c474aSSimon Glass /* Information about the display controller */ 35ce0c474aSSimon Glass struct tegra_lcd_priv { 36*f20b2c06SSimon Glass enum stage_t stage; /* Current stage we are at */ 37*f20b2c06SSimon Glass unsigned long timer_next; /* Time we can move onto next stage */ 38ce0c474aSSimon Glass int width; /* width in pixels */ 39ce0c474aSSimon Glass int height; /* height in pixels */ 40ce0c474aSSimon Glass int bpp; /* number of bits per pixel */ 41ce0c474aSSimon Glass 42ce0c474aSSimon Glass /* 43ce0c474aSSimon Glass * log2 of number of bpp, in general, unless it bpp is 24 in which 44ce0c474aSSimon Glass * case this field holds 24 also! This is a U-Boot thing. 45ce0c474aSSimon Glass */ 46ce0c474aSSimon Glass int log2_bpp; 47ce0c474aSSimon Glass struct disp_ctlr *disp; /* Display controller to use */ 48ce0c474aSSimon Glass fdt_addr_t frame_buffer; /* Address of frame buffer */ 49ce0c474aSSimon Glass unsigned pixel_clock; /* Pixel clock in Hz */ 50ce0c474aSSimon Glass uint horiz_timing[FDT_LCD_TIMING_COUNT]; /* Horizontal timing */ 51ce0c474aSSimon Glass uint vert_timing[FDT_LCD_TIMING_COUNT]; /* Vertical timing */ 52ce0c474aSSimon Glass int panel_node; /* node offset of panel information */ 53ce0c474aSSimon Glass int pwm_channel; /* PWM channel to use for backlight */ 54ce0c474aSSimon Glass enum lcd_cache_t cache_type; 55ce0c474aSSimon Glass 56ce0c474aSSimon Glass struct gpio_desc backlight_en; /* GPIO for backlight enable */ 57ce0c474aSSimon Glass struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */ 58ce0c474aSSimon Glass struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */ 59ce0c474aSSimon Glass struct gpio_desc panel_vdd; /* GPIO for panel vdd */ 60ce0c474aSSimon Glass /* 61ce0c474aSSimon Glass * Panel required timings 62ce0c474aSSimon Glass * Timing 1: delay between panel_vdd-rise and data-rise 63ce0c474aSSimon Glass * Timing 2: delay between data-rise and backlight_vdd-rise 64ce0c474aSSimon Glass * Timing 3: delay between backlight_vdd and pwm-rise 65ce0c474aSSimon Glass * Timing 4: delay between pwm-rise and backlight_en-rise 66ce0c474aSSimon Glass */ 67ce0c474aSSimon Glass uint panel_timings[FDT_LCD_TIMINGS]; 68ce0c474aSSimon Glass }; 69ce0c474aSSimon Glass 700be8f203SSimon Glass enum { 710be8f203SSimon Glass /* Maximum LCD size we support */ 720be8f203SSimon Glass LCD_MAX_WIDTH = 1366, 730be8f203SSimon Glass LCD_MAX_HEIGHT = 768, 749e6866d3SSimon Glass LCD_MAX_LOG2_BPP = VIDEO_BPP16, 750be8f203SSimon Glass }; 760be8f203SSimon Glass 7771cafc3fSSimon Glass static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) 7871cafc3fSSimon Glass { 7971cafc3fSSimon Glass unsigned h_dda, v_dda; 8071cafc3fSSimon Glass unsigned long val; 8171cafc3fSSimon Glass 8271cafc3fSSimon Glass val = readl(&dc->cmd.disp_win_header); 8371cafc3fSSimon Glass val |= WINDOW_A_SELECT; 8471cafc3fSSimon Glass writel(val, &dc->cmd.disp_win_header); 8571cafc3fSSimon Glass 8671cafc3fSSimon Glass writel(win->fmt, &dc->win.color_depth); 8771cafc3fSSimon Glass 8871cafc3fSSimon Glass clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, 8971cafc3fSSimon Glass BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); 9071cafc3fSSimon Glass 9171cafc3fSSimon Glass val = win->out_x << H_POSITION_SHIFT; 9271cafc3fSSimon Glass val |= win->out_y << V_POSITION_SHIFT; 9371cafc3fSSimon Glass writel(val, &dc->win.pos); 9471cafc3fSSimon Glass 9571cafc3fSSimon Glass val = win->out_w << H_SIZE_SHIFT; 9671cafc3fSSimon Glass val |= win->out_h << V_SIZE_SHIFT; 9771cafc3fSSimon Glass writel(val, &dc->win.size); 9871cafc3fSSimon Glass 9971cafc3fSSimon Glass val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; 10071cafc3fSSimon Glass val |= win->h << V_PRESCALED_SIZE_SHIFT; 10171cafc3fSSimon Glass writel(val, &dc->win.prescaled_size); 10271cafc3fSSimon Glass 10371cafc3fSSimon Glass writel(0, &dc->win.h_initial_dda); 10471cafc3fSSimon Glass writel(0, &dc->win.v_initial_dda); 10571cafc3fSSimon Glass 10671cafc3fSSimon Glass h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); 10771cafc3fSSimon Glass v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); 10871cafc3fSSimon Glass 10971cafc3fSSimon Glass val = h_dda << H_DDA_INC_SHIFT; 11071cafc3fSSimon Glass val |= v_dda << V_DDA_INC_SHIFT; 11171cafc3fSSimon Glass writel(val, &dc->win.dda_increment); 11271cafc3fSSimon Glass 11371cafc3fSSimon Glass writel(win->stride, &dc->win.line_stride); 11471cafc3fSSimon Glass writel(0, &dc->win.buf_stride); 11571cafc3fSSimon Glass 11671cafc3fSSimon Glass val = WIN_ENABLE; 11771cafc3fSSimon Glass if (win->bpp < 24) 11871cafc3fSSimon Glass val |= COLOR_EXPAND; 11971cafc3fSSimon Glass writel(val, &dc->win.win_opt); 12071cafc3fSSimon Glass 12171cafc3fSSimon Glass writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); 12271cafc3fSSimon Glass writel(win->x, &dc->winbuf.addr_h_offset); 12371cafc3fSSimon Glass writel(win->y, &dc->winbuf.addr_v_offset); 12471cafc3fSSimon Glass 12571cafc3fSSimon Glass writel(0xff00, &dc->win.blend_nokey); 12671cafc3fSSimon Glass writel(0xff00, &dc->win.blend_1win); 12771cafc3fSSimon Glass 12871cafc3fSSimon Glass val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; 12971cafc3fSSimon Glass val |= GENERAL_UPDATE | WIN_A_UPDATE; 13071cafc3fSSimon Glass writel(val, &dc->cmd.state_ctrl); 13171cafc3fSSimon Glass } 13271cafc3fSSimon Glass 1339e6866d3SSimon Glass static void write_pair(struct tegra_lcd_priv *priv, int item, u32 *reg) 13471cafc3fSSimon Glass { 1359e6866d3SSimon Glass writel(priv->horiz_timing[item] | 1369e6866d3SSimon Glass (priv->vert_timing[item] << 16), reg); 13771cafc3fSSimon Glass } 13871cafc3fSSimon Glass 13971cafc3fSSimon Glass static int update_display_mode(struct dc_disp_reg *disp, 1409e6866d3SSimon Glass struct tegra_lcd_priv *priv) 14171cafc3fSSimon Glass { 14271cafc3fSSimon Glass unsigned long val; 14371cafc3fSSimon Glass unsigned long rate; 14471cafc3fSSimon Glass unsigned long div; 14571cafc3fSSimon Glass 14671cafc3fSSimon Glass writel(0x0, &disp->disp_timing_opt); 1479e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); 1489e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); 1499e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); 1509e6866d3SSimon Glass write_pair(priv, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); 15171cafc3fSSimon Glass 1529e6866d3SSimon Glass writel(priv->width | (priv->height << 16), &disp->disp_active); 15371cafc3fSSimon Glass 15471cafc3fSSimon Glass val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; 15571cafc3fSSimon Glass val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; 15671cafc3fSSimon Glass writel(val, &disp->data_enable_opt); 15771cafc3fSSimon Glass 15871cafc3fSSimon Glass val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; 15971cafc3fSSimon Glass val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; 16071cafc3fSSimon Glass val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; 16171cafc3fSSimon Glass writel(val, &disp->disp_interface_ctrl); 16271cafc3fSSimon Glass 16371cafc3fSSimon Glass /* 16471cafc3fSSimon Glass * The pixel clock divider is in 7.1 format (where the bottom bit 16571cafc3fSSimon Glass * represents 0.5). Here we calculate the divider needed to get from 16671cafc3fSSimon Glass * the display clock (typically 600MHz) to the pixel clock. We round 16771cafc3fSSimon Glass * up or down as requried. 16871cafc3fSSimon Glass */ 16971cafc3fSSimon Glass rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); 1709e6866d3SSimon Glass div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; 17171cafc3fSSimon Glass debug("Display clock %lu, divider %lu\n", rate, div); 17271cafc3fSSimon Glass 17371cafc3fSSimon Glass writel(0x00010001, &disp->shift_clk_opt); 17471cafc3fSSimon Glass 17571cafc3fSSimon Glass val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; 17671cafc3fSSimon Glass val |= div << SHIFT_CLK_DIVIDER_SHIFT; 17771cafc3fSSimon Glass writel(val, &disp->disp_clk_ctrl); 17871cafc3fSSimon Glass 17971cafc3fSSimon Glass return 0; 18071cafc3fSSimon Glass } 18171cafc3fSSimon Glass 18271cafc3fSSimon Glass /* Start up the display and turn on power to PWMs */ 18371cafc3fSSimon Glass static void basic_init(struct dc_cmd_reg *cmd) 18471cafc3fSSimon Glass { 18571cafc3fSSimon Glass u32 val; 18671cafc3fSSimon Glass 18771cafc3fSSimon Glass writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); 18871cafc3fSSimon Glass writel(0x0000011a, &cmd->cont_syncpt_vsync); 18971cafc3fSSimon Glass writel(0x00000000, &cmd->int_type); 19071cafc3fSSimon Glass writel(0x00000000, &cmd->int_polarity); 19171cafc3fSSimon Glass writel(0x00000000, &cmd->int_mask); 19271cafc3fSSimon Glass writel(0x00000000, &cmd->int_enb); 19371cafc3fSSimon Glass 19471cafc3fSSimon Glass val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; 19571cafc3fSSimon Glass val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; 19671cafc3fSSimon Glass val |= PM1_ENABLE; 19771cafc3fSSimon Glass writel(val, &cmd->disp_pow_ctrl); 19871cafc3fSSimon Glass 19971cafc3fSSimon Glass val = readl(&cmd->disp_cmd); 20071cafc3fSSimon Glass val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; 20171cafc3fSSimon Glass writel(val, &cmd->disp_cmd); 20271cafc3fSSimon Glass } 20371cafc3fSSimon Glass 20471cafc3fSSimon Glass static void basic_init_timer(struct dc_disp_reg *disp) 20571cafc3fSSimon Glass { 20671cafc3fSSimon Glass writel(0x00000020, &disp->mem_high_pri); 20771cafc3fSSimon Glass writel(0x00000001, &disp->mem_high_pri_timer); 20871cafc3fSSimon Glass } 20971cafc3fSSimon Glass 21071cafc3fSSimon Glass static const u32 rgb_enb_tab[PIN_REG_COUNT] = { 21171cafc3fSSimon Glass 0x00000000, 21271cafc3fSSimon Glass 0x00000000, 21371cafc3fSSimon Glass 0x00000000, 21471cafc3fSSimon Glass 0x00000000, 21571cafc3fSSimon Glass }; 21671cafc3fSSimon Glass 21771cafc3fSSimon Glass static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { 21871cafc3fSSimon Glass 0x00000000, 21971cafc3fSSimon Glass 0x01000000, 22071cafc3fSSimon Glass 0x00000000, 22171cafc3fSSimon Glass 0x00000000, 22271cafc3fSSimon Glass }; 22371cafc3fSSimon Glass 22471cafc3fSSimon Glass static const u32 rgb_data_tab[PIN_REG_COUNT] = { 22571cafc3fSSimon Glass 0x00000000, 22671cafc3fSSimon Glass 0x00000000, 22771cafc3fSSimon Glass 0x00000000, 22871cafc3fSSimon Glass 0x00000000, 22971cafc3fSSimon Glass }; 23071cafc3fSSimon Glass 23171cafc3fSSimon Glass static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { 23271cafc3fSSimon Glass 0x00000000, 23371cafc3fSSimon Glass 0x00000000, 23471cafc3fSSimon Glass 0x00000000, 23571cafc3fSSimon Glass 0x00000000, 23671cafc3fSSimon Glass 0x00210222, 23771cafc3fSSimon Glass 0x00002200, 23871cafc3fSSimon Glass 0x00020000, 23971cafc3fSSimon Glass }; 24071cafc3fSSimon Glass 24171cafc3fSSimon Glass static void rgb_enable(struct dc_com_reg *com) 24271cafc3fSSimon Glass { 24371cafc3fSSimon Glass int i; 24471cafc3fSSimon Glass 24571cafc3fSSimon Glass for (i = 0; i < PIN_REG_COUNT; i++) { 24671cafc3fSSimon Glass writel(rgb_enb_tab[i], &com->pin_output_enb[i]); 24771cafc3fSSimon Glass writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); 24871cafc3fSSimon Glass writel(rgb_data_tab[i], &com->pin_output_data[i]); 24971cafc3fSSimon Glass } 25071cafc3fSSimon Glass 25171cafc3fSSimon Glass for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) 25271cafc3fSSimon Glass writel(rgb_sel_tab[i], &com->pin_output_sel[i]); 25371cafc3fSSimon Glass } 25471cafc3fSSimon Glass 25571cafc3fSSimon Glass static int setup_window(struct disp_ctl_win *win, 2569e6866d3SSimon Glass struct tegra_lcd_priv *priv) 25771cafc3fSSimon Glass { 25871cafc3fSSimon Glass win->x = 0; 25971cafc3fSSimon Glass win->y = 0; 2609e6866d3SSimon Glass win->w = priv->width; 2619e6866d3SSimon Glass win->h = priv->height; 26271cafc3fSSimon Glass win->out_x = 0; 26371cafc3fSSimon Glass win->out_y = 0; 2649e6866d3SSimon Glass win->out_w = priv->width; 2659e6866d3SSimon Glass win->out_h = priv->height; 2669e6866d3SSimon Glass win->phys_addr = priv->frame_buffer; 2679e6866d3SSimon Glass win->stride = priv->width * (1 << priv->log2_bpp) / 8; 2689e6866d3SSimon Glass debug("%s: depth = %d\n", __func__, priv->log2_bpp); 2699e6866d3SSimon Glass switch (priv->log2_bpp) { 27071cafc3fSSimon Glass case 5: 27171cafc3fSSimon Glass case 24: 27271cafc3fSSimon Glass win->fmt = COLOR_DEPTH_R8G8B8A8; 27371cafc3fSSimon Glass win->bpp = 32; 27471cafc3fSSimon Glass break; 27571cafc3fSSimon Glass case 4: 27671cafc3fSSimon Glass win->fmt = COLOR_DEPTH_B5G6R5; 27771cafc3fSSimon Glass win->bpp = 16; 27871cafc3fSSimon Glass break; 27971cafc3fSSimon Glass 28071cafc3fSSimon Glass default: 28171cafc3fSSimon Glass debug("Unsupported LCD bit depth"); 28271cafc3fSSimon Glass return -1; 28371cafc3fSSimon Glass } 28471cafc3fSSimon Glass 28571cafc3fSSimon Glass return 0; 28671cafc3fSSimon Glass } 28771cafc3fSSimon Glass 28871cafc3fSSimon Glass static void debug_timing(const char *name, unsigned int timing[]) 28971cafc3fSSimon Glass { 29071cafc3fSSimon Glass #ifdef DEBUG 29171cafc3fSSimon Glass int i; 29271cafc3fSSimon Glass 29371cafc3fSSimon Glass debug("%s timing: ", name); 29471cafc3fSSimon Glass for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) 29571cafc3fSSimon Glass debug("%d ", timing[i]); 29671cafc3fSSimon Glass debug("\n"); 29771cafc3fSSimon Glass #endif 29871cafc3fSSimon Glass } 29971cafc3fSSimon Glass 30071cafc3fSSimon Glass /** 30171cafc3fSSimon Glass * Decode panel information from the fdt, according to a standard binding 30271cafc3fSSimon Glass * 30371cafc3fSSimon Glass * @param blob fdt blob 30471cafc3fSSimon Glass * @param node offset of fdt node to read from 3059e6866d3SSimon Glass * @param priv structure to store fdt config into 30671cafc3fSSimon Glass * @return 0 if ok, -ve on error 30771cafc3fSSimon Glass */ 30871cafc3fSSimon Glass static int tegra_decode_panel(const void *blob, int node, 3099e6866d3SSimon Glass struct tegra_lcd_priv *priv) 31071cafc3fSSimon Glass { 31171cafc3fSSimon Glass int front, back, ref; 31271cafc3fSSimon Glass 3139e6866d3SSimon Glass priv->width = fdtdec_get_int(blob, node, "xres", -1); 3149e6866d3SSimon Glass priv->height = fdtdec_get_int(blob, node, "yres", -1); 3159e6866d3SSimon Glass priv->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); 3169e6866d3SSimon Glass if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) { 31771cafc3fSSimon Glass debug("%s: Pixel parameters missing\n", __func__); 31871cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 31971cafc3fSSimon Glass } 32071cafc3fSSimon Glass 32171cafc3fSSimon Glass back = fdtdec_get_int(blob, node, "left-margin", -1); 32271cafc3fSSimon Glass front = fdtdec_get_int(blob, node, "right-margin", -1); 32371cafc3fSSimon Glass ref = fdtdec_get_int(blob, node, "hsync-len", -1); 32471cafc3fSSimon Glass if ((back | front | ref) == -1) { 32571cafc3fSSimon Glass debug("%s: Horizontal parameters missing\n", __func__); 32671cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 32771cafc3fSSimon Glass } 32871cafc3fSSimon Glass 32971cafc3fSSimon Glass /* Use a ref-to-sync of 1 always, and take this from the front porch */ 3309e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 3319e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 3329e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 3339e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 3349e6866d3SSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 3359e6866d3SSimon Glass debug_timing("horiz", priv->horiz_timing); 33671cafc3fSSimon Glass 33771cafc3fSSimon Glass back = fdtdec_get_int(blob, node, "upper-margin", -1); 33871cafc3fSSimon Glass front = fdtdec_get_int(blob, node, "lower-margin", -1); 33971cafc3fSSimon Glass ref = fdtdec_get_int(blob, node, "vsync-len", -1); 34071cafc3fSSimon Glass if ((back | front | ref) == -1) { 34171cafc3fSSimon Glass debug("%s: Vertical parameters missing\n", __func__); 34271cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 34371cafc3fSSimon Glass } 34471cafc3fSSimon Glass 3459e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 3469e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 3479e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 3489e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 3499e6866d3SSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 3509e6866d3SSimon Glass debug_timing("vert", priv->vert_timing); 35171cafc3fSSimon Glass 35271cafc3fSSimon Glass return 0; 35371cafc3fSSimon Glass } 35471cafc3fSSimon Glass 35571cafc3fSSimon Glass /** 35671cafc3fSSimon Glass * Decode the display controller information from the fdt. 35771cafc3fSSimon Glass * 35871cafc3fSSimon Glass * @param blob fdt blob 3599e6866d3SSimon Glass * @param priv structure to store fdt priv into 36071cafc3fSSimon Glass * @return 0 if ok, -ve on error 36171cafc3fSSimon Glass */ 3629e6866d3SSimon Glass static int tegra_display_decode_config(const void *blob, int node, 3639e6866d3SSimon Glass struct tegra_lcd_priv *priv) 36471cafc3fSSimon Glass { 3659e6866d3SSimon Glass int rgb; 36671cafc3fSSimon Glass int bpp, bit; 36771cafc3fSSimon Glass 3689e6866d3SSimon Glass priv->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); 3699e6866d3SSimon Glass if (!priv->disp) { 37071cafc3fSSimon Glass debug("%s: No display controller address\n", __func__); 37171cafc3fSSimon Glass return -1; 37271cafc3fSSimon Glass } 37371cafc3fSSimon Glass 37471cafc3fSSimon Glass rgb = fdt_subnode_offset(blob, node, "rgb"); 37571cafc3fSSimon Glass 3769e6866d3SSimon Glass priv->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); 3779e6866d3SSimon Glass if (priv->panel_node < 0) { 37871cafc3fSSimon Glass debug("%s: Cannot find panel information\n", __func__); 37971cafc3fSSimon Glass return -1; 38071cafc3fSSimon Glass } 38171cafc3fSSimon Glass 3829e6866d3SSimon Glass if (tegra_decode_panel(blob, priv->panel_node, priv)) { 38371cafc3fSSimon Glass debug("%s: Failed to decode panel information\n", __func__); 38471cafc3fSSimon Glass return -1; 38571cafc3fSSimon Glass } 38671cafc3fSSimon Glass 3879e6866d3SSimon Glass bpp = fdtdec_get_int(blob, priv->panel_node, "nvidia,bits-per-pixel", 38871cafc3fSSimon Glass -1); 38971cafc3fSSimon Glass bit = ffs(bpp) - 1; 39071cafc3fSSimon Glass if (bpp == (1 << bit)) 3919e6866d3SSimon Glass priv->log2_bpp = bit; 39271cafc3fSSimon Glass else 3939e6866d3SSimon Glass priv->log2_bpp = bpp; 39471cafc3fSSimon Glass if (bpp == -1) { 39571cafc3fSSimon Glass debug("%s: Pixel bpp parameters missing\n", __func__); 39671cafc3fSSimon Glass return -FDT_ERR_NOTFOUND; 39771cafc3fSSimon Glass } 3989e6866d3SSimon Glass priv->bpp = bpp; 39971cafc3fSSimon Glass 40071cafc3fSSimon Glass return 0; 40171cafc3fSSimon Glass } 40271cafc3fSSimon Glass 40371cafc3fSSimon Glass /** 40471cafc3fSSimon Glass * Register a new display based on device tree configuration. 40571cafc3fSSimon Glass * 40671cafc3fSSimon Glass * The frame buffer can be positioned by U-Boot or overriden by the fdt. 40771cafc3fSSimon Glass * You should pass in the U-Boot address here, and check the contents of 408ce0c474aSSimon Glass * struct tegra_lcd_priv to see what was actually chosen. 40971cafc3fSSimon Glass * 41071cafc3fSSimon Glass * @param blob Device tree blob 4119e6866d3SSimon Glass * @param priv Driver's private data 41271cafc3fSSimon Glass * @param default_lcd_base Default address of LCD frame buffer 41371cafc3fSSimon Glass * @return 0 if ok, -1 on error (unsupported bits per pixel) 41471cafc3fSSimon Glass */ 4159e6866d3SSimon Glass static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, 4169e6866d3SSimon Glass void *default_lcd_base) 41771cafc3fSSimon Glass { 41871cafc3fSSimon Glass struct disp_ctl_win window; 41971cafc3fSSimon Glass struct dc_ctlr *dc; 42071cafc3fSSimon Glass 4219e6866d3SSimon Glass priv->frame_buffer = (u32)default_lcd_base; 42271cafc3fSSimon Glass 4239e6866d3SSimon Glass dc = (struct dc_ctlr *)priv->disp; 42471cafc3fSSimon Glass 42571cafc3fSSimon Glass /* 42671cafc3fSSimon Glass * A header file for clock constants was NAKed upstream. 42771cafc3fSSimon Glass * TODO: Put this into the FDT and fdt_lcd struct when we have clock 42871cafc3fSSimon Glass * support there 42971cafc3fSSimon Glass */ 43071cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 43171cafc3fSSimon Glass 144 * 1000000); 43271cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, 43371cafc3fSSimon Glass 600 * 1000000); 43471cafc3fSSimon Glass basic_init(&dc->cmd); 43571cafc3fSSimon Glass basic_init_timer(&dc->disp); 43671cafc3fSSimon Glass rgb_enable(&dc->com); 43771cafc3fSSimon Glass 4389e6866d3SSimon Glass if (priv->pixel_clock) 4399e6866d3SSimon Glass update_display_mode(&dc->disp, priv); 44071cafc3fSSimon Glass 4419e6866d3SSimon Glass if (setup_window(&window, priv)) 44271cafc3fSSimon Glass return -1; 44371cafc3fSSimon Glass 44471cafc3fSSimon Glass update_window(dc, &window); 44571cafc3fSSimon Glass 44671cafc3fSSimon Glass return 0; 44771cafc3fSSimon Glass } 44871cafc3fSSimon Glass 4490be8f203SSimon Glass /** 4500be8f203SSimon Glass * Decode the panel information from the fdt. 4510be8f203SSimon Glass * 4520be8f203SSimon Glass * @param blob fdt blob 4539e6866d3SSimon Glass * @param priv structure to store fdt config into 4540be8f203SSimon Glass * @return 0 if ok, -ve on error 4550be8f203SSimon Glass */ 4569e6866d3SSimon Glass static int fdt_decode_lcd(const void *blob, struct tegra_lcd_priv *priv) 4570be8f203SSimon Glass { 4580be8f203SSimon Glass int display_node; 4590be8f203SSimon Glass 4609e6866d3SSimon Glass display_node = priv->panel_node; 4610be8f203SSimon Glass if (display_node < 0) { 4620be8f203SSimon Glass debug("%s: No panel configuration available\n", __func__); 4630be8f203SSimon Glass return -1; 4640be8f203SSimon Glass } 4650be8f203SSimon Glass 4669e6866d3SSimon Glass priv->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); 4679e6866d3SSimon Glass if (priv->pwm_channel < 0) { 4680be8f203SSimon Glass debug("%s: Unable to request PWM channel\n", __func__); 4690be8f203SSimon Glass return -1; 4700be8f203SSimon Glass } 4710be8f203SSimon Glass 4729e6866d3SSimon Glass priv->cache_type = fdtdec_get_int(blob, display_node, 4730be8f203SSimon Glass "nvidia,cache-type", 4740be8f203SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH); 4750be8f203SSimon Glass 4760be8f203SSimon Glass /* These GPIOs are all optional */ 47704072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 47804072cbaSSimon Glass "nvidia,backlight-enable-gpios", 0, 4799e6866d3SSimon Glass &priv->backlight_en, GPIOD_IS_OUT); 48004072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 48104072cbaSSimon Glass "nvidia,lvds-shutdown-gpios", 0, 4829e6866d3SSimon Glass &priv->lvds_shutdown, GPIOD_IS_OUT); 48304072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 48404072cbaSSimon Glass "nvidia,backlight-vdd-gpios", 0, 4859e6866d3SSimon Glass &priv->backlight_vdd, GPIOD_IS_OUT); 48604072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 48704072cbaSSimon Glass "nvidia,panel-vdd-gpios", 0, 4889e6866d3SSimon Glass &priv->panel_vdd, GPIOD_IS_OUT); 4890be8f203SSimon Glass 4900be8f203SSimon Glass return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", 4919e6866d3SSimon Glass priv->panel_timings, FDT_LCD_TIMINGS); 4920be8f203SSimon Glass } 4930be8f203SSimon Glass 4940be8f203SSimon Glass /** 4950be8f203SSimon Glass * Handle the next stage of device init 4960be8f203SSimon Glass */ 4979e6866d3SSimon Glass static int handle_stage(const void *blob, struct tegra_lcd_priv *priv) 4980be8f203SSimon Glass { 499*f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 5000be8f203SSimon Glass 5010be8f203SSimon Glass /* do the things for this stage */ 502*f20b2c06SSimon Glass switch (priv->stage) { 5030be8f203SSimon Glass case STAGE_START: 5040be8f203SSimon Glass /* 5050be8f203SSimon Glass * It is possible that the FDT has requested that the LCD be 5060be8f203SSimon Glass * disabled. We currently don't support this. It would require 5070be8f203SSimon Glass * changes to U-Boot LCD subsystem to have LCD support 5080be8f203SSimon Glass * compiled in but not used. An easier option might be to 5090be8f203SSimon Glass * still have a frame buffer, but leave the backlight off and 5100be8f203SSimon Glass * remove all mention of lcd in the stdout environment 5110be8f203SSimon Glass * variable. 5120be8f203SSimon Glass */ 5130be8f203SSimon Glass 5140be8f203SSimon Glass funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 5150be8f203SSimon Glass break; 5160be8f203SSimon Glass case STAGE_PANEL_VDD: 5179e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->panel_vdd)) 5189e6866d3SSimon Glass dm_gpio_set_value(&priv->panel_vdd, 1); 5190be8f203SSimon Glass break; 5200be8f203SSimon Glass case STAGE_LVDS: 5219e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->lvds_shutdown)) 5229e6866d3SSimon Glass dm_gpio_set_value(&priv->lvds_shutdown, 1); 5230be8f203SSimon Glass break; 5240be8f203SSimon Glass case STAGE_BACKLIGHT_VDD: 5259e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_vdd)) 5269e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_vdd, 1); 5270be8f203SSimon Glass break; 5280be8f203SSimon Glass case STAGE_PWM: 5290be8f203SSimon Glass /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 53070ad375eSStephen Warren pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); 53170ad375eSStephen Warren pinmux_tristate_disable(PMUX_PINGRP_GPU); 5320be8f203SSimon Glass 5339e6866d3SSimon Glass pwm_enable(priv->pwm_channel, 32768, 0xdf, 1); 5340be8f203SSimon Glass break; 5350be8f203SSimon Glass case STAGE_BACKLIGHT_EN: 5369e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_en)) 5379e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_en, 1); 5380be8f203SSimon Glass break; 5390be8f203SSimon Glass case STAGE_DONE: 5400be8f203SSimon Glass break; 5410be8f203SSimon Glass } 5420be8f203SSimon Glass 5430be8f203SSimon Glass /* set up timer for next stage */ 544*f20b2c06SSimon Glass priv->timer_next = timer_get_us(); 545*f20b2c06SSimon Glass if (priv->stage < FDT_LCD_TIMINGS) 546*f20b2c06SSimon Glass priv->timer_next += priv->panel_timings[priv->stage] * 1000; 5470be8f203SSimon Glass 5480be8f203SSimon Glass /* move to next stage */ 549*f20b2c06SSimon Glass priv->stage++; 5500be8f203SSimon Glass return 0; 5510be8f203SSimon Glass } 5520be8f203SSimon Glass 5539e6866d3SSimon Glass /** 5549e6866d3SSimon Glass * Perform the next stage of the LCD init if it is time to do so. 5559e6866d3SSimon Glass * 5569e6866d3SSimon Glass * LCD init can be time-consuming because of the number of delays we need 5579e6866d3SSimon Glass * while waiting for the backlight power supply, etc. This function can 5589e6866d3SSimon Glass * be called at various times during U-Boot operation to advance the 5599e6866d3SSimon Glass * initialization of the LCD to the next stage if sufficient time has 5609e6866d3SSimon Glass * passed since the last stage. It keeps track of what stage it is up to 5619e6866d3SSimon Glass * and the time that it is permitted to move to the next stage. 5629e6866d3SSimon Glass * 5639e6866d3SSimon Glass * The final call should have wait=1 to complete the init. 5649e6866d3SSimon Glass * 5659e6866d3SSimon Glass * @param blob fdt blob containing LCD information 5669e6866d3SSimon Glass * @param wait 1 to wait until all init is complete, and then return 5679e6866d3SSimon Glass * 0 to return immediately, potentially doing nothing if it is 5689e6866d3SSimon Glass * not yet time for the next init. 5699e6866d3SSimon Glass */ 5709e6866d3SSimon Glass static int tegra_lcd_check_next_stage(const void *blob, 5719e6866d3SSimon Glass struct tegra_lcd_priv *priv, int wait) 5720be8f203SSimon Glass { 573*f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 5740be8f203SSimon Glass return 0; 5750be8f203SSimon Glass 5760be8f203SSimon Glass do { 5770be8f203SSimon Glass /* wait if we need to */ 578*f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 579*f20b2c06SSimon Glass if (priv->stage != STAGE_START) { 580*f20b2c06SSimon Glass int delay = priv->timer_next - timer_get_us(); 5810be8f203SSimon Glass 5820be8f203SSimon Glass if (delay > 0) { 5830be8f203SSimon Glass if (wait) 5840be8f203SSimon Glass udelay(delay); 5850be8f203SSimon Glass else 5860be8f203SSimon Glass return 0; 5870be8f203SSimon Glass } 5880be8f203SSimon Glass } 5890be8f203SSimon Glass 5909e6866d3SSimon Glass if (handle_stage(blob, priv)) 5910be8f203SSimon Glass return -1; 592*f20b2c06SSimon Glass } while (wait && priv->stage != STAGE_DONE); 593*f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 5940be8f203SSimon Glass debug("%s: LCD init complete\n", __func__); 5950be8f203SSimon Glass 5960be8f203SSimon Glass return 0; 5970be8f203SSimon Glass } 5980be8f203SSimon Glass 5999e6866d3SSimon Glass static int tegra_lcd_probe(struct udevice *dev) 6000be8f203SSimon Glass { 6019e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 6029e6866d3SSimon Glass struct video_priv *uc_priv = dev_get_uclass_priv(dev); 6039e6866d3SSimon Glass struct tegra_lcd_priv *priv = dev_get_priv(dev); 6049e6866d3SSimon Glass const void *blob = gd->fdt_blob; 6059e6866d3SSimon Glass int type = DCACHE_OFF; 6069e6866d3SSimon Glass 6079e6866d3SSimon Glass if (tegra_display_decode_config(blob, dev->of_offset, priv)) 6089e6866d3SSimon Glass return -1; 6099e6866d3SSimon Glass 6109e6866d3SSimon Glass /* get panel details */ 6119e6866d3SSimon Glass if (fdt_decode_lcd(blob, priv)) { 6129e6866d3SSimon Glass printf("No valid LCD information in device tree\n"); 6139e6866d3SSimon Glass return -1; 6140be8f203SSimon Glass } 6159e6866d3SSimon Glass 6169e6866d3SSimon Glass /* Initialize the Tegra display controller */ 6179e6866d3SSimon Glass if (tegra_display_probe(blob, priv, (void *)plat->base)) { 6189e6866d3SSimon Glass printf("%s: Failed to probe display driver\n", __func__); 6199e6866d3SSimon Glass return -1; 6200be8f203SSimon Glass } 6219e6866d3SSimon Glass 6229e6866d3SSimon Glass tegra_lcd_check_next_stage(blob, priv, 1); 6239e6866d3SSimon Glass 6249e6866d3SSimon Glass /* Set up the LCD caching as requested */ 6259e6866d3SSimon Glass if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 6269e6866d3SSimon Glass type = DCACHE_WRITETHROUGH; 6279e6866d3SSimon Glass else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK) 6289e6866d3SSimon Glass type = DCACHE_WRITEBACK; 6299e6866d3SSimon Glass mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type); 6309e6866d3SSimon Glass 6319e6866d3SSimon Glass /* Enable flushing after LCD writes if requested */ 6329e6866d3SSimon Glass video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH); 6339e6866d3SSimon Glass 6349e6866d3SSimon Glass uc_priv->xsize = priv->width; 6359e6866d3SSimon Glass uc_priv->ysize = priv->height; 6369e6866d3SSimon Glass uc_priv->bpix = priv->log2_bpp; 6379e6866d3SSimon Glass debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer, 6389e6866d3SSimon Glass plat->size); 6399e6866d3SSimon Glass 6409e6866d3SSimon Glass return 0; 6419e6866d3SSimon Glass } 6429e6866d3SSimon Glass 6439e6866d3SSimon Glass static int tegra_lcd_bind(struct udevice *dev) 6449e6866d3SSimon Glass { 6459e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 6469e6866d3SSimon Glass 6479e6866d3SSimon Glass plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * 6489e6866d3SSimon Glass (1 << LCD_MAX_LOG2_BPP) / 8; 6499e6866d3SSimon Glass 6509e6866d3SSimon Glass return 0; 6519e6866d3SSimon Glass } 6529e6866d3SSimon Glass 6539e6866d3SSimon Glass static const struct video_ops tegra_lcd_ops = { 6549e6866d3SSimon Glass }; 6559e6866d3SSimon Glass 6569e6866d3SSimon Glass static const struct udevice_id tegra_lcd_ids[] = { 6579e6866d3SSimon Glass { .compatible = "nvidia,tegra20-dc" }, 6589e6866d3SSimon Glass { } 6599e6866d3SSimon Glass }; 6609e6866d3SSimon Glass 6619e6866d3SSimon Glass U_BOOT_DRIVER(tegra_lcd) = { 6629e6866d3SSimon Glass .name = "tegra_lcd", 6639e6866d3SSimon Glass .id = UCLASS_VIDEO, 6649e6866d3SSimon Glass .of_match = tegra_lcd_ids, 6659e6866d3SSimon Glass .ops = &tegra_lcd_ops, 6669e6866d3SSimon Glass .bind = tegra_lcd_bind, 6679e6866d3SSimon Glass .probe = tegra_lcd_probe, 6689e6866d3SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_lcd_priv), 6699e6866d3SSimon Glass }; 670