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

Commit 1ddd36ed authored by Dave Airlie's avatar Dave Airlie
Browse files

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

drm/tegra: Changes for v4.1-rc1

Perhaps the most noteworthy change in this set is the implementation of
a hardware VBLANK counter using host1x syncpoints. The SOR registers can
now be dumped via debugfs, which can be useful while debugging. The IOVA
address space maintained by the driver can also be dumped via debugfs.

Other than than, these changes are mostly cleanup work, such as making
register names more consistent or removing unused code (that was left
over after the atomic mode-setting conversion). There's also a fix for
eDP that makes the driver cope with firmware that already initialized
the display (such as the firmware on the Tegra-based Chromebooks).

* tag 'drm/tegra/for-4.1-rc1' of git://anongit.freedesktop.org/tegra/linux:
  drm/tegra: sor: Reset during initialization
  drm/tegra: gem: Return 64-bit offset for mmap(2)
  drm/tegra: hdmi: Name register fields consistently
  drm/tegra: hdmi: Resets are synchronous
  drm/tegra: dc: Document tegra_dc_state_setup_clock()
  drm/tegra: dc: Remove unused callbacks
  drm/tegra: dc: Remove unused function
  drm/tegra: dc: Use base atomic state helpers
  drm/atomic: Add helpers for state-subclassing drivers
  drm/tegra: dc: Implement hardware VBLANK counter
  gpu: host1x: Export host1x_syncpt_read()
  drm/tegra: sor: Dump registers via debugfs
  drm/tegra: sor: Registers are 32-bit
  drm/tegra: Provide debugfs file for the IOVA space
  drm/tegra: dc: Check for valid parent clock
parents 67a0375f 535a65db
Loading
Loading
Loading
Loading
+128 −16
Original line number Diff line number Diff line
@@ -2068,6 +2068,26 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);

/**
 * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
 * @crtc: CRTC object
 * @state: atomic CRTC state
 *
 * Copies atomic state from a CRTC's current state and resets inferred values.
 * This is useful for drivers that subclass the CRTC state.
 */
void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
					      struct drm_crtc_state *state)
{
	memcpy(state, crtc->state, sizeof(*state));

	state->mode_changed = false;
	state->active_changed = false;
	state->planes_changed = false;
	state->event = NULL;
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);

/**
 * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
 * @crtc: drm CRTC
@@ -2083,19 +2103,34 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
	if (WARN_ON(!crtc->state))
		return NULL;

	state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL);

	if (state) {
		state->mode_changed = false;
		state->active_changed = false;
		state->planes_changed = false;
		state->event = NULL;
	}
	state = kmalloc(sizeof(*state), GFP_KERNEL);
	if (state)
		__drm_atomic_helper_crtc_duplicate_state(crtc, state);

	return state;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);

/**
 * __drm_atomic_helper_crtc_destroy_state - release CRTC state
 * @crtc: CRTC object
 * @state: CRTC state object to release
 *
 * Releases all resources stored in the CRTC state without actually freeing
 * the memory of the CRTC state. This is useful for drivers that subclass the
 * CRTC state.
 */
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
					    struct drm_crtc_state *state)
{
	/*
	 * This is currently a placeholder so that drivers that subclass the
	 * state will automatically do the right thing if code is ever added
	 * to this function.
	 */
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);

/**
 * drm_atomic_helper_crtc_destroy_state - default state destroy hook
 * @crtc: drm CRTC
@@ -2107,6 +2142,7 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
					  struct drm_crtc_state *state)
{
	__drm_atomic_helper_crtc_destroy_state(crtc, state);
	kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
@@ -2131,6 +2167,24 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
}
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);

/**
 * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
 * @plane: plane object
 * @state: atomic plane state
 *
 * Copies atomic state from a plane's current state. This is useful for
 * drivers that subclass the plane state.
 */
void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
					       struct drm_plane_state *state)
{
	memcpy(state, plane->state, sizeof(*state));

