diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2713574824018..29777604df392 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -959,37 +959,370 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, kfree(state); } -static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->syncpt) - return host1x_syncpt_read(dc->syncpt); +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - /* fallback to software emulated VBLANK counter */ - return drm_crtc_vblank_count(&dc->base); -} +static const struct debugfs_reg32 tegra_dc_regs[] = { + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), + DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), + DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), + DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), + DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), + DEBUGFS_REG32(DC_CMD_INT_STATUS), + DEBUGFS_REG32(DC_CMD_INT_MASK), + DEBUGFS_REG32(DC_CMD_INT_ENABLE), + DEBUGFS_REG32(DC_CMD_INT_TYPE), + DEBUGFS_REG32(DC_CMD_INT_POLARITY), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), + DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), + DEBUGFS_REG32(DC_CMD_STATE_ACCESS), + DEBUGFS_REG32(DC_CMD_STATE_CONTROL), + DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), + DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CONTROL), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), + DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), + DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), + DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), + DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), + DEBUGFS_REG32(DC_COM_SPI_CONTROL), + DEBUGFS_REG32(DC_COM_SPI_START_BYTE), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), + DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), + DEBUGFS_REG32(DC_COM_HSPI_CS_DC), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), + DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), + DEBUGFS_REG32(DC_COM_GPIO_CTRL), + DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), + DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), + DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), + DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), + DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), + DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), + DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), + DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), + DEBUGFS_REG32(DC_DISP_BACK_PORCH), + DEBUGFS_REG32(DC_DISP_ACTIVE), + DEBUGFS_REG32(DC_DISP_FRONT_PORCH), + DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), + DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), + DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), + DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), + DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), + DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), + DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), + DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), + DEBUGFS_REG32(DC_DISP_M0_CONTROL), + DEBUGFS_REG32(DC_DISP_M1_CONTROL), + DEBUGFS_REG32(DC_DISP_DI_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_CONTROL), + DEBUGFS_REG32(DC_DISP_PP_SELECT_A), + DEBUGFS_REG32(DC_DISP_PP_SELECT_B), + DEBUGFS_REG32(DC_DISP_PP_SELECT_C), + DEBUGFS_REG32(DC_DISP_PP_SELECT_D), + DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), + DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), + DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), + DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), + DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), + DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), + DEBUGFS_REG32(DC_DISP_BORDER_COLOR), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), + DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), + DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), + DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), + DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), + DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), + DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), + DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), + DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), + DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), + DEBUGFS_REG32(DC_DISP_SD_LUT(0)), + DEBUGFS_REG32(DC_DISP_SD_LUT(1)), + DEBUGFS_REG32(DC_DISP_SD_LUT(2)), + DEBUGFS_REG32(DC_DISP_SD_LUT(3)), + DEBUGFS_REG32(DC_DISP_SD_LUT(4)), + DEBUGFS_REG32(DC_DISP_SD_LUT(5)), + DEBUGFS_REG32(DC_DISP_SD_LUT(6)), + DEBUGFS_REG32(DC_DISP_SD_LUT(7)), + DEBUGFS_REG32(DC_DISP_SD_LUT(8)), + DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), + DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), + DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), + DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), + DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), + DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), + DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), + DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), + DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), + DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), + DEBUGFS_REG32(DC_WIN_BYTE_SWAP), + DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), + DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), + DEBUGFS_REG32(DC_WIN_POSITION), + DEBUGFS_REG32(DC_WIN_SIZE), + DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), + DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), + DEBUGFS_REG32(DC_WIN_DDA_INC), + DEBUGFS_REG32(DC_WIN_LINE_STRIDE), + DEBUGFS_REG32(DC_WIN_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), + DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), + DEBUGFS_REG32(DC_WIN_DV_CONTROL), + DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), + DEBUGFS_REG32(DC_WIN_BLEND_1WIN), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), + DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), + DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), + DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), + DEBUGFS_REG32(DC_WINBUF_START_ADDR), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), + DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), + DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), + DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), + DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), +}; -static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_regs(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + unsigned int i; + int err = 0; - spin_lock_irqsave(&dc->lock, flags); + drm_modeset_lock(&dc->base.mutex, NULL); - value = tegra_dc_readl(dc, DC_CMD_INT_MASK); - value |= VBLANK_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } - spin_unlock_irqrestore(&dc->lock, flags); + for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { + unsigned int offset = tegra_dc_regs[i].offset; - return 0; + seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, + offset, tegra_dc_readl(dc, offset)); + } + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; } -static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +static int tegra_dc_show_crc(struct seq_file *s, void *data) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned long value, flags; + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + int err = 0; + u32 value; + + drm_modeset_lock(&dc->base.mutex, NULL); + + if (!dc->base.state->active) { + err = -EBUSY; + goto unlock; + } + + value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; + tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); + tegra_dc_commit(dc); + + drm_crtc_wait_one_vblank(&dc->base); + drm_crtc_wait_one_vblank(&dc->base); + + value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); + seq_printf(s, "%08x\n", value); + + tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + +unlock: + drm_modeset_unlock(&dc->base.mutex); + return err; +} + +static int tegra_dc_show_stats(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + + seq_printf(s, "frames: %lu\n", dc->stats.frames); + seq_printf(s, "vblank: %lu\n", dc->stats.vblank); + seq_printf(s, "underflow: %lu\n", dc->stats.underflow); + seq_printf(s, "overflow: %lu\n", dc->stats.overflow); + + return 0; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_dc_show_regs, 0, NULL }, + { "crc", tegra_dc_show_crc, 0, NULL }, + { "stats", tegra_dc_show_stats, 0, NULL }, +}; + +static int tegra_dc_late_register(struct drm_crtc *crtc) +{ + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct dentry *root = crtc->debugfs_entry; + struct tegra_dc *dc = to_tegra_dc(crtc); + int err; + + dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!dc->debugfs_files) + return -ENOMEM; + + for (i = 0; i < count; i++) + dc->debugfs_files[i].data = dc; + + err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); + if (err < 0) + goto free; + + return 0; + +free: + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; + + return err; +} + +static void tegra_dc_early_unregister(struct drm_crtc *crtc) +{ + unsigned int count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = crtc->dev->primary; + struct tegra_dc *dc = to_tegra_dc(crtc); + + drm_debugfs_remove_files(dc->debugfs_files, count, minor); + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; +} + +static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (dc->syncpt) + return host1x_syncpt_read(dc->syncpt); + + /* fallback to software emulated VBLANK counter */ + return drm_crtc_vblank_count(&dc->base); +} + +static int tegra_dc_enable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; + + spin_lock_irqsave(&dc->lock, flags); + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value |= VBLANK_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + spin_unlock_irqrestore(&dc->lock, flags); + + return 0; +} + +static void tegra_dc_disable_vblank(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long value, flags; spin_lock_irqsave(&dc->lock, flags); @@ -1007,6 +1340,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .reset = tegra_crtc_reset, .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, .atomic_destroy_state = tegra_crtc_atomic_destroy_state, + .late_register = tegra_dc_late_register, + .early_unregister = tegra_dc_early_unregister, .get_vblank_counter = tegra_dc_get_vblank_counter, .enable_vblank = tegra_dc_enable_vblank, .disable_vblank = tegra_dc_disable_vblank, @@ -1148,590 +1483,239 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - if (!tegra_dc_idle(dc)) { - tegra_dc_stop(dc); - - /* - * Ignore the return value, there isn't anything useful to do - * in case this fails. - */ - tegra_dc_wait_idle(dc, 100); - } - - /* - * This should really be part of the RGB encoder driver, but clearing - * these bits has the side-effect of stopping the display controller. - * When that happens no VBLANK interrupts will be raised. At the same - * time the encoder is disabled before the display controller, so the - * above code is always going to timeout waiting for the controller - * to go idle. - * - * Given the close coupling between the RGB encoder and the display - * controller doing it here is still kind of okay. None of the other - * encoder drivers require these bits to be cleared. - * - * XXX: Perhaps given that the display controller is switched off at - * this point anyway maybe clearing these bits isn't even useful for - * the RGB encoder? - */ - if (dc->rgb) { - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - } - - tegra_dc_stats_reset(&dc->stats); - drm_crtc_vblank_off(crtc); - - pm_runtime_put_sync(dc->dev); -} - -static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - struct drm_display_mode *mode = &crtc->state->adjusted_mode; - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value; - - pm_runtime_get_sync(dc->dev); - - /* initialize display controller */ - if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); - - value = SYNCPT_CNTRL_NO_STALL; - tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - - value = SYNCPT_VSYNC_ENABLE | syncpt; - tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); - } - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - if (dc->soc->supports_border_color) - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); - - /* apply PLL and pixel clock changes */ - tegra_dc_commit_state(dc, state); - - /* program display mode */ - tegra_dc_set_timings(dc, mode); - - /* interlacing isn't supported yet, so disable it */ - if (dc->soc->supports_interlacing) { - value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); - value &= ~INTERLACE_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); - } - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - tegra_dc_commit(dc); - - drm_crtc_vblank_on(crtc); -} - -static int tegra_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - return 0; -} - -static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - dc->event = crtc->state->event; - crtc->state->event = NULL; - } -} - -static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct tegra_dc_state *state = to_dc_state(crtc->state); - struct tegra_dc *dc = to_tegra_dc(crtc); - - tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); -} - -static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { - .atomic_check = tegra_crtc_atomic_check, - .atomic_begin = tegra_crtc_atomic_begin, - .atomic_flush = tegra_crtc_atomic_flush, - .atomic_enable = tegra_crtc_atomic_enable, - .atomic_disable = tegra_crtc_atomic_disable, -}; - -static void tegra_dc_finish_page_flip(struct tegra_dc *dc) -{ - struct drm_device *drm = dc->base.dev; - struct drm_crtc *crtc = &dc->base; - unsigned long flags, base; - struct tegra_bo *bo; - - spin_lock_irqsave(&drm->event_lock, flags); - - if (!dc->event) { - spin_unlock_irqrestore(&drm->event_lock, flags); - return; - } - - bo = tegra_fb_get_plane(crtc->primary->fb, 0); - - spin_lock(&dc->lock); - - /* check if new start address has been latched */ - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); - base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - - spin_unlock(&dc->lock); - - if (base == bo->paddr + crtc->primary->fb->offsets[0]) { - drm_crtc_send_vblank_event(crtc, dc->event); - drm_crtc_vblank_put(crtc); - dc->event = NULL; - } - - spin_unlock_irqrestore(&drm->event_lock, flags); -} - -static irqreturn_t tegra_dc_irq(int irq, void *data) -{ - struct tegra_dc *dc = data; - unsigned long status; - - status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); - tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); - - if (status & FRAME_END_INT) { - /* - dev_dbg(dc->dev, "%s(): frame end\n", __func__); - */ - dc->stats.frames++; - } - - if (status & VBLANK_INT) { - /* - dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); - */ - drm_crtc_handle_vblank(&dc->base); - tegra_dc_finish_page_flip(dc); - dc->stats.vblank++; - } - - if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { - /* - dev_dbg(dc->dev, "%s(): underflow\n", __func__); - */ - dc->stats.underflow++; - } - - if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { - /* - dev_dbg(dc->dev, "%s(): overflow\n", __func__); - */ - dc->stats.overflow++; - } - - return IRQ_HANDLED; -} - -#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } - -static const struct debugfs_reg32 tegra_dc_regs[] = { - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), - DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), - DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), - DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), - DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), - DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), - DEBUGFS_REG32(DC_CMD_INT_STATUS), - DEBUGFS_REG32(DC_CMD_INT_MASK), - DEBUGFS_REG32(DC_CMD_INT_ENABLE), - DEBUGFS_REG32(DC_CMD_INT_TYPE), - DEBUGFS_REG32(DC_CMD_INT_POLARITY), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), - DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), - DEBUGFS_REG32(DC_CMD_STATE_ACCESS), - DEBUGFS_REG32(DC_CMD_STATE_CONTROL), - DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), - DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), - DEBUGFS_REG32(DC_COM_CRC_CONTROL), - DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), - DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), - DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), - DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), - DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), - DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), - DEBUGFS_REG32(DC_COM_SPI_CONTROL), - DEBUGFS_REG32(DC_COM_SPI_START_BYTE), - DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), - DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), - DEBUGFS_REG32(DC_COM_HSPI_CS_DC), - DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), - DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), - DEBUGFS_REG32(DC_COM_GPIO_CTRL), - DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), - DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), - DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), - DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), - DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), - DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), - DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), - DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), - DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), - DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), - DEBUGFS_REG32(DC_DISP_BACK_PORCH), - DEBUGFS_REG32(DC_DISP_ACTIVE), - DEBUGFS_REG32(DC_DISP_FRONT_PORCH), - DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), - DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), - DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), - DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), - DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), - DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), - DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), - DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), - DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), - DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), - DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), - DEBUGFS_REG32(DC_DISP_M0_CONTROL), - DEBUGFS_REG32(DC_DISP_M1_CONTROL), - DEBUGFS_REG32(DC_DISP_DI_CONTROL), - DEBUGFS_REG32(DC_DISP_PP_CONTROL), - DEBUGFS_REG32(DC_DISP_PP_SELECT_A), - DEBUGFS_REG32(DC_DISP_PP_SELECT_B), - DEBUGFS_REG32(DC_DISP_PP_SELECT_C), - DEBUGFS_REG32(DC_DISP_PP_SELECT_D), - DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), - DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), - DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), - DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), - DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), - DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), - DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), - DEBUGFS_REG32(DC_DISP_BORDER_COLOR), - DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), - DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), - DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), - DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), - DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), - DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), - DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), - DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), - DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), - DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), - DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), - DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), - DEBUGFS_REG32(DC_DISP_SD_LUT(0)), - DEBUGFS_REG32(DC_DISP_SD_LUT(1)), - DEBUGFS_REG32(DC_DISP_SD_LUT(2)), - DEBUGFS_REG32(DC_DISP_SD_LUT(3)), - DEBUGFS_REG32(DC_DISP_SD_LUT(4)), - DEBUGFS_REG32(DC_DISP_SD_LUT(5)), - DEBUGFS_REG32(DC_DISP_SD_LUT(6)), - DEBUGFS_REG32(DC_DISP_SD_LUT(7)), - DEBUGFS_REG32(DC_DISP_SD_LUT(8)), - DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), - DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), - DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), - DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), - DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), - DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), - DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), - DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), - DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), - DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), - DEBUGFS_REG32(DC_WIN_BYTE_SWAP), - DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), - DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), - DEBUGFS_REG32(DC_WIN_POSITION), - DEBUGFS_REG32(DC_WIN_SIZE), - DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), - DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), - DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), - DEBUGFS_REG32(DC_WIN_DDA_INC), - DEBUGFS_REG32(DC_WIN_LINE_STRIDE), - DEBUGFS_REG32(DC_WIN_BUF_STRIDE), - DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), - DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), - DEBUGFS_REG32(DC_WIN_DV_CONTROL), - DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), - DEBUGFS_REG32(DC_WIN_BLEND_1WIN), - DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), - DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), - DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), - DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), - DEBUGFS_REG32(DC_WINBUF_START_ADDR), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), - DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), - DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), - DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), - DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), - DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), - DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), - DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), -}; - -static int tegra_dc_show_regs(struct seq_file *s, void *data) -{ - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - unsigned int i; - int err = 0; +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + if (!tegra_dc_idle(dc)) { + tegra_dc_stop(dc); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* + * Ignore the return value, there isn't anything useful to do + * in case this fails. + */ + tegra_dc_wait_idle(dc, 100); } - for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { - unsigned int offset = tegra_dc_regs[i].offset; - - seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, - offset, tegra_dc_readl(dc, offset)); + /* + * This should really be part of the RGB encoder driver, but clearing + * these bits has the side-effect of stopping the display controller. + * When that happens no VBLANK interrupts will be raised. At the same + * time the encoder is disabled before the display controller, so the + * above code is always going to timeout waiting for the controller + * to go idle. + * + * Given the close coupling between the RGB encoder and the display + * controller doing it here is still kind of okay. None of the other + * encoder drivers require these bits to be cleared. + * + * XXX: Perhaps given that the display controller is switched off at + * this point anyway maybe clearing these bits isn't even useful for + * the RGB encoder? + */ + if (dc->rgb) { + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); } -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + tegra_dc_stats_reset(&dc->stats); + drm_crtc_vblank_off(crtc); + + pm_runtime_put_sync(dc->dev); } -static int tegra_dc_show_crc(struct seq_file *s, void *data) +static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; - int err = 0; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; - drm_modeset_lock(&dc->base.mutex, NULL); + pm_runtime_get_sync(dc->dev); - if (!dc->base.state->active) { - err = -EBUSY; - goto unlock; + /* initialize display controller */ + if (dc->syncpt) { + u32 syncpt = host1x_syncpt_id(dc->syncpt); + + value = SYNCPT_CNTRL_NO_STALL; + tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + + value = SYNCPT_VSYNC_ENABLE | syncpt; + tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); } - value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; - tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); - tegra_dc_commit(dc); + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - drm_crtc_wait_one_vblank(&dc->base); - drm_crtc_wait_one_vblank(&dc->base); + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); - seq_printf(s, "%08x\n", value); + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); -unlock: - drm_modeset_unlock(&dc->base.mutex); - return err; + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + if (dc->soc->supports_border_color) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + + /* apply PLL and pixel clock changes */ + tegra_dc_commit_state(dc, state); + + /* program display mode */ + tegra_dc_set_timings(dc, mode); + + /* interlacing isn't supported yet, so disable it */ + if (dc->soc->supports_interlacing) { + value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); + value &= ~INTERLACE_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); + } + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + value |= DISP_CTRL_MODE_C_DISPLAY; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + + tegra_dc_commit(dc); + + drm_crtc_vblank_on(crtc); } -static int tegra_dc_show_stats(struct seq_file *s, void *data) +static int tegra_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct drm_info_node *node = s->private; - struct tegra_dc *dc = node->info_ent->data; + return 0; +} - seq_printf(s, "frames: %lu\n", dc->stats.frames); - seq_printf(s, "vblank: %lu\n", dc->stats.vblank); - seq_printf(s, "underflow: %lu\n", dc->stats.underflow); - seq_printf(s, "overflow: %lu\n", dc->stats.overflow); +static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); - return 0; + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + dc->event = crtc->state->event; + crtc->state->event = NULL; + } } -static struct drm_info_list debugfs_files[] = { - { "regs", tegra_dc_show_regs, 0, NULL }, - { "crc", tegra_dc_show_crc, 0, NULL }, - { "stats", tegra_dc_show_stats, 0, NULL }, +static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); + + tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); +} + +static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { + .atomic_check = tegra_crtc_atomic_check, + .atomic_begin = tegra_crtc_atomic_begin, + .atomic_flush = tegra_crtc_atomic_flush, + .atomic_enable = tegra_crtc_atomic_enable, + .atomic_disable = tegra_crtc_atomic_disable, }; -static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor) +static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { - unsigned int i; - char *name; - int err; - - name = kasprintf(GFP_KERNEL, "dc.%d", dc->pipe); - dc->debugfs = debugfs_create_dir(name, minor->debugfs_root); - kfree(name); + struct drm_device *drm = dc->base.dev; + struct drm_crtc *crtc = &dc->base; + unsigned long flags, base; + struct tegra_bo *bo; - if (!dc->debugfs) - return -ENOMEM; + spin_lock_irqsave(&drm->event_lock, flags); - dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), - GFP_KERNEL); - if (!dc->debugfs_files) { - err = -ENOMEM; - goto remove; + if (!dc->event) { + spin_unlock_irqrestore(&drm->event_lock, flags); + return; } - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) - dc->debugfs_files[i].data = dc; + bo = tegra_fb_get_plane(crtc->primary->fb, 0); - err = drm_debugfs_create_files(dc->debugfs_files, - ARRAY_SIZE(debugfs_files), - dc->debugfs, minor); - if (err < 0) - goto free; + spin_lock(&dc->lock); - dc->minor = minor; + /* check if new start address has been latched */ + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - return 0; + spin_unlock(&dc->lock); -free: - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; -remove: - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + if (base == bo->paddr + crtc->primary->fb->offsets[0]) { + drm_crtc_send_vblank_event(crtc, dc->event); + drm_crtc_vblank_put(crtc); + dc->event = NULL; + } - return err; + spin_unlock_irqrestore(&drm->event_lock, flags); } -static int tegra_dc_debugfs_exit(struct tegra_dc *dc) +static irqreturn_t tegra_dc_irq(int irq, void *data) { - drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files), - dc->minor); - dc->minor = NULL; + struct tegra_dc *dc = data; + unsigned long status; - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; + status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); + tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); + + if (status & FRAME_END_INT) { + /* + dev_dbg(dc->dev, "%s(): frame end\n", __func__); + */ + dc->stats.frames++; + } - debugfs_remove(dc->debugfs); - dc->debugfs = NULL; + if (status & VBLANK_INT) { + /* + dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); + */ + drm_crtc_handle_vblank(&dc->base); + tegra_dc_finish_page_flip(dc); + dc->stats.vblank++; + } - return 0; + if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { + /* + dev_dbg(dc->dev, "%s(): underflow\n", __func__); + */ + dc->stats.underflow++; + } + + if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { + /* + dev_dbg(dc->dev, "%s(): overflow\n", __func__); + */ + dc->stats.overflow++; + } + + return IRQ_HANDLED; } static int tegra_dc_init(struct host1x_client *client) @@ -1797,12 +1781,6 @@ static int tegra_dc_init(struct host1x_client *client) if (err < 0) goto cleanup; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, drm->primary); - if (err < 0) - dev_err(dc->dev, "debugfs setup failed: %d\n", err); - } - err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, dev_name(dc->dev), dc); if (err < 0) { @@ -1835,12 +1813,6 @@ static int tegra_dc_exit(struct host1x_client *client) devm_free_irq(dc->dev, dc->irq, dc); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_exit(dc); - if (err < 0) - dev_err(dc->dev, "debugfs cleanup failed: %d\n", err); - } - err = tegra_dc_rgb_exit(dc); if (err) { dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index cb100b6e32821..032c734dd4703 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -56,8 +56,6 @@ struct tegra_dc { struct list_head list; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; /* page-flip handling */ struct drm_pending_vblank_event *event;