Commit 63b0951b authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm/tegra/for-5.17-rc1' of https://gitlab.freedesktop.org/drm/tegra into drm-next



drm/tegra: Changes for v5.17-rc1

This contains a fairly large rework that makes the buffer objects behave
more according to what the DMA-BUF infrastructure expects. A buffer
object cache is implemented on top of that to make certain operations
such as page-flipping more efficient by avoiding needless map/unmap
operations. This in turn is useful to implement asynchronous commits to
support legacy cursor updates.

Another fairly big addition is the NVDEC driver. This uses the updated
UABI introduced in v5.15-rc1 to provide access to the video decode
engines found on Tegra210 and later.

This also includes some power management improvements that are useful on
older devices in particular because they, together with a bunch of other
changes across the kernel, allow the system to scale down frequency and
voltages when mostly idle and prevent these devices from becoming
excessively hot.

The remainder of these changes is an assortment of cleanups and minor
fixes.

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

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20211217142912.558095-1-thierry.reding@gmail.com
parents b06103b5 d210919d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@ config DRM_TEGRA
	select INTERCONNECT
	select IOMMU_IOVA
	select CEC_CORE if CEC_NOTIFIER
	select SND_SIMPLE_CARD if SND_SOC_TEGRA20_SPDIF
	select SND_SOC_HDMI_CODEC if SND_SOC_TEGRA20_SPDIF
	select SND_AUDIO_GRAPH_CARD if SND_SOC_TEGRA20_SPDIF
	help
	  Choose this option if you have an NVIDIA Tegra SoC.

+2 −1
Original line number Diff line number Diff line
@@ -23,7 +23,8 @@ tegra-drm-y := \
	gr2d.o \
	gr3d.o \
	falcon.o \
	vic.o
	vic.o \
	nvdec.o

tegra-drm-y += trace.o

+179 −15
Original line number Diff line number Diff line
@@ -11,9 +11,12 @@
#include <linux/interconnect.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>

#include <soc/tegra/common.h>
#include <soc/tegra/pmc.h>

#include <drm/drm_atomic.h>
@@ -890,11 +893,9 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
	return 0;
}

static void tegra_cursor_atomic_update(struct drm_plane *plane,
				       struct drm_atomic_state *state)
static void __tegra_cursor_atomic_update(struct drm_plane *plane,
					 struct drm_plane_state *new_state)
{
	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
									   plane);
	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
	struct tegra_drm *tegra = plane->dev->dev_private;
@@ -990,6 +991,14 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
}

static void tegra_cursor_atomic_update(struct drm_plane *plane,
				       struct drm_atomic_state *state)
{
	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);

	__tegra_cursor_atomic_update(plane, new_state);
}

static void tegra_cursor_atomic_disable(struct drm_plane *plane,
					struct drm_atomic_state *state)
{
@@ -1009,12 +1018,78 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
}

static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
{
	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
	struct drm_crtc_state *crtc_state;
	int min_scale, max_scale;
	int err;

	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
	if (WARN_ON(!crtc_state))
		return -EINVAL;

	if (!crtc_state->active)
		return -EINVAL;

	if (plane->state->crtc != new_state->crtc ||
	    plane->state->src_w != new_state->src_w ||
	    plane->state->src_h != new_state->src_h ||
	    plane->state->crtc_w != new_state->crtc_w ||
	    plane->state->crtc_h != new_state->crtc_h ||
	    plane->state->fb != new_state->fb ||
	    plane->state->fb == NULL)
		return -EINVAL;

	min_scale = (1 << 16) / 8;
	max_scale = (8 << 16) / 1;

	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
						  true, true);
	if (err < 0)
		return err;

	if (new_state->visible != plane->state->visible)
		return -EINVAL;

	return 0;
}

static void tegra_cursor_atomic_async_update(struct drm_plane *plane,
					     struct drm_atomic_state *state)
{
	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);

	plane->state->src_x = new_state->src_x;
	plane->state->src_y = new_state->src_y;
	plane->state->crtc_x = new_state->crtc_x;
	plane->state->crtc_y = new_state->crtc_y;

	if (new_state->visible) {
		struct tegra_plane *p = to_tegra_plane(plane);
		u32 value;

		__tegra_cursor_atomic_update(plane, new_state);

		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);

		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
	}
}

static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
	.prepare_fb = tegra_plane_prepare_fb,
	.cleanup_fb = tegra_plane_cleanup_fb,
	.atomic_check = tegra_cursor_atomic_check,
	.atomic_update = tegra_cursor_atomic_update,
	.atomic_disable = tegra_cursor_atomic_disable,
	.atomic_async_check = tegra_cursor_atomic_async_check,
	.atomic_async_update = tegra_cursor_atomic_async_update,
};

