Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93396d0f authored by Sean Paul's avatar Sean Paul Committed by Thierry Reding
Browse files

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: default avatarSean Paul <seanpaul@chromium.org>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 73c42c79
Loading
Loading
Loading
Loading
+22 −2
Original line number Original line Diff line number Diff line
@@ -168,7 +168,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
				 const struct tegra_dc_window *window)
				 const struct tegra_dc_window *window)
{
{
	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
	unsigned long value;
	unsigned long value, flags;
	bool yuv, planar;
	bool yuv, planar;


	/*
	/*
@@ -181,6 +181,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
	else
	else
		bpp = planar ? 1 : 2;
		bpp = planar ? 1 : 2;


	spin_lock_irqsave(&dc->lock, flags);

	value = WINDOW_A_SELECT << index;
	value = WINDOW_A_SELECT << index;
	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);


@@ -273,6 +275,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,


		case TEGRA_BO_TILING_MODE_BLOCK:
		case TEGRA_BO_TILING_MODE_BLOCK:
			DRM_ERROR("hardware doesn't support block linear mode\n");
			DRM_ERROR("hardware doesn't support block linear mode\n");
			spin_unlock_irqrestore(&dc->lock, flags);
			return -EINVAL;
			return -EINVAL;
		}
		}


@@ -331,6 +334,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,


	tegra_dc_window_commit(dc, index);
	tegra_dc_window_commit(dc, index);


	spin_unlock_irqrestore(&dc->lock, flags);

	return 0;
	return 0;
}
}


@@ -338,11 +343,14 @@ static int tegra_window_plane_disable(struct drm_plane *plane)
{
{
	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
	struct tegra_plane *p = to_tegra_plane(plane);
	struct tegra_plane *p = to_tegra_plane(plane);
	unsigned long flags;
	u32 value;
	u32 value;


	if (!plane->crtc)
	if (!plane->crtc)
		return 0;
		return 0;


	spin_lock_irqsave(&dc->lock, flags);

	value = WINDOW_A_SELECT << p->index;
	value = WINDOW_A_SELECT << p->index;
	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);


@@ -352,6 +360,8 @@ static int tegra_window_plane_disable(struct drm_plane *plane)


	tegra_dc_window_commit(dc, p->index);
	tegra_dc_window_commit(dc, p->index);


	spin_unlock_irqrestore(&dc->lock, flags);

	return 0;
	return 0;
}
}


@@ -699,14 +709,16 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
	unsigned int h_offset = 0, v_offset = 0;
	unsigned int h_offset = 0, v_offset = 0;
	struct tegra_bo_tiling tiling;
	struct tegra_bo_tiling tiling;
	unsigned long value, flags;
	unsigned int format, swap;
	unsigned int format, swap;
	unsigned long value;
	int err;
	int err;


	err = tegra_fb_get_tiling(fb, &tiling);
	err = tegra_fb_get_tiling(fb, &tiling);
	if (err < 0)
	if (err < 0)
		return err;
		return err;


	spin_lock_irqsave(&dc->lock, flags);

	tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
	tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);


	value = fb->offsets[0] + y * fb->pitches[0] +
	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,


		case TEGRA_BO_TILING_MODE_BLOCK:
		case TEGRA_BO_TILING_MODE_BLOCK:
			DRM_ERROR("hardware doesn't support block linear mode\n");
			DRM_ERROR("hardware doesn't support block linear mode\n");
			spin_unlock_irqrestore(&dc->lock, flags);
			return -EINVAL;
			return -EINVAL;
		}
		}


@@ -778,6 +791,8 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);


	spin_unlock_irqrestore(&dc->lock, flags);

	return 0;
	return 0;
}
}


@@ -823,11 +838,16 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc)


	bo = tegra_fb_get_plane(crtc->primary->fb, 0);
	bo = tegra_fb_get_plane(crtc->primary->fb, 0);


	spin_lock_irqsave(&dc->lock, flags);

	/* check if new start address has been latched */
	/* 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);
	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
	base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
	base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);


	spin_unlock_irqrestore(&dc->lock, flags);

	if (base == bo->paddr + crtc->primary->fb->offsets[0]) {
	if (base == bo->paddr + crtc->primary->fb->offsets[0]) {
		drm_crtc_send_vblank_event(crtc, dc->event);
		drm_crtc_send_vblank_event(crtc, dc->event);
		drm_crtc_vblank_put(crtc);
		drm_crtc_vblank_put(crtc);