static void tegra_dsi_set_dc_clk(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { u32 shift_clk_div; u32 val; if (dsi->info.video_burst_mode == TEGRA_DSI_VIDEO_NONE_BURST_MODE || dsi->info.video_burst_mode == TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END) shift_clk_div = NUMOF_BIT_PER_BYTE * dsi->pixel_scaler_mul / (dsi->pixel_scaler_div * dsi->info.n_data_lanes) - 2; else shift_clk_div = (dsi->current_dsi_clk_khz * 2 + dsi->default_hs_clk_khz - 1) / (dsi->default_hs_clk_khz) - 2; #ifdef CONFIG_TEGRA_FPGA_PLATFORM shift_clk_div = 1; #endif /* TODO: find out if PCD3 option is required */ val = PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(shift_clk_div); tegra_dc_writel(dc, val, DC_DISP_DISP_CLOCK_CONTROL); clk_enable(dsi->dc_clk); }
int update_display_mode(struct display_controller *disp_ctrl, struct soc_nvidia_tegra132_config *config) { print_mode(config); printk(BIOS_ERR, "config: xres:yres: %d x %d\n ", config->xres, config->yres); printk(BIOS_ERR, " href_sync:vref_sync: %d x %d\n ", config->href_to_sync, config->vref_to_sync); printk(BIOS_ERR, " hsyn_width:vsyn_width: %d x %d\n ", config->hsync_width, config->vsync_width); printk(BIOS_ERR, " hfnt_porch:vfnt_porch: %d x %d\n ", config->hfront_porch, config->vfront_porch); printk(BIOS_ERR, " hbk_porch:vbk_porch: %d x %d\n ", config->hback_porch, config->vback_porch); WRITEL(0x0, &disp_ctrl->disp.disp_timing_opt); WRITEL(0x0, &disp_ctrl->disp.disp_color_ctrl); /* select win opt */ WRITEL(config->win_opt, &disp_ctrl->disp.disp_win_opt); WRITEL(config->vref_to_sync << 16 | config->href_to_sync, &disp_ctrl->disp.ref_to_sync); WRITEL(config->vsync_width << 16 | config->hsync_width, &disp_ctrl->disp.sync_width); WRITEL((config->vback_porch << 16) | config->hback_porch, &disp_ctrl->disp.back_porch); WRITEL((config->vfront_porch << 16) | config->hfront_porch, &disp_ctrl->disp.front_porch); WRITEL(config->xres | (config->yres << 16), &disp_ctrl->disp.disp_active); /* * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv. * * default: Set both shift_clk_div and pixel_clock_div to 1 */ update_display_shift_clock_divider(disp_ctrl, SHIFT_CLK_DIVIDER(1)); return 0; }
int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) { unsigned long val; unsigned long rate; unsigned long div; unsigned long pclk; print_mode(dc, mode, __func__); /* use default EMC rate when switching modes */ dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); tegra_dc_program_bandwidth(dc, true); tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), DC_DISP_REF_TO_SYNC); tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), DC_DISP_SYNC_WIDTH); tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), DC_DISP_BACK_PORCH); tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), DC_DISP_DISP_ACTIVE); tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), DC_DISP_FRONT_PORCH); tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, DC_DISP_DATA_ENABLE_OPTIONS); /* TODO: MIPI/CRT/HDMI clock cals */ val = 0; if (!(dc->out->type == TEGRA_DC_OUT_DSI || dc->out->type == TEGRA_DC_OUT_HDMI)) { val = DISP_DATA_FORMAT_DF1P1C; if (dc->out->align == TEGRA_DC_ALIGN_MSB) val |= DISP_DATA_ALIGNMENT_MSB; else val |= DISP_DATA_ALIGNMENT_LSB; if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) val |= DISP_DATA_ORDER_RED_BLUE; else val |= DISP_DATA_ORDER_BLUE_RED; } tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); rate = tegra_dc_clk_get_rate(dc); pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); if (pclk < (mode->pclk / 100 * 99) || pclk > (mode->pclk / 100 * 109)) { dev_err(&dc->ndev->dev, "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", rate, mode->pclk, pclk, (mode->pclk / 100 * 99), (mode->pclk / 100 * 109)); return -EINVAL; } div = (rate * 2 / pclk) - 2; tegra_dc_writel(dc, 0x00010001, DC_DISP_SHIFT_CLOCK_OPTIONS); tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), DC_DISP_DISP_CLOCK_CONTROL); #ifdef CONFIG_SWITCH switch_set_state(&dc->modeset_switch, (mode->h_active << 16) | mode->v_active); #endif tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); dc->mode_dirty = false; trace_display_mode(dc, &dc->mode); return 0; }
static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) { unsigned long val; unsigned long rate; unsigned long div; unsigned long pclk; tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16), DC_DISP_REF_TO_SYNC); tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16), DC_DISP_SYNC_WIDTH); tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16), DC_DISP_BACK_PORCH); tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16), DC_DISP_DISP_ACTIVE); tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16), DC_DISP_FRONT_PORCH); tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL, DC_DISP_DATA_ENABLE_OPTIONS); val = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY1); if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) val |= PIN1_LVS_OUTPUT; else val &= ~PIN1_LVS_OUTPUT; if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) val |= PIN1_LHS_OUTPUT; else val &= ~PIN1_LHS_OUTPUT; tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_POLARITY1); /* TODO: MIPI/CRT/HDMI clock cals */ val = DISP_DATA_FORMAT_DF1P1C; if (dc->out->align == TEGRA_DC_ALIGN_MSB) val |= DISP_DATA_ALIGNMENT_MSB; else val |= DISP_DATA_ALIGNMENT_LSB; if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE) val |= DISP_DATA_ORDER_RED_BLUE; else val |= DISP_DATA_ORDER_BLUE_RED; tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL); rate = clk_get_rate(dc->clk); pclk = tegra_dc_pclk_round_rate(dc, mode->pclk); if (pclk < (mode->pclk / 100 * 99) || pclk > (mode->pclk / 100 * 109)) { dev_err(&dc->ndev->dev, "can't divide %ld clock to %d -1/+9%% %ld %d %d\n", rate, mode->pclk, pclk, (mode->pclk / 100 * 99), (mode->pclk / 100 * 109)); return -EINVAL; } div = (rate * 2 / pclk) - 2; tegra_dc_writel(dc, 0x00010001, DC_DISP_SHIFT_CLOCK_OPTIONS); tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div), DC_DISP_DISP_CLOCK_CONTROL); return 0; }