static const uint64_t linear_modifiers[] = {
@@ -1267,9 +1342,9 @@ static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
			err = PTR_ERR(planes[i]);

			while (i--)
				tegra_plane_funcs.destroy(planes[i]);
				planes[i]->funcs->destroy(planes[i]);

			tegra_plane_funcs.destroy(primary);
			primary->funcs->destroy(primary);
			return ERR_PTR(err);
		}
	}
@@ -1762,10 +1837,55 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
	return 0;
}

static void tegra_dc_commit_state(struct tegra_dc *dc,
static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
					  struct tegra_dc_state *state)
{
	unsigned long rate, pstate;
	struct dev_pm_opp *opp;
	int err;

	if (!dc->has_opp_table)
		return;

	/* calculate actual pixel clock rate which depends on internal divider */
	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);

	/* find suitable OPP for the rate */
	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);

	/*
	 * Very high resolution modes may results in a clock rate that is
	 * above the characterized maximum. In this case it's okay to fall
	 * back to the characterized maximum.
	 */
	if (opp == ERR_PTR(-ERANGE))
		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);

	if (IS_ERR(opp)) {
		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
			rate, opp);
		return;
	}

	pstate = dev_pm_opp_get_required_pstate(opp, 0);
	dev_pm_opp_put(opp);

	/*
	 * The minimum core voltage depends on the pixel clock rate (which
	 * depends on internal clock divider of the CRTC) and not on the
	 * rate of the display controller clock. This is why we're not using
	 * dev_pm_opp_set_rate() API and instead controlling the power domain
	 * directly.
	 */
	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
	if (err)
		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
			pstate, err);
}

static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
				    struct tegra_dc_state *state)
{
	u32 value;
	int err;

	err = clk_set_parent(dc->clk, state->clk);
@@ -1797,10 +1917,7 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,
		      state->div);
	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);

	if (!dc->soc->has_nvdisplay) {
		value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
	}
	tegra_dc_update_voltage_state(dc, state);
}

static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1991,6 +2108,13 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
	err = host1x_client_suspend(&dc->client);
	if (err < 0)
		dev_err(dc->dev, "failed to suspend: %d\n", err);

	if (dc->has_opp_table) {
		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
		if (err)
			dev_err(dc->dev,
				"failed to clear power domain state: %d\n", err);
	}
}

static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -2002,6 +2126,9 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
	u32 value;
	int err;

	/* apply PLL changes */
	tegra_dc_set_clock_rate(dc, crtc_state);

	err = host1x_client_resume(&dc->client);
	if (err < 0) {
		dev_err(dc->dev, "failed to resume: %d\n", err);
@@ -2076,8 +2203,11 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
	else
		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);

	/* apply PLL and pixel clock changes */
	tegra_dc_commit_state(dc, crtc_state);
	/* apply pixel clock changes */
	if (!dc->soc->has_nvdisplay) {
		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
	}

	/* program display mode */
	tegra_dc_set_timings(dc, mode);
@@ -2107,6 +2237,12 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
	}

	if (dc->rgb) {
		/* XXX: parameterize? */
		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
	}

	tegra_dc_commit(dc);

	drm_crtc_vblank_on(crtc);
@@ -2685,6 +2821,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
	.has_win_b_vfilter_mem_client = true,
	.has_win_c_without_vert_filter = true,
	.plane_tiled_memory_bandwidth_x2 = false,
	.has_pll_d2_out0 = false,
};

static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
@@ -2707,6 +2844,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
	.has_win_b_vfilter_mem_client = true,
	.has_win_c_without_vert_filter = false,
	.plane_tiled_memory_bandwidth_x2 = true,
	.has_pll_d2_out0 = true,
};

static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
@@ -2729,6 +2867,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
	.has_win_b_vfilter_mem_client = false,
	.has_win_c_without_vert_filter = false,
	.plane_tiled_memory_bandwidth_x2 = true,
	.has_pll_d2_out0 = true,
};

static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
@@ -2751,6 +2890,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
	.has_win_b_vfilter_mem_client = false,
	.has_win_c_without_vert_filter = false,
	.plane_tiled_memory_bandwidth_x2 = false,
	.has_pll_d2_out0 = true,
};

static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
@@ -2773,6 +2913,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
	.has_win_b_vfilter_mem_client = false,
	.has_win_c_without_vert_filter = false,
	.plane_tiled_memory_bandwidth_x2 = false,
	.has_pll_d2_out0 = true,
};

static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
@@ -2823,6 +2964,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
	.wgrps = tegra186_dc_wgrps,
	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
	.plane_tiled_memory_bandwidth_x2 = false,
	.has_pll_d2_out0 = false,
};

