diff options
| author | Sean Paul <seanpaul@chromium.org> | 2014-11-19 13:04:49 -0500 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2014-12-17 08:27:39 -0500 |
| commit | 93396d0f9c027654eb09151d2e22fe78a39feedb (patch) | |
| tree | 7604f2eda411a260f1be1522b6051591d2d42943 | |
| parent | 73c42c79767a03ae64d11457e3ce80e80e09e514 (diff) | |
drm/tegra: dc: Select root window for event dispatch
In finish pageflip, the driver was not selecting the root window when
dispatching events. This exposed a race where a plane update would
change the window selection and cause tegra_dc_finish_page_flip to check
the wrong base address.
This patch also protects access to the window selection register as well
as the registers affected by it.
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
| -rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 24 |
1 files changed, 22 insertions, 2 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index ef36f9d5e35e..978993fa3a36 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
| @@ -168,7 +168,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | |||
| 168 | const struct tegra_dc_window *window) | 168 | const struct tegra_dc_window *window) |
| 169 | { | 169 | { |
| 170 | unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; | 170 | unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; |
| 171 | unsigned long value; | 171 | unsigned long value, flags; |
| 172 | bool yuv, planar; | 172 | bool yuv, planar; |
| 173 | 173 | ||
| 174 | /* | 174 | /* |
| @@ -181,6 +181,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | |||
| 181 | else | 181 | else |
| 182 | bpp = planar ? 1 : 2; | 182 | bpp = planar ? 1 : 2; |
| 183 | 183 | ||
| 184 | spin_lock_irqsave(&dc->lock, flags); | ||
| 185 | |||
| 184 | value = WINDOW_A_SELECT << index; | 186 | value = WINDOW_A_SELECT << index; |
| 185 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | 187 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); |
| 186 | 188 | ||
| @@ -273,6 +275,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | |||
| 273 | 275 | ||
| 274 | case TEGRA_BO_TILING_MODE_BLOCK: | 276 | case TEGRA_BO_TILING_MODE_BLOCK: |
| 275 | DRM_ERROR("hardware doesn't support block linear mode\n"); | 277 | DRM_ERROR("hardware doesn't support block linear mode\n"); |
| 278 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 276 | return -EINVAL; | 279 | return -EINVAL; |
| 277 | } | 280 | } |
| 278 | 281 | ||
| @@ -331,6 +334,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | |||
| 331 | 334 | ||
| 332 | tegra_dc_window_commit(dc, index); | 335 | tegra_dc_window_commit(dc, index); |
| 333 | 336 | ||
| 337 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 338 | |||
| 334 | return 0; | 339 | return 0; |
| 335 | } | 340 | } |
| 336 | 341 | ||
| @@ -338,11 +343,14 @@ static int tegra_window_plane_disable(struct drm_plane *plane) | |||
| 338 | { | 343 | { |
| 339 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); | 344 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); |
| 340 | struct tegra_plane *p = to_tegra_plane(plane); | 345 | struct tegra_plane *p = to_tegra_plane(plane); |
| 346 | unsigned long flags; | ||
| 341 | u32 value; | 347 | u32 value; |
| 342 | 348 | ||
| 343 | if (!plane->crtc) | 349 | if (!plane->crtc) |
| 344 | return 0; | 350 | return 0; |
| 345 | 351 | ||
| 352 | spin_lock_irqsave(&dc->lock, flags); | ||
| 353 | |||
| 346 | value = WINDOW_A_SELECT << p->index; | 354 | value = WINDOW_A_SELECT << p->index; |
| 347 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | 355 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); |
| 348 | 356 | ||
| @@ -352,6 +360,8 @@ static int tegra_window_plane_disable(struct drm_plane *plane) | |||
| 352 | 360 | ||
| 353 | tegra_dc_window_commit(dc, p->index); | 361 | tegra_dc_window_commit(dc, p->index); |
| 354 | 362 | ||
| 363 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 364 | |||
| 355 | return 0; | 365 | return 0; |
| 356 | } | 366 | } |
| 357 | 367 | ||
| @@ -699,14 +709,16 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | |||
| 699 | struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); | 709 | struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); |
| 700 | unsigned int h_offset = 0, v_offset = 0; | 710 | unsigned int h_offset = 0, v_offset = 0; |
| 701 | struct tegra_bo_tiling tiling; | 711 | struct tegra_bo_tiling tiling; |
| 712 | unsigned long value, flags; | ||
| 702 | unsigned int format, swap; | 713 | unsigned int format, swap; |
| 703 | unsigned long value; | ||
| 704 | int err; | 714 | int err; |
| 705 | 715 | ||
| 706 | err = tegra_fb_get_tiling(fb, &tiling); | 716 | err = tegra_fb_get_tiling(fb, &tiling); |
| 707 | if (err < 0) | 717 | if (err < 0) |
| 708 | return err; | 718 | return err; |
| 709 | 719 | ||
| 720 | spin_lock_irqsave(&dc->lock, flags); | ||
| 721 | |||
| 710 | tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); | 722 | tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); |
| 711 | 723 | ||
| 712 | value = fb->offsets[0] + y * fb->pitches[0] + | 724 | value = fb->offsets[0] + y * fb->pitches[0] + |
| @@ -752,6 +764,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | |||
| 752 | 764 | ||
| 753 | case TEGRA_BO_TILING_MODE_BLOCK: | 765 | case TEGRA_BO_TILING_MODE_BLOCK: |
| 754 | DRM_ERROR("hardware doesn't support block linear mode\n"); | 766 | DRM_ERROR("hardware doesn't support block linear mode\n"); |
| 767 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 755 | return -EINVAL; | 768 | return -EINVAL; |
| 756 | } | 769 | } |
| 757 | 770 | ||
| @@ -778,6 +791,8 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | |||
| 778 | tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); | 791 | tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); |
| 779 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | 792 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); |
| 780 | 793 | ||
| 794 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 795 | |||
| 781 | return 0; | 796 | return 0; |
| 782 | } | 797 | } |
| 783 | 798 | ||
| @@ -823,11 +838,16 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) | |||
| 823 | 838 | ||
| 824 | bo = tegra_fb_get_plane(crtc->primary->fb, 0); | 839 | bo = tegra_fb_get_plane(crtc->primary->fb, 0); |
| 825 | 840 | ||
| 841 | spin_lock_irqsave(&dc->lock, flags); | ||
| 842 | |||
| 826 | /* check if new start address has been latched */ | 843 | /* check if new start address has been latched */ |
| 844 | tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 827 | tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); | 845 | tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); |
| 828 | base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); | 846 | base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); |
| 829 | tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); | 847 | tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); |
| 830 | 848 | ||
| 849 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 850 | |||
| 831 | if (base == bo->paddr + crtc->primary->fb->offsets[0]) { | 851 | if (base == bo->paddr + crtc->primary->fb->offsets[0]) { |
| 832 | drm_crtc_send_vblank_event(crtc, dc->event); | 852 | drm_crtc_send_vblank_event(crtc, dc->event); |
| 833 | drm_crtc_vblank_put(crtc); | 853 | drm_crtc_vblank_put(crtc); |
