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> 991c08afeSSimon Glass #include <pwm.h> 109e6866d3SSimon Glass #include <video.h> 110be8f203SSimon Glass #include <asm/system.h> 120be8f203SSimon Glass #include <asm/gpio.h> 1371cafc3fSSimon Glass #include <asm/io.h> 140be8f203SSimon Glass 150be8f203SSimon Glass #include <asm/arch/clock.h> 160be8f203SSimon Glass #include <asm/arch/funcmux.h> 170be8f203SSimon Glass #include <asm/arch/pinmux.h> 180be8f203SSimon Glass #include <asm/arch/pwm.h> 190be8f203SSimon Glass #include <asm/arch/display.h> 200be8f203SSimon Glass #include <asm/arch-tegra/timer.h> 210be8f203SSimon Glass 220be8f203SSimon Glass DECLARE_GLOBAL_DATA_PTR; 230be8f203SSimon Glass 240be8f203SSimon Glass /* These are the stages we go throuh in enabling the LCD */ 250be8f203SSimon Glass enum stage_t { 260be8f203SSimon Glass STAGE_START, 270be8f203SSimon Glass STAGE_PANEL_VDD, 280be8f203SSimon Glass STAGE_LVDS, 290be8f203SSimon Glass STAGE_BACKLIGHT_VDD, 300be8f203SSimon Glass STAGE_PWM, 310be8f203SSimon Glass STAGE_BACKLIGHT_EN, 320be8f203SSimon Glass STAGE_DONE, 330be8f203SSimon Glass }; 340be8f203SSimon Glass 35bfda0377SSimon Glass #define FDT_LCD_TIMINGS 4 36bfda0377SSimon Glass 37bfda0377SSimon Glass enum { 38bfda0377SSimon Glass FDT_LCD_TIMING_REF_TO_SYNC, 39bfda0377SSimon Glass FDT_LCD_TIMING_SYNC_WIDTH, 40bfda0377SSimon Glass FDT_LCD_TIMING_BACK_PORCH, 41bfda0377SSimon Glass FDT_LCD_TIMING_FRONT_PORCH, 42bfda0377SSimon Glass 43bfda0377SSimon Glass FDT_LCD_TIMING_COUNT, 44bfda0377SSimon Glass }; 45bfda0377SSimon Glass 46bfda0377SSimon Glass enum lcd_cache_t { 47bfda0377SSimon Glass FDT_LCD_CACHE_OFF = 0, 48bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_THROUGH = 1 << 0, 49bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_BACK = 1 << 1, 50bfda0377SSimon Glass FDT_LCD_CACHE_FLUSH = 1 << 2, 51bfda0377SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH = FDT_LCD_CACHE_WRITE_BACK | 52bfda0377SSimon Glass FDT_LCD_CACHE_FLUSH, 53bfda0377SSimon Glass }; 54bfda0377SSimon Glass 55ce0c474aSSimon Glass /* Information about the display controller */ 56ce0c474aSSimon Glass struct tegra_lcd_priv { 57f20b2c06SSimon Glass enum stage_t stage; /* Current stage we are at */ 58f20b2c06SSimon Glass unsigned long timer_next; /* Time we can move onto next stage */ 59ce0c474aSSimon Glass int width; /* width in pixels */ 60ce0c474aSSimon Glass int height; /* height in pixels */ 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 */ 7291c08afeSSimon Glass struct udevice *pwm; 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 * Register a new display based on device tree configuration. 32271cafc3fSSimon Glass * 32371cafc3fSSimon Glass * The frame buffer can be positioned by U-Boot or overriden by the fdt. 32471cafc3fSSimon Glass * You should pass in the U-Boot address here, and check the contents of 325ce0c474aSSimon Glass * struct tegra_lcd_priv to see what was actually chosen. 32671cafc3fSSimon Glass * 32771cafc3fSSimon Glass * @param blob Device tree blob 3289e6866d3SSimon Glass * @param priv Driver's private data 32971cafc3fSSimon Glass * @param default_lcd_base Default address of LCD frame buffer 33071cafc3fSSimon Glass * @return 0 if ok, -1 on error (unsupported bits per pixel) 33171cafc3fSSimon Glass */ 3329e6866d3SSimon Glass static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, 3339e6866d3SSimon Glass void *default_lcd_base) 33471cafc3fSSimon Glass { 33571cafc3fSSimon Glass struct disp_ctl_win window; 33671cafc3fSSimon Glass struct dc_ctlr *dc; 33771cafc3fSSimon Glass 3389e6866d3SSimon Glass priv->frame_buffer = (u32)default_lcd_base; 33971cafc3fSSimon Glass 3409e6866d3SSimon Glass dc = (struct dc_ctlr *)priv->disp; 34171cafc3fSSimon Glass 34271cafc3fSSimon Glass /* 34371cafc3fSSimon Glass * A header file for clock constants was NAKed upstream. 34471cafc3fSSimon Glass * TODO: Put this into the FDT and fdt_lcd struct when we have clock 34571cafc3fSSimon Glass * support there 34671cafc3fSSimon Glass */ 34771cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 34871cafc3fSSimon Glass 144 * 1000000); 34971cafc3fSSimon Glass clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, 35071cafc3fSSimon Glass 600 * 1000000); 35171cafc3fSSimon Glass basic_init(&dc->cmd); 35271cafc3fSSimon Glass basic_init_timer(&dc->disp); 35371cafc3fSSimon Glass rgb_enable(&dc->com); 35471cafc3fSSimon Glass 3559e6866d3SSimon Glass if (priv->pixel_clock) 3569e6866d3SSimon Glass update_display_mode(&dc->disp, priv); 35771cafc3fSSimon Glass 3589e6866d3SSimon Glass if (setup_window(&window, priv)) 35971cafc3fSSimon Glass return -1; 36071cafc3fSSimon Glass 36171cafc3fSSimon Glass update_window(dc, &window); 36271cafc3fSSimon Glass 36371cafc3fSSimon Glass return 0; 36471cafc3fSSimon Glass } 36571cafc3fSSimon Glass 3660be8f203SSimon Glass /** 3670be8f203SSimon Glass * Handle the next stage of device init 3680be8f203SSimon Glass */ 3699e6866d3SSimon Glass static int handle_stage(const void *blob, struct tegra_lcd_priv *priv) 3700be8f203SSimon Glass { 371f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 3720be8f203SSimon Glass 3730be8f203SSimon Glass /* do the things for this stage */ 374f20b2c06SSimon Glass switch (priv->stage) { 3750be8f203SSimon Glass case STAGE_START: 3760be8f203SSimon Glass /* 3770be8f203SSimon Glass * It is possible that the FDT has requested that the LCD be 3780be8f203SSimon Glass * disabled. We currently don't support this. It would require 3790be8f203SSimon Glass * changes to U-Boot LCD subsystem to have LCD support 3800be8f203SSimon Glass * compiled in but not used. An easier option might be to 3810be8f203SSimon Glass * still have a frame buffer, but leave the backlight off and 3820be8f203SSimon Glass * remove all mention of lcd in the stdout environment 3830be8f203SSimon Glass * variable. 3840be8f203SSimon Glass */ 3850be8f203SSimon Glass 3860be8f203SSimon Glass funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 3870be8f203SSimon Glass break; 3880be8f203SSimon Glass case STAGE_PANEL_VDD: 3899e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->panel_vdd)) 3909e6866d3SSimon Glass dm_gpio_set_value(&priv->panel_vdd, 1); 3910be8f203SSimon Glass break; 3920be8f203SSimon Glass case STAGE_LVDS: 3939e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->lvds_shutdown)) 3949e6866d3SSimon Glass dm_gpio_set_value(&priv->lvds_shutdown, 1); 3950be8f203SSimon Glass break; 3960be8f203SSimon Glass case STAGE_BACKLIGHT_VDD: 3979e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_vdd)) 3989e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_vdd, 1); 3990be8f203SSimon Glass break; 4000be8f203SSimon Glass case STAGE_PWM: 4010be8f203SSimon Glass /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 40270ad375eSStephen Warren pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); 40370ad375eSStephen Warren pinmux_tristate_disable(PMUX_PINGRP_GPU); 4040be8f203SSimon Glass 40591c08afeSSimon Glass pwm_set_config(priv->pwm, priv->pwm_channel, 0xdf, 0xff); 40691c08afeSSimon Glass pwm_set_enable(priv->pwm, priv->pwm_channel, true); 4070be8f203SSimon Glass break; 4080be8f203SSimon Glass case STAGE_BACKLIGHT_EN: 4099e6866d3SSimon Glass if (dm_gpio_is_valid(&priv->backlight_en)) 4109e6866d3SSimon Glass dm_gpio_set_value(&priv->backlight_en, 1); 4110be8f203SSimon Glass break; 4120be8f203SSimon Glass case STAGE_DONE: 4130be8f203SSimon Glass break; 4140be8f203SSimon Glass } 4150be8f203SSimon Glass 4160be8f203SSimon Glass /* set up timer for next stage */ 417f20b2c06SSimon Glass priv->timer_next = timer_get_us(); 418f20b2c06SSimon Glass if (priv->stage < FDT_LCD_TIMINGS) 419f20b2c06SSimon Glass priv->timer_next += priv->panel_timings[priv->stage] * 1000; 4200be8f203SSimon Glass 4210be8f203SSimon Glass /* move to next stage */ 422f20b2c06SSimon Glass priv->stage++; 4230be8f203SSimon Glass return 0; 4240be8f203SSimon Glass } 4250be8f203SSimon Glass 4269e6866d3SSimon Glass /** 4279e6866d3SSimon Glass * Perform the next stage of the LCD init if it is time to do so. 4289e6866d3SSimon Glass * 4299e6866d3SSimon Glass * LCD init can be time-consuming because of the number of delays we need 4309e6866d3SSimon Glass * while waiting for the backlight power supply, etc. This function can 4319e6866d3SSimon Glass * be called at various times during U-Boot operation to advance the 4329e6866d3SSimon Glass * initialization of the LCD to the next stage if sufficient time has 4339e6866d3SSimon Glass * passed since the last stage. It keeps track of what stage it is up to 4349e6866d3SSimon Glass * and the time that it is permitted to move to the next stage. 4359e6866d3SSimon Glass * 4369e6866d3SSimon Glass * The final call should have wait=1 to complete the init. 4379e6866d3SSimon Glass * 4389e6866d3SSimon Glass * @param blob fdt blob containing LCD information 4399e6866d3SSimon Glass * @param wait 1 to wait until all init is complete, and then return 4409e6866d3SSimon Glass * 0 to return immediately, potentially doing nothing if it is 4419e6866d3SSimon Glass * not yet time for the next init. 4429e6866d3SSimon Glass */ 4439e6866d3SSimon Glass static int tegra_lcd_check_next_stage(const void *blob, 4449e6866d3SSimon Glass struct tegra_lcd_priv *priv, int wait) 4450be8f203SSimon Glass { 446f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 4470be8f203SSimon Glass return 0; 4480be8f203SSimon Glass 4490be8f203SSimon Glass do { 4500be8f203SSimon Glass /* wait if we need to */ 451f20b2c06SSimon Glass debug("%s: stage %d\n", __func__, priv->stage); 452f20b2c06SSimon Glass if (priv->stage != STAGE_START) { 453f20b2c06SSimon Glass int delay = priv->timer_next - timer_get_us(); 4540be8f203SSimon Glass 4550be8f203SSimon Glass if (delay > 0) { 4560be8f203SSimon Glass if (wait) 4570be8f203SSimon Glass udelay(delay); 4580be8f203SSimon Glass else 4590be8f203SSimon Glass return 0; 4600be8f203SSimon Glass } 4610be8f203SSimon Glass } 4620be8f203SSimon Glass 4639e6866d3SSimon Glass if (handle_stage(blob, priv)) 4640be8f203SSimon Glass return -1; 465f20b2c06SSimon Glass } while (wait && priv->stage != STAGE_DONE); 466f20b2c06SSimon Glass if (priv->stage == STAGE_DONE) 4670be8f203SSimon Glass debug("%s: LCD init complete\n", __func__); 4680be8f203SSimon Glass 4690be8f203SSimon Glass return 0; 4700be8f203SSimon Glass } 4710be8f203SSimon Glass 4729e6866d3SSimon Glass static int tegra_lcd_probe(struct udevice *dev) 4730be8f203SSimon Glass { 4749e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 4759e6866d3SSimon Glass struct video_priv *uc_priv = dev_get_uclass_priv(dev); 4769e6866d3SSimon Glass struct tegra_lcd_priv *priv = dev_get_priv(dev); 4779e6866d3SSimon Glass const void *blob = gd->fdt_blob; 4789e6866d3SSimon Glass int type = DCACHE_OFF; 4799e6866d3SSimon Glass 4809e6866d3SSimon Glass /* Initialize the Tegra display controller */ 4819e6866d3SSimon Glass if (tegra_display_probe(blob, priv, (void *)plat->base)) { 4829e6866d3SSimon Glass printf("%s: Failed to probe display driver\n", __func__); 4839e6866d3SSimon Glass return -1; 4840be8f203SSimon Glass } 4859e6866d3SSimon Glass 4869e6866d3SSimon Glass tegra_lcd_check_next_stage(blob, priv, 1); 4879e6866d3SSimon Glass 4889e6866d3SSimon Glass /* Set up the LCD caching as requested */ 4899e6866d3SSimon Glass if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 4909e6866d3SSimon Glass type = DCACHE_WRITETHROUGH; 4919e6866d3SSimon Glass else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK) 4929e6866d3SSimon Glass type = DCACHE_WRITEBACK; 4939e6866d3SSimon Glass mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type); 4949e6866d3SSimon Glass 4959e6866d3SSimon Glass /* Enable flushing after LCD writes if requested */ 4969e6866d3SSimon Glass video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH); 4979e6866d3SSimon Glass 4989e6866d3SSimon Glass uc_priv->xsize = priv->width; 4999e6866d3SSimon Glass uc_priv->ysize = priv->height; 5009e6866d3SSimon Glass uc_priv->bpix = priv->log2_bpp; 5019e6866d3SSimon Glass debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer, 5029e6866d3SSimon Glass plat->size); 5039e6866d3SSimon Glass 5049e6866d3SSimon Glass return 0; 5059e6866d3SSimon Glass } 5069e6866d3SSimon Glass 507f5acf91fSSimon Glass static int tegra_lcd_ofdata_to_platdata(struct udevice *dev) 508f5acf91fSSimon Glass { 509f5acf91fSSimon Glass struct tegra_lcd_priv *priv = dev_get_priv(dev); 51091c08afeSSimon Glass struct fdtdec_phandle_args args; 511f5acf91fSSimon Glass const void *blob = gd->fdt_blob; 512f5acf91fSSimon Glass int node = dev->of_offset; 513f5acf91fSSimon Glass int front, back, ref; 514f5acf91fSSimon Glass int panel_node; 515f5acf91fSSimon Glass int rgb; 516f5acf91fSSimon Glass int bpp, bit; 51791c08afeSSimon Glass int ret; 518f5acf91fSSimon Glass 519f5acf91fSSimon Glass priv->disp = (struct disp_ctlr *)dev_get_addr(dev); 520f5acf91fSSimon Glass if (!priv->disp) { 521f5acf91fSSimon Glass debug("%s: No display controller address\n", __func__); 522f5acf91fSSimon Glass return -EINVAL; 523f5acf91fSSimon Glass } 524f5acf91fSSimon Glass 525f5acf91fSSimon Glass rgb = fdt_subnode_offset(blob, node, "rgb"); 526f5acf91fSSimon Glass 527f5acf91fSSimon Glass panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); 528f5acf91fSSimon Glass if (panel_node < 0) { 529f5acf91fSSimon Glass debug("%s: Cannot find panel information\n", __func__); 530f5acf91fSSimon Glass return -EINVAL; 531f5acf91fSSimon Glass } 532f5acf91fSSimon Glass 533f5acf91fSSimon Glass priv->width = fdtdec_get_int(blob, panel_node, "xres", -1); 534f5acf91fSSimon Glass priv->height = fdtdec_get_int(blob, panel_node, "yres", -1); 535f5acf91fSSimon Glass priv->pixel_clock = fdtdec_get_int(blob, panel_node, "clock", 0); 536f5acf91fSSimon Glass if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) { 537f5acf91fSSimon Glass debug("%s: Pixel parameters missing\n", __func__); 538f5acf91fSSimon Glass return -EINVAL; 539f5acf91fSSimon Glass } 540f5acf91fSSimon Glass 541f5acf91fSSimon Glass back = fdtdec_get_int(blob, panel_node, "left-margin", -1); 542f5acf91fSSimon Glass front = fdtdec_get_int(blob, panel_node, "right-margin", -1); 543f5acf91fSSimon Glass ref = fdtdec_get_int(blob, panel_node, "hsync-len", -1); 544f5acf91fSSimon Glass if ((back | front | ref) == -1) { 545f5acf91fSSimon Glass debug("%s: Horizontal parameters missing\n", __func__); 546f5acf91fSSimon Glass return -EINVAL; 547f5acf91fSSimon Glass } 548f5acf91fSSimon Glass 549f5acf91fSSimon Glass /* Use a ref-to-sync of 1 always, and take this from the front porch */ 550f5acf91fSSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 551f5acf91fSSimon Glass priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 552f5acf91fSSimon Glass priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 553f5acf91fSSimon Glass priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 554f5acf91fSSimon Glass priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 555f5acf91fSSimon Glass debug_timing("horiz", priv->horiz_timing); 556f5acf91fSSimon Glass 557f5acf91fSSimon Glass back = fdtdec_get_int(blob, panel_node, "upper-margin", -1); 558f5acf91fSSimon Glass front = fdtdec_get_int(blob, panel_node, "lower-margin", -1); 559f5acf91fSSimon Glass ref = fdtdec_get_int(blob, panel_node, "vsync-len", -1); 560f5acf91fSSimon Glass if ((back | front | ref) == -1) { 561f5acf91fSSimon Glass debug("%s: Vertical parameters missing\n", __func__); 562f5acf91fSSimon Glass return -EINVAL; 563f5acf91fSSimon Glass } 564f5acf91fSSimon Glass 565f5acf91fSSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; 566f5acf91fSSimon Glass priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; 567f5acf91fSSimon Glass priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; 568f5acf91fSSimon Glass priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - 569f5acf91fSSimon Glass priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; 570f5acf91fSSimon Glass debug_timing("vert", priv->vert_timing); 571f5acf91fSSimon Glass 572f5acf91fSSimon Glass bpp = fdtdec_get_int(blob, panel_node, "nvidia,bits-per-pixel", -1); 573f5acf91fSSimon Glass bit = ffs(bpp) - 1; 574f5acf91fSSimon Glass if (bpp == (1 << bit)) 575f5acf91fSSimon Glass priv->log2_bpp = bit; 576f5acf91fSSimon Glass else 577f5acf91fSSimon Glass priv->log2_bpp = bpp; 578f5acf91fSSimon Glass if (bpp == -1) { 579f5acf91fSSimon Glass debug("%s: Pixel bpp parameters missing\n", __func__); 580f5acf91fSSimon Glass return -EINVAL; 581f5acf91fSSimon Glass } 582f5acf91fSSimon Glass 58391c08afeSSimon Glass if (fdtdec_parse_phandle_with_args(blob, panel_node, "nvidia,pwm", 58491c08afeSSimon Glass "#pwm-cells", 0, 0, &args)) { 58591c08afeSSimon Glass debug("%s: Unable to decode PWM\n", __func__); 586f5acf91fSSimon Glass return -EINVAL; 587f5acf91fSSimon Glass } 588f5acf91fSSimon Glass 58991c08afeSSimon Glass ret = uclass_get_device_by_of_offset(UCLASS_PWM, args.node, &priv->pwm); 59091c08afeSSimon Glass if (ret) { 59191c08afeSSimon Glass debug("%s: Unable to find PWM\n", __func__); 59291c08afeSSimon Glass return -EINVAL; 59391c08afeSSimon Glass } 59491c08afeSSimon Glass priv->pwm_channel = args.args[0]; 59591c08afeSSimon Glass 596f5acf91fSSimon Glass priv->cache_type = fdtdec_get_int(blob, panel_node, "nvidia,cache-type", 597f5acf91fSSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH); 598f5acf91fSSimon Glass 599f5acf91fSSimon Glass /* These GPIOs are all optional */ 600f5acf91fSSimon Glass gpio_request_by_name_nodev(blob, panel_node, 601f5acf91fSSimon Glass "nvidia,backlight-enable-gpios", 0, 602f5acf91fSSimon Glass &priv->backlight_en, GPIOD_IS_OUT); 603f5acf91fSSimon Glass gpio_request_by_name_nodev(blob, panel_node, 604f5acf91fSSimon Glass "nvidia,lvds-shutdown-gpios", 0, 605f5acf91fSSimon Glass &priv->lvds_shutdown, GPIOD_IS_OUT); 606f5acf91fSSimon Glass gpio_request_by_name_nodev(blob, panel_node, 607f5acf91fSSimon Glass "nvidia,backlight-vdd-gpios", 0, 608f5acf91fSSimon Glass &priv->backlight_vdd, GPIOD_IS_OUT); 609f5acf91fSSimon Glass gpio_request_by_name_nodev(blob, panel_node, 610f5acf91fSSimon Glass "nvidia,panel-vdd-gpios", 0, 611f5acf91fSSimon Glass &priv->panel_vdd, GPIOD_IS_OUT); 612f5acf91fSSimon Glass 613f5acf91fSSimon Glass if (fdtdec_get_int_array(blob, panel_node, "nvidia,panel-timings", 614f5acf91fSSimon Glass priv->panel_timings, FDT_LCD_TIMINGS)) 615f5acf91fSSimon Glass return -EINVAL; 616f5acf91fSSimon Glass 617f5acf91fSSimon Glass return 0; 618f5acf91fSSimon Glass } 619f5acf91fSSimon Glass 6209e6866d3SSimon Glass static int tegra_lcd_bind(struct udevice *dev) 6219e6866d3SSimon Glass { 6229e6866d3SSimon Glass struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 623*54693cbdSStephen Warren const void *blob = gd->fdt_blob; 624*54693cbdSStephen Warren int node = dev->of_offset; 625*54693cbdSStephen Warren int rgb; 626*54693cbdSStephen Warren 627*54693cbdSStephen Warren rgb = fdt_subnode_offset(blob, node, "rgb"); 628*54693cbdSStephen Warren if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb)) 629*54693cbdSStephen Warren return -ENODEV; 6309e6866d3SSimon Glass 6319e6866d3SSimon Glass plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * 6329e6866d3SSimon Glass (1 << LCD_MAX_LOG2_BPP) / 8; 6339e6866d3SSimon Glass 6349e6866d3SSimon Glass return 0; 6359e6866d3SSimon Glass } 6369e6866d3SSimon Glass 6379e6866d3SSimon Glass static const struct video_ops tegra_lcd_ops = { 6389e6866d3SSimon Glass }; 6399e6866d3SSimon Glass 6409e6866d3SSimon Glass static const struct udevice_id tegra_lcd_ids[] = { 6419e6866d3SSimon Glass { .compatible = "nvidia,tegra20-dc" }, 6429e6866d3SSimon Glass { } 6439e6866d3SSimon Glass }; 6449e6866d3SSimon Glass 6459e6866d3SSimon Glass U_BOOT_DRIVER(tegra_lcd) = { 6469e6866d3SSimon Glass .name = "tegra_lcd", 6479e6866d3SSimon Glass .id = UCLASS_VIDEO, 6489e6866d3SSimon Glass .of_match = tegra_lcd_ids, 6499e6866d3SSimon Glass .ops = &tegra_lcd_ops, 6509e6866d3SSimon Glass .bind = tegra_lcd_bind, 6519e6866d3SSimon Glass .probe = tegra_lcd_probe, 652f5acf91fSSimon Glass .ofdata_to_platdata = tegra_lcd_ofdata_to_platdata, 6539e6866d3SSimon Glass .priv_auto_alloc_size = sizeof(struct tegra_lcd_priv), 6549e6866d3SSimon Glass }; 655