Commit 12fc11bc authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm/tegra/for-5.13-rc1' of ssh://git.freedesktop.org/git/tegra/linux into drm-next



drm/tegra: Changes for v5.13-rc1

The changes this time around contain a couple of fixes for host1x along
with some improvements for Tegra DRM. Most notably the Tegra DRM driver
now supports the hardware cursor on Tegra186 and later, more correctly
reflects the capabilities of the display pipelines on various Tegra SoC
generations and knows how to deal with the dGPU sector layout by using
framebuffer modifiers.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210401164430.3349105-1-thierry.reding@gmail.com
parents a1a1ca70 7b6f8467
Loading
Loading
Loading
Loading
+103 −10
Original line number Diff line number Diff line
@@ -832,10 +832,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
	return &plane->base;
}

static const u32 tegra_cursor_plane_formats[] = {
static const u32 tegra_legacy_cursor_plane_formats[] = {
	DRM_FORMAT_RGBA8888,
};

static const u32 tegra_cursor_plane_formats[] = {
	DRM_FORMAT_ARGB8888,
};

static int tegra_cursor_atomic_check(struct drm_plane *plane,
				     struct drm_atomic_state *state)
{
@@ -875,12 +879,24 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
									   plane);
	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
	u32 value = CURSOR_CLIP_DISPLAY;
	struct tegra_drm *tegra = plane->dev->dev_private;
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
	u64 dma_mask = *dc->dev->dma_mask;
#endif
	unsigned int x, y;
	u32 value = 0;

	/* rien ne va plus */
	if (!new_state->crtc || !new_state->fb)
		return;

	/*
	 * Legacy display supports hardware clipping of the cursor, but
	 * nvdisplay relies on software to clip the cursor to the screen.
	 */
	if (!dc->soc->has_nvdisplay)
		value |= CURSOR_CLIP_DISPLAY;

	switch (new_state->crtc_w) {
	case 32:
		value |= CURSOR_SIZE_32x32;
@@ -908,7 +924,7 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
	value = (tegra_plane_state->iova[0] >> 32) & 0x3;
	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
#endif

@@ -920,15 +936,39 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
	value &= ~CURSOR_DST_BLEND_MASK;
	value &= ~CURSOR_SRC_BLEND_MASK;

	if (dc->soc->has_nvdisplay)
		value &= ~CURSOR_COMPOSITION_MODE_XOR;
	else
		value |= CURSOR_MODE_NORMAL;

	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
	value |= CURSOR_ALPHA;
	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);

	/* nvdisplay relies on software for clipping */
	if (dc->soc->has_nvdisplay) {
		struct drm_rect src;

		x = new_state->dst.x1;
		y = new_state->dst.y1;

		drm_rect_fp_to_int(&src, &new_state->src);

		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);

		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
			(drm_rect_width(&src) & tegra->hmask);
		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
	} else {
		x = new_state->crtc_x;
		y = new_state->crtc_y;
	}

	/* position the cursor */
	value = (new_state->crtc_y & 0x3fff) << 16 |
		(new_state->crtc_x & 0x3fff);
	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
}

@@ -982,8 +1022,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
	plane->index = 6;
	plane->dc = dc;

	if (!dc->soc->has_nvdisplay) {
		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
		formats = tegra_legacy_cursor_plane_formats;
	} else {
		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
		formats = tegra_cursor_plane_formats;
	}

	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
				       &tegra_plane_funcs, formats,
@@ -2035,6 +2080,16 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
	return false;
}

static int tegra_dc_early_init(struct host1x_client *client)
{
	struct drm_device *drm = dev_get_drvdata(client->host);
	struct tegra_drm *tegra = drm->dev_private;

	tegra->num_crtcs++;

	return 0;
}

