diff options
Diffstat (limited to 'src/soc/nvidia/tegra124/display.c')
-rw-r--r-- | src/soc/nvidia/tegra124/display.c | 356 |
1 files changed, 147 insertions, 209 deletions
diff --git a/src/soc/nvidia/tegra124/display.c b/src/soc/nvidia/tegra124/display.c index 8c4e8232dd..4a0f4501a2 100644 --- a/src/soc/nvidia/tegra124/display.c +++ b/src/soc/nvidia/tegra124/display.c @@ -38,18 +38,26 @@ #include "chip.h" #include <soc/display.h> +struct tegra_dc dc_data; + int dump = 0; -unsigned long READL(void * p); -void WRITEL(unsigned long value, void * p); unsigned long READL(void * p) { - unsigned long value = readl(p); + unsigned long value; + + /* + * In case of hard hung on readl(p), we can set dump > 1 to print out + * the address accessed. + */ + if (dump > 1) + printk(BIOS_SPEW, "readl %p\n", p); + + value = readl(p); if (dump) printk(BIOS_SPEW, "readl %p %08lx\n", p, value); return value; } - void WRITEL(unsigned long value, void * p) { if (dump) @@ -57,174 +65,133 @@ void WRITEL(unsigned long value, void * p) writel(value, p); } -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 int update_display_mode(struct dc_disp_reg *disp, - struct soc_nvidia_tegra124_config *config) +/* return in 1000ths of a Hertz */ +static int tegra_dc_calc_refresh(const struct soc_nvidia_tegra124_config *config) { - u32 val; - u32 rate; - u32 div; - - WRITEL(0x0, &disp->disp_timing_opt); + int h_total, v_total, refresh; + int pclk = config->pixel_clock; + + h_total = config->xres + config->hfront_porch + config->hback_porch + + config->hsync_width; + v_total = config->yres + config->vfront_porch + config->vback_porch + + config->vsync_width; + if (!pclk || !h_total || !v_total) + return 0; + refresh = pclk / h_total; + refresh *= 1000; + refresh /= v_total; + return refresh; +} - WRITEL(config->vref_to_sync << 16 | config->href_to_sync, - &disp->ref_to_sync); - WRITEL(config->vsync_width << 16 | config->hsync_width, &disp->sync_width); - WRITEL(config->vback_porch << 16 | config->hback_porch, &disp->back_porch); - WRITEL(config->vfront_porch << 16 | config->hfront_porch, - &disp->front_porch); +static void print_mode(const struct soc_nvidia_tegra124_config *config) +{ + if (config) { + int refresh = tegra_dc_calc_refresh(config); + printk(BIOS_ERR, + "MODE:%dx%d@%d.%03uHz pclk=%d\n", + config->xres, config->yres, + refresh / 1000, refresh % 1000, + config->pixel_clock); + } +} - WRITEL(config->xres | (config->yres << 16), &disp->disp_active); +static int update_display_mode(struct display_controller *disp_ctrl, + struct soc_nvidia_tegra124_config *config) +{ + unsigned long div = config->pll_div; - val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; - val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; - WRITEL(val, &disp->data_enable_opt); + print_mode(config); - 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); + WRITEL(0x1, &disp_ctrl->disp.disp_timing_opt); - /* - * 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. - * We use pllp for now. - */ - rate = 600 * 1000000; - div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; - printk(BIOS_SPEW, "Display clock %d, divider %d\n", rate, div); + WRITEL(config->vref_to_sync << 16 | config->href_to_sync, + &disp_ctrl->disp.ref_to_sync); - WRITEL(0x00010001, &disp->shift_clk_opt); + WRITEL(config->vsync_width << 16 | config->hsync_width, + &disp_ctrl->disp.sync_width); - val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; - val |= div << SHIFT_CLK_DIVIDER_SHIFT; - WRITEL(val, &disp->disp_clk_ctrl); + WRITEL(((config->vback_porch - config->vref_to_sync) << 16) | config->hback_porch, + &disp_ctrl->disp.back_porch); - return 0; -} + WRITEL(((config->vfront_porch + config->vref_to_sync) << 16) | config->hfront_porch, + &disp_ctrl->disp.front_porch); -static int setup_window(struct disp_ctl_win *win, - struct soc_nvidia_tegra124_config *config) -{ - int log2_bpp = log2(config->framebuffer_bits_per_pixel); - win->x = 0; - win->y = 0; - win->w = config->xres; - win->h = config->yres; - win->out_x = 0; - win->out_y = 0; - win->out_w = config->xres; - win->out_h = config->yres; - win->phys_addr = config->framebuffer_base; - win->stride = config->xres * (1 << log2_bpp) / 8; - printk(BIOS_SPEW, "%s: depth = %d\n", __func__, log2_bpp); - switch (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: - printk(BIOS_SPEW, "Unsupported LCD bit depth"); - return -1; - } + WRITEL(config->xres | (config->yres << 16), + &disp_ctrl->disp.disp_active); + WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) | + SHIFT_CLK_DIVIDER(div), + &disp_ctrl->disp.disp_clk_ctrl); return 0; } -static void update_window(struct display_controller *dc, - struct disp_ctl_win *win, - struct soc_nvidia_tegra124_config *config) +static void update_window(struct display_controller *disp_ctrl, + struct soc_nvidia_tegra124_config *config) { - u32 h_dda, v_dda; u32 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); + WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); - val = win->out_x << H_POSITION_SHIFT; - val |= win->out_y << V_POSITION_SHIFT; - WRITEL(val, &dc->win.pos); + WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size); + WRITEL(((config->yres << 16) | + (config->xres * config->framebuffer_bits_per_pixel / 8)), + &disp_ctrl->win.prescaled_size); + WRITEL(((config->xres * config->framebuffer_bits_per_pixel / 8 + 31) / + 32 * 32), &disp_ctrl->win.line_stride); - val = win->out_w << H_SIZE_SHIFT; - val |= win->out_h << V_SIZE_SHIFT; - WRITEL(val, &dc->win.size); + WRITEL(config->color_depth, &disp_ctrl->win.color_depth); - val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; - val |= win->h << V_PRESCALED_SIZE_SHIFT; - WRITEL(val, &dc->win.prescaled_size); + WRITEL(config->framebuffer_base, &disp_ctrl->winbuf.start_addr); + WRITEL((V_DDA_INC(0x1000) | H_DDA_INC(0x1000)), &disp_ctrl->win.dda_increment); - WRITEL(0, &dc->win.h_initial_dda); - WRITEL(0, &dc->win.v_initial_dda); + WRITEL(COLOR_WHITE, &disp_ctrl->disp.blend_background_color); + WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd); - h_dda = (win->w * 0x1000) / MAX(win->out_w - 1, 1); - v_dda = (win->h * 0x1000) / MAX(win->out_h - 1, 1); + WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); - 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); + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + WRITEL(val, &disp_ctrl->cmd.state_ctrl); - WRITEL((u32) win->phys_addr, &dc->winbuf.start_addr); - WRITEL(win->x, &dc->winbuf.addr_h_offset); - WRITEL(win->y, &dc->winbuf.addr_v_offset); + // Enable win_a + val = READL(&disp_ctrl->win.win_opt); + WRITEL(val | WIN_ENABLE, &disp_ctrl->win.win_opt); +} - WRITEL(0xff00, &dc->win.blend_nokey); - WRITEL(0xff00, &dc->win.blend_1win); +static int tegra_dc_init(struct display_controller *disp_ctrl) +{ + /* do not accept interrupts during initialization */ + WRITEL(0x00000000, &disp_ctrl->cmd.int_mask); + WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, + &disp_ctrl->cmd.state_access); + WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); + WRITEL(0x00000000, &disp_ctrl->win.win_opt); + WRITEL(0x00000000, &disp_ctrl->win.byte_swap); + WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl); + + WRITEL(0x00000000, &disp_ctrl->win.pos); + WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda); + WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda); + WRITEL(0x00000000, &disp_ctrl->win.dda_increment); + WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl); + + WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl); + WRITEL(0x00000000, &disp_ctrl->win.blend_match_select); + WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select); + WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit); + + WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi); + WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset); + WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset); + + WRITEL(0x00000000, &disp_ctrl->com.crc_checksum); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]); + WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0); - val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; - val |= GENERAL_UPDATE | WIN_A_UPDATE; - WRITEL(val, &dc->cmd.state_ctrl); + return 0; } uint32_t fb_base_mb(void) @@ -237,16 +204,22 @@ uint32_t fb_base_mb(void) */ void display_startup(device_t dev) { - u32 val; - int i; struct soc_nvidia_tegra124_config *config = dev->chip_info; - struct display_controller *dc = (void *)config->display_controller; - struct pwm_controller *pwm = (void *)TEGRA_PWM_BASE; - struct disp_ctl_win window; + struct display_controller *disp_ctrl = (void *)config->display_controller; + struct pwm_controller *pwm = (void *)TEGRA_PWM_BASE; + struct tegra_dc *dc = &dc_data; /* should probably just make it all MiB ... in future */ u32 framebuffer_size_mb = config->framebuffer_size / MiB; u32 framebuffer_base_mb= config->framebuffer_base / MiB; + + /* init dc */ + dc->base = (void *)TEGRA_ARM_DISPLAYA; + dc->config = config; + config->dc_data = dc; + + dp_init(config); + /* light it all up */ /* This one may have been done in romstage but that's ok for now. */ if (config->panel_vdd_gpio){ @@ -254,13 +227,12 @@ void display_startup(device_t dev) printk(BIOS_SPEW,"%s: panel_vdd setting gpio %08x to %d\n", __func__, config->panel_vdd_gpio, 1); } - delay(1); + udelay(config->vdd_delay_ms * 1000); if (config->backlight_vdd_gpio){ gpio_output(config->backlight_vdd_gpio, 1); printk(BIOS_SPEW,"%s: backlight vdd setting gpio %08x to %d\n", __func__, config->backlight_vdd_gpio, 1); } - delay(1); if (config->lvds_shutdown_gpio){ gpio_output(config->lvds_shutdown_gpio, 0); printk(BIOS_SPEW,"%s: lvds shutdown setting gpio %08x to %d\n", @@ -281,10 +253,6 @@ void display_startup(device_t dev) 0x02e), /* frequency divider */ &pwm->pwm[config->pwm].csr); - printk(BIOS_SPEW, - "%s: xres %d yres %d framebuffer_bits_per_pixel %d\n", - __func__, - config->xres, config->yres, config->framebuffer_bits_per_pixel); if (framebuffer_size_mb == 0){ framebuffer_size_mb = ALIGN_UP(config->xres * config->yres * (config->framebuffer_bits_per_pixel / 8), MiB)/MiB; @@ -293,13 +261,12 @@ void display_startup(device_t dev) if (! framebuffer_base_mb) framebuffer_base_mb = fb_base_mb(); + config->framebuffer_size = framebuffer_size_mb * MiB; + config->framebuffer_base = framebuffer_base_mb * MiB; + mmu_config_range(framebuffer_base_mb, framebuffer_size_mb, config->cache_policy); - /* Enable flushing after LCD writes if requested */ - /* I don't understand this part yet. - lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); - */ printk(BIOS_SPEW, "LCD frame buffer at %dMiB to %dMiB\n", framebuffer_base_mb, framebuffer_base_mb + framebuffer_size_mb); @@ -311,63 +278,34 @@ void display_startup(device_t dev) * light things up here once we're sure it's all working. */ - /* init dc_a */ - init_dca_regs(); + /* Init dc */ + if (tegra_dc_init(disp_ctrl)) { + printk(BIOS_ERR, "dc: init failed\n"); + return; + } - /* power up perip */ - dp_io_powerup(); + /* Configure dc mode */ + update_display_mode(disp_ctrl, config); - /* bringup dp */ - dp_bringup(framebuffer_base_mb*MiB); + /* Enable dp */ + dp_enable(dc->out); - /* init frame buffer */ + /* Init frame buffer */ memset((void *)(framebuffer_base_mb*MiB), 0x00, framebuffer_size_mb*MiB); + update_window(disp_ctrl, config); + + printk(BIOS_INFO, "%s: display init done.\n", __func__); + /* tell depthcharge ... */ struct edid edid; - edid.x_resolution = 1376; - edid.y_resolution = 768; - edid.bytes_per_line = 1376 * 2; - edid.framebuffer_bits_per_pixel = 16; + edid.bytes_per_line = ((config->xres * config->framebuffer_bits_per_pixel / 8 + 31) / + 32 * 32); + edid.x_resolution = edid.bytes_per_line / (config->framebuffer_bits_per_pixel / 8); + edid.y_resolution = config->yres; + edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel; set_vbe_mode_info_valid(&edid, (uintptr_t)(framebuffer_base_mb*MiB)); - - if (0){ -/* do we still need these? */ - WRITEL(0x00000100, &dc->cmd.gen_incr_syncpt_ctrl); - WRITEL(0x0000011a, &dc->cmd.cont_syncpt_vsync); - WRITEL(0x00000000, &dc->cmd.int_type); - WRITEL(0x00000000, &dc->cmd.int_polarity); - WRITEL(0x00000000, &dc->cmd.int_mask); - WRITEL(0x00000000, &dc->cmd.int_enb); - - val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; - val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; - val |= PM1_ENABLE; - WRITEL(val, &dc->cmd.disp_pow_ctrl); - - val = READL(&dc->cmd.disp_cmd); - val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; - WRITEL(val, &dc->cmd.disp_cmd); - - WRITEL(0x00000020, &dc->disp.mem_high_pri); - WRITEL(0x00000001, &dc->disp.mem_high_pri_timer); - - for (i = 0; i < PIN_REG_COUNT; i++) { - WRITEL(rgb_enb_tab[i], &dc->com.pin_output_enb[i]); - WRITEL(rgb_polarity_tab[i], &dc->com.pin_output_polarity[i]); - WRITEL(rgb_data_tab[i], &dc->com.pin_output_data[i]); - } - - for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) - WRITEL(rgb_sel_tab[i], &dc->com.pin_output_sel[i]); - - if (config->pixel_clock) - update_display_mode(&dc->disp, config); - - if (!setup_window(&window, config)) - update_window(dc, &window, config); - } } |