	if (state->fb)
		drm_framebuffer_reference(state->fb);
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);

/**
 * drm_atomic_helper_plane_duplicate_state - default state duplicate hook
 * @plane: drm plane
@@ -2146,15 +2200,31 @@ drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
	if (WARN_ON(!plane->state))
		return NULL;

	state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL);

	if (state && state->fb)
		drm_framebuffer_reference(state->fb);
	state = kmalloc(sizeof(*state), GFP_KERNEL);
	if (state)
		__drm_atomic_helper_plane_duplicate_state(plane, state);

	return state;
}
EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);

/**
 * __drm_atomic_helper_plane_destroy_state - release plane state
 * @plane: plane object
 * @state: plane state object to release
 *
 * Releases all resources stored in the plane state without actually freeing
 * the memory of the plane state. This is useful for drivers that subclass the
 * plane state.
 */
void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
					     struct drm_plane_state *state)
{
	if (state->fb)
		drm_framebuffer_unreference(state->fb);
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);

/**
 * drm_atomic_helper_plane_destroy_state - default state destroy hook
 * @plane: drm plane
@@ -2166,9 +2236,7 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
					   struct drm_plane_state *state)
{
	if (state->fb)
		drm_framebuffer_unreference(state->fb);

	__drm_atomic_helper_plane_destroy_state(plane, state);
	kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
@@ -2191,6 +2259,22 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);

/**
 * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
 * @connector: connector object
 * @state: atomic connector state
 *
 * Copies atomic state from a connector's current state. This is useful for
 * drivers that subclass the connector state.
 */
void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
					    struct drm_connector_state *state)
{
	memcpy(state, connector->state, sizeof(*state));
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);

/**
 * drm_atomic_helper_connector_duplicate_state - default state duplicate hook
 * @connector: drm connector
@@ -2201,13 +2285,40 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
{
	struct drm_connector_state *state;

	if (WARN_ON(!connector->state))
		return NULL;

	return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL);
	state = kmalloc(sizeof(*state), GFP_KERNEL);
	if (state)
		__drm_atomic_helper_connector_duplicate_state(connector, state);

	return state;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);

/**
 * __drm_atomic_helper_connector_destroy_state - release connector state
 * @connector: connector object
 * @state: connector state object to release
 *
 * Releases all resources stored in the connector state without actually
 * freeing the memory of the connector state. This is useful for drivers that
 * subclass the connector state.
 */
void
__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
					    struct drm_connector_state *state)
{
	/*
	 * This is currently a placeholder so that drivers that subclass the
	 * state will automatically do the right thing if code is ever added
	 * to this function.
	 */
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);

/**
 * drm_atomic_helper_connector_destroy_state - default state destroy hook
 * @connector: drm connector
@@ -2219,6 +2330,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
					  struct drm_connector_state *state)
{
	__drm_atomic_helper_connector_destroy_state(connector, state);
	kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
+57 −42
Original line number Diff line number Diff line
@@ -425,8 +425,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
{
	struct tegra_plane_state *state;

	if (plane->state && plane->state->fb)
		drm_framebuffer_unreference(plane->state->fb);
	if (plane->state)
		__drm_atomic_helper_plane_destroy_state(plane, plane->state);

	kfree(plane->state);
	plane->state = NULL;
@@ -443,12 +443,14 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla
	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
	struct tegra_plane_state *copy;

	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
	if (!copy)
		return NULL;

	if (copy->base.fb)
		drm_framebuffer_reference(copy->base.fb);
	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
	copy->tiling = state->tiling;
	copy->format = state->format;
	copy->swap = state->swap;

	return &copy->base;
}
@@ -456,9 +458,7 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla
static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
					     struct drm_plane_state *state)
{
	if (state->fb)
		drm_framebuffer_unreference(state->fb);

	__drm_atomic_helper_plane_destroy_state(plane, state);
	kfree(state);
}

@@ -908,6 +908,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
	return 0;
}

u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc)
{
	if (dc->syncpt)
		return host1x_syncpt_read(dc->syncpt);

	/* fallback to software emulated VBLANK counter */
	return drm_crtc_vblank_count(&dc->base);
}