static int tegra_dc_init(struct host1x_client *client)
{
	struct drm_device *drm = dev_get_drvdata(client->host);
@@ -2045,6 +2100,12 @@ static int tegra_dc_init(struct host1x_client *client)
	struct drm_plane *cursor = NULL;
	int err;

	/*
	 * DC has been reset by now, so VBLANK syncpoint can be released
	 * for general use.
	 */
	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);

	/*
	 * XXX do not register DCs with no window groups because we cannot
	 * assign a primary plane to them, which in turn will cause KMS to
@@ -2111,6 +2172,12 @@ static int tegra_dc_init(struct host1x_client *client)
	if (dc->soc->pitch_align > tegra->pitch_align)
		tegra->pitch_align = dc->soc->pitch_align;

	/* track maximum resolution */
	if (dc->soc->has_nvdisplay)
		drm->mode_config.max_width = drm->mode_config.max_height = 16384;
	else
		drm->mode_config.max_width = drm->mode_config.max_height = 4096;

	err = tegra_dc_rgb_init(drm, dc);
	if (err < 0 && err != -ENODEV) {
		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
@@ -2141,7 +2208,7 @@ static int tegra_dc_init(struct host1x_client *client)
		drm_plane_cleanup(primary);

	host1x_client_iommu_detach(client);
	host1x_syncpt_free(dc->syncpt);
	host1x_syncpt_put(dc->syncpt);

	return err;
}
@@ -2166,7 +2233,17 @@ static int tegra_dc_exit(struct host1x_client *client)
	}

	host1x_client_iommu_detach(client);
	host1x_syncpt_free(dc->syncpt);
	host1x_syncpt_put(dc->syncpt);

	return 0;
}

static int tegra_dc_late_exit(struct host1x_client *client)
{
	struct drm_device *drm = dev_get_drvdata(client->host);
	struct tegra_drm *tegra = drm->dev_private;

	tegra->num_crtcs--;

	return 0;
}
@@ -2235,8 +2312,10 @@ static int tegra_dc_runtime_resume(struct host1x_client *client)
}

static const struct host1x_client_ops dc_client_ops = {
	.early_init = tegra_dc_early_init,
	.init = tegra_dc_init,
	.exit = tegra_dc_exit,
	.late_exit = tegra_dc_late_exit,
	.suspend = tegra_dc_runtime_suspend,
	.resume = tegra_dc_runtime_resume,
};
@@ -2246,6 +2325,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
	.supports_interlacing = false,
	.supports_cursor = false,
	.supports_block_linear = false,
	.supports_sector_layout = false,
	.has_legacy_blending = true,
	.pitch_align = 8,
	.has_powergate = false,
@@ -2265,6 +2345,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
	.supports_interlacing = false,
	.supports_cursor = false,
	.supports_block_linear = false,
	.supports_sector_layout = false,
	.has_legacy_blending = true,
	.pitch_align = 8,
	.has_powergate = false,
@@ -2284,6 +2365,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
	.supports_interlacing = false,
	.supports_cursor = false,
	.supports_block_linear = false,
	.supports_sector_layout = false,
	.has_legacy_blending = true,
	.pitch_align = 64,
	.has_powergate = true,
@@ -2303,6 +2385,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
	.supports_interlacing = true,
	.supports_cursor = true,
	.supports_block_linear = true,
	.supports_sector_layout = false,
	.has_legacy_blending = false,
	.pitch_align = 64,
	.has_powergate = true,
@@ -2322,6 +2405,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
	.supports_interlacing = true,
	.supports_cursor = true,
	.supports_block_linear = true,
	.supports_sector_layout = false,
	.has_legacy_blending = false,
	.pitch_align = 64,
	.has_powergate = true,
@@ -2375,6 +2459,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
	.supports_interlacing = true,
	.supports_cursor = true,
	.supports_block_linear = true,
	.supports_sector_layout = false,
	.has_legacy_blending = false,
	.pitch_align = 64,
	.has_powergate = false,
@@ -2423,6 +2508,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
	.supports_interlacing = true,
	.supports_cursor = true,
	.supports_block_linear = true,
	.supports_sector_layout = true,
	.has_legacy_blending = false,
	.pitch_align = 64,
	.has_powergate = false,
@@ -2532,9 +2618,16 @@ static int tegra_dc_couple(struct tegra_dc *dc)

static int tegra_dc_probe(struct platform_device *pdev)
{
	u64 dma_mask = dma_get_mask(pdev->dev.parent);
	struct tegra_dc *dc;
	int err;

	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
		return err;
	}

	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
	if (!dc)
		return -ENOMEM;
+6 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ struct tegra_dc_soc_info {
	bool supports_interlacing;
	bool supports_cursor;
	bool supports_block_linear;
	bool supports_sector_layout;
	bool has_legacy_blending;
	unsigned int pitch_align;
	bool has_powergate;
@@ -511,6 +512,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);

#define DC_DISP_CURSOR_START_ADDR_HI		0x4ec
#define DC_DISP_BLEND_CURSOR_CONTROL		0x4f1
#define CURSOR_COMPOSITION_MODE_BLEND		(0 << 25)
#define CURSOR_COMPOSITION_MODE_XOR		(1 << 25)
#define CURSOR_MODE_LEGACY			(0 << 24)
#define CURSOR_MODE_NORMAL			(1 << 24)
#define CURSOR_DST_BLEND_ZERO			(0 << 16)
@@ -705,6 +708,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define PROTOCOL_MASK (0xf << 8)
#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8)

