From patchwork Sat Jan 30 23:37:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 576112 X-Patchwork-Delegate: twarren@nvidia.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3765B14076E for ; Sun, 31 Jan 2016 10:43:26 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b=hjYl3OWA; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C47FFA7593; Sun, 31 Jan 2016 00:42:54 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id wKb-HA95cUxB; Sun, 31 Jan 2016 00:42:54 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AB43CA7594; Sun, 31 Jan 2016 00:41:00 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CDF29A74F9 for ; Sun, 31 Jan 2016 00:38:43 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 8wF-zH5rySaf for ; Sun, 31 Jan 2016 00:38:43 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ob0-f173.google.com (mail-ob0-f173.google.com [209.85.214.173]) by theia.denx.de (Postfix) with ESMTPS id 5DBC4A7576 for ; Sun, 31 Jan 2016 00:38:27 +0100 (CET) Received: by mail-ob0-f173.google.com with SMTP id is5so91728492obc.0 for ; Sat, 30 Jan 2016 15:38:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=pVZRjt2qaj3DNtvjmF3/iH9je9V62FVPLy4vXBUOLlE=; b=hjYl3OWAnV58rj3+ayGEioHYnD1WU9tZc9Zk15Nw3OFFzqbj+Je1ZH3ft+lwvfNrLR oGpwUDion6MjxEhJGGFCSC5iP/Jkd4mwEEbv7QM6TXnUpILdUFIhAT5rT6uVKcSYEO2s 1sjJfbsbLw5hIHz0MnZhPMJQVQKAFgRDjVcAf7TD5v1YP9qHQoaFBI5kyCLygMQ9lS6N l61Om3WOKu9e8c9E+iIC83m1qBHU9Fu4GUHnjfn9Nqxt26FYoV1L+vOUU50H3XuMk0cl yIXU4mlOVYKNnJoNpjsOMrAge3il7+HWTXFwISAKhWBZuY5Zzg68cnGZfse4oIL3ctOe EfKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=pVZRjt2qaj3DNtvjmF3/iH9je9V62FVPLy4vXBUOLlE=; b=F297MlPKDkPFA4D6r3o86MsLF3rzkRxy5WUvuJvzECRg8PUs4yJ9QOTEATG231mSU9 Fo0ZVBFe8RCAsgtiFiqUmmK07yERCEMxNJlcV/SmXNXXwuGHrInf+vS3XpxR7ZL69ZuU GUuw/lIPKaOpcaPv0nMMEEYpDvz77Olnwd5m+uzRrFPv1xqY8Gle4nWQE87F783TElcx Uwle0OHT7FilsX1zIbs2j+W6TvvtQro+tNQKL4eanvAgOiNGnxO8qHIrr3xfA0eiKjHH N82Da1JPME4fVJXvTvGav49S8p+pzgOClWGDzbNllaVVWueKVM+N21he9pJuK0tyjQjX L4FA== X-Gm-Message-State: AG10YORzeYBjQpdf7dH2rBZVBcj1+dIHeb/parbzyfYEWyhgr5RbZPP0oteLv9HR8UhLHxC1 X-Received: by 10.182.16.233 with SMTP id j9mr13010443obd.9.1454197106091; Sat, 30 Jan 2016 15:38:26 -0800 (PST) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by smtp.gmail.com with ESMTPSA id k5sm11278451oed.1.2016.01.30.15.38.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 30 Jan 2016 15:38:24 -0800 (PST) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 19D15221A89; Sat, 30 Jan 2016 16:38:18 -0700 (MST) From: Simon Glass To: U-Boot Mailing List Date: Sat, 30 Jan 2016 16:37:53 -0700 Message-Id: <1454197082-1312-15-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.7.0.rc3.207.g0ac5344 In-Reply-To: <1454197082-1312-1-git-send-email-sjg@chromium.org> References: <1454197082-1312-1-git-send-email-sjg@chromium.org> Cc: Stephen Warren , Marcel Ziswiler , Tom Warren Subject: [U-Boot] [PATCH v2 14/23] tegra: video: Merge the display driver into one file X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" At present we have code in arch/arm and code in drivers/video. Move it all into drivers/video since it is a display driver and our current approach is to put all driver code in drivers/. Make a few functions static now that they are not used outside the file. Signed-off-by: Simon Glass Acked-by: Anatolij Gustschin --- Changes in v2: - Fix a checkpatch warning arch/arm/include/asm/arch-tegra20/display.h | 21 -- arch/arm/mach-tegra/tegra20/Makefile | 2 - arch/arm/mach-tegra/tegra20/display.c | 378 -------------------------- drivers/video/tegra.c | 396 ++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+), 401 deletions(-) diff --git a/arch/arm/include/asm/arch-tegra20/display.h b/arch/arm/include/asm/arch-tegra20/display.h index 018c9f9..35123ed 100644 --- a/arch/arm/include/asm/arch-tegra20/display.h +++ b/arch/arm/include/asm/arch-tegra20/display.h @@ -88,27 +88,6 @@ struct fdt_panel_config { }; /** - * Register a new display based on device tree configuration. - * - * The frame buffer can be positioned by U-Boot or overriden by the fdt. - * You should pass in the U-Boot address here, and check the contents of - * struct fdt_disp_config to see what was actually chosen. - * - * @param blob Device tree blob - * @param default_lcd_base Default address of LCD frame buffer - * @return 0 if ok, -1 on error (unsupported bits per pixel) - */ -int tegra_display_probe(const void *blob, void *default_lcd_base); - -/** - * Return the current display configuration - * - * @return pointer to display configuration, or NULL if there is no valid - * config - */ -struct fdt_disp_config *tegra_display_get_config(void); - -/** * Perform the next stage of the LCD init if it is time to do so. * * LCD init can be time-consuming because of the number of delays we need diff --git a/arch/arm/mach-tegra/tegra20/Makefile b/arch/arm/mach-tegra/tegra20/Makefile index efcf843..17c1990 100644 --- a/arch/arm/mach-tegra/tegra20/Makefile +++ b/arch/arm/mach-tegra/tegra20/Makefile @@ -6,8 +6,6 @@ ifdef CONFIG_SPL_BUILD obj-y += cpu.o -else -obj-$(CONFIG_VIDEO_TEGRA20) += display.o endif # The AVP is ARMv4T architecture so we must use special compiler diff --git a/arch/arm/mach-tegra/tegra20/display.c b/arch/arm/mach-tegra/tegra20/display.c index b7605ff..73be9a9 100644 --- a/arch/arm/mach-tegra/tegra20/display.c +++ b/arch/arm/mach-tegra/tegra20/display.c @@ -14,381 +14,3 @@ #include #include -static struct fdt_disp_config config; - -static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) -{ - unsigned h_dda, v_dda; - unsigned long val; - - val = readl(&dc->cmd.disp_win_header); - val |= WINDOW_A_SELECT; - writel(val, &dc->cmd.disp_win_header); - - writel(win->fmt, &dc->win.color_depth); - - clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, - BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); - - val = win->out_x << H_POSITION_SHIFT; - val |= win->out_y << V_POSITION_SHIFT; - writel(val, &dc->win.pos); - - val = win->out_w << H_SIZE_SHIFT; - val |= win->out_h << V_SIZE_SHIFT; - writel(val, &dc->win.size); - - val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; - val |= win->h << V_PRESCALED_SIZE_SHIFT; - writel(val, &dc->win.prescaled_size); - - writel(0, &dc->win.h_initial_dda); - writel(0, &dc->win.v_initial_dda); - - h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); - v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); - - val = h_dda << H_DDA_INC_SHIFT; - val |= v_dda << V_DDA_INC_SHIFT; - writel(val, &dc->win.dda_increment); - - writel(win->stride, &dc->win.line_stride); - writel(0, &dc->win.buf_stride); - - val = WIN_ENABLE; - if (win->bpp < 24) - val |= COLOR_EXPAND; - writel(val, &dc->win.win_opt); - - writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); - writel(win->x, &dc->winbuf.addr_h_offset); - writel(win->y, &dc->winbuf.addr_v_offset); - - writel(0xff00, &dc->win.blend_nokey); - writel(0xff00, &dc->win.blend_1win); - - val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; - val |= GENERAL_UPDATE | WIN_A_UPDATE; - writel(val, &dc->cmd.state_ctrl); -} - -static void write_pair(struct fdt_disp_config *config, int item, u32 *reg) -{ - writel(config->horiz_timing[item] | - (config->vert_timing[item] << 16), reg); -} - -static int update_display_mode(struct dc_disp_reg *disp, - struct fdt_disp_config *config) -{ - unsigned long val; - unsigned long rate; - unsigned long div; - - writel(0x0, &disp->disp_timing_opt); - write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); - write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); - write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); - write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); - - writel(config->width | (config->height << 16), &disp->disp_active); - - val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; - val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; - writel(val, &disp->data_enable_opt); - - val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; - val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; - val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; - writel(val, &disp->disp_interface_ctrl); - - /* - * The pixel clock divider is in 7.1 format (where the bottom bit - * represents 0.5). Here we calculate the divider needed to get from - * the display clock (typically 600MHz) to the pixel clock. We round - * up or down as requried. - */ - rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); - div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; - debug("Display clock %lu, divider %lu\n", rate, div); - - writel(0x00010001, &disp->shift_clk_opt); - - val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; - val |= div << SHIFT_CLK_DIVIDER_SHIFT; - writel(val, &disp->disp_clk_ctrl); - - return 0; -} - -/* Start up the display and turn on power to PWMs */ -static void basic_init(struct dc_cmd_reg *cmd) -{ - u32 val; - - writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); - writel(0x0000011a, &cmd->cont_syncpt_vsync); - writel(0x00000000, &cmd->int_type); - writel(0x00000000, &cmd->int_polarity); - writel(0x00000000, &cmd->int_mask); - writel(0x00000000, &cmd->int_enb); - - val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; - val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; - val |= PM1_ENABLE; - writel(val, &cmd->disp_pow_ctrl); - - val = readl(&cmd->disp_cmd); - val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; - writel(val, &cmd->disp_cmd); -} - -static void basic_init_timer(struct dc_disp_reg *disp) -{ - writel(0x00000020, &disp->mem_high_pri); - writel(0x00000001, &disp->mem_high_pri_timer); -} - -static const u32 rgb_enb_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x01000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_data_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00210222, - 0x00002200, - 0x00020000, -}; - -static void rgb_enable(struct dc_com_reg *com) -{ - int i; - - for (i = 0; i < PIN_REG_COUNT; i++) { - writel(rgb_enb_tab[i], &com->pin_output_enb[i]); - writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); - writel(rgb_data_tab[i], &com->pin_output_data[i]); - } - - for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) - writel(rgb_sel_tab[i], &com->pin_output_sel[i]); -} - -static int setup_window(struct disp_ctl_win *win, - struct fdt_disp_config *config) -{ - win->x = 0; - win->y = 0; - win->w = config->width; - win->h = config->height; - win->out_x = 0; - win->out_y = 0; - win->out_w = config->width; - win->out_h = config->height; - win->phys_addr = config->frame_buffer; - win->stride = config->width * (1 << config->log2_bpp) / 8; - debug("%s: depth = %d\n", __func__, config->log2_bpp); - switch (config->log2_bpp) { - case 5: - case 24: - win->fmt = COLOR_DEPTH_R8G8B8A8; - win->bpp = 32; - break; - case 4: - win->fmt = COLOR_DEPTH_B5G6R5; - win->bpp = 16; - break; - - default: - debug("Unsupported LCD bit depth"); - return -1; - } - - return 0; -} - -struct fdt_disp_config *tegra_display_get_config(void) -{ - return config.valid ? &config : NULL; -} - -static void debug_timing(const char *name, unsigned int timing[]) -{ -#ifdef DEBUG - int i; - - debug("%s timing: ", name); - for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) - debug("%d ", timing[i]); - debug("\n"); -#endif -} - -/** - * Decode panel information from the fdt, according to a standard binding - * - * @param blob fdt blob - * @param node offset of fdt node to read from - * @param config structure to store fdt config into - * @return 0 if ok, -ve on error - */ -static int tegra_decode_panel(const void *blob, int node, - struct fdt_disp_config *config) -{ - int front, back, ref; - - config->width = fdtdec_get_int(blob, node, "xres", -1); - config->height = fdtdec_get_int(blob, node, "yres", -1); - config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); - if (!config->pixel_clock || config->width == -1 || - config->height == -1) { - debug("%s: Pixel parameters missing\n", __func__); - return -FDT_ERR_NOTFOUND; - } - - back = fdtdec_get_int(blob, node, "left-margin", -1); - front = fdtdec_get_int(blob, node, "right-margin", -1); - ref = fdtdec_get_int(blob, node, "hsync-len", -1); - if ((back | front | ref) == -1) { - debug("%s: Horizontal parameters missing\n", __func__); - return -FDT_ERR_NOTFOUND; - } - - /* Use a ref-to-sync of 1 always, and take this from the front porch */ - config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; - config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; - config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; - config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - - config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; - debug_timing("horiz", config->horiz_timing); - - back = fdtdec_get_int(blob, node, "upper-margin", -1); - front = fdtdec_get_int(blob, node, "lower-margin", -1); - ref = fdtdec_get_int(blob, node, "vsync-len", -1); - if ((back | front | ref) == -1) { - debug("%s: Vertical parameters missing\n", __func__); - return -FDT_ERR_NOTFOUND; - } - - config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; - config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; - config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; - config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - - config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; - debug_timing("vert", config->vert_timing); - - return 0; -} - -/** - * Decode the display controller information from the fdt. - * - * @param blob fdt blob - * @param config structure to store fdt config into - * @return 0 if ok, -ve on error - */ -static int tegra_display_decode_config(const void *blob, - struct fdt_disp_config *config) -{ - int node, rgb; - int bpp, bit; - - /* TODO: Support multiple controllers */ - node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC); - if (node < 0) { - debug("%s: Cannot find display controller node in fdt\n", - __func__); - return node; - } - config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); - if (!config->disp) { - debug("%s: No display controller address\n", __func__); - return -1; - } - - rgb = fdt_subnode_offset(blob, node, "rgb"); - - config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); - if (config->panel_node < 0) { - debug("%s: Cannot find panel information\n", __func__); - return -1; - } - - if (tegra_decode_panel(blob, config->panel_node, config)) { - debug("%s: Failed to decode panel information\n", __func__); - return -1; - } - - bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel", - -1); - bit = ffs(bpp) - 1; - if (bpp == (1 << bit)) - config->log2_bpp = bit; - else - config->log2_bpp = bpp; - if (bpp == -1) { - debug("%s: Pixel bpp parameters missing\n", __func__); - return -FDT_ERR_NOTFOUND; - } - config->bpp = bpp; - - config->valid = 1; /* we have a valid configuration */ - - return 0; -} - -int tegra_display_probe(const void *blob, void *default_lcd_base) -{ - struct disp_ctl_win window; - struct dc_ctlr *dc; - - if (tegra_display_decode_config(blob, &config)) - return -1; - - config.frame_buffer = (u32)default_lcd_base; - - dc = (struct dc_ctlr *)config.disp; - - /* - * A header file for clock constants was NAKed upstream. - * TODO: Put this into the FDT and fdt_lcd struct when we have clock - * support there - */ - clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, - 144 * 1000000); - clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, - 600 * 1000000); - basic_init(&dc->cmd); - basic_init_timer(&dc->disp); - rgb_enable(&dc->com); - - if (config.pixel_clock) - update_display_mode(&dc->disp, &config); - - if (setup_window(&window, &config)) - return -1; - - update_window(dc, &window); - - return 0; -} diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index 8e81346..5b8d623 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ static unsigned long timer_next; /* Time we can move onto next stage */ /* Our LCD config, set up in handle_stage() */ static struct fdt_panel_config config; struct fdt_disp_config *disp_config; /* Display controller config */ +static struct fdt_disp_config dconfig; enum { /* Maximum LCD size we support */ @@ -53,6 +55,400 @@ vidinfo_t panel_info = { #error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" #endif +static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) +{ + unsigned h_dda, v_dda; + unsigned long val; + + val = readl(&dc->cmd.disp_win_header); + val |= WINDOW_A_SELECT; + writel(val, &dc->cmd.disp_win_header); + + writel(win->fmt, &dc->win.color_depth); + + clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, + BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); + + val = win->out_x << H_POSITION_SHIFT; + val |= win->out_y << V_POSITION_SHIFT; + writel(val, &dc->win.pos); + + val = win->out_w << H_SIZE_SHIFT; + val |= win->out_h << V_SIZE_SHIFT; + writel(val, &dc->win.size); + + val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; + val |= win->h << V_PRESCALED_SIZE_SHIFT; + writel(val, &dc->win.prescaled_size); + + writel(0, &dc->win.h_initial_dda); + writel(0, &dc->win.v_initial_dda); + + h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); + v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); + + val = h_dda << H_DDA_INC_SHIFT; + val |= v_dda << V_DDA_INC_SHIFT; + writel(val, &dc->win.dda_increment); + + writel(win->stride, &dc->win.line_stride); + writel(0, &dc->win.buf_stride); + + val = WIN_ENABLE; + if (win->bpp < 24) + val |= COLOR_EXPAND; + writel(val, &dc->win.win_opt); + + writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); + writel(win->x, &dc->winbuf.addr_h_offset); + writel(win->y, &dc->winbuf.addr_v_offset); + + writel(0xff00, &dc->win.blend_nokey); + writel(0xff00, &dc->win.blend_1win); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &dc->cmd.state_ctrl); +} + +static void write_pair(struct fdt_disp_config *config, int item, u32 *reg) +{ + writel(config->horiz_timing[item] | + (config->vert_timing[item] << 16), reg); +} + +static int update_display_mode(struct dc_disp_reg *disp, + struct fdt_disp_config *config) +{ + unsigned long val; + unsigned long rate; + unsigned long div; + + writel(0x0, &disp->disp_timing_opt); + write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); + write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); + write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); + write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); + + writel(config->width | (config->height << 16), &disp->disp_active); + + val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; + val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; + writel(val, &disp->data_enable_opt); + + val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; + val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; + val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; + writel(val, &disp->disp_interface_ctrl); + + /* + * The pixel clock divider is in 7.1 format (where the bottom bit + * represents 0.5). Here we calculate the divider needed to get from + * the display clock (typically 600MHz) to the pixel clock. We round + * up or down as requried. + */ + rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); + div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; + debug("Display clock %lu, divider %lu\n", rate, div); + + writel(0x00010001, &disp->shift_clk_opt); + + val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; + val |= div << SHIFT_CLK_DIVIDER_SHIFT; + writel(val, &disp->disp_clk_ctrl); + + return 0; +} + +/* Start up the display and turn on power to PWMs */ +static void basic_init(struct dc_cmd_reg *cmd) +{ + u32 val; + + writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); + writel(0x0000011a, &cmd->cont_syncpt_vsync); + writel(0x00000000, &cmd->int_type); + writel(0x00000000, &cmd->int_polarity); + writel(0x00000000, &cmd->int_mask); + writel(0x00000000, &cmd->int_enb); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; + val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; + val |= PM1_ENABLE; + writel(val, &cmd->disp_pow_ctrl); + + val = readl(&cmd->disp_cmd); + val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; + writel(val, &cmd->disp_cmd); +} + +static void basic_init_timer(struct dc_disp_reg *disp) +{ + writel(0x00000020, &disp->mem_high_pri); + writel(0x00000001, &disp->mem_high_pri_timer); +} + +static const u32 rgb_enb_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x01000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_data_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00210222, + 0x00002200, + 0x00020000, +}; + +static void rgb_enable(struct dc_com_reg *com) +{ + int i; + + for (i = 0; i < PIN_REG_COUNT; i++) { + writel(rgb_enb_tab[i], &com->pin_output_enb[i]); + writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); + writel(rgb_data_tab[i], &com->pin_output_data[i]); + } + + for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) + writel(rgb_sel_tab[i], &com->pin_output_sel[i]); +} + +static int setup_window(struct disp_ctl_win *win, + struct fdt_disp_config *config) +{ + win->x = 0; + win->y = 0; + win->w = config->width; + win->h = config->height; + win->out_x = 0; + win->out_y = 0; + win->out_w = config->width; + win->out_h = config->height; + win->phys_addr = config->frame_buffer; + win->stride = config->width * (1 << config->log2_bpp) / 8; + debug("%s: depth = %d\n", __func__, config->log2_bpp); + switch (config->log2_bpp) { + case 5: + case 24: + win->fmt = COLOR_DEPTH_R8G8B8A8; + win->bpp = 32; + break; + case 4: + win->fmt = COLOR_DEPTH_B5G6R5; + win->bpp = 16; + break; + + default: + debug("Unsupported LCD bit depth"); + return -1; + } + + return 0; +} + +/** + * Return the current display configuration + * + * @return pointer to display configuration, or NULL if there is no valid + * config + */ +struct fdt_disp_config *tegra_display_get_config(void) +{ + return dconfig.valid ? &dconfig : NULL; +} + +static void debug_timing(const char *name, unsigned int timing[]) +{ +#ifdef DEBUG + int i; + + debug("%s timing: ", name); + for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) + debug("%d ", timing[i]); + debug("\n"); +#endif +} + +/** + * Decode panel information from the fdt, according to a standard binding + * + * @param blob fdt blob + * @param node offset of fdt node to read from + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_decode_panel(const void *blob, int node, + struct fdt_disp_config *config) +{ + int front, back, ref; + + config->width = fdtdec_get_int(blob, node, "xres", -1); + config->height = fdtdec_get_int(blob, node, "yres", -1); + config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); + if (!config->pixel_clock || config->width == -1 || + config->height == -1) { + debug("%s: Pixel parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + back = fdtdec_get_int(blob, node, "left-margin", -1); + front = fdtdec_get_int(blob, node, "right-margin", -1); + ref = fdtdec_get_int(blob, node, "hsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Horizontal parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* Use a ref-to-sync of 1 always, and take this from the front porch */ + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("horiz", config->horiz_timing); + + back = fdtdec_get_int(blob, node, "upper-margin", -1); + front = fdtdec_get_int(blob, node, "lower-margin", -1); + ref = fdtdec_get_int(blob, node, "vsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Vertical parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("vert", config->vert_timing); + + return 0; +} + +/** + * Decode the display controller information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_display_decode_config(const void *blob, + struct fdt_disp_config *config) +{ + int node, rgb; + int bpp, bit; + + /* TODO: Support multiple controllers */ + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC); + if (node < 0) { + debug("%s: Cannot find display controller node in fdt\n", + __func__); + return node; + } + config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); + if (!config->disp) { + debug("%s: No display controller address\n", __func__); + return -1; + } + + rgb = fdt_subnode_offset(blob, node, "rgb"); + + config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); + if (config->panel_node < 0) { + debug("%s: Cannot find panel information\n", __func__); + return -1; + } + + if (tegra_decode_panel(blob, config->panel_node, config)) { + debug("%s: Failed to decode panel information\n", __func__); + return -1; + } + + bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel", + -1); + bit = ffs(bpp) - 1; + if (bpp == (1 << bit)) + config->log2_bpp = bit; + else + config->log2_bpp = bpp; + if (bpp == -1) { + debug("%s: Pixel bpp parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + config->bpp = bpp; + + config->valid = 1; /* we have a valid configuration */ + + return 0; +} + +/** + * Register a new display based on device tree configuration. + * + * The frame buffer can be positioned by U-Boot or overriden by the fdt. + * You should pass in the U-Boot address here, and check the contents of + * struct fdt_disp_config to see what was actually chosen. + * + * @param blob Device tree blob + * @param default_lcd_base Default address of LCD frame buffer + * @return 0 if ok, -1 on error (unsupported bits per pixel) + */ +static int tegra_display_probe(const void *blob, void *default_lcd_base) +{ + struct disp_ctl_win window; + struct dc_ctlr *dc; + + if (tegra_display_decode_config(blob, &dconfig)) + return -1; + + dconfig.frame_buffer = (u32)default_lcd_base; + + dc = (struct dc_ctlr *)dconfig.disp; + + /* + * A header file for clock constants was NAKed upstream. + * TODO: Put this into the FDT and fdt_lcd struct when we have clock + * support there + */ + clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, + 144 * 1000000); + clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, + 600 * 1000000); + basic_init(&dc->cmd); + basic_init_timer(&dc->disp); + rgb_enable(&dc->com); + + if (dconfig.pixel_clock) + update_display_mode(&dc->disp, &dconfig); + + if (setup_window(&window, &dconfig)) + return -1; + + update_window(dc, &window); + + return 0; +} + static void update_panel_size(struct fdt_disp_config *config) { panel_info.vl_col = config->width;