void tegra_dc_enable_vblank(struct tegra_dc *dc)
{
	unsigned long value, flags;
@@ -995,6 +1004,9 @@ static void tegra_crtc_reset(struct drm_crtc *crtc)
{
	struct tegra_dc_state *state;

	if (crtc->state)
		__drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);

	kfree(crtc->state);
	crtc->state = NULL;

@@ -1011,14 +1023,15 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
	struct tegra_dc_state *state = to_dc_state(crtc->state);
	struct tegra_dc_state *copy;

	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
	if (!copy)
		return NULL;

	copy->base.mode_changed = false;
	copy->base.active_changed = false;
	copy->base.planes_changed = false;
	copy->base.event = NULL;
	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
	copy->clk = state->clk;
	copy->pclk = state->pclk;
	copy->div = state->div;
	copy->planes = state->planes;

	return &copy->base;
}
@@ -1026,6 +1039,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
					    struct drm_crtc_state *state)
{
	__drm_atomic_helper_crtc_destroy_state(crtc, state);
	kfree(state);
}

@@ -1152,26 +1166,18 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
	return 0;
}

int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
			 unsigned long pclk, unsigned int div)
{
	u32 value;
	int err;

	err = clk_set_parent(dc->clk, parent);
	if (err < 0) {
		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
		return err;
	}

	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div);

	value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
	tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);

	return 0;
}

/**
 * tegra_dc_state_setup_clock - check clock settings and store them in atomic
 *     state
 * @dc: display controller
 * @crtc_state: CRTC atomic state
 * @clk: parent clock for display controller
 * @pclk: pixel clock
 * @div: shift clock divider
 *
 * Returns:
 * 0 on success or a negative error-code on failure.
 */
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
			       struct drm_crtc_state *crtc_state,
			       struct clk *clk, unsigned long pclk,
@@ -1179,6 +1185,9 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
{
	struct tegra_dc_state *state = to_dc_state(crtc_state);

	if (!clk_has_parent(dc->clk, clk))
		return -EINVAL;

	state->clk = clk;
	state->pclk = pclk;
	state->div = div;
@@ -1294,9 +1303,7 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
	.disable = tegra_crtc_disable,
	.mode_fixup = tegra_crtc_mode_fixup,
	.mode_set = drm_helper_crtc_mode_set,
	.mode_set_nofb = tegra_crtc_mode_set_nofb,
	.mode_set_base = drm_helper_crtc_mode_set_base,
	.prepare = tegra_crtc_prepare,
	.commit = tegra_crtc_commit,
	.atomic_check = tegra_crtc_atomic_check,
@@ -1631,7 +1638,6 @@ static int tegra_dc_init(struct host1x_client *client)
	struct tegra_drm *tegra = drm->dev_private;
	struct drm_plane *primary = NULL;
	struct drm_plane *cursor = NULL;
	unsigned int syncpt;
	u32 value;
	int err;

@@ -1700,13 +1706,15 @@ static int tegra_dc_init(struct host1x_client *client)
	}

	/* initialize display controller */
	if (dc->pipe)
		syncpt = SYNCPT_VBLANK1;
	else
		syncpt = SYNCPT_VBLANK0;
	if (dc->syncpt) {
		u32 syncpt = host1x_syncpt_id(dc->syncpt);

	tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
	tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
		value = SYNCPT_CNTRL_NO_STALL;
		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);

		value = SYNCPT_VSYNC_ENABLE | syncpt;
		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
	}

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
@@ -1874,6 +1882,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)