#define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR	0x442
#define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR	0x446

#define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL	0x702
#define OWNER_MASK (0xf << 0)
#define OWNER(x) (((x) & 0xf) << 0)
+17 −10
Original line number Diff line number Diff line
@@ -174,7 +174,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
	struct drm_tegra_syncpt syncpt;
	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
	struct drm_gem_object **refs;
	struct host1x_syncpt *sp;
	struct host1x_syncpt *sp = NULL;
	struct host1x_job *job;
	unsigned int num_refs;
	int err;
@@ -301,8 +301,8 @@ int tegra_drm_submit(struct tegra_drm_context *context,
		goto fail;
	}

	/* check whether syncpoint ID is valid */
	sp = host1x_syncpt_get(host1x, syncpt.id);
	/* Syncpoint ref will be dropped on job release. */
	sp = host1x_syncpt_get_by_id(host1x, syncpt.id);
	if (!sp) {
		err = -ENOENT;
		goto fail;
@@ -311,7 +311,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
	job->is_addr_reg = context->client->ops->is_addr_reg;
	job->is_valid_class = context->client->ops->is_valid_class;
	job->syncpt_incrs = syncpt.incrs;
	job->syncpt_id = syncpt.id;
	job->syncpt = sp;
	job->timeout = 10000;

	if (args->timeout && args->timeout < 10000)
@@ -383,7 +383,7 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data,
	struct drm_tegra_syncpt_read *args = data;
	struct host1x_syncpt *sp;

	sp = host1x_syncpt_get(host, args->id);
	sp = host1x_syncpt_get_by_id_noref(host, args->id);
	if (!sp)
		return -EINVAL;

@@ -398,7 +398,7 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data,
	struct drm_tegra_syncpt_incr *args = data;
	struct host1x_syncpt *sp;

	sp = host1x_syncpt_get(host1x, args->id);
	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
	if (!sp)
		return -EINVAL;

@@ -412,7 +412,7 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data,
	struct drm_tegra_syncpt_wait *args = data;
	struct host1x_syncpt *sp;

	sp = host1x_syncpt_get(host1x, args->id);
	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
	if (!sp)
		return -EINVAL;

@@ -1121,9 +1121,8 @@ static int host1x_drm_probe(struct host1x_device *dev)

	drm->mode_config.min_width = 0;
	drm->mode_config.min_height = 0;

	drm->mode_config.max_width = 4096;
	drm->mode_config.max_height = 4096;
	drm->mode_config.max_width = 0;
	drm->mode_config.max_height = 0;

	drm->mode_config.allow_fb_modifiers = true;

@@ -1142,6 +1141,14 @@ static int host1x_drm_probe(struct host1x_device *dev)
	if (err < 0)
		goto fbdev;

	/*
	 * Now that all display controller have been initialized, the maximum
	 * supported resolution is known and the bitmask for horizontal and
	 * vertical bitfields can be computed.
	 */
	tegra->hmask = drm->mode_config.max_width - 1;
	tegra->vmask = drm->mode_config.max_height - 1;

	if (tegra->use_explicit_iommu) {
		u64 carveout_start, carveout_end, gem_start, gem_end;
		u64 dma_mask = dma_get_mask(&dev->dev);
+5 −0
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@
#include "hub.h"
#include "trace.h"

/* XXX move to include/uapi/drm/drm_fourcc.h? */
#define DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT BIT(22)

struct reset_control;

#ifdef CONFIG_DRM_FBDEV_EMULATION
@@ -54,7 +57,9 @@ struct tegra_drm {
	struct tegra_fbdev *fbdev;
#endif

	unsigned int hmask, vmask;
	unsigned int pitch_align;
	unsigned int num_crtcs;

	struct tegra_display_hub *hub;
};
+10 −0
Original line number Diff line number Diff line
@@ -44,6 +44,15 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
{
	uint64_t modifier = framebuffer->modifier;

	if ((modifier >> 56) == DRM_FORMAT_MOD_VENDOR_NVIDIA) {
		if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0)
			tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA;
		else
			tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_GPU;

		modifier &= ~DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT;
	}

	switch (modifier) {
	case DRM_FORMAT_MOD_LINEAR:
		tiling->mode = TEGRA_BO_TILING_MODE_PITCH;
@@ -86,6 +95,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
		break;

	default:
		DRM_DEBUG_KMS("unknown format modifier: %llx\n", modifier);
		return -EINVAL;
	}

Loading