diff options
author | Robert Morell <rmorell@nvidia.com> | 2011-03-03 14:58:06 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:48:11 -0800 |
commit | c1c9c8660b49eb0b67ae02a3e21e07fe7135232a (patch) | |
tree | 8568972953eacb5c92323efc76a5445200df0065 /drivers/video/tegra | |
parent | 3517cb3da60dc94c158c7cf2b39ad83e387a55dd (diff) |
video: tegra: nvhost: Use a syncpoint per window
Reserve one syncpoint per window per display controller instead of one
for the entire display controller. This is necessary to allow multiple
windows on a single display controller to flip asynchronously.
bug 818525
Original-Change-Id: Ide1de2bf2ed0bfea7f6abe9aa93815efd0824db1
Signed-off-by: Robert Morell <rmorell@nvidia.com>
Reviewed-on: http://git-master/r/40516
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: R49886938a74e71db0c8f53edc8ac45e5015ffe84
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 87 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 8 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/dev.c | 91 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | 6 | ||||
-rw-r--r-- | drivers/video/tegra/dc/overlay.c | 6 | ||||
-rw-r--r-- | drivers/video/tegra/host/t20/channel_t20.c | 4 | ||||
-rw-r--r-- | drivers/video/tegra/host/t20/syncpt_t20.h | 13 |
7 files changed, 158 insertions, 57 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 3d26d2c633d0..36834743a9db 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -1024,33 +1024,33 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) } EXPORT_SYMBOL(tegra_dc_update_windows); -u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc) +u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i) { - return dc->syncpt_id; + return dc->syncpt[i].id; } EXPORT_SYMBOL(tegra_dc_get_syncpt_id); -u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc) +u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i) { u32 max; mutex_lock(&dc->lock); - max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, dc->syncpt_id, - ((dc->enabled) ? 1 : 0) ); - dc->syncpt_max = max; + max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, + dc->syncpt[i].id, ((dc->enabled) ? 1 : 0)); + dc->syncpt[i].max = max; mutex_unlock(&dc->lock); return max; } -void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val) +void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val) { mutex_lock(&dc->lock); if ( dc->enabled ) - while (dc->syncpt_min < val) { - dc->syncpt_min++; + while (dc->syncpt[i].min < val) { + dc->syncpt[i].min++; nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, - dc->syncpt_id); + dc->syncpt[i].id); } mutex_unlock(&dc->lock); } @@ -1902,15 +1902,58 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc) tegra_dc_writel(dc, color_control, DC_DISP_DISP_COLOR_CONTROL); } +static u32 get_syncpt(struct tegra_dc *dc, int idx) +{ + u32 syncpt_id; + + switch (dc->ndev->id) { + case 0: + switch (idx) { + case 0: + syncpt_id = NVSYNCPT_DISP0_A; + break; + case 1: + syncpt_id = NVSYNCPT_DISP0_B; + break; + case 2: + syncpt_id = NVSYNCPT_DISP0_C; + break; + default: + BUG(); + break; + } + break; + case 1: + switch (idx) { + case 0: + syncpt_id = NVSYNCPT_DISP1_A; + break; + case 1: + syncpt_id = NVSYNCPT_DISP1_B; + break; + case 2: + syncpt_id = NVSYNCPT_DISP1_C; + break; + default: + BUG(); + break; + } + break; + default: + BUG(); + break; + } + + return syncpt_id; +} + static void tegra_dc_init(struct tegra_dc *dc) { - u32 disp_syncpt = 0; u32 vblank_syncpt = 0; int i; tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); if (dc->ndev->id == 0) { - disp_syncpt = NVSYNCPT_DISP0; vblank_syncpt = NVSYNCPT_VBLANK0; tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0A, @@ -1924,7 +1967,6 @@ static void tegra_dc_init(struct tegra_dc *dc) tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHC, TEGRA_MC_PRIO_HIGH); } else if (dc->ndev->id == 1) { - disp_syncpt = NVSYNCPT_DISP1; vblank_syncpt = NVSYNCPT_VBLANK1; tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0AB, @@ -1960,10 +2002,14 @@ static void tegra_dc_init(struct tegra_dc *dc) } - dc->syncpt_id = disp_syncpt; + for (i = 0; i < dc->n_windows; i++) { + u32 syncpt = get_syncpt(dc, i); + + dc->syncpt[i].id = syncpt; - dc->syncpt_min = dc->syncpt_max = - nvhost_syncpt_read(&dc->ndev->host->syncpt, disp_syncpt); + dc->syncpt[i].min = dc->syncpt[i].max = + nvhost_syncpt_read(&dc->ndev->host->syncpt, syncpt); + } print_mode(dc, &dc->mode, __func__); @@ -2105,9 +2151,12 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) dc->out->disable(); /* flush any pending syncpt waits */ - while (dc->syncpt_min < dc->syncpt_max) { - dc->syncpt_min++; - nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id); + for (i = 0; i < dc->n_windows; i++) { + while (dc->syncpt[i].min < dc->syncpt[i].max) { + dc->syncpt[i].min++; + nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, + dc->syncpt[i].id); + } } } diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index 4bf429f7e68c..e7e86cc9f11f 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -105,9 +105,11 @@ struct tegra_dc { struct tegra_overlay_info *overlay; - u32 syncpt_id; - u32 syncpt_min; - u32 syncpt_max; + struct { + u32 id; + u32 min; + u32 max; + } syncpt[DC_N_WINDOWS]; unsigned long underflow_mask; struct work_struct reset_work; diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c index 8905402954bf..b549f77222c8 100644 --- a/drivers/video/tegra/dc/ext/dev.c +++ b/drivers/video/tegra/dc/ext/dev.c @@ -46,13 +46,13 @@ struct tegra_dc_ext_flip_win { struct nvmap_handle_ref *handle; /* ugh. is this really necessary */ dma_addr_t phys_addr; + u32 syncpt_max; }; struct tegra_dc_ext_flip_data { struct tegra_dc_ext *ext; struct work_struct work; struct tegra_dc_ext_flip_win win[DC_N_WINDOWS]; - u32 syncpt_max; }; static int tegra_dc_ext_set_nvmap_fd(struct tegra_dc_ext_user *user, @@ -112,10 +112,12 @@ static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user, mutex_lock(&win->lock); - if (win->user == user) + if (win->user == user) { + flush_workqueue(win->flip_wq); win->user = 0; - else + } else { ret = -EACCES; + } mutex_unlock(&win->lock); @@ -124,7 +126,13 @@ static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user, void tegra_dc_ext_suspend(struct tegra_dc_ext *ext) { - flush_workqueue(ext->flip_wq); + int i; + + for (i = 0; i < ext->dc->n_windows; i++) { + struct tegra_dc_ext_win *win = &ext->win[i]; + + flush_workqueue(win->flip_wq); + } } static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext, @@ -209,7 +217,16 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work) /* TODO: implement swapinterval here */ tegra_dc_sync_windows(wins, nr_win); - tegra_dc_incr_syncpt_min(ext->dc, data->syncpt_max); + for (i = 0; i < DC_N_WINDOWS; i++) { + struct tegra_dc_ext_flip_win *flip_win = &data->win[i]; + int index = flip_win->attr.index; + + if (index < 0) + continue; + + tegra_dc_incr_syncpt_min(ext->dc, index, + flip_win->syncpt_max); + } /* unpin and deref previous front buffers */ for (i = 0; i < nr_unpin; i++) { @@ -351,7 +368,7 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user, { struct tegra_dc_ext *ext = user->ext; struct tegra_dc_ext_flip_data *data; - u32 syncpt_max; + int work_index; int i, ret = 0; if (!user->nvmap) @@ -386,13 +403,26 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user, if (ret) goto fail_pin; - syncpt_max = tegra_dc_incr_syncpt_max(ext->dc); - data->syncpt_max = syncpt_max; + for (i = 0; i < DC_N_WINDOWS; i++) { + u32 syncpt_max; + int index = args->win[i].index; + + if (index < 0) + continue; - args->post_syncpt_val = syncpt_max; - args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc); + syncpt_max = tegra_dc_incr_syncpt_max(ext->dc, index); - queue_work(ext->flip_wq, &data->work); + data->win[i].syncpt_max = syncpt_max; + + /* + * Any of these windows' syncpoints should be equivalent for + * the client, so we just send back an arbitrary one of them + */ + args->post_syncpt_val = syncpt_max; + args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc, index); + work_index = index; + } + queue_work(ext->win[work_index].flip_wq, &data->work); unlock_windows_for_flip(user, args); @@ -485,18 +515,35 @@ static int tegra_dc_release(struct inode *inode, struct file *filp) static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext) { - int i; + int i, ret; for (i = 0; i < ext->dc->n_windows; i++) { struct tegra_dc_ext_win *win = &ext->win[i]; + char name[32]; win->ext = ext; win->idx = i; + snprintf(name, sizeof(name), "tegradc.%d/%c", + ext->dc->ndev->id, 'a' + i); + win->flip_wq = create_singlethread_workqueue(name); + if (!win->flip_wq) { + ret = -ENOMEM; + goto cleanup; + } + mutex_init(&win->lock); } return 0; + +cleanup: + while (i--) { + struct tegra_dc_ext_win *win = &ext->win[i]; + destroy_workqueue(win->flip_wq); + } + + return ret; } static const struct file_operations tegra_dc_devops = { @@ -545,23 +592,14 @@ struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev, goto cleanup_device; } - ext->flip_wq = create_singlethread_workqueue(dev_name(&ndev->dev)); - if (!ext->flip_wq) { - ret = -ENOMEM; - goto cleanup_nvmap; - } - ret = tegra_dc_ext_setup_windows(ext); if (ret) - goto cleanup_wq; + goto cleanup_nvmap; tegra_dc_ext_devno++; return ext; -cleanup_wq: - destroy_workqueue(ext->flip_wq); - cleanup_nvmap: nvmap_client_put(ext->nvmap); @@ -579,9 +617,14 @@ cleanup_alloc: void tegra_dc_ext_unregister(struct tegra_dc_ext *ext) { + int i; - flush_workqueue(ext->flip_wq); - destroy_workqueue(ext->flip_wq); + for (i = 0; i < ext->dc->n_windows; i++) { + struct tegra_dc_ext_win *win = &ext->win[i]; + + flush_workqueue(win->flip_wq); + destroy_workqueue(win->flip_wq); + } nvmap_client_put(ext->nvmap); device_del(ext->dev); diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h index 20018c9fbe20..b0a9bf65c6cd 100644 --- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h +++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h @@ -40,9 +40,9 @@ struct tegra_dc_ext_win { struct mutex lock; - u32 syncpt_id; - struct nvmap_handle_ref *cur_handle; + + struct workqueue_struct *flip_wq; }; struct tegra_dc_ext { @@ -54,8 +54,6 @@ struct tegra_dc_ext { struct nvmap_client *nvmap; struct tegra_dc_ext_win win[DC_N_WINDOWS]; - - struct workqueue_struct *flip_wq; }; #endif /* __TEGRA_DC_EXT_PRIV_H */ diff --git a/drivers/video/tegra/dc/overlay.c b/drivers/video/tegra/dc/overlay.c index 7879100be8e1..3444b2f9fa34 100644 --- a/drivers/video/tegra/dc/overlay.c +++ b/drivers/video/tegra/dc/overlay.c @@ -318,7 +318,7 @@ static void tegra_overlay_flip_worker(struct work_struct *work) tegra_dc_sync_windows(wins, nr_win); } - tegra_dc_incr_syncpt_min(overlay->dc, data->syncpt_max); + tegra_dc_incr_syncpt_min(overlay->dc, 0, data->syncpt_max); /* unpin and deref previous front buffers */ for (i = 0; i < nr_unpin; i++) { @@ -378,13 +378,13 @@ static int tegra_overlay_flip(struct tegra_overlay_info *overlay, } } - syncpt_max = tegra_dc_incr_syncpt_max(overlay->dc); + syncpt_max = tegra_dc_incr_syncpt_max(overlay->dc, 0); data->syncpt_max = syncpt_max; queue_work(overlay->flip_wq, &data->work); args->post_syncpt_val = syncpt_max; - args->post_syncpt_id = tegra_dc_get_syncpt_id(overlay->dc); + args->post_syncpt_id = tegra_dc_get_syncpt_id(overlay->dc, 0); mutex_unlock(&tegra_flip_lock); return 0; diff --git a/drivers/video/tegra/host/t20/channel_t20.c b/drivers/video/tegra/host/t20/channel_t20.c index 480dacf374f6..ad6a02e16f8c 100644 --- a/drivers/video/tegra/host/t20/channel_t20.c +++ b/drivers/video/tegra/host/t20/channel_t20.c @@ -52,7 +52,9 @@ static const struct nvhost_channeldesc channelmap[] = { { /* channel 0 */ .name = "display", - .syncpts = BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | + .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | + BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) | + BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) | BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1), .modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB), }, diff --git a/drivers/video/tegra/host/t20/syncpt_t20.h b/drivers/video/tegra/host/t20/syncpt_t20.h index 0f80367eab43..5e0c978ef7aa 100644 --- a/drivers/video/tegra/host/t20/syncpt_t20.h +++ b/drivers/video/tegra/host/t20/syncpt_t20.h @@ -23,6 +23,8 @@ #ifndef __NVHOST_SYNCPT_T20_H #define __NVHOST_SYNCPT_T20_H +#define NVSYNCPT_DISP0_A (8) +#define NVSYNCPT_DISP1_A (9) #define NVSYNCPT_AVP_0 (10) #define NVSYNCPT_CSI_VI_0 (11) #define NVSYNCPT_CSI_VI_1 (12) @@ -33,10 +35,12 @@ #define NVSYNCPT_VI_ISP_4 (17) #define NVSYNCPT_2D_0 (18) #define NVSYNCPT_2D_1 (19) +#define NVSYNCPT_DISP0_B (20) +#define NVSYNCPT_DISP1_B (21) #define NVSYNCPT_3D (22) #define NVSYNCPT_MPE (23) -#define NVSYNCPT_DISP0 (24) -#define NVSYNCPT_DISP1 (25) +#define NVSYNCPT_DISP0_C (24) +#define NVSYNCPT_DISP1_C (25) #define NVSYNCPT_VBLANK0 (26) #define NVSYNCPT_VBLANK1 (27) #define NVSYNCPT_MPE_EBM_EOF (28) @@ -51,7 +55,10 @@ /* sync points that are wholly managed by the client */ #define NVSYNCPTS_CLIENT_MANAGED ( \ - BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | BIT(NVSYNCPT_DSI) | \ + BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | \ + BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) | \ + BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) | \ + BIT(NVSYNCPT_DSI) | \ BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | \ BIT(NVSYNCPT_VI_ISP_1) | BIT(NVSYNCPT_VI_ISP_2) | \ BIT(NVSYNCPT_VI_ISP_3) | BIT(NVSYNCPT_VI_ISP_4) | \ |