static int tegra_dc_probe(struct platform_device *pdev)
{
	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
	const struct of_device_id *id;
	struct resource *regs;
	struct tegra_dc *dc;
@@ -1965,6 +1974,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
		return err;
	}

	dc->syncpt = host1x_syncpt_request(&pdev->dev, flags);
	if (!dc->syncpt)
		dev_warn(&pdev->dev, "failed to allocate syncpoint\n");

	platform_set_drvdata(pdev, dc);

	return 0;
@@ -1975,6 +1988,8 @@ static int tegra_dc_remove(struct platform_device *pdev)
	struct tegra_dc *dc = platform_get_drvdata(pdev);
	int err;

	host1x_syncpt_free(dc->syncpt);

	err = host1x_client_unregister(&dc->client);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+3 −4
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@

#define DC_CMD_GENERAL_INCR_SYNCPT		0x000
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL	0x001
#define  SYNCPT_CNTRL_NO_STALL   (1 << 8)
#define  SYNCPT_CNTRL_SOFT_RESET (1 << 0)
#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR	0x002
#define DC_CMD_WIN_A_INCR_SYNCPT		0x008
#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL		0x009
@@ -23,6 +25,7 @@
#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL		0x019
#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR		0x01a
#define DC_CMD_CONT_SYNCPT_VSYNC		0x028
#define  SYNCPT_VSYNC_ENABLE (1 << 8)
#define DC_CMD_DISPLAY_COMMAND_OPTION0		0x031
#define DC_CMD_DISPLAY_COMMAND			0x032
#define DISP_CTRL_MODE_STOP (0 << 5)
@@ -438,8 +441,4 @@
#define DC_WINBUF_BD_UFLOW_STATUS		0xdca
#define DC_WINBUF_CD_UFLOW_STATUS		0xfca

/* synchronization points */
#define SYNCPT_VBLANK0 26
#define SYNCPT_VBLANK1 27

#endif /* TEGRA_DC_H */
+16 −2
Original line number Diff line number Diff line
@@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
	 */
	drm->irq_enabled = true;

	/* syncpoints are used for full 32-bit hardware VBLANK counters */
	drm->vblank_disable_immediate = true;
	drm->max_vblank_count = 0xffffffff;

	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
	if (err < 0)
		goto device;
@@ -813,12 +817,12 @@ static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm,
static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe)
{
	struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
	struct tegra_dc *dc = to_tegra_dc(crtc);

	if (!crtc)
		return 0;

	/* TODO: implement real hardware counter using syncpoints */
	return drm_crtc_vblank_count(crtc);
	return tegra_dc_get_vblank_counter(dc);
}

static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
@@ -879,8 +883,18 @@ static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
	return 0;
}

static int tegra_debugfs_iova(struct seq_file *s, void *data)
{
	struct drm_info_node *node = (struct drm_info_node *)s->private;
	struct drm_device *drm = node->minor->dev;
	struct tegra_drm *tegra = drm->dev_private;

	return drm_mm_dump_table(s, &tegra->mm);
}

static struct drm_info_list tegra_debugfs_list[] = {
	{ "framebuffers", tegra_debugfs_framebuffers, 0 },
	{ "iova", tegra_debugfs_iova, 0 },
};

static int tegra_debugfs_init(struct drm_minor *minor)
+2 −2
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ struct tegra_output;

struct tegra_dc {
	struct host1x_client client;
	struct host1x_syncpt *syncpt;
	struct device *dev;
	spinlock_t lock;

@@ -180,12 +181,11 @@ struct tegra_dc_window {
};

/* from dc.c */
u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc);
void tegra_dc_enable_vblank(struct tegra_dc *dc);
void tegra_dc_disable_vblank(struct tegra_dc *dc);
void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
void tegra_dc_commit(struct tegra_dc *dc);
int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
			 unsigned long pclk, unsigned int div);
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
			       struct drm_crtc_state *crtc_state,
			       struct clk *clk, unsigned long pclk,
Loading