From d29827484b9cf6026506a0e376fb5d246580ffd8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 22 Jan 2015 08:48:25 +0100 Subject: [PATCH 01/15] drm/tegra: dc: Check for valid parent clock Check that the desired parent clock is indeed a valid parent for the display controller clock. This is purely cosmetic at this point since the parent clocks are specified in DT and all the currently defined parents are in fact valid parents of the display controller clock. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 1a52522f5da7..8746a9ce6a8f 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1177,6 +1177,9 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc, { struct tegra_dc_state *state = to_dc_state(crtc_state); + if (!clk_has_parent(dc->clk, clk)) + return -EINVAL; + state->clk = clk; state->pclk = pclk; state->div = div; From 28c23373b88bcc244b573ea45596a51e9db73d2c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 23 Jan 2015 09:16:03 +0100 Subject: [PATCH 02/15] drm/tegra: Provide debugfs file for the IOVA space The Tegra DRM driver uses a single IO virtual address space for buffer mappings. Provide a table of the address space usage in debugfs. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 7dd328d77996..7ba7e2860ac8 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -879,8 +879,18 @@ static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) return 0; } +static int tegra_debugfs_iova(struct seq_file *s, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct drm_device *drm = node->minor->dev; + struct tegra_drm *tegra = drm->dev_private; + + return drm_mm_dump_table(s, &tegra->mm); +} + static struct drm_info_list tegra_debugfs_list[] = { { "framebuffers", tegra_debugfs_framebuffers, 0 }, + { "iova", tegra_debugfs_iova, 0 }, }; static int tegra_debugfs_init(struct drm_minor *minor) From 28fe2076004da062e9affac1cec98c697de53eb1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Jan 2015 16:02:48 +0100 Subject: [PATCH 03/15] drm/tegra: sor: Registers are 32-bit Use a sized unsigned 32-bit data type (u32) to store register contents. The SOR registers are 32 bits wide irrespective of the architecture's data width. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 2afe478ded3b..59b8aec1aeae 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -68,13 +68,12 @@ static inline struct tegra_sor *to_sor(struct tegra_output *output) return container_of(output, struct tegra_sor, output); } -static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, - unsigned long offset) +static inline u32 tegra_sor_readl(struct tegra_sor *sor, unsigned long offset) { return readl(sor->regs + (offset << 2)); } -static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, +static inline void tegra_sor_writel(struct tegra_sor *sor, u32 value, unsigned long offset) { writel(value, sor->regs + (offset << 2)); @@ -83,9 +82,9 @@ static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, static int tegra_sor_dp_train_fast(struct tegra_sor *sor, struct drm_dp_link *link) { - unsigned long value; unsigned int i; u8 pattern; + u32 value; int err; /* setup lane parameters */ @@ -202,7 +201,7 @@ static void tegra_sor_update(struct tegra_sor *sor) static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) { - unsigned long value; + u32 value; value = tegra_sor_readl(sor, SOR_PWM_DIV); value &= ~SOR_PWM_DIV_MASK; @@ -281,7 +280,7 @@ static int tegra_sor_wakeup(struct tegra_sor *sor) static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) { - unsigned long value; + u32 value; value = tegra_sor_readl(sor, SOR_PWR); value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; @@ -791,8 +790,8 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder, struct tegra_sor_config config; struct drm_dp_link link; struct drm_dp_aux *aux; - unsigned long value; int err = 0; + u32 value; mutex_lock(&sor->lock); From dab163366a2a0f134cfa13a64ab750945648a18a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Jan 2015 16:04:08 +0100 Subject: [PATCH 04/15] drm/tegra: sor: Dump registers via debugfs Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 175 ++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 59b8aec1aeae..41c5a6ed2c36 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -41,6 +41,8 @@ struct tegra_sor { struct mutex lock; bool enabled; + struct drm_info_list *debugfs_files; + struct drm_minor *minor; struct dentry *debugfs; }; @@ -673,38 +675,195 @@ static const struct file_operations tegra_sor_crc_fops = { .release = tegra_sor_crc_release, }; +static int tegra_sor_show_regs(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_sor *sor = node->info_ent->data; + +#define DUMP_REG(name) \ + seq_printf(s, "%-38s %#05x %08x\n", #name, name, \ + tegra_sor_readl(sor, name)) + + DUMP_REG(SOR_CTXSW); + DUMP_REG(SOR_SUPER_STATE_0); + DUMP_REG(SOR_SUPER_STATE_1); + DUMP_REG(SOR_STATE_0); + DUMP_REG(SOR_STATE_1); + DUMP_REG(SOR_HEAD_STATE_0(0)); + DUMP_REG(SOR_HEAD_STATE_0(1)); + DUMP_REG(SOR_HEAD_STATE_1(0)); + DUMP_REG(SOR_HEAD_STATE_1(1)); + DUMP_REG(SOR_HEAD_STATE_2(0)); + DUMP_REG(SOR_HEAD_STATE_2(1)); + DUMP_REG(SOR_HEAD_STATE_3(0)); + DUMP_REG(SOR_HEAD_STATE_3(1)); + DUMP_REG(SOR_HEAD_STATE_4(0)); + DUMP_REG(SOR_HEAD_STATE_4(1)); + DUMP_REG(SOR_HEAD_STATE_5(0)); + DUMP_REG(SOR_HEAD_STATE_5(1)); + DUMP_REG(SOR_CRC_CNTRL); + DUMP_REG(SOR_DP_DEBUG_MVID); + DUMP_REG(SOR_CLK_CNTRL); + DUMP_REG(SOR_CAP); + DUMP_REG(SOR_PWR); + DUMP_REG(SOR_TEST); + DUMP_REG(SOR_PLL_0); + DUMP_REG(SOR_PLL_1); + DUMP_REG(SOR_PLL_2); + DUMP_REG(SOR_PLL_3); + DUMP_REG(SOR_CSTM); + DUMP_REG(SOR_LVDS); + DUMP_REG(SOR_CRC_A); + DUMP_REG(SOR_CRC_B); + DUMP_REG(SOR_BLANK); + DUMP_REG(SOR_SEQ_CTL); + DUMP_REG(SOR_LANE_SEQ_CTL); + DUMP_REG(SOR_SEQ_INST(0)); + DUMP_REG(SOR_SEQ_INST(1)); + DUMP_REG(SOR_SEQ_INST(2)); + DUMP_REG(SOR_SEQ_INST(3)); + DUMP_REG(SOR_SEQ_INST(4)); + DUMP_REG(SOR_SEQ_INST(5)); + DUMP_REG(SOR_SEQ_INST(6)); + DUMP_REG(SOR_SEQ_INST(7)); + DUMP_REG(SOR_SEQ_INST(8)); + DUMP_REG(SOR_SEQ_INST(9)); + DUMP_REG(SOR_SEQ_INST(10)); + DUMP_REG(SOR_SEQ_INST(11)); + DUMP_REG(SOR_SEQ_INST(12)); + DUMP_REG(SOR_SEQ_INST(13)); + DUMP_REG(SOR_SEQ_INST(14)); + DUMP_REG(SOR_SEQ_INST(15)); + DUMP_REG(SOR_PWM_DIV); + DUMP_REG(SOR_PWM_CTL); + DUMP_REG(SOR_VCRC_A_0); + DUMP_REG(SOR_VCRC_A_1); + DUMP_REG(SOR_VCRC_B_0); + DUMP_REG(SOR_VCRC_B_1); + DUMP_REG(SOR_CCRC_A_0); + DUMP_REG(SOR_CCRC_A_1); + DUMP_REG(SOR_CCRC_B_0); + DUMP_REG(SOR_CCRC_B_1); + DUMP_REG(SOR_EDATA_A_0); + DUMP_REG(SOR_EDATA_A_1); + DUMP_REG(SOR_EDATA_B_0); + DUMP_REG(SOR_EDATA_B_1); + DUMP_REG(SOR_COUNT_A_0); + DUMP_REG(SOR_COUNT_A_1); + DUMP_REG(SOR_COUNT_B_0); + DUMP_REG(SOR_COUNT_B_1); + DUMP_REG(SOR_DEBUG_A_0); + DUMP_REG(SOR_DEBUG_A_1); + DUMP_REG(SOR_DEBUG_B_0); + DUMP_REG(SOR_DEBUG_B_1); + DUMP_REG(SOR_TRIG); + DUMP_REG(SOR_MSCHECK); + DUMP_REG(SOR_XBAR_CTRL); + DUMP_REG(SOR_XBAR_POL); + DUMP_REG(SOR_DP_LINKCTL_0); + DUMP_REG(SOR_DP_LINKCTL_1); + DUMP_REG(SOR_LANE_DRIVE_CURRENT_0); + DUMP_REG(SOR_LANE_DRIVE_CURRENT_1); + DUMP_REG(SOR_LANE4_DRIVE_CURRENT_0); + DUMP_REG(SOR_LANE4_DRIVE_CURRENT_1); + DUMP_REG(SOR_LANE_PREEMPHASIS_0); + DUMP_REG(SOR_LANE_PREEMPHASIS_1); + DUMP_REG(SOR_LANE4_PREEMPHASIS_0); + DUMP_REG(SOR_LANE4_PREEMPHASIS_1); + DUMP_REG(SOR_LANE_POST_CURSOR_0); + DUMP_REG(SOR_LANE_POST_CURSOR_1); + DUMP_REG(SOR_DP_CONFIG_0); + DUMP_REG(SOR_DP_CONFIG_1); + DUMP_REG(SOR_DP_MN_0); + DUMP_REG(SOR_DP_MN_1); + DUMP_REG(SOR_DP_PADCTL_0); + DUMP_REG(SOR_DP_PADCTL_1); + DUMP_REG(SOR_DP_DEBUG_0); + DUMP_REG(SOR_DP_DEBUG_1); + DUMP_REG(SOR_DP_SPARE_0); + DUMP_REG(SOR_DP_SPARE_1); + DUMP_REG(SOR_DP_AUDIO_CTRL); + DUMP_REG(SOR_DP_AUDIO_HBLANK_SYMBOLS); + DUMP_REG(SOR_DP_AUDIO_VBLANK_SYMBOLS); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_HEADER); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_0); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_1); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_2); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_3); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_4); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_5); + DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_6); + DUMP_REG(SOR_DP_TPG); + DUMP_REG(SOR_DP_TPG_CONFIG); + DUMP_REG(SOR_DP_LQ_CSTM_0); + DUMP_REG(SOR_DP_LQ_CSTM_1); + DUMP_REG(SOR_DP_LQ_CSTM_2); + +#undef DUMP_REG + + return 0; +} + +static const struct drm_info_list debugfs_files[] = { + { "regs", tegra_sor_show_regs, 0, NULL }, +}; + static int tegra_sor_debugfs_init(struct tegra_sor *sor, struct drm_minor *minor) { struct dentry *entry; + unsigned int i; int err = 0; sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); if (!sor->debugfs) return -ENOMEM; - entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, - &tegra_sor_crc_fops); - if (!entry) { - dev_err(sor->dev, - "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", - minor->debugfs_root->d_name.name); + sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!sor->debugfs_files) { err = -ENOMEM; goto remove; } + for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + sor->debugfs_files[i].data = sor; + + err = drm_debugfs_create_files(sor->debugfs_files, + ARRAY_SIZE(debugfs_files), + sor->debugfs, minor); + if (err < 0) + goto free; + + entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, + &tegra_sor_crc_fops); + if (!entry) { + err = -ENOMEM; + goto free; + } + return err; +free: + kfree(sor->debugfs_files); + sor->debugfs_files = NULL; remove: - debugfs_remove(sor->debugfs); + debugfs_remove_recursive(sor->debugfs); sor->debugfs = NULL; return err; } static void tegra_sor_debugfs_exit(struct tegra_sor *sor) { - debugfs_remove_recursive(sor->debugfs); + drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), + sor->minor); + sor->minor = NULL; + + kfree(sor->debugfs_files); sor->debugfs = NULL; + + debugfs_remove_recursive(sor->debugfs); + sor->debugfs_files = NULL; } static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode) From b4a20144e0c0a45431695fa5968ce2ed8c9ce6ca Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 14:29:02 +0100 Subject: [PATCH 05/15] gpu: host1x: Export host1x_syncpt_read() This function is used to read the current value of the syncpt and is useful in situations where drivers don't schedule work and wait for the syncpoint to increment. One particular use-case is using the syncpoint as a VBLANK counter. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/syncpt.c | 6 ++++++ include/linux/host1x.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index b10550ee1d89..6b7fdc1e2ed0 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -425,6 +425,12 @@ u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) } EXPORT_SYMBOL(host1x_syncpt_read_min); +u32 host1x_syncpt_read(struct host1x_syncpt *sp) +{ + return host1x_syncpt_load(sp); +} +EXPORT_SYMBOL(host1x_syncpt_read); + int host1x_syncpt_nb_pts(struct host1x *host) { return host->info->nb_pts; diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 464f33814a94..d2ba7d334039 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -135,6 +135,7 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); u32 host1x_syncpt_id(struct host1x_syncpt *sp); u32 host1x_syncpt_read_min(struct host1x_syncpt *sp); u32 host1x_syncpt_read_max(struct host1x_syncpt *sp); +u32 host1x_syncpt_read(struct host1x_syncpt *sp); int host1x_syncpt_incr(struct host1x_syncpt *sp); u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, From 42e9ce0523699b3f3383b7c5c0bcc2657a77d0ca Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 14:43:05 +0100 Subject: [PATCH 06/15] drm/tegra: dc: Implement hardware VBLANK counter The display controller on Tegra can use syncpoints to count VBLANK events. syncpoints are 32-bit unsigned integers, so well suited as VBLANK counters. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 31 ++++++++++++++++++++++++------- drivers/gpu/drm/tegra/dc.h | 7 +++---- drivers/gpu/drm/tegra/drm.c | 8 ++++++-- drivers/gpu/drm/tegra/drm.h | 2 ++ 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 8746a9ce6a8f..9e329465ab11 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -906,6 +906,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) return 0; } +u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc) +{ + if (dc->syncpt) + return host1x_syncpt_read(dc->syncpt); + + /* fallback to software emulated VBLANK counter */ + return drm_crtc_vblank_count(&dc->base); +} + void tegra_dc_enable_vblank(struct tegra_dc *dc) { unsigned long value, flags; @@ -1632,7 +1641,6 @@ static int tegra_dc_init(struct host1x_client *client) struct tegra_drm *tegra = drm->dev_private; struct drm_plane *primary = NULL; struct drm_plane *cursor = NULL; - unsigned int syncpt; u32 value; int err; @@ -1701,13 +1709,15 @@ static int tegra_dc_init(struct host1x_client *client) } /* initialize display controller */ - if (dc->pipe) - syncpt = SYNCPT_VBLANK1; - else - syncpt = SYNCPT_VBLANK0; + if (dc->syncpt) { + u32 syncpt = host1x_syncpt_id(dc->syncpt); - tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC); + 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; tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); @@ -1875,6 +1885,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) static int tegra_dc_probe(struct platform_device *pdev) { + unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; const struct of_device_id *id; struct resource *regs; struct tegra_dc *dc; @@ -1966,6 +1977,10 @@ static int tegra_dc_probe(struct platform_device *pdev) return err; } + dc->syncpt = host1x_syncpt_request(&pdev->dev, flags); + if (!dc->syncpt) + dev_warn(&pdev->dev, "failed to allocate syncpoint\n"); + platform_set_drvdata(pdev, dc); return 0; @@ -1976,6 +1991,8 @@ static int tegra_dc_remove(struct platform_device *pdev) struct tegra_dc *dc = platform_get_drvdata(pdev); int err; + host1x_syncpt_free(dc->syncpt); + err = host1x_client_unregister(&dc->client); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 705c93b00794..55792daabbb5 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -12,6 +12,8 @@ #define DC_CMD_GENERAL_INCR_SYNCPT 0x000 #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 +#define SYNCPT_CNTRL_NO_STALL (1 << 8) +#define SYNCPT_CNTRL_SOFT_RESET (1 << 0) #define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002 #define DC_CMD_WIN_A_INCR_SYNCPT 0x008 #define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009 @@ -23,6 +25,7 @@ #define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019 #define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a #define DC_CMD_CONT_SYNCPT_VSYNC 0x028 +#define SYNCPT_VSYNC_ENABLE (1 << 8) #define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031 #define DC_CMD_DISPLAY_COMMAND 0x032 #define DISP_CTRL_MODE_STOP (0 << 5) @@ -438,8 +441,4 @@ #define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca -/* synchronization points */ -#define SYNCPT_VBLANK0 26 -#define SYNCPT_VBLANK1 27 - #endif /* TEGRA_DC_H */ diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 7ba7e2860ac8..8de17f9e5fe1 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) */ drm->irq_enabled = true; + /* syncpoints are used for full 32-bit hardware VBLANK counters */ + drm->vblank_disable_immediate = true; + drm->max_vblank_count = 0xffffffff; + err = drm_vblank_init(drm, drm->mode_config.num_crtc); if (err < 0) goto device; @@ -813,12 +817,12 @@ static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe) { struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct tegra_dc *dc = to_tegra_dc(crtc); if (!crtc) return 0; - /* TODO: implement real hardware counter using syncpoints */ - return drm_crtc_vblank_count(crtc); + return tegra_dc_get_vblank_counter(dc); } static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 8cb2dfeaa957..ded04e3473e9 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -106,6 +106,7 @@ struct tegra_output; struct tegra_dc { struct host1x_client client; + struct host1x_syncpt *syncpt; struct device *dev; spinlock_t lock; @@ -180,6 +181,7 @@ struct tegra_dc_window { }; /* from dc.c */ +u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc); void tegra_dc_enable_vblank(struct tegra_dc *dc); void tegra_dc_disable_vblank(struct tegra_dc *dc); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); From f5e7840b0c4368f8cdbb055188c2a0eef50c3052 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 14:54:32 +0100 Subject: [PATCH 07/15] drm/atomic: Add helpers for state-subclassing drivers Drivers that subclass CRTC, plane or connector state need to carefully duplicate the code that the atomic helpers have. This is bound to cause breakage eventually because it requires auditing all drivers and update them when code is added to the helpers. In order to avoid that, implement new helpers that perform the required steps when copying and destroying state. These new helpers are exported so that state-subclassing drivers can use them. The default helpers are implemented using them as well, providing a single location that needs to be changed when adding to base atomic states. Reviewed-by: Daniel Vetter Reviewed-by: Eric Anholt Signed-off-by: Thierry Reding --- drivers/gpu/drm/drm_atomic_helper.c | 144 ++++++++++++++++++++++++---- include/drm/drm_atomic_helper.h | 14 +++ 2 files changed, 142 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7e3a52b97c7d..60d28a18f0a3 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2049,6 +2049,26 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); +/** + * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state + * @crtc: CRTC object + * @state: atomic CRTC state + * + * Copies atomic state from a CRTC's current state and resets inferred values. + * This is useful for drivers that subclass the CRTC state. + */ +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + memcpy(state, crtc->state, sizeof(*state)); + + state->mode_changed = false; + state->active_changed = false; + state->planes_changed = false; + state->event = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); + /** * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook * @crtc: drm CRTC @@ -2064,19 +2084,34 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) if (WARN_ON(!crtc->state)) return NULL; - state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL); - - if (state) { - state->mode_changed = false; - state->active_changed = false; - state->planes_changed = false; - state->event = NULL; - } + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_duplicate_state(crtc, state); return state; } EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); +/** + * __drm_atomic_helper_crtc_destroy_state - release CRTC state + * @crtc: CRTC object + * @state: CRTC state object to release + * + * Releases all resources stored in the CRTC state without actually freeing + * the memory of the CRTC state. This is useful for drivers that subclass the + * CRTC state. + */ +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + /* + * This is currently a placeholder so that drivers that subclass the + * state will automatically do the right thing if code is ever added + * to this function. + */ +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); + /** * drm_atomic_helper_crtc_destroy_state - default state destroy hook * @crtc: drm CRTC @@ -2088,6 +2123,7 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { + __drm_atomic_helper_crtc_destroy_state(crtc, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); @@ -2112,6 +2148,24 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); +/** + * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state + * @plane: plane object + * @state: atomic plane state + * + * Copies atomic state from a plane's current state. This is useful for + * drivers that subclass the plane state. + */ +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + memcpy(state, plane->state, sizeof(*state)); + + if (state->fb) + drm_framebuffer_reference(state->fb); +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); + /** * drm_atomic_helper_plane_duplicate_state - default state duplicate hook * @plane: drm plane @@ -2127,15 +2181,31 @@ drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) if (WARN_ON(!plane->state)) return NULL; - state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL); - - if (state && state->fb) - drm_framebuffer_reference(state->fb); + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, state); return state; } EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); +/** + * __drm_atomic_helper_plane_destroy_state - release plane state + * @plane: plane object + * @state: plane state object to release + * + * Releases all resources stored in the plane state without actually freeing + * the memory of the plane state. This is useful for drivers that subclass the + * plane state. + */ +void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_unreference(state->fb); +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); + /** * drm_atomic_helper_plane_destroy_state - default state destroy hook * @plane: drm plane @@ -2147,9 +2217,7 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) { - if (state->fb) - drm_framebuffer_unreference(state->fb); - + __drm_atomic_helper_plane_destroy_state(plane, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); @@ -2172,6 +2240,22 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); +/** + * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state + * @connector: connector object + * @state: atomic connector state + * + * Copies atomic state from a connector's current state. This is useful for + * drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + memcpy(state, connector->state, sizeof(*state)); +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); + /** * drm_atomic_helper_connector_duplicate_state - default state duplicate hook * @connector: drm connector @@ -2182,13 +2266,40 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_reset); struct drm_connector_state * drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) { + struct drm_connector_state *state; + if (WARN_ON(!connector->state)) return NULL; - return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL); + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; } EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); +/** + * __drm_atomic_helper_connector_destroy_state - release connector state + * @connector: connector object + * @state: connector state object to release + * + * Releases all resources stored in the connector state without actually + * freeing the memory of the connector state. This is useful for drivers that + * subclass the connector state. + */ +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + /* + * This is currently a placeholder so that drivers that subclass the + * state will automatically do the right thing if code is ever added + * to this function. + */ +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); + /** * drm_atomic_helper_connector_destroy_state - default state destroy hook * @connector: drm connector @@ -2200,6 +2311,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state) { + __drm_atomic_helper_connector_destroy_state(connector, state); kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 8039d54a7441..e2086372ef61 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -87,20 +87,34 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector, /* default implementations for state handling */ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); struct drm_crtc_state * drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc); +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); void drm_atomic_helper_plane_reset(struct drm_plane *plane); +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state); struct drm_plane_state * drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane); +void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state); struct drm_connector_state * drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); From 3b59b7ac02e434f267f7cb1ef8fe48c23ddbe9a2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 15:01:22 +0100 Subject: [PATCH 08/15] drm/tegra: dc: Use base atomic state helpers Instead of duplicating the code, make use of the newly introduced atomic state duplicate and destroy helpers. This allows changes to the base atomic state handling to automatically propagate to the Tegra driver and thereby prevent breakage resulting from both copies going out of sync. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 9e329465ab11..d12c701ed292 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -425,8 +425,8 @@ static void tegra_plane_reset(struct drm_plane *plane) { struct tegra_plane_state *state; - if (plane->state && plane->state->fb) - drm_framebuffer_unreference(plane->state->fb); + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane, plane->state); kfree(plane->state); plane->state = NULL; @@ -443,12 +443,14 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_plane_state *copy; - copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) return NULL; - if (copy->base.fb) - drm_framebuffer_reference(copy->base.fb); + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); + copy->tiling = state->tiling; + copy->format = state->format; + copy->swap = state->swap; return ©->base; } @@ -456,9 +458,7 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) { - if (state->fb) - drm_framebuffer_unreference(state->fb); - + __drm_atomic_helper_plane_destroy_state(plane, state); kfree(state); } @@ -1002,6 +1002,9 @@ static void tegra_crtc_reset(struct drm_crtc *crtc) { struct tegra_dc_state *state; + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc, crtc->state); + kfree(crtc->state); crtc->state = NULL; @@ -1018,14 +1021,15 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) struct tegra_dc_state *state = to_dc_state(crtc->state); struct tegra_dc_state *copy; - copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) return NULL; - copy->base.mode_changed = false; - copy->base.active_changed = false; - copy->base.planes_changed = false; - copy->base.event = NULL; + __drm_atomic_helper_crtc_duplicate_state(crtc, ©->base); + copy->clk = state->clk; + copy->pclk = state->pclk; + copy->div = state->div; + copy->planes = state->planes; return ©->base; } @@ -1033,6 +1037,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { + __drm_atomic_helper_crtc_destroy_state(crtc, state); kfree(state); } From 3774363c75fa32cfb415df14b362e0d426418e93 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 15:08:40 +0100 Subject: [PATCH 09/15] drm/tegra: dc: Remove unused function The tegra_dc_setup_clock() function is unused after the conversion to atomic mode-setting, so remove it. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 20 -------------------- drivers/gpu/drm/tegra/drm.h | 2 -- 2 files changed, 22 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index d12c701ed292..e892f8c64bc7 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1164,26 +1164,6 @@ static int tegra_dc_set_timings(struct tegra_dc *dc, return 0; } -int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, - unsigned long pclk, unsigned int div) -{ - u32 value; - int err; - - err = clk_set_parent(dc->clk, parent); - if (err < 0) { - dev_err(dc->dev, "failed to set parent clock: %d\n", err); - return err; - } - - DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div); - - value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; - tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); - - return 0; -} - int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, struct clk *clk, unsigned long pclk, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ded04e3473e9..659b2fcc986d 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -186,8 +186,6 @@ void tegra_dc_enable_vblank(struct tegra_dc *dc); void tegra_dc_disable_vblank(struct tegra_dc *dc); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); void tegra_dc_commit(struct tegra_dc *dc); -int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, - unsigned long pclk, unsigned int div); int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, struct clk *clk, unsigned long pclk, From d0852ab9bb63aaf8e31d39f8819e67ce210d8dbb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 15:10:48 +0100 Subject: [PATCH 10/15] drm/tegra: dc: Remove unused callbacks The ->mode_set() and ->mode_set_base() callbacks are no longer used with full atomic mode-setting drivers, so remove them. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index e892f8c64bc7..1ec24c37d310 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1289,9 +1289,7 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .disable = tegra_crtc_disable, .mode_fixup = tegra_crtc_mode_fixup, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = tegra_crtc_mode_set_nofb, - .mode_set_base = drm_helper_crtc_mode_set_base, .prepare = tegra_crtc_prepare, .commit = tegra_crtc_commit, .atomic_check = tegra_crtc_atomic_check, From 9d910b6068be9b6670d0ebba2ddc3e7816dd8881 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 15:25:54 +0100 Subject: [PATCH 11/15] drm/tegra: dc: Document tegra_dc_state_setup_clock() This function is called by output drivers so should be documented. While at it, move it to a more appropriate location. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 1ec24c37d310..076769aba42e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1164,6 +1164,18 @@ static int tegra_dc_set_timings(struct tegra_dc *dc, return 0; } +/** + * tegra_dc_state_setup_clock - check clock settings and store them in atomic + * state + * @dc: display controller + * @crtc_state: CRTC atomic state + * @clk: parent clock for display controller + * @pclk: pixel clock + * @div: shift clock divider + * + * Returns: + * 0 on success or a negative error-code on failure. + */ int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, struct clk *clk, unsigned long pclk, From 375e118437716acdccda224abb3d464ecfe92884 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 16:14:26 +0100 Subject: [PATCH 12/15] drm/tegra: hdmi: Resets are synchronous Resets on Tegra are synchronous, so keep the clock enabled while asserting the reset. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 7eaaee74a039..05213b2ec2bd 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1394,8 +1394,8 @@ static int tegra_hdmi_exit(struct host1x_client *client) tegra_output_exit(&hdmi->output); - clk_disable_unprepare(hdmi->clk); reset_control_assert(hdmi->rst); + clk_disable_unprepare(hdmi->clk); regulator_disable(hdmi->vdd); regulator_disable(hdmi->pll); From 5c1c071a3667600d1b8426dba031b2d4a20a3efa Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Jan 2015 16:32:52 +0100 Subject: [PATCH 13/15] drm/tegra: hdmi: Name register fields consistently Name the fields of the SOR_SEQ_CTL register consistently. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 2 +- drivers/gpu/drm/tegra/hdmi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 05213b2ec2bd..06ab1783bba1 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -952,7 +952,7 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder, } tegra_hdmi_writel(hdmi, - SOR_SEQ_CTL_PU_PC(0) | + SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8), diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h index 919a19df4e1b..a882514389cd 100644 --- a/drivers/gpu/drm/tegra/hdmi.h +++ b/drivers/gpu/drm/tegra/hdmi.h @@ -201,7 +201,7 @@ #define HDMI_NV_PDISP_SOR_CRCB 0x5d #define HDMI_NV_PDISP_SOR_BLANK 0x5e #define HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f -#define SOR_SEQ_CTL_PU_PC(x) (((x) & 0xf) << 0) +#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0) #define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) #define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) #define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) From bdf765071a8b573f7b1ba14c02881fa3e623825e Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Fri, 30 Jan 2015 13:57:01 -0500 Subject: [PATCH 14/15] drm/tegra: gem: Return 64-bit offset for mmap(2) On 64-bit targets, tegra_gem_mmap() only returns a partial offset to userspace. As such, subsequent calls to mmap(2) may fail. Change the arguments to use a 64-bit offset to fix this. Signed-off-by: Sean Paul Acked-by: Erik Faye-Lund [treding@nvidia.com: tweak commit message] Signed-off-by: Thierry Reding --- include/uapi/drm/tegra_drm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index c15d781ecc0f..5391780c2b05 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -36,7 +36,8 @@ struct drm_tegra_gem_create { struct drm_tegra_gem_mmap { __u32 handle; - __u32 offset; + __u32 pad; + __u64 offset; }; struct drm_tegra_syncpt_read { From 535a65db484ff4e4654a037f2ea7e1ff21431b77 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 30 Mar 2015 10:33:03 +0200 Subject: [PATCH 15/15] drm/tegra: sor: Reset during initialization As there isn't a way for the firmware on the Nyan Chromebooks to hand over the display to the kernel, and the kernel isn't redoing the whole configuration at present. With this patch, the SOR is brought to a known state and we get correct display on every boot. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 41c5a6ed2c36..7591d8901f9a 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1512,12 +1512,30 @@ static int tegra_sor_init(struct host1x_client *client) } } + /* + * XXX: Remove this reset once proper hand-over from firmware to + * kernel is possible. + */ + err = reset_control_assert(sor->rst); + if (err < 0) { + dev_err(sor->dev, "failed to assert SOR reset: %d\n", err); + return err; + } + err = clk_prepare_enable(sor->clk); if (err < 0) { dev_err(sor->dev, "failed to enable clock: %d\n", err); return err; } + usleep_range(1000, 3000); + + err = reset_control_deassert(sor->rst); + if (err < 0) { + dev_err(sor->dev, "failed to deassert SOR reset: %d\n", err); + return err; + } + err = clk_prepare_enable(sor->clk_safe); if (err < 0) return err;