void tegra_dc_disable(struct tegra_dc *dc) { tegra_dc_ext_disable(dc->ext); /* it's important that new underflow work isn't scheduled before the * lock is acquired. */ cancel_delayed_work_sync(&dc->underflow_work); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { mutex_lock(&dc->one_shot_lock); cancel_delayed_work_sync(&dc->one_shot_work); } mutex_lock(&dc->lock); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) tegra_dc_host_resume(dc); if (dc->enabled) { dc->enabled = false; if (!dc->suspended) _tegra_dc_disable(dc); } #ifdef CONFIG_SWITCH switch_set_state(&dc->modeset_switch, 0); #endif mutex_unlock(&dc->lock); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) mutex_unlock(&dc->one_shot_lock); print_mode_info(dc, dc->mode); }
void tegra_dc_enable(struct tegra_dc *dc) { mutex_lock(&dc->lock); if (!dc->enabled) dc->enabled = _tegra_dc_enable(dc); mutex_unlock(&dc->lock); print_mode_info(dc, dc->mode); }
void tegra_dc_disable(struct tegra_dc *dc) { tegra_dc_ext_disable(dc->ext); /* it's important that new underflow work isn't scheduled before the * lock is acquired. */ cancel_delayed_work_sync(&dc->underflow_work); mutex_lock(&dc->lock); if (dc->enabled) { dc->enabled = false; if (!dc->suspended) _tegra_dc_disable(dc); } #ifdef CONFIG_SWITCH switch_set_state(&dc->modeset_switch, 0); #endif mutex_unlock(&dc->lock); print_mode_info(dc, dc->mode); }
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 = 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); trace_printk("%s:pclk=%ld\n", dc->ndev->name, 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; trace_printk("%s:div=%ld\n", dc->ndev->name, div); 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); print_mode_info(dc, dc->mode); return 0; }
static int tegra_dc_init(struct tegra_dc *dc) { int i; int int_enable; tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); if (dc->ndev->id == 0) { tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0A, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0B, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0C, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1B, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHC, TEGRA_MC_PRIO_HIGH); } else if (dc->ndev->id == 1) { tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0AB, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0BB, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0CB, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1BB, TEGRA_MC_PRIO_MED); tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHCB, TEGRA_MC_PRIO_HIGH); } tegra_dc_writel(dc, 0x00000100 | dc->vblank_syncpt, DC_CMD_CONT_SYNCPT_VSYNC); tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE); tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY); tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY); tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER); #ifdef CONFIG_ARCH_TEGRA_3x_SOC tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL); #endif /* enable interrupts for vblank, frame_end and underflows */ int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); /* for panels with one-shot mode enable tearing effect interrupt */ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) int_enable |= MSF_INT; tegra_dc_writel(dc, int_enable, DC_CMD_INT_ENABLE); tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK); tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); tegra_dc_set_color_control(dc); for (i = 0; i < DC_N_WINDOWS; i++) { struct tegra_dc_win *win = &dc->windows[i]; tegra_dc_writel(dc, WINDOW_A_SELECT << i, DC_CMD_DISPLAY_WINDOW_HEADER); tegra_dc_set_csc(dc, &win->csc); tegra_dc_set_lut(dc, win); tegra_dc_set_scaling_filter(dc); } for (i = 0; i < dc->n_windows; i++) { u32 syncpt = get_syncpt(dc, i); dc->syncpt[i].id = syncpt; dc->syncpt[i].min = dc->syncpt[i].max = nvhost_syncpt_read_ext(dc->ndev, syncpt); } print_mode_info(dc, dc->mode); if (dc->mode.pclk) if (tegra_dc_program_mode(dc, &dc->mode)) return -EINVAL; /* Initialize SD AFTER the modeset. nvsd_init handles the sd_settings = NULL case. */ nvsd_init(dc, dc->out->sd_settings); return 0; }