static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
@@ -2873,6 +3015,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
	.wgrps = tegra194_dc_wgrps,
	.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
	.plane_tiled_memory_bandwidth_x2 = false,
	.has_pll_d2_out0 = false,
};

static const struct of_device_id tegra_dc_of_match[] = {
@@ -2973,6 +3116,23 @@ static int tegra_dc_couple(struct tegra_dc *dc)
	return 0;
}

static int tegra_dc_init_opp_table(struct tegra_dc *dc)
{
	struct tegra_core_opp_params opp_params = {};
	int err;

	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
	if (err && err != -ENODEV)
		return err;

	if (err)
		dc->has_opp_table = false;
	else
		dc->has_opp_table = true;

	return 0;
}

static int tegra_dc_probe(struct platform_device *pdev)
{
	u64 dma_mask = dma_get_mask(pdev->dev.parent);
@@ -3038,6 +3198,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
		tegra_powergate_power_off(dc->powergate);
	}

	err = tegra_dc_init_opp_table(dc);
	if (err < 0)
		return err;

	dc->regs = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(dc->regs))
		return PTR_ERR(dc->regs);
+3 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ struct tegra_dc_soc_info {
	bool has_win_b_vfilter_mem_client;
	bool has_win_c_without_vert_filter;
	bool plane_tiled_memory_bandwidth_x2;
	bool has_pll_d2_out0;
};

struct tegra_dc {
@@ -100,6 +101,8 @@ struct tegra_dc {
	struct drm_info_list *debugfs_files;

	const struct tegra_dc_soc_info *soc;

	bool has_opp_table;
};

static inline struct tegra_dc *
+29 −1
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#include <drm/drm_aperture.h>
#include <drm/drm_atomic.h>
@@ -21,6 +22,10 @@
#include <drm/drm_prime.h>
#include <drm/drm_vblank.h>

#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
#endif

#include "dc.h"
#include "drm.h"
#include "gem.h"
@@ -116,6 +121,7 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
static void tegra_drm_context_free(struct tegra_drm_context *context)
{
	context->client->ops->close_channel(context);
	pm_runtime_put(context->client->base.dev);
	kfree(context);
}

@@ -427,13 +433,20 @@ static int tegra_client_open(struct tegra_drm_file *fpriv,
{
	int err;

	err = pm_runtime_resume_and_get(client->base.dev);
	if (err)
		return err;

	err = client->ops->open_channel(client, context);
	if (err < 0)
	if (err < 0) {
		pm_runtime_put(client->base.dev);
		return err;
	}

	err = idr_alloc(&fpriv->legacy_contexts, context, 1, 0, GFP_KERNEL);
	if (err < 0) {
		client->ops->close_channel(context);
		pm_runtime_put(client->base.dev);
		return err;
	}

@@ -936,6 +949,17 @@ int host1x_client_iommu_attach(struct host1x_client *client)
	struct iommu_group *group = NULL;
	int err;

#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
	if (client->dev->archdata.mapping) {
		struct dma_iommu_mapping *mapping =
				to_dma_iommu_mapping(client->dev);
		arm_iommu_detach_device(client->dev);
		arm_iommu_release_mapping(mapping);

		domain = iommu_get_domain_for_dev(client->dev);
	}
#endif

	/*
	 * If the host1x client is already attached to an IOMMU domain that is
	 * not the shared IOMMU domain, don't try to attach it to a different
@@ -1344,15 +1368,18 @@ static const struct of_device_id host1x_drm_subdevs[] = {
	{ .compatible = "nvidia,tegra210-sor", },
	{ .compatible = "nvidia,tegra210-sor1", },
	{ .compatible = "nvidia,tegra210-vic", },
	{ .compatible = "nvidia,tegra210-nvdec", },
	{ .compatible = "nvidia,tegra186-display", },
	{ .compatible = "nvidia,tegra186-dc", },
	{ .compatible = "nvidia,tegra186-sor", },
	{ .compatible = "nvidia,tegra186-sor1", },
	{ .compatible = "nvidia,tegra186-vic", },
	{ .compatible = "nvidia,tegra186-nvdec", },
	{ .compatible = "nvidia,tegra194-display", },
	{ .compatible = "nvidia,tegra194-dc", },
	{ .compatible = "nvidia,tegra194-sor", },
	{ .compatible = "nvidia,tegra194-vic", },
	{ .compatible = "nvidia,tegra194-nvdec", },
	{ /* sentinel */ }
};

@@ -1376,6 +1403,7 @@ static struct platform_driver * const drivers[] = {
	&tegra_gr2d_driver,
	&tegra_gr3d_driver,
	&tegra_vic_driver,
	&tegra_nvdec_driver,
};

static int __init host1x_drm_init(void)
Loading