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);
.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,
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)
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) {
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);