All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/36] drm/tegra: Atomic mode-setting conversion
@ 2015-01-20 10:48 ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Russell King,
	Mike Turquette, Stephen Boyd

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

This series converts the Tegra DRM driver to atomic mode-setting. The
size of the is somewhat misleading, because a bunch of preparatory work
is included.

The first patch adds a new function to the clk API that allows checking
that setting a parent clock can succeed. This is needed to make sure an
atomic state can be applied before actually making any changes to the
hardware.

Patch 2 makes the plane's ->atomic_update() callback mandatory because
there is no use-case in which it makes sense to omit it.

Patches 3 and 4 add additional callbacks for planes and encoders, which
are needed by subsequent patches.

Patch 5 adds a helper to obtain the state of the CRTC that a plane is
being attached to. This is non-trivial, so a new helper is warranted.

Patches 6 to 21 are preparatory work and cleanup needed to simplify the
atomic mode-setting transition.

Patches 22 to 32 are the actual conversion to atomic mode-setting. This
is followed-up by patch 33 removing now unused code. Patches 34 and 35
use the ->atomic_check() callbacks to store data that can be reused at
the commit stage to avoid having to recompute it. Finally patch 36 can
now move some duplicate code from the output drivers to the display
controller.

Thierry

Thierry Reding (36):
  clk: Introduce clk_try_parent()
  drm/plane: Make ->atomic_update() mandatory
  drm/plane: Add optional ->atomic_disable() callback
  drm/atomic: Add ->atomic_check() to encoder helpers
  drm/atomic: Add drm_atomic_plane_get_crtc_state()
  drm/tegra: Use tegra_commit_dc() in output drivers
  drm/tegra: Stop CRTC at CRTC disable time
  drm/tegra: dc: Wait for idle when disabled
  drm/tegra: Move tegra_drm_mode_funcs to the core
  drm/tegra: dc: No longer disable planes at CRTC disable
  drm/tegra: Convert output midlayer to helpers
  drm/tegra: output: Make ->setup_clock() optional
  drm/tegra: Add tegra_dc_setup_clock() helper
  drm/tegra: rgb: Demidlayer
  drm/tegra: hdmi: Demidlayer
  drm/tegra: dsi: Demidlayer
  drm/tegra: sor: Demidlayer
  drm/tegra: debugfs cleanup cannot fail
  drm/tegra: Remove remnants of the output midlayer
  drm/tegra: Output cleanup functions cannot fail
  drm/tegra: dc: Do not needlessly deassert reset
  drm/tegra: Atomic conversion, phase 1
  drm/tegra: Atomic conversion, phase 2
  drm/tegra: Atomic conversion, phase 3, step 1
  drm/tegra: dc: Store clock setup in atomic state
  drm/tegra: rgb: Implement ->atomic_check()
  drm/tegra: dsi: Implement ->atomic_check()
  drm/tegra: hdmi: Implement ->atomic_check()
  drm/tegra: sor: Implement ->atomic_check()
  drm/tegra: dc: Use atomic clock state in modeset
  drm/tegra: Atomic conversion, phase 3, step 2
  drm/tegra: Atomic conversion, phase 3, step 3
  drm/tegra: Remove unused ->mode_fixup() callbacks
  drm/tegra: Track active planes in CRTC state
  drm/tegra: Track tiling and format in plane state
  drm/tegra: dc: Unify enabling the display controller

 drivers/clk/clk.c                   |   36 ++
 drivers/gpu/drm/drm_atomic_helper.c |   34 +-
 drivers/gpu/drm/drm_plane_helper.c  |   10 +-
 drivers/gpu/drm/tegra/dc.c          |  916 +++++++++++++++++------------
 drivers/gpu/drm/tegra/drm.c         |  105 +++-
 drivers/gpu/drm/tegra/drm.h         |   91 +--
 drivers/gpu/drm/tegra/dsi.c         |  516 ++++++++--------
 drivers/gpu/drm/tegra/fb.c          |   25 +-
 drivers/gpu/drm/tegra/hdmi.c        |  291 +++++----
 drivers/gpu/drm/tegra/output.c      |  156 +----
 drivers/gpu/drm/tegra/rgb.c         |  218 +++----
 drivers/gpu/drm/tegra/sor.c         | 1105 ++++++++++++++++++-----------------
 include/drm/drm_atomic.h            |   21 +
 include/drm/drm_atomic_helper.h     |   37 ++
 include/drm/drm_crtc_helper.h       |    6 +
 include/drm/drm_plane_helper.h      |    3 +
 include/linux/clk.h                 |   14 +
 17 files changed, 1963 insertions(+), 1621 deletions(-)

-- 
2.1.3

^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH 00/36] drm/tegra: Atomic mode-setting conversion
@ 2015-01-20 10:48 ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Mike Turquette, Stephen Boyd

From: Thierry Reding <treding@nvidia.com>

This series converts the Tegra DRM driver to atomic mode-setting. The
size of the is somewhat misleading, because a bunch of preparatory work
is included.

The first patch adds a new function to the clk API that allows checking
that setting a parent clock can succeed. This is needed to make sure an
atomic state can be applied before actually making any changes to the
hardware.

Patch 2 makes the plane's ->atomic_update() callback mandatory because
there is no use-case in which it makes sense to omit it.

Patches 3 and 4 add additional callbacks for planes and encoders, which
are needed by subsequent patches.

Patch 5 adds a helper to obtain the state of the CRTC that a plane is
being attached to. This is non-trivial, so a new helper is warranted.

Patches 6 to 21 are preparatory work and cleanup needed to simplify the
atomic mode-setting transition.

Patches 22 to 32 are the actual conversion to atomic mode-setting. This
is followed-up by patch 33 removing now unused code. Patches 34 and 35
use the ->atomic_check() callbacks to store data that can be reused at
the commit stage to avoid having to recompute it. Finally patch 36 can
now move some duplicate code from the output drivers to the display
controller.

Thierry

Thierry Reding (36):
  clk: Introduce clk_try_parent()
  drm/plane: Make ->atomic_update() mandatory
  drm/plane: Add optional ->atomic_disable() callback
  drm/atomic: Add ->atomic_check() to encoder helpers
  drm/atomic: Add drm_atomic_plane_get_crtc_state()
  drm/tegra: Use tegra_commit_dc() in output drivers
  drm/tegra: Stop CRTC at CRTC disable time
  drm/tegra: dc: Wait for idle when disabled
  drm/tegra: Move tegra_drm_mode_funcs to the core
  drm/tegra: dc: No longer disable planes at CRTC disable
  drm/tegra: Convert output midlayer to helpers
  drm/tegra: output: Make ->setup_clock() optional
  drm/tegra: Add tegra_dc_setup_clock() helper
  drm/tegra: rgb: Demidlayer
  drm/tegra: hdmi: Demidlayer
  drm/tegra: dsi: Demidlayer
  drm/tegra: sor: Demidlayer
  drm/tegra: debugfs cleanup cannot fail
  drm/tegra: Remove remnants of the output midlayer
  drm/tegra: Output cleanup functions cannot fail
  drm/tegra: dc: Do not needlessly deassert reset
  drm/tegra: Atomic conversion, phase 1
  drm/tegra: Atomic conversion, phase 2
  drm/tegra: Atomic conversion, phase 3, step 1
  drm/tegra: dc: Store clock setup in atomic state
  drm/tegra: rgb: Implement ->atomic_check()
  drm/tegra: dsi: Implement ->atomic_check()
  drm/tegra: hdmi: Implement ->atomic_check()
  drm/tegra: sor: Implement ->atomic_check()
  drm/tegra: dc: Use atomic clock state in modeset
  drm/tegra: Atomic conversion, phase 3, step 2
  drm/tegra: Atomic conversion, phase 3, step 3
  drm/tegra: Remove unused ->mode_fixup() callbacks
  drm/tegra: Track active planes in CRTC state
  drm/tegra: Track tiling and format in plane state
  drm/tegra: dc: Unify enabling the display controller

 drivers/clk/clk.c                   |   36 ++
 drivers/gpu/drm/drm_atomic_helper.c |   34 +-
 drivers/gpu/drm/drm_plane_helper.c  |   10 +-
 drivers/gpu/drm/tegra/dc.c          |  916 +++++++++++++++++------------
 drivers/gpu/drm/tegra/drm.c         |  105 +++-
 drivers/gpu/drm/tegra/drm.h         |   91 +--
 drivers/gpu/drm/tegra/dsi.c         |  516 ++++++++--------
 drivers/gpu/drm/tegra/fb.c          |   25 +-
 drivers/gpu/drm/tegra/hdmi.c        |  291 +++++----
 drivers/gpu/drm/tegra/output.c      |  156 +----
 drivers/gpu/drm/tegra/rgb.c         |  218 +++----
 drivers/gpu/drm/tegra/sor.c         | 1105 ++++++++++++++++++-----------------
 include/drm/drm_atomic.h            |   21 +
 include/drm/drm_atomic_helper.h     |   37 ++
 include/drm/drm_crtc_helper.h       |    6 +
 include/drm/drm_plane_helper.h      |    3 +
 include/linux/clk.h                 |   14 +
 17 files changed, 1963 insertions(+), 1621 deletions(-)

-- 
2.1.3


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-tegra, Mike Turquette, Stephen Boyd, Russell King, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This new function is similar to clk_set_parent(), except that it doesn't
actually change the parent. It merely checks that the given parent clock
can be a parent for the given clock.

A situation where this is useful is to check that a particular setup is
valid before switching to it. One specific use-case for this is atomic
modesetting in the DRM framework where setting a mode is divided into a
check phase where a given configuration is validated before applying
changes to the hardware.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/clk/clk.c   | 36 ++++++++++++++++++++++++++++++++++++
 include/linux/clk.h | 14 ++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index af06b7377d37..297910815dea 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1672,6 +1672,42 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
 }
 
 /**
+ * clk_try_parent - check if a clock can be the parent clock source of another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This is like clk_set_parent(), except that it only checks that parent can
+ * be the parent clock source for clock.
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_try_parent(struct clk *clk, struct clk *parent)
+{
+	int err = 0;
+
+	if (!clk || !parent)
+		return -EINVAL;
+
+	if ((clk->num_parents > 1) && !clk->ops->set_parent)
+		return -ENOSYS;
+
+	clk_prepare_lock();
+
+	if (clk->parent == parent)
+		goto unlock;
+
+	err = clk_fetch_parent_index(clk, parent);
+	if (err > 0)
+		err = 0;
+
+unlock:
+	clk_prepare_unlock();
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(clk_try_parent);
+
+/**
  * clk_set_parent - switch the parent of a mux clk
  * @clk: the mux clk whose input we are switching
  * @parent: the new input to clk
diff --git a/include/linux/clk.h b/include/linux/clk.h
index fb1ac65f127c..94da8c68a515 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
 int clk_set_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_try_parent - check if a clock can be the parent clock source of another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_try_parent(struct clk *clk, struct clk *parent);
+
+/**
  * clk_set_parent - set the parent clock source for this clock
  * @clk: clock source
  * @parent: parent clock source
@@ -400,6 +409,11 @@ static inline long clk_round_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static inline int clk_try_parent(struct clk *clk, struct clk *parent)
+{
+	return 0;
+}
+
 static inline int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	return 0;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 01/36] clk: Introduce clk_try_parent()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Mike Turquette, Stephen Boyd

From: Thierry Reding <treding@nvidia.com>

This new function is similar to clk_set_parent(), except that it doesn't
actually change the parent. It merely checks that the given parent clock
can be a parent for the given clock.

A situation where this is useful is to check that a particular setup is
valid before switching to it. One specific use-case for this is atomic
modesetting in the DRM framework where setting a mode is divided into a
check phase where a given configuration is validated before applying
changes to the hardware.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/clk/clk.c   | 36 ++++++++++++++++++++++++++++++++++++
 include/linux/clk.h | 14 ++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index af06b7377d37..297910815dea 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1672,6 +1672,42 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
 }
 
 /**
+ * clk_try_parent - check if a clock can be the parent clock source of another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This is like clk_set_parent(), except that it only checks that parent can
+ * be the parent clock source for clock.
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_try_parent(struct clk *clk, struct clk *parent)
+{
+	int err = 0;
+
+	if (!clk || !parent)
+		return -EINVAL;
+
+	if ((clk->num_parents > 1) && !clk->ops->set_parent)
+		return -ENOSYS;
+
+	clk_prepare_lock();
+
+	if (clk->parent == parent)
+		goto unlock;
+
+	err = clk_fetch_parent_index(clk, parent);
+	if (err > 0)
+		err = 0;
+
+unlock:
+	clk_prepare_unlock();
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(clk_try_parent);
+
+/**
  * clk_set_parent - switch the parent of a mux clk
  * @clk: the mux clk whose input we are switching
  * @parent: the new input to clk
diff --git a/include/linux/clk.h b/include/linux/clk.h
index fb1ac65f127c..94da8c68a515 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
 int clk_set_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_try_parent - check if a clock can be the parent clock source of another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_try_parent(struct clk *clk, struct clk *parent);
+
+/**
  * clk_set_parent - set the parent clock source for this clock
  * @clk: clock source
  * @parent: parent clock source
@@ -400,6 +409,11 @@ static inline long clk_round_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static inline int clk_try_parent(struct clk *clk, struct clk *parent)
+{
+	return 0;
+}
+
 static inline int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	return 0;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

There is no use-case where it would be useful for drivers not to
implement this function and the transitional plane helpers already
require drivers to provide an implementation.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d467185f7942..010661f23035 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		funcs = plane->helper_private;
 
-		if (!funcs || !funcs->atomic_update)
+		if (!funcs)
 			continue;
 
 		old_plane_state = old_state->plane_states[i];
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

There is no use-case where it would be useful for drivers not to
implement this function and the transitional plane helpers already
require drivers to provide an implementation.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d467185f7942..010661f23035 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		funcs = plane->helper_private;
 
-		if (!funcs || !funcs->atomic_update)
+		if (!funcs)
 			continue;
 
 		old_plane_state = old_state->plane_states[i];
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to prevent drivers from having to perform the same checks over
and over again, add an optional ->atomic_disable callback which the core
calls under the right circumstances.

v2: pass old state and detect edges to avoid calling ->atomic_disable on
already disabled planes, remove redundant comment (Daniel Vetter)

v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
checking for transitions, move helper to drm_atomic_helper.h, clarify
check for !old_state and its relation to transitional helpers

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
 drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
 include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_plane_helper.h      |  3 +++
 4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 010661f23035..1cb04402cd73 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		old_plane_state = old_state->plane_states[i];
 
-		funcs->atomic_update(plane, old_plane_state);
+		/*
+		 * Special-case disabling the plane if drivers support it.
+		 */
+		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+		    funcs->atomic_disable)
+			funcs->atomic_disable(plane, old_plane_state);
+		else
+			funcs->atomic_update(plane, old_plane_state);
 	}
 
 	for (i = 0; i < ncrtcs; i++) {
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 2f961c180273..02c1a0b74e04 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 			crtc_funcs[i]->atomic_begin(crtc[i]);
 	}
 
-	plane_funcs->atomic_update(plane, plane_state);
+	/*
+	 * Drivers may optionally implement the ->atomic_disable callback, so
+	 * special-case that here.
+	 */
+	if (drm_atomic_plane_disabling(plane, plane_state) &&
+	    plane_funcs->atomic_disable)
+		plane_funcs->atomic_disable(plane, plane_state);
+	else
+		plane_funcs->atomic_update(plane, plane_state);
 
 	for (i = 0; i < 2; i++) {
 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 2095917ff8c7..a0ea4ded3cb5 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
 #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
 	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
 
+/*
+ * drm_atomic_plane_disabling - check whether a plane is being disabled
+ * @plane: plane object
+ * @old_state: previous atomic state
+ *
+ * Checks the atomic state of a plane to determine whether it's being disabled
+ * or not. This also WARNs if it detects an invalid state (both CRTC and FB
+ * need to either both be NULL or both be non-NULL).
+ *
+ * RETURNS:
+ * True if the plane is being disabled, false otherwise.
+ */
+static inline bool
+drm_atomic_plane_disabling(struct drm_plane *plane,
+			   struct drm_plane_state *old_state)
+{
+	/*
+	 * When disabling a plane, CRTC and FB should always be NULL together.
+	 * Anything else should be considered a bug in the atomic core, so we
+	 * gently warn about it.
+	 */
+	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
+		(plane->state->crtc != NULL && plane->state->fb == NULL));
+
+	/*
+	 * When using the transitional helpers, old_state may be NULL. If so,
+	 * we know nothing about the current state and have to assume that it
+	 * might be enabled.
+	 *
+	 * When using the atomic helpers, old_state won't be NULL. Therefore
+	 * this check assumes that either the driver will have reconstructed
+	 * the correct state in ->reset() or that the driver will have taken
+	 * appropriate measures to disable all planes.
+	 */
+	return (!old_state || old_state->crtc) && !plane->state->crtc;
+}
+
 #endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index 0f2230311aa8..680be61ef20a 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
  * @atomic_update: apply an atomic state to the plane
+ * @atomic_disable: disable the plane
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
 			    struct drm_plane_state *state);
 	void (*atomic_update)(struct drm_plane *plane,
 			      struct drm_plane_state *old_state);
+	void (*atomic_disable)(struct drm_plane *plane,
+			       struct drm_plane_state *old_state);
 };
 
 static inline void drm_plane_helper_add(struct drm_plane *plane,
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to prevent drivers from having to perform the same checks over
and over again, add an optional ->atomic_disable callback which the core
calls under the right circumstances.

v2: pass old state and detect edges to avoid calling ->atomic_disable on
already disabled planes, remove redundant comment (Daniel Vetter)

v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
checking for transitions, move helper to drm_atomic_helper.h, clarify
check for !old_state and its relation to transitional helpers

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
 drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
 include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_plane_helper.h      |  3 +++
 4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 010661f23035..1cb04402cd73 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		old_plane_state = old_state->plane_states[i];
 
-		funcs->atomic_update(plane, old_plane_state);
+		/*
+		 * Special-case disabling the plane if drivers support it.
+		 */
+		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+		    funcs->atomic_disable)
+			funcs->atomic_disable(plane, old_plane_state);
+		else
+			funcs->atomic_update(plane, old_plane_state);
 	}
 
 	for (i = 0; i < ncrtcs; i++) {
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 2f961c180273..02c1a0b74e04 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 			crtc_funcs[i]->atomic_begin(crtc[i]);
 	}
 
-	plane_funcs->atomic_update(plane, plane_state);
+	/*
+	 * Drivers may optionally implement the ->atomic_disable callback, so
+	 * special-case that here.
+	 */
+	if (drm_atomic_plane_disabling(plane, plane_state) &&
+	    plane_funcs->atomic_disable)
+		plane_funcs->atomic_disable(plane, plane_state);
+	else
+		plane_funcs->atomic_update(plane, plane_state);
 
 	for (i = 0; i < 2; i++) {
 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 2095917ff8c7..a0ea4ded3cb5 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
 #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
 	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
 
+/*
+ * drm_atomic_plane_disabling - check whether a plane is being disabled
+ * @plane: plane object
+ * @old_state: previous atomic state
+ *
+ * Checks the atomic state of a plane to determine whether it's being disabled
+ * or not. This also WARNs if it detects an invalid state (both CRTC and FB
+ * need to either both be NULL or both be non-NULL).
+ *
+ * RETURNS:
+ * True if the plane is being disabled, false otherwise.
+ */
+static inline bool
+drm_atomic_plane_disabling(struct drm_plane *plane,
+			   struct drm_plane_state *old_state)
+{
+	/*
+	 * When disabling a plane, CRTC and FB should always be NULL together.
+	 * Anything else should be considered a bug in the atomic core, so we
+	 * gently warn about it.
+	 */
+	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
+		(plane->state->crtc != NULL && plane->state->fb == NULL));
+
+	/*
+	 * When using the transitional helpers, old_state may be NULL. If so,
+	 * we know nothing about the current state and have to assume that it
+	 * might be enabled.
+	 *
+	 * When using the atomic helpers, old_state won't be NULL. Therefore
+	 * this check assumes that either the driver will have reconstructed
+	 * the correct state in ->reset() or that the driver will have taken
+	 * appropriate measures to disable all planes.
+	 */
+	return (!old_state || old_state->crtc) && !plane->state->crtc;
+}
+
 #endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index 0f2230311aa8..680be61ef20a 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
  * @atomic_update: apply an atomic state to the plane
+ * @atomic_disable: disable the plane
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
 			    struct drm_plane_state *state);
 	void (*atomic_update)(struct drm_plane *plane,
 			      struct drm_plane_state *old_state);
+	void (*atomic_disable)(struct drm_plane *plane,
+			       struct drm_plane_state *old_state);
 };
 
 static inline void drm_plane_helper_add(struct drm_plane *plane,
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This callback can be used instead of the legacy ->mode_fixup() and is
passed the CRTC and connector states. It can thus use these states to
validate the modeset and cache values in the state to be used during
the actual modeset.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++-------
 include/drm/drm_crtc_helper.h       |  6 ++++++
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 1cb04402cd73..b837d148d880 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
 			}
 		}
 
-
-		ret = funcs->mode_fixup(encoder, &crtc_state->mode,
-					&crtc_state->adjusted_mode);
-		if (!ret) {
-			DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
-				      encoder->base.id, encoder->name);
-			return -EINVAL;
+		if (funcs->atomic_check) {
+			ret = funcs->atomic_check(encoder, crtc_state,
+						  conn_state);
+			if (ret) {
+				DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
+					      encoder->base.id, encoder->name);
+				return ret;
+			}
+		} else {
+			ret = funcs->mode_fixup(encoder, &crtc_state->mode,
+						&crtc_state->adjusted_mode);
+			if (!ret) {
+				DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
+					      encoder->base.id, encoder->name);
+				return -EINVAL;
+			}
 		}
 	}
 
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index e76828d81a8b..5810c027acdc 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -115,6 +115,7 @@ struct drm_crtc_helper_funcs {
  * @get_crtc: return CRTC that the encoder is currently attached to
  * @detect: connection status detection
  * @disable: disable encoder when not in use (overrides DPMS off)
+ * @atomic_check: check for validity of an atomic update
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -137,6 +138,11 @@ struct drm_encoder_helper_funcs {
 					    struct drm_connector *connector);
 	/* disable encoder when not in use - more explicit than dpms off */
 	void (*disable)(struct drm_encoder *encoder);
+
+	/* atomic helpers */
+	int (*atomic_check)(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state);
 };
 
 /**
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This callback can be used instead of the legacy ->mode_fixup() and is
passed the CRTC and connector states. It can thus use these states to
validate the modeset and cache values in the state to be used during
the actual modeset.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++-------
 include/drm/drm_crtc_helper.h       |  6 ++++++
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 1cb04402cd73..b837d148d880 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
 			}
 		}
 
-
-		ret = funcs->mode_fixup(encoder, &crtc_state->mode,
-					&crtc_state->adjusted_mode);
-		if (!ret) {
-			DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
-				      encoder->base.id, encoder->name);
-			return -EINVAL;
+		if (funcs->atomic_check) {
+			ret = funcs->atomic_check(encoder, crtc_state,
+						  conn_state);
+			if (ret) {
+				DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
+					      encoder->base.id, encoder->name);
+				return ret;
+			}
+		} else {
+			ret = funcs->mode_fixup(encoder, &crtc_state->mode,
+						&crtc_state->adjusted_mode);
+			if (!ret) {
+				DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
+					      encoder->base.id, encoder->name);
+				return -EINVAL;
+			}
 		}
 	}
 
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index e76828d81a8b..5810c027acdc 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -115,6 +115,7 @@ struct drm_crtc_helper_funcs {
  * @get_crtc: return CRTC that the encoder is currently attached to
  * @detect: connection status detection
  * @disable: disable encoder when not in use (overrides DPMS off)
+ * @atomic_check: check for validity of an atomic update
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -137,6 +138,11 @@ struct drm_encoder_helper_funcs {
 					    struct drm_connector *connector);
 	/* disable encoder when not in use - more explicit than dpms off */
 	void (*disable)(struct drm_encoder *encoder);
+
+	/* atomic helpers */
+	int (*atomic_check)(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state);
 };
 
 /**
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This function is useful to obtain the state of the CRTC that a plane is
being attached to.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 include/drm/drm_atomic.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 51168a8b723a..ddca107e5cce 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
 int __must_check drm_atomic_commit(struct drm_atomic_state *state);
 int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
 
+/**
+ * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
+ *     plane is being attached to
+ * @state: atomic state of plane
+ *
+ * Getting the atomic state of the CRTC that a given plane being attached to
+ * is non-trivial. We need to look at the global atomic state that the plane
+ * state is part of and extract the CRTC state using the CRTC's index. Using
+ * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
+ * locks, too.
+ *
+ * RETURNS:
+ * The atomic state of the CRTC that the plane is being attached to, or an
+ * ERR_PTR()-encoded negative error code on failure.
+ */
+static inline struct drm_crtc_state *
+drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
+{
+	return drm_atomic_get_crtc_state(state->state, state->crtc);
+}
+
 #endif /* DRM_ATOMIC_H_ */
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This function is useful to obtain the state of the CRTC that a plane is
being attached to.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 include/drm/drm_atomic.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 51168a8b723a..ddca107e5cce 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
 int __must_check drm_atomic_commit(struct drm_atomic_state *state);
 int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
 
+/**
+ * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
+ *     plane is being attached to
+ * @state: atomic state of plane
+ *
+ * Getting the atomic state of the CRTC that a given plane being attached to
+ * is non-trivial. We need to look at the global atomic state that the plane
+ * state is part of and extract the CRTC state using the CRTC's index. Using
+ * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
+ * locks, too.
+ *
+ * RETURNS:
+ * The atomic state of the CRTC that the plane is being attached to, or an
+ * ERR_PTR()-encoded negative error code on failure.
+ */
+static inline struct drm_crtc_state *
+drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
+{
+	return drm_atomic_get_crtc_state(state->state, state->crtc);
+}
+
 #endif /* DRM_ATOMIC_H_ */
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 06/36] drm/tegra: Use tegra_commit_dc() in output drivers
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All output drivers have open-coded variants of this function, so export
it to remove some code duplication.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 2 +-
 drivers/gpu/drm/tegra/drm.h  | 1 +
 drivers/gpu/drm/tegra/dsi.c  | 6 ++----
 drivers/gpu/drm/tegra/hdmi.c | 6 ++----
 drivers/gpu/drm/tegra/rgb.c  | 8 +++-----
 drivers/gpu/drm/tegra/sor.c  | 6 ++----
 6 files changed, 11 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 8f64d8fdf967..6450f1a308cf 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -72,7 +72,7 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc)
  * into the ACTIVE copy, either immediately if the display controller is in
  * STOP mode, or at the next frame boundary otherwise.
  */
-static void tegra_dc_commit(struct tegra_dc *dc)
+void tegra_dc_commit(struct tegra_dc *dc)
 {
 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 3a3b2e7b5b3f..5a0e96debcb1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -177,6 +177,7 @@ struct tegra_dc_window {
 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);
 
 struct tegra_output_ops {
 	int (*enable)(struct tegra_output *output);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index c5a14eea5e55..6554eac858b7 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -658,8 +658,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	/* enable DSI controller */
 	tegra_dsi_enable(dsi);
@@ -778,8 +777,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
 		value &= ~DSI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	err = tegra_dsi_wait_idle(dsi, 100);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 0f122eae7c64..f118b914293e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -997,8 +997,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
 
@@ -1042,8 +1041,7 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	clk_disable_unprepare(hdmi->clk);
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index d6af9be48f42..3b851abbccae 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -123,8 +123,7 @@ static int tegra_output_rgb_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(rgb->dc);
 
 	rgb->enabled = true;
 
@@ -148,11 +147,10 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
 	value &= ~DISP_CTRL_MODE_MASK;
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
 
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
-
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
+	tegra_dc_commit(rgb->dc);
+
 	rgb->enabled = false;
 
 	return 0;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 7829e81f065d..6a341822abe9 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -267,8 +267,7 @@ static int tegra_sor_wakeup(struct tegra_sor *sor)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	timeout = jiffies + msecs_to_jiffies(250);
 
@@ -1080,8 +1079,7 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	err = tegra_sor_power_down(sor);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 06/36] drm/tegra: Use tegra_commit_dc() in output drivers
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All output drivers have open-coded variants of this function, so export
it to remove some code duplication.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 2 +-
 drivers/gpu/drm/tegra/drm.h  | 1 +
 drivers/gpu/drm/tegra/dsi.c  | 6 ++----
 drivers/gpu/drm/tegra/hdmi.c | 6 ++----
 drivers/gpu/drm/tegra/rgb.c  | 8 +++-----
 drivers/gpu/drm/tegra/sor.c  | 6 ++----
 6 files changed, 11 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 8f64d8fdf967..6450f1a308cf 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -72,7 +72,7 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc)
  * into the ACTIVE copy, either immediately if the display controller is in
  * STOP mode, or at the next frame boundary otherwise.
  */
-static void tegra_dc_commit(struct tegra_dc *dc)
+void tegra_dc_commit(struct tegra_dc *dc)
 {
 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 3a3b2e7b5b3f..5a0e96debcb1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -177,6 +177,7 @@ struct tegra_dc_window {
 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);
 
 struct tegra_output_ops {
 	int (*enable)(struct tegra_output *output);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index c5a14eea5e55..6554eac858b7 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -658,8 +658,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	/* enable DSI controller */
 	tegra_dsi_enable(dsi);
@@ -778,8 +777,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
 		value &= ~DSI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	err = tegra_dsi_wait_idle(dsi, 100);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 0f122eae7c64..f118b914293e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -997,8 +997,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
 
@@ -1042,8 +1041,7 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	clk_disable_unprepare(hdmi->clk);
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index d6af9be48f42..3b851abbccae 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -123,8 +123,7 @@ static int tegra_output_rgb_enable(struct tegra_output *output)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(rgb->dc);
 
 	rgb->enabled = true;
 
@@ -148,11 +147,10 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
 	value &= ~DISP_CTRL_MODE_MASK;
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
 
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
-
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
+	tegra_dc_commit(rgb->dc);
+
 	rgb->enabled = false;
 
 	return 0;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 7829e81f065d..6a341822abe9 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -267,8 +267,7 @@ static int tegra_sor_wakeup(struct tegra_sor *sor)
 		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+	tegra_dc_commit(dc);
 
 	timeout = jiffies + msecs_to_jiffies(250);
 
@@ -1080,8 +1079,7 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-		tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+		tegra_dc_commit(dc);
 	}
 
 	err = tegra_sor_power_down(sor);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 07/36] drm/tegra: Stop CRTC at CRTC disable time
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Previously output drivers would all stop the display controller in their
disable path. However with the transition to atomic modesetting the
display controller needs to be kept running until all planes have been
disabled so that software can properly determine (using VBLANK counts)
when it is safe to remove the framebuffers associated with the planes.

Moving this code into the display controller's disable path also gets
rid of the duplication of this into all output drivers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 6 ++++++
 drivers/gpu/drm/tegra/dsi.c  | 4 ----
 drivers/gpu/drm/tegra/hdmi.c | 4 ----
 drivers/gpu/drm/tegra/rgb.c  | 4 ----
 drivers/gpu/drm/tegra/sor.c  | 4 ----
 5 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 6450f1a308cf..db7865ab85c7 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -947,6 +947,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct drm_device *drm = crtc->dev;
 	struct drm_plane *plane;
+	u32 value;
 
 	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
 		if (plane->crtc == crtc) {
@@ -960,6 +961,11 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		}
 	}
 
+	/* stop the display controller */
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
 }
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 6554eac858b7..a8c208dab698 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -769,10 +769,6 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~DSI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index f118b914293e..d4c635148cc7 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1033,10 +1033,6 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 		*/
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 3b851abbccae..39b8d5fe04b2 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -143,10 +143,6 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
 		   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
-
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
 	tegra_dc_commit(rgb->dc);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 6a341822abe9..1fe801ee8eb0 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1071,10 +1071,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 		*/
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 07/36] drm/tegra: Stop CRTC at CRTC disable time
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Previously output drivers would all stop the display controller in their
disable path. However with the transition to atomic modesetting the
display controller needs to be kept running until all planes have been
disabled so that software can properly determine (using VBLANK counts)
when it is safe to remove the framebuffers associated with the planes.

Moving this code into the display controller's disable path also gets
rid of the duplication of this into all output drivers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 6 ++++++
 drivers/gpu/drm/tegra/dsi.c  | 4 ----
 drivers/gpu/drm/tegra/hdmi.c | 4 ----
 drivers/gpu/drm/tegra/rgb.c  | 4 ----
 drivers/gpu/drm/tegra/sor.c  | 4 ----
 5 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 6450f1a308cf..db7865ab85c7 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -947,6 +947,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct drm_device *drm = crtc->dev;
 	struct drm_plane *plane;
+	u32 value;
 
 	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
 		if (plane->crtc == crtc) {
@@ -960,6 +961,11 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		}
 	}
 
+	/* stop the display controller */
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
 }
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 6554eac858b7..a8c208dab698 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -769,10 +769,6 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~DSI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index f118b914293e..d4c635148cc7 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1033,10 +1033,6 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 		*/
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 3b851abbccae..39b8d5fe04b2 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -143,10 +143,6 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
 		   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
 	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
-
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
 	tegra_dc_commit(rgb->dc);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 6a341822abe9..1fe801ee8eb0 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1071,10 +1071,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
 		*/
 
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-		value &= ~DISP_CTRL_MODE_MASK;
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 08/36] drm/tegra: dc: Wait for idle when disabled
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

When disabling the display controller, stop it and wait for it to become
idle. Doing so ensures that no further accesses to the framebuffer occur
and the buffers can be safely unmapped or freed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 70 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 65 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index db7865ab85c7..0c2972e4b41a 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -61,6 +61,26 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc)
 }
 
 /*
+ * Reads the active copy of a register. This takes the dc->lock spinlock to
+ * prevent races with the VBLANK processing which also needs access to the
+ * active copy of some registers.
+ */
+static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
+{
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&dc->lock, flags);
+
+	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
+	value = tegra_dc_readl(dc, offset);
+	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
+
+	spin_unlock_irqrestore(&dc->lock, flags);
+	return value;
+}
+
+/*
  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
  * Latching happens mmediately if the display controller is in STOP mode or
@@ -942,12 +962,47 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.destroy = tegra_dc_destroy,
 };
 
+static void tegra_dc_stop(struct tegra_dc *dc)
+{
+	u32 value;
+
+	/* stop the display controller */
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	tegra_dc_commit(dc);
+}
+
+static bool tegra_dc_idle(struct tegra_dc *dc)
+{
+	u32 value;
+
+	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
+
+	return (value & DISP_CTRL_MODE_MASK) == 0;
+}
+
+static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
+{
+	timeout = jiffies + msecs_to_jiffies(timeout);
+
+	while (time_before(jiffies, timeout)) {
+		if (tegra_dc_idle(dc))
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+
+	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
+	return -ETIMEDOUT;
+}
+
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct drm_device *drm = crtc->dev;
 	struct drm_plane *plane;
-	u32 value;
 
 	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
 		if (plane->crtc == crtc) {
@@ -961,10 +1016,15 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		}
 	}
 
-	/* stop the display controller */
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+	if (!tegra_dc_idle(dc)) {
+		tegra_dc_stop(dc);
+
+		/*
+		 * Ignore the return value, there isn't anything useful to do
+		 * in case this fails.
+		 */
+		tegra_dc_wait_idle(dc, 100);
+	}
 
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 08/36] drm/tegra: dc: Wait for idle when disabled
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

When disabling the display controller, stop it and wait for it to become
idle. Doing so ensures that no further accesses to the framebuffer occur
and the buffers can be safely unmapped or freed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 70 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 65 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index db7865ab85c7..0c2972e4b41a 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -61,6 +61,26 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc)
 }
 
 /*
+ * Reads the active copy of a register. This takes the dc->lock spinlock to
+ * prevent races with the VBLANK processing which also needs access to the
+ * active copy of some registers.
+ */
+static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
+{
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&dc->lock, flags);
+
+	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
+	value = tegra_dc_readl(dc, offset);
+	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
+
+	spin_unlock_irqrestore(&dc->lock, flags);
+	return value;
+}
+
+/*
  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
  * Latching happens mmediately if the display controller is in STOP mode or
@@ -942,12 +962,47 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.destroy = tegra_dc_destroy,
 };
 
+static void tegra_dc_stop(struct tegra_dc *dc)
+{
+	u32 value;
+
+	/* stop the display controller */
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	tegra_dc_commit(dc);
+}
+
+static bool tegra_dc_idle(struct tegra_dc *dc)
+{
+	u32 value;
+
+	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
+
+	return (value & DISP_CTRL_MODE_MASK) == 0;
+}
+
+static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
+{
+	timeout = jiffies + msecs_to_jiffies(timeout);
+
+	while (time_before(jiffies, timeout)) {
+		if (tegra_dc_idle(dc))
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+
+	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
+	return -ETIMEDOUT;
+}
+
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct drm_device *drm = crtc->dev;
 	struct drm_plane *plane;
-	u32 value;
 
 	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
 		if (plane->crtc == crtc) {
@@ -961,10 +1016,15 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		}
 	}
 
-	/* stop the display controller */
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+	if (!tegra_dc_idle(dc)) {
+		tegra_dc_stop(dc);
+
+		/*
+		 * Ignore the return value, there isn't anything useful to do
+		 * in case this fails.
+		 */
+		tegra_dc_wait_idle(dc, 100);
+	}
 
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 09/36] drm/tegra: Move tegra_drm_mode_funcs to the core
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This structure will be extended using non-framebuffer related callbacks
in subsequent patches, so it should move to a more central location.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.c | 15 +++++++++++++++
 drivers/gpu/drm/tegra/drm.h |  4 ++++
 drivers/gpu/drm/tegra/fb.c  | 25 ++++---------------------
 3 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 362fcb785f70..121e022bdab0 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -24,6 +24,13 @@ struct tegra_drm_file {
 	struct list_head contexts;
 };
 
+static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
+	.fb_create = tegra_fb_create,
+#ifdef CONFIG_DRM_TEGRA_FBDEV
+	.output_poll_changed = tegra_fb_output_poll_changed,
+#endif
+};
+
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 {
 	struct host1x_device *device = to_host1x_device(drm->dev);
@@ -52,6 +59,14 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	drm_mode_config_init(drm);
 
+	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.funcs = &tegra_drm_mode_funcs;
+
 	err = tegra_drm_fb_prepare(drm);
 	if (err < 0)
 		goto config;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 5a0e96debcb1..286a970a4432 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -292,12 +292,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
 int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
 			struct tegra_bo_tiling *tiling);
+struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
+					struct drm_file *file,
+					struct drm_mode_fb_cmd2 *cmd);
 int tegra_drm_fb_prepare(struct drm_device *drm);
 void tegra_drm_fb_free(struct drm_device *drm);
 int tegra_drm_fb_init(struct drm_device *drm);
 void tegra_drm_fb_exit(struct drm_device *drm);
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
+void tegra_fb_output_poll_changed(struct drm_device *drm);
 #endif
 
 extern struct platform_driver tegra_dc_driver;
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index e9c715d89261..397fb34d5d5b 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -129,9 +129,9 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
 	return fb;
 }
 
-static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
-					       struct drm_file *file,
-					       struct drm_mode_fb_cmd2 *cmd)
+struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
+					struct drm_file *file,
+					struct drm_mode_fb_cmd2 *cmd)
 {
 	unsigned int hsub, vsub, i;
 	struct tegra_bo *planes[4];
@@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
 		drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
 }
 
-static void tegra_fb_output_poll_changed(struct drm_device *drm)
+void tegra_fb_output_poll_changed(struct drm_device *drm)
 {
 	struct tegra_drm *tegra = drm->dev_private;
 
@@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm)
 }
 #endif
 
-static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
-	.fb_create = tegra_fb_create,
-#ifdef CONFIG_DRM_TEGRA_FBDEV
-	.output_poll_changed = tegra_fb_output_poll_changed,
-#endif
-};
-
 int tegra_drm_fb_prepare(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	struct tegra_drm *tegra = drm->dev_private;
-#endif
 
-	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.funcs = &tegra_drm_mode_funcs;
-
-#ifdef CONFIG_DRM_TEGRA_FBDEV
 	tegra->fbdev = tegra_fbdev_create(drm);
 	if (IS_ERR(tegra->fbdev))
 		return PTR_ERR(tegra->fbdev);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 09/36] drm/tegra: Move tegra_drm_mode_funcs to the core
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This structure will be extended using non-framebuffer related callbacks
in subsequent patches, so it should move to a more central location.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.c | 15 +++++++++++++++
 drivers/gpu/drm/tegra/drm.h |  4 ++++
 drivers/gpu/drm/tegra/fb.c  | 25 ++++---------------------
 3 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 362fcb785f70..121e022bdab0 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -24,6 +24,13 @@ struct tegra_drm_file {
 	struct list_head contexts;
 };
 
+static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
+	.fb_create = tegra_fb_create,
+#ifdef CONFIG_DRM_TEGRA_FBDEV
+	.output_poll_changed = tegra_fb_output_poll_changed,
+#endif
+};
+
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 {
 	struct host1x_device *device = to_host1x_device(drm->dev);
@@ -52,6 +59,14 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	drm_mode_config_init(drm);
 
+	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.funcs = &tegra_drm_mode_funcs;
+
 	err = tegra_drm_fb_prepare(drm);
 	if (err < 0)
 		goto config;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 5a0e96debcb1..286a970a4432 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -292,12 +292,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
 int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
 			struct tegra_bo_tiling *tiling);
+struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
+					struct drm_file *file,
+					struct drm_mode_fb_cmd2 *cmd);
 int tegra_drm_fb_prepare(struct drm_device *drm);
 void tegra_drm_fb_free(struct drm_device *drm);
 int tegra_drm_fb_init(struct drm_device *drm);
 void tegra_drm_fb_exit(struct drm_device *drm);
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
+void tegra_fb_output_poll_changed(struct drm_device *drm);
 #endif
 
 extern struct platform_driver tegra_dc_driver;
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index e9c715d89261..397fb34d5d5b 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -129,9 +129,9 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
 	return fb;
 }
 
-static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
-					       struct drm_file *file,
-					       struct drm_mode_fb_cmd2 *cmd)
+struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
+					struct drm_file *file,
+					struct drm_mode_fb_cmd2 *cmd)
 {
 	unsigned int hsub, vsub, i;
 	struct tegra_bo *planes[4];
@@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
 		drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
 }
 
-static void tegra_fb_output_poll_changed(struct drm_device *drm)
+void tegra_fb_output_poll_changed(struct drm_device *drm)
 {
 	struct tegra_drm *tegra = drm->dev_private;
 
@@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm)
 }
 #endif
 
-static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
-	.fb_create = tegra_fb_create,
-#ifdef CONFIG_DRM_TEGRA_FBDEV
-	.output_poll_changed = tegra_fb_output_poll_changed,
-#endif
-};
-
 int tegra_drm_fb_prepare(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	struct tegra_drm *tegra = drm->dev_private;
-#endif
 
-	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.funcs = &tegra_drm_mode_funcs;
-
-#ifdef CONFIG_DRM_TEGRA_FBDEV
 	tegra->fbdev = tegra_fbdev_create(drm);
 	if (IS_ERR(tegra->fbdev))
 		return PTR_ERR(tegra->fbdev);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 10/36] drm/tegra: dc: No longer disable planes at CRTC disable
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The DRM core should take care of disabling all unneeded planes, so there
is no need to do this explicitly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 0c2972e4b41a..9cee6465b753 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1001,20 +1001,6 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct drm_device *drm = crtc->dev;
-	struct drm_plane *plane;
-
-	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
-		if (plane->crtc == crtc) {
-			tegra_window_plane_disable(plane);
-			plane->crtc = NULL;
-
-			if (plane->fb) {
-				drm_framebuffer_unreference(plane->fb);
-				plane->fb = NULL;
-			}
-		}
-	}
 
 	if (!tegra_dc_idle(dc)) {
 		tegra_dc_stop(dc);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 10/36] drm/tegra: dc: No longer disable planes at CRTC disable
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The DRM core should take care of disabling all unneeded planes, so there
is no need to do this explicitly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 0c2972e4b41a..9cee6465b753 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1001,20 +1001,6 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct drm_device *drm = crtc->dev;
-	struct drm_plane *plane;
-
-	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
-		if (plane->crtc == crtc) {
-			tegra_window_plane_disable(plane);
-			plane->crtc = NULL;
-
-			if (plane->fb) {
-				drm_framebuffer_unreference(plane->fb);
-				plane->fb = NULL;
-			}
-		}
-	}
 
 	if (!tegra_dc_idle(dc)) {
 		tegra_dc_stop(dc);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 11/36] drm/tegra: Convert output midlayer to helpers
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The output layer was initially designed to help reduce the amount of
code duplicated in output drivers. An unfortunate side-effect of that
was that it turned into a midlayer and it became difficult to make the
output drivers work without bending over backwards to fit into the
midlayer.

This commit starts to convert the midlayer into a helper library by
exporting most of the common functions so that they can be used by the
output drivers directly. Doing so will allow output drivers to reuse
common code paths but more easily override them where necessary.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |  9 +++++++++
 drivers/gpu/drm/tegra/output.c | 24 ++++++++++++------------
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 286a970a4432..bf749ac4a344 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -272,6 +272,15 @@ int tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 int tegra_output_exit(struct tegra_output *output);
 
+int tegra_output_connector_get_modes(struct drm_connector *connector);
+struct drm_encoder *
+tegra_output_connector_best_encoder(struct drm_connector *connector);
+enum drm_connector_status
+tegra_output_connector_detect(struct drm_connector *connector, bool force);
+void tegra_output_connector_destroy(struct drm_connector *connector);
+
+void tegra_output_encoder_destroy(struct drm_encoder *encoder);
+
 /* from dpaux.c */
 struct tegra_dpaux;
 struct drm_dp_link;
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index c40b38e4a859..bc82743904c1 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -12,7 +12,7 @@
 #include <drm/drm_panel.h>
 #include "drm.h"
 
-static int tegra_connector_get_modes(struct drm_connector *connector)
+int tegra_output_connector_get_modes(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	struct edid *edid = NULL;
@@ -57,8 +57,8 @@ static int tegra_connector_mode_valid(struct drm_connector *connector,
 	return status;
 }
 
-static struct drm_encoder *
-tegra_connector_best_encoder(struct drm_connector *connector)
+struct drm_encoder *
+tegra_output_connector_best_encoder(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
 
@@ -66,13 +66,13 @@ tegra_connector_best_encoder(struct drm_connector *connector)
 }
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = tegra_connector_get_modes,
+	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_connector_mode_valid,
-	.best_encoder = tegra_connector_best_encoder,
+	.best_encoder = tegra_output_connector_best_encoder,
 };
 
-static enum drm_connector_status
-tegra_connector_detect(struct drm_connector *connector, bool force)
+enum drm_connector_status
+tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	enum drm_connector_status status = connector_status_unknown;
@@ -98,7 +98,7 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
 	return status;
 }
 
-static void tegra_connector_destroy(struct drm_connector *connector)
+void tegra_output_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
@@ -106,18 +106,18 @@ static void tegra_connector_destroy(struct drm_connector *connector)
 
 static const struct drm_connector_funcs connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
-	.detect = tegra_connector_detect,
+	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = tegra_connector_destroy,
+	.destroy = tegra_output_connector_destroy,
 };
 
-static void tegra_encoder_destroy(struct drm_encoder *encoder)
+void tegra_output_encoder_destroy(struct drm_encoder *encoder)
 {
 	drm_encoder_cleanup(encoder);
 }
 
 static const struct drm_encoder_funcs encoder_funcs = {
-	.destroy = tegra_encoder_destroy,
+	.destroy = tegra_output_encoder_destroy,
 };
 
 static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 11/36] drm/tegra: Convert output midlayer to helpers
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The output layer was initially designed to help reduce the amount of
code duplicated in output drivers. An unfortunate side-effect of that
was that it turned into a midlayer and it became difficult to make the
output drivers work without bending over backwards to fit into the
midlayer.

This commit starts to convert the midlayer into a helper library by
exporting most of the common functions so that they can be used by the
output drivers directly. Doing so will allow output drivers to reuse
common code paths but more easily override them where necessary.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |  9 +++++++++
 drivers/gpu/drm/tegra/output.c | 24 ++++++++++++------------
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 286a970a4432..bf749ac4a344 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -272,6 +272,15 @@ int tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 int tegra_output_exit(struct tegra_output *output);
 
+int tegra_output_connector_get_modes(struct drm_connector *connector);
+struct drm_encoder *
+tegra_output_connector_best_encoder(struct drm_connector *connector);
+enum drm_connector_status
+tegra_output_connector_detect(struct drm_connector *connector, bool force);
+void tegra_output_connector_destroy(struct drm_connector *connector);
+
+void tegra_output_encoder_destroy(struct drm_encoder *encoder);
+
 /* from dpaux.c */
 struct tegra_dpaux;
 struct drm_dp_link;
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index c40b38e4a859..bc82743904c1 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -12,7 +12,7 @@
 #include <drm/drm_panel.h>
 #include "drm.h"
 
-static int tegra_connector_get_modes(struct drm_connector *connector)
+int tegra_output_connector_get_modes(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	struct edid *edid = NULL;
@@ -57,8 +57,8 @@ static int tegra_connector_mode_valid(struct drm_connector *connector,
 	return status;
 }
 
-static struct drm_encoder *
-tegra_connector_best_encoder(struct drm_connector *connector)
+struct drm_encoder *
+tegra_output_connector_best_encoder(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
 
@@ -66,13 +66,13 @@ tegra_connector_best_encoder(struct drm_connector *connector)
 }
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = tegra_connector_get_modes,
+	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_connector_mode_valid,
-	.best_encoder = tegra_connector_best_encoder,
+	.best_encoder = tegra_output_connector_best_encoder,
 };
 
-static enum drm_connector_status
-tegra_connector_detect(struct drm_connector *connector, bool force)
+enum drm_connector_status
+tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	enum drm_connector_status status = connector_status_unknown;
@@ -98,7 +98,7 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
 	return status;
 }
 
-static void tegra_connector_destroy(struct drm_connector *connector)
+void tegra_output_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
@@ -106,18 +106,18 @@ static void tegra_connector_destroy(struct drm_connector *connector)
 
 static const struct drm_connector_funcs connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
-	.detect = tegra_connector_detect,
+	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = tegra_connector_destroy,
+	.destroy = tegra_output_connector_destroy,
 };
 
-static void tegra_encoder_destroy(struct drm_encoder *encoder)
+void tegra_output_encoder_destroy(struct drm_encoder *encoder)
 {
 	drm_encoder_cleanup(encoder);
 }
 
 static const struct drm_encoder_funcs encoder_funcs = {
-	.destroy = tegra_encoder_destroy,
+	.destroy = tegra_output_encoder_destroy,
 };
 
 static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 12/36] drm/tegra: output: Make ->setup_clock() optional
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to transition output drivers to using the struct tegra_output
as a helper rather than midlayer, make this callback optional. Instead
drivers should implement the equivalent as part of ->mode_fixup(). For
the conversion to atomic modesetting a new callback ->atomic_check()
should be implemented that updates the display controller's state with
the corresponding parent clock, rate and shift clock divider.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 10 +++++++++-
 drivers/gpu/drm/tegra/drm.h | 10 ----------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 9cee6465b753..46fde25863a2 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1074,10 +1074,18 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
 		return -ENODEV;
 
 	/*
+	 * The ->setup_clock() callback is optional, but if encoders don't
+	 * implement it they most likely need to do the equivalent within the
+	 * ->mode_fixup() callback.
+	 */
+	if (!output->ops || !output->ops->setup_clock)
+		return 0;
+
+	/*
 	 * This assumes that the parent clock is pll_d_out0 or pll_d2_out
 	 * respectively, each of which divides the base pll_d by 2.
 	 */
-	err = tegra_output_setup_clock(output, dc->clk, pclk, &div);
+	err = output->ops->setup_clock(output, dc->clk, pclk, &div);
 	if (err < 0) {
 		dev_err(dc->dev, "failed to setup clock: %ld\n", err);
 		return err;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bf749ac4a344..d7433976a40b 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -240,16 +240,6 @@ static inline int tegra_output_disable(struct tegra_output *output)
 	return output ? -ENOSYS : -EINVAL;
 }
 
-static inline int tegra_output_setup_clock(struct tegra_output *output,
-					   struct clk *clk, unsigned long pclk,
-					   unsigned int *div)
-{
-	if (output && output->ops && output->ops->setup_clock)
-		return output->ops->setup_clock(output, clk, pclk, div);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
 static inline int tegra_output_check_mode(struct tegra_output *output,
 					  struct drm_display_mode *mode,
 					  enum drm_mode_status *status)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 12/36] drm/tegra: output: Make ->setup_clock() optional
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to transition output drivers to using the struct tegra_output
as a helper rather than midlayer, make this callback optional. Instead
drivers should implement the equivalent as part of ->mode_fixup(). For
the conversion to atomic modesetting a new callback ->atomic_check()
should be implemented that updates the display controller's state with
the corresponding parent clock, rate and shift clock divider.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 10 +++++++++-
 drivers/gpu/drm/tegra/drm.h | 10 ----------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 9cee6465b753..46fde25863a2 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1074,10 +1074,18 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
 		return -ENODEV;
 
 	/*
+	 * The ->setup_clock() callback is optional, but if encoders don't
+	 * implement it they most likely need to do the equivalent within the
+	 * ->mode_fixup() callback.
+	 */
+	if (!output->ops || !output->ops->setup_clock)
+		return 0;
+
+	/*
 	 * This assumes that the parent clock is pll_d_out0 or pll_d2_out
 	 * respectively, each of which divides the base pll_d by 2.
 	 */
-	err = tegra_output_setup_clock(output, dc->clk, pclk, &div);
+	err = output->ops->setup_clock(output, dc->clk, pclk, &div);
 	if (err < 0) {
 		dev_err(dc->dev, "failed to setup clock: %ld\n", err);
 		return err;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bf749ac4a344..d7433976a40b 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -240,16 +240,6 @@ static inline int tegra_output_disable(struct tegra_output *output)
 	return output ? -ENOSYS : -EINVAL;
 }
 
-static inline int tegra_output_setup_clock(struct tegra_output *output,
-					   struct clk *clk, unsigned long pclk,
-					   unsigned int *div)
-{
-	if (output && output->ops && output->ops->setup_clock)
-		return output->ops->setup_clock(output, clk, pclk, div);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
 static inline int tegra_output_check_mode(struct tegra_output *output,
 					  struct drm_display_mode *mode,
 					  enum drm_mode_status *status)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 13/36] drm/tegra: Add tegra_dc_setup_clock() helper
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This is a small helper that performs the basic steps required by all
output drivers to prepare the display controller for use with a given
encoder.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 20 ++++++++++++++++++++
 drivers/gpu/drm/tegra/drm.h |  2 ++
 2 files changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 46fde25863a2..39e1b3b38536 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1099,6 +1099,26 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
 	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;
+}
+
 static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted,
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index d7433976a40b..e1374ec2b76e 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -178,6 +178,8 @@ 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);
 
 struct tegra_output_ops {
 	int (*enable)(struct tegra_output *output);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 13/36] drm/tegra: Add tegra_dc_setup_clock() helper
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This is a small helper that performs the basic steps required by all
output drivers to prepare the display controller for use with a given
encoder.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 20 ++++++++++++++++++++
 drivers/gpu/drm/tegra/drm.h |  2 ++
 2 files changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 46fde25863a2..39e1b3b38536 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1099,6 +1099,26 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
 	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;
+}
+
 static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted,
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index d7433976a40b..e1374ec2b76e 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -178,6 +178,8 @@ 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);
 
 struct tegra_output_ops {
 	int (*enable)(struct tegra_output *output);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 14/36] drm/tegra: rgb: Demidlayer
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the RGB driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     |  24 ++++
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/output.c |   5 -
 drivers/gpu/drm/tegra/rgb.c    | 243 +++++++++++++++++++++++------------------
 4 files changed, 161 insertions(+), 112 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 39e1b3b38536..c51fc4db73db 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1001,6 +1001,7 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
+	u32 value;
 
 	if (!tegra_dc_idle(dc)) {
 		tegra_dc_stop(dc);
@@ -1012,6 +1013,29 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		tegra_dc_wait_idle(dc, 100);
 	}
 
+	/*
+	 * This should really be part of the RGB encoder driver, but clearing
+	 * these bits has the side-effect of stopping the display controller.
+	 * When that happens no VBLANK interrupts will be raised. At the same
+	 * time the encoder is disabled before the display controller, so the
+	 * above code is always going to timeout waiting for the controller
+	 * to go idle.
+	 *
+	 * Given the close coupling between the RGB encoder and the display
+	 * controller doing it here is still kind of okay. None of the other
+	 * encoder drivers require these bits to be cleared.
+	 *
+	 * XXX: Perhaps given that the display controller is switched off at
+	 * this point anyway maybe clearing these bits isn't even useful for
+	 * the RGB encoder?
+	 */
+	if (dc->rgb) {
+		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
+		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+	}
+
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
 }
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index e1374ec2b76e..dbc1f83327ea 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_RGB,
 	TEGRA_OUTPUT_HDMI,
 	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index bc82743904c1..9dd3c34d47fe 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_RGB:
-		connector = DRM_MODE_CONNECTOR_LVDS;
-		encoder = DRM_MODE_ENCODER_LVDS;
-		break;
-
 	case TEGRA_OUTPUT_HDMI:
 		connector = DRM_MODE_CONNECTOR_HDMIA;
 		encoder = DRM_MODE_ENCODER_TMDS;
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 39b8d5fe04b2..30d7ae02ace8 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -9,6 +9,8 @@
 
 #include <linux/clk.h>
 
+#include <drm/drm_panel.h>
+
 #include "drm.h"
 #include "dc.h"
 
@@ -85,13 +87,99 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
 		tegra_dc_writel(dc, table[i].value, table[i].offset);
 }
 
-static int tegra_output_rgb_enable(struct tegra_output *output)
+static void tegra_rgb_connector_dpms(struct drm_connector *connector,
+				     int mode)
+{
+}
+
+static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
+	.dpms = tegra_rgb_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static enum drm_mode_status
+tegra_rgb_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	/*
+	 * FIXME: For now, always assume that the mode is okay. There are
+	 * unresolved issues with clk_round_rate(), which doesn't always
+	 * reliably report whether a frequency can be set or not.
+	 */
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_rgb_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
+	unsigned long pclk = mode->clock * 1000;
 	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned long value;
+	unsigned int div;
+	int err;
 
-	if (rgb->enabled)
-		return 0;
+	/*
+	 * We may not want to change the frequency of the parent clock, since
+	 * it may be a parent for other peripherals. This is due to the fact
+	 * that on Tegra20 there's only a single clock dedicated to display
+	 * (pll_d_out0), whereas later generations have a second one that can
+	 * be used to independently drive a second output (pll_d2_out0).
+	 *
+	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
+	 * typically used as the parent clock for the display controllers.
+	 * But this comes at a cost: pll_p is the parent of several other
+	 * peripherals, so its frequency shouldn't change out of the blue.
+	 *
+	 * The best we can do at this point is to use the shift clock divider
+	 * and hope that the desired frequency can be matched (or at least
+	 * matched sufficiently close that the panel will still work).
+	 */
+	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
+
+	err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	return true;
+}
+
+static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_rgb_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_rgb *rgb = to_rgb(output);
+	u32 value;
+
+	if (output->panel)
+		drm_panel_prepare(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
 
@@ -125,88 +213,31 @@ static int tegra_output_rgb_enable(struct tegra_output *output)
 
 	tegra_dc_commit(rgb->dc);
 
-	rgb->enabled = true;
-
-	return 0;
+	if (output->panel)
+		drm_panel_enable(output->panel);
 }
 
-static int tegra_output_rgb_disable(struct tegra_output *output)
+static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned long value;
 
-	if (!rgb->enabled)
-		return 0;
-
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+	if (output->panel)
+		drm_panel_disable(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
-	tegra_dc_commit(rgb->dc);
-
-	rgb->enabled = false;
-
-	return 0;
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
 }
 
-static int tegra_output_rgb_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *div)
-{
-	struct tegra_rgb *rgb = to_rgb(output);
-	int err;
-
-	err = clk_set_parent(clk, rgb->clk_parent);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set parent: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * We may not want to change the frequency of the parent clock, since
-	 * it may be a parent for other peripherals. This is due to the fact
-	 * that on Tegra20 there's only a single clock dedicated to display
-	 * (pll_d_out0), whereas later generations have a second one that can
-	 * be used to independently drive a second output (pll_d2_out0).
-	 *
-	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
-	 * typically used as the parent clock for the display controllers.
-	 * But this comes at a cost: pll_p is the parent of several other
-	 * peripherals, so its frequency shouldn't change out of the blue.
-	 *
-	 * The best we can do at this point is to use the shift clock divider
-	 * and hope that the desired frequency can be matched (or at least
-	 * matched sufficiently close that the panel will still work).
-	 */
-
-	*div = ((clk_get_rate(clk) * 2) / pclk) - 2;
-
-	return 0;
-}
-
-static int tegra_output_rgb_check_mode(struct tegra_output *output,
-				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
-{
-	/*
-	 * FIXME: For now, always assume that the mode is okay. There are
-	 * unresolved issues with clk_round_rate(), which doesn't always
-	 * reliably report whether a frequency can be set or not.
-	 */
-
-	*status = MODE_OK;
-
-	return 0;
-}
-
-static const struct tegra_output_ops rgb_ops = {
-	.enable = tegra_output_rgb_enable,
-	.disable = tegra_output_rgb_disable,
-	.setup_clock = tegra_output_rgb_setup_clock,
-	.check_mode = tegra_output_rgb_check_mode,
+static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
+	.dpms = tegra_rgb_encoder_dpms,
+	.mode_fixup = tegra_rgb_encoder_mode_fixup,
+	.prepare = tegra_rgb_encoder_prepare,
+	.commit = tegra_rgb_encoder_commit,
+	.mode_set = tegra_rgb_encoder_mode_set,
+	.disable = tegra_rgb_encoder_disable,
 };
 
 int tegra_dc_rgb_probe(struct tegra_dc *dc)
@@ -265,55 +296,55 @@ int tegra_dc_rgb_remove(struct tegra_dc *dc)
 	if (err < 0)
 		return err;
 
+	dc->rgb = NULL;
+
 	return 0;
 }
 
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 {
-	struct tegra_rgb *rgb = to_rgb(dc->rgb);
+	struct tegra_output *output = dc->rgb;
 	int err;
 
 	if (!dc->rgb)
 		return -ENODEV;
 
-	rgb->output.type = TEGRA_OUTPUT_RGB;
-	rgb->output.ops = &rgb_ops;
-
-	err = tegra_output_init(dc->base.dev, &rgb->output);
-	if (err < 0) {
-		dev_err(dc->dev, "output setup failed: %d\n", err);
-		return err;
+	drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
+			   DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&output->connector,
+				 &tegra_rgb_connector_helper_funcs);
+	output->connector.dpms = DRM_MODE_DPMS_OFF;
+
+	if (output->panel) {
+		err = drm_panel_attach(output->panel, &output->connector);
+		if (err < 0)
+			dev_err(output->dev, "failed to attach panel: %d\n",
+				err);
 	}
 
+	drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&output->encoder,
+			       &tegra_rgb_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&output->connector,
+					  &output->encoder);
+	drm_connector_register(&output->connector);
+
 	/*
-	 * By default, outputs can be associated with each display controller.
-	 * RGB outputs are an exception, so we make sure they can be attached
-	 * to only their parent display controller.
+	 * Other outputs can be attached to either display controller. The RGB
+	 * outputs are an exception and work only with their parent display
+	 * controller.
 	 */
-	rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
+	output->encoder.possible_crtcs = drm_crtc_mask(&dc->base);
 
 	return 0;
 }
 
 int tegra_dc_rgb_exit(struct tegra_dc *dc)
 {
-	if (dc->rgb) {
-		int err;
-
-		err = tegra_output_disable(dc->rgb);
-		if (err < 0) {
-			dev_err(dc->dev, "output failed to disable: %d\n", err);
-			return err;
-		}
-
-		err = tegra_output_exit(dc->rgb);
-		if (err < 0) {
-			dev_err(dc->dev, "output cleanup failed: %d\n", err);
-			return err;
-		}
-
-		dc->rgb = NULL;
-	}
+	if (!dc->rgb)
+		return 0;
 
-	return 0;
+	return tegra_output_exit(dc->rgb);
 }
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 14/36] drm/tegra: rgb: Demidlayer
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the RGB driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     |  24 ++++
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/output.c |   5 -
 drivers/gpu/drm/tegra/rgb.c    | 243 +++++++++++++++++++++++------------------
 4 files changed, 161 insertions(+), 112 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 39e1b3b38536..c51fc4db73db 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1001,6 +1001,7 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
+	u32 value;
 
 	if (!tegra_dc_idle(dc)) {
 		tegra_dc_stop(dc);
@@ -1012,6 +1013,29 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 		tegra_dc_wait_idle(dc, 100);
 	}
 
+	/*
+	 * This should really be part of the RGB encoder driver, but clearing
+	 * these bits has the side-effect of stopping the display controller.
+	 * When that happens no VBLANK interrupts will be raised. At the same
+	 * time the encoder is disabled before the display controller, so the
+	 * above code is always going to timeout waiting for the controller
+	 * to go idle.
+	 *
+	 * Given the close coupling between the RGB encoder and the display
+	 * controller doing it here is still kind of okay. None of the other
+	 * encoder drivers require these bits to be cleared.
+	 *
+	 * XXX: Perhaps given that the display controller is switched off at
+	 * this point anyway maybe clearing these bits isn't even useful for
+	 * the RGB encoder?
+	 */
+	if (dc->rgb) {
+		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
+		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+	}
+
 	drm_crtc_vblank_off(crtc);
 	tegra_dc_commit(dc);
 }
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index e1374ec2b76e..dbc1f83327ea 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_RGB,
 	TEGRA_OUTPUT_HDMI,
 	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index bc82743904c1..9dd3c34d47fe 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_RGB:
-		connector = DRM_MODE_CONNECTOR_LVDS;
-		encoder = DRM_MODE_ENCODER_LVDS;
-		break;
-
 	case TEGRA_OUTPUT_HDMI:
 		connector = DRM_MODE_CONNECTOR_HDMIA;
 		encoder = DRM_MODE_ENCODER_TMDS;
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 39b8d5fe04b2..30d7ae02ace8 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -9,6 +9,8 @@
 
 #include <linux/clk.h>
 
+#include <drm/drm_panel.h>
+
 #include "drm.h"
 #include "dc.h"
 
@@ -85,13 +87,99 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
 		tegra_dc_writel(dc, table[i].value, table[i].offset);
 }
 
-static int tegra_output_rgb_enable(struct tegra_output *output)
+static void tegra_rgb_connector_dpms(struct drm_connector *connector,
+				     int mode)
+{
+}
+
+static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
+	.dpms = tegra_rgb_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static enum drm_mode_status
+tegra_rgb_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	/*
+	 * FIXME: For now, always assume that the mode is okay. There are
+	 * unresolved issues with clk_round_rate(), which doesn't always
+	 * reliably report whether a frequency can be set or not.
+	 */
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_rgb_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
+	unsigned long pclk = mode->clock * 1000;
 	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned long value;
+	unsigned int div;
+	int err;
 
-	if (rgb->enabled)
-		return 0;
+	/*
+	 * We may not want to change the frequency of the parent clock, since
+	 * it may be a parent for other peripherals. This is due to the fact
+	 * that on Tegra20 there's only a single clock dedicated to display
+	 * (pll_d_out0), whereas later generations have a second one that can
+	 * be used to independently drive a second output (pll_d2_out0).
+	 *
+	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
+	 * typically used as the parent clock for the display controllers.
+	 * But this comes at a cost: pll_p is the parent of several other
+	 * peripherals, so its frequency shouldn't change out of the blue.
+	 *
+	 * The best we can do at this point is to use the shift clock divider
+	 * and hope that the desired frequency can be matched (or at least
+	 * matched sufficiently close that the panel will still work).
+	 */
+	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
+
+	err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	return true;
+}
+
+static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_rgb_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_rgb *rgb = to_rgb(output);
+	u32 value;
+
+	if (output->panel)
+		drm_panel_prepare(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
 
@@ -125,88 +213,31 @@ static int tegra_output_rgb_enable(struct tegra_output *output)
 
 	tegra_dc_commit(rgb->dc);
 
-	rgb->enabled = true;
-
-	return 0;
+	if (output->panel)
+		drm_panel_enable(output->panel);
 }
 
-static int tegra_output_rgb_disable(struct tegra_output *output)
+static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned long value;
 
-	if (!rgb->enabled)
-		return 0;
-
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+	if (output->panel)
+		drm_panel_disable(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
 
-	tegra_dc_commit(rgb->dc);
-
-	rgb->enabled = false;
-
-	return 0;
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
 }
 
-static int tegra_output_rgb_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *div)
-{
-	struct tegra_rgb *rgb = to_rgb(output);
-	int err;
-
-	err = clk_set_parent(clk, rgb->clk_parent);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set parent: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * We may not want to change the frequency of the parent clock, since
-	 * it may be a parent for other peripherals. This is due to the fact
-	 * that on Tegra20 there's only a single clock dedicated to display
-	 * (pll_d_out0), whereas later generations have a second one that can
-	 * be used to independently drive a second output (pll_d2_out0).
-	 *
-	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
-	 * typically used as the parent clock for the display controllers.
-	 * But this comes at a cost: pll_p is the parent of several other
-	 * peripherals, so its frequency shouldn't change out of the blue.
-	 *
-	 * The best we can do at this point is to use the shift clock divider
-	 * and hope that the desired frequency can be matched (or at least
-	 * matched sufficiently close that the panel will still work).
-	 */
-
-	*div = ((clk_get_rate(clk) * 2) / pclk) - 2;
-
-	return 0;
-}
-
-static int tegra_output_rgb_check_mode(struct tegra_output *output,
-				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
-{
-	/*
-	 * FIXME: For now, always assume that the mode is okay. There are
-	 * unresolved issues with clk_round_rate(), which doesn't always
-	 * reliably report whether a frequency can be set or not.
-	 */
-
-	*status = MODE_OK;
-
-	return 0;
-}
-
-static const struct tegra_output_ops rgb_ops = {
-	.enable = tegra_output_rgb_enable,
-	.disable = tegra_output_rgb_disable,
-	.setup_clock = tegra_output_rgb_setup_clock,
-	.check_mode = tegra_output_rgb_check_mode,
+static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
+	.dpms = tegra_rgb_encoder_dpms,
+	.mode_fixup = tegra_rgb_encoder_mode_fixup,
+	.prepare = tegra_rgb_encoder_prepare,
+	.commit = tegra_rgb_encoder_commit,
+	.mode_set = tegra_rgb_encoder_mode_set,
+	.disable = tegra_rgb_encoder_disable,
 };
 
 int tegra_dc_rgb_probe(struct tegra_dc *dc)
@@ -265,55 +296,55 @@ int tegra_dc_rgb_remove(struct tegra_dc *dc)
 	if (err < 0)
 		return err;
 
+	dc->rgb = NULL;
+
 	return 0;
 }
 
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 {
-	struct tegra_rgb *rgb = to_rgb(dc->rgb);
+	struct tegra_output *output = dc->rgb;
 	int err;
 
 	if (!dc->rgb)
 		return -ENODEV;
 
-	rgb->output.type = TEGRA_OUTPUT_RGB;
-	rgb->output.ops = &rgb_ops;
-
-	err = tegra_output_init(dc->base.dev, &rgb->output);
-	if (err < 0) {
-		dev_err(dc->dev, "output setup failed: %d\n", err);
-		return err;
+	drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
+			   DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&output->connector,
+				 &tegra_rgb_connector_helper_funcs);
+	output->connector.dpms = DRM_MODE_DPMS_OFF;
+
+	if (output->panel) {
+		err = drm_panel_attach(output->panel, &output->connector);
+		if (err < 0)
+			dev_err(output->dev, "failed to attach panel: %d\n",
+				err);
 	}
 
+	drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&output->encoder,
+			       &tegra_rgb_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&output->connector,
+					  &output->encoder);
+	drm_connector_register(&output->connector);
+
 	/*
-	 * By default, outputs can be associated with each display controller.
-	 * RGB outputs are an exception, so we make sure they can be attached
-	 * to only their parent display controller.
+	 * Other outputs can be attached to either display controller. The RGB
+	 * outputs are an exception and work only with their parent display
+	 * controller.
 	 */
-	rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base);
+	output->encoder.possible_crtcs = drm_crtc_mask(&dc->base);
 
 	return 0;
 }
 
 int tegra_dc_rgb_exit(struct tegra_dc *dc)
 {
-	if (dc->rgb) {
-		int err;
-
-		err = tegra_output_disable(dc->rgb);
-		if (err < 0) {
-			dev_err(dc->dev, "output failed to disable: %d\n", err);
-			return err;
-		}
-
-		err = tegra_output_exit(dc->rgb);
-		if (err < 0) {
-			dev_err(dc->dev, "output cleanup failed: %d\n", err);
-			return err;
-		}
-
-		dc->rgb = NULL;
-	}
+	if (!dc->rgb)
+		return 0;
 
-	return 0;
+	return tegra_output_exit(dc->rgb);
 }
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 15/36] drm/tegra: hdmi: Demidlayer
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the HDMI driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/hdmi.c   | 280 +++++++++++++++++++++--------------------
 drivers/gpu/drm/tegra/output.c |   5 -
 3 files changed, 147 insertions(+), 139 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index dbc1f83327ea..95b6aebfaf00 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_HDMI,
 	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
 };
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index d4c635148cc7..056bb2c1c426 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -9,10 +9,14 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/hdmi.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -40,7 +44,6 @@ struct tegra_hdmi {
 	struct host1x_client client;
 	struct tegra_output output;
 	struct device *dev;
-	bool enabled;
 
 	struct regulator *hdmi;
 	struct regulator *pll;
@@ -768,53 +771,107 @@ static bool tegra_output_is_hdmi(struct tegra_output *output)
 	return drm_detect_hdmi_monitor(edid);
 }
 
-static int tegra_output_hdmi_enable(struct tegra_output *output)
+static void tegra_hdmi_connector_dpms(struct drm_connector *connector,
+				      int mode)
 {
-	unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+}
+
+static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
+	.dpms = tegra_hdmi_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static enum drm_mode_status
+tegra_hdmi_connector_mode_valid(struct drm_connector *connector,
+				struct drm_display_mode *mode)
+{
+	struct tegra_output *output = connector_to_output(connector);
 	struct tegra_hdmi *hdmi = to_hdmi(output);
-	struct device_node *node = hdmi->dev->of_node;
-	unsigned int pulse_start, div82, pclk;
-	int retries = 1000;
-	u32 value;
-	int err;
+	unsigned long pclk = mode->clock * 1000;
+	enum drm_mode_status status = MODE_OK;
+	struct clk *parent;
+	long err;
 
-	if (hdmi->enabled)
-		return 0;
+	parent = clk_get_parent(hdmi->clk_parent);
 
-	hdmi->dvi = !tegra_output_is_hdmi(output);
+	err = clk_round_rate(parent, pclk * 4);
+	if (err <= 0)
+		status = MODE_NOCLOCK;
 
-	pclk = mode->clock * 1000;
-	h_sync_width = mode->hsync_end - mode->hsync_start;
-	h_back_porch = mode->htotal - mode->hsync_end;
-	h_front_porch = mode->hsync_start - mode->hdisplay;
+	return status;
+}
 
-	err = regulator_enable(hdmi->pll);
+static const struct drm_connector_helper_funcs
+tegra_hdmi_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_hdmi_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					  const struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	unsigned long pclk = mode->clock * 1000;
+	int err;
+
+	err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0);
 	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
-		return err;
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
 	}
 
-	err = regulator_enable(hdmi->vdd);
+	err = clk_set_rate(hdmi->clk_parent, pclk);
 	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
-		return err;
+		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+			pclk);
+		return false;
 	}
 
-	err = clk_set_rate(hdmi->clk, pclk);
-	if (err < 0)
-		return err;
+	return true;
+}
 
-	err = clk_prepare_enable(hdmi->clk);
-	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
-		return err;
-	}
+static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+}
 
-	reset_control_assert(hdmi->rst);
-	usleep_range(1000, 2000);
-	reset_control_deassert(hdmi->rst);
+static void tegra_hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted)
+{
+	unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct device_node *node = output->dev->of_node;
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	unsigned int pulse_start, div82, pclk;
+	int retries = 1000;
+	u32 value;
+	int err;
+
+	hdmi->dvi = !tegra_output_is_hdmi(output);
+
+	pclk = mode->clock * 1000;
+	h_sync_width = mode->hsync_end - mode->hsync_start;
+	h_back_porch = mode->htotal - mode->hsync_end;
+	h_front_porch = mode->hsync_start - mode->hdisplay;
 
 	/* power up sequence */
 	value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
@@ -1000,104 +1057,33 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
 	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
-
-	hdmi->enabled = true;
-
-	return 0;
 }
 
-static int tegra_output_hdmi_disable(struct tegra_output *output)
+static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct tegra_hdmi *hdmi = to_hdmi(output);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	u32 value;
 
-	if (!hdmi->enabled)
-		return 0;
-
 	/*
 	 * The following accesses registers of the display controller, so make
 	 * sure it's only executed when the output is attached to one.
 	 */
 	if (dc) {
-		/*
-		 * XXX: We can't do this here because it causes HDMI to go
-		 * into an erroneous state with the result that HDMI won't
-		 * properly work once disabled. See also a similar symptom
-		 * for the SOR output.
-		 */
-		/*
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-		*/
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
 		tegra_dc_commit(dc);
 	}
-
-	clk_disable_unprepare(hdmi->clk);
-	reset_control_assert(hdmi->rst);
-	regulator_disable(hdmi->vdd);
-	regulator_disable(hdmi->pll);
-
-	hdmi->enabled = false;
-
-	return 0;
-}
-
-static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
-					 struct clk *clk, unsigned long pclk,
-					 unsigned int *div)
-{
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	int err;
-
-	err = clk_set_parent(clk, hdmi->clk_parent);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set parent: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(hdmi->clk_parent, pclk);
-	if (err < 0)
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-
-	*div = 0;
-
-	return 0;
-}
-
-static int tegra_output_hdmi_check_mode(struct tegra_output *output,
-					struct drm_display_mode *mode,
-					enum drm_mode_status *status)
-{
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	unsigned long pclk = mode->clock * 1000;
-	struct clk *parent;
-	long err;
-
-	parent = clk_get_parent(hdmi->clk_parent);
-
-	err = clk_round_rate(parent, pclk * 4);
-	if (err <= 0)
-		*status = MODE_NOCLOCK;
-	else
-		*status = MODE_OK;
-
-	return 0;
 }
 
-static const struct tegra_output_ops hdmi_ops = {
-	.enable = tegra_output_hdmi_enable,
-	.disable = tegra_output_hdmi_disable,
-	.setup_clock = tegra_output_hdmi_setup_clock,
-	.check_mode = tegra_output_hdmi_check_mode,
+static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
+	.dpms = tegra_hdmi_encoder_dpms,
+	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
+	.prepare = tegra_hdmi_encoder_prepare,
+	.commit = tegra_hdmi_encoder_commit,
+	.mode_set = tegra_hdmi_encoder_mode_set,
+	.disable = tegra_hdmi_encoder_disable,
 };
 
 static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
@@ -1345,15 +1331,28 @@ static int tegra_hdmi_init(struct host1x_client *client)
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
 	int err;
 
-	hdmi->output.type = TEGRA_OUTPUT_HDMI;
 	hdmi->output.dev = client->dev;
-	hdmi->output.ops = &hdmi_ops;
 
-	err = tegra_output_init(drm, &hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output setup failed: %d\n", err);
-		return err;
-	}
+	drm_connector_init(drm, &hdmi->output.connector,
+			   &tegra_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(&hdmi->output.connector,
+				 &tegra_hdmi_connector_helper_funcs);
+	hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&hdmi->output.encoder,
+			       &tegra_hdmi_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&hdmi->output.connector,
+					  &hdmi->output.encoder);
+	drm_connector_register(&hdmi->output.connector);
+
+	hdmi->output.encoder.possible_crtcs = 0x3;
+
+	if (gpio_is_valid(hdmi->output.hpd_gpio))
+		enable_irq(hdmi->output.hpd_irq);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
@@ -1368,6 +1367,26 @@ static int tegra_hdmi_init(struct host1x_client *client)
 		return err;
 	}
 
+	err = regulator_enable(hdmi->pll);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
+		return err;
+	}
+
+	err = regulator_enable(hdmi->vdd);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(hdmi->clk);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	reset_control_deassert(hdmi->rst);
+
 	return 0;
 }
 
@@ -1376,6 +1395,13 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
 	int err;
 
+	tegra_output_exit(&hdmi->output);
+
+	clk_disable_unprepare(hdmi->clk);
+	reset_control_assert(hdmi->rst);
+
+	regulator_disable(hdmi->vdd);
+	regulator_disable(hdmi->pll);
 	regulator_disable(hdmi->hdmi);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -1385,18 +1411,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 				err);
 	}
 
-	err = tegra_output_disable(&hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output failed to disable: %d\n", err);
-		return err;
-	}
-
-	err = tegra_output_exit(&hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output cleanup failed: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 9dd3c34d47fe..9cf851bf414b 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_HDMI:
-		connector = DRM_MODE_CONNECTOR_HDMIA;
-		encoder = DRM_MODE_ENCODER_TMDS;
-		break;
-
 	case TEGRA_OUTPUT_DSI:
 		connector = DRM_MODE_CONNECTOR_DSI;
 		encoder = DRM_MODE_ENCODER_DSI;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 15/36] drm/tegra: hdmi: Demidlayer
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the HDMI driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/hdmi.c   | 280 +++++++++++++++++++++--------------------
 drivers/gpu/drm/tegra/output.c |   5 -
 3 files changed, 147 insertions(+), 139 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index dbc1f83327ea..95b6aebfaf00 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_HDMI,
 	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
 };
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index d4c635148cc7..056bb2c1c426 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -9,10 +9,14 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/hdmi.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -40,7 +44,6 @@ struct tegra_hdmi {
 	struct host1x_client client;
 	struct tegra_output output;
 	struct device *dev;
-	bool enabled;
 
 	struct regulator *hdmi;
 	struct regulator *pll;
@@ -768,53 +771,107 @@ static bool tegra_output_is_hdmi(struct tegra_output *output)
 	return drm_detect_hdmi_monitor(edid);
 }
 
-static int tegra_output_hdmi_enable(struct tegra_output *output)
+static void tegra_hdmi_connector_dpms(struct drm_connector *connector,
+				      int mode)
 {
-	unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+}
+
+static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
+	.dpms = tegra_hdmi_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static enum drm_mode_status
+tegra_hdmi_connector_mode_valid(struct drm_connector *connector,
+				struct drm_display_mode *mode)
+{
+	struct tegra_output *output = connector_to_output(connector);
 	struct tegra_hdmi *hdmi = to_hdmi(output);
-	struct device_node *node = hdmi->dev->of_node;
-	unsigned int pulse_start, div82, pclk;
-	int retries = 1000;
-	u32 value;
-	int err;
+	unsigned long pclk = mode->clock * 1000;
+	enum drm_mode_status status = MODE_OK;
+	struct clk *parent;
+	long err;
 
-	if (hdmi->enabled)
-		return 0;
+	parent = clk_get_parent(hdmi->clk_parent);
 
-	hdmi->dvi = !tegra_output_is_hdmi(output);
+	err = clk_round_rate(parent, pclk * 4);
+	if (err <= 0)
+		status = MODE_NOCLOCK;
 
-	pclk = mode->clock * 1000;
-	h_sync_width = mode->hsync_end - mode->hsync_start;
-	h_back_porch = mode->htotal - mode->hsync_end;
-	h_front_porch = mode->hsync_start - mode->hdisplay;
+	return status;
+}
 
-	err = regulator_enable(hdmi->pll);
+static const struct drm_connector_helper_funcs
+tegra_hdmi_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_hdmi_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					  const struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	unsigned long pclk = mode->clock * 1000;
+	int err;
+
+	err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0);
 	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
-		return err;
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
 	}
 
-	err = regulator_enable(hdmi->vdd);
+	err = clk_set_rate(hdmi->clk_parent, pclk);
 	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
-		return err;
+		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+			pclk);
+		return false;
 	}
 
-	err = clk_set_rate(hdmi->clk, pclk);
-	if (err < 0)
-		return err;
+	return true;
+}
 
-	err = clk_prepare_enable(hdmi->clk);
-	if (err < 0) {
-		dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
-		return err;
-	}
+static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+}
 
-	reset_control_assert(hdmi->rst);
-	usleep_range(1000, 2000);
-	reset_control_deassert(hdmi->rst);
+static void tegra_hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted)
+{
+	unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct device_node *node = output->dev->of_node;
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	unsigned int pulse_start, div82, pclk;
+	int retries = 1000;
+	u32 value;
+	int err;
+
+	hdmi->dvi = !tegra_output_is_hdmi(output);
+
+	pclk = mode->clock * 1000;
+	h_sync_width = mode->hsync_end - mode->hsync_start;
+	h_back_porch = mode->htotal - mode->hsync_end;
+	h_front_porch = mode->hsync_start - mode->hdisplay;
 
 	/* power up sequence */
 	value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
@@ -1000,104 +1057,33 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
 	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
-
-	hdmi->enabled = true;
-
-	return 0;
 }
 
-static int tegra_output_hdmi_disable(struct tegra_output *output)
+static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct tegra_hdmi *hdmi = to_hdmi(output);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	u32 value;
 
-	if (!hdmi->enabled)
-		return 0;
-
 	/*
 	 * The following accesses registers of the display controller, so make
 	 * sure it's only executed when the output is attached to one.
 	 */
 	if (dc) {
-		/*
-		 * XXX: We can't do this here because it causes HDMI to go
-		 * into an erroneous state with the result that HDMI won't
-		 * properly work once disabled. See also a similar symptom
-		 * for the SOR output.
-		 */
-		/*
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-		*/
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~HDMI_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
 		tegra_dc_commit(dc);
 	}
-
-	clk_disable_unprepare(hdmi->clk);
-	reset_control_assert(hdmi->rst);
-	regulator_disable(hdmi->vdd);
-	regulator_disable(hdmi->pll);
-
-	hdmi->enabled = false;
-
-	return 0;
-}
-
-static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
-					 struct clk *clk, unsigned long pclk,
-					 unsigned int *div)
-{
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	int err;
-
-	err = clk_set_parent(clk, hdmi->clk_parent);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set parent: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(hdmi->clk_parent, pclk);
-	if (err < 0)
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-
-	*div = 0;
-
-	return 0;
-}
-
-static int tegra_output_hdmi_check_mode(struct tegra_output *output,
-					struct drm_display_mode *mode,
-					enum drm_mode_status *status)
-{
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	unsigned long pclk = mode->clock * 1000;
-	struct clk *parent;
-	long err;
-
-	parent = clk_get_parent(hdmi->clk_parent);
-
-	err = clk_round_rate(parent, pclk * 4);
-	if (err <= 0)
-		*status = MODE_NOCLOCK;
-	else
-		*status = MODE_OK;
-
-	return 0;
 }
 
-static const struct tegra_output_ops hdmi_ops = {
-	.enable = tegra_output_hdmi_enable,
-	.disable = tegra_output_hdmi_disable,
-	.setup_clock = tegra_output_hdmi_setup_clock,
-	.check_mode = tegra_output_hdmi_check_mode,
+static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
+	.dpms = tegra_hdmi_encoder_dpms,
+	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
+	.prepare = tegra_hdmi_encoder_prepare,
+	.commit = tegra_hdmi_encoder_commit,
+	.mode_set = tegra_hdmi_encoder_mode_set,
+	.disable = tegra_hdmi_encoder_disable,
 };
 
 static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
@@ -1345,15 +1331,28 @@ static int tegra_hdmi_init(struct host1x_client *client)
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
 	int err;
 
-	hdmi->output.type = TEGRA_OUTPUT_HDMI;
 	hdmi->output.dev = client->dev;
-	hdmi->output.ops = &hdmi_ops;
 
-	err = tegra_output_init(drm, &hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output setup failed: %d\n", err);
-		return err;
-	}
+	drm_connector_init(drm, &hdmi->output.connector,
+			   &tegra_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(&hdmi->output.connector,
+				 &tegra_hdmi_connector_helper_funcs);
+	hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&hdmi->output.encoder,
+			       &tegra_hdmi_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&hdmi->output.connector,
+					  &hdmi->output.encoder);
+	drm_connector_register(&hdmi->output.connector);
+
+	hdmi->output.encoder.possible_crtcs = 0x3;
+
+	if (gpio_is_valid(hdmi->output.hpd_gpio))
+		enable_irq(hdmi->output.hpd_irq);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
@@ -1368,6 +1367,26 @@ static int tegra_hdmi_init(struct host1x_client *client)
 		return err;
 	}
 
+	err = regulator_enable(hdmi->pll);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
+		return err;
+	}
+
+	err = regulator_enable(hdmi->vdd);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(hdmi->clk);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	reset_control_deassert(hdmi->rst);
+
 	return 0;
 }
 
@@ -1376,6 +1395,13 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
 	int err;
 
+	tegra_output_exit(&hdmi->output);
+
+	clk_disable_unprepare(hdmi->clk);
+	reset_control_assert(hdmi->rst);
+
+	regulator_disable(hdmi->vdd);
+	regulator_disable(hdmi->pll);
 	regulator_disable(hdmi->hdmi);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -1385,18 +1411,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 				err);
 	}
 
-	err = tegra_output_disable(&hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output failed to disable: %d\n", err);
-		return err;
-	}
-
-	err = tegra_output_exit(&hdmi->output);
-	if (err < 0) {
-		dev_err(client->dev, "output cleanup failed: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 9dd3c34d47fe..9cf851bf414b 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_HDMI:
-		connector = DRM_MODE_CONNECTOR_HDMIA;
-		encoder = DRM_MODE_ENCODER_TMDS;
-		break;
-
 	case TEGRA_OUTPUT_DSI:
 		connector = DRM_MODE_CONNECTOR_DSI;
 		encoder = DRM_MODE_ENCODER_DSI;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 16/36] drm/tegra: dsi: Demidlayer
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the DSI driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/dsi.c    | 358 ++++++++++++++++++++++-------------------
 drivers/gpu/drm/tegra/output.c |   5 -
 3 files changed, 195 insertions(+), 169 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 95b6aebfaf00..ae3daa436ee6 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
 };
 
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index a8c208dab698..817114ce42fb 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -51,7 +51,6 @@ struct tegra_dsi {
 	struct mipi_dsi_host host;
 
 	struct regulator *vdd;
-	bool enabled;
 
 	unsigned int video_fifo_depth;
 	unsigned int host_fifo_depth;
@@ -628,46 +627,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 	return 0;
 }
 
-static int tegra_output_dsi_enable(struct tegra_output *output)
-{
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	const struct drm_display_mode *mode = &dc->base.mode;
-	struct tegra_dsi *dsi = to_dsi(output);
-	u32 value;
-	int err;
-
-	if (dsi->enabled)
-		return 0;
-
-	err = tegra_dsi_configure(dsi, dc->pipe, mode);
-	if (err < 0)
-		return err;
-
-	/* enable display controller */
-	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-	value |= DSI_ENABLE;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-	tegra_dc_commit(dc);
-
-	/* enable DSI controller */
-	tegra_dsi_enable(dsi);
-
-	dsi->enabled = true;
-
-	return 0;
-}
-
 static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
 {
 	u32 value;
@@ -704,6 +663,29 @@ static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi)
 	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
 }
 
+static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
+				  unsigned int vrefresh)
+{
+	unsigned int timeout;
+	u32 value;
+
+	/* one frame high-speed transmission timeout */
+	timeout = (bclk / vrefresh) / 512;
+	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+
+	/* 2 ms peripheral timeout for panel */
+	timeout = 2 * bclk / 512 * 1000;
+	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+
+	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+	tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+
+	if (dsi->slave)
+		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+}
+
 static void tegra_dsi_disable(struct tegra_dsi *dsi)
 {
 	u32 value;
@@ -747,82 +729,51 @@ static void tegra_dsi_soft_reset(struct tegra_dsi *dsi)
 		tegra_dsi_soft_reset(dsi->slave);
 }
 
-static int tegra_output_dsi_disable(struct tegra_output *output)
+static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct tegra_dsi *dsi = to_dsi(output);
-	u32 value;
-	int err;
-
-	if (!dsi->enabled)
-		return 0;
-
-	tegra_dsi_video_disable(dsi);
-
-	/*
-	 * The following accesses registers of the display controller, so make
-	 * sure it's only executed when the output is attached to one.
-	 */
-	if (dc) {
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-		value &= ~DSI_ENABLE;
-		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-		tegra_dc_commit(dc);
-	}
-
-	err = tegra_dsi_wait_idle(dsi, 100);
-	if (err < 0)
-		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
-
-	tegra_dsi_soft_reset(dsi);
-	tegra_dsi_disable(dsi);
-
-	dsi->enabled = false;
-
-	return 0;
 }
 
-static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
-				  unsigned int vrefresh)
-{
-	unsigned int timeout;
-	u32 value;
+static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
+	.dpms = tegra_dsi_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
 
-	/* one frame high-speed transmission timeout */
-	timeout = (bclk / vrefresh) / 512;
-	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
-	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+static enum drm_mode_status
+tegra_dsi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
 
-	/* 2 ms peripheral timeout for panel */
-	timeout = 2 * bclk / 512 * 1000;
-	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
-	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_dsi_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
 
-	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
-	tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
 
-	if (dsi->slave)
-		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
 }
 
-static int tegra_output_dsi_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *divp)
+static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	unsigned int mul, div, scdiv, vrefresh, lanes;
 	struct tegra_dsi *dsi = to_dsi(output);
-	unsigned int mul, div, vrefresh, lanes;
-	unsigned long bclk, plld;
+	unsigned long pclk, bclk, plld;
 	int err;
 
 	lanes = tegra_dsi_get_lanes(dsi);
+	pclk = mode->clock * 1000;
 
 	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
 	if (err < 0)
@@ -847,19 +798,6 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
 	 */
 	plld /= 2;
 
-	err = clk_set_parent(clk, dsi->clk_parent);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set parent clock: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(dsi->clk_parent, plld);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
-			plld);
-		return err;
-	}
-
 	/*
 	 * Derive pixel clock from bit clock using the shift clock divider.
 	 * Note that this is only half of what we would expect, but we need
@@ -870,39 +808,132 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
 	 * not working properly otherwise. Perhaps the PLLs cannot generate
 	 * frequencies sufficiently high.
 	 */
-	*divp = ((8 * mul) / (div * lanes)) - 2;
+	scdiv = ((8 * mul) / (div * lanes)) - 2;
+
+	err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	err = clk_set_rate(dsi->clk_parent, plld);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n",
+			plld);
+		return false;
+	}
 
-	/*
-	 * XXX: Move the below somewhere else so that we don't need to have
-	 * access to the vrefresh in this function?
-	 */
 	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
 
 	err = tegra_dsi_set_phy_timing(dsi);
-	if (err < 0)
-		return err;
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err);
+		return false;
+	}
 
-	return 0;
+	return true;
+}
+
+static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_dsi_encoder_commit(struct drm_encoder *encoder)
+{
 }
 
-static int tegra_output_dsi_check_mode(struct tegra_output *output,
+static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	u32 value;
+	int err;
+
+
+	err = tegra_dsi_configure(dsi, dc->pipe, mode);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to configure DSI: %d\n", err);
+		return;
+	}
+
+	if (output->panel)
+		drm_panel_prepare(output->panel);
+
+	/* enable display controller */
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value |= DSI_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	value |= DISP_CTRL_MODE_C_DISPLAY;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+	tegra_dc_commit(dc);
+
+	/* enable DSI controller */
+	tegra_dsi_enable(dsi);
+
+	if (output->panel)
+		drm_panel_enable(output->panel);
+
+	return;
+}
+
+static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	u32 value;
+	int err;
+
+	if (output->panel)
+		drm_panel_disable(output->panel);
+
+	tegra_dsi_video_disable(dsi);
+
 	/*
-	 * FIXME: For now, always assume that the mode is okay.
+	 * The following accesses registers of the display controller, so make
+	 * sure it's only executed when the output is attached to one.
 	 */
+	if (dc) {
+		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+		value &= ~DSI_ENABLE;
+		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	*status = MODE_OK;
+		tegra_dc_commit(dc);
+	}
 
-	return 0;
+	err = tegra_dsi_wait_idle(dsi, 100);
+	if (err < 0)
+		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
+
+	tegra_dsi_soft_reset(dsi);
+
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
+
+	tegra_dsi_disable(dsi);
+
+	return;
 }
 
-static const struct tegra_output_ops dsi_ops = {
-	.enable = tegra_output_dsi_enable,
-	.disable = tegra_output_dsi_disable,
-	.setup_clock = tegra_output_dsi_setup_clock,
-	.check_mode = tegra_output_dsi_check_mode,
+static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
+	.dpms = tegra_dsi_encoder_dpms,
+	.mode_fixup = tegra_dsi_encoder_mode_fixup,
+	.prepare = tegra_dsi_encoder_prepare,
+	.commit = tegra_dsi_encoder_commit,
+	.mode_set = tegra_dsi_encoder_mode_set,
+	.disable = tegra_dsi_encoder_disable,
 };
 
 static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
@@ -952,15 +983,30 @@ static int tegra_dsi_init(struct host1x_client *client)
 
 	/* Gangsters must not register their own outputs. */
 	if (!dsi->master) {
-		dsi->output.type = TEGRA_OUTPUT_DSI;
 		dsi->output.dev = client->dev;
-		dsi->output.ops = &dsi_ops;
 
-		err = tegra_output_init(drm, &dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output setup failed: %d\n", err);
-			return err;
-		}
+		drm_connector_init(drm, &dsi->output.connector,
+				   &tegra_dsi_connector_funcs,
+				   DRM_MODE_CONNECTOR_DSI);
+		drm_connector_helper_add(&dsi->output.connector,
+					 &tegra_dsi_connector_helper_funcs);
+		dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+		if (dsi->output.panel)
+			drm_panel_attach(dsi->output.panel,
+					 &dsi->output.connector);
+
+		drm_encoder_init(drm, &dsi->output.encoder,
+				 &tegra_dsi_encoder_funcs,
+				 DRM_MODE_ENCODER_DSI);
+		drm_encoder_helper_add(&dsi->output.encoder,
+				       &tegra_dsi_encoder_helper_funcs);
+
+		drm_mode_connector_attach_encoder(&dsi->output.connector,
+						  &dsi->output.encoder);
+		drm_connector_register(&dsi->output.connector);
+
+		dsi->output.encoder.possible_crtcs = 0x3;
 	}
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -981,28 +1027,14 @@ static int tegra_dsi_exit(struct host1x_client *client)
 	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
 	int err;
 
+	tegra_output_exit(&dsi->output);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_dsi_debugfs_exit(dsi);
 		if (err < 0)
 			dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);
 	}
 
-	if (!dsi->master) {
-		err = tegra_output_disable(&dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output failed to disable: %d\n",
-				err);
-			return err;
-		}
-
-		err = tegra_output_exit(&dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output cleanup failed: %d\n",
-				err);
-			return err;
-		}
-	}
-
 	reset_control_assert(dsi->rst);
 
 	return 0;
@@ -1548,6 +1580,12 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 		return err;
 	}
 
+	err = tegra_output_remove(&dsi->output);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+		return err;
+	}
+
 	mipi_dsi_host_unregister(&dsi->host);
 	tegra_mipi_free(dsi->mipi);
 
@@ -1556,12 +1594,6 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 	clk_disable_unprepare(dsi->clk);
 	reset_control_assert(dsi->rst);
 
-	err = tegra_output_remove(&dsi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 9cf851bf414b..bc5393fd03c7 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_DSI:
-		connector = DRM_MODE_CONNECTOR_DSI;
-		encoder = DRM_MODE_ENCODER_DSI;
-		break;
-
 	case TEGRA_OUTPUT_EDP:
 		connector = DRM_MODE_CONNECTOR_eDP;
 		encoder = DRM_MODE_ENCODER_TMDS;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 16/36] drm/tegra: dsi: Demidlayer
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the DSI driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   1 -
 drivers/gpu/drm/tegra/dsi.c    | 358 ++++++++++++++++++++++-------------------
 drivers/gpu/drm/tegra/output.c |   5 -
 3 files changed, 195 insertions(+), 169 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 95b6aebfaf00..ae3daa436ee6 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-	TEGRA_OUTPUT_DSI,
 	TEGRA_OUTPUT_EDP,
 };
 
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index a8c208dab698..817114ce42fb 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -51,7 +51,6 @@ struct tegra_dsi {
 	struct mipi_dsi_host host;
 
 	struct regulator *vdd;
-	bool enabled;
 
 	unsigned int video_fifo_depth;
 	unsigned int host_fifo_depth;
@@ -628,46 +627,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 	return 0;
 }
 
-static int tegra_output_dsi_enable(struct tegra_output *output)
-{
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	const struct drm_display_mode *mode = &dc->base.mode;
-	struct tegra_dsi *dsi = to_dsi(output);
-	u32 value;
-	int err;
-
-	if (dsi->enabled)
-		return 0;
-
-	err = tegra_dsi_configure(dsi, dc->pipe, mode);
-	if (err < 0)
-		return err;
-
-	/* enable display controller */
-	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-	value |= DSI_ENABLE;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-	tegra_dc_commit(dc);
-
-	/* enable DSI controller */
-	tegra_dsi_enable(dsi);
-
-	dsi->enabled = true;
-
-	return 0;
-}
-
 static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
 {
 	u32 value;
@@ -704,6 +663,29 @@ static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi)
 	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
 }
 
+static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
+				  unsigned int vrefresh)
+{
+	unsigned int timeout;
+	u32 value;
+
+	/* one frame high-speed transmission timeout */
+	timeout = (bclk / vrefresh) / 512;
+	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+
+	/* 2 ms peripheral timeout for panel */
+	timeout = 2 * bclk / 512 * 1000;
+	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+
+	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+	tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+
+	if (dsi->slave)
+		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+}
+
 static void tegra_dsi_disable(struct tegra_dsi *dsi)
 {
 	u32 value;
@@ -747,82 +729,51 @@ static void tegra_dsi_soft_reset(struct tegra_dsi *dsi)
 		tegra_dsi_soft_reset(dsi->slave);
 }
 
-static int tegra_output_dsi_disable(struct tegra_output *output)
+static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct tegra_dsi *dsi = to_dsi(output);
-	u32 value;
-	int err;
-
-	if (!dsi->enabled)
-		return 0;
-
-	tegra_dsi_video_disable(dsi);
-
-	/*
-	 * The following accesses registers of the display controller, so make
-	 * sure it's only executed when the output is attached to one.
-	 */
-	if (dc) {
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-		value &= ~DSI_ENABLE;
-		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-		tegra_dc_commit(dc);
-	}
-
-	err = tegra_dsi_wait_idle(dsi, 100);
-	if (err < 0)
-		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
-
-	tegra_dsi_soft_reset(dsi);
-	tegra_dsi_disable(dsi);
-
-	dsi->enabled = false;
-
-	return 0;
 }
 
-static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
-				  unsigned int vrefresh)
-{
-	unsigned int timeout;
-	u32 value;
+static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
+	.dpms = tegra_dsi_connector_dpms,
+	.detect = tegra_output_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
 
-	/* one frame high-speed transmission timeout */
-	timeout = (bclk / vrefresh) / 512;
-	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
-	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+static enum drm_mode_status
+tegra_dsi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
 
-	/* 2 ms peripheral timeout for panel */
-	timeout = 2 * bclk / 512 * 1000;
-	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
-	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = {
+	.get_modes = tegra_output_connector_get_modes,
+	.mode_valid = tegra_dsi_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
 
-	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
-	tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
 
-	if (dsi->slave)
-		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
 }
 
-static int tegra_output_dsi_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *divp)
+static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	unsigned int mul, div, scdiv, vrefresh, lanes;
 	struct tegra_dsi *dsi = to_dsi(output);
-	unsigned int mul, div, vrefresh, lanes;
-	unsigned long bclk, plld;
+	unsigned long pclk, bclk, plld;
 	int err;
 
 	lanes = tegra_dsi_get_lanes(dsi);
+	pclk = mode->clock * 1000;
 
 	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
 	if (err < 0)
@@ -847,19 +798,6 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
 	 */
 	plld /= 2;
 
-	err = clk_set_parent(clk, dsi->clk_parent);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set parent clock: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(dsi->clk_parent, plld);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
-			plld);
-		return err;
-	}
-
 	/*
 	 * Derive pixel clock from bit clock using the shift clock divider.
 	 * Note that this is only half of what we would expect, but we need
@@ -870,39 +808,132 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
 	 * not working properly otherwise. Perhaps the PLLs cannot generate
 	 * frequencies sufficiently high.
 	 */
-	*divp = ((8 * mul) / (div * lanes)) - 2;
+	scdiv = ((8 * mul) / (div * lanes)) - 2;
+
+	err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	err = clk_set_rate(dsi->clk_parent, plld);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n",
+			plld);
+		return false;
+	}
 
-	/*
-	 * XXX: Move the below somewhere else so that we don't need to have
-	 * access to the vrefresh in this function?
-	 */
 	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
 
 	err = tegra_dsi_set_phy_timing(dsi);
-	if (err < 0)
-		return err;
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err);
+		return false;
+	}
 
-	return 0;
+	return true;
+}
+
+static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_dsi_encoder_commit(struct drm_encoder *encoder)
+{
 }
 
-static int tegra_output_dsi_check_mode(struct tegra_output *output,
+static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	u32 value;
+	int err;
+
+
+	err = tegra_dsi_configure(dsi, dc->pipe, mode);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to configure DSI: %d\n", err);
+		return;
+	}
+
+	if (output->panel)
+		drm_panel_prepare(output->panel);
+
+	/* enable display controller */
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value |= DSI_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	value |= DISP_CTRL_MODE_C_DISPLAY;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+	tegra_dc_commit(dc);
+
+	/* enable DSI controller */
+	tegra_dsi_enable(dsi);
+
+	if (output->panel)
+		drm_panel_enable(output->panel);
+
+	return;
+}
+
+static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
 {
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	u32 value;
+	int err;
+
+	if (output->panel)
+		drm_panel_disable(output->panel);
+
+	tegra_dsi_video_disable(dsi);
+
 	/*
-	 * FIXME: For now, always assume that the mode is okay.
+	 * The following accesses registers of the display controller, so make
+	 * sure it's only executed when the output is attached to one.
 	 */
+	if (dc) {
+		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+		value &= ~DSI_ENABLE;
+		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	*status = MODE_OK;
+		tegra_dc_commit(dc);
+	}
 
-	return 0;
+	err = tegra_dsi_wait_idle(dsi, 100);
+	if (err < 0)
+		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
+
+	tegra_dsi_soft_reset(dsi);
+
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
+
+	tegra_dsi_disable(dsi);
+
+	return;
 }
 
-static const struct tegra_output_ops dsi_ops = {
-	.enable = tegra_output_dsi_enable,
-	.disable = tegra_output_dsi_disable,
-	.setup_clock = tegra_output_dsi_setup_clock,
-	.check_mode = tegra_output_dsi_check_mode,
+static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
+	.dpms = tegra_dsi_encoder_dpms,
+	.mode_fixup = tegra_dsi_encoder_mode_fixup,
+	.prepare = tegra_dsi_encoder_prepare,
+	.commit = tegra_dsi_encoder_commit,
+	.mode_set = tegra_dsi_encoder_mode_set,
+	.disable = tegra_dsi_encoder_disable,
 };
 
 static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
@@ -952,15 +983,30 @@ static int tegra_dsi_init(struct host1x_client *client)
 
 	/* Gangsters must not register their own outputs. */
 	if (!dsi->master) {
-		dsi->output.type = TEGRA_OUTPUT_DSI;
 		dsi->output.dev = client->dev;
-		dsi->output.ops = &dsi_ops;
 
-		err = tegra_output_init(drm, &dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output setup failed: %d\n", err);
-			return err;
-		}
+		drm_connector_init(drm, &dsi->output.connector,
+				   &tegra_dsi_connector_funcs,
+				   DRM_MODE_CONNECTOR_DSI);
+		drm_connector_helper_add(&dsi->output.connector,
+					 &tegra_dsi_connector_helper_funcs);
+		dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+		if (dsi->output.panel)
+			drm_panel_attach(dsi->output.panel,
+					 &dsi->output.connector);
+
+		drm_encoder_init(drm, &dsi->output.encoder,
+				 &tegra_dsi_encoder_funcs,
+				 DRM_MODE_ENCODER_DSI);
+		drm_encoder_helper_add(&dsi->output.encoder,
+				       &tegra_dsi_encoder_helper_funcs);
+
+		drm_mode_connector_attach_encoder(&dsi->output.connector,
+						  &dsi->output.encoder);
+		drm_connector_register(&dsi->output.connector);
+
+		dsi->output.encoder.possible_crtcs = 0x3;
 	}
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -981,28 +1027,14 @@ static int tegra_dsi_exit(struct host1x_client *client)
 	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
 	int err;
 
+	tegra_output_exit(&dsi->output);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_dsi_debugfs_exit(dsi);
 		if (err < 0)
 			dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);
 	}
 
-	if (!dsi->master) {
-		err = tegra_output_disable(&dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output failed to disable: %d\n",
-				err);
-			return err;
-		}
-
-		err = tegra_output_exit(&dsi->output);
-		if (err < 0) {
-			dev_err(client->dev, "output cleanup failed: %d\n",
-				err);
-			return err;
-		}
-	}
-
 	reset_control_assert(dsi->rst);
 
 	return 0;
@@ -1548,6 +1580,12 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 		return err;
 	}
 
+	err = tegra_output_remove(&dsi->output);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+		return err;
+	}
+
 	mipi_dsi_host_unregister(&dsi->host);
 	tegra_mipi_free(dsi->mipi);
 
@@ -1556,12 +1594,6 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 	clk_disable_unprepare(dsi->clk);
 	reset_control_assert(dsi->rst);
 
-	err = tegra_output_remove(&dsi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 9cf851bf414b..bc5393fd03c7 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -276,11 +276,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	int connector, encoder;
 
 	switch (output->type) {
-	case TEGRA_OUTPUT_DSI:
-		connector = DRM_MODE_CONNECTOR_DSI;
-		encoder = DRM_MODE_ENCODER_DSI;
-		break;
-
 	case TEGRA_OUTPUT_EDP:
 		connector = DRM_MODE_CONNECTOR_eDP;
 		encoder = DRM_MODE_ENCODER_TMDS;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 17/36] drm/tegra: sor: Demidlayer
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the eDP driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   5 -
 drivers/gpu/drm/tegra/output.c |  15 +-
 drivers/gpu/drm/tegra/sor.c    | 810 ++++++++++++++++++++++-------------------
 3 files changed, 436 insertions(+), 394 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index ae3daa436ee6..c74d5db47537 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -192,16 +192,11 @@ struct tegra_output_ops {
 	enum drm_connector_status (*detect)(struct tegra_output *output);
 };
 
-enum tegra_output_type {
-	TEGRA_OUTPUT_EDP,
-};
-
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
 
 	const struct tegra_output_ops *ops;
-	enum tegra_output_type type;
 
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index bc5393fd03c7..042b421416a8 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -273,19 +273,8 @@ int tegra_output_remove(struct tegra_output *output)
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 {
-	int connector, encoder;
-
-	switch (output->type) {
-	case TEGRA_OUTPUT_EDP:
-		connector = DRM_MODE_CONNECTOR_eDP;
-		encoder = DRM_MODE_ENCODER_TMDS;
-		break;
-
-	default:
-		connector = DRM_MODE_CONNECTOR_Unknown;
-		encoder = DRM_MODE_ENCODER_NONE;
-		break;
-	}
+	int connector = DRM_MODE_CONNECTOR_Unknown;
+	int encoder = DRM_MODE_ENCODER_NONE;
 
 	drm_connector_init(drm, &output->connector, &connector_funcs,
 			   connector);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 1fe801ee8eb0..be1ad42c69be 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -8,6 +8,7 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
@@ -15,6 +16,7 @@
 #include <soc/tegra/pmc.h>
 
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
 
 #include "dc.h"
 #include "drm.h"
@@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor,
 	return 0;
 }
 
-static int tegra_output_sor_enable(struct tegra_output *output)
+static int tegra_sor_detach(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+
+	/* switch to safe mode */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if (value & SOR_PWR_MODE_SAFE)
+			break;
+	}
+
+	if ((value & SOR_PWR_MODE_SAFE) == 0)
+		return -ETIMEDOUT;
+
+	/* go to sleep */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	/* detach */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_ATTACHED;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_TEST);
+		if ((value & SOR_TEST_ATTACHED) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_TEST_ATTACHED) != 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int tegra_sor_power_down(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+	int err;
+
+	value = tegra_sor_readl(sor, SOR_PWR);
+	value &= ~SOR_PWR_NORMAL_STATE_PU;
+	value |= SOR_PWR_TRIGGER;
+	tegra_sor_writel(sor, value, SOR_PWR);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if ((value & SOR_PWR_TRIGGER) == 0)
+			return 0;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_PWR_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	err = clk_set_parent(sor->clk, sor->clk_safe);
+	if (err < 0)
+		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
+
+	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
+		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
+	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+	/* stop lane sequencer */
+	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
+		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
+	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
+		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_PORT_POWERDOWN;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	value = tegra_sor_readl(sor, SOR_PLL_0);
+	value |= SOR_PLL_0_POWER_OFF;
+	value |= SOR_PLL_0_VCOPD;
+	tegra_sor_writel(sor, value, SOR_PLL_0);
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_SEQ_PLLCAPPD;
+	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	return 0;
+}
+
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+	u32 value;
+
+	timeout = jiffies + msecs_to_jiffies(timeout);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_CRC_A);
+		if (value & SOR_CRC_A_VALID)
+			return 0;
+
+		usleep_range(100, 200);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+				  size_t size, loff_t *ppos)
+{
+	struct tegra_sor *sor = file->private_data;
+	ssize_t num, err;
+	char buf[10];
+	u32 value;
+
+	mutex_lock(&sor->lock);
+
+	if (!sor->enabled) {
+		err = -EAGAIN;
+		goto unlock;
+	}
+
+	value = tegra_sor_readl(sor, SOR_STATE_1);
+	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_STATE_1);
+
+	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+	value |= SOR_CRC_CNTRL_ENABLE;
+	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+	value = tegra_sor_readl(sor, SOR_TEST);
+	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+	tegra_sor_writel(sor, value, SOR_TEST);
+
+	err = tegra_sor_crc_wait(sor, 100);
+	if (err < 0)
+		goto unlock;
+
+	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+	value = tegra_sor_readl(sor, SOR_CRC_B);
+
+	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+	mutex_unlock(&sor->lock);
+	return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+	.owner = THIS_MODULE,
+	.open = tegra_sor_crc_open,
+	.read = tegra_sor_crc_read,
+	.release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor,
+				  struct drm_minor *minor)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+	struct dentry *entry;
+	int err = 0;
+
+	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
+	if (!sor->debugfs)
+		return -ENOMEM;
+
+	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+				    &tegra_sor_crc_fops);
+	if (!entry) {
+		dev_err(sor->dev,
+			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+			minor->debugfs_root->d_name.name);
+		err = -ENOMEM;
+		goto remove;
+	}
+
+	return err;
+
+remove:
+	debugfs_remove(sor->debugfs);
+	sor->debugfs = NULL;
+	return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+	debugfs_remove_recursive(sor->debugfs);
+	sor->debugfs = NULL;
+
+	return 0;
+}
+
+static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
+{
+}
+
+static enum drm_connector_status
+tegra_sor_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+
+	if (sor->dpaux)
+		return tegra_dpaux_detect(sor->dpaux);
+
+	return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs tegra_sor_connector_funcs = {
+	.dpms = tegra_sor_connector_dpms,
+	.detect = tegra_sor_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static int tegra_sor_connector_get_modes(struct drm_connector *connector)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	if (sor->dpaux)
+		tegra_dpaux_enable(sor->dpaux);
+
+	err = tegra_output_connector_get_modes(connector);
+
+	if (sor->dpaux)
+		tegra_dpaux_disable(sor->dpaux);
+
+	return err;
+}
+
+static enum drm_mode_status
+tegra_sor_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
+	.get_modes = tegra_sor_connector_get_modes,
+	.mode_valid = tegra_sor_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	unsigned long pclk = mode->clock * 1000;
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	err = clk_set_rate(sor->clk_parent, pclk);
+	if (err < 0) {
+		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+			pclk);
+		return false;
+	}
+
+	return true;
+}
+
+static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
 	struct tegra_sor *sor = to_sor(output);
 	struct tegra_sor_config config;
@@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 
 	reset_control_deassert(sor->rst);
 
+	if (output->panel)
+		drm_panel_prepare(output->panel);
+
 	/* FIXME: properly convert to struct drm_dp_aux */
 	aux = (struct drm_dp_aux *)sor->dpaux;
 
@@ -873,175 +1210,61 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
 
-	/* CSTM (LVDS, link A/B, upper) */
-	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
-		SOR_CSTM_UPPER;
-	tegra_sor_writel(sor, value, SOR_CSTM);
-
-	/* PWM setup */
-	err = tegra_sor_setup_pwm(sor, 250);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-		goto unlock;
-	}
-
-	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-	value |= SOR_ENABLE;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_sor_update(sor);
-
-	err = tegra_sor_attach(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-		goto unlock;
-	}
-
-	err = tegra_sor_wakeup(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to enable DC: %d\n", err);
-		goto unlock;
-	}
-
-	sor->enabled = true;
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_sor_detach(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-
-	/* switch to safe mode */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if (value & SOR_PWR_MODE_SAFE)
-			break;
-	}
-
-	if ((value & SOR_PWR_MODE_SAFE) == 0)
-		return -ETIMEDOUT;
-
-	/* go to sleep */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	/* detach */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_ATTACHED;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_TEST);
-		if ((value & SOR_TEST_ATTACHED) == 0)
-			break;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_TEST_ATTACHED) != 0)
-		return -ETIMEDOUT;
-
-	return 0;
-}
-
-static int tegra_sor_power_down(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-	int err;
-
-	value = tegra_sor_readl(sor, SOR_PWR);
-	value &= ~SOR_PWR_NORMAL_STATE_PU;
-	value |= SOR_PWR_TRIGGER;
-	tegra_sor_writel(sor, value, SOR_PWR);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if ((value & SOR_PWR_TRIGGER) == 0)
-			return 0;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_PWR_TRIGGER) != 0)
-		return -ETIMEDOUT;
-
-	err = clk_set_parent(sor->clk, sor->clk_safe);
-	if (err < 0)
-		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
-
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
-	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
-		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
-
-	/* stop lane sequencer */
-	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
-		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
-	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
-		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
-			break;
-
-		usleep_range(25, 100);
+	/* CSTM (LVDS, link A/B, upper) */
+	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
+		SOR_CSTM_UPPER;
+	tegra_sor_writel(sor, value, SOR_CSTM);
+
+	/* PWM setup */
+	err = tegra_sor_setup_pwm(sor, 250);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
+		goto unlock;
 	}
 
-	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
-		return -ETIMEDOUT;
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value |= SOR_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	tegra_sor_update(sor);
 
-	usleep_range(20, 100);
+	err = tegra_sor_attach(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_0);
-	value |= SOR_PLL_0_POWER_OFF;
-	value |= SOR_PLL_0_VCOPD;
-	tegra_sor_writel(sor, value, SOR_PLL_0);
+	err = tegra_sor_wakeup(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable DC: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_SEQ_PLLCAPPD;
-	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	if (output->panel)
+		drm_panel_enable(output->panel);
 
-	usleep_range(20, 100);
+	sor->enabled = true;
 
-	return 0;
+unlock:
+	mutex_unlock(&sor->lock);
 }
 
-static int tegra_output_sor_disable(struct tegra_output *output)
+static void tegra_sor_encoder_disable(struct drm_encoder *encoder)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_sor *sor = to_sor(output);
-	unsigned long value;
-	int err = 0;
+	u32 value;
+	int err;
 
 	mutex_lock(&sor->lock);
 
 	if (!sor->enabled)
 		goto unlock;
 
+	if (output->panel)
+		drm_panel_disable(output->panel);
+
 	err = tegra_sor_detach(sor);
 	if (err < 0) {
 		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
@@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 	 * sure it's only executed when the output is attached to one.
 	 */
 	if (dc) {
-		/*
-		 * XXX: We can't do this here because it causes the SOR to go
-		 * into an erroneous state and the output will look scrambled
-		 * the next time it is enabled. Presumably this is because we
-		 * should be doing this only on the next VBLANK. A possible
-		 * solution would be to queue a "power-off" event to trigger
-		 * this code to be run during the next VBLANK.
-		 */
-		/*
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-		*/
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		goto unlock;
 	}
 
-	reset_control_assert(sor->rst);
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
+
 	clk_disable_unprepare(sor->clk);
+	reset_control_assert(sor->rst);
 
 	sor->enabled = false;
 
 unlock:
 	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_output_sor_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *div)
-{
-	struct tegra_sor *sor = to_sor(output);
-	int err;
-
-	err = clk_set_parent(clk, sor->clk_parent);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set parent clock: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(sor->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
-		return err;
-	}
-
-	*div = 0;
-
-	return 0;
-}
-
-static int tegra_output_sor_check_mode(struct tegra_output *output,
-				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
-{
-	/*
-	 * FIXME: For now, always assume that the mode is okay.
-	 */
-
-	*status = MODE_OK;
-
-	return 0;
-}
-
-static enum drm_connector_status
-tegra_output_sor_detect(struct tegra_output *output)
-{
-	struct tegra_sor *sor = to_sor(output);
-
-	if (sor->dpaux)
-		return tegra_dpaux_detect(sor->dpaux);
-
-	return connector_status_unknown;
-}
-
-static const struct tegra_output_ops sor_ops = {
-	.enable = tegra_output_sor_enable,
-	.disable = tegra_output_sor_disable,
-	.setup_clock = tegra_output_sor_setup_clock,
-	.check_mode = tegra_output_sor_check_mode,
-	.detect = tegra_output_sor_detect,
-};
-
-static int tegra_sor_crc_open(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-
-	return 0;
-}
-
-static int tegra_sor_crc_release(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
-{
-	u32 value;
-
-	timeout = jiffies + msecs_to_jiffies(timeout);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_CRC_A);
-		if (value & SOR_CRC_A_VALID)
-			return 0;
-
-		usleep_range(100, 200);
-	}
-
-	return -ETIMEDOUT;
-}
-
-static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
-				  size_t size, loff_t *ppos)
-{
-	struct tegra_sor *sor = file->private_data;
-	ssize_t num, err;
-	char buf[10];
-	u32 value;
-
-	mutex_lock(&sor->lock);
-
-	if (!sor->enabled) {
-		err = -EAGAIN;
-		goto unlock;
-	}
-
-	value = tegra_sor_readl(sor, SOR_STATE_1);
-	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_STATE_1);
-
-	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
-	value |= SOR_CRC_CNTRL_ENABLE;
-	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
-
-	value = tegra_sor_readl(sor, SOR_TEST);
-	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
-	tegra_sor_writel(sor, value, SOR_TEST);
-
-	err = tegra_sor_crc_wait(sor, 100);
-	if (err < 0)
-		goto unlock;
-
-	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
-	value = tegra_sor_readl(sor, SOR_CRC_B);
-
-	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
-
-	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
 }
 
-static const struct file_operations tegra_sor_crc_fops = {
-	.owner = THIS_MODULE,
-	.open = tegra_sor_crc_open,
-	.read = tegra_sor_crc_read,
-	.release = tegra_sor_crc_release,
+static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
+	.dpms = tegra_sor_encoder_dpms,
+	.mode_fixup = tegra_sor_encoder_mode_fixup,
+	.prepare = tegra_sor_encoder_prepare,
+	.commit = tegra_sor_encoder_commit,
+	.mode_set = tegra_sor_encoder_mode_set,
+	.disable = tegra_sor_encoder_disable,
 };
 
-static int tegra_sor_debugfs_init(struct tegra_sor *sor,
-				  struct drm_minor *minor)
-{
-	struct dentry *entry;
-	int err = 0;
-
-	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
-	if (!sor->debugfs)
-		return -ENOMEM;
-
-	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
-				    &tegra_sor_crc_fops);
-	if (!entry) {
-		dev_err(sor->dev,
-			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
-			minor->debugfs_root->d_name.name);
-		err = -ENOMEM;
-		goto remove;
-	}
-
-	return err;
-
-remove:
-	debugfs_remove(sor->debugfs);
-	sor->debugfs = NULL;
-	return err;
-}
-
-static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
-{
-	debugfs_remove_recursive(sor->debugfs);
-	sor->debugfs = NULL;
-
-	return 0;
-}
-
 static int tegra_sor_init(struct host1x_client *client)
 {
 	struct drm_device *drm = dev_get_drvdata(client->parent);
@@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client)
 	if (!sor->dpaux)
 		return -ENODEV;
 
-	sor->output.type = TEGRA_OUTPUT_EDP;
-
 	sor->output.dev = sor->dev;
-	sor->output.ops = &sor_ops;
 
-	err = tegra_output_init(drm, &sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output setup failed: %d\n", err);
-		return err;
-	}
+	drm_connector_init(drm, &sor->output.connector,
+			   &tegra_sor_connector_funcs,
+			   DRM_MODE_CONNECTOR_eDP);
+	drm_connector_helper_add(&sor->output.connector,
+				 &tegra_sor_connector_helper_funcs);
+	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	if (sor->output.panel)
+		drm_panel_attach(sor->output.panel, &sor->output.connector);
+
+	drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&sor->output.encoder,
+			       &tegra_sor_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&sor->output.connector,
+					  &sor->output.encoder);
+	drm_connector_register(&sor->output.connector);
+
+	sor->output.encoder.possible_crtcs = 0x3;
+
+	if (gpio_is_valid(sor->output.hpd_gpio))
+		enable_irq(sor->output.hpd_irq);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_init(sor, drm->primary);
@@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client)
 		}
 	}
 
+	err = clk_prepare_enable(sor->clk);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(sor->clk_safe);
+	if (err < 0)
+		return err;
+
+	err = clk_prepare_enable(sor->clk_dp);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client)
 	struct tegra_sor *sor = host1x_client_to_sor(client);
 	int err;
 
-	err = tegra_output_disable(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output failed to disable: %d\n", err);
-		return err;
-	}
-
 	if (sor->dpaux) {
 		err = tegra_dpaux_detach(sor->dpaux);
 		if (err < 0) {
@@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client)
 		}
 	}
 
+	clk_disable_unprepare(sor->clk_safe);
+	clk_disable_unprepare(sor->clk_dp);
+	clk_disable_unprepare(sor->clk);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_exit(sor);
 		if (err < 0)
 			dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
 	}
 
-	err = tegra_output_exit(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output cleanup failed: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
@@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev)
 	if (IS_ERR(sor->clk_parent))
 		return PTR_ERR(sor->clk_parent);
 
-	err = clk_prepare_enable(sor->clk_parent);
-	if (err < 0)
-		return err;
-
 	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
 	if (IS_ERR(sor->clk_safe))
 		return PTR_ERR(sor->clk_safe);
 
-	err = clk_prepare_enable(sor->clk_safe);
-	if (err < 0)
-		return err;
-
 	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
 	if (IS_ERR(sor->clk_dp))
 		return PTR_ERR(sor->clk_dp);
 
-	err = clk_prepare_enable(sor->clk_dp);
-	if (err < 0)
-		return err;
-
 	INIT_LIST_HEAD(&sor->client.list);
 	sor->client.ops = &sor_client_ops;
 	sor->client.dev = &pdev->dev;
@@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	clk_disable_unprepare(sor->clk_parent);
-	clk_disable_unprepare(sor->clk_safe);
-	clk_disable_unprepare(sor->clk_dp);
-	clk_disable_unprepare(sor->clk);
+	err = tegra_output_remove(&sor->output);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+		return err;
+	}
 
 	return 0;
 }
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 17/36] drm/tegra: sor: Demidlayer
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement encoder and connector within the eDP driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |   5 -
 drivers/gpu/drm/tegra/output.c |  15 +-
 drivers/gpu/drm/tegra/sor.c    | 810 ++++++++++++++++++++++-------------------
 3 files changed, 436 insertions(+), 394 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index ae3daa436ee6..c74d5db47537 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -192,16 +192,11 @@ struct tegra_output_ops {
 	enum drm_connector_status (*detect)(struct tegra_output *output);
 };
 
-enum tegra_output_type {
-	TEGRA_OUTPUT_EDP,
-};
-
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
 
 	const struct tegra_output_ops *ops;
-	enum tegra_output_type type;
 
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index bc5393fd03c7..042b421416a8 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -273,19 +273,8 @@ int tegra_output_remove(struct tegra_output *output)
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 {
-	int connector, encoder;
-
-	switch (output->type) {
-	case TEGRA_OUTPUT_EDP:
-		connector = DRM_MODE_CONNECTOR_eDP;
-		encoder = DRM_MODE_ENCODER_TMDS;
-		break;
-
-	default:
-		connector = DRM_MODE_CONNECTOR_Unknown;
-		encoder = DRM_MODE_ENCODER_NONE;
-		break;
-	}
+	int connector = DRM_MODE_CONNECTOR_Unknown;
+	int encoder = DRM_MODE_ENCODER_NONE;
 
 	drm_connector_init(drm, &output->connector, &connector_funcs,
 			   connector);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 1fe801ee8eb0..be1ad42c69be 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -8,6 +8,7 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
@@ -15,6 +16,7 @@
 #include <soc/tegra/pmc.h>
 
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
 
 #include "dc.h"
 #include "drm.h"
@@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor,
 	return 0;
 }
 
-static int tegra_output_sor_enable(struct tegra_output *output)
+static int tegra_sor_detach(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+
+	/* switch to safe mode */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if (value & SOR_PWR_MODE_SAFE)
+			break;
+	}
+
+	if ((value & SOR_PWR_MODE_SAFE) == 0)
+		return -ETIMEDOUT;
+
+	/* go to sleep */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	/* detach */
+	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+	value &= ~SOR_SUPER_STATE_ATTACHED;
+	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+	tegra_sor_super_update(sor);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_TEST);
+		if ((value & SOR_TEST_ATTACHED) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_TEST_ATTACHED) != 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int tegra_sor_power_down(struct tegra_sor *sor)
+{
+	unsigned long value, timeout;
+	int err;
+
+	value = tegra_sor_readl(sor, SOR_PWR);
+	value &= ~SOR_PWR_NORMAL_STATE_PU;
+	value |= SOR_PWR_TRIGGER;
+	tegra_sor_writel(sor, value, SOR_PWR);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_PWR);
+		if ((value & SOR_PWR_TRIGGER) == 0)
+			return 0;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_PWR_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	err = clk_set_parent(sor->clk, sor->clk_safe);
+	if (err < 0)
+		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
+
+	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
+		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
+	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+	/* stop lane sequencer */
+	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
+		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
+	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
+
+	timeout = jiffies + msecs_to_jiffies(250);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
+		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
+			break;
+
+		usleep_range(25, 100);
+	}
+
+	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
+		return -ETIMEDOUT;
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_PORT_POWERDOWN;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	value = tegra_sor_readl(sor, SOR_PLL_0);
+	value |= SOR_PLL_0_POWER_OFF;
+	value |= SOR_PLL_0_VCOPD;
+	tegra_sor_writel(sor, value, SOR_PLL_0);
+
+	value = tegra_sor_readl(sor, SOR_PLL_2);
+	value |= SOR_PLL_2_SEQ_PLLCAPPD;
+	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+	tegra_sor_writel(sor, value, SOR_PLL_2);
+
+	usleep_range(20, 100);
+
+	return 0;
+}
+
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+	u32 value;
+
+	timeout = jiffies + msecs_to_jiffies(timeout);
+
+	while (time_before(jiffies, timeout)) {
+		value = tegra_sor_readl(sor, SOR_CRC_A);
+		if (value & SOR_CRC_A_VALID)
+			return 0;
+
+		usleep_range(100, 200);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+				  size_t size, loff_t *ppos)
+{
+	struct tegra_sor *sor = file->private_data;
+	ssize_t num, err;
+	char buf[10];
+	u32 value;
+
+	mutex_lock(&sor->lock);
+
+	if (!sor->enabled) {
+		err = -EAGAIN;
+		goto unlock;
+	}
+
+	value = tegra_sor_readl(sor, SOR_STATE_1);
+	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+	tegra_sor_writel(sor, value, SOR_STATE_1);
+
+	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+	value |= SOR_CRC_CNTRL_ENABLE;
+	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+	value = tegra_sor_readl(sor, SOR_TEST);
+	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+	tegra_sor_writel(sor, value, SOR_TEST);
+
+	err = tegra_sor_crc_wait(sor, 100);
+	if (err < 0)
+		goto unlock;
+
+	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+	value = tegra_sor_readl(sor, SOR_CRC_B);
+
+	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+	mutex_unlock(&sor->lock);
+	return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+	.owner = THIS_MODULE,
+	.open = tegra_sor_crc_open,
+	.read = tegra_sor_crc_read,
+	.release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor,
+				  struct drm_minor *minor)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-	struct drm_display_mode *mode = &dc->base.mode;
+	struct dentry *entry;
+	int err = 0;
+
+	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
+	if (!sor->debugfs)
+		return -ENOMEM;
+
+	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+				    &tegra_sor_crc_fops);
+	if (!entry) {
+		dev_err(sor->dev,
+			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+			minor->debugfs_root->d_name.name);
+		err = -ENOMEM;
+		goto remove;
+	}
+
+	return err;
+
+remove:
+	debugfs_remove(sor->debugfs);
+	sor->debugfs = NULL;
+	return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+	debugfs_remove_recursive(sor->debugfs);
+	sor->debugfs = NULL;
+
+	return 0;
+}
+
+static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
+{
+}
+
+static enum drm_connector_status
+tegra_sor_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+
+	if (sor->dpaux)
+		return tegra_dpaux_detect(sor->dpaux);
+
+	return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs tegra_sor_connector_funcs = {
+	.dpms = tegra_sor_connector_dpms,
+	.detect = tegra_sor_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_output_connector_destroy,
+};
+
+static int tegra_sor_connector_get_modes(struct drm_connector *connector)
+{
+	struct tegra_output *output = connector_to_output(connector);
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	if (sor->dpaux)
+		tegra_dpaux_enable(sor->dpaux);
+
+	err = tegra_output_connector_get_modes(connector);
+
+	if (sor->dpaux)
+		tegra_dpaux_disable(sor->dpaux);
+
+	return err;
+}
+
+static enum drm_mode_status
+tegra_sor_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
+	.get_modes = tegra_sor_connector_get_modes,
+	.mode_valid = tegra_sor_connector_mode_valid,
+	.best_encoder = tegra_output_connector_best_encoder,
+};
+
+static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
+	.destroy = tegra_output_encoder_destroy,
+};
+
+static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	unsigned long pclk = mode->clock * 1000;
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+		return false;
+	}
+
+	err = clk_set_rate(sor->clk_parent, pclk);
+	if (err < 0) {
+		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+			pclk);
+		return false;
+	}
+
+	return true;
+}
+
+static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
 	struct tegra_sor *sor = to_sor(output);
 	struct tegra_sor_config config;
@@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 
 	reset_control_deassert(sor->rst);
 
+	if (output->panel)
+		drm_panel_prepare(output->panel);
+
 	/* FIXME: properly convert to struct drm_dp_aux */
 	aux = (struct drm_dp_aux *)sor->dpaux;
 
@@ -873,175 +1210,61 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
 	tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
 
-	/* CSTM (LVDS, link A/B, upper) */
-	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
-		SOR_CSTM_UPPER;
-	tegra_sor_writel(sor, value, SOR_CSTM);
-
-	/* PWM setup */
-	err = tegra_sor_setup_pwm(sor, 250);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-		goto unlock;
-	}
-
-	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-	value |= SOR_ENABLE;
-	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_sor_update(sor);
-
-	err = tegra_sor_attach(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-		goto unlock;
-	}
-
-	err = tegra_sor_wakeup(sor);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to enable DC: %d\n", err);
-		goto unlock;
-	}
-
-	sor->enabled = true;
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_sor_detach(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-
-	/* switch to safe mode */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if (value & SOR_PWR_MODE_SAFE)
-			break;
-	}
-
-	if ((value & SOR_PWR_MODE_SAFE) == 0)
-		return -ETIMEDOUT;
-
-	/* go to sleep */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	/* detach */
-	value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
-	value &= ~SOR_SUPER_STATE_ATTACHED;
-	tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
-	tegra_sor_super_update(sor);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_TEST);
-		if ((value & SOR_TEST_ATTACHED) == 0)
-			break;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_TEST_ATTACHED) != 0)
-		return -ETIMEDOUT;
-
-	return 0;
-}
-
-static int tegra_sor_power_down(struct tegra_sor *sor)
-{
-	unsigned long value, timeout;
-	int err;
-
-	value = tegra_sor_readl(sor, SOR_PWR);
-	value &= ~SOR_PWR_NORMAL_STATE_PU;
-	value |= SOR_PWR_TRIGGER;
-	tegra_sor_writel(sor, value, SOR_PWR);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_PWR);
-		if ((value & SOR_PWR_TRIGGER) == 0)
-			return 0;
-
-		usleep_range(25, 100);
-	}
-
-	if ((value & SOR_PWR_TRIGGER) != 0)
-		return -ETIMEDOUT;
-
-	err = clk_set_parent(sor->clk, sor->clk_safe);
-	if (err < 0)
-		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
-
-	value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
-	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
-		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
-	tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
-
-	/* stop lane sequencer */
-	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
-		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
-	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
-
-	timeout = jiffies + msecs_to_jiffies(250);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
-		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
-			break;
-
-		usleep_range(25, 100);
+	/* CSTM (LVDS, link A/B, upper) */
+	value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
+		SOR_CSTM_UPPER;
+	tegra_sor_writel(sor, value, SOR_CSTM);
+
+	/* PWM setup */
+	err = tegra_sor_setup_pwm(sor, 250);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to setup PWM: %d\n", err);
+		goto unlock;
 	}
 
-	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
-		return -ETIMEDOUT;
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value |= SOR_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_PORT_POWERDOWN;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	tegra_sor_update(sor);
 
-	usleep_range(20, 100);
+	err = tegra_sor_attach(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_0);
-	value |= SOR_PLL_0_POWER_OFF;
-	value |= SOR_PLL_0_VCOPD;
-	tegra_sor_writel(sor, value, SOR_PLL_0);
+	err = tegra_sor_wakeup(sor);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable DC: %d\n", err);
+		goto unlock;
+	}
 
-	value = tegra_sor_readl(sor, SOR_PLL_2);
-	value |= SOR_PLL_2_SEQ_PLLCAPPD;
-	value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
-	tegra_sor_writel(sor, value, SOR_PLL_2);
+	if (output->panel)
+		drm_panel_enable(output->panel);
 
-	usleep_range(20, 100);
+	sor->enabled = true;
 
-	return 0;
+unlock:
+	mutex_unlock(&sor->lock);
 }
 
-static int tegra_output_sor_disable(struct tegra_output *output)
+static void tegra_sor_encoder_disable(struct drm_encoder *encoder)
 {
-	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_sor *sor = to_sor(output);
-	unsigned long value;
-	int err = 0;
+	u32 value;
+	int err;
 
 	mutex_lock(&sor->lock);
 
 	if (!sor->enabled)
 		goto unlock;
 
+	if (output->panel)
+		drm_panel_disable(output->panel);
+
 	err = tegra_sor_detach(sor);
 	if (err < 0) {
 		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
@@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 	 * sure it's only executed when the output is attached to one.
 	 */
 	if (dc) {
-		/*
-		 * XXX: We can't do this here because it causes the SOR to go
-		 * into an erroneous state and the output will look scrambled
-		 * the next time it is enabled. Presumably this is because we
-		 * should be doing this only on the next VBLANK. A possible
-		 * solution would be to queue a "power-off" event to trigger
-		 * this code to be run during the next VBLANK.
-		 */
-		/*
-		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
-		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-		*/
-
 		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 		value &= ~SOR_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 		goto unlock;
 	}
 
-	reset_control_assert(sor->rst);
+	if (output->panel)
+		drm_panel_unprepare(output->panel);
+
 	clk_disable_unprepare(sor->clk);
+	reset_control_assert(sor->rst);
 
 	sor->enabled = false;
 
 unlock:
 	mutex_unlock(&sor->lock);
-	return err;
-}
-
-static int tegra_output_sor_setup_clock(struct tegra_output *output,
-					struct clk *clk, unsigned long pclk,
-					unsigned int *div)
-{
-	struct tegra_sor *sor = to_sor(output);
-	int err;
-
-	err = clk_set_parent(clk, sor->clk_parent);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set parent clock: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(sor->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
-		return err;
-	}
-
-	*div = 0;
-
-	return 0;
-}
-
-static int tegra_output_sor_check_mode(struct tegra_output *output,
-				       struct drm_display_mode *mode,
-				       enum drm_mode_status *status)
-{
-	/*
-	 * FIXME: For now, always assume that the mode is okay.
-	 */
-
-	*status = MODE_OK;
-
-	return 0;
-}
-
-static enum drm_connector_status
-tegra_output_sor_detect(struct tegra_output *output)
-{
-	struct tegra_sor *sor = to_sor(output);
-
-	if (sor->dpaux)
-		return tegra_dpaux_detect(sor->dpaux);
-
-	return connector_status_unknown;
-}
-
-static const struct tegra_output_ops sor_ops = {
-	.enable = tegra_output_sor_enable,
-	.disable = tegra_output_sor_disable,
-	.setup_clock = tegra_output_sor_setup_clock,
-	.check_mode = tegra_output_sor_check_mode,
-	.detect = tegra_output_sor_detect,
-};
-
-static int tegra_sor_crc_open(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-
-	return 0;
-}
-
-static int tegra_sor_crc_release(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
-{
-	u32 value;
-
-	timeout = jiffies + msecs_to_jiffies(timeout);
-
-	while (time_before(jiffies, timeout)) {
-		value = tegra_sor_readl(sor, SOR_CRC_A);
-		if (value & SOR_CRC_A_VALID)
-			return 0;
-
-		usleep_range(100, 200);
-	}
-
-	return -ETIMEDOUT;
-}
-
-static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
-				  size_t size, loff_t *ppos)
-{
-	struct tegra_sor *sor = file->private_data;
-	ssize_t num, err;
-	char buf[10];
-	u32 value;
-
-	mutex_lock(&sor->lock);
-
-	if (!sor->enabled) {
-		err = -EAGAIN;
-		goto unlock;
-	}
-
-	value = tegra_sor_readl(sor, SOR_STATE_1);
-	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
-	tegra_sor_writel(sor, value, SOR_STATE_1);
-
-	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
-	value |= SOR_CRC_CNTRL_ENABLE;
-	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
-
-	value = tegra_sor_readl(sor, SOR_TEST);
-	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
-	tegra_sor_writel(sor, value, SOR_TEST);
-
-	err = tegra_sor_crc_wait(sor, 100);
-	if (err < 0)
-		goto unlock;
-
-	tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
-	value = tegra_sor_readl(sor, SOR_CRC_B);
-
-	num = scnprintf(buf, sizeof(buf), "%08x\n", value);
-
-	err = simple_read_from_buffer(buffer, size, ppos, buf, num);
-
-unlock:
-	mutex_unlock(&sor->lock);
-	return err;
 }
 
-static const struct file_operations tegra_sor_crc_fops = {
-	.owner = THIS_MODULE,
-	.open = tegra_sor_crc_open,
-	.read = tegra_sor_crc_read,
-	.release = tegra_sor_crc_release,
+static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
+	.dpms = tegra_sor_encoder_dpms,
+	.mode_fixup = tegra_sor_encoder_mode_fixup,
+	.prepare = tegra_sor_encoder_prepare,
+	.commit = tegra_sor_encoder_commit,
+	.mode_set = tegra_sor_encoder_mode_set,
+	.disable = tegra_sor_encoder_disable,
 };
 
-static int tegra_sor_debugfs_init(struct tegra_sor *sor,
-				  struct drm_minor *minor)
-{
-	struct dentry *entry;
-	int err = 0;
-
-	sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
-	if (!sor->debugfs)
-		return -ENOMEM;
-
-	entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
-				    &tegra_sor_crc_fops);
-	if (!entry) {
-		dev_err(sor->dev,
-			"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
-			minor->debugfs_root->d_name.name);
-		err = -ENOMEM;
-		goto remove;
-	}
-
-	return err;
-
-remove:
-	debugfs_remove(sor->debugfs);
-	sor->debugfs = NULL;
-	return err;
-}
-
-static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
-{
-	debugfs_remove_recursive(sor->debugfs);
-	sor->debugfs = NULL;
-
-	return 0;
-}
-
 static int tegra_sor_init(struct host1x_client *client)
 {
 	struct drm_device *drm = dev_get_drvdata(client->parent);
@@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client)
 	if (!sor->dpaux)
 		return -ENODEV;
 
-	sor->output.type = TEGRA_OUTPUT_EDP;
-
 	sor->output.dev = sor->dev;
-	sor->output.ops = &sor_ops;
 
-	err = tegra_output_init(drm, &sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output setup failed: %d\n", err);
-		return err;
-	}
+	drm_connector_init(drm, &sor->output.connector,
+			   &tegra_sor_connector_funcs,
+			   DRM_MODE_CONNECTOR_eDP);
+	drm_connector_helper_add(&sor->output.connector,
+				 &tegra_sor_connector_helper_funcs);
+	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
+
+	if (sor->output.panel)
+		drm_panel_attach(sor->output.panel, &sor->output.connector);
+
+	drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&sor->output.encoder,
+			       &tegra_sor_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&sor->output.connector,
+					  &sor->output.encoder);
+	drm_connector_register(&sor->output.connector);
+
+	sor->output.encoder.possible_crtcs = 0x3;
+
+	if (gpio_is_valid(sor->output.hpd_gpio))
+		enable_irq(sor->output.hpd_irq);
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_init(sor, drm->primary);
@@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client)
 		}
 	}
 
+	err = clk_prepare_enable(sor->clk);
+	if (err < 0) {
+		dev_err(sor->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(sor->clk_safe);
+	if (err < 0)
+		return err;
+
+	err = clk_prepare_enable(sor->clk_dp);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client)
 	struct tegra_sor *sor = host1x_client_to_sor(client);
 	int err;
 
-	err = tegra_output_disable(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output failed to disable: %d\n", err);
-		return err;
-	}
-
 	if (sor->dpaux) {
 		err = tegra_dpaux_detach(sor->dpaux);
 		if (err < 0) {
@@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client)
 		}
 	}
 
+	clk_disable_unprepare(sor->clk_safe);
+	clk_disable_unprepare(sor->clk_dp);
+	clk_disable_unprepare(sor->clk);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_exit(sor);
 		if (err < 0)
 			dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
 	}
 
-	err = tegra_output_exit(&sor->output);
-	if (err < 0) {
-		dev_err(sor->dev, "output cleanup failed: %d\n", err);
-		return err;
-	}
-
 	return 0;
 }
 
@@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev)
 	if (IS_ERR(sor->clk_parent))
 		return PTR_ERR(sor->clk_parent);
 
-	err = clk_prepare_enable(sor->clk_parent);
-	if (err < 0)
-		return err;
-
 	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
 	if (IS_ERR(sor->clk_safe))
 		return PTR_ERR(sor->clk_safe);
 
-	err = clk_prepare_enable(sor->clk_safe);
-	if (err < 0)
-		return err;
-
 	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
 	if (IS_ERR(sor->clk_dp))
 		return PTR_ERR(sor->clk_dp);
 
-	err = clk_prepare_enable(sor->clk_dp);
-	if (err < 0)
-		return err;
-
 	INIT_LIST_HEAD(&sor->client.list);
 	sor->client.ops = &sor_client_ops;
 	sor->client.dev = &pdev->dev;
@@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	clk_disable_unprepare(sor->clk_parent);
-	clk_disable_unprepare(sor->clk_safe);
-	clk_disable_unprepare(sor->clk_dp);
-	clk_disable_unprepare(sor->clk);
+	err = tegra_output_remove(&sor->output);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+		return err;
+	}
 
 	return 0;
 }
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 18/36] drm/tegra: debugfs cleanup cannot fail
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The debugfs cleanup code never fails, so no error is returned. Therefore
the functions can all return void instead.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c  | 12 +++---------
 drivers/gpu/drm/tegra/hdmi.c | 13 +++----------
 drivers/gpu/drm/tegra/sor.c  | 11 +++--------
 3 files changed, 9 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 817114ce42fb..4cdfd471aef7 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -228,7 +228,7 @@ remove:
 	return err;
 }
 
-static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
+static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
 {
 	drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files),
 				 dsi->minor);
@@ -239,8 +239,6 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
 
 	debugfs_remove(dsi->debugfs);
 	dsi->debugfs = NULL;
-
-	return 0;
 }
 
 #define PKT_ID0(id)	((((id) & 0x3f) <<  3) | (1 <<  9))
@@ -1025,15 +1023,11 @@ reset:
 static int tegra_dsi_exit(struct host1x_client *client)
 {
 	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
-	int err;
 
 	tegra_output_exit(&dsi->output);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_dsi_debugfs_exit(dsi);
-		if (err < 0)
-			dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_dsi_debugfs_exit(dsi);
 
 	reset_control_assert(dsi->rst);
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 056bb2c1c426..b4fe90949f27 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1310,7 +1310,7 @@ remove:
 	return err;
 }
 
-static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
+static void tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
 {
 	drm_debugfs_remove_files(hdmi->debugfs_files, ARRAY_SIZE(debugfs_files),
 				 hdmi->minor);
@@ -1321,8 +1321,6 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
 
 	debugfs_remove(hdmi->debugfs);
 	hdmi->debugfs = NULL;
-
-	return 0;
 }
 
 static int tegra_hdmi_init(struct host1x_client *client)
@@ -1393,7 +1391,6 @@ static int tegra_hdmi_init(struct host1x_client *client)
 static int tegra_hdmi_exit(struct host1x_client *client)
 {
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
-	int err;
 
 	tegra_output_exit(&hdmi->output);
 
@@ -1404,12 +1401,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 	regulator_disable(hdmi->pll);
 	regulator_disable(hdmi->hdmi);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_hdmi_debugfs_exit(hdmi);
-		if (err < 0)
-			dev_err(client->dev, "debugfs cleanup failed: %d\n",
-				err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_hdmi_debugfs_exit(hdmi);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index be1ad42c69be..a1c16c5c0cf6 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -710,12 +710,10 @@ remove:
 	return err;
 }
 
-static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+static void tegra_sor_debugfs_exit(struct tegra_sor *sor)
 {
 	debugfs_remove_recursive(sor->debugfs);
 	sor->debugfs = NULL;
-
-	return 0;
 }
 
 static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
@@ -1410,11 +1408,8 @@ static int tegra_sor_exit(struct host1x_client *client)
 	clk_disable_unprepare(sor->clk_dp);
 	clk_disable_unprepare(sor->clk);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_sor_debugfs_exit(sor);
-		if (err < 0)
-			dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_sor_debugfs_exit(sor);
 
 	return 0;
 }
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 18/36] drm/tegra: debugfs cleanup cannot fail
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The debugfs cleanup code never fails, so no error is returned. Therefore
the functions can all return void instead.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c  | 12 +++---------
 drivers/gpu/drm/tegra/hdmi.c | 13 +++----------
 drivers/gpu/drm/tegra/sor.c  | 11 +++--------
 3 files changed, 9 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 817114ce42fb..4cdfd471aef7 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -228,7 +228,7 @@ remove:
 	return err;
 }
 
-static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
+static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
 {
 	drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files),
 				 dsi->minor);
@@ -239,8 +239,6 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
 
 	debugfs_remove(dsi->debugfs);
 	dsi->debugfs = NULL;
-
-	return 0;
 }
 
 #define PKT_ID0(id)	((((id) & 0x3f) <<  3) | (1 <<  9))
@@ -1025,15 +1023,11 @@ reset:
 static int tegra_dsi_exit(struct host1x_client *client)
 {
 	struct tegra_dsi *dsi = host1x_client_to_dsi(client);
-	int err;
 
 	tegra_output_exit(&dsi->output);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_dsi_debugfs_exit(dsi);
-		if (err < 0)
-			dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_dsi_debugfs_exit(dsi);
 
 	reset_control_assert(dsi->rst);
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 056bb2c1c426..b4fe90949f27 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1310,7 +1310,7 @@ remove:
 	return err;
 }
 
-static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
+static void tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
 {
 	drm_debugfs_remove_files(hdmi->debugfs_files, ARRAY_SIZE(debugfs_files),
 				 hdmi->minor);
@@ -1321,8 +1321,6 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
 
 	debugfs_remove(hdmi->debugfs);
 	hdmi->debugfs = NULL;
-
-	return 0;
 }
 
 static int tegra_hdmi_init(struct host1x_client *client)
@@ -1393,7 +1391,6 @@ static int tegra_hdmi_init(struct host1x_client *client)
 static int tegra_hdmi_exit(struct host1x_client *client)
 {
 	struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
-	int err;
 
 	tegra_output_exit(&hdmi->output);
 
@@ -1404,12 +1401,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
 	regulator_disable(hdmi->pll);
 	regulator_disable(hdmi->hdmi);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_hdmi_debugfs_exit(hdmi);
-		if (err < 0)
-			dev_err(client->dev, "debugfs cleanup failed: %d\n",
-				err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_hdmi_debugfs_exit(hdmi);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index be1ad42c69be..a1c16c5c0cf6 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -710,12 +710,10 @@ remove:
 	return err;
 }
 
-static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+static void tegra_sor_debugfs_exit(struct tegra_sor *sor)
 {
 	debugfs_remove_recursive(sor->debugfs);
 	sor->debugfs = NULL;
-
-	return 0;
 }
 
 static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
@@ -1410,11 +1408,8 @@ static int tegra_sor_exit(struct host1x_client *client)
 	clk_disable_unprepare(sor->clk_dp);
 	clk_disable_unprepare(sor->clk);
 
-	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-		err = tegra_sor_debugfs_exit(sor);
-		if (err < 0)
-			dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
-	}
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		tegra_sor_debugfs_exit(sor);
 
 	return 0;
 }
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 19/36] drm/tegra: Remove remnants of the output midlayer
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The tegra_output midlayer is now completely gone and output drivers use
it purely as a helper library.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     |  52 --------------------
 drivers/gpu/drm/tegra/drm.h    |  39 ---------------
 drivers/gpu/drm/tegra/dsi.c    |  12 +++--
 drivers/gpu/drm/tegra/hdmi.c   |   9 ++--
 drivers/gpu/drm/tegra/output.c | 107 +++--------------------------------------
 drivers/gpu/drm/tegra/rgb.c    |  13 +++--
 drivers/gpu/drm/tegra/sor.c    |  12 ++---
 7 files changed, 32 insertions(+), 212 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index c51fc4db73db..462945abded2 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1077,52 +1077,6 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
 	return 0;
 }
 
-static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
-				struct drm_display_mode *mode)
-{
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_output *output = NULL;
-	struct drm_encoder *encoder;
-	unsigned int div;
-	u32 value;
-	long err;
-
-	list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
-		if (encoder->crtc == crtc) {
-			output = encoder_to_output(encoder);
-			break;
-		}
-
-	if (!output)
-		return -ENODEV;
-
-	/*
-	 * The ->setup_clock() callback is optional, but if encoders don't
-	 * implement it they most likely need to do the equivalent within the
-	 * ->mode_fixup() callback.
-	 */
-	if (!output->ops || !output->ops->setup_clock)
-		return 0;
-
-	/*
-	 * This assumes that the parent clock is pll_d_out0 or pll_d2_out
-	 * respectively, each of which divides the base pll_d by 2.
-	 */
-	err = output->ops->setup_clock(output, dc->clk, pclk, &div);
-	if (err < 0) {
-		dev_err(dc->dev, "failed to setup clock: %ld\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;
-}
-
 int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 			 unsigned long pclk, unsigned int div)
 {
@@ -1154,12 +1108,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 	u32 value;
 	int err;
 
-	err = tegra_crtc_setup_clk(crtc, mode);
-	if (err) {
-		dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
-		return err;
-	}
-
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index c74d5db47537..699211a89a2e 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -181,23 +181,10 @@ 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);
 
-struct tegra_output_ops {
-	int (*enable)(struct tegra_output *output);
-	int (*disable)(struct tegra_output *output);
-	int (*setup_clock)(struct tegra_output *output, struct clk *clk,
-			   unsigned long pclk, unsigned int *div);
-	int (*check_mode)(struct tegra_output *output,
-			  struct drm_display_mode *mode,
-			  enum drm_mode_status *status);
-	enum drm_connector_status (*detect)(struct tegra_output *output);
-};
-
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
 
-	const struct tegra_output_ops *ops;
-
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
 	const struct edid *edid;
@@ -218,32 +205,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c)
 	return container_of(c, struct tegra_output, connector);
 }
 
-static inline int tegra_output_enable(struct tegra_output *output)
-{
-	if (output && output->ops && output->ops->enable)
-		return output->ops->enable(output);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
-static inline int tegra_output_disable(struct tegra_output *output)
-{
-	if (output && output->ops && output->ops->disable)
-		return output->ops->disable(output);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
-static inline int tegra_output_check_mode(struct tegra_output *output,
-					  struct drm_display_mode *mode,
-					  enum drm_mode_status *status)
-{
-	if (output && output->ops && output->ops->check_mode)
-		return output->ops->check_mode(output, mode, status);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
 /* from rgb.c */
 int tegra_dc_rgb_probe(struct tegra_dc *dc);
 int tegra_dc_rgb_remove(struct tegra_dc *dc);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 4cdfd471aef7..8568e1b44397 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -990,10 +990,6 @@ static int tegra_dsi_init(struct host1x_client *client)
 					 &tegra_dsi_connector_helper_funcs);
 		dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
-		if (dsi->output.panel)
-			drm_panel_attach(dsi->output.panel,
-					 &dsi->output.connector);
-
 		drm_encoder_init(drm, &dsi->output.encoder,
 				 &tegra_dsi_encoder_funcs,
 				 DRM_MODE_ENCODER_DSI);
@@ -1004,6 +1000,14 @@ static int tegra_dsi_init(struct host1x_client *client)
 						  &dsi->output.encoder);
 		drm_connector_register(&dsi->output.connector);
 
+		err = tegra_output_init(drm, &dsi->output);
+		if (err < 0) {
+			dev_err(client->dev,
+				"failed to initialize output: %d\n",
+				err);
+			goto reset;
+		}
+
 		dsi->output.encoder.possible_crtcs = 0x3;
 	}
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index b4fe90949f27..03ceb50b1dc9 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1347,10 +1347,13 @@ static int tegra_hdmi_init(struct host1x_client *client)
 					  &hdmi->output.encoder);
 	drm_connector_register(&hdmi->output.connector);
 
-	hdmi->output.encoder.possible_crtcs = 0x3;
+	err = tegra_output_init(drm, &hdmi->output);
+	if (err < 0) {
+		dev_err(client->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
 
-	if (gpio_is_valid(hdmi->output.hpd_gpio))
-		enable_irq(hdmi->output.hpd_irq);
+	hdmi->output.encoder.possible_crtcs = 0x3;
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 042b421416a8..c6d70d774494 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -43,20 +43,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 	return err;
 }
 
-static int tegra_connector_mode_valid(struct drm_connector *connector,
-				      struct drm_display_mode *mode)
-{
-	struct tegra_output *output = connector_to_output(connector);
-	enum drm_mode_status status = MODE_OK;
-	int err;
-
-	err = tegra_output_check_mode(output, mode, &status);
-	if (err < 0)
-		return MODE_ERROR;
-
-	return status;
-}
-
 struct drm_encoder *
 tegra_output_connector_best_encoder(struct drm_connector *connector)
 {
@@ -65,21 +51,12 @@ tegra_output_connector_best_encoder(struct drm_connector *connector)
 	return &output->encoder;
 }
 
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = tegra_output_connector_get_modes,
-	.mode_valid = tegra_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
-};
-
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	enum drm_connector_status status = connector_status_unknown;
 
-	if (output->ops->detect)
-		return output->ops->detect(output);
-
 	if (gpio_is_valid(output->hpd_gpio)) {
 		if (gpio_get_value(output->hpd_gpio) == 0)
 			status = connector_status_disconnected;
@@ -90,9 +67,6 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
 			status = connector_status_disconnected;
 		else
 			status = connector_status_connected;
-
-		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
-			status = connector_status_connected;
 	}
 
 	return status;
@@ -104,69 +78,11 @@ void tegra_output_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
-static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
-	.detect = tegra_output_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = tegra_output_connector_destroy,
-};
-
 void tegra_output_encoder_destroy(struct drm_encoder *encoder)
 {
 	drm_encoder_cleanup(encoder);
 }
 
-static const struct drm_encoder_funcs encoder_funcs = {
-	.destroy = tegra_output_encoder_destroy,
-};
-
-static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct drm_panel *panel = output->panel;
-
-	if (mode != DRM_MODE_DPMS_ON) {
-		drm_panel_disable(panel);
-		tegra_output_disable(output);
-		drm_panel_unprepare(panel);
-	} else {
-		drm_panel_prepare(panel);
-		tegra_output_enable(output);
-		drm_panel_enable(panel);
-	}
-}
-
-static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
-				     const struct drm_display_mode *mode,
-				     struct drm_display_mode *adjusted)
-{
-	return true;
-}
-
-static void tegra_encoder_prepare(struct drm_encoder *encoder)
-{
-	tegra_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-}
-
-static void tegra_encoder_commit(struct drm_encoder *encoder)
-{
-	tegra_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
-}
-
-static void tegra_encoder_mode_set(struct drm_encoder *encoder,
-				   struct drm_display_mode *mode,
-				   struct drm_display_mode *adjusted)
-{
-}
-
-static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
-	.dpms = tegra_encoder_dpms,
-	.mode_fixup = tegra_encoder_mode_fixup,
-	.prepare = tegra_encoder_prepare,
-	.commit = tegra_encoder_commit,
-	.mode_set = tegra_encoder_mode_set,
-};
-
 static irqreturn_t hpd_irq(int irq, void *data)
 {
 	struct tegra_output *output = data;
@@ -273,24 +189,13 @@ int tegra_output_remove(struct tegra_output *output)
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 {
-	int connector = DRM_MODE_CONNECTOR_Unknown;
-	int encoder = DRM_MODE_ENCODER_NONE;
-
-	drm_connector_init(drm, &output->connector, &connector_funcs,
-			   connector);
-	drm_connector_helper_add(&output->connector, &connector_helper_funcs);
-	output->connector.dpms = DRM_MODE_DPMS_OFF;
-
-	if (output->panel)
-		drm_panel_attach(output->panel, &output->connector);
-
-	drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
-	drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
-
-	drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
-	drm_connector_register(&output->connector);
+	int err;
 
-	output->encoder.possible_crtcs = 0x3;
+	if (output->panel) {
+		err = drm_panel_attach(output->panel, &output->connector);
+		if (err < 0)
+			return err;
+	}
 
 	/*
 	 * The connector is now registered and ready to receive hotplug events
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 30d7ae02ace8..ab6093889be8 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -315,13 +315,6 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 				 &tegra_rgb_connector_helper_funcs);
 	output->connector.dpms = DRM_MODE_DPMS_OFF;
 
-	if (output->panel) {
-		err = drm_panel_attach(output->panel, &output->connector);
-		if (err < 0)
-			dev_err(output->dev, "failed to attach panel: %d\n",
-				err);
-	}
-
 	drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
 			 DRM_MODE_ENCODER_LVDS);
 	drm_encoder_helper_add(&output->encoder,
@@ -331,6 +324,12 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 					  &output->encoder);
 	drm_connector_register(&output->connector);
 
+	err = tegra_output_init(drm, output);
+	if (err < 0) {
+		dev_err(output->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
+
 	/*
 	 * Other outputs can be attached to either display controller. The RGB
 	 * outputs are an exception and work only with their parent display
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index a1c16c5c0cf6..ee18adf7f653 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1343,9 +1343,6 @@ static int tegra_sor_init(struct host1x_client *client)
 				 &tegra_sor_connector_helper_funcs);
 	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
-	if (sor->output.panel)
-		drm_panel_attach(sor->output.panel, &sor->output.connector);
-
 	drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS);
 	drm_encoder_helper_add(&sor->output.encoder,
@@ -1355,10 +1352,13 @@ static int tegra_sor_init(struct host1x_client *client)
 					  &sor->output.encoder);
 	drm_connector_register(&sor->output.connector);
 
-	sor->output.encoder.possible_crtcs = 0x3;
+	err = tegra_output_init(drm, &sor->output);
+	if (err < 0) {
+		dev_err(client->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
 
-	if (gpio_is_valid(sor->output.hpd_gpio))
-		enable_irq(sor->output.hpd_irq);
+	sor->output.encoder.possible_crtcs = 0x3;
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_init(sor, drm->primary);
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 19/36] drm/tegra: Remove remnants of the output midlayer
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The tegra_output midlayer is now completely gone and output drivers use
it purely as a helper library.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     |  52 --------------------
 drivers/gpu/drm/tegra/drm.h    |  39 ---------------
 drivers/gpu/drm/tegra/dsi.c    |  12 +++--
 drivers/gpu/drm/tegra/hdmi.c   |   9 ++--
 drivers/gpu/drm/tegra/output.c | 107 +++--------------------------------------
 drivers/gpu/drm/tegra/rgb.c    |  13 +++--
 drivers/gpu/drm/tegra/sor.c    |  12 ++---
 7 files changed, 32 insertions(+), 212 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index c51fc4db73db..462945abded2 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1077,52 +1077,6 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
 	return 0;
 }
 
-static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
-				struct drm_display_mode *mode)
-{
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_output *output = NULL;
-	struct drm_encoder *encoder;
-	unsigned int div;
-	u32 value;
-	long err;
-
-	list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
-		if (encoder->crtc == crtc) {
-			output = encoder_to_output(encoder);
-			break;
-		}
-
-	if (!output)
-		return -ENODEV;
-
-	/*
-	 * The ->setup_clock() callback is optional, but if encoders don't
-	 * implement it they most likely need to do the equivalent within the
-	 * ->mode_fixup() callback.
-	 */
-	if (!output->ops || !output->ops->setup_clock)
-		return 0;
-
-	/*
-	 * This assumes that the parent clock is pll_d_out0 or pll_d2_out
-	 * respectively, each of which divides the base pll_d by 2.
-	 */
-	err = output->ops->setup_clock(output, dc->clk, pclk, &div);
-	if (err < 0) {
-		dev_err(dc->dev, "failed to setup clock: %ld\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;
-}
-
 int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 			 unsigned long pclk, unsigned int div)
 {
@@ -1154,12 +1108,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 	u32 value;
 	int err;
 
-	err = tegra_crtc_setup_clk(crtc, mode);
-	if (err) {
-		dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
-		return err;
-	}
-
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index c74d5db47537..699211a89a2e 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -181,23 +181,10 @@ 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);
 
-struct tegra_output_ops {
-	int (*enable)(struct tegra_output *output);
-	int (*disable)(struct tegra_output *output);
-	int (*setup_clock)(struct tegra_output *output, struct clk *clk,
-			   unsigned long pclk, unsigned int *div);
-	int (*check_mode)(struct tegra_output *output,
-			  struct drm_display_mode *mode,
-			  enum drm_mode_status *status);
-	enum drm_connector_status (*detect)(struct tegra_output *output);
-};
-
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
 
-	const struct tegra_output_ops *ops;
-
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
 	const struct edid *edid;
@@ -218,32 +205,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c)
 	return container_of(c, struct tegra_output, connector);
 }
 
-static inline int tegra_output_enable(struct tegra_output *output)
-{
-	if (output && output->ops && output->ops->enable)
-		return output->ops->enable(output);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
-static inline int tegra_output_disable(struct tegra_output *output)
-{
-	if (output && output->ops && output->ops->disable)
-		return output->ops->disable(output);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
-static inline int tegra_output_check_mode(struct tegra_output *output,
-					  struct drm_display_mode *mode,
-					  enum drm_mode_status *status)
-{
-	if (output && output->ops && output->ops->check_mode)
-		return output->ops->check_mode(output, mode, status);
-
-	return output ? -ENOSYS : -EINVAL;
-}
-
 /* from rgb.c */
 int tegra_dc_rgb_probe(struct tegra_dc *dc);
 int tegra_dc_rgb_remove(struct tegra_dc *dc);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 4cdfd471aef7..8568e1b44397 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -990,10 +990,6 @@ static int tegra_dsi_init(struct host1x_client *client)
 					 &tegra_dsi_connector_helper_funcs);
 		dsi->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
-		if (dsi->output.panel)
-			drm_panel_attach(dsi->output.panel,
-					 &dsi->output.connector);
-
 		drm_encoder_init(drm, &dsi->output.encoder,
 				 &tegra_dsi_encoder_funcs,
 				 DRM_MODE_ENCODER_DSI);
@@ -1004,6 +1000,14 @@ static int tegra_dsi_init(struct host1x_client *client)
 						  &dsi->output.encoder);
 		drm_connector_register(&dsi->output.connector);
 
+		err = tegra_output_init(drm, &dsi->output);
+		if (err < 0) {
+			dev_err(client->dev,
+				"failed to initialize output: %d\n",
+				err);
+			goto reset;
+		}
+
 		dsi->output.encoder.possible_crtcs = 0x3;
 	}
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index b4fe90949f27..03ceb50b1dc9 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1347,10 +1347,13 @@ static int tegra_hdmi_init(struct host1x_client *client)
 					  &hdmi->output.encoder);
 	drm_connector_register(&hdmi->output.connector);
 
-	hdmi->output.encoder.possible_crtcs = 0x3;
+	err = tegra_output_init(drm, &hdmi->output);
+	if (err < 0) {
+		dev_err(client->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
 
-	if (gpio_is_valid(hdmi->output.hpd_gpio))
-		enable_irq(hdmi->output.hpd_irq);
+	hdmi->output.encoder.possible_crtcs = 0x3;
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 042b421416a8..c6d70d774494 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -43,20 +43,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 	return err;
 }
 
-static int tegra_connector_mode_valid(struct drm_connector *connector,
-				      struct drm_display_mode *mode)
-{
-	struct tegra_output *output = connector_to_output(connector);
-	enum drm_mode_status status = MODE_OK;
-	int err;
-
-	err = tegra_output_check_mode(output, mode, &status);
-	if (err < 0)
-		return MODE_ERROR;
-
-	return status;
-}
-
 struct drm_encoder *
 tegra_output_connector_best_encoder(struct drm_connector *connector)
 {
@@ -65,21 +51,12 @@ tegra_output_connector_best_encoder(struct drm_connector *connector)
 	return &output->encoder;
 }
 
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = tegra_output_connector_get_modes,
-	.mode_valid = tegra_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
-};
-
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
 	struct tegra_output *output = connector_to_output(connector);
 	enum drm_connector_status status = connector_status_unknown;
 
-	if (output->ops->detect)
-		return output->ops->detect(output);
-
 	if (gpio_is_valid(output->hpd_gpio)) {
 		if (gpio_get_value(output->hpd_gpio) == 0)
 			status = connector_status_disconnected;
@@ -90,9 +67,6 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
 			status = connector_status_disconnected;
 		else
 			status = connector_status_connected;
-
-		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
-			status = connector_status_connected;
 	}
 
 	return status;
@@ -104,69 +78,11 @@ void tegra_output_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
-static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
-	.detect = tegra_output_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = tegra_output_connector_destroy,
-};
-
 void tegra_output_encoder_destroy(struct drm_encoder *encoder)
 {
 	drm_encoder_cleanup(encoder);
 }
 
-static const struct drm_encoder_funcs encoder_funcs = {
-	.destroy = tegra_output_encoder_destroy,
-};
-
-static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct drm_panel *panel = output->panel;
-
-	if (mode != DRM_MODE_DPMS_ON) {
-		drm_panel_disable(panel);
-		tegra_output_disable(output);
-		drm_panel_unprepare(panel);
-	} else {
-		drm_panel_prepare(panel);
-		tegra_output_enable(output);
-		drm_panel_enable(panel);
-	}
-}
-
-static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
-				     const struct drm_display_mode *mode,
-				     struct drm_display_mode *adjusted)
-{
-	return true;
-}
-
-static void tegra_encoder_prepare(struct drm_encoder *encoder)
-{
-	tegra_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-}
-
-static void tegra_encoder_commit(struct drm_encoder *encoder)
-{
-	tegra_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
-}
-
-static void tegra_encoder_mode_set(struct drm_encoder *encoder,
-				   struct drm_display_mode *mode,
-				   struct drm_display_mode *adjusted)
-{
-}
-
-static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
-	.dpms = tegra_encoder_dpms,
-	.mode_fixup = tegra_encoder_mode_fixup,
-	.prepare = tegra_encoder_prepare,
-	.commit = tegra_encoder_commit,
-	.mode_set = tegra_encoder_mode_set,
-};
-
 static irqreturn_t hpd_irq(int irq, void *data)
 {
 	struct tegra_output *output = data;
@@ -273,24 +189,13 @@ int tegra_output_remove(struct tegra_output *output)
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 {
-	int connector = DRM_MODE_CONNECTOR_Unknown;
-	int encoder = DRM_MODE_ENCODER_NONE;
-
-	drm_connector_init(drm, &output->connector, &connector_funcs,
-			   connector);
-	drm_connector_helper_add(&output->connector, &connector_helper_funcs);
-	output->connector.dpms = DRM_MODE_DPMS_OFF;
-
-	if (output->panel)
-		drm_panel_attach(output->panel, &output->connector);
-
-	drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
-	drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
-
-	drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
-	drm_connector_register(&output->connector);
+	int err;
 
-	output->encoder.possible_crtcs = 0x3;
+	if (output->panel) {
+		err = drm_panel_attach(output->panel, &output->connector);
+		if (err < 0)
+			return err;
+	}
 
 	/*
 	 * The connector is now registered and ready to receive hotplug events
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 30d7ae02ace8..ab6093889be8 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -315,13 +315,6 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 				 &tegra_rgb_connector_helper_funcs);
 	output->connector.dpms = DRM_MODE_DPMS_OFF;
 
-	if (output->panel) {
-		err = drm_panel_attach(output->panel, &output->connector);
-		if (err < 0)
-			dev_err(output->dev, "failed to attach panel: %d\n",
-				err);
-	}
-
 	drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs,
 			 DRM_MODE_ENCODER_LVDS);
 	drm_encoder_helper_add(&output->encoder,
@@ -331,6 +324,12 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 					  &output->encoder);
 	drm_connector_register(&output->connector);
 
+	err = tegra_output_init(drm, output);
+	if (err < 0) {
+		dev_err(output->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
+
 	/*
 	 * Other outputs can be attached to either display controller. The RGB
 	 * outputs are an exception and work only with their parent display
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index a1c16c5c0cf6..ee18adf7f653 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1343,9 +1343,6 @@ static int tegra_sor_init(struct host1x_client *client)
 				 &tegra_sor_connector_helper_funcs);
 	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
 
-	if (sor->output.panel)
-		drm_panel_attach(sor->output.panel, &sor->output.connector);
-
 	drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS);
 	drm_encoder_helper_add(&sor->output.encoder,
@@ -1355,10 +1352,13 @@ static int tegra_sor_init(struct host1x_client *client)
 					  &sor->output.encoder);
 	drm_connector_register(&sor->output.connector);
 
-	sor->output.encoder.possible_crtcs = 0x3;
+	err = tegra_output_init(drm, &sor->output);
+	if (err < 0) {
+		dev_err(client->dev, "failed to initialize output: %d\n", err);
+		return err;
+	}
 
-	if (gpio_is_valid(sor->output.hpd_gpio))
-		enable_irq(sor->output.hpd_irq);
+	sor->output.encoder.possible_crtcs = 0x3;
 
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_sor_debugfs_init(sor, drm->primary);
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 20/36] drm/tegra: Output cleanup functions cannot fail
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The tegra_output_exit() and tegra_output_remove() functions cannot fail,
so make them return void.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |  4 ++--
 drivers/gpu/drm/tegra/dsi.c    |  6 +-----
 drivers/gpu/drm/tegra/hdmi.c   |  6 +-----
 drivers/gpu/drm/tegra/output.c |  8 ++------
 drivers/gpu/drm/tegra/rgb.c    | 13 ++++---------
 drivers/gpu/drm/tegra/sor.c    |  8 +++-----
 6 files changed, 13 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 699211a89a2e..bf1c47ec46b6 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -213,9 +213,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* from output.c */
 int tegra_output_probe(struct tegra_output *output);
-int tegra_output_remove(struct tegra_output *output);
+void tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
-int tegra_output_exit(struct tegra_output *output);
+void tegra_output_exit(struct tegra_output *output);
 
 int tegra_output_connector_get_modes(struct drm_connector *connector);
 struct drm_encoder *
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 8568e1b44397..f8bfb45587d8 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1578,11 +1578,7 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&dsi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&dsi->output);
 
 	mipi_dsi_host_unregister(&dsi->host);
 	tegra_mipi_free(dsi->mipi);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 03ceb50b1dc9..d41530f1097c 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1563,11 +1563,7 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&hdmi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&hdmi->output);
 
 	clk_disable_unprepare(hdmi->clk_parent);
 	clk_disable_unprepare(hdmi->clk);
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index c6d70d774494..d027e3079c17 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -172,7 +172,7 @@ int tegra_output_probe(struct tegra_output *output)
 	return 0;
 }
 
-int tegra_output_remove(struct tegra_output *output)
+void tegra_output_remove(struct tegra_output *output)
 {
 	if (gpio_is_valid(output->hpd_gpio)) {
 		free_irq(output->hpd_irq, output);
@@ -183,8 +183,6 @@ int tegra_output_remove(struct tegra_output *output)
 		put_device(&output->ddc->dev);
 
 	drm_panel_unref(output->panel);
-
-	return 0;
 }
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
@@ -207,7 +205,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	return 0;
 }
 
-int tegra_output_exit(struct tegra_output *output)
+void tegra_output_exit(struct tegra_output *output)
 {
 	/*
 	 * The connector is going away, so the interrupt must be disabled to
@@ -218,6 +216,4 @@ int tegra_output_exit(struct tegra_output *output)
 
 	if (output->panel)
 		drm_panel_detach(output->panel);
-
-	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index ab6093889be8..0c932f9dc12d 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -287,15 +287,10 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
 
 int tegra_dc_rgb_remove(struct tegra_dc *dc)
 {
-	int err;
-
 	if (!dc->rgb)
 		return 0;
 
-	err = tegra_output_remove(dc->rgb);
-	if (err < 0)
-		return err;
-
+	tegra_output_remove(dc->rgb);
 	dc->rgb = NULL;
 
 	return 0;
@@ -342,8 +337,8 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 
 int tegra_dc_rgb_exit(struct tegra_dc *dc)
 {
-	if (!dc->rgb)
-		return 0;
+	if (dc->rgb)
+		tegra_output_exit(dc->rgb);
 
-	return tegra_output_exit(dc->rgb);
+	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index ee18adf7f653..9e67838c1562 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1396,6 +1396,8 @@ static int tegra_sor_exit(struct host1x_client *client)
 	struct tegra_sor *sor = host1x_client_to_sor(client);
 	int err;
 
+	tegra_output_exit(&sor->output);
+
 	if (sor->dpaux) {
 		err = tegra_dpaux_detach(sor->dpaux);
 		if (err < 0) {
@@ -1500,11 +1502,7 @@ static int tegra_sor_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&sor->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&sor->output);
 
 	return 0;
 }
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 20/36] drm/tegra: Output cleanup functions cannot fail
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The tegra_output_exit() and tegra_output_remove() functions cannot fail,
so make them return void.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h    |  4 ++--
 drivers/gpu/drm/tegra/dsi.c    |  6 +-----
 drivers/gpu/drm/tegra/hdmi.c   |  6 +-----
 drivers/gpu/drm/tegra/output.c |  8 ++------
 drivers/gpu/drm/tegra/rgb.c    | 13 ++++---------
 drivers/gpu/drm/tegra/sor.c    |  8 +++-----
 6 files changed, 13 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 699211a89a2e..bf1c47ec46b6 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -213,9 +213,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* from output.c */
 int tegra_output_probe(struct tegra_output *output);
-int tegra_output_remove(struct tegra_output *output);
+void tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
-int tegra_output_exit(struct tegra_output *output);
+void tegra_output_exit(struct tegra_output *output);
 
 int tegra_output_connector_get_modes(struct drm_connector *connector);
 struct drm_encoder *
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 8568e1b44397..f8bfb45587d8 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1578,11 +1578,7 @@ static int tegra_dsi_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&dsi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&dsi->output);
 
 	mipi_dsi_host_unregister(&dsi->host);
 	tegra_mipi_free(dsi->mipi);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 03ceb50b1dc9..d41530f1097c 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1563,11 +1563,7 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&hdmi->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&hdmi->output);
 
 	clk_disable_unprepare(hdmi->clk_parent);
 	clk_disable_unprepare(hdmi->clk);
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index c6d70d774494..d027e3079c17 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -172,7 +172,7 @@ int tegra_output_probe(struct tegra_output *output)
 	return 0;
 }
 
-int tegra_output_remove(struct tegra_output *output)
+void tegra_output_remove(struct tegra_output *output)
 {
 	if (gpio_is_valid(output->hpd_gpio)) {
 		free_irq(output->hpd_irq, output);
@@ -183,8 +183,6 @@ int tegra_output_remove(struct tegra_output *output)
 		put_device(&output->ddc->dev);
 
 	drm_panel_unref(output->panel);
-
-	return 0;
 }
 
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
@@ -207,7 +205,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
 	return 0;
 }
 
-int tegra_output_exit(struct tegra_output *output)
+void tegra_output_exit(struct tegra_output *output)
 {
 	/*
 	 * The connector is going away, so the interrupt must be disabled to
@@ -218,6 +216,4 @@ int tegra_output_exit(struct tegra_output *output)
 
 	if (output->panel)
 		drm_panel_detach(output->panel);
-
-	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index ab6093889be8..0c932f9dc12d 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -287,15 +287,10 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
 
 int tegra_dc_rgb_remove(struct tegra_dc *dc)
 {
-	int err;
-
 	if (!dc->rgb)
 		return 0;
 
-	err = tegra_output_remove(dc->rgb);
-	if (err < 0)
-		return err;
-
+	tegra_output_remove(dc->rgb);
 	dc->rgb = NULL;
 
 	return 0;
@@ -342,8 +337,8 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 
 int tegra_dc_rgb_exit(struct tegra_dc *dc)
 {
-	if (!dc->rgb)
-		return 0;
+	if (dc->rgb)
+		tegra_output_exit(dc->rgb);
 
-	return tegra_output_exit(dc->rgb);
+	return 0;
 }
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index ee18adf7f653..9e67838c1562 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1396,6 +1396,8 @@ static int tegra_sor_exit(struct host1x_client *client)
 	struct tegra_sor *sor = host1x_client_to_sor(client);
 	int err;
 
+	tegra_output_exit(&sor->output);
+
 	if (sor->dpaux) {
 		err = tegra_dpaux_detach(sor->dpaux);
 		if (err < 0) {
@@ -1500,11 +1502,7 @@ static int tegra_sor_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	err = tegra_output_remove(&sor->output);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to remove output: %d\n", err);
-		return err;
-	}
+	tegra_output_remove(&sor->output);
 
 	return 0;
 }
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 21/36] drm/tegra: dc: Do not needlessly deassert reset
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Commit 9c0127004ff4 ("drm/tegra: dc: Add powergate support") changed the
driver's ->probe() implementation to deassert the module reset, and with
there being nobody else to assert it until ->remove() there is no need
to deassert again later on.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 462945abded2..846daf48cf61 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1160,10 +1160,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 	drm_crtc_vblank_off(crtc);
 
-	/* hardware initialization */
-	reset_control_deassert(dc->rst);
-	usleep_range(10000, 20000);
-
 	if (dc->pipe)
 		syncpt = SYNCPT_VBLANK1;
 	else
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 21/36] drm/tegra: dc: Do not needlessly deassert reset
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Commit 9c0127004ff4 ("drm/tegra: dc: Add powergate support") changed the
driver's ->probe() implementation to deassert the module reset, and with
there being nobody else to assert it until ->remove() there is no need
to deassert again later on.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 462945abded2..846daf48cf61 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1160,10 +1160,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 	drm_crtc_vblank_off(crtc);
 
-	/* hardware initialization */
-	reset_control_deassert(dc->rst);
-	usleep_range(10000, 20000);
-
 	if (dc->pipe)
 		syncpt = SYNCPT_VBLANK1;
 	else
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 22/36] drm/tegra: Atomic conversion, phase 1
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement initial atomic state handling. Hook up the CRTCs, planes' and
connectors' ->atomic_destroy_state() callback to ensure that the atomic
state objects don't leak.

Furthermore the CRTC now implements the ->mode_set_nofb() callback that
is used by new helpers to implement ->mode_set() and ->mode_set_base().
These new helpers also make use of the new plane helper functions which
the driver now provides.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     | 397 ++++++++++++++++++++++-------------------
 drivers/gpu/drm/tegra/drm.c    |   2 +-
 drivers/gpu/drm/tegra/dsi.c    |   2 +
 drivers/gpu/drm/tegra/hdmi.c   |   2 +
 drivers/gpu/drm/tegra/output.c |   1 +
 drivers/gpu/drm/tegra/rgb.c    |   2 +
 drivers/gpu/drm/tegra/sor.c    |   2 +
 7 files changed, 223 insertions(+), 185 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 846daf48cf61..e7efb6ca127c 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -25,6 +25,7 @@
 #include "drm.h"
 #include "gem.h"
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
 struct tegra_dc_soc_info {
@@ -207,8 +208,8 @@ static inline u32 compute_initial_dda(unsigned int in)
 	return dfixed_frac(inf);
 }
 
-static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
-				 const struct tegra_dc_window *window)
+static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+				  const struct tegra_dc_window *window)
 {
 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
 	unsigned long value, flags;
@@ -317,9 +318,11 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 			break;
 
 		case TEGRA_BO_TILING_MODE_BLOCK:
-			DRM_ERROR("hardware doesn't support block linear mode\n");
-			spin_unlock_irqrestore(&dc->lock, flags);
-			return -EINVAL;
+			/*
+			 * No need to handle this here because ->atomic_check
+			 * will already have filtered it out.
+			 */
+			break;
 		}
 
 		tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
@@ -378,34 +381,6 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 	tegra_dc_window_commit(dc, index);
 
 	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
-}
-
-static int tegra_window_plane_disable(struct drm_plane *plane)
-{
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
-	struct tegra_plane *p = to_tegra_plane(plane);
-	unsigned long flags;
-	u32 value;
-
-	if (!plane->crtc)
-		return 0;
-
-	spin_lock_irqsave(&dc->lock, flags);
-
-	value = WINDOW_A_SELECT << p->index;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
-	value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-	value &= ~WIN_ENABLE;
-	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
-	tegra_dc_window_commit(dc, p->index);
-
-	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
 }
 
 static void tegra_plane_destroy(struct drm_plane *plane)
@@ -422,57 +397,139 @@ static const u32 tegra_primary_plane_formats[] = {
 	DRM_FORMAT_RGB565,
 };
 
-static int tegra_primary_plane_update(struct drm_plane *plane,
-				      struct drm_crtc *crtc,
-				      struct drm_framebuffer *fb, int crtc_x,
-				      int crtc_y, unsigned int crtc_w,
-				      unsigned int crtc_h, uint32_t src_x,
-				      uint32_t src_y, uint32_t src_w,
-				      uint32_t src_h)
+static void tegra_primary_plane_destroy(struct drm_plane *plane)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+	tegra_plane_destroy(plane);
+}
+
+static const struct drm_plane_funcs tegra_primary_plane_funcs = {
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
+	.destroy = tegra_primary_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int tegra_plane_prepare_fb(struct drm_plane *plane,
+				  struct drm_framebuffer *fb)
+{
+	return 0;
+}
+
+static void tegra_plane_cleanup_fb(struct drm_plane *plane,
+				   struct drm_framebuffer *fb)
+{
+}
+
+static int tegra_plane_atomic_check(struct drm_plane *plane,
+				    struct drm_plane_state *state)
+{
+	struct tegra_dc *dc = to_tegra_dc(state->crtc);
+	struct tegra_bo_tiling tiling;
+	int err;
+
+	/* no need for further checks if the plane is being disabled */
+	if (!state->crtc)
+		return 0;
+
+	err = tegra_fb_get_tiling(state->fb, &tiling);
+	if (err < 0)
+		return err;
+
+	if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK &&
+	    !dc->soc->supports_block_linear) {
+		DRM_ERROR("hardware doesn't support block linear mode\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Tegra doesn't support different strides for U and V planes so we
+	 * error out if the user tries to display a framebuffer with such a
+	 * configuration.
+	 */
+	if (drm_format_num_planes(state->fb->pixel_format) > 2) {
+		if (state->fb->pitches[2] != state->fb->pitches[1]) {
+			DRM_ERROR("unsupported UV-plane configuration\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void tegra_plane_atomic_update(struct drm_plane *plane,
+				      struct drm_plane_state *old_state)
+{
+	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+	struct drm_framebuffer *fb = plane->state->fb;
 	struct tegra_plane *p = to_tegra_plane(plane);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct tegra_dc_window window;
+	unsigned int i;
 	int err;
 
+	/* rien ne va plus */
+	if (!plane->state->crtc || !plane->state->fb)
+		return;
+
 	memset(&window, 0, sizeof(window));
-	window.src.x = src_x >> 16;
-	window.src.y = src_y >> 16;
-	window.src.w = src_w >> 16;
-	window.src.h = src_h >> 16;
-	window.dst.x = crtc_x;
-	window.dst.y = crtc_y;
-	window.dst.w = crtc_w;
-	window.dst.h = crtc_h;
+	window.src.x = plane->state->src_x >> 16;
+	window.src.y = plane->state->src_y >> 16;
+	window.src.w = plane->state->src_w >> 16;
+	window.src.h = plane->state->src_h >> 16;
+	window.dst.x = plane->state->crtc_x;
+	window.dst.y = plane->state->crtc_y;
+	window.dst.w = plane->state->crtc_w;
+	window.dst.h = plane->state->crtc_h;
 	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
 	window.bits_per_pixel = fb->bits_per_pixel;
 	window.bottom_up = tegra_fb_is_bottom_up(fb);
 
 	err = tegra_fb_get_tiling(fb, &window.tiling);
-	if (err < 0)
-		return err;
+	WARN_ON(err < 0);
 
-	window.base[0] = bo->paddr + fb->offsets[0];
-	window.stride[0] = fb->pitches[0];
+	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
+		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
 
-	err = tegra_dc_setup_window(dc, p->index, &window);
-	if (err < 0)
-		return err;
+		window.base[i] = bo->paddr + fb->offsets[i];
+		window.stride[i] = fb->pitches[i];
+	}
 
-	return 0;
+	tegra_dc_setup_window(dc, p->index, &window);
 }
 
-static void tegra_primary_plane_destroy(struct drm_plane *plane)
+static void tegra_plane_atomic_disable(struct drm_plane *plane,
+				       struct drm_plane_state *old_state)
 {
-	tegra_window_plane_disable(plane);
-	tegra_plane_destroy(plane);
+	struct tegra_plane *p = to_tegra_plane(plane);
+	struct tegra_dc *dc;
+	unsigned long flags;
+	u32 value;
+
+	/* rien ne va plus */
+	if (!old_state || !old_state->crtc)
+		return;
+
+	dc = to_tegra_dc(old_state->crtc);
+
+	spin_lock_irqsave(&dc->lock, flags);
+
+	value = WINDOW_A_SELECT << p->index;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+	value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+	value &= ~WIN_ENABLE;
+	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+	tegra_dc_window_commit(dc, p->index);
+
+	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
-static const struct drm_plane_funcs tegra_primary_plane_funcs = {
-	.update_plane = tegra_primary_plane_update,
-	.disable_plane = tegra_window_plane_disable,
-	.destroy = tegra_primary_plane_destroy,
+static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = {
+	.prepare_fb = tegra_plane_prepare_fb,
+	.cleanup_fb = tegra_plane_cleanup_fb,
+	.atomic_check = tegra_plane_atomic_check,
+	.atomic_update = tegra_plane_atomic_update,
+	.atomic_disable = tegra_plane_atomic_disable,
 };
 
 static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
@@ -511,6 +568,8 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
+	drm_plane_helper_add(&plane->base, &tegra_primary_plane_helper_funcs);
+
 	return &plane->base;
 }
 
@@ -518,27 +577,42 @@ static const u32 tegra_cursor_plane_formats[] = {
 	DRM_FORMAT_RGBA8888,
 };
 
-static int tegra_cursor_plane_update(struct drm_plane *plane,
-				     struct drm_crtc *crtc,
-				     struct drm_framebuffer *fb, int crtc_x,
-				     int crtc_y, unsigned int crtc_w,
-				     unsigned int crtc_h, uint32_t src_x,
-				     uint32_t src_y, uint32_t src_w,
-				     uint32_t src_h)
+static int tegra_cursor_atomic_check(struct drm_plane *plane,
+				     struct drm_plane_state *state)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	u32 value = CURSOR_CLIP_DISPLAY;
+	/* no need for further checks if the plane is being disabled */
+	if (!state->crtc)
+		return 0;
 
 	/* scaling not supported for cursor */
-	if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h))
+	if ((state->src_w >> 16 != state->crtc_w) ||
+	    (state->src_h >> 16 != state->crtc_h))
 		return -EINVAL;
 
 	/* only square cursors supported */
-	if (src_w != src_h)
+	if (state->src_w != state->src_h)
+		return -EINVAL;
+
+	if (state->crtc_w != 32 && state->crtc_w != 64 &&
+	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
-	switch (crtc_w) {
+	return 0;
+}
+
+static void tegra_cursor_atomic_update(struct drm_plane *plane,
+				       struct drm_plane_state *old_state)
+{
+	struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0);
+	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+	struct drm_plane_state *state = plane->state;
+	u32 value = CURSOR_CLIP_DISPLAY;
+
+	/* rien ne va plus */
+	if (!plane->state->crtc || !plane->state->fb)
+		return;
+
+	switch (state->crtc_w) {
 	case 32:
 		value |= CURSOR_SIZE_32x32;
 		break;
@@ -556,7 +630,9 @@ static int tegra_cursor_plane_update(struct drm_plane *plane,
 		break;
 
 	default:
-		return -EINVAL;
+		WARN(1, "cursor size %ux%u not supported\n", state->crtc_w,
+		     state->crtc_h);
+		return;
 	}
 
 	value |= (bo->paddr >> 10) & 0x3fffff;
@@ -582,23 +658,25 @@ static int tegra_cursor_plane_update(struct drm_plane *plane,
 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
 
 	/* position the cursor */
-	value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff);
+	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
 	/* apply changes */
 	tegra_dc_cursor_commit(dc);
 	tegra_dc_commit(dc);
-
-	return 0;
 }
 
-static int tegra_cursor_plane_disable(struct drm_plane *plane)
+static void tegra_cursor_atomic_disable(struct drm_plane *plane,
+					struct drm_plane_state *old_state)
 {
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
+	struct tegra_dc *dc;
 	u32 value;
 
-	if (!plane->crtc)
-		return 0;
+	/* rien ne va plus */
+	if (!old_state || !old_state->crtc)
+		return;
+
+	dc = to_tegra_dc(old_state->crtc);
 
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
@@ -606,14 +684,21 @@ static int tegra_cursor_plane_disable(struct drm_plane *plane)
 
 	tegra_dc_cursor_commit(dc);
 	tegra_dc_commit(dc);
-
-	return 0;
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
-	.update_plane = tegra_cursor_plane_update,
-	.disable_plane = tegra_cursor_plane_disable,
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+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,
 };
 
 static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
@@ -639,71 +724,21 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
-	return &plane->base;
-}
-
-static int tegra_overlay_plane_update(struct drm_plane *plane,
-				      struct drm_crtc *crtc,
-				      struct drm_framebuffer *fb, int crtc_x,
-				      int crtc_y, unsigned int crtc_w,
-				      unsigned int crtc_h, uint32_t src_x,
-				      uint32_t src_y, uint32_t src_w,
-				      uint32_t src_h)
-{
-	struct tegra_plane *p = to_tegra_plane(plane);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_dc_window window;
-	unsigned int i;
-	int err;
-
-	memset(&window, 0, sizeof(window));
-	window.src.x = src_x >> 16;
-	window.src.y = src_y >> 16;
-	window.src.w = src_w >> 16;
-	window.src.h = src_h >> 16;
-	window.dst.x = crtc_x;
-	window.dst.y = crtc_y;
-	window.dst.w = crtc_w;
-	window.dst.h = crtc_h;
-	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
-	window.bits_per_pixel = fb->bits_per_pixel;
-	window.bottom_up = tegra_fb_is_bottom_up(fb);
-
-	err = tegra_fb_get_tiling(fb, &window.tiling);
-	if (err < 0)
-		return err;
+	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
 
-	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
-		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
-
-		window.base[i] = bo->paddr + fb->offsets[i];
-
-		/*
-		 * Tegra doesn't support different strides for U and V planes
-		 * so we display a warning if the user tries to display a
-		 * framebuffer with such a configuration.
-		 */
-		if (i >= 2) {
-			if (fb->pitches[i] != window.stride[1])
-				DRM_ERROR("unsupported UV-plane configuration\n");
-		} else {
-			window.stride[i] = fb->pitches[i];
-		}
-	}
-
-	return tegra_dc_setup_window(dc, p->index, &window);
+	return &plane->base;
 }
 
 static void tegra_overlay_plane_destroy(struct drm_plane *plane)
 {
-	tegra_window_plane_disable(plane);
 	tegra_plane_destroy(plane);
 }
 
 static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
-	.update_plane = tegra_overlay_plane_update,
-	.disable_plane = tegra_window_plane_disable,
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_overlay_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
 static const uint32_t tegra_overlay_plane_formats[] = {
@@ -716,6 +751,14 @@ static const uint32_t tegra_overlay_plane_formats[] = {
 	DRM_FORMAT_YUV422,
 };
 
+static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = {
+	.prepare_fb = tegra_plane_prepare_fb,
+	.cleanup_fb = tegra_plane_cleanup_fb,
+	.atomic_check = tegra_plane_atomic_check,
+	.atomic_update = tegra_plane_atomic_update,
+	.atomic_disable = tegra_plane_atomic_disable,
+};
+
 static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 						       struct tegra_dc *dc,
 						       unsigned int index)
@@ -742,6 +785,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
+	drm_plane_helper_add(&plane->base, &tegra_overlay_plane_helper_funcs);
+
 	return &plane->base;
 }
 
@@ -960,6 +1005,7 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
 static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1097,16 +1143,11 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 	return 0;
 }
 
-static int tegra_crtc_mode_set(struct drm_crtc *crtc,
-			       struct drm_display_mode *mode,
-			       struct drm_display_mode *adjusted,
-			       int x, int y, struct drm_framebuffer *old_fb)
+static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_dc_window window;
 	u32 value;
-	int err;
 
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
@@ -1120,36 +1161,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 		value &= ~INTERLACE_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
 	}
-
-	/* setup window parameters */
-	memset(&window, 0, sizeof(window));
-	window.src.x = 0;
-	window.src.y = 0;
-	window.src.w = mode->hdisplay;
-	window.src.h = mode->vdisplay;
-	window.dst.x = 0;
-	window.dst.y = 0;
-	window.dst.w = mode->hdisplay;
-	window.dst.h = mode->vdisplay;
-	window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
-					&window.swap);
-	window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
-	window.stride[0] = crtc->primary->fb->pitches[0];
-	window.base[0] = bo->paddr;
-
-	err = tegra_dc_setup_window(dc, 0, &window);
-	if (err < 0)
-		dev_err(dc->dev, "failed to enable root plane\n");
-
-	return 0;
-}
-
-static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-				    struct drm_framebuffer *old_fb)
-{
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
-	return tegra_dc_set_base(dc, x, y, crtc->primary->fb);
 }
 
 static void tegra_crtc_prepare(struct drm_crtc *crtc)
@@ -1200,13 +1211,31 @@ static void tegra_crtc_commit(struct drm_crtc *crtc)
 	tegra_dc_commit(dc);
 }
 
+static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	return 0;
+}
+
+static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
+{
+}
+
+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 = tegra_crtc_mode_set,
-	.mode_set_base = tegra_crtc_mode_set_base,
+	.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,
+	.atomic_begin = tegra_crtc_atomic_begin,
+	.atomic_flush = tegra_crtc_atomic_flush,
 };
 
 static irqreturn_t tegra_dc_irq(int irq, void *data)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 121e022bdab0..3326df252c52 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -121,8 +121,8 @@ static int tegra_drm_unload(struct drm_device *drm)
 
 	drm_kms_helper_poll_fini(drm);
 	tegra_drm_fb_exit(drm);
-	drm_vblank_cleanup(drm);
 	drm_mode_config_cleanup(drm);
+	drm_vblank_cleanup(drm);
 
 	err = host1x_device_exit(device);
 	if (err < 0)
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index f8bfb45587d8..4bb06c119a18 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -17,6 +17,7 @@
 
 #include <linux/regulator/consumer.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
 
@@ -736,6 +737,7 @@ static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index d41530f1097c..bf868d54ef7e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -14,6 +14,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -781,6 +782,7 @@ static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index d027e3079c17..79530c23790b 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -9,6 +9,7 @@
 
 #include <linux/of_gpio.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 #include "drm.h"
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0c932f9dc12d..23ba40370eb2 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -9,6 +9,7 @@
 
 #include <linux/clk.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 
 #include "drm.h"
@@ -97,6 +98,7 @@ static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 9e67838c1562..e2f399363f09 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -15,6 +15,7 @@
 
 #include <soc/tegra/pmc.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 
@@ -737,6 +738,7 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = {
 	.detect = tegra_sor_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static int tegra_sor_connector_get_modes(struct drm_connector *connector)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 22/36] drm/tegra: Atomic conversion, phase 1
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Implement initial atomic state handling. Hook up the CRTCs, planes' and
connectors' ->atomic_destroy_state() callback to ensure that the atomic
state objects don't leak.

Furthermore the CRTC now implements the ->mode_set_nofb() callback that
is used by new helpers to implement ->mode_set() and ->mode_set_base().
These new helpers also make use of the new plane helper functions which
the driver now provides.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c     | 397 ++++++++++++++++++++++-------------------
 drivers/gpu/drm/tegra/drm.c    |   2 +-
 drivers/gpu/drm/tegra/dsi.c    |   2 +
 drivers/gpu/drm/tegra/hdmi.c   |   2 +
 drivers/gpu/drm/tegra/output.c |   1 +
 drivers/gpu/drm/tegra/rgb.c    |   2 +
 drivers/gpu/drm/tegra/sor.c    |   2 +
 7 files changed, 223 insertions(+), 185 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 846daf48cf61..e7efb6ca127c 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -25,6 +25,7 @@
 #include "drm.h"
 #include "gem.h"
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
 struct tegra_dc_soc_info {
@@ -207,8 +208,8 @@ static inline u32 compute_initial_dda(unsigned int in)
 	return dfixed_frac(inf);
 }
 
-static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
-				 const struct tegra_dc_window *window)
+static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+				  const struct tegra_dc_window *window)
 {
 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
 	unsigned long value, flags;
@@ -317,9 +318,11 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 			break;
 
 		case TEGRA_BO_TILING_MODE_BLOCK:
-			DRM_ERROR("hardware doesn't support block linear mode\n");
-			spin_unlock_irqrestore(&dc->lock, flags);
-			return -EINVAL;
+			/*
+			 * No need to handle this here because ->atomic_check
+			 * will already have filtered it out.
+			 */
+			break;
 		}
 
 		tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
@@ -378,34 +381,6 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 	tegra_dc_window_commit(dc, index);
 
 	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
-}
-
-static int tegra_window_plane_disable(struct drm_plane *plane)
-{
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
-	struct tegra_plane *p = to_tegra_plane(plane);
-	unsigned long flags;
-	u32 value;
-
-	if (!plane->crtc)
-		return 0;
-
-	spin_lock_irqsave(&dc->lock, flags);
-
-	value = WINDOW_A_SELECT << p->index;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
-	value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-	value &= ~WIN_ENABLE;
-	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
-	tegra_dc_window_commit(dc, p->index);
-
-	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
 }
 
 static void tegra_plane_destroy(struct drm_plane *plane)
@@ -422,57 +397,139 @@ static const u32 tegra_primary_plane_formats[] = {
 	DRM_FORMAT_RGB565,
 };
 
-static int tegra_primary_plane_update(struct drm_plane *plane,
-				      struct drm_crtc *crtc,
-				      struct drm_framebuffer *fb, int crtc_x,
-				      int crtc_y, unsigned int crtc_w,
-				      unsigned int crtc_h, uint32_t src_x,
-				      uint32_t src_y, uint32_t src_w,
-				      uint32_t src_h)
+static void tegra_primary_plane_destroy(struct drm_plane *plane)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+	tegra_plane_destroy(plane);
+}
+
+static const struct drm_plane_funcs tegra_primary_plane_funcs = {
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
+	.destroy = tegra_primary_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int tegra_plane_prepare_fb(struct drm_plane *plane,
+				  struct drm_framebuffer *fb)
+{
+	return 0;
+}
+
+static void tegra_plane_cleanup_fb(struct drm_plane *plane,
+				   struct drm_framebuffer *fb)
+{
+}
+
+static int tegra_plane_atomic_check(struct drm_plane *plane,
+				    struct drm_plane_state *state)
+{
+	struct tegra_dc *dc = to_tegra_dc(state->crtc);
+	struct tegra_bo_tiling tiling;
+	int err;
+
+	/* no need for further checks if the plane is being disabled */
+	if (!state->crtc)
+		return 0;
+
+	err = tegra_fb_get_tiling(state->fb, &tiling);
+	if (err < 0)
+		return err;
+
+	if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK &&
+	    !dc->soc->supports_block_linear) {
+		DRM_ERROR("hardware doesn't support block linear mode\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Tegra doesn't support different strides for U and V planes so we
+	 * error out if the user tries to display a framebuffer with such a
+	 * configuration.
+	 */
+	if (drm_format_num_planes(state->fb->pixel_format) > 2) {
+		if (state->fb->pitches[2] != state->fb->pitches[1]) {
+			DRM_ERROR("unsupported UV-plane configuration\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void tegra_plane_atomic_update(struct drm_plane *plane,
+				      struct drm_plane_state *old_state)
+{
+	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+	struct drm_framebuffer *fb = plane->state->fb;
 	struct tegra_plane *p = to_tegra_plane(plane);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
 	struct tegra_dc_window window;
+	unsigned int i;
 	int err;
 
+	/* rien ne va plus */
+	if (!plane->state->crtc || !plane->state->fb)
+		return;
+
 	memset(&window, 0, sizeof(window));
-	window.src.x = src_x >> 16;
-	window.src.y = src_y >> 16;
-	window.src.w = src_w >> 16;
-	window.src.h = src_h >> 16;
-	window.dst.x = crtc_x;
-	window.dst.y = crtc_y;
-	window.dst.w = crtc_w;
-	window.dst.h = crtc_h;
+	window.src.x = plane->state->src_x >> 16;
+	window.src.y = plane->state->src_y >> 16;
+	window.src.w = plane->state->src_w >> 16;
+	window.src.h = plane->state->src_h >> 16;
+	window.dst.x = plane->state->crtc_x;
+	window.dst.y = plane->state->crtc_y;
+	window.dst.w = plane->state->crtc_w;
+	window.dst.h = plane->state->crtc_h;
 	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
 	window.bits_per_pixel = fb->bits_per_pixel;
 	window.bottom_up = tegra_fb_is_bottom_up(fb);
 
 	err = tegra_fb_get_tiling(fb, &window.tiling);
-	if (err < 0)
-		return err;
+	WARN_ON(err < 0);
 
-	window.base[0] = bo->paddr + fb->offsets[0];
-	window.stride[0] = fb->pitches[0];
+	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
+		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
 
-	err = tegra_dc_setup_window(dc, p->index, &window);
-	if (err < 0)
-		return err;
+		window.base[i] = bo->paddr + fb->offsets[i];
+		window.stride[i] = fb->pitches[i];
+	}
 
-	return 0;
+	tegra_dc_setup_window(dc, p->index, &window);
 }
 
-static void tegra_primary_plane_destroy(struct drm_plane *plane)
+static void tegra_plane_atomic_disable(struct drm_plane *plane,
+				       struct drm_plane_state *old_state)
 {
-	tegra_window_plane_disable(plane);
-	tegra_plane_destroy(plane);
+	struct tegra_plane *p = to_tegra_plane(plane);
+	struct tegra_dc *dc;
+	unsigned long flags;
+	u32 value;
+
+	/* rien ne va plus */
+	if (!old_state || !old_state->crtc)
+		return;
+
+	dc = to_tegra_dc(old_state->crtc);
+
+	spin_lock_irqsave(&dc->lock, flags);
+
+	value = WINDOW_A_SELECT << p->index;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+	value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+	value &= ~WIN_ENABLE;
+	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+	tegra_dc_window_commit(dc, p->index);
+
+	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
-static const struct drm_plane_funcs tegra_primary_plane_funcs = {
-	.update_plane = tegra_primary_plane_update,
-	.disable_plane = tegra_window_plane_disable,
-	.destroy = tegra_primary_plane_destroy,
+static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = {
+	.prepare_fb = tegra_plane_prepare_fb,
+	.cleanup_fb = tegra_plane_cleanup_fb,
+	.atomic_check = tegra_plane_atomic_check,
+	.atomic_update = tegra_plane_atomic_update,
+	.atomic_disable = tegra_plane_atomic_disable,
 };
 
 static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
@@ -511,6 +568,8 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
+	drm_plane_helper_add(&plane->base, &tegra_primary_plane_helper_funcs);
+
 	return &plane->base;
 }
 
@@ -518,27 +577,42 @@ static const u32 tegra_cursor_plane_formats[] = {
 	DRM_FORMAT_RGBA8888,
 };
 
-static int tegra_cursor_plane_update(struct drm_plane *plane,
-				     struct drm_crtc *crtc,
-				     struct drm_framebuffer *fb, int crtc_x,
-				     int crtc_y, unsigned int crtc_w,
-				     unsigned int crtc_h, uint32_t src_x,
-				     uint32_t src_y, uint32_t src_w,
-				     uint32_t src_h)
+static int tegra_cursor_atomic_check(struct drm_plane *plane,
+				     struct drm_plane_state *state)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	u32 value = CURSOR_CLIP_DISPLAY;
+	/* no need for further checks if the plane is being disabled */
+	if (!state->crtc)
+		return 0;
 
 	/* scaling not supported for cursor */
-	if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h))
+	if ((state->src_w >> 16 != state->crtc_w) ||
+	    (state->src_h >> 16 != state->crtc_h))
 		return -EINVAL;
 
 	/* only square cursors supported */
-	if (src_w != src_h)
+	if (state->src_w != state->src_h)
+		return -EINVAL;
+
+	if (state->crtc_w != 32 && state->crtc_w != 64 &&
+	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
-	switch (crtc_w) {
+	return 0;
+}
+
+static void tegra_cursor_atomic_update(struct drm_plane *plane,
+				       struct drm_plane_state *old_state)
+{
+	struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0);
+	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+	struct drm_plane_state *state = plane->state;
+	u32 value = CURSOR_CLIP_DISPLAY;
+
+	/* rien ne va plus */
+	if (!plane->state->crtc || !plane->state->fb)
+		return;
+
+	switch (state->crtc_w) {
 	case 32:
 		value |= CURSOR_SIZE_32x32;
 		break;
@@ -556,7 +630,9 @@ static int tegra_cursor_plane_update(struct drm_plane *plane,
 		break;
 
 	default:
-		return -EINVAL;
+		WARN(1, "cursor size %ux%u not supported\n", state->crtc_w,
+		     state->crtc_h);
+		return;
 	}
 
 	value |= (bo->paddr >> 10) & 0x3fffff;
@@ -582,23 +658,25 @@ static int tegra_cursor_plane_update(struct drm_plane *plane,
 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
 
 	/* position the cursor */
-	value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff);
+	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
 	/* apply changes */
 	tegra_dc_cursor_commit(dc);
 	tegra_dc_commit(dc);
-
-	return 0;
 }
 
-static int tegra_cursor_plane_disable(struct drm_plane *plane)
+static void tegra_cursor_atomic_disable(struct drm_plane *plane,
+					struct drm_plane_state *old_state)
 {
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
+	struct tegra_dc *dc;
 	u32 value;
 
-	if (!plane->crtc)
-		return 0;
+	/* rien ne va plus */
+	if (!old_state || !old_state->crtc)
+		return;
+
+	dc = to_tegra_dc(old_state->crtc);
 
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
@@ -606,14 +684,21 @@ static int tegra_cursor_plane_disable(struct drm_plane *plane)
 
 	tegra_dc_cursor_commit(dc);
 	tegra_dc_commit(dc);
-
-	return 0;
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
-	.update_plane = tegra_cursor_plane_update,
-	.disable_plane = tegra_cursor_plane_disable,
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+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,
 };
 
 static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
@@ -639,71 +724,21 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
-	return &plane->base;
-}
-
-static int tegra_overlay_plane_update(struct drm_plane *plane,
-				      struct drm_crtc *crtc,
-				      struct drm_framebuffer *fb, int crtc_x,
-				      int crtc_y, unsigned int crtc_w,
-				      unsigned int crtc_h, uint32_t src_x,
-				      uint32_t src_y, uint32_t src_w,
-				      uint32_t src_h)
-{
-	struct tegra_plane *p = to_tegra_plane(plane);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_dc_window window;
-	unsigned int i;
-	int err;
-
-	memset(&window, 0, sizeof(window));
-	window.src.x = src_x >> 16;
-	window.src.y = src_y >> 16;
-	window.src.w = src_w >> 16;
-	window.src.h = src_h >> 16;
-	window.dst.x = crtc_x;
-	window.dst.y = crtc_y;
-	window.dst.w = crtc_w;
-	window.dst.h = crtc_h;
-	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
-	window.bits_per_pixel = fb->bits_per_pixel;
-	window.bottom_up = tegra_fb_is_bottom_up(fb);
-
-	err = tegra_fb_get_tiling(fb, &window.tiling);
-	if (err < 0)
-		return err;
+	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
 
-	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
-		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
-
-		window.base[i] = bo->paddr + fb->offsets[i];
-
-		/*
-		 * Tegra doesn't support different strides for U and V planes
-		 * so we display a warning if the user tries to display a
-		 * framebuffer with such a configuration.
-		 */
-		if (i >= 2) {
-			if (fb->pitches[i] != window.stride[1])
-				DRM_ERROR("unsupported UV-plane configuration\n");
-		} else {
-			window.stride[i] = fb->pitches[i];
-		}
-	}
-
-	return tegra_dc_setup_window(dc, p->index, &window);
+	return &plane->base;
 }
 
 static void tegra_overlay_plane_destroy(struct drm_plane *plane)
 {
-	tegra_window_plane_disable(plane);
 	tegra_plane_destroy(plane);
 }
 
 static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
-	.update_plane = tegra_overlay_plane_update,
-	.disable_plane = tegra_window_plane_disable,
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_overlay_plane_destroy,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
 static const uint32_t tegra_overlay_plane_formats[] = {
@@ -716,6 +751,14 @@ static const uint32_t tegra_overlay_plane_formats[] = {
 	DRM_FORMAT_YUV422,
 };
 
+static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = {
+	.prepare_fb = tegra_plane_prepare_fb,
+	.cleanup_fb = tegra_plane_cleanup_fb,
+	.atomic_check = tegra_plane_atomic_check,
+	.atomic_update = tegra_plane_atomic_update,
+	.atomic_disable = tegra_plane_atomic_disable,
+};
+
 static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 						       struct tegra_dc *dc,
 						       unsigned int index)
@@ -742,6 +785,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
 		return ERR_PTR(err);
 	}
 
+	drm_plane_helper_add(&plane->base, &tegra_overlay_plane_helper_funcs);
+
 	return &plane->base;
 }
 
@@ -960,6 +1005,7 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
 static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1097,16 +1143,11 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 	return 0;
 }
 
-static int tegra_crtc_mode_set(struct drm_crtc *crtc,
-			       struct drm_display_mode *mode,
-			       struct drm_display_mode *adjusted,
-			       int x, int y, struct drm_framebuffer *old_fb)
+static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
-	struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 	struct tegra_dc *dc = to_tegra_dc(crtc);
-	struct tegra_dc_window window;
 	u32 value;
-	int err;
 
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
@@ -1120,36 +1161,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
 		value &= ~INTERLACE_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
 	}
-
-	/* setup window parameters */
-	memset(&window, 0, sizeof(window));
-	window.src.x = 0;
-	window.src.y = 0;
-	window.src.w = mode->hdisplay;
-	window.src.h = mode->vdisplay;
-	window.dst.x = 0;
-	window.dst.y = 0;
-	window.dst.w = mode->hdisplay;
-	window.dst.h = mode->vdisplay;
-	window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
-					&window.swap);
-	window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
-	window.stride[0] = crtc->primary->fb->pitches[0];
-	window.base[0] = bo->paddr;
-
-	err = tegra_dc_setup_window(dc, 0, &window);
-	if (err < 0)
-		dev_err(dc->dev, "failed to enable root plane\n");
-
-	return 0;
-}
-
-static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-				    struct drm_framebuffer *old_fb)
-{
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
-	return tegra_dc_set_base(dc, x, y, crtc->primary->fb);
 }
 
 static void tegra_crtc_prepare(struct drm_crtc *crtc)
@@ -1200,13 +1211,31 @@ static void tegra_crtc_commit(struct drm_crtc *crtc)
 	tegra_dc_commit(dc);
 }
 
+static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	return 0;
+}
+
+static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
+{
+}
+
+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 = tegra_crtc_mode_set,
-	.mode_set_base = tegra_crtc_mode_set_base,
+	.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,
+	.atomic_begin = tegra_crtc_atomic_begin,
+	.atomic_flush = tegra_crtc_atomic_flush,
 };
 
 static irqreturn_t tegra_dc_irq(int irq, void *data)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 121e022bdab0..3326df252c52 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -121,8 +121,8 @@ static int tegra_drm_unload(struct drm_device *drm)
 
 	drm_kms_helper_poll_fini(drm);
 	tegra_drm_fb_exit(drm);
-	drm_vblank_cleanup(drm);
 	drm_mode_config_cleanup(drm);
+	drm_vblank_cleanup(drm);
 
 	err = host1x_device_exit(device);
 	if (err < 0)
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index f8bfb45587d8..4bb06c119a18 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -17,6 +17,7 @@
 
 #include <linux/regulator/consumer.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
 
@@ -736,6 +737,7 @@ static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index d41530f1097c..bf868d54ef7e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -14,6 +14,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -781,6 +782,7 @@ static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index d027e3079c17..79530c23790b 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -9,6 +9,7 @@
 
 #include <linux/of_gpio.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 #include "drm.h"
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0c932f9dc12d..23ba40370eb2 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -9,6 +9,7 @@
 
 #include <linux/clk.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 
 #include "drm.h"
@@ -97,6 +98,7 @@ static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 9e67838c1562..e2f399363f09 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -15,6 +15,7 @@
 
 #include <soc/tegra/pmc.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 
@@ -737,6 +738,7 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = {
 	.detect = tegra_sor_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static int tegra_sor_connector_get_modes(struct drm_connector *connector)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 23/36] drm/tegra: Atomic conversion, phase 2
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Hook up the default ->reset() and ->atomic_duplicate_state() helpers.
This ensures that state objects are properly created and framebuffer
reference counts correctly maintained.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 12 ++++++++++++
 drivers/gpu/drm/tegra/drm.c  |  2 ++
 drivers/gpu/drm/tegra/dsi.c  |  2 ++
 drivers/gpu/drm/tegra/hdmi.c |  2 ++
 drivers/gpu/drm/tegra/rgb.c  |  2 ++
 drivers/gpu/drm/tegra/sor.c  |  2 ++
 6 files changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index e7efb6ca127c..cb2bd0088295 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -25,6 +25,7 @@
 #include "drm.h"
 #include "gem.h"
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
@@ -406,6 +407,8 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_primary_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -690,6 +693,8 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -738,6 +743,8 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_overlay_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -990,6 +997,9 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		drm_crtc_vblank_get(crtc);
 	}
 
+	if (crtc->primary->state)
+		drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
+
 	tegra_dc_set_base(dc, 0, 0, fb);
 	crtc->primary->fb = fb;
 
@@ -1005,6 +1015,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 3326df252c52..58a4ed192e61 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -77,6 +77,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 	if (err < 0)
 		goto fbdev;
 
+	drm_mode_config_reset(drm);
+
 	/*
 	 * We don't use the drm_irq_install() helpers provided by the DRM
 	 * core, so we need to set this manually in order to allow the
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 4bb06c119a18..9799f3edc3f8 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -734,9 +734,11 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 
 static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.dpms = tegra_dsi_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index bf868d54ef7e..728681a3f8c9 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -779,9 +779,11 @@ static void tegra_hdmi_connector_dpms(struct drm_connector *connector,
 
 static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
 	.dpms = tegra_hdmi_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 23ba40370eb2..78e3cb1529d0 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -95,9 +95,11 @@ static void tegra_rgb_connector_dpms(struct drm_connector *connector,
 
 static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
 	.dpms = tegra_rgb_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index e2f399363f09..2793e75c6573 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -735,9 +735,11 @@ tegra_sor_connector_detect(struct drm_connector *connector, bool force)
 
 static const struct drm_connector_funcs tegra_sor_connector_funcs = {
 	.dpms = tegra_sor_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_sor_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 23/36] drm/tegra: Atomic conversion, phase 2
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Hook up the default ->reset() and ->atomic_duplicate_state() helpers.
This ensures that state objects are properly created and framebuffer
reference counts correctly maintained.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 12 ++++++++++++
 drivers/gpu/drm/tegra/drm.c  |  2 ++
 drivers/gpu/drm/tegra/dsi.c  |  2 ++
 drivers/gpu/drm/tegra/hdmi.c |  2 ++
 drivers/gpu/drm/tegra/rgb.c  |  2 ++
 drivers/gpu/drm/tegra/sor.c  |  2 ++
 6 files changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index e7efb6ca127c..cb2bd0088295 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -25,6 +25,7 @@
 #include "drm.h"
 #include "gem.h"
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
@@ -406,6 +407,8 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_primary_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -690,6 +693,8 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -738,6 +743,8 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
 	.update_plane = drm_plane_helper_update,
 	.disable_plane = drm_plane_helper_disable,
 	.destroy = tegra_overlay_plane_destroy,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
@@ -990,6 +997,9 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		drm_crtc_vblank_get(crtc);
 	}
 
+	if (crtc->primary->state)
+		drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
+
 	tegra_dc_set_base(dc, 0, 0, fb);
 	crtc->primary->fb = fb;
 
@@ -1005,6 +1015,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 3326df252c52..58a4ed192e61 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -77,6 +77,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 	if (err < 0)
 		goto fbdev;
 
+	drm_mode_config_reset(drm);
+
 	/*
 	 * We don't use the drm_irq_install() helpers provided by the DRM
 	 * core, so we need to set this manually in order to allow the
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 4bb06c119a18..9799f3edc3f8 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -734,9 +734,11 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 
 static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.dpms = tegra_dsi_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index bf868d54ef7e..728681a3f8c9 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -779,9 +779,11 @@ static void tegra_hdmi_connector_dpms(struct drm_connector *connector,
 
 static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
 	.dpms = tegra_hdmi_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 23ba40370eb2..78e3cb1529d0 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -95,9 +95,11 @@ static void tegra_rgb_connector_dpms(struct drm_connector *connector,
 
 static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
 	.dpms = tegra_rgb_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index e2f399363f09..2793e75c6573 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -735,9 +735,11 @@ tegra_sor_connector_detect(struct drm_connector *connector, bool force)
 
 static const struct drm_connector_funcs tegra_sor_connector_funcs = {
 	.dpms = tegra_sor_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = tegra_sor_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 24/36] drm/tegra: Atomic conversion, phase 3, step 1
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Switch out the regular plane helpers for the atomic plane helpers. Also
use the default atomic helpers to implement the ->atomic_check() and
->atomic_commit() callbacks. The driver now exclusively uses the atomic
interfaces.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 12 ++++++------
 drivers/gpu/drm/tegra/drm.c |  4 ++++
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index cb2bd0088295..469bfa0fe096 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -404,8 +404,8 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane)
 }
 
 static const struct drm_plane_funcs tegra_primary_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_primary_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
@@ -690,8 +690,8 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
@@ -740,8 +740,8 @@ static void tegra_overlay_plane_destroy(struct drm_plane *plane)
 }
 
 static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_overlay_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 58a4ed192e61..0dd81d050ccb 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -10,6 +10,8 @@
 #include <linux/host1x.h>
 #include <linux/iommu.h>
 
+#include <drm/drm_atomic_helper.h>
+
 #include "drm.h"
 #include "gem.h"
 
@@ -29,6 +31,8 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	.output_poll_changed = tegra_fb_output_poll_changed,
 #endif
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
 };
 
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 24/36] drm/tegra: Atomic conversion, phase 3, step 1
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Switch out the regular plane helpers for the atomic plane helpers. Also
use the default atomic helpers to implement the ->atomic_check() and
->atomic_commit() callbacks. The driver now exclusively uses the atomic
interfaces.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 12 ++++++------
 drivers/gpu/drm/tegra/drm.c |  4 ++++
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index cb2bd0088295..469bfa0fe096 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -404,8 +404,8 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane)
 }
 
 static const struct drm_plane_funcs tegra_primary_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_primary_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
@@ -690,8 +690,8 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
@@ -740,8 +740,8 @@ static void tegra_overlay_plane_destroy(struct drm_plane *plane)
 }
 
 static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
-	.update_plane = drm_plane_helper_update,
-	.disable_plane = drm_plane_helper_disable,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_overlay_plane_destroy,
 	.reset = drm_atomic_helper_plane_reset,
 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 58a4ed192e61..0dd81d050ccb 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -10,6 +10,8 @@
 #include <linux/host1x.h>
 #include <linux/iommu.h>
 
+#include <drm/drm_atomic_helper.h>
+
 #include "drm.h"
 #include "gem.h"
 
@@ -29,6 +31,8 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	.output_poll_changed = tegra_fb_output_poll_changed,
 #endif
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
 };
 
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 25/36] drm/tegra: dc: Store clock setup in atomic state
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This allows the clock setup to be separated from the clock programming
and better matches the expectations of the atomic modesetting where no
code paths must fail during modeset.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 76 +++++++++++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/tegra/drm.h |  4 +++
 2 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 469bfa0fe096..1d8052a8d4c3 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -48,6 +48,22 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
 	return container_of(plane, struct tegra_plane, base);
 }
 
+struct tegra_dc_state {
+	struct drm_crtc_state base;
+
+	struct clk *clk;
+	unsigned long pclk;
+	unsigned int div;
+};
+
+static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
+{
+	if (state)
+		return container_of(state, struct tegra_dc_state, base);
+
+	return NULL;
+}
+
 static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
 {
 	u32 value = WIN_A_ACT_REQ << index;
@@ -1011,13 +1027,48 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
 	drm_crtc_cleanup(crtc);
 }
 
+static void tegra_crtc_reset(struct drm_crtc *crtc)
+{
+	struct tegra_dc_state *state;
+
+	kfree(crtc->state);
+	crtc->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state)
+		crtc->state = &state->base;
+}
+
+static struct drm_crtc_state *
+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);
+	if (!copy)
+		return NULL;
+
+	copy->base.mode_changed = false;
+	copy->base.planes_changed = false;
+	copy->base.event = NULL;
+
+	return &copy->base;
+}
+
+static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					    struct drm_crtc_state *state)
+{
+	kfree(state);
+}
+
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
-	.reset = drm_atomic_helper_crtc_reset,
-	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.reset = tegra_crtc_reset,
+	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
 };
 
 static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1155,6 +1206,25 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 	return 0;
 }
 
+int tegra_dc_state_setup_clock(struct tegra_dc *dc,
+			       struct drm_crtc_state *crtc_state,
+			       struct clk *clk, unsigned long pclk,
+			       unsigned int div)
+{
+	struct tegra_dc_state *state = to_dc_state(crtc_state);
+	int err;
+
+	err = clk_try_parent(dc->clk, clk);
+	if (err < 0)
+		return err;
+
+	state->clk = clk;
+	state->pclk = pclk;
+	state->div = div;
+
+	return 0;
+}
+
 static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bf1c47ec46b6..3db719de312f 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -180,6 +180,10 @@ 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,
+			       unsigned int div);
 
 struct tegra_output {
 	struct device_node *of_node;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 25/36] drm/tegra: dc: Store clock setup in atomic state
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This allows the clock setup to be separated from the clock programming
and better matches the expectations of the atomic modesetting where no
code paths must fail during modeset.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 76 +++++++++++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/tegra/drm.h |  4 +++
 2 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 469bfa0fe096..1d8052a8d4c3 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -48,6 +48,22 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
 	return container_of(plane, struct tegra_plane, base);
 }
 
+struct tegra_dc_state {
+	struct drm_crtc_state base;
+
+	struct clk *clk;
+	unsigned long pclk;
+	unsigned int div;
+};
+
+static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
+{
+	if (state)
+		return container_of(state, struct tegra_dc_state, base);
+
+	return NULL;
+}
+
 static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
 {
 	u32 value = WIN_A_ACT_REQ << index;
@@ -1011,13 +1027,48 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
 	drm_crtc_cleanup(crtc);
 }
 
+static void tegra_crtc_reset(struct drm_crtc *crtc)
+{
+	struct tegra_dc_state *state;
+
+	kfree(crtc->state);
+	crtc->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state)
+		crtc->state = &state->base;
+}
+
+static struct drm_crtc_state *
+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);
+	if (!copy)
+		return NULL;
+
+	copy->base.mode_changed = false;
+	copy->base.planes_changed = false;
+	copy->base.event = NULL;
+
+	return &copy->base;
+}
+
+static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+					    struct drm_crtc_state *state)
+{
+	kfree(state);
+}
+
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = tegra_dc_destroy,
-	.reset = drm_atomic_helper_crtc_reset,
-	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.reset = tegra_crtc_reset,
+	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
 };
 
 static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1155,6 +1206,25 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
 	return 0;
 }
 
+int tegra_dc_state_setup_clock(struct tegra_dc *dc,
+			       struct drm_crtc_state *crtc_state,
+			       struct clk *clk, unsigned long pclk,
+			       unsigned int div)
+{
+	struct tegra_dc_state *state = to_dc_state(crtc_state);
+	int err;
+
+	err = clk_try_parent(dc->clk, clk);
+	if (err < 0)
+		return err;
+
+	state->clk = clk;
+	state->pclk = pclk;
+	state->div = div;
+
+	return 0;
+}
+
 static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bf1c47ec46b6..3db719de312f 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -180,6 +180,10 @@ 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,
+			       unsigned int div);
 
 struct tegra_output {
 	struct device_node *of_node;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 26/36] drm/tegra: rgb: Implement ->atomic_check()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/rgb.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 78e3cb1529d0..be1b38936dbe 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -235,6 +235,47 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 		drm_panel_unprepare(output->panel);
 }
 
+static int
+tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_rgb *rgb = to_rgb(output);
+	unsigned int div;
+	int err;
+
+	/*
+	 * We may not want to change the frequency of the parent clock, since
+	 * it may be a parent for other peripherals. This is due to the fact
+	 * that on Tegra20 there's only a single clock dedicated to display
+	 * (pll_d_out0), whereas later generations have a second one that can
+	 * be used to independently drive a second output (pll_d2_out0).
+	 *
+	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
+	 * typically used as the parent clock for the display controllers.
+	 * But this comes at a cost: pll_p is the parent of several other
+	 * peripherals, so its frequency shouldn't change out of the blue.
+	 *
+	 * The best we can do at this point is to use the shift clock divider
+	 * and hope that the desired frequency can be matched (or at least
+	 * matched sufficiently close that the panel will still work).
+	 */
+	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
+	pclk = 0;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,
+					 pclk, div);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.dpms = tegra_rgb_encoder_dpms,
 	.mode_fixup = tegra_rgb_encoder_mode_fixup,
@@ -242,6 +283,7 @@ static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.commit = tegra_rgb_encoder_commit,
 	.mode_set = tegra_rgb_encoder_mode_set,
 	.disable = tegra_rgb_encoder_disable,
+	.atomic_check = tegra_rgb_encoder_atomic_check,
 };
 
 int tegra_dc_rgb_probe(struct tegra_dc *dc)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 26/36] drm/tegra: rgb: Implement ->atomic_check()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/rgb.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 78e3cb1529d0..be1b38936dbe 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -235,6 +235,47 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 		drm_panel_unprepare(output->panel);
 }
 
+static int
+tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_rgb *rgb = to_rgb(output);
+	unsigned int div;
+	int err;
+
+	/*
+	 * We may not want to change the frequency of the parent clock, since
+	 * it may be a parent for other peripherals. This is due to the fact
+	 * that on Tegra20 there's only a single clock dedicated to display
+	 * (pll_d_out0), whereas later generations have a second one that can
+	 * be used to independently drive a second output (pll_d2_out0).
+	 *
+	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
+	 * typically used as the parent clock for the display controllers.
+	 * But this comes at a cost: pll_p is the parent of several other
+	 * peripherals, so its frequency shouldn't change out of the blue.
+	 *
+	 * The best we can do at this point is to use the shift clock divider
+	 * and hope that the desired frequency can be matched (or at least
+	 * matched sufficiently close that the panel will still work).
+	 */
+	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
+	pclk = 0;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,
+					 pclk, div);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.dpms = tegra_rgb_encoder_dpms,
 	.mode_fixup = tegra_rgb_encoder_mode_fixup,
@@ -242,6 +283,7 @@ static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.commit = tegra_rgb_encoder_commit,
 	.mode_set = tegra_rgb_encoder_mode_set,
 	.disable = tegra_rgb_encoder_disable,
+	.atomic_check = tegra_rgb_encoder_atomic_check,
 };
 
 int tegra_dc_rgb_probe(struct tegra_dc *dc)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 27/36] drm/tegra: dsi: Implement ->atomic_check()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c | 269 ++++++++++++++++++++++++++++++++------------
 1 file changed, 196 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 9799f3edc3f8..55a6a95bda84 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -28,6 +28,28 @@
 #include "dsi.h"
 #include "mipi-phy.h"
 
+struct tegra_dsi_state {
+	struct drm_connector_state base;
+
+	struct mipi_dphy_timing timing;
+	unsigned long period;
+
+	unsigned int vrefresh;
+	unsigned int lanes;
+	unsigned long pclk;
+	unsigned long bclk;
+
+	enum tegra_dsi_format format;
+	unsigned int mul;
+	unsigned int div;
+};
+
+static inline struct tegra_dsi_state *
+to_dsi_state(struct drm_connector_state *state)
+{
+	return container_of(state, struct tegra_dsi_state, base);
+}
+
 struct tegra_dsi {
 	struct host1x_client client;
 	struct tegra_output output;
@@ -77,6 +99,11 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output)
 	return container_of(output, struct tegra_dsi, output);
 }
 
+static struct tegra_dsi_state *tegra_dsi_get_state(struct tegra_dsi *dsi)
+{
+	return to_dsi_state(dsi->output.connector.state);
+}
+
 static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg)
 {
 	return readl(dsi->regs + (reg << 2));
@@ -335,62 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
 	[11] = 0,
 };
 
-static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
+static void tegra_dsi_set_phy_timing(struct tegra_dsi *dsi,
+				     unsigned long period,
+				     const struct mipi_dphy_timing *timing)
 {
-	struct mipi_dphy_timing timing;
-	unsigned long period;
 	u32 value;
-	long rate;
-	int err;
-
-	rate = clk_get_rate(dsi->clk);
-	if (rate < 0)
-		return rate;
-
-	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate * 2);
 
-	err = mipi_dphy_timing_get_default(&timing, period);
-	if (err < 0)
-		return err;
-
-	err = mipi_dphy_timing_validate(&timing, period);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * The D-PHY timing fields below are expressed in byte-clock cycles,
-	 * so multiply the period by 8.
-	 */
-	period *= 8;
-
-	value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 |
-		DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 |
-		DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 |
-		DSI_TIMING_FIELD(timing.hsprepare, period, 1);
+	value = DSI_TIMING_FIELD(timing->hsexit, period, 1) << 24 |
+		DSI_TIMING_FIELD(timing->hstrail, period, 0) << 16 |
+		DSI_TIMING_FIELD(timing->hszero, period, 3) << 8 |
+		DSI_TIMING_FIELD(timing->hsprepare, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
 
-	value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 |
-		DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 |
-		DSI_TIMING_FIELD(timing.lpx, period, 1);
+	value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 |
+		DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 |
+		DSI_TIMING_FIELD(timing->lpx, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
 
-	value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 |
+	value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 |
 		DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
 
-	value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 |
-		DSI_TIMING_FIELD(timing.tago, period, 1);
+	value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 |
+		DSI_TIMING_FIELD(timing->tago, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
 
 	if (dsi->slave)
-		return tegra_dsi_set_phy_timing(dsi->slave);
-
-	return 0;
+		tegra_dsi_set_phy_timing(dsi->slave, period, timing);
 }
 
 static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
@@ -482,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi)
 	return dsi->lanes;
 }
 
-static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
-			       const struct drm_display_mode *mode)
+static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
+				const struct drm_display_mode *mode)
 {
 	unsigned int hact, hsw, hbp, hfp, i, mul, div;
-	enum tegra_dsi_format format;
+	struct tegra_dsi_state *state;
 	const u32 *pkt_seq;
 	u32 value;
-	int err;
+
+	/* XXX: pass in state into this function? */
+	if (dsi->master)
+		state = tegra_dsi_get_state(dsi->master);
+	else
+		state = tegra_dsi_get_state(dsi);
+
+	mul = state->mul;
+	div = state->div;
 
 	if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
 		DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
@@ -502,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 		pkt_seq = pkt_seq_command_mode;
 	}
 
-	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
-	if (err < 0)
-		return err;
-
-	err = tegra_dsi_get_format(dsi->format, &format);
-	if (err < 0)
-		return err;
-
-	value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
+	value = DSI_CONTROL_CHANNEL(0) |
+		DSI_CONTROL_FORMAT(state->format) |
 		DSI_CONTROL_LANES(dsi->lanes - 1) |
 		DSI_CONTROL_SOURCE(pipe);
 	tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -589,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 
 		/* set SOL delay */
 		if (dsi->master || dsi->slave) {
-			unsigned int lanes = tegra_dsi_get_lanes(dsi);
 			unsigned long delay, bclk, bclk_ganged;
+			unsigned int lanes = state->lanes;
 
 			/* SOL to valid, valid to FIFO and FIFO write delay */
 			delay = 4 + 4 + 2;
@@ -610,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 	}
 
 	if (dsi->slave) {
-		err = tegra_dsi_configure(dsi->slave, pipe, mode);
-		if (err < 0)
-			return err;
+		tegra_dsi_configure(dsi->slave, pipe, mode);
 
 		/*
 		 * TODO: Support modes other than symmetrical left-right
@@ -622,8 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 		tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2,
 					mode->hdisplay / 2);
 	}
-
-	return 0;
 }
 
 static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
@@ -732,13 +730,38 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 {
 }
 
+static void tegra_dsi_connector_reset(struct drm_connector *connector)
+{
+	struct tegra_dsi_state *state;
+
+	kfree(connector->state);
+	connector->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state)
+		connector->state = &state->base;
+}
+
+static struct drm_connector_state *
+tegra_dsi_connector_duplicate_state(struct drm_connector *connector)
+{
+	struct tegra_dsi_state *state = to_dsi_state(connector->state);
+	struct tegra_dsi_state *copy;
+
+	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+	if (!copy)
+		return NULL;
+
+	return &copy->base;
+}
+
 static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.dpms = tegra_dsi_connector_dpms,
-	.reset = drm_atomic_helper_connector_reset,
+	.reset = tegra_dsi_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_duplicate_state = tegra_dsi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
@@ -771,7 +794,9 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	unsigned int mul, div, scdiv, vrefresh, lanes;
 	struct tegra_dsi *dsi = to_dsi(output);
+	struct mipi_dphy_timing timing;
 	unsigned long pclk, bclk, plld;
+	unsigned long period;
 	int err;
 
 	lanes = tegra_dsi_get_lanes(dsi);
@@ -792,6 +817,7 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 	 * Compute bit clock and round up to the next MHz.
 	 */
 	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
+	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
 
 	/*
 	 * We divide the frequency by two here, but we make up for that by
@@ -827,12 +853,22 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 
 	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
 
-	err = tegra_dsi_set_phy_timing(dsi);
+	err = mipi_dphy_timing_get_default(&timing, period);
+	if (err < 0)
+		return err;
+
+	err = mipi_dphy_timing_validate(&timing, period);
 	if (err < 0) {
-		dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err);
-		return false;
+		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
+		return err;
 	}
 
+	/*
+	 * The D-PHY timing fields are expressed in byte-clock cycles, so
+	 * multiply the period by 8.
+	 */
+	tegra_dsi_set_phy_timing(dsi, period * 8, &timing);
+
 	return true;
 }
 
@@ -851,19 +887,24 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_dsi *dsi = to_dsi(output);
+	struct tegra_dsi_state *state;
 	u32 value;
-	int err;
 
+	state = tegra_dsi_get_state(dsi);
 
-	err = tegra_dsi_configure(dsi, dc->pipe, mode);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to configure DSI: %d\n", err);
-		return;
-	}
+	tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh);
+
+	/*
+	 * The D-PHY timing fields are expressed in byte-clock cycles, so
+	 * multiply the period by 8.
+	 */
+	tegra_dsi_set_phy_timing(dsi, state->period * 8, &state->timing);
 
 	if (output->panel)
 		drm_panel_prepare(output->panel);
 
+	tegra_dsi_configure(dsi, dc->pipe, mode);
+
 	/* enable display controller */
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value |= DSI_ENABLE;
@@ -929,6 +970,87 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
 	return;
 }
 
+static int
+tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dsi_state *state = to_dsi_state(conn_state);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	unsigned int scdiv;
+	unsigned long plld;
+	int err;
+
+	state->pclk = crtc_state->mode.clock * 1000;
+
+	err = tegra_dsi_get_muldiv(dsi->format, &state->mul, &state->div);
+	if (err < 0)
+		return err;
+
+	state->lanes = tegra_dsi_get_lanes(dsi);
+
+	err = tegra_dsi_get_format(dsi->format, &state->format);
+	if (err < 0)
+		return err;
+
+	state->vrefresh = drm_mode_vrefresh(&crtc_state->mode);
+
+	/* compute byte clock */
+	state->bclk = (state->pclk * state->mul) / (state->div * state->lanes);
+
+	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", state->mul, state->div,
+		      state->lanes);
+	DRM_DEBUG_KMS("format: %u, vrefresh: %u\n", state->format,
+		      state->vrefresh);
+	DRM_DEBUG_KMS("bclk: %lu\n", state->bclk);
+
+	/*
+	 * Compute bit clock and round up to the next MHz.
+	 */
+	plld = DIV_ROUND_UP(state->bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
+
+	err = mipi_dphy_timing_get_default(&state->timing, state->period);
+	if (err < 0)
+		return err;
+
+	err = mipi_dphy_timing_validate(&state->timing, state->period);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * We divide the frequency by two here, but we make up for that by
+	 * setting the shift clock divider (further below) to half of the
+	 * correct value.
+	 */
+	plld /= 2;
+
+	/*
+	 * Derive pixel clock from bit clock using the shift clock divider.
+	 * Note that this is only half of what we would expect, but we need
+	 * that to make up for the fact that we divided the bit clock by a
+	 * factor of two above.
+	 *
+	 * It's not clear exactly why this is necessary, but the display is
+	 * not working properly otherwise. Perhaps the PLLs cannot generate
+	 * frequencies sufficiently high.
+	 */
+	scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, dsi->clk_parent,
+					 plld, scdiv);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.dpms = tegra_dsi_encoder_dpms,
 	.mode_fixup = tegra_dsi_encoder_mode_fixup,
@@ -936,6 +1058,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.commit = tegra_dsi_encoder_commit,
 	.mode_set = tegra_dsi_encoder_mode_set,
 	.disable = tegra_dsi_encoder_disable,
+	.atomic_check = tegra_dsi_encoder_atomic_check,
 };
 
 static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 27/36] drm/tegra: dsi: Implement ->atomic_check()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c | 269 ++++++++++++++++++++++++++++++++------------
 1 file changed, 196 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 9799f3edc3f8..55a6a95bda84 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -28,6 +28,28 @@
 #include "dsi.h"
 #include "mipi-phy.h"
 
+struct tegra_dsi_state {
+	struct drm_connector_state base;
+
+	struct mipi_dphy_timing timing;
+	unsigned long period;
+
+	unsigned int vrefresh;
+	unsigned int lanes;
+	unsigned long pclk;
+	unsigned long bclk;
+
+	enum tegra_dsi_format format;
+	unsigned int mul;
+	unsigned int div;
+};
+
+static inline struct tegra_dsi_state *
+to_dsi_state(struct drm_connector_state *state)
+{
+	return container_of(state, struct tegra_dsi_state, base);
+}
+
 struct tegra_dsi {
 	struct host1x_client client;
 	struct tegra_output output;
@@ -77,6 +99,11 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output)
 	return container_of(output, struct tegra_dsi, output);
 }
 
+static struct tegra_dsi_state *tegra_dsi_get_state(struct tegra_dsi *dsi)
+{
+	return to_dsi_state(dsi->output.connector.state);
+}
+
 static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg)
 {
 	return readl(dsi->regs + (reg << 2));
@@ -335,62 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
 	[11] = 0,
 };
 
-static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
+static void tegra_dsi_set_phy_timing(struct tegra_dsi *dsi,
+				     unsigned long period,
+				     const struct mipi_dphy_timing *timing)
 {
-	struct mipi_dphy_timing timing;
-	unsigned long period;
 	u32 value;
-	long rate;
-	int err;
-
-	rate = clk_get_rate(dsi->clk);
-	if (rate < 0)
-		return rate;
-
-	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate * 2);
 
-	err = mipi_dphy_timing_get_default(&timing, period);
-	if (err < 0)
-		return err;
-
-	err = mipi_dphy_timing_validate(&timing, period);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * The D-PHY timing fields below are expressed in byte-clock cycles,
-	 * so multiply the period by 8.
-	 */
-	period *= 8;
-
-	value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 |
-		DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 |
-		DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 |
-		DSI_TIMING_FIELD(timing.hsprepare, period, 1);
+	value = DSI_TIMING_FIELD(timing->hsexit, period, 1) << 24 |
+		DSI_TIMING_FIELD(timing->hstrail, period, 0) << 16 |
+		DSI_TIMING_FIELD(timing->hszero, period, 3) << 8 |
+		DSI_TIMING_FIELD(timing->hsprepare, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
 
-	value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 |
-		DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 |
-		DSI_TIMING_FIELD(timing.lpx, period, 1);
+	value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 |
+		DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 |
+		DSI_TIMING_FIELD(timing->lpx, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
 
-	value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 |
+	value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 |
 		DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
 	tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
 
-	value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 |
-		DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 |
-		DSI_TIMING_FIELD(timing.tago, period, 1);
+	value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 |
+		DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 |
+		DSI_TIMING_FIELD(timing->tago, period, 1);
 	tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
 
 	if (dsi->slave)
-		return tegra_dsi_set_phy_timing(dsi->slave);
-
-	return 0;
+		tegra_dsi_set_phy_timing(dsi->slave, period, timing);
 }
 
 static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
@@ -482,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi)
 	return dsi->lanes;
 }
 
-static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
-			       const struct drm_display_mode *mode)
+static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
+				const struct drm_display_mode *mode)
 {
 	unsigned int hact, hsw, hbp, hfp, i, mul, div;
-	enum tegra_dsi_format format;
+	struct tegra_dsi_state *state;
 	const u32 *pkt_seq;
 	u32 value;
-	int err;
+
+	/* XXX: pass in state into this function? */
+	if (dsi->master)
+		state = tegra_dsi_get_state(dsi->master);
+	else
+		state = tegra_dsi_get_state(dsi);
+
+	mul = state->mul;
+	div = state->div;
 
 	if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
 		DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
@@ -502,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 		pkt_seq = pkt_seq_command_mode;
 	}
 
-	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
-	if (err < 0)
-		return err;
-
-	err = tegra_dsi_get_format(dsi->format, &format);
-	if (err < 0)
-		return err;
-
-	value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
+	value = DSI_CONTROL_CHANNEL(0) |
+		DSI_CONTROL_FORMAT(state->format) |
 		DSI_CONTROL_LANES(dsi->lanes - 1) |
 		DSI_CONTROL_SOURCE(pipe);
 	tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -589,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 
 		/* set SOL delay */
 		if (dsi->master || dsi->slave) {
-			unsigned int lanes = tegra_dsi_get_lanes(dsi);
 			unsigned long delay, bclk, bclk_ganged;
+			unsigned int lanes = state->lanes;
 
 			/* SOL to valid, valid to FIFO and FIFO write delay */
 			delay = 4 + 4 + 2;
@@ -610,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 	}
 
 	if (dsi->slave) {
-		err = tegra_dsi_configure(dsi->slave, pipe, mode);
-		if (err < 0)
-			return err;
+		tegra_dsi_configure(dsi->slave, pipe, mode);
 
 		/*
 		 * TODO: Support modes other than symmetrical left-right
@@ -622,8 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
 		tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2,
 					mode->hdisplay / 2);
 	}
-
-	return 0;
 }
 
 static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
@@ -732,13 +730,38 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 {
 }
 
+static void tegra_dsi_connector_reset(struct drm_connector *connector)
+{
+	struct tegra_dsi_state *state;
+
+	kfree(connector->state);
+	connector->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state)
+		connector->state = &state->base;
+}
+
+static struct drm_connector_state *
+tegra_dsi_connector_duplicate_state(struct drm_connector *connector)
+{
+	struct tegra_dsi_state *state = to_dsi_state(connector->state);
+	struct tegra_dsi_state *copy;
+
+	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+	if (!copy)
+		return NULL;
+
+	return &copy->base;
+}
+
 static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
 	.dpms = tegra_dsi_connector_dpms,
-	.reset = drm_atomic_helper_connector_reset,
+	.reset = tegra_dsi_connector_reset,
 	.detect = tegra_output_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = tegra_output_connector_destroy,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_duplicate_state = tegra_dsi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
@@ -771,7 +794,9 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	unsigned int mul, div, scdiv, vrefresh, lanes;
 	struct tegra_dsi *dsi = to_dsi(output);
+	struct mipi_dphy_timing timing;
 	unsigned long pclk, bclk, plld;
+	unsigned long period;
 	int err;
 
 	lanes = tegra_dsi_get_lanes(dsi);
@@ -792,6 +817,7 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 	 * Compute bit clock and round up to the next MHz.
 	 */
 	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
+	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
 
 	/*
 	 * We divide the frequency by two here, but we make up for that by
@@ -827,12 +853,22 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 
 	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
 
-	err = tegra_dsi_set_phy_timing(dsi);
+	err = mipi_dphy_timing_get_default(&timing, period);
+	if (err < 0)
+		return err;
+
+	err = mipi_dphy_timing_validate(&timing, period);
 	if (err < 0) {
-		dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err);
-		return false;
+		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
+		return err;
 	}
 
+	/*
+	 * The D-PHY timing fields are expressed in byte-clock cycles, so
+	 * multiply the period by 8.
+	 */
+	tegra_dsi_set_phy_timing(dsi, period * 8, &timing);
+
 	return true;
 }
 
@@ -851,19 +887,24 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_dsi *dsi = to_dsi(output);
+	struct tegra_dsi_state *state;
 	u32 value;
-	int err;
 
+	state = tegra_dsi_get_state(dsi);
 
-	err = tegra_dsi_configure(dsi, dc->pipe, mode);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to configure DSI: %d\n", err);
-		return;
-	}
+	tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh);
+
+	/*
+	 * The D-PHY timing fields are expressed in byte-clock cycles, so
+	 * multiply the period by 8.
+	 */
+	tegra_dsi_set_phy_timing(dsi, state->period * 8, &state->timing);
 
 	if (output->panel)
 		drm_panel_prepare(output->panel);
 
+	tegra_dsi_configure(dsi, dc->pipe, mode);
+
 	/* enable display controller */
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value |= DSI_ENABLE;
@@ -929,6 +970,87 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
 	return;
 }
 
+static int
+tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dsi_state *state = to_dsi_state(conn_state);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	struct tegra_dsi *dsi = to_dsi(output);
+	unsigned int scdiv;
+	unsigned long plld;
+	int err;
+
+	state->pclk = crtc_state->mode.clock * 1000;
+
+	err = tegra_dsi_get_muldiv(dsi->format, &state->mul, &state->div);
+	if (err < 0)
+		return err;
+
+	state->lanes = tegra_dsi_get_lanes(dsi);
+
+	err = tegra_dsi_get_format(dsi->format, &state->format);
+	if (err < 0)
+		return err;
+
+	state->vrefresh = drm_mode_vrefresh(&crtc_state->mode);
+
+	/* compute byte clock */
+	state->bclk = (state->pclk * state->mul) / (state->div * state->lanes);
+
+	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", state->mul, state->div,
+		      state->lanes);
+	DRM_DEBUG_KMS("format: %u, vrefresh: %u\n", state->format,
+		      state->vrefresh);
+	DRM_DEBUG_KMS("bclk: %lu\n", state->bclk);
+
+	/*
+	 * Compute bit clock and round up to the next MHz.
+	 */
+	plld = DIV_ROUND_UP(state->bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
+
+	err = mipi_dphy_timing_get_default(&state->timing, state->period);
+	if (err < 0)
+		return err;
+
+	err = mipi_dphy_timing_validate(&state->timing, state->period);
+	if (err < 0) {
+		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * We divide the frequency by two here, but we make up for that by
+	 * setting the shift clock divider (further below) to half of the
+	 * correct value.
+	 */
+	plld /= 2;
+
+	/*
+	 * Derive pixel clock from bit clock using the shift clock divider.
+	 * Note that this is only half of what we would expect, but we need
+	 * that to make up for the fact that we divided the bit clock by a
+	 * factor of two above.
+	 *
+	 * It's not clear exactly why this is necessary, but the display is
+	 * not working properly otherwise. Perhaps the PLLs cannot generate
+	 * frequencies sufficiently high.
+	 */
+	scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, dsi->clk_parent,
+					 plld, scdiv);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.dpms = tegra_dsi_encoder_dpms,
 	.mode_fixup = tegra_dsi_encoder_mode_fixup,
@@ -936,6 +1058,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.commit = tegra_dsi_encoder_commit,
 	.mode_set = tegra_dsi_encoder_mode_set,
 	.disable = tegra_dsi_encoder_disable,
+	.atomic_check = tegra_dsi_encoder_atomic_check,
 };
 
 static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 28/36] drm/tegra: hdmi: Implement ->atomic_check()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/hdmi.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 728681a3f8c9..abb1ea0385ec 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1081,6 +1081,27 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
 	}
 }
 
+static int
+tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+				struct drm_crtc_state *crtc_state,
+				struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	int err;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, hdmi->clk_parent,
+					 pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.dpms = tegra_hdmi_encoder_dpms,
 	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
@@ -1088,6 +1109,7 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.commit = tegra_hdmi_encoder_commit,
 	.mode_set = tegra_hdmi_encoder_mode_set,
 	.disable = tegra_hdmi_encoder_disable,
+	.atomic_check = tegra_hdmi_encoder_atomic_check,
 };
 
 static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 28/36] drm/tegra: hdmi: Implement ->atomic_check()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/hdmi.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 728681a3f8c9..abb1ea0385ec 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1081,6 +1081,27 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
 	}
 }
 
+static int
+tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+				struct drm_crtc_state *crtc_state,
+				struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_hdmi *hdmi = to_hdmi(output);
+	int err;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, hdmi->clk_parent,
+					 pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return err;
+}
+
 static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.dpms = tegra_hdmi_encoder_dpms,
 	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
@@ -1088,6 +1109,7 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.commit = tegra_hdmi_encoder_commit,
 	.mode_set = tegra_hdmi_encoder_mode_set,
 	.disable = tegra_hdmi_encoder_disable,
+	.atomic_check = tegra_hdmi_encoder_atomic_check,
 };
 
 static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 29/36] drm/tegra: sor: Implement ->atomic_check()
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/sor.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 2793e75c6573..7463ea02a083 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1320,6 +1320,27 @@ unlock:
 	mutex_unlock(&sor->lock);
 }
 
+static int
+tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
+					 pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
 static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.dpms = tegra_sor_encoder_dpms,
 	.mode_fixup = tegra_sor_encoder_mode_fixup,
@@ -1327,6 +1348,7 @@ static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.commit = tegra_sor_encoder_commit,
 	.mode_set = tegra_sor_encoder_mode_set,
 	.disable = tegra_sor_encoder_disable,
+	.atomic_check = tegra_sor_encoder_atomic_check,
 };
 
 static int tegra_sor_init(struct host1x_client *client)
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 29/36] drm/tegra: sor: Implement ->atomic_check()
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/sor.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 2793e75c6573..7463ea02a083 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1320,6 +1320,27 @@ unlock:
 	mutex_unlock(&sor->lock);
 }
 
+static int
+tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
+			       struct drm_crtc_state *crtc_state,
+			       struct drm_connector_state *conn_state)
+{
+	struct tegra_output *output = encoder_to_output(encoder);
+	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
+	unsigned long pclk = crtc_state->mode.clock * 1000;
+	struct tegra_sor *sor = to_sor(output);
+	int err;
+
+	err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
+					 pclk, 0);
+	if (err < 0) {
+		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
 static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.dpms = tegra_sor_encoder_dpms,
 	.mode_fixup = tegra_sor_encoder_mode_fixup,
@@ -1327,6 +1348,7 @@ static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.commit = tegra_sor_encoder_commit,
 	.mode_set = tegra_sor_encoder_mode_set,
 	.disable = tegra_sor_encoder_disable,
+	.atomic_check = tegra_sor_encoder_atomic_check,
 };
 
 static int tegra_sor_init(struct host1x_client *client)
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 30/36] drm/tegra: dc: Use atomic clock state in modeset
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All clock state is now stored in the display controller's atomic state,
so the output drivers no longer need to call back into the display
controller driver to set up the clock. This is also required to make
sure no hardware changes are made before validating a configuration.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 1d8052a8d4c3..b4a2a17caf05 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1225,12 +1225,49 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 	return 0;
 }
 
+static void tegra_dc_commit_state(struct tegra_dc *dc,
+				  struct tegra_dc_state *state)
+{
+	u32 value;
+	int err;
+
+	err = clk_set_parent(dc->clk, state->clk);
+	if (err < 0)
+		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
+
+	/*
+	 * Outputs may not want to change the parent clock rate. This is only
+	 * relevant to Tegra20 where only a single display PLL is available.
+	 * Since that PLL would typically be used for HDMI, an internal LVDS
+	 * panel would need to be driven by some other clock such as PLL_P
+	 * which is shared with other peripherals. Changing the clock rate
+	 * should therefore be avoided.
+	 */
+	if (state->pclk > 0) {
+		err = clk_set_rate(state->clk, state->pclk);
+		if (err < 0)
+			dev_err(dc->dev,
+				"failed to set clock rate to %lu Hz\n",
+				state->pclk);
+	}
+
+	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
+		      state->div);
+	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
+
+	value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
+}
+
 static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
 
+	tegra_dc_commit_state(dc, state);
+
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
 
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 30/36] drm/tegra: dc: Use atomic clock state in modeset
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All clock state is now stored in the display controller's atomic state,
so the output drivers no longer need to call back into the display
controller driver to set up the clock. This is also required to make
sure no hardware changes are made before validating a configuration.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 1d8052a8d4c3..b4a2a17caf05 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1225,12 +1225,49 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 	return 0;
 }
 
+static void tegra_dc_commit_state(struct tegra_dc *dc,
+				  struct tegra_dc_state *state)
+{
+	u32 value;
+	int err;
+
+	err = clk_set_parent(dc->clk, state->clk);
+	if (err < 0)
+		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
+
+	/*
+	 * Outputs may not want to change the parent clock rate. This is only
+	 * relevant to Tegra20 where only a single display PLL is available.
+	 * Since that PLL would typically be used for HDMI, an internal LVDS
+	 * panel would need to be driven by some other clock such as PLL_P
+	 * which is shared with other peripherals. Changing the clock rate
+	 * should therefore be avoided.
+	 */
+	if (state->pclk > 0) {
+		err = clk_set_rate(state->clk, state->pclk);
+		if (err < 0)
+			dev_err(dc->dev,
+				"failed to set clock rate to %lu Hz\n",
+				state->pclk);
+	}
+
+	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
+		      state->div);
+	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
+
+	value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
+}
+
 static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
 
+	tegra_dc_commit_state(dc, state);
+
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
 
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 31/36] drm/tegra: Atomic conversion, phase 3, step 2
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Replace drm_crtc_helper_set_config() by drm_atomic_helper_set_config().
All drivers have now been converted to use ->atomic_check() to set the
atomic state, therefore the atomic mode setting helpers can be used.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index b4a2a17caf05..9a934a6077a6 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1064,7 +1064,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
-	.set_config = drm_crtc_helper_set_config,
+	.set_config = drm_atomic_helper_set_config,
 	.destroy = tegra_dc_destroy,
 	.reset = tegra_crtc_reset,
 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 31/36] drm/tegra: Atomic conversion, phase 3, step 2
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Replace drm_crtc_helper_set_config() by drm_atomic_helper_set_config().
All drivers have now been converted to use ->atomic_check() to set the
atomic state, therefore the atomic mode setting helpers can be used.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index b4a2a17caf05..9a934a6077a6 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1064,7 +1064,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = tegra_dc_page_flip,
-	.set_config = drm_crtc_helper_set_config,
+	.set_config = drm_atomic_helper_set_config,
 	.destroy = tegra_dc_destroy,
 	.reset = tegra_crtc_reset,
 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 32/36] drm/tegra: Atomic conversion, phase 3, step 3
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Provide a custom ->atomic_commit() implementation which supports async
commits. The generic atomic page-flip helper can use this to implement
page-flipping.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 129 ++++----------------------------------------
 drivers/gpu/drm/tegra/drm.c |  84 ++++++++++++++++++++++++++++-
 drivers/gpu/drm/tegra/drm.h |   6 +++
 3 files changed, 100 insertions(+), 119 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 9a934a6077a6..52ae563cb531 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -827,99 +827,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
 	return 0;
 }
 
-static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
-			     struct drm_framebuffer *fb)
-{
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
-	unsigned int h_offset = 0, v_offset = 0;
-	struct tegra_bo_tiling tiling;
-	unsigned long value, flags;
-	unsigned int format, swap;
-	int err;
-
-	err = tegra_fb_get_tiling(fb, &tiling);
-	if (err < 0)
-		return err;
-
-	spin_lock_irqsave(&dc->lock, flags);
-
-	tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
-
-	value = fb->offsets[0] + y * fb->pitches[0] +
-		x * fb->bits_per_pixel / 8;
-
-	tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
-	tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
-
-	format = tegra_dc_format(fb->pixel_format, &swap);
-	tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
-	tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP);
-
-	if (dc->soc->supports_block_linear) {
-		unsigned long height = tiling.value;
-
-		switch (tiling.mode) {
-		case TEGRA_BO_TILING_MODE_PITCH:
-			value = DC_WINBUF_SURFACE_KIND_PITCH;
-			break;
-
-		case TEGRA_BO_TILING_MODE_TILED:
-			value = DC_WINBUF_SURFACE_KIND_TILED;
-			break;
-
-		case TEGRA_BO_TILING_MODE_BLOCK:
-			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
-				DC_WINBUF_SURFACE_KIND_BLOCK;
-			break;
-		}
-
-		tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND);
-	} else {
-		switch (tiling.mode) {
-		case TEGRA_BO_TILING_MODE_PITCH:
-			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
-				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
-			break;
-
-		case TEGRA_BO_TILING_MODE_TILED:
-			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
-				DC_WIN_BUFFER_ADDR_MODE_TILE;
-			break;
-
-		case TEGRA_BO_TILING_MODE_BLOCK:
-			DRM_ERROR("hardware doesn't support block linear mode\n");
-			spin_unlock_irqrestore(&dc->lock, flags);
-			return -EINVAL;
-		}
-
-		tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
-	}
-
-	/* make sure bottom-up buffers are properly displayed */
-	if (tegra_fb_is_bottom_up(fb)) {
-		value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-		value |= V_DIRECTION;
-		tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
-		v_offset += fb->height - 1;
-	} else {
-		value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-		value &= ~V_DIRECTION;
-		tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-	}
-
-	tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
-	tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
-
-	value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-
-	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
-}
-
 void tegra_dc_enable_vblank(struct tegra_dc *dc)
 {
 	unsigned long value, flags;
@@ -998,30 +905,6 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
 	spin_unlock_irqrestore(&drm->event_lock, flags);
 }
 
-static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-			      struct drm_pending_vblank_event *event, uint32_t page_flip_flags)
-{
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
-	if (dc->event)
-		return -EBUSY;
-
-	if (event) {
-		event->pipe = pipe;
-		dc->event = event;
-		drm_crtc_vblank_get(crtc);
-	}
-
-	if (crtc->primary->state)
-		drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
-
-	tegra_dc_set_base(dc, 0, 0, fb);
-	crtc->primary->fb = fb;
-
-	return 0;
-}
-
 static void tegra_dc_destroy(struct drm_crtc *crtc)
 {
 	drm_crtc_cleanup(crtc);
@@ -1063,7 +946,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
 }
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
-	.page_flip = tegra_dc_page_flip,
+	.page_flip = drm_atomic_helper_page_flip,
 	.set_config = drm_atomic_helper_set_config,
 	.destroy = tegra_dc_destroy,
 	.reset = tegra_crtc_reset,
@@ -1338,6 +1221,16 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
 
 static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 {
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	if (crtc->state->event) {
+		crtc->state->event->pipe = drm_crtc_index(crtc);
+
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		dc->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
 }
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 0dd81d050ccb..5b81b44af9e4 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -10,6 +10,7 @@
 #include <linux/host1x.h>
 #include <linux/iommu.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 
 #include "drm.h"
@@ -26,13 +27,90 @@ struct tegra_drm_file {
 	struct list_head contexts;
 };
 
+static void tegra_atomic_schedule(struct tegra_drm *tegra,
+				  struct drm_atomic_state *state)
+{
+	tegra->commit.state = state;
+	schedule_work(&tegra->commit.work);
+}
+
+static void tegra_atomic_complete(struct tegra_drm *tegra,
+				  struct drm_atomic_state *state)
+{
+	struct drm_device *drm = tegra->drm;
+
+	/*
+	 * Everything below can be run asynchronously without the need to grab
+	 * any modeset locks at all under one condition: It must be guaranteed
+	 * that the asynchronous work has either been cancelled (if the driver
+	 * supports it, which at least requires that the framebuffers get
+	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
+	 * before the new state gets committed on the software side with
+	 * drm_atomic_helper_swap_state().
+	 *
+	 * This scheme allows new atomic state updates to be prepared and
+	 * checked in parallel to the asynchronous completion of the previous
+	 * update. Which is important since compositors need to figure out the
+	 * composition of the next frame right after having submitted the
+	 * current layout.
+	 */
+
+	drm_atomic_helper_commit_pre_planes(drm, state);
+	drm_atomic_helper_commit_planes(drm, state);
+	drm_atomic_helper_commit_post_planes(drm, state);
+
+	drm_atomic_helper_wait_for_vblanks(drm, state);
+
+	drm_atomic_helper_cleanup_planes(drm, state);
+	drm_atomic_state_free(state);
+}
+
+static void tegra_atomic_work(struct work_struct *work)
+{
+	struct tegra_drm *tegra = container_of(work, struct tegra_drm,
+					       commit.work);
+
+	tegra_atomic_complete(tegra, tegra->commit.state);
+}
+
+static int tegra_atomic_commit(struct drm_device *drm,
+			       struct drm_atomic_state *state, bool async)
+{
+	struct tegra_drm *tegra = drm->dev_private;
+	int err;
+
+	err = drm_atomic_helper_prepare_planes(drm, state);
+	if (err)
+		return err;
+
+	/* serialize outstanding asynchronous commits */
+	mutex_lock(&tegra->commit.lock);
+	flush_work(&tegra->commit.work);
+
+	/*
+	 * This is the point of no return - everything below never fails except
+	 * when the hw goes bonghits. Which means we can commit the new state on
+	 * the software side now.
+	 */
+
+	drm_atomic_helper_swap_state(drm, state);
+
+	if (async)
+		tegra_atomic_schedule(tegra, state);
+	else
+		tegra_atomic_complete(tegra, state);
+
+	mutex_unlock(&tegra->commit.lock);
+	return 0;
+}
+
 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 	.fb_create = tegra_fb_create,
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	.output_poll_changed = tegra_fb_output_poll_changed,
 #endif
 	.atomic_check = drm_atomic_helper_check,
-	.atomic_commit = drm_atomic_helper_commit,
+	.atomic_commit = tegra_atomic_commit,
 };
 
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
@@ -58,6 +136,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	mutex_init(&tegra->clients_lock);
 	INIT_LIST_HEAD(&tegra->clients);
+
+	mutex_init(&tegra->commit.lock);
+	INIT_WORK(&tegra->commit.work, tegra_atomic_work);
+
 	drm->dev_private = tegra;
 	tegra->drm = drm;
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 3db719de312f..b1c7027b26e7 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -50,6 +50,12 @@ struct tegra_drm {
 #endif
 
 	unsigned int pitch_align;
+
+	struct {
+		struct drm_atomic_state *state;
+		struct work_struct work;
+		struct mutex lock;
+	} commit;
 };
 
 struct tegra_drm_client;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 32/36] drm/tegra: Atomic conversion, phase 3, step 3
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Provide a custom ->atomic_commit() implementation which supports async
commits. The generic atomic page-flip helper can use this to implement
page-flipping.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 129 ++++----------------------------------------
 drivers/gpu/drm/tegra/drm.c |  84 ++++++++++++++++++++++++++++-
 drivers/gpu/drm/tegra/drm.h |   6 +++
 3 files changed, 100 insertions(+), 119 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 9a934a6077a6..52ae563cb531 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -827,99 +827,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
 	return 0;
 }
 
-static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
-			     struct drm_framebuffer *fb)
-{
-	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
-	unsigned int h_offset = 0, v_offset = 0;
-	struct tegra_bo_tiling tiling;
-	unsigned long value, flags;
-	unsigned int format, swap;
-	int err;
-
-	err = tegra_fb_get_tiling(fb, &tiling);
-	if (err < 0)
-		return err;
-
-	spin_lock_irqsave(&dc->lock, flags);
-
-	tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
-
-	value = fb->offsets[0] + y * fb->pitches[0] +
-		x * fb->bits_per_pixel / 8;
-
-	tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
-	tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
-
-	format = tegra_dc_format(fb->pixel_format, &swap);
-	tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
-	tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP);
-
-	if (dc->soc->supports_block_linear) {
-		unsigned long height = tiling.value;
-
-		switch (tiling.mode) {
-		case TEGRA_BO_TILING_MODE_PITCH:
-			value = DC_WINBUF_SURFACE_KIND_PITCH;
-			break;
-
-		case TEGRA_BO_TILING_MODE_TILED:
-			value = DC_WINBUF_SURFACE_KIND_TILED;
-			break;
-
-		case TEGRA_BO_TILING_MODE_BLOCK:
-			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
-				DC_WINBUF_SURFACE_KIND_BLOCK;
-			break;
-		}
-
-		tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND);
-	} else {
-		switch (tiling.mode) {
-		case TEGRA_BO_TILING_MODE_PITCH:
-			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
-				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
-			break;
-
-		case TEGRA_BO_TILING_MODE_TILED:
-			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
-				DC_WIN_BUFFER_ADDR_MODE_TILE;
-			break;
-
-		case TEGRA_BO_TILING_MODE_BLOCK:
-			DRM_ERROR("hardware doesn't support block linear mode\n");
-			spin_unlock_irqrestore(&dc->lock, flags);
-			return -EINVAL;
-		}
-
-		tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
-	}
-
-	/* make sure bottom-up buffers are properly displayed */
-	if (tegra_fb_is_bottom_up(fb)) {
-		value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-		value |= V_DIRECTION;
-		tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
-		v_offset += fb->height - 1;
-	} else {
-		value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-		value &= ~V_DIRECTION;
-		tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-	}
-
-	tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
-	tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
-
-	value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-
-	spin_unlock_irqrestore(&dc->lock, flags);
-
-	return 0;
-}
-
 void tegra_dc_enable_vblank(struct tegra_dc *dc)
 {
 	unsigned long value, flags;
@@ -998,30 +905,6 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
 	spin_unlock_irqrestore(&drm->event_lock, flags);
 }
 
-static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-			      struct drm_pending_vblank_event *event, uint32_t page_flip_flags)
-{
-	unsigned int pipe = drm_crtc_index(crtc);
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
-	if (dc->event)
-		return -EBUSY;
-
-	if (event) {
-		event->pipe = pipe;
-		dc->event = event;
-		drm_crtc_vblank_get(crtc);
-	}
-
-	if (crtc->primary->state)
-		drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
-
-	tegra_dc_set_base(dc, 0, 0, fb);
-	crtc->primary->fb = fb;
-
-	return 0;
-}
-
 static void tegra_dc_destroy(struct drm_crtc *crtc)
 {
 	drm_crtc_cleanup(crtc);
@@ -1063,7 +946,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
 }
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
-	.page_flip = tegra_dc_page_flip,
+	.page_flip = drm_atomic_helper_page_flip,
 	.set_config = drm_atomic_helper_set_config,
 	.destroy = tegra_dc_destroy,
 	.reset = tegra_crtc_reset,
@@ -1338,6 +1221,16 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
 
 static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 {
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	if (crtc->state->event) {
+		crtc->state->event->pipe = drm_crtc_index(crtc);
+
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		dc->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
 }
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 0dd81d050ccb..5b81b44af9e4 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -10,6 +10,7 @@
 #include <linux/host1x.h>
 #include <linux/iommu.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 
 #include "drm.h"
@@ -26,13 +27,90 @@ struct tegra_drm_file {
 	struct list_head contexts;
 };
 
+static void tegra_atomic_schedule(struct tegra_drm *tegra,
+				  struct drm_atomic_state *state)
+{
+	tegra->commit.state = state;
+	schedule_work(&tegra->commit.work);
+}
+
+static void tegra_atomic_complete(struct tegra_drm *tegra,
+				  struct drm_atomic_state *state)
+{
+	struct drm_device *drm = tegra->drm;
+
+	/*
+	 * Everything below can be run asynchronously without the need to grab
+	 * any modeset locks at all under one condition: It must be guaranteed
+	 * that the asynchronous work has either been cancelled (if the driver
+	 * supports it, which at least requires that the framebuffers get
+	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
+	 * before the new state gets committed on the software side with
+	 * drm_atomic_helper_swap_state().
+	 *
+	 * This scheme allows new atomic state updates to be prepared and
+	 * checked in parallel to the asynchronous completion of the previous
+	 * update. Which is important since compositors need to figure out the
+	 * composition of the next frame right after having submitted the
+	 * current layout.
+	 */
+
+	drm_atomic_helper_commit_pre_planes(drm, state);
+	drm_atomic_helper_commit_planes(drm, state);
+	drm_atomic_helper_commit_post_planes(drm, state);
+
+	drm_atomic_helper_wait_for_vblanks(drm, state);
+
+	drm_atomic_helper_cleanup_planes(drm, state);
+	drm_atomic_state_free(state);
+}
+
+static void tegra_atomic_work(struct work_struct *work)
+{
+	struct tegra_drm *tegra = container_of(work, struct tegra_drm,
+					       commit.work);
+
+	tegra_atomic_complete(tegra, tegra->commit.state);
+}
+
+static int tegra_atomic_commit(struct drm_device *drm,
+			       struct drm_atomic_state *state, bool async)
+{
+	struct tegra_drm *tegra = drm->dev_private;
+	int err;
+
+	err = drm_atomic_helper_prepare_planes(drm, state);
+	if (err)
+		return err;
+
+	/* serialize outstanding asynchronous commits */
+	mutex_lock(&tegra->commit.lock);
+	flush_work(&tegra->commit.work);
+
+	/*
+	 * This is the point of no return - everything below never fails except
+	 * when the hw goes bonghits. Which means we can commit the new state on
+	 * the software side now.
+	 */
+
+	drm_atomic_helper_swap_state(drm, state);
+
+	if (async)
+		tegra_atomic_schedule(tegra, state);
+	else
+		tegra_atomic_complete(tegra, state);
+
+	mutex_unlock(&tegra->commit.lock);
+	return 0;
+}
+
 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 	.fb_create = tegra_fb_create,
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	.output_poll_changed = tegra_fb_output_poll_changed,
 #endif
 	.atomic_check = drm_atomic_helper_check,
-	.atomic_commit = drm_atomic_helper_commit,
+	.atomic_commit = tegra_atomic_commit,
 };
 
 static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
@@ -58,6 +136,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	mutex_init(&tegra->clients_lock);
 	INIT_LIST_HEAD(&tegra->clients);
+
+	mutex_init(&tegra->commit.lock);
+	INIT_WORK(&tegra->commit.work, tegra_atomic_work);
+
 	drm->dev_private = tegra;
 	tegra->drm = drm;
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 3db719de312f..b1c7027b26e7 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -50,6 +50,12 @@ struct tegra_drm {
 #endif
 
 	unsigned int pitch_align;
+
+	struct {
+		struct drm_atomic_state *state;
+		struct work_struct work;
+		struct mutex lock;
+	} commit;
 };
 
 struct tegra_drm_client;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 33/36] drm/tegra: Remove unused ->mode_fixup() callbacks
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All output drivers have now been converted to use the ->atomic_check()
callback, so the ->mode_fixup() callbacks are no longer used.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c  | 87 --------------------------------------------
 drivers/gpu/drm/tegra/hdmi.c | 27 --------------
 drivers/gpu/drm/tegra/rgb.c  | 38 -------------------
 drivers/gpu/drm/tegra/sor.c  | 27 --------------
 4 files changed, 179 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 55a6a95bda84..39c79a65b4f3 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -786,92 +786,6 @@ static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	unsigned int mul, div, scdiv, vrefresh, lanes;
-	struct tegra_dsi *dsi = to_dsi(output);
-	struct mipi_dphy_timing timing;
-	unsigned long pclk, bclk, plld;
-	unsigned long period;
-	int err;
-
-	lanes = tegra_dsi_get_lanes(dsi);
-	pclk = mode->clock * 1000;
-
-	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
-	if (err < 0)
-		return err;
-
-	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, lanes);
-	vrefresh = drm_mode_vrefresh(mode);
-	DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);
-
-	/* compute byte clock */
-	bclk = (pclk * mul) / (div * lanes);
-
-	/*
-	 * Compute bit clock and round up to the next MHz.
-	 */
-	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
-	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
-
-	/*
-	 * We divide the frequency by two here, but we make up for that by
-	 * setting the shift clock divider (further below) to half of the
-	 * correct value.
-	 */
-	plld /= 2;
-
-	/*
-	 * Derive pixel clock from bit clock using the shift clock divider.
-	 * Note that this is only half of what we would expect, but we need
-	 * that to make up for the fact that we divided the bit clock by a
-	 * factor of two above.
-	 *
-	 * It's not clear exactly why this is necessary, but the display is
-	 * not working properly otherwise. Perhaps the PLLs cannot generate
-	 * frequencies sufficiently high.
-	 */
-	scdiv = ((8 * mul) / (div * lanes)) - 2;
-
-	err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(dsi->clk_parent, plld);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n",
-			plld);
-		return false;
-	}
-
-	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
-
-	err = mipi_dphy_timing_get_default(&timing, period);
-	if (err < 0)
-		return err;
-
-	err = mipi_dphy_timing_validate(&timing, period);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * The D-PHY timing fields are expressed in byte-clock cycles, so
-	 * multiply the period by 8.
-	 */
-	tegra_dsi_set_phy_timing(dsi, period * 8, &timing);
-
-	return true;
-}
-
 static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1053,7 +967,6 @@ tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.dpms = tegra_dsi_encoder_dpms,
-	.mode_fixup = tegra_dsi_encoder_mode_fixup,
 	.prepare = tegra_dsi_encoder_prepare,
 	.commit = tegra_dsi_encoder_commit,
 	.mode_set = tegra_dsi_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index abb1ea0385ec..07771956cc94 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -822,32 +822,6 @@ static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
-					  const struct drm_display_mode *mode,
-					  struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	unsigned long pclk = mode->clock * 1000;
-	int err;
-
-	err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(hdmi->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1104,7 +1078,6 @@ tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.dpms = tegra_hdmi_encoder_dpms,
-	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
 	.prepare = tegra_hdmi_encoder_prepare,
 	.commit = tegra_hdmi_encoder_commit,
 	.mode_set = tegra_hdmi_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index be1b38936dbe..0c8b458b2364 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -129,43 +129,6 @@ static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned int div;
-	int err;
-
-	/*
-	 * We may not want to change the frequency of the parent clock, since
-	 * it may be a parent for other peripherals. This is due to the fact
-	 * that on Tegra20 there's only a single clock dedicated to display
-	 * (pll_d_out0), whereas later generations have a second one that can
-	 * be used to independently drive a second output (pll_d2_out0).
-	 *
-	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
-	 * typically used as the parent clock for the display controllers.
-	 * But this comes at a cost: pll_p is the parent of several other
-	 * peripherals, so its frequency shouldn't change out of the blue.
-	 *
-	 * The best we can do at this point is to use the shift clock divider
-	 * and hope that the desired frequency can be matched (or at least
-	 * matched sufficiently close that the panel will still work).
-	 */
-	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
-
-	err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -278,7 +241,6 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.dpms = tegra_rgb_encoder_dpms,
-	.mode_fixup = tegra_rgb_encoder_mode_fixup,
 	.prepare = tegra_rgb_encoder_prepare,
 	.commit = tegra_rgb_encoder_commit,
 	.mode_set = tegra_rgb_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 7463ea02a083..e813df71e30c 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -781,32 +781,6 @@ static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_sor *sor = to_sor(output);
-	int err;
-
-	err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(sor->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1343,7 +1317,6 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.dpms = tegra_sor_encoder_dpms,
-	.mode_fixup = tegra_sor_encoder_mode_fixup,
 	.prepare = tegra_sor_encoder_prepare,
 	.commit = tegra_sor_encoder_commit,
 	.mode_set = tegra_sor_encoder_mode_set,
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 33/36] drm/tegra: Remove unused ->mode_fixup() callbacks
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

All output drivers have now been converted to use the ->atomic_check()
callback, so the ->mode_fixup() callbacks are no longer used.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dsi.c  | 87 --------------------------------------------
 drivers/gpu/drm/tegra/hdmi.c | 27 --------------
 drivers/gpu/drm/tegra/rgb.c  | 38 -------------------
 drivers/gpu/drm/tegra/sor.c  | 27 --------------
 4 files changed, 179 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 55a6a95bda84..39c79a65b4f3 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -786,92 +786,6 @@ static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	unsigned int mul, div, scdiv, vrefresh, lanes;
-	struct tegra_dsi *dsi = to_dsi(output);
-	struct mipi_dphy_timing timing;
-	unsigned long pclk, bclk, plld;
-	unsigned long period;
-	int err;
-
-	lanes = tegra_dsi_get_lanes(dsi);
-	pclk = mode->clock * 1000;
-
-	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
-	if (err < 0)
-		return err;
-
-	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, lanes);
-	vrefresh = drm_mode_vrefresh(mode);
-	DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);
-
-	/* compute byte clock */
-	bclk = (pclk * mul) / (div * lanes);
-
-	/*
-	 * Compute bit clock and round up to the next MHz.
-	 */
-	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
-	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
-
-	/*
-	 * We divide the frequency by two here, but we make up for that by
-	 * setting the shift clock divider (further below) to half of the
-	 * correct value.
-	 */
-	plld /= 2;
-
-	/*
-	 * Derive pixel clock from bit clock using the shift clock divider.
-	 * Note that this is only half of what we would expect, but we need
-	 * that to make up for the fact that we divided the bit clock by a
-	 * factor of two above.
-	 *
-	 * It's not clear exactly why this is necessary, but the display is
-	 * not working properly otherwise. Perhaps the PLLs cannot generate
-	 * frequencies sufficiently high.
-	 */
-	scdiv = ((8 * mul) / (div * lanes)) - 2;
-
-	err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(dsi->clk_parent, plld);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n",
-			plld);
-		return false;
-	}
-
-	tegra_dsi_set_timeout(dsi, bclk, vrefresh);
-
-	err = mipi_dphy_timing_get_default(&timing, period);
-	if (err < 0)
-		return err;
-
-	err = mipi_dphy_timing_validate(&timing, period);
-	if (err < 0) {
-		dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
-		return err;
-	}
-
-	/*
-	 * The D-PHY timing fields are expressed in byte-clock cycles, so
-	 * multiply the period by 8.
-	 */
-	tegra_dsi_set_phy_timing(dsi, period * 8, &timing);
-
-	return true;
-}
-
 static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1053,7 +967,6 @@ tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
 	.dpms = tegra_dsi_encoder_dpms,
-	.mode_fixup = tegra_dsi_encoder_mode_fixup,
 	.prepare = tegra_dsi_encoder_prepare,
 	.commit = tegra_dsi_encoder_commit,
 	.mode_set = tegra_dsi_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index abb1ea0385ec..07771956cc94 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -822,32 +822,6 @@ static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
-					  const struct drm_display_mode *mode,
-					  struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	struct tegra_hdmi *hdmi = to_hdmi(output);
-	unsigned long pclk = mode->clock * 1000;
-	int err;
-
-	err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(hdmi->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1104,7 +1078,6 @@ tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
 	.dpms = tegra_hdmi_encoder_dpms,
-	.mode_fixup = tegra_hdmi_encoder_mode_fixup,
 	.prepare = tegra_hdmi_encoder_prepare,
 	.commit = tegra_hdmi_encoder_commit,
 	.mode_set = tegra_hdmi_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index be1b38936dbe..0c8b458b2364 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -129,43 +129,6 @@ static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_rgb *rgb = to_rgb(output);
-	unsigned int div;
-	int err;
-
-	/*
-	 * We may not want to change the frequency of the parent clock, since
-	 * it may be a parent for other peripherals. This is due to the fact
-	 * that on Tegra20 there's only a single clock dedicated to display
-	 * (pll_d_out0), whereas later generations have a second one that can
-	 * be used to independently drive a second output (pll_d2_out0).
-	 *
-	 * As a way to support multiple outputs on Tegra20 as well, pll_p is
-	 * typically used as the parent clock for the display controllers.
-	 * But this comes at a cost: pll_p is the parent of several other
-	 * peripherals, so its frequency shouldn't change out of the blue.
-	 *
-	 * The best we can do at this point is to use the shift clock divider
-	 * and hope that the desired frequency can be matched (or at least
-	 * matched sufficiently close that the panel will still work).
-	 */
-	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
-
-	err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -278,7 +241,6 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
 	.dpms = tegra_rgb_encoder_dpms,
-	.mode_fixup = tegra_rgb_encoder_mode_fixup,
 	.prepare = tegra_rgb_encoder_prepare,
 	.commit = tegra_rgb_encoder_commit,
 	.mode_set = tegra_rgb_encoder_mode_set,
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 7463ea02a083..e813df71e30c 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -781,32 +781,6 @@ static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
 }
 
-static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
-					 const struct drm_display_mode *mode,
-					 struct drm_display_mode *adjusted)
-{
-	struct tegra_output *output = encoder_to_output(encoder);
-	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
-	unsigned long pclk = mode->clock * 1000;
-	struct tegra_sor *sor = to_sor(output);
-	int err;
-
-	err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
-	if (err < 0) {
-		dev_err(output->dev, "failed to setup DC clock: %d\n", err);
-		return false;
-	}
-
-	err = clk_set_rate(sor->clk_parent, pclk);
-	if (err < 0) {
-		dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
-			pclk);
-		return false;
-	}
-
-	return true;
-}
-
 static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
 {
 }
@@ -1343,7 +1317,6 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
 	.dpms = tegra_sor_encoder_dpms,
-	.mode_fixup = tegra_sor_encoder_mode_fixup,
 	.prepare = tegra_sor_encoder_prepare,
 	.commit = tegra_sor_encoder_commit,
 	.mode_set = tegra_sor_encoder_mode_set,
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 34/36] drm/tegra: Track active planes in CRTC state
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Wrap struct drm_crtc_state in a driver-specific structure and add the
planes field which keeps track of which planes are updated or disabled
during a modeset. This allows atomic updates of the the display engine
at ->atomic_flush() time.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 52ae563cb531..835de4398c8f 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -54,6 +54,8 @@ struct tegra_dc_state {
 	struct clk *clk;
 	unsigned long pclk;
 	unsigned int div;
+
+	u32 planes;
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
-static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
-{
-	u32 value = WIN_A_ACT_REQ << index;
-
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-}
-
-static void tegra_dc_cursor_commit(struct tegra_dc *dc)
-{
-	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
-}
-
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 		break;
 	}
 
-	tegra_dc_window_commit(dc, index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
 {
 }
 
+static int tegra_plane_state_add(struct tegra_plane *plane,
+				 struct drm_plane_state *state)
+{
+	struct drm_crtc_state *crtc_state;
+	struct tegra_dc_state *tegra;
+
+	/* Propagate errors from allocation or locking failures. */
+	crtc_state = drm_atomic_plane_get_crtc_state(state);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	tegra = to_dc_state(crtc_state);
+
+	tegra->planes |= WIN_A_ACT_REQ << plane->index;
+
+	return 0;
+}
+
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_bo_tiling tiling;
 	int err;
@@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 		}
 	}
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
 	value &= ~WIN_ENABLE;
 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
-	tegra_dc_window_commit(dc, p->index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
 static int tegra_cursor_atomic_check(struct drm_plane *plane,
 				     struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+	int err;
+
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
@@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
 	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
 	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
-	/* apply changes */
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static void tegra_cursor_atomic_disable(struct drm_plane *plane,
@@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
@@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	if (!plane)
 		return ERR_PTR(-ENOMEM);
 
+	/*
+	 * We'll treat the cursor as an overlay plane with index 6 here so
+	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
+	 * match up.
+	 */
+	plane->index = 6;
+
 	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
 	formats = tegra_cursor_plane_formats;
 
@@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	}
 
 	drm_crtc_vblank_off(crtc);
-	tegra_dc_commit(dc);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1207,10 +1221,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 static void tegra_crtc_commit(struct drm_crtc *crtc)
 {
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
 	drm_crtc_vblank_on(crtc);
-	tegra_dc_commit(dc);
 }
 
 static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
@@ -1235,6 +1246,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
 {
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
+	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
 }
 
 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 34/36] drm/tegra: Track active planes in CRTC state
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Wrap struct drm_crtc_state in a driver-specific structure and add the
planes field which keeps track of which planes are updated or disabled
during a modeset. This allows atomic updates of the the display engine
at ->atomic_flush() time.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 52ae563cb531..835de4398c8f 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -54,6 +54,8 @@ struct tegra_dc_state {
 	struct clk *clk;
 	unsigned long pclk;
 	unsigned int div;
+
+	u32 planes;
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
-static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
-{
-	u32 value = WIN_A_ACT_REQ << index;
-
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-}
-
-static void tegra_dc_cursor_commit(struct tegra_dc *dc)
-{
-	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
-}
-
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 		break;
 	}
 
-	tegra_dc_window_commit(dc, index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
 {
 }
 
+static int tegra_plane_state_add(struct tegra_plane *plane,
+				 struct drm_plane_state *state)
+{
+	struct drm_crtc_state *crtc_state;
+	struct tegra_dc_state *tegra;
+
+	/* Propagate errors from allocation or locking failures. */
+	crtc_state = drm_atomic_plane_get_crtc_state(state);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	tegra = to_dc_state(crtc_state);
+
+	tegra->planes |= WIN_A_ACT_REQ << plane->index;
+
+	return 0;
+}
+
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_bo_tiling tiling;
 	int err;
@@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 		}
 	}
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
 	value &= ~WIN_ENABLE;
 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
-	tegra_dc_window_commit(dc, p->index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
 static int tegra_cursor_atomic_check(struct drm_plane *plane,
 				     struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+	int err;
+
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
@@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
 	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
 	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
-	/* apply changes */
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static void tegra_cursor_atomic_disable(struct drm_plane *plane,
@@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
@@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	if (!plane)
 		return ERR_PTR(-ENOMEM);
 
+	/*
+	 * We'll treat the cursor as an overlay plane with index 6 here so
+	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
+	 * match up.
+	 */
+	plane->index = 6;
+
 	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
 	formats = tegra_cursor_plane_formats;
 
@@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	}
 
 	drm_crtc_vblank_off(crtc);
-	tegra_dc_commit(dc);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1207,10 +1221,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 static void tegra_crtc_commit(struct drm_crtc *crtc)
 {
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
 	drm_crtc_vblank_on(crtc);
-	tegra_dc_commit(dc);
 }
 
 static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
@@ -1235,6 +1246,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
 {
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
+	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
 }
 
 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 35/36] drm/tegra: Track tiling and format in plane state
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Tracking these in the plane state allows them to be computed in the
->atomic_check() callback and reused when applying the configuration in
->atomic_update().

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 127 ++++++++++++++++++++++++++++++++++----------
 drivers/gpu/drm/tegra/drm.h |   4 +-
 2 files changed, 101 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 835de4398c8f..1280b886481d 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -66,6 +66,23 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
+struct tegra_plane_state {
+	struct drm_plane_state base;
+
+	struct tegra_bo_tiling tiling;
+	u32 format;
+	u32 swap;
+};
+
+static inline struct tegra_plane_state *
+to_tegra_plane_state(struct drm_plane_state *state)
+{
+	if (state)
+		return container_of(state, struct tegra_plane_state, base);
+
+	return NULL;
+}
+
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -104,43 +121,49 @@ void tegra_dc_commit(struct tegra_dc *dc)
 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 }
 
-static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
+static int tegra_dc_format(u32 fourcc, u32 *format, u32 *swap)
 {
 	/* assume no swapping of fetched data */
 	if (swap)
 		*swap = BYTE_SWAP_NOSWAP;
 
-	switch (format) {
+	switch (fourcc) {
 	case DRM_FORMAT_XBGR8888:
-		return WIN_COLOR_DEPTH_R8G8B8A8;
+		*format = WIN_COLOR_DEPTH_R8G8B8A8;
+		break;
 
 	case DRM_FORMAT_XRGB8888:
-		return WIN_COLOR_DEPTH_B8G8R8A8;
+		*format = WIN_COLOR_DEPTH_B8G8R8A8;
+		break;
 
 	case DRM_FORMAT_RGB565:
-		return WIN_COLOR_DEPTH_B5G6R5;
+		*format = WIN_COLOR_DEPTH_B5G6R5;
+		break;
 
 	case DRM_FORMAT_UYVY:
-		return WIN_COLOR_DEPTH_YCbCr422;
+		*format = WIN_COLOR_DEPTH_YCbCr422;
+		break;
 
 	case DRM_FORMAT_YUYV:
 		if (swap)
 			*swap = BYTE_SWAP_SWAP2;
 
-		return WIN_COLOR_DEPTH_YCbCr422;
+		*format = WIN_COLOR_DEPTH_YCbCr422;
+		break;
 
 	case DRM_FORMAT_YUV420:
-		return WIN_COLOR_DEPTH_YCbCr420P;
+		*format = WIN_COLOR_DEPTH_YCbCr420P;
+		break;
 
 	case DRM_FORMAT_YUV422:
-		return WIN_COLOR_DEPTH_YCbCr422P;
+		*format = WIN_COLOR_DEPTH_YCbCr422P;
+		break;
 
 	default:
-		break;
+		return -EINVAL;
 	}
 
-	WARN(1, "unsupported pixel format %u, using default\n", format);
-	return WIN_COLOR_DEPTH_B8G8R8A8;
+	return 0;
 }
 
 static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
@@ -405,13 +428,54 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane)
 	tegra_plane_destroy(plane);
 }
 
+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);
+
+	kfree(plane->state);
+	plane->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state) {
+		plane->state = &state->base;
+		plane->state->plane = plane;
+	}
+}
+
+static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
+	struct tegra_plane_state *copy;
+
+	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+	if (!copy)
+		return NULL;
+
+	if (copy->base.fb)
+		drm_framebuffer_reference(copy->base.fb);
+
+	return &copy->base;
+}
+
+static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
+					     struct drm_plane_state *state)
+{
+	if (state->fb)
+		drm_framebuffer_unreference(state->fb);
+
+	kfree(state);
+}
+
 static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_primary_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static int tegra_plane_prepare_fb(struct drm_plane *plane,
@@ -446,20 +510,26 @@ static int tegra_plane_state_add(struct tegra_plane *plane,
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
+	struct tegra_bo_tiling *tiling = &plane_state->tiling;
 	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
-	struct tegra_bo_tiling tiling;
 	int err;
 
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
 
-	err = tegra_fb_get_tiling(state->fb, &tiling);
+	err = tegra_dc_format(state->fb->pixel_format, &plane_state->format,
+			      &plane_state->swap);
+	if (err < 0)
+		return err;
+
+	err = tegra_fb_get_tiling(state->fb, tiling);
 	if (err < 0)
 		return err;
 
-	if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK &&
+	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
 	    !dc->soc->supports_block_linear) {
 		DRM_ERROR("hardware doesn't support block linear mode\n");
 		return -EINVAL;
@@ -487,12 +557,12 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 static void tegra_plane_atomic_update(struct drm_plane *plane,
 				      struct drm_plane_state *old_state)
 {
+	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
 	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
 	struct drm_framebuffer *fb = plane->state->fb;
 	struct tegra_plane *p = to_tegra_plane(plane);
 	struct tegra_dc_window window;
 	unsigned int i;
-	int err;
 
 	/* rien ne va plus */
 	if (!plane->state->crtc || !plane->state->fb)
@@ -507,12 +577,13 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
 	window.dst.y = plane->state->crtc_y;
 	window.dst.w = plane->state->crtc_w;
 	window.dst.h = plane->state->crtc_h;
-	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
 	window.bits_per_pixel = fb->bits_per_pixel;
 	window.bottom_up = tegra_fb_is_bottom_up(fb);
 
-	err = tegra_fb_get_tiling(fb, &window.tiling);
-	WARN_ON(err < 0);
+	/* copy from state */
+	window.tiling = state->tiling;
+	window.format = state->format;
+	window.swap = state->swap;
 
 	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
 		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -717,9 +788,9 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
@@ -774,9 +845,9 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_overlay_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static const uint32_t tegra_overlay_plane_formats[] = {
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index b1c7027b26e7..8cb2dfeaa957 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -170,13 +170,13 @@ struct tegra_dc_window {
 		unsigned int h;
 	} dst;
 	unsigned int bits_per_pixel;
-	unsigned int format;
-	unsigned int swap;
 	unsigned int stride[2];
 	unsigned long base[3];
 	bool bottom_up;
 
 	struct tegra_bo_tiling tiling;
+	u32 format;
+	u32 swap;
 };
 
 /* from dc.c */
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 35/36] drm/tegra: Track tiling and format in plane state
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Tracking these in the plane state allows them to be computed in the
->atomic_check() callback and reused when applying the configuration in
->atomic_update().

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c  | 127 ++++++++++++++++++++++++++++++++++----------
 drivers/gpu/drm/tegra/drm.h |   4 +-
 2 files changed, 101 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 835de4398c8f..1280b886481d 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -66,6 +66,23 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
+struct tegra_plane_state {
+	struct drm_plane_state base;
+
+	struct tegra_bo_tiling tiling;
+	u32 format;
+	u32 swap;
+};
+
+static inline struct tegra_plane_state *
+to_tegra_plane_state(struct drm_plane_state *state)
+{
+	if (state)
+		return container_of(state, struct tegra_plane_state, base);
+
+	return NULL;
+}
+
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -104,43 +121,49 @@ void tegra_dc_commit(struct tegra_dc *dc)
 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 }
 
-static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
+static int tegra_dc_format(u32 fourcc, u32 *format, u32 *swap)
 {
 	/* assume no swapping of fetched data */
 	if (swap)
 		*swap = BYTE_SWAP_NOSWAP;
 
-	switch (format) {
+	switch (fourcc) {
 	case DRM_FORMAT_XBGR8888:
-		return WIN_COLOR_DEPTH_R8G8B8A8;
+		*format = WIN_COLOR_DEPTH_R8G8B8A8;
+		break;
 
 	case DRM_FORMAT_XRGB8888:
-		return WIN_COLOR_DEPTH_B8G8R8A8;
+		*format = WIN_COLOR_DEPTH_B8G8R8A8;
+		break;
 
 	case DRM_FORMAT_RGB565:
-		return WIN_COLOR_DEPTH_B5G6R5;
+		*format = WIN_COLOR_DEPTH_B5G6R5;
+		break;
 
 	case DRM_FORMAT_UYVY:
-		return WIN_COLOR_DEPTH_YCbCr422;
+		*format = WIN_COLOR_DEPTH_YCbCr422;
+		break;
 
 	case DRM_FORMAT_YUYV:
 		if (swap)
 			*swap = BYTE_SWAP_SWAP2;
 
-		return WIN_COLOR_DEPTH_YCbCr422;
+		*format = WIN_COLOR_DEPTH_YCbCr422;
+		break;
 
 	case DRM_FORMAT_YUV420:
-		return WIN_COLOR_DEPTH_YCbCr420P;
+		*format = WIN_COLOR_DEPTH_YCbCr420P;
+		break;
 
 	case DRM_FORMAT_YUV422:
-		return WIN_COLOR_DEPTH_YCbCr422P;
+		*format = WIN_COLOR_DEPTH_YCbCr422P;
+		break;
 
 	default:
-		break;
+		return -EINVAL;
 	}
 
-	WARN(1, "unsupported pixel format %u, using default\n", format);
-	return WIN_COLOR_DEPTH_B8G8R8A8;
+	return 0;
 }
 
 static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
@@ -405,13 +428,54 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane)
 	tegra_plane_destroy(plane);
 }
 
+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);
+
+	kfree(plane->state);
+	plane->state = NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state) {
+		plane->state = &state->base;
+		plane->state->plane = plane;
+	}
+}
+
+static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
+	struct tegra_plane_state *copy;
+
+	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+	if (!copy)
+		return NULL;
+
+	if (copy->base.fb)
+		drm_framebuffer_reference(copy->base.fb);
+
+	return &copy->base;
+}
+
+static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
+					     struct drm_plane_state *state)
+{
+	if (state->fb)
+		drm_framebuffer_unreference(state->fb);
+
+	kfree(state);
+}
+
 static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_primary_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static int tegra_plane_prepare_fb(struct drm_plane *plane,
@@ -446,20 +510,26 @@ static int tegra_plane_state_add(struct tegra_plane *plane,
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
+	struct tegra_bo_tiling *tiling = &plane_state->tiling;
 	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
-	struct tegra_bo_tiling tiling;
 	int err;
 
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
 
-	err = tegra_fb_get_tiling(state->fb, &tiling);
+	err = tegra_dc_format(state->fb->pixel_format, &plane_state->format,
+			      &plane_state->swap);
+	if (err < 0)
+		return err;
+
+	err = tegra_fb_get_tiling(state->fb, tiling);
 	if (err < 0)
 		return err;
 
-	if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK &&
+	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
 	    !dc->soc->supports_block_linear) {
 		DRM_ERROR("hardware doesn't support block linear mode\n");
 		return -EINVAL;
@@ -487,12 +557,12 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 static void tegra_plane_atomic_update(struct drm_plane *plane,
 				      struct drm_plane_state *old_state)
 {
+	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
 	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
 	struct drm_framebuffer *fb = plane->state->fb;
 	struct tegra_plane *p = to_tegra_plane(plane);
 	struct tegra_dc_window window;
 	unsigned int i;
-	int err;
 
 	/* rien ne va plus */
 	if (!plane->state->crtc || !plane->state->fb)
@@ -507,12 +577,13 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
 	window.dst.y = plane->state->crtc_y;
 	window.dst.w = plane->state->crtc_w;
 	window.dst.h = plane->state->crtc_h;
-	window.format = tegra_dc_format(fb->pixel_format, &window.swap);
 	window.bits_per_pixel = fb->bits_per_pixel;
 	window.bottom_up = tegra_fb_is_bottom_up(fb);
 
-	err = tegra_fb_get_tiling(fb, &window.tiling);
-	WARN_ON(err < 0);
+	/* copy from state */
+	window.tiling = state->tiling;
+	window.format = state->format;
+	window.swap = state->swap;
 
 	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
 		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -717,9 +788,9 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
@@ -774,9 +845,9 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
 	.destroy = tegra_overlay_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.reset = tegra_plane_reset,
+	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
+	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
 static const uint32_t tegra_overlay_plane_formats[] = {
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index b1c7027b26e7..8cb2dfeaa957 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -170,13 +170,13 @@ struct tegra_dc_window {
 		unsigned int h;
 	} dst;
 	unsigned int bits_per_pixel;
-	unsigned int format;
-	unsigned int swap;
 	unsigned int stride[2];
 	unsigned long base[3];
 	bool bottom_up;
 
 	struct tegra_bo_tiling tiling;
+	u32 format;
+	u32 swap;
 };
 
 /* from dc.c */
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 36/36] drm/tegra: dc: Unify enabling the display controller
  2015-01-20 10:48 ` Thierry Reding
@ 2015-01-20 10:48   ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Previously output drivers would enable continuous display mode and power
up the display controller at various points during the initialization.
This is suboptimal because it accesses display controller registers in
output drivers and duplicates a bit of code.

Move this code into the display controller driver and enable the display
controller as the final step of the ->mode_set_nofb() implementation.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 12 ++++++++++++
 drivers/gpu/drm/tegra/dsi.c  | 10 ----------
 drivers/gpu/drm/tegra/hdmi.c | 10 ----------
 drivers/gpu/drm/tegra/rgb.c  | 11 +----------
 drivers/gpu/drm/tegra/sor.c  | 25 +++----------------------
 5 files changed, 16 insertions(+), 52 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 1280b886481d..c4cbcbfbd3ab 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1248,6 +1248,18 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 		value &= ~INTERLACE_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
 	}
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	value |= DISP_CTRL_MODE_C_DISPLAY;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+	tegra_dc_commit(dc);
 }
 
 static void tegra_crtc_prepare(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 39c79a65b4f3..82f95f7f819c 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -824,16 +824,6 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	value |= DSI_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(dc);
 
 	/* enable DSI controller */
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 07771956cc94..7e06657ae58b 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1022,16 +1022,6 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 	value |= HDMI_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0c8b458b2364..7cd833f5b5b5 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -168,16 +168,6 @@ static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder,
 	value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
 	tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
 
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(rgb->dc);
 
 	if (output->panel)
@@ -193,6 +183,7 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 		drm_panel_disable(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
+	tegra_dc_commit(rgb->dc);
 
 	if (output->panel)
 		drm_panel_unprepare(output->panel);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index e813df71e30c..2afe478ded3b 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -261,17 +261,8 @@ static int tegra_sor_attach(struct tegra_sor *sor)
 
 static int tegra_sor_wakeup(struct tegra_sor *sor)
 {
-	struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
 	unsigned long value, timeout;
 
-	/* enable display controller outputs */
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-	tegra_dc_commit(dc);
-
 	timeout = jiffies + msecs_to_jiffies(250);
 
 	/* wait for head to wake up */
@@ -1112,18 +1103,6 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
 		goto unlock;
 	}
 
-	/* start display controller in continuous mode */
-	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
-	value |= WRITE_MUX;
-	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
-
-	tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS);
-	tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
-	value &= ~WRITE_MUX;
-	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
-
 	/*
 	 * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
 	 * raster, associate with display controller)
@@ -1198,11 +1177,13 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
 		goto unlock;
 	}
 
+	tegra_sor_update(sor);
+
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value |= SOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	tegra_sor_update(sor);
+	tegra_dc_commit(dc);
 
 	err = tegra_sor_attach(sor);
 	if (err < 0) {
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH 36/36] drm/tegra: dc: Unify enabling the display controller
@ 2015-01-20 10:48   ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 10:48 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Previously output drivers would enable continuous display mode and power
up the display controller at various points during the initialization.
This is suboptimal because it accesses display controller registers in
output drivers and duplicates a bit of code.

Move this code into the display controller driver and enable the display
controller as the final step of the ->mode_set_nofb() implementation.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c   | 12 ++++++++++++
 drivers/gpu/drm/tegra/dsi.c  | 10 ----------
 drivers/gpu/drm/tegra/hdmi.c | 10 ----------
 drivers/gpu/drm/tegra/rgb.c  | 11 +----------
 drivers/gpu/drm/tegra/sor.c  | 25 +++----------------------
 5 files changed, 16 insertions(+), 52 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 1280b886481d..c4cbcbfbd3ab 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1248,6 +1248,18 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
 		value &= ~INTERLACE_ENABLE;
 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
 	}
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	value |= DISP_CTRL_MODE_C_DISPLAY;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+	tegra_dc_commit(dc);
 }
 
 static void tegra_crtc_prepare(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 39c79a65b4f3..82f95f7f819c 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -824,16 +824,6 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	value |= DSI_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(dc);
 
 	/* enable DSI controller */
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 07771956cc94..7e06657ae58b 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1022,16 +1022,6 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 	value |= HDMI_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(dc);
 
 	/* TODO: add HDCP support */
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0c8b458b2364..7cd833f5b5b5 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -168,16 +168,6 @@ static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder,
 	value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
 	tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
 
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND);
-	value &= ~DISP_CTRL_MODE_MASK;
-	value |= DISP_CTRL_MODE_C_DISPLAY;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
 	tegra_dc_commit(rgb->dc);
 
 	if (output->panel)
@@ -193,6 +183,7 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 		drm_panel_disable(output->panel);
 
 	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
+	tegra_dc_commit(rgb->dc);
 
 	if (output->panel)
 		drm_panel_unprepare(output->panel);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index e813df71e30c..2afe478ded3b 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -261,17 +261,8 @@ static int tegra_sor_attach(struct tegra_sor *sor)
 
 static int tegra_sor_wakeup(struct tegra_sor *sor)
 {
-	struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
 	unsigned long value, timeout;
 
-	/* enable display controller outputs */
-	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
-	value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-		 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
-	tegra_dc_commit(dc);
-
 	timeout = jiffies + msecs_to_jiffies(250);
 
 	/* wait for head to wake up */
@@ -1112,18 +1103,6 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
 		goto unlock;
 	}
 
-	/* start display controller in continuous mode */
-	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
-	value |= WRITE_MUX;
-	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
-
-	tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS);
-	tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
-
-	value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
-	value &= ~WRITE_MUX;
-	tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
-
 	/*
 	 * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
 	 * raster, associate with display controller)
@@ -1198,11 +1177,13 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
 		goto unlock;
 	}
 
+	tegra_sor_update(sor);
+
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value |= SOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-	tegra_sor_update(sor);
+	tegra_dc_commit(dc);
 
 	err = tegra_sor_attach(sor);
 	if (err < 0) {
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 11:07     ` Daniel Vetter
  -1 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:07 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

On Tue, Jan 20, 2015 at 11:48:23AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This callback can be used instead of the legacy ->mode_fixup() and is
> passed the CRTC and connector states. It can thus use these states to
> validate the modeset and cache values in the state to be used during
> the actual modeset.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Yeah, this addition makes sense.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++-------
>  include/drm/drm_crtc_helper.h       |  6 ++++++
>  2 files changed, 22 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 1cb04402cd73..b837d148d880 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
>  			}
>  		}
>  
> -
> -		ret = funcs->mode_fixup(encoder, &crtc_state->mode,
> -					&crtc_state->adjusted_mode);
> -		if (!ret) {
> -			DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
> -				      encoder->base.id, encoder->name);
> -			return -EINVAL;
> +		if (funcs->atomic_check) {
> +			ret = funcs->atomic_check(encoder, crtc_state,
> +						  conn_state);
> +			if (ret) {
> +				DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
> +					      encoder->base.id, encoder->name);
> +				return ret;
> +			}
> +		} else {
> +			ret = funcs->mode_fixup(encoder, &crtc_state->mode,
> +						&crtc_state->adjusted_mode);
> +			if (!ret) {
> +				DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
> +					      encoder->base.id, encoder->name);
> +				return -EINVAL;
> +			}
>  		}
>  	}
>  
> diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
> index e76828d81a8b..5810c027acdc 100644
> --- a/include/drm/drm_crtc_helper.h
> +++ b/include/drm/drm_crtc_helper.h
> @@ -115,6 +115,7 @@ struct drm_crtc_helper_funcs {
>   * @get_crtc: return CRTC that the encoder is currently attached to
>   * @detect: connection status detection
>   * @disable: disable encoder when not in use (overrides DPMS off)
> + * @atomic_check: check for validity of an atomic update
>   *
>   * The helper operations are called by the mid-layer CRTC helper.
>   */
> @@ -137,6 +138,11 @@ struct drm_encoder_helper_funcs {
>  					    struct drm_connector *connector);
>  	/* disable encoder when not in use - more explicit than dpms off */
>  	void (*disable)(struct drm_encoder *encoder);
> +
> +	/* atomic helpers */
> +	int (*atomic_check)(struct drm_encoder *encoder,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state);
>  };
>  
>  /**
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers
@ 2015-01-20 11:07     ` Daniel Vetter
  0 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:07 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

On Tue, Jan 20, 2015 at 11:48:23AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This callback can be used instead of the legacy ->mode_fixup() and is
> passed the CRTC and connector states. It can thus use these states to
> validate the modeset and cache values in the state to be used during
> the actual modeset.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Yeah, this addition makes sense.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++-------
>  include/drm/drm_crtc_helper.h       |  6 ++++++
>  2 files changed, 22 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 1cb04402cd73..b837d148d880 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
>  			}
>  		}
>  
> -
> -		ret = funcs->mode_fixup(encoder, &crtc_state->mode,
> -					&crtc_state->adjusted_mode);
> -		if (!ret) {
> -			DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
> -				      encoder->base.id, encoder->name);
> -			return -EINVAL;
> +		if (funcs->atomic_check) {
> +			ret = funcs->atomic_check(encoder, crtc_state,
> +						  conn_state);
> +			if (ret) {
> +				DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
> +					      encoder->base.id, encoder->name);
> +				return ret;
> +			}
> +		} else {
> +			ret = funcs->mode_fixup(encoder, &crtc_state->mode,
> +						&crtc_state->adjusted_mode);
> +			if (!ret) {
> +				DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
> +					      encoder->base.id, encoder->name);
> +				return -EINVAL;
> +			}
>  		}
>  	}
>  
> diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
> index e76828d81a8b..5810c027acdc 100644
> --- a/include/drm/drm_crtc_helper.h
> +++ b/include/drm/drm_crtc_helper.h
> @@ -115,6 +115,7 @@ struct drm_crtc_helper_funcs {
>   * @get_crtc: return CRTC that the encoder is currently attached to
>   * @detect: connection status detection
>   * @disable: disable encoder when not in use (overrides DPMS off)
> + * @atomic_check: check for validity of an atomic update
>   *
>   * The helper operations are called by the mid-layer CRTC helper.
>   */
> @@ -137,6 +138,11 @@ struct drm_encoder_helper_funcs {
>  					    struct drm_connector *connector);
>  	/* disable encoder when not in use - more explicit than dpms off */
>  	void (*disable)(struct drm_encoder *encoder);
> +
> +	/* atomic helpers */
> +	int (*atomic_check)(struct drm_encoder *encoder,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state);
>  };
>  
>  /**
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 11:10     ` Daniel Vetter
  -1 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:10 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

On Tue, Jan 20, 2015 at 11:48:24AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This function is useful to obtain the state of the CRTC that a plane is
> being attached to.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  include/drm/drm_atomic.h | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index 51168a8b723a..ddca107e5cce 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
>  int __must_check drm_atomic_commit(struct drm_atomic_state *state);
>  int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
>  
> +/**
> + * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
> + *     plane is being attached to
> + * @state: atomic state of plane
> + *
> + * Getting the atomic state of the CRTC that a given plane being attached to
> + * is non-trivial. We need to look at the global atomic state that the plane
> + * state is part of and extract the CRTC state using the CRTC's index. Using
> + * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
> + * locks, too.
> + *
> + * RETURNS:
> + * The atomic state of the CRTC that the plane is being attached to, or an
> + * ERR_PTR()-encoded negative error code on failure.
> + */
> +static inline struct drm_crtc_state *
> +drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
> +{
> +	return drm_atomic_get_crtc_state(state->state, state->crtc);

Hm, imo open-coding this is totally ok. Actually better since state->crtc
might be NULL, and hiding that isn't a good idea.

If you have a atomic_plane_disable hook then ->crtc is guaranteed to be
non-NULL in atomic_plane_update, but being explicit here is imo a feature.
So nacked from my side.
-Daniel

> +}
> +
>  #endif /* DRM_ATOMIC_H_ */
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
@ 2015-01-20 11:10     ` Daniel Vetter
  0 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:10 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

On Tue, Jan 20, 2015 at 11:48:24AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This function is useful to obtain the state of the CRTC that a plane is
> being attached to.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  include/drm/drm_atomic.h | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index 51168a8b723a..ddca107e5cce 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
>  int __must_check drm_atomic_commit(struct drm_atomic_state *state);
>  int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
>  
> +/**
> + * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
> + *     plane is being attached to
> + * @state: atomic state of plane
> + *
> + * Getting the atomic state of the CRTC that a given plane being attached to
> + * is non-trivial. We need to look at the global atomic state that the plane
> + * state is part of and extract the CRTC state using the CRTC's index. Using
> + * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
> + * locks, too.
> + *
> + * RETURNS:
> + * The atomic state of the CRTC that the plane is being attached to, or an
> + * ERR_PTR()-encoded negative error code on failure.
> + */
> +static inline struct drm_crtc_state *
> +drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
> +{
> +	return drm_atomic_get_crtc_state(state->state, state->crtc);

Hm, imo open-coding this is totally ok. Actually better since state->crtc
might be NULL, and hiding that isn't a good idea.

If you have a atomic_plane_disable hook then ->crtc is guaranteed to be
non-NULL in atomic_plane_update, but being explicit here is imo a feature.
So nacked from my side.
-Daniel

> +}
> +
>  #endif /* DRM_ATOMIC_H_ */
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 11:10     ` Daniel Vetter
  -1 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:10 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

On Tue, Jan 20, 2015 at 11:48:21AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> There is no use-case where it would be useful for drivers not to
> implement this function and the transitional plane helpers already
> require drivers to provide an implementation.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index d467185f7942..010661f23035 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>  
>  		funcs = plane->helper_private;
>  
> -		if (!funcs || !funcs->atomic_update)
> +		if (!funcs)
>  			continue;
>  
>  		old_plane_state = old_state->plane_states[i];
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
@ 2015-01-20 11:10     ` Daniel Vetter
  0 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:10 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

On Tue, Jan 20, 2015 at 11:48:21AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> There is no use-case where it would be useful for drivers not to
> implement this function and the transitional plane helpers already
> require drivers to provide an implementation.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index d467185f7942..010661f23035 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>  
>  		funcs = plane->helper_private;
>  
> -		if (!funcs || !funcs->atomic_update)
> +		if (!funcs)
>  			continue;
>  
>  		old_plane_state = old_state->plane_states[i];
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 11:13     ` Daniel Vetter
  -1 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:13 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

On Tue, Jan 20, 2015 at 11:48:22AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> In order to prevent drivers from having to perform the same checks over
> and over again, add an optional ->atomic_disable callback which the core
> calls under the right circumstances.
> 
> v2: pass old state and detect edges to avoid calling ->atomic_disable on
> already disabled planes, remove redundant comment (Daniel Vetter)
> 
> v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
> checking for transitions, move helper to drm_atomic_helper.h, clarify
> check for !old_state and its relation to transitional helpers
> 

I'd have pasted the entire thread from the discussion into the commit
message. But at least add a References: line for the mail that has all the
details would be good. Either way

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
>  drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
>  include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
>  include/drm/drm_plane_helper.h      |  3 +++
>  4 files changed, 57 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 010661f23035..1cb04402cd73 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>  
>  		old_plane_state = old_state->plane_states[i];
>  
> -		funcs->atomic_update(plane, old_plane_state);
> +		/*
> +		 * Special-case disabling the plane if drivers support it.
> +		 */
> +		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
> +		    funcs->atomic_disable)
> +			funcs->atomic_disable(plane, old_plane_state);
> +		else
> +			funcs->atomic_update(plane, old_plane_state);
>  	}
>  
>  	for (i = 0; i < ncrtcs; i++) {
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index 2f961c180273..02c1a0b74e04 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
>  			crtc_funcs[i]->atomic_begin(crtc[i]);
>  	}
>  
> -	plane_funcs->atomic_update(plane, plane_state);
> +	/*
> +	 * Drivers may optionally implement the ->atomic_disable callback, so
> +	 * special-case that here.
> +	 */
> +	if (drm_atomic_plane_disabling(plane, plane_state) &&
> +	    plane_funcs->atomic_disable)
> +		plane_funcs->atomic_disable(plane, plane_state);
> +	else
> +		plane_funcs->atomic_update(plane, plane_state);
>  
>  	for (i = 0; i < 2; i++) {
>  		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
> diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
> index 2095917ff8c7..a0ea4ded3cb5 100644
> --- a/include/drm/drm_atomic_helper.h
> +++ b/include/drm/drm_atomic_helper.h
> @@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
>  #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
>  	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
>  
> +/*
> + * drm_atomic_plane_disabling - check whether a plane is being disabled
> + * @plane: plane object
> + * @old_state: previous atomic state
> + *
> + * Checks the atomic state of a plane to determine whether it's being disabled
> + * or not. This also WARNs if it detects an invalid state (both CRTC and FB
> + * need to either both be NULL or both be non-NULL).
> + *
> + * RETURNS:
> + * True if the plane is being disabled, false otherwise.
> + */
> +static inline bool
> +drm_atomic_plane_disabling(struct drm_plane *plane,
> +			   struct drm_plane_state *old_state)
> +{
> +	/*
> +	 * When disabling a plane, CRTC and FB should always be NULL together.
> +	 * Anything else should be considered a bug in the atomic core, so we
> +	 * gently warn about it.
> +	 */
> +	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
> +		(plane->state->crtc != NULL && plane->state->fb == NULL));
> +
> +	/*
> +	 * When using the transitional helpers, old_state may be NULL. If so,
> +	 * we know nothing about the current state and have to assume that it
> +	 * might be enabled.
> +	 *
> +	 * When using the atomic helpers, old_state won't be NULL. Therefore
> +	 * this check assumes that either the driver will have reconstructed
> +	 * the correct state in ->reset() or that the driver will have taken
> +	 * appropriate measures to disable all planes.
> +	 */
> +	return (!old_state || old_state->crtc) && !plane->state->crtc;
> +}
> +
>  #endif /* DRM_ATOMIC_HELPER_H_ */
> diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
> index 0f2230311aa8..680be61ef20a 100644
> --- a/include/drm/drm_plane_helper.h
> +++ b/include/drm/drm_plane_helper.h
> @@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
>   * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
>   * @atomic_check: check that a given atomic state is valid and can be applied
>   * @atomic_update: apply an atomic state to the plane
> + * @atomic_disable: disable the plane
>   *
>   * The helper operations are called by the mid-layer CRTC helper.
>   */
> @@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
>  			    struct drm_plane_state *state);
>  	void (*atomic_update)(struct drm_plane *plane,
>  			      struct drm_plane_state *old_state);
> +	void (*atomic_disable)(struct drm_plane *plane,
> +			       struct drm_plane_state *old_state);
>  };
>  
>  static inline void drm_plane_helper_add(struct drm_plane *plane,
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
@ 2015-01-20 11:13     ` Daniel Vetter
  0 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:13 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

On Tue, Jan 20, 2015 at 11:48:22AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> In order to prevent drivers from having to perform the same checks over
> and over again, add an optional ->atomic_disable callback which the core
> calls under the right circumstances.
> 
> v2: pass old state and detect edges to avoid calling ->atomic_disable on
> already disabled planes, remove redundant comment (Daniel Vetter)
> 
> v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
> checking for transitions, move helper to drm_atomic_helper.h, clarify
> check for !old_state and its relation to transitional helpers
> 

I'd have pasted the entire thread from the discussion into the commit
message. But at least add a References: line for the mail that has all the
details would be good. Either way

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
>  drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
>  include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
>  include/drm/drm_plane_helper.h      |  3 +++
>  4 files changed, 57 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 010661f23035..1cb04402cd73 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>  
>  		old_plane_state = old_state->plane_states[i];
>  
> -		funcs->atomic_update(plane, old_plane_state);
> +		/*
> +		 * Special-case disabling the plane if drivers support it.
> +		 */
> +		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
> +		    funcs->atomic_disable)
> +			funcs->atomic_disable(plane, old_plane_state);
> +		else
> +			funcs->atomic_update(plane, old_plane_state);
>  	}
>  
>  	for (i = 0; i < ncrtcs; i++) {
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index 2f961c180273..02c1a0b74e04 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
>  			crtc_funcs[i]->atomic_begin(crtc[i]);
>  	}
>  
> -	plane_funcs->atomic_update(plane, plane_state);
> +	/*
> +	 * Drivers may optionally implement the ->atomic_disable callback, so
> +	 * special-case that here.
> +	 */
> +	if (drm_atomic_plane_disabling(plane, plane_state) &&
> +	    plane_funcs->atomic_disable)
> +		plane_funcs->atomic_disable(plane, plane_state);
> +	else
> +		plane_funcs->atomic_update(plane, plane_state);
>  
>  	for (i = 0; i < 2; i++) {
>  		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
> diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
> index 2095917ff8c7..a0ea4ded3cb5 100644
> --- a/include/drm/drm_atomic_helper.h
> +++ b/include/drm/drm_atomic_helper.h
> @@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
>  #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
>  	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
>  
> +/*
> + * drm_atomic_plane_disabling - check whether a plane is being disabled
> + * @plane: plane object
> + * @old_state: previous atomic state
> + *
> + * Checks the atomic state of a plane to determine whether it's being disabled
> + * or not. This also WARNs if it detects an invalid state (both CRTC and FB
> + * need to either both be NULL or both be non-NULL).
> + *
> + * RETURNS:
> + * True if the plane is being disabled, false otherwise.
> + */
> +static inline bool
> +drm_atomic_plane_disabling(struct drm_plane *plane,
> +			   struct drm_plane_state *old_state)
> +{
> +	/*
> +	 * When disabling a plane, CRTC and FB should always be NULL together.
> +	 * Anything else should be considered a bug in the atomic core, so we
> +	 * gently warn about it.
> +	 */
> +	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
> +		(plane->state->crtc != NULL && plane->state->fb == NULL));
> +
> +	/*
> +	 * When using the transitional helpers, old_state may be NULL. If so,
> +	 * we know nothing about the current state and have to assume that it
> +	 * might be enabled.
> +	 *
> +	 * When using the atomic helpers, old_state won't be NULL. Therefore
> +	 * this check assumes that either the driver will have reconstructed
> +	 * the correct state in ->reset() or that the driver will have taken
> +	 * appropriate measures to disable all planes.
> +	 */
> +	return (!old_state || old_state->crtc) && !plane->state->crtc;
> +}
> +
>  #endif /* DRM_ATOMIC_HELPER_H_ */
> diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
> index 0f2230311aa8..680be61ef20a 100644
> --- a/include/drm/drm_plane_helper.h
> +++ b/include/drm/drm_plane_helper.h
> @@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
>   * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
>   * @atomic_check: check that a given atomic state is valid and can be applied
>   * @atomic_update: apply an atomic state to the plane
> + * @atomic_disable: disable the plane
>   *
>   * The helper operations are called by the mid-layer CRTC helper.
>   */
> @@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
>  			    struct drm_plane_state *state);
>  	void (*atomic_update)(struct drm_plane *plane,
>  			      struct drm_plane_state *old_state);
> +	void (*atomic_disable)(struct drm_plane *plane,
> +			       struct drm_plane_state *old_state);
>  };
>  
>  static inline void drm_plane_helper_add(struct drm_plane *plane,
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 34/36] drm/tegra: Track active planes in CRTC state
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 11:18     ` Daniel Vetter
  -1 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:18 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

On Tue, Jan 20, 2015 at 11:48:53AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Wrap struct drm_crtc_state in a driver-specific structure and add the
> planes field which keeps track of which planes are updated or disabled
> during a modeset. This allows atomic updates of the the display engine
> at ->atomic_flush() time.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

commit 6ddd388ab222b66b596342becc76d5031c0e2fc8
Author: Rob Clark <robdclark@gmail.com>
Date:   Fri Nov 21 15:28:31 2014 -0500

    drm/atomic: track bitmask of planes attached to crtc

added this to the core since it seems to be generally useful. Does tegra
need more?
-Daniel

> ---
>  drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
>  1 file changed, 44 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
> index 52ae563cb531..835de4398c8f 100644
> --- a/drivers/gpu/drm/tegra/dc.c
> +++ b/drivers/gpu/drm/tegra/dc.c
> @@ -54,6 +54,8 @@ struct tegra_dc_state {
>  	struct clk *clk;
>  	unsigned long pclk;
>  	unsigned int div;
> +
> +	u32 planes;
>  };
>  
>  static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
> @@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
>  	return NULL;
>  }
>  
> -static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
> -{
> -	u32 value = WIN_A_ACT_REQ << index;
> -
> -	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
> -	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
> -}
> -
> -static void tegra_dc_cursor_commit(struct tegra_dc *dc)
> -{
> -	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
> -	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
> -}
> -
>  /*
>   * Reads the active copy of a register. This takes the dc->lock spinlock to
>   * prevent races with the VBLANK processing which also needs access to the
> @@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
>  		break;
>  	}
>  
> -	tegra_dc_window_commit(dc, index);
> -
>  	spin_unlock_irqrestore(&dc->lock, flags);
>  }
>  
> @@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
>  {
>  }
>  
> +static int tegra_plane_state_add(struct tegra_plane *plane,
> +				 struct drm_plane_state *state)
> +{
> +	struct drm_crtc_state *crtc_state;
> +	struct tegra_dc_state *tegra;
> +
> +	/* Propagate errors from allocation or locking failures. */
> +	crtc_state = drm_atomic_plane_get_crtc_state(state);
> +	if (IS_ERR(crtc_state))
> +		return PTR_ERR(crtc_state);
> +
> +	tegra = to_dc_state(crtc_state);
> +
> +	tegra->planes |= WIN_A_ACT_REQ << plane->index;
> +
> +	return 0;
> +}
> +
>  static int tegra_plane_atomic_check(struct drm_plane *plane,
>  				    struct drm_plane_state *state)
>  {
> +	struct tegra_plane *tegra = to_tegra_plane(plane);
>  	struct tegra_dc *dc = to_tegra_dc(state->crtc);
>  	struct tegra_bo_tiling tiling;
>  	int err;
> @@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
>  		}
>  	}
>  
> +	err = tegra_plane_state_add(tegra, state);
> +	if (err < 0)
> +		return err;
> +
>  	return 0;
>  }
>  
> @@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
>  	value &= ~WIN_ENABLE;
>  	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
>  
> -	tegra_dc_window_commit(dc, p->index);
> -
>  	spin_unlock_irqrestore(&dc->lock, flags);
>  }
>  
> @@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
>  static int tegra_cursor_atomic_check(struct drm_plane *plane,
>  				     struct drm_plane_state *state)
>  {
> +	struct tegra_plane *tegra = to_tegra_plane(plane);
> +	int err;
> +
>  	/* no need for further checks if the plane is being disabled */
>  	if (!state->crtc)
>  		return 0;
> @@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
>  	    state->crtc_w != 128 && state->crtc_w != 256)
>  		return -EINVAL;
>  
> +	err = tegra_plane_state_add(tegra, state);
> +	if (err < 0)
> +		return err;
> +
>  	return 0;
>  }
>  
> @@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
>  	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
>  	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
>  
> -	/* apply changes */
> -	tegra_dc_cursor_commit(dc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static void tegra_cursor_atomic_disable(struct drm_plane *plane,
> @@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
>  	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
>  	value &= ~CURSOR_ENABLE;
>  	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
> -
> -	tegra_dc_cursor_commit(dc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
> @@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
>  	if (!plane)
>  		return ERR_PTR(-ENOMEM);
>  
> +	/*
> +	 * We'll treat the cursor as an overlay plane with index 6 here so
> +	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
> +	 * match up.
> +	 */
> +	plane->index = 6;
> +
>  	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
>  	formats = tegra_cursor_plane_formats;
>  
> @@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
>  	}
>  
>  	drm_crtc_vblank_off(crtc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
> @@ -1207,10 +1221,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
>  
>  static void tegra_crtc_commit(struct drm_crtc *crtc)
>  {
> -	struct tegra_dc *dc = to_tegra_dc(crtc);
> -
>  	drm_crtc_vblank_on(crtc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
> @@ -1235,6 +1246,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
>  
>  static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
>  {
> +	struct tegra_dc_state *state = to_dc_state(crtc->state);
> +	struct tegra_dc *dc = to_tegra_dc(crtc);
> +
> +	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
> +	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
>  }
>  
>  static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 34/36] drm/tegra: Track active planes in CRTC state
@ 2015-01-20 11:18     ` Daniel Vetter
  0 siblings, 0 replies; 120+ messages in thread
From: Daniel Vetter @ 2015-01-20 11:18 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

On Tue, Jan 20, 2015 at 11:48:53AM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Wrap struct drm_crtc_state in a driver-specific structure and add the
> planes field which keeps track of which planes are updated or disabled
> during a modeset. This allows atomic updates of the the display engine
> at ->atomic_flush() time.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

commit 6ddd388ab222b66b596342becc76d5031c0e2fc8
Author: Rob Clark <robdclark@gmail.com>
Date:   Fri Nov 21 15:28:31 2014 -0500

    drm/atomic: track bitmask of planes attached to crtc

added this to the core since it seems to be generally useful. Does tegra
need more?
-Daniel

> ---
>  drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
>  1 file changed, 44 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
> index 52ae563cb531..835de4398c8f 100644
> --- a/drivers/gpu/drm/tegra/dc.c
> +++ b/drivers/gpu/drm/tegra/dc.c
> @@ -54,6 +54,8 @@ struct tegra_dc_state {
>  	struct clk *clk;
>  	unsigned long pclk;
>  	unsigned int div;
> +
> +	u32 planes;
>  };
>  
>  static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
> @@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
>  	return NULL;
>  }
>  
> -static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
> -{
> -	u32 value = WIN_A_ACT_REQ << index;
> -
> -	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
> -	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
> -}
> -
> -static void tegra_dc_cursor_commit(struct tegra_dc *dc)
> -{
> -	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
> -	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
> -}
> -
>  /*
>   * Reads the active copy of a register. This takes the dc->lock spinlock to
>   * prevent races with the VBLANK processing which also needs access to the
> @@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
>  		break;
>  	}
>  
> -	tegra_dc_window_commit(dc, index);
> -
>  	spin_unlock_irqrestore(&dc->lock, flags);
>  }
>  
> @@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
>  {
>  }
>  
> +static int tegra_plane_state_add(struct tegra_plane *plane,
> +				 struct drm_plane_state *state)
> +{
> +	struct drm_crtc_state *crtc_state;
> +	struct tegra_dc_state *tegra;
> +
> +	/* Propagate errors from allocation or locking failures. */
> +	crtc_state = drm_atomic_plane_get_crtc_state(state);
> +	if (IS_ERR(crtc_state))
> +		return PTR_ERR(crtc_state);
> +
> +	tegra = to_dc_state(crtc_state);
> +
> +	tegra->planes |= WIN_A_ACT_REQ << plane->index;
> +
> +	return 0;
> +}
> +
>  static int tegra_plane_atomic_check(struct drm_plane *plane,
>  				    struct drm_plane_state *state)
>  {
> +	struct tegra_plane *tegra = to_tegra_plane(plane);
>  	struct tegra_dc *dc = to_tegra_dc(state->crtc);
>  	struct tegra_bo_tiling tiling;
>  	int err;
> @@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
>  		}
>  	}
>  
> +	err = tegra_plane_state_add(tegra, state);
> +	if (err < 0)
> +		return err;
> +
>  	return 0;
>  }
>  
> @@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
>  	value &= ~WIN_ENABLE;
>  	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
>  
> -	tegra_dc_window_commit(dc, p->index);
> -
>  	spin_unlock_irqrestore(&dc->lock, flags);
>  }
>  
> @@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
>  static int tegra_cursor_atomic_check(struct drm_plane *plane,
>  				     struct drm_plane_state *state)
>  {
> +	struct tegra_plane *tegra = to_tegra_plane(plane);
> +	int err;
> +
>  	/* no need for further checks if the plane is being disabled */
>  	if (!state->crtc)
>  		return 0;
> @@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
>  	    state->crtc_w != 128 && state->crtc_w != 256)
>  		return -EINVAL;
>  
> +	err = tegra_plane_state_add(tegra, state);
> +	if (err < 0)
> +		return err;
> +
>  	return 0;
>  }
>  
> @@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
>  	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
>  	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
>  
> -	/* apply changes */
> -	tegra_dc_cursor_commit(dc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static void tegra_cursor_atomic_disable(struct drm_plane *plane,
> @@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
>  	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
>  	value &= ~CURSOR_ENABLE;
>  	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
> -
> -	tegra_dc_cursor_commit(dc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
> @@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
>  	if (!plane)
>  		return ERR_PTR(-ENOMEM);
>  
> +	/*
> +	 * We'll treat the cursor as an overlay plane with index 6 here so
> +	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
> +	 * match up.
> +	 */
> +	plane->index = 6;
> +
>  	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
>  	formats = tegra_cursor_plane_formats;
>  
> @@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
>  	}
>  
>  	drm_crtc_vblank_off(crtc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
> @@ -1207,10 +1221,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
>  
>  static void tegra_crtc_commit(struct drm_crtc *crtc)
>  {
> -	struct tegra_dc *dc = to_tegra_dc(crtc);
> -
>  	drm_crtc_vblank_on(crtc);
> -	tegra_dc_commit(dc);
>  }
>  
>  static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
> @@ -1235,6 +1246,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
>  
>  static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
>  {
> +	struct tegra_dc_state *state = to_dc_state(crtc->state);
> +	struct tegra_dc *dc = to_tegra_dc(crtc);
> +
> +	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
> +	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
>  }
>  
>  static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
> -- 
> 2.1.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 34/36] drm/tegra: Track active planes in CRTC state
  2015-01-20 11:18     ` Daniel Vetter
@ 2015-01-20 11:43       ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 11:43 UTC (permalink / raw)
  To: dri-devel, linux-tegra, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1303 bytes --]

On Tue, Jan 20, 2015 at 12:18:45PM +0100, Daniel Vetter wrote:
> On Tue, Jan 20, 2015 at 11:48:53AM +0100, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > Wrap struct drm_crtc_state in a driver-specific structure and add the
> > planes field which keeps track of which planes are updated or disabled
> > during a modeset. This allows atomic updates of the the display engine
> > at ->atomic_flush() time.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> 
> commit 6ddd388ab222b66b596342becc76d5031c0e2fc8
> Author: Rob Clark <robdclark@gmail.com>
> Date:   Fri Nov 21 15:28:31 2014 -0500
> 
>     drm/atomic: track bitmask of planes attached to crtc
> 
> added this to the core since it seems to be generally useful. Does tegra
> need more?

That commit adds code to track planes that are being activated, whereas
this commit sets bits for all planes that are being changed, including
those that are being disabled.

The difference is important because we have "GO" bits for each of the
planes, so if one is being disabled we have to set the "GO" bit to make
sure the changes are applied on the next VBLANK.

Perhaps the subject should be updated to something like:

	drm/tegra: Track changed planes in CRTC state

?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 34/36] drm/tegra: Track active planes in CRTC state
@ 2015-01-20 11:43       ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-20 11:43 UTC (permalink / raw)
  To: dri-devel, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1303 bytes --]

On Tue, Jan 20, 2015 at 12:18:45PM +0100, Daniel Vetter wrote:
> On Tue, Jan 20, 2015 at 11:48:53AM +0100, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > Wrap struct drm_crtc_state in a driver-specific structure and add the
> > planes field which keeps track of which planes are updated or disabled
> > during a modeset. This allows atomic updates of the the display engine
> > at ->atomic_flush() time.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> 
> commit 6ddd388ab222b66b596342becc76d5031c0e2fc8
> Author: Rob Clark <robdclark@gmail.com>
> Date:   Fri Nov 21 15:28:31 2014 -0500
> 
>     drm/atomic: track bitmask of planes attached to crtc
> 
> added this to the core since it seems to be generally useful. Does tegra
> need more?

That commit adds code to track planes that are being activated, whereas
this commit sets bits for all planes that are being changed, including
those that are being disabled.

The difference is important because we have "GO" bits for each of the
planes, so if one is being disabled we have to set the "GO" bit to make
sure the changes are applied on the next VBLANK.

Perhaps the subject should be updated to something like:

	drm/tegra: Track changed planes in CRTC state

?

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 13:51       ` Rob Clark
  -1 siblings, 0 replies; 120+ messages in thread
From: Rob Clark @ 2015-01-20 13:51 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List

On Tue, Jan 20, 2015 at 5:48 AM, Thierry Reding
<thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> There is no use-case where it would be useful for drivers not to
> implement this function and the transitional plane helpers already
> require drivers to provide an implementation.
>
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index d467185f7942..010661f23035 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>
>                 funcs = plane->helper_private;
>
> -               if (!funcs || !funcs->atomic_update)
> +               if (!funcs)

maybe WARN_ON() here.. or drm_universal_plane_init(), or somewhere,
for missing mandatory fxns would be a nice thing to avoid explosions
under console_lock for first modeset?

BR,
-R

>                         continue;
>
>                 old_plane_state = old_state->plane_states[i];
> --
> 2.1.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
@ 2015-01-20 13:51       ` Rob Clark
  0 siblings, 0 replies; 120+ messages in thread
From: Rob Clark @ 2015-01-20 13:51 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, Linux Kernel Mailing List

On Tue, Jan 20, 2015 at 5:48 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> There is no use-case where it would be useful for drivers not to
> implement this function and the transitional plane helpers already
> require drivers to provide an implementation.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index d467185f7942..010661f23035 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
>
>                 funcs = plane->helper_private;
>
> -               if (!funcs || !funcs->atomic_update)
> +               if (!funcs)

maybe WARN_ON() here.. or drm_universal_plane_init(), or somewhere,
for missing mandatory fxns would be a nice thing to avoid explosions
under console_lock for first modeset?

BR,
-R

>                         continue;
>
>                 old_plane_state = old_state->plane_states[i];
> --
> 2.1.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 10:48   ` Thierry Reding
  (?)
@ 2015-01-20 18:02   ` Mike Turquette
  2015-01-20 18:16       ` Rob Clark
  -1 siblings, 1 reply; 120+ messages in thread
From: Mike Turquette @ 2015-01-20 18:02 UTC (permalink / raw)
  To: Thierry Reding, dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Stephen Boyd

Quoting Thierry Reding (2015-01-20 02:48:20)
> From: Thierry Reding <treding@nvidia.com>
> 
> This new function is similar to clk_set_parent(), except that it doesn't
> actually change the parent. It merely checks that the given parent clock
> can be a parent for the given clock.
> 
> A situation where this is useful is to check that a particular setup is
> valid before switching to it. One specific use-case for this is atomic
> modesetting in the DRM framework where setting a mode is divided into a
> check phase where a given configuration is validated before applying
> changes to the hardware.

Can you describe why this was needed for your atomic modesetting work?
What problem did you hit in the driver that required this new check?

Thanks,
Mike

> 
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/clk/clk.c   | 36 ++++++++++++++++++++++++++++++++++++
>  include/linux/clk.h | 14 ++++++++++++++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index af06b7377d37..297910815dea 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1672,6 +1672,42 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
>  }
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * This is like clk_set_parent(), except that it only checks that parent can
> + * be the parent clock source for clock.
> + *
> + * Returns success (0) or negative errno.
> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent)
> +{
> +       int err = 0;
> +
> +       if (!clk || !parent)
> +               return -EINVAL;
> +
> +       if ((clk->num_parents > 1) && !clk->ops->set_parent)
> +               return -ENOSYS;
> +
> +       clk_prepare_lock();
> +
> +       if (clk->parent == parent)
> +               goto unlock;
> +
> +       err = clk_fetch_parent_index(clk, parent);
> +       if (err > 0)
> +               err = 0;
> +
> +unlock:
> +       clk_prepare_unlock();
> +
> +       return err;
> +}
> +EXPORT_SYMBOL_GPL(clk_try_parent);
> +
> +/**
>   * clk_set_parent - switch the parent of a mux clk
>   * @clk: the mux clk whose input we are switching
>   * @parent: the new input to clk
> diff --git a/include/linux/clk.h b/include/linux/clk.h
> index fb1ac65f127c..94da8c68a515 100644
> --- a/include/linux/clk.h
> +++ b/include/linux/clk.h
> @@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
>  int clk_set_rate(struct clk *clk, unsigned long rate);
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * Returns success (0) or negative errno.
> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent);
> +
> +/**
>   * clk_set_parent - set the parent clock source for this clock
>   * @clk: clock source
>   * @parent: parent clock source
> @@ -400,6 +409,11 @@ static inline long clk_round_rate(struct clk *clk, unsigned long rate)
>         return 0;
>  }
>  
> +static inline int clk_try_parent(struct clk *clk, struct clk *parent)
> +{
> +       return 0;
> +}
> +
>  static inline int clk_set_parent(struct clk *clk, struct clk *parent)
>  {
>         return 0;
> -- 
> 2.1.3
> 

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 18:02   ` Mike Turquette
@ 2015-01-20 18:16       ` Rob Clark
  0 siblings, 0 replies; 120+ messages in thread
From: Rob Clark @ 2015-01-20 18:16 UTC (permalink / raw)
  To: Mike Turquette
  Cc: Russell King, Stephen Boyd, Linux Kernel Mailing List, dri-devel,
	linux-tegra

On Tue, Jan 20, 2015 at 1:02 PM, Mike Turquette <mturquette@linaro.org> wrote:
> Quoting Thierry Reding (2015-01-20 02:48:20)
>> From: Thierry Reding <treding@nvidia.com>
>>
>> This new function is similar to clk_set_parent(), except that it doesn't
>> actually change the parent. It merely checks that the given parent clock
>> can be a parent for the given clock.
>>
>> A situation where this is useful is to check that a particular setup is
>> valid before switching to it. One specific use-case for this is atomic
>> modesetting in the DRM framework where setting a mode is divided into a
>> check phase where a given configuration is validated before applying
>> changes to the hardware.
>
> Can you describe why this was needed for your atomic modesetting work?
> What problem did you hit in the driver that required this new check?

In particular, for an async atomic update, we need to do everything
that may potentially fail up front before returning to userspace.
Additionally there is a 'testonly' flag so that userspace can ask if a
particular display configuration is possible.

BR,
-R

> Thanks,
> Mike
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
@ 2015-01-20 18:16       ` Rob Clark
  0 siblings, 0 replies; 120+ messages in thread
From: Rob Clark @ 2015-01-20 18:16 UTC (permalink / raw)
  To: Mike Turquette
  Cc: Thierry Reding, dri-devel, linux-tegra,
	Linux Kernel Mailing List, Russell King, Stephen Boyd

On Tue, Jan 20, 2015 at 1:02 PM, Mike Turquette <mturquette@linaro.org> wrote:
> Quoting Thierry Reding (2015-01-20 02:48:20)
>> From: Thierry Reding <treding@nvidia.com>
>>
>> This new function is similar to clk_set_parent(), except that it doesn't
>> actually change the parent. It merely checks that the given parent clock
>> can be a parent for the given clock.
>>
>> A situation where this is useful is to check that a particular setup is
>> valid before switching to it. One specific use-case for this is atomic
>> modesetting in the DRM framework where setting a mode is divided into a
>> check phase where a given configuration is validated before applying
>> changes to the hardware.
>
> Can you describe why this was needed for your atomic modesetting work?
> What problem did you hit in the driver that required this new check?

In particular, for an async atomic update, we need to do everything
that may potentially fail up front before returning to userspace.
Additionally there is a 'testonly' flag so that userspace can ask if a
particular display configuration is possible.

BR,
-R

> Thanks,
> Mike
>

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-20 19:21       ` Stephen Boyd
  -1 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-20 19:21 UTC (permalink / raw)
  To: Thierry Reding, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Russell King,
	Mike Turquette

On 01/20/2015 02:48 AM, Thierry Reding wrote:
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * This is like clk_set_parent(), except that it only checks that parent can
> + * be the parent clock source for clock.
> + *
> + * Returns success (0) or negative errno.
> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent)
> +{
> +	int err = 0;
> +
> +	if (!clk || !parent)
> +		return -EINVAL;

NULL clock should be a nop, so return success in either case.

> +
> +	if ((clk->num_parents > 1) && !clk->ops->set_parent)
> +		return -ENOSYS;

This suffers from the same problem as discussed in another thread where
the mux is read-only and the parent is the current parent. That case
shouldn't fail.

> +
> +	clk_prepare_lock();
> +
> +	if (clk->parent == parent)
> +		goto unlock;
> +
> +	err = clk_fetch_parent_index(clk, parent);
> +	if (err > 0)
> +		err = 0;
> +

Given that we just throw away the index, perhaps we should just loop
over the parent_names array searching for a name match on the parent's
name. If we did that this entire function would be lockless too.

> +unlock:
> +	clk_prepare_unlock();
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(clk_try_parent);
> +
> +/**
>   * clk_set_parent - switch the parent of a mux clk
>   * @clk: the mux clk whose input we are switching
>   * @parent: the new input to clk
> diff --git a/include/linux/clk.h b/include/linux/clk.h
> index fb1ac65f127c..94da8c68a515 100644
> --- a/include/linux/clk.h
> +++ b/include/linux/clk.h
> @@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
>  int clk_set_rate(struct clk *clk, unsigned long rate);
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * Returns success (0) or negative errno.

Why not a bool? Do we really care why we can't set the parent in the
error case?

> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent);

The name makes me think of mutex_trylock(), so I immediately think this
tries to set the parent. Perhaps a better name would be
clk_can_have_parent() or clk_has_parent()?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
@ 2015-01-20 19:21       ` Stephen Boyd
  0 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-20 19:21 UTC (permalink / raw)
  To: Thierry Reding, dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Mike Turquette

On 01/20/2015 02:48 AM, Thierry Reding wrote:
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * This is like clk_set_parent(), except that it only checks that parent can
> + * be the parent clock source for clock.
> + *
> + * Returns success (0) or negative errno.
> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent)
> +{
> +	int err = 0;
> +
> +	if (!clk || !parent)
> +		return -EINVAL;

NULL clock should be a nop, so return success in either case.

> +
> +	if ((clk->num_parents > 1) && !clk->ops->set_parent)
> +		return -ENOSYS;

This suffers from the same problem as discussed in another thread where
the mux is read-only and the parent is the current parent. That case
shouldn't fail.

> +
> +	clk_prepare_lock();
> +
> +	if (clk->parent == parent)
> +		goto unlock;
> +
> +	err = clk_fetch_parent_index(clk, parent);
> +	if (err > 0)
> +		err = 0;
> +

Given that we just throw away the index, perhaps we should just loop
over the parent_names array searching for a name match on the parent's
name. If we did that this entire function would be lockless too.

> +unlock:
> +	clk_prepare_unlock();
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(clk_try_parent);
> +
> +/**
>   * clk_set_parent - switch the parent of a mux clk
>   * @clk: the mux clk whose input we are switching
>   * @parent: the new input to clk
> diff --git a/include/linux/clk.h b/include/linux/clk.h
> index fb1ac65f127c..94da8c68a515 100644
> --- a/include/linux/clk.h
> +++ b/include/linux/clk.h
> @@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
>  int clk_set_rate(struct clk *clk, unsigned long rate);
>  
>  /**
> + * clk_try_parent - check if a clock can be the parent clock source of another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * Returns success (0) or negative errno.

Why not a bool? Do we really care why we can't set the parent in the
error case?

> + */
> +int clk_try_parent(struct clk *clk, struct clk *parent);

The name makes me think of mutex_trylock(), so I immediately think this
tries to set the parent. Perhaps a better name would be
clk_can_have_parent() or clk_has_parent()?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 19:21       ` Stephen Boyd
@ 2015-01-21 15:05         ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 15:05 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-tegra, Mike Turquette, Russell King, linux-kernel, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2905 bytes --]

On Tue, Jan 20, 2015 at 11:21:24AM -0800, Stephen Boyd wrote:
> On 01/20/2015 02:48 AM, Thierry Reding wrote:
> >  
> >  /**
> > + * clk_try_parent - check if a clock can be the parent clock source of another
> > + * @clk: clock source
> > + * @parent: parent clock source
> > + *
> > + * This is like clk_set_parent(), except that it only checks that parent can
> > + * be the parent clock source for clock.
> > + *
> > + * Returns success (0) or negative errno.
> > + */
> > +int clk_try_parent(struct clk *clk, struct clk *parent)
> > +{
> > +	int err = 0;
> > +
> > +	if (!clk || !parent)
> > +		return -EINVAL;
> 
> NULL clock should be a nop, so return success in either case.

Okay.

> > +
> > +	if ((clk->num_parents > 1) && !clk->ops->set_parent)
> > +		return -ENOSYS;
> 
> This suffers from the same problem as discussed in another thread where
> the mux is read-only and the parent is the current parent. That case
> shouldn't fail.

Okay, if I do a lookup on the parent names array as you suggested below
I don't need to consider this anyway.

> > +
> > +	clk_prepare_lock();
> > +
> > +	if (clk->parent == parent)
> > +		goto unlock;
> > +
> > +	err = clk_fetch_parent_index(clk, parent);
> > +	if (err > 0)
> > +		err = 0;
> > +
> 
> Given that we just throw away the index, perhaps we should just loop
> over the parent_names array searching for a name match on the parent's
> name. If we did that this entire function would be lockless too.

Done.

> > +unlock:
> > +	clk_prepare_unlock();
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(clk_try_parent);
> > +
> > +/**
> >   * clk_set_parent - switch the parent of a mux clk
> >   * @clk: the mux clk whose input we are switching
> >   * @parent: the new input to clk
> > diff --git a/include/linux/clk.h b/include/linux/clk.h
> > index fb1ac65f127c..94da8c68a515 100644
> > --- a/include/linux/clk.h
> > +++ b/include/linux/clk.h
> > @@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
> >  int clk_set_rate(struct clk *clk, unsigned long rate);
> >  
> >  /**
> > + * clk_try_parent - check if a clock can be the parent clock source of another
> > + * @clk: clock source
> > + * @parent: parent clock source
> > + *
> > + * Returns success (0) or negative errno.
> 
> Why not a bool? Do we really care why we can't set the parent in the
> error case?

A bool should do fine. I guess a negative error code would've been
easier to propagate, but we can probably just return an -EINVAL if
clk_has_parent() fails.

> > + */
> > +int clk_try_parent(struct clk *clk, struct clk *parent);
> 
> The name makes me think of mutex_trylock(), so I immediately think this
> tries to set the parent. Perhaps a better name would be
> clk_can_have_parent() or clk_has_parent()?

clk_has_parent() sounds good to me.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
@ 2015-01-21 15:05         ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 15:05 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: dri-devel, linux-tegra, linux-kernel, Russell King, Mike Turquette

[-- Attachment #1: Type: text/plain, Size: 2905 bytes --]

On Tue, Jan 20, 2015 at 11:21:24AM -0800, Stephen Boyd wrote:
> On 01/20/2015 02:48 AM, Thierry Reding wrote:
> >  
> >  /**
> > + * clk_try_parent - check if a clock can be the parent clock source of another
> > + * @clk: clock source
> > + * @parent: parent clock source
> > + *
> > + * This is like clk_set_parent(), except that it only checks that parent can
> > + * be the parent clock source for clock.
> > + *
> > + * Returns success (0) or negative errno.
> > + */
> > +int clk_try_parent(struct clk *clk, struct clk *parent)
> > +{
> > +	int err = 0;
> > +
> > +	if (!clk || !parent)
> > +		return -EINVAL;
> 
> NULL clock should be a nop, so return success in either case.

Okay.

> > +
> > +	if ((clk->num_parents > 1) && !clk->ops->set_parent)
> > +		return -ENOSYS;
> 
> This suffers from the same problem as discussed in another thread where
> the mux is read-only and the parent is the current parent. That case
> shouldn't fail.

Okay, if I do a lookup on the parent names array as you suggested below
I don't need to consider this anyway.

> > +
> > +	clk_prepare_lock();
> > +
> > +	if (clk->parent == parent)
> > +		goto unlock;
> > +
> > +	err = clk_fetch_parent_index(clk, parent);
> > +	if (err > 0)
> > +		err = 0;
> > +
> 
> Given that we just throw away the index, perhaps we should just loop
> over the parent_names array searching for a name match on the parent's
> name. If we did that this entire function would be lockless too.

Done.

> > +unlock:
> > +	clk_prepare_unlock();
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(clk_try_parent);
> > +
> > +/**
> >   * clk_set_parent - switch the parent of a mux clk
> >   * @clk: the mux clk whose input we are switching
> >   * @parent: the new input to clk
> > diff --git a/include/linux/clk.h b/include/linux/clk.h
> > index fb1ac65f127c..94da8c68a515 100644
> > --- a/include/linux/clk.h
> > +++ b/include/linux/clk.h
> > @@ -328,6 +328,15 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
> >  int clk_set_rate(struct clk *clk, unsigned long rate);
> >  
> >  /**
> > + * clk_try_parent - check if a clock can be the parent clock source of another
> > + * @clk: clock source
> > + * @parent: parent clock source
> > + *
> > + * Returns success (0) or negative errno.
> 
> Why not a bool? Do we really care why we can't set the parent in the
> error case?

A bool should do fine. I guess a negative error code would've been
easier to propagate, but we can probably just return an -EINVAL if
clk_has_parent() fails.

> > + */
> > +int clk_try_parent(struct clk *clk, struct clk *parent);
> 
> The name makes me think of mutex_trylock(), so I immediately think this
> tries to set the parent. Perhaps a better name would be
> clk_can_have_parent() or clk_has_parent()?

clk_has_parent() sounds good to me.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
  2015-01-20 18:16       ` Rob Clark
@ 2015-01-21 15:12         ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 15:12 UTC (permalink / raw)
  To: Rob Clark
  Cc: Russell King, Stephen Boyd, Linux Kernel Mailing List, dri-devel,
	linux-tegra, Mike Turquette


[-- Attachment #1.1: Type: text/plain, Size: 1719 bytes --]

On Tue, Jan 20, 2015 at 01:16:16PM -0500, Rob Clark wrote:
> On Tue, Jan 20, 2015 at 1:02 PM, Mike Turquette <mturquette@linaro.org> wrote:
> > Quoting Thierry Reding (2015-01-20 02:48:20)
> >> From: Thierry Reding <treding@nvidia.com>
> >>
> >> This new function is similar to clk_set_parent(), except that it doesn't
> >> actually change the parent. It merely checks that the given parent clock
> >> can be a parent for the given clock.
> >>
> >> A situation where this is useful is to check that a particular setup is
> >> valid before switching to it. One specific use-case for this is atomic
> >> modesetting in the DRM framework where setting a mode is divided into a
> >> check phase where a given configuration is validated before applying
> >> changes to the hardware.
> >
> > Can you describe why this was needed for your atomic modesetting work?
> > What problem did you hit in the driver that required this new check?
> 
> In particular, for an async atomic update, we need to do everything
> that may potentially fail up front before returning to userspace.
> Additionally there is a 'testonly' flag so that userspace can ask if a
> particular display configuration is possible.

One specific use-case where this is needed is if we want to set a mode
but halfway through the modeset we find out that we can't actually set
it, then we need to somehow rollback the changes, or be stuck with a
mode that doesn't work.

Splitting this into two separate stages allows all the checks to be
performed before making any changes to the hardware. The current mode
will remain active until the changes are committed. Therefore rollback
of a partial modeset is not required.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 01/36] clk: Introduce clk_try_parent()
@ 2015-01-21 15:12         ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 15:12 UTC (permalink / raw)
  To: Rob Clark
  Cc: Mike Turquette, dri-devel, linux-tegra,
	Linux Kernel Mailing List, Russell King, Stephen Boyd

[-- Attachment #1: Type: text/plain, Size: 1719 bytes --]

On Tue, Jan 20, 2015 at 01:16:16PM -0500, Rob Clark wrote:
> On Tue, Jan 20, 2015 at 1:02 PM, Mike Turquette <mturquette@linaro.org> wrote:
> > Quoting Thierry Reding (2015-01-20 02:48:20)
> >> From: Thierry Reding <treding@nvidia.com>
> >>
> >> This new function is similar to clk_set_parent(), except that it doesn't
> >> actually change the parent. It merely checks that the given parent clock
> >> can be a parent for the given clock.
> >>
> >> A situation where this is useful is to check that a particular setup is
> >> valid before switching to it. One specific use-case for this is atomic
> >> modesetting in the DRM framework where setting a mode is divided into a
> >> check phase where a given configuration is validated before applying
> >> changes to the hardware.
> >
> > Can you describe why this was needed for your atomic modesetting work?
> > What problem did you hit in the driver that required this new check?
> 
> In particular, for an async atomic update, we need to do everything
> that may potentially fail up front before returning to userspace.
> Additionally there is a 'testonly' flag so that userspace can ask if a
> particular display configuration is possible.

One specific use-case where this is needed is if we want to set a mode
but halfway through the modeset we find out that we can't actually set
it, then we need to somehow rollback the changes, or be stuck with a
mode that doesn't work.

Splitting this into two separate stages allows all the checks to be
performed before making any changes to the hardware. The current mode
will remain active until the changes are committed. Therefore rollback
of a partial modeset is not required.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-21 16:13     ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 16:13 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-tegra, Mike Turquette, Stephen Boyd, Russell King, linux-kernel

From: Thierry Reding <treding@nvidia.com>

This new function is similar to clk_set_parent(), except that it doesn't
actually change the parent. It merely checks that the given parent clock
can be a parent for the given clock.

A situation where this is useful is to check that a particular setup is
valid before switching to it. One specific use-case for this is atomic
modesetting in the DRM framework where setting a mode is divided into a
check phase where a given configuration is validated before applying
changes to the hardware.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v2:
- lookup parent name in parent names array and make function lockless
- rename function from clk_try_parent() to clk_has_parent()
- return boolean

 drivers/clk/clk.c   | 30 ++++++++++++++++++++++++++++++
 include/linux/clk.h | 17 +++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index af06b7377d37..470266297dea 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1672,6 +1672,36 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
 }
 
 /**
+ * clk_has_parent - check if a clock is a possible parent for another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This function can be used in drivers that need to check that a clock can be
+ * the parent of another without actually changing the parent.
+ *
+ * Returns true if @parent is a possible parent for @clk, false otherwise.
+ */
+bool clk_has_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned int i;
+
+	/* NULL clocks should be nops, so return success if either is NULL. */
+	if (!clk || !parent)
+		return true;
+
+	/* Optimize for the case where the parent is already the parent. */
+	if (clk->parent == parent)
+		return true;
+
+	for (i = 0; i < clk->num_parents; i++)
+		if (strcmp(clk->parent_names[i], parent->name) == 0)
+			return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(clk_has_parent);
+
+/**
  * clk_set_parent - switch the parent of a mux clk
  * @clk: the mux clk whose input we are switching
  * @parent: the new input to clk
diff --git a/include/linux/clk.h b/include/linux/clk.h
index fb1ac65f127c..df28673be2a1 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -328,6 +328,18 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
 int clk_set_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_has_parent - check if a clock is a possible parent for another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This function can be used in drivers that need to check that a clock can be
+ * the parent of another without actually changing the parent.
+ *
+ * Returns true if @parent is a possible parent for @clk, false otherwise.
+ */
+bool clk_has_parent(struct clk *clk, struct clk *parent);
+
+/**
  * clk_set_parent - set the parent clock source for this clock
  * @clk: clock source
  * @parent: parent clock source
@@ -400,6 +412,11 @@ static inline long clk_round_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static inline bool clk_has_parent(struct clk *clk, struct clk *parent)
+{
+	return true;
+}
+
 static inline int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	return 0;
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2] clk: Introduce clk_has_parent()
@ 2015-01-21 16:13     ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-21 16:13 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Mike Turquette, Stephen Boyd

From: Thierry Reding <treding@nvidia.com>

This new function is similar to clk_set_parent(), except that it doesn't
actually change the parent. It merely checks that the given parent clock
can be a parent for the given clock.

A situation where this is useful is to check that a particular setup is
valid before switching to it. One specific use-case for this is atomic
modesetting in the DRM framework where setting a mode is divided into a
check phase where a given configuration is validated before applying
changes to the hardware.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v2:
- lookup parent name in parent names array and make function lockless
- rename function from clk_try_parent() to clk_has_parent()
- return boolean

 drivers/clk/clk.c   | 30 ++++++++++++++++++++++++++++++
 include/linux/clk.h | 17 +++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index af06b7377d37..470266297dea 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1672,6 +1672,36 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
 }
 
 /**
+ * clk_has_parent - check if a clock is a possible parent for another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This function can be used in drivers that need to check that a clock can be
+ * the parent of another without actually changing the parent.
+ *
+ * Returns true if @parent is a possible parent for @clk, false otherwise.
+ */
+bool clk_has_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned int i;
+
+	/* NULL clocks should be nops, so return success if either is NULL. */
+	if (!clk || !parent)
+		return true;
+
+	/* Optimize for the case where the parent is already the parent. */
+	if (clk->parent == parent)
+		return true;
+
+	for (i = 0; i < clk->num_parents; i++)
+		if (strcmp(clk->parent_names[i], parent->name) == 0)
+			return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(clk_has_parent);
+
+/**
  * clk_set_parent - switch the parent of a mux clk
  * @clk: the mux clk whose input we are switching
  * @parent: the new input to clk
diff --git a/include/linux/clk.h b/include/linux/clk.h
index fb1ac65f127c..df28673be2a1 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -328,6 +328,18 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
 int clk_set_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_has_parent - check if a clock is a possible parent for another
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * This function can be used in drivers that need to check that a clock can be
+ * the parent of another without actually changing the parent.
+ *
+ * Returns true if @parent is a possible parent for @clk, false otherwise.
+ */
+bool clk_has_parent(struct clk *clk, struct clk *parent);
+
+/**
  * clk_set_parent - set the parent clock source for this clock
  * @clk: clock source
  * @parent: parent clock source
@@ -400,6 +412,11 @@ static inline long clk_round_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static inline bool clk_has_parent(struct clk *clk, struct clk *parent)
+{
+	return true;
+}
+
 static inline int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	return 0;
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-21 16:13     ` Thierry Reding
@ 2015-01-22  0:16         ` Stephen Boyd
  -1 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-22  0:16 UTC (permalink / raw)
  To: Thierry Reding, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Russell King,
	Mike Turquette, Tomeu Vizoso

On 01/21/2015 08:13 AM, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This new function is similar to clk_set_parent(), except that it doesn't
> actually change the parent. It merely checks that the given parent clock
> can be a parent for the given clock.
>
> A situation where this is useful is to check that a particular setup is
> valid before switching to it. One specific use-case for this is atomic
> modesetting in the DRM framework where setting a mode is divided into a
> check phase where a given configuration is validated before applying
> changes to the hardware.
>
> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---

Reviewed-by: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

This will slightly conflict with Tomeu's  patches for per-user clock
constraints. It would be best if we can take this through the clk tree
to fix up any conflicts

> Changes in v2:
> - lookup parent name in parent names array and make function lockless
> - rename function from clk_try_parent() to clk_has_parent()
> - return boolean
>
>  drivers/clk/clk.c   | 30 ++++++++++++++++++++++++++++++
>  include/linux/clk.h | 17 +++++++++++++++++
>  2 files changed, 47 insertions(+)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index af06b7377d37..470266297dea 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1672,6 +1672,36 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
>  }
>  
>  /**
> + * clk_has_parent - check if a clock is a possible parent for another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * This function can be used in drivers that need to check that a clock can be
> + * the parent of another without actually changing the parent.
> + *
> + * Returns true if @parent is a possible parent for @clk, false otherwise.
> + */
> +bool clk_has_parent(struct clk *clk, struct clk *parent)
> +{
> +	unsigned int i;
> +
> +	/* NULL clocks should be nops, so return success if either is NULL. */
> +	if (!clk || !parent)
> +		return true;
> +
> +	/* Optimize for the case where the parent is already the parent. */
> +	if (clk->parent == parent)
> +		return true;

I worry that we'll need to grab a lock here with Tomeu's patches, but
maybe I'm wrong.

> +
> +	for (i = 0; i < clk->num_parents; i++)
> +		if (strcmp(clk->parent_names[i], parent->name) == 0)
> +			return true;
> +
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(clk_has_parent);
> +
> +/**


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
@ 2015-01-22  0:16         ` Stephen Boyd
  0 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-22  0:16 UTC (permalink / raw)
  To: Thierry Reding, dri-devel
  Cc: linux-tegra, linux-kernel, Russell King, Mike Turquette, Tomeu Vizoso

On 01/21/2015 08:13 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> This new function is similar to clk_set_parent(), except that it doesn't
> actually change the parent. It merely checks that the given parent clock
> can be a parent for the given clock.
>
> A situation where this is useful is to check that a particular setup is
> valid before switching to it. One specific use-case for this is atomic
> modesetting in the DRM framework where setting a mode is divided into a
> check phase where a given configuration is validated before applying
> changes to the hardware.
>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---

Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>

This will slightly conflict with Tomeu's  patches for per-user clock
constraints. It would be best if we can take this through the clk tree
to fix up any conflicts

> Changes in v2:
> - lookup parent name in parent names array and make function lockless
> - rename function from clk_try_parent() to clk_has_parent()
> - return boolean
>
>  drivers/clk/clk.c   | 30 ++++++++++++++++++++++++++++++
>  include/linux/clk.h | 17 +++++++++++++++++
>  2 files changed, 47 insertions(+)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index af06b7377d37..470266297dea 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1672,6 +1672,36 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
>  }
>  
>  /**
> + * clk_has_parent - check if a clock is a possible parent for another
> + * @clk: clock source
> + * @parent: parent clock source
> + *
> + * This function can be used in drivers that need to check that a clock can be
> + * the parent of another without actually changing the parent.
> + *
> + * Returns true if @parent is a possible parent for @clk, false otherwise.
> + */
> +bool clk_has_parent(struct clk *clk, struct clk *parent)
> +{
> +	unsigned int i;
> +
> +	/* NULL clocks should be nops, so return success if either is NULL. */
> +	if (!clk || !parent)
> +		return true;
> +
> +	/* Optimize for the case where the parent is already the parent. */
> +	if (clk->parent == parent)
> +		return true;

I worry that we'll need to grab a lock here with Tomeu's patches, but
maybe I'm wrong.

> +
> +	for (i = 0; i < clk->num_parents; i++)
> +		if (strcmp(clk->parent_names[i], parent->name) == 0)
> +			return true;
> +
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(clk_has_parent);
> +
> +/**


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-22  0:16         ` Stephen Boyd
@ 2015-01-22  7:37             ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-22  7:37 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Russell King,
	Mike Turquette, Tomeu Vizoso

[-- Attachment #1: Type: text/plain, Size: 2899 bytes --]

On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> >
> > This new function is similar to clk_set_parent(), except that it doesn't
> > actually change the parent. It merely checks that the given parent clock
> > can be a parent for the given clock.
> >
> > A situation where this is useful is to check that a particular setup is
> > valid before switching to it. One specific use-case for this is atomic
> > modesetting in the DRM framework where setting a mode is divided into a
> > check phase where a given configuration is validated before applying
> > changes to the hardware.
> >
> > Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> > Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> 
> Reviewed-by: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> 
> This will slightly conflict with Tomeu's  patches for per-user clock
> constraints. It would be best if we can take this through the clk tree
> to fix up any conflicts

I had hoped to take this through the drm tree to resolve the build-time.
Another possibility would be for me to include the clk tree (or a subset
thereof) in my pull-request. That way you can still fix things up in the
clock tree if there are any conflicts with other work. We could make
that work two ways: this patch gets applied to the clk tree and I pull
it, or I provide a stable branch that I base my pull request on and that
branch can be pulled into the clk tree.

Yet another alternative would be to split out the clk_has_parent()
change from the series and not use it for now. That way we're going to
miss this check, but we do that anyway currently and it will only be
temporary until v3.21.

Perhaps given where we are in the release cycle the latter would make
the most sense for now.

> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
[...]
> > +	/* Optimize for the case where the parent is already the parent. */
> > +	if (clk->parent == parent)
> > +		return true;
> 
> I worry that we'll need to grab a lock here with Tomeu's patches, but
> maybe I'm wrong.

Why would this need a lock? Worst case somebody concurrently changes the
parent, in which case will not succeed and fallback to the lookup below.

It's been a while since I last looked at Tomeu's series, but I seem to
remember that struct clk was going to be per-user, in which case I guess
the code would have to be modified anyway since ->parent and
->parent_names will likely become properties of the clock structure
shared by all users (was it struct clk_core?).

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
@ 2015-01-22  7:37             ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-22  7:37 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: dri-devel, linux-tegra, linux-kernel, Russell King,
	Mike Turquette, Tomeu Vizoso

[-- Attachment #1: Type: text/plain, Size: 2739 bytes --]

On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > This new function is similar to clk_set_parent(), except that it doesn't
> > actually change the parent. It merely checks that the given parent clock
> > can be a parent for the given clock.
> >
> > A situation where this is useful is to check that a particular setup is
> > valid before switching to it. One specific use-case for this is atomic
> > modesetting in the DRM framework where setting a mode is divided into a
> > check phase where a given configuration is validated before applying
> > changes to the hardware.
> >
> > Cc: Russell King <linux@arm.linux.org.uk>
> > Cc: Mike Turquette <mturquette@linaro.org>
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> 
> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> 
> This will slightly conflict with Tomeu's  patches for per-user clock
> constraints. It would be best if we can take this through the clk tree
> to fix up any conflicts

I had hoped to take this through the drm tree to resolve the build-time.
Another possibility would be for me to include the clk tree (or a subset
thereof) in my pull-request. That way you can still fix things up in the
clock tree if there are any conflicts with other work. We could make
that work two ways: this patch gets applied to the clk tree and I pull
it, or I provide a stable branch that I base my pull request on and that
branch can be pulled into the clk tree.

Yet another alternative would be to split out the clk_has_parent()
change from the series and not use it for now. That way we're going to
miss this check, but we do that anyway currently and it will only be
temporary until v3.21.

Perhaps given where we are in the release cycle the latter would make
the most sense for now.

> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
[...]
> > +	/* Optimize for the case where the parent is already the parent. */
> > +	if (clk->parent == parent)
> > +		return true;
> 
> I worry that we'll need to grab a lock here with Tomeu's patches, but
> maybe I'm wrong.

Why would this need a lock? Worst case somebody concurrently changes the
parent, in which case will not succeed and fallback to the lookup below.

It's been a while since I last looked at Tomeu's series, but I seem to
remember that struct clk was going to be per-user, in which case I guess
the code would have to be modified anyway since ->parent and
->parent_names will likely become properties of the clock structure
shared by all users (was it struct clk_core?).

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-22 18:57     ` Gustavo Padovan
  -1 siblings, 0 replies; 120+ messages in thread
From: Gustavo Padovan @ 2015-01-22 18:57 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, dri-devel

2015-01-20 Thierry Reding <thierry.reding@gmail.com>:

> From: Thierry Reding <treding@nvidia.com>
> 
> In order to prevent drivers from having to perform the same checks over
> and over again, add an optional ->atomic_disable callback which the core
> calls under the right circumstances.
> 
> v2: pass old state and detect edges to avoid calling ->atomic_disable on
> already disabled planes, remove redundant comment (Daniel Vetter)
> 
> v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
> checking for transitions, move helper to drm_atomic_helper.h, clarify
> check for !old_state and its relation to transitional helpers
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
>  drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
>  include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
>  include/drm/drm_plane_helper.h      |  3 +++
>  4 files changed, 57 insertions(+), 2 deletions(-)

Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

	Gustavo
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback
@ 2015-01-22 18:57     ` Gustavo Padovan
  0 siblings, 0 replies; 120+ messages in thread
From: Gustavo Padovan @ 2015-01-22 18:57 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel, linux-tegra, linux-kernel

2015-01-20 Thierry Reding <thierry.reding@gmail.com>:

> From: Thierry Reding <treding@nvidia.com>
> 
> In order to prevent drivers from having to perform the same checks over
> and over again, add an optional ->atomic_disable callback which the core
> calls under the right circumstances.
> 
> v2: pass old state and detect edges to avoid calling ->atomic_disable on
> already disabled planes, remove redundant comment (Daniel Vetter)
> 
> v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
> checking for transitions, move helper to drm_atomic_helper.h, clarify
> check for !old_state and its relation to transitional helpers
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
>  drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
>  include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
>  include/drm/drm_plane_helper.h      |  3 +++
>  4 files changed, 57 insertions(+), 2 deletions(-)

Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

	Gustavo

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-22  7:37             ` Thierry Reding
@ 2015-01-22 20:25               ` Stephen Boyd
  -1 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-22 20:25 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Russell King,
	Mike Turquette, Tomeu Vizoso

On 01/22, Thierry Reding wrote:
> On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> > On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > >
> > > This new function is similar to clk_set_parent(), except that it doesn't
> > > actually change the parent. It merely checks that the given parent clock
> > > can be a parent for the given clock.
> > >
> > > A situation where this is useful is to check that a particular setup is
> > > valid before switching to it. One specific use-case for this is atomic
> > > modesetting in the DRM framework where setting a mode is divided into a
> > > check phase where a given configuration is validated before applying
> > > changes to the hardware.
> > >
> > > Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> > > Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > > Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> > > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > > ---
> > 
> > Reviewed-by: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> > 
> > This will slightly conflict with Tomeu's  patches for per-user clock
> > constraints. It would be best if we can take this through the clk tree
> > to fix up any conflicts
> 
> I had hoped to take this through the drm tree to resolve the build-time.
> Another possibility would be for me to include the clk tree (or a subset
> thereof) in my pull-request. That way you can still fix things up in the
> clock tree if there are any conflicts with other work. We could make
> that work two ways: this patch gets applied to the clk tree and I pull
> it, or I provide a stable branch that I base my pull request on and that
> branch can be pulled into the clk tree.
> 
> Yet another alternative would be to split out the clk_has_parent()
> change from the series and not use it for now. That way we're going to
> miss this check, but we do that anyway currently and it will only be
> temporary until v3.21.
> 
> Perhaps given where we are in the release cycle the latter would make
> the most sense for now.

Ok well let's see what Mike wants to do given that he's doing all
the patch applying right now. I'd think that we could put this
one patch on a different branch that we can merge into clk-next
and you can merge into the drm tree. At least that's the typical
workflow that usually works for everyone.

> 
> > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> [...]
> > > +	/* Optimize for the case where the parent is already the parent. */
> > > +	if (clk->parent == parent)
> > > +		return true;
> > 
> > I worry that we'll need to grab a lock here with Tomeu's patches, but
> > maybe I'm wrong.
> 
> Why would this need a lock? Worst case somebody concurrently changes the
> parent, in which case will not succeed and fallback to the lookup below.

I was mostly worried about clk_core going away but we would
already have a reference on it so you're right, we don't need any
locks.

> 
> It's been a while since I last looked at Tomeu's series, but I seem to
> remember that struct clk was going to be per-user, in which case I guess
> the code would have to be modified anyway since ->parent and
> ->parent_names will likely become properties of the clock structure
> shared by all users (was it struct clk_core?).
> 

Yes it will have to be fixed up, hence the comment about slight
conflicts.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
@ 2015-01-22 20:25               ` Stephen Boyd
  0 siblings, 0 replies; 120+ messages in thread
From: Stephen Boyd @ 2015-01-22 20:25 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel, linux-tegra, linux-kernel, Russell King,
	Mike Turquette, Tomeu Vizoso

On 01/22, Thierry Reding wrote:
> On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> > On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > > From: Thierry Reding <treding@nvidia.com>
> > >
> > > This new function is similar to clk_set_parent(), except that it doesn't
> > > actually change the parent. It merely checks that the given parent clock
> > > can be a parent for the given clock.
> > >
> > > A situation where this is useful is to check that a particular setup is
> > > valid before switching to it. One specific use-case for this is atomic
> > > modesetting in the DRM framework where setting a mode is divided into a
> > > check phase where a given configuration is validated before applying
> > > changes to the hardware.
> > >
> > > Cc: Russell King <linux@arm.linux.org.uk>
> > > Cc: Mike Turquette <mturquette@linaro.org>
> > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > ---
> > 
> > Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> > 
> > This will slightly conflict with Tomeu's  patches for per-user clock
> > constraints. It would be best if we can take this through the clk tree
> > to fix up any conflicts
> 
> I had hoped to take this through the drm tree to resolve the build-time.
> Another possibility would be for me to include the clk tree (or a subset
> thereof) in my pull-request. That way you can still fix things up in the
> clock tree if there are any conflicts with other work. We could make
> that work two ways: this patch gets applied to the clk tree and I pull
> it, or I provide a stable branch that I base my pull request on and that
> branch can be pulled into the clk tree.
> 
> Yet another alternative would be to split out the clk_has_parent()
> change from the series and not use it for now. That way we're going to
> miss this check, but we do that anyway currently and it will only be
> temporary until v3.21.
> 
> Perhaps given where we are in the release cycle the latter would make
> the most sense for now.

Ok well let's see what Mike wants to do given that he's doing all
the patch applying right now. I'd think that we could put this
one patch on a different branch that we can merge into clk-next
and you can merge into the drm tree. At least that's the typical
workflow that usually works for everyone.

> 
> > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> [...]
> > > +	/* Optimize for the case where the parent is already the parent. */
> > > +	if (clk->parent == parent)
> > > +		return true;
> > 
> > I worry that we'll need to grab a lock here with Tomeu's patches, but
> > maybe I'm wrong.
> 
> Why would this need a lock? Worst case somebody concurrently changes the
> parent, in which case will not succeed and fallback to the lookup below.

I was mostly worried about clk_core going away but we would
already have a reference on it so you're right, we don't need any
locks.

> 
> It's been a while since I last looked at Tomeu's series, but I seem to
> remember that struct clk was going to be per-user, in which case I guess
> the code would have to be modified anyway since ->parent and
> ->parent_names will likely become properties of the clock structure
> shared by all users (was it struct clk_core?).
> 

Yes it will have to be fixed up, hence the comment about slight
conflicts.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
  2015-01-20 13:51       ` Rob Clark
@ 2015-01-23  9:18         ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:18 UTC (permalink / raw)
  To: Rob Clark; +Cc: linux-tegra, Linux Kernel Mailing List, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1497 bytes --]

On Tue, Jan 20, 2015 at 08:51:15AM -0500, Rob Clark wrote:
> On Tue, Jan 20, 2015 at 5:48 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > There is no use-case where it would be useful for drivers not to
> > implement this function and the transitional plane helpers already
> > require drivers to provide an implementation.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> > index d467185f7942..010661f23035 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
> >
> >                 funcs = plane->helper_private;
> >
> > -               if (!funcs || !funcs->atomic_update)
> > +               if (!funcs)
> 
> maybe WARN_ON() here.. or drm_universal_plane_init(), or somewhere,
> for missing mandatory fxns would be a nice thing to avoid explosions
> under console_lock for first modeset?

WARN_ON() here isn't going to do you any good under console_lock and
may drivers use drm_universal_plane_init() without having converted to
atomic mode-setting.

Adding a comment to kerneldoc that it's mandatory is probably the best
we can do here.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory
@ 2015-01-23  9:18         ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:18 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel, linux-tegra, Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]

On Tue, Jan 20, 2015 at 08:51:15AM -0500, Rob Clark wrote:
> On Tue, Jan 20, 2015 at 5:48 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > There is no use-case where it would be useful for drivers not to
> > implement this function and the transitional plane helpers already
> > require drivers to provide an implementation.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/gpu/drm/drm_atomic_helper.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> > index d467185f7942..010661f23035 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
> >
> >                 funcs = plane->helper_private;
> >
> > -               if (!funcs || !funcs->atomic_update)
> > +               if (!funcs)
> 
> maybe WARN_ON() here.. or drm_universal_plane_init(), or somewhere,
> for missing mandatory fxns would be a nice thing to avoid explosions
> under console_lock for first modeset?

WARN_ON() here isn't going to do you any good under console_lock and
may drivers use drm_universal_plane_init() without having converted to
atomic mode-setting.

Adding a comment to kerneldoc that it's mandatory is probably the best
we can do here.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v2] drm/plane: Make ->atomic_update() mandatory
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-23  9:26       ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:26 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Daniel Vetter, Rob Clark, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

There is no use-case where it would be useful for drivers not to
implement this function and the transitional plane helpers already
require drivers to provide an implementation.

v2: add new requirement to kerneldoc

Reviewed-by: Daniel Vetter <daniel.vetter-/w4YWyX8dFk@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/gpu/drm/drm_atomic_helper.c | 2 +-
 include/drm/drm_plane_helper.h      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d467185f7942..010661f23035 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		funcs = plane->helper_private;
 
-		if (!funcs || !funcs->atomic_update)
+		if (!funcs)
 			continue;
 
 		old_plane_state = old_state->plane_states[i];
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index 0f2230311aa8..a44139abbd5e 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -51,7 +51,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @prepare_fb: prepare a framebuffer for use by the plane
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
- * @atomic_update: apply an atomic state to the plane
+ * @atomic_update: apply an atomic state to the plane (mandatory)
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
-- 
2.1.3

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2] drm/plane: Make ->atomic_update() mandatory
@ 2015-01-23  9:26       ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:26 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Rob Clark, linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

There is no use-case where it would be useful for drivers not to
implement this function and the transitional plane helpers already
require drivers to provide an implementation.

v2: add new requirement to kerneldoc

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 2 +-
 include/drm/drm_plane_helper.h      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d467185f7942..010661f23035 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1108,7 +1108,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		funcs = plane->helper_private;
 
-		if (!funcs || !funcs->atomic_update)
+		if (!funcs)
 			continue;
 
 		old_plane_state = old_state->plane_states[i];
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index 0f2230311aa8..a44139abbd5e 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -51,7 +51,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @prepare_fb: prepare a framebuffer for use by the plane
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
- * @atomic_update: apply an atomic state to the plane
+ * @atomic_update: apply an atomic state to the plane (mandatory)
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4] drm/plane: Add optional ->atomic_disable() callback
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-23  9:28     ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:28 UTC (permalink / raw)
  To: dri-devel; +Cc: linux-tegra, Gustavo Padovan, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to prevent drivers from having to perform the same checks over
and over again, add an optional ->atomic_disable callback which the core
calls under the right circumstances.

v2: pass old state and detect edges to avoid calling ->atomic_disable on
already disabled planes, remove redundant comment (Daniel Vetter)

v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
checking for transitions, move helper to drm_atomic_helper.h, clarify
check for !old_state and its relation to transitional helpers

Here's an extract from some discussion rationalizing the behaviour (for
a full version, see the reference below):

    > > Hm, thinking about this some more this will result in a slight difference
    > > in behaviour, at least when drivers just use the helper ->reset functions
    > > but don't disable everything:
    > > - With transitional helpers we assume we know nothing and call
    > >   ->atomic_disable.
    > > - With atomic old_state->crtc == NULL in the same situation right after
    > >   boot-up, but we asssume the plane is really off and _dont_ call
    > >   ->atomic_disable.
    > >
    > > Should we instead check for (old_state && old_state->crtc) and state that
    > > drivers need to make sure they don't have stuff hanging around?
    >
    > I don't think we can check for old_state because otherwise this will
    > always return false, whereas we really want it to force-disable planes
    > that could be on (lacking any more accurate information). For
    > transitional helpers anyway.
    >
    > For the atomic helpers, old_state will never be NULL, but I'd assume
    > that the driver would reconstruct the current state in ->reset().

    By the way, the reason for why old_state can be NULL with transitional
    helpers is the ordering of the steps in the atomic transition. Currently
    the Tegra patches do this (based on your blog post and the Exynos proto-
    type):

        1) atomic conversion, phase 1:
           - implement ->atomic_{check,update,disable}()
           - use drm_plane_helper_{update,disable}()

        2) atomic conversion, phase 2:
           - call drm_mode_config_reset() from ->load()
           - implement ->reset()

    That's only a partial list of what's done in these steps, but that's the
    only relevant pieces for why old_state is NULL.

    What happens is that without ->reset() implemented there won't be any
    initial state, hence plane->state (the old_state here) will be NULL the
    first time atomic state is applied.

    We could of course reorder the sequence such that drivers are required
    to hook up ->reset() before they can (or at the same as they) hook up
    the transitional helpers. We could add an appropriate WARN_ON to this
    helper to make that more obvious.

    However, that will not solve the problem because it only gets rid of the
    special case. We still don't know whether old_state->crtc == NULL is the
    current state or just the initial default.

    So no matter which way we do this, I don't see a way to get away without
    requiring specific semantics from drivers. They would be that:

        - drivers recreate the correct state in ->reset() so that
          old_state->crtc != NULL if the plane is really enabled

    or

        - drivers have to ensure that the real state in fact mirrors the
          initial default as encoded in the state (plane disabled)

References: http://lists.freedesktop.org/archives/dri-devel/2015-January/075578.html
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
 drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
 include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_plane_helper.h      |  3 +++
 4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 010661f23035..1cb04402cd73 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		old_plane_state = old_state->plane_states[i];
 
-		funcs->atomic_update(plane, old_plane_state);
+		/*
+		 * Special-case disabling the plane if drivers support it.
+		 */
+		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+		    funcs->atomic_disable)
+			funcs->atomic_disable(plane, old_plane_state);
+		else
+			funcs->atomic_update(plane, old_plane_state);
 	}
 
 	for (i = 0; i < ncrtcs; i++) {
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 2f961c180273..02c1a0b74e04 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 			crtc_funcs[i]->atomic_begin(crtc[i]);
 	}
 
-	plane_funcs->atomic_update(plane, plane_state);
+	/*
+	 * Drivers may optionally implement the ->atomic_disable callback, so
+	 * special-case that here.
+	 */
+	if (drm_atomic_plane_disabling(plane, plane_state) &&
+	    plane_funcs->atomic_disable)
+		plane_funcs->atomic_disable(plane, plane_state);
+	else
+		plane_funcs->atomic_update(plane, plane_state);
 
 	for (i = 0; i < 2; i++) {
 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 2095917ff8c7..a0ea4ded3cb5 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
 #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
 	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
 
+/*
+ * drm_atomic_plane_disabling - check whether a plane is being disabled
+ * @plane: plane object
+ * @old_state: previous atomic state
+ *
+ * Checks the atomic state of a plane to determine whether it's being disabled
+ * or not. This also WARNs if it detects an invalid state (both CRTC and FB
+ * need to either both be NULL or both be non-NULL).
+ *
+ * RETURNS:
+ * True if the plane is being disabled, false otherwise.
+ */
+static inline bool
+drm_atomic_plane_disabling(struct drm_plane *plane,
+			   struct drm_plane_state *old_state)
+{
+	/*
+	 * When disabling a plane, CRTC and FB should always be NULL together.
+	 * Anything else should be considered a bug in the atomic core, so we
+	 * gently warn about it.
+	 */
+	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
+		(plane->state->crtc != NULL && plane->state->fb == NULL));
+
+	/*
+	 * When using the transitional helpers, old_state may be NULL. If so,
+	 * we know nothing about the current state and have to assume that it
+	 * might be enabled.
+	 *
+	 * When using the atomic helpers, old_state won't be NULL. Therefore
+	 * this check assumes that either the driver will have reconstructed
+	 * the correct state in ->reset() or that the driver will have taken
+	 * appropriate measures to disable all planes.
+	 */
+	return (!old_state || old_state->crtc) && !plane->state->crtc;
+}
+
 #endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index a44139abbd5e..25b27ffd73c8 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
  * @atomic_update: apply an atomic state to the plane (mandatory)
+ * @atomic_disable: disable the plane
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
 			    struct drm_plane_state *state);
 	void (*atomic_update)(struct drm_plane *plane,
 			      struct drm_plane_state *old_state);
+	void (*atomic_disable)(struct drm_plane *plane,
+			       struct drm_plane_state *old_state);
 };
 
 static inline void drm_plane_helper_add(struct drm_plane *plane,
-- 
2.1.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4] drm/plane: Add optional ->atomic_disable() callback
@ 2015-01-23  9:28     ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:28 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, Gustavo Padovan, linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

In order to prevent drivers from having to perform the same checks over
and over again, add an optional ->atomic_disable callback which the core
calls under the right circumstances.

v2: pass old state and detect edges to avoid calling ->atomic_disable on
already disabled planes, remove redundant comment (Daniel Vetter)

v3: rename helper to drm_atomic_plane_disabling() to clarify that it is
checking for transitions, move helper to drm_atomic_helper.h, clarify
check for !old_state and its relation to transitional helpers

Here's an extract from some discussion rationalizing the behaviour (for
a full version, see the reference below):

    > > Hm, thinking about this some more this will result in a slight difference
    > > in behaviour, at least when drivers just use the helper ->reset functions
    > > but don't disable everything:
    > > - With transitional helpers we assume we know nothing and call
    > >   ->atomic_disable.
    > > - With atomic old_state->crtc == NULL in the same situation right after
    > >   boot-up, but we asssume the plane is really off and _dont_ call
    > >   ->atomic_disable.
    > >
    > > Should we instead check for (old_state && old_state->crtc) and state that
    > > drivers need to make sure they don't have stuff hanging around?
    >
    > I don't think we can check for old_state because otherwise this will
    > always return false, whereas we really want it to force-disable planes
    > that could be on (lacking any more accurate information). For
    > transitional helpers anyway.
    >
    > For the atomic helpers, old_state will never be NULL, but I'd assume
    > that the driver would reconstruct the current state in ->reset().

    By the way, the reason for why old_state can be NULL with transitional
    helpers is the ordering of the steps in the atomic transition. Currently
    the Tegra patches do this (based on your blog post and the Exynos proto-
    type):

        1) atomic conversion, phase 1:
           - implement ->atomic_{check,update,disable}()
           - use drm_plane_helper_{update,disable}()

        2) atomic conversion, phase 2:
           - call drm_mode_config_reset() from ->load()
           - implement ->reset()

    That's only a partial list of what's done in these steps, but that's the
    only relevant pieces for why old_state is NULL.

    What happens is that without ->reset() implemented there won't be any
    initial state, hence plane->state (the old_state here) will be NULL the
    first time atomic state is applied.

    We could of course reorder the sequence such that drivers are required
    to hook up ->reset() before they can (or at the same as they) hook up
    the transitional helpers. We could add an appropriate WARN_ON to this
    helper to make that more obvious.

    However, that will not solve the problem because it only gets rid of the
    special case. We still don't know whether old_state->crtc == NULL is the
    current state or just the initial default.

    So no matter which way we do this, I don't see a way to get away without
    requiring specific semantics from drivers. They would be that:

        - drivers recreate the correct state in ->reset() so that
          old_state->crtc != NULL if the plane is really enabled

    or

        - drivers have to ensure that the real state in fact mirrors the
          initial default as encoded in the state (plane disabled)

References: http://lists.freedesktop.org/archives/dri-devel/2015-January/075578.html
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_atomic_helper.c |  9 ++++++++-
 drivers/gpu/drm/drm_plane_helper.c  | 10 +++++++++-
 include/drm/drm_atomic_helper.h     | 37 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_plane_helper.h      |  3 +++
 4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 010661f23035..1cb04402cd73 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1113,7 +1113,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
 		old_plane_state = old_state->plane_states[i];
 
-		funcs->atomic_update(plane, old_plane_state);
+		/*
+		 * Special-case disabling the plane if drivers support it.
+		 */
+		if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+		    funcs->atomic_disable)
+			funcs->atomic_disable(plane, old_plane_state);
+		else
+			funcs->atomic_update(plane, old_plane_state);
 	}
 
 	for (i = 0; i < ncrtcs; i++) {
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 2f961c180273..02c1a0b74e04 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -449,7 +449,15 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 			crtc_funcs[i]->atomic_begin(crtc[i]);
 	}
 
-	plane_funcs->atomic_update(plane, plane_state);
+	/*
+	 * Drivers may optionally implement the ->atomic_disable callback, so
+	 * special-case that here.
+	 */
+	if (drm_atomic_plane_disabling(plane, plane_state) &&
+	    plane_funcs->atomic_disable)
+		plane_funcs->atomic_disable(plane, plane_state);
+	else
+		plane_funcs->atomic_update(plane, plane_state);
 
 	for (i = 0; i < 2; i++) {
 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 2095917ff8c7..a0ea4ded3cb5 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -127,4 +127,41 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
 #define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \
 	drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask)
 
+/*
+ * drm_atomic_plane_disabling - check whether a plane is being disabled
+ * @plane: plane object
+ * @old_state: previous atomic state
+ *
+ * Checks the atomic state of a plane to determine whether it's being disabled
+ * or not. This also WARNs if it detects an invalid state (both CRTC and FB
+ * need to either both be NULL or both be non-NULL).
+ *
+ * RETURNS:
+ * True if the plane is being disabled, false otherwise.
+ */
+static inline bool
+drm_atomic_plane_disabling(struct drm_plane *plane,
+			   struct drm_plane_state *old_state)
+{
+	/*
+	 * When disabling a plane, CRTC and FB should always be NULL together.
+	 * Anything else should be considered a bug in the atomic core, so we
+	 * gently warn about it.
+	 */
+	WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) ||
+		(plane->state->crtc != NULL && plane->state->fb == NULL));
+
+	/*
+	 * When using the transitional helpers, old_state may be NULL. If so,
+	 * we know nothing about the current state and have to assume that it
+	 * might be enabled.
+	 *
+	 * When using the atomic helpers, old_state won't be NULL. Therefore
+	 * this check assumes that either the driver will have reconstructed
+	 * the correct state in ->reset() or that the driver will have taken
+	 * appropriate measures to disable all planes.
+	 */
+	return (!old_state || old_state->crtc) && !plane->state->crtc;
+}
+
 #endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index a44139abbd5e..25b27ffd73c8 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -52,6 +52,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane
  * @atomic_check: check that a given atomic state is valid and can be applied
  * @atomic_update: apply an atomic state to the plane (mandatory)
+ * @atomic_disable: disable the plane
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -65,6 +66,8 @@ struct drm_plane_helper_funcs {
 			    struct drm_plane_state *state);
 	void (*atomic_update)(struct drm_plane *plane,
 			      struct drm_plane_state *old_state);
+	void (*atomic_disable)(struct drm_plane *plane,
+			       struct drm_plane_state *old_state);
 };
 
 static inline void drm_plane_helper_add(struct drm_plane *plane,
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2] drm/tegra: Track active planes in CRTC state
  2015-01-20 10:48   ` Thierry Reding
@ 2015-01-23  9:31       ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:31 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Daniel Vetter, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Wrap struct drm_crtc_state in a driver-specific structure and add the
planes field which keeps track of which planes are updated or disabled
during a modeset. This allows atomic updates of the the display engine
at ->atomic_flush() time.

v2: open-code getting the state of the CRTC that the plane is being
    attached to (Daniel Vetter)

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 5da1b59e7cfd..c283ea4d0095 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -54,6 +54,8 @@ struct tegra_dc_state {
 	struct clk *clk;
 	unsigned long pclk;
 	unsigned int div;
+
+	u32 planes;
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
-static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
-{
-	u32 value = WIN_A_ACT_REQ << index;
-
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-}
-
-static void tegra_dc_cursor_commit(struct tegra_dc *dc)
-{
-	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
-}
-
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 		break;
 	}
 
-	tegra_dc_window_commit(dc, index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
 {
 }
 
+static int tegra_plane_state_add(struct tegra_plane *plane,
+				 struct drm_plane_state *state)
+{
+	struct drm_crtc_state *crtc_state;
+	struct tegra_dc_state *tegra;
+
+	/* Propagate errors from allocation or locking failures. */
+	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	tegra = to_dc_state(crtc_state);
+
+	tegra->planes |= WIN_A_ACT_REQ << plane->index;
+
+	return 0;
+}
+
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_bo_tiling tiling;
 	int err;
@@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 		}
 	}
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
 	value &= ~WIN_ENABLE;
 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
-	tegra_dc_window_commit(dc, p->index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
 static int tegra_cursor_atomic_check(struct drm_plane *plane,
 				     struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+	int err;
+
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
@@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
 	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
 	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
-	/* apply changes */
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static void tegra_cursor_atomic_disable(struct drm_plane *plane,
@@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
@@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	if (!plane)
 		return ERR_PTR(-ENOMEM);
 
+	/*
+	 * We'll treat the cursor as an overlay plane with index 6 here so
+	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
+	 * match up.
+	 */
+	plane->index = 6;
+
 	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
 	formats = tegra_cursor_plane_formats;
 
@@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	}
 
 	drm_crtc_vblank_off(crtc);
-	tegra_dc_commit(dc);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1202,10 +1216,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 static void tegra_crtc_commit(struct drm_crtc *crtc)
 {
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
 	drm_crtc_vblank_on(crtc);
-	tegra_dc_commit(dc);
 }
 
 static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
@@ -1230,6 +1241,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
 {
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
+	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
 }
 
 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
-- 
2.1.3

^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2] drm/tegra: Track active planes in CRTC state
@ 2015-01-23  9:31       ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:31 UTC (permalink / raw)
  To: dri-devel; +Cc: Daniel Vetter, linux-tegra, linux-kernel

From: Thierry Reding <treding@nvidia.com>

Wrap struct drm_crtc_state in a driver-specific structure and add the
planes field which keeps track of which planes are updated or disabled
during a modeset. This allows atomic updates of the the display engine
at ->atomic_flush() time.

v2: open-code getting the state of the CRTC that the plane is being
    attached to (Daniel Vetter)

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 5da1b59e7cfd..c283ea4d0095 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -54,6 +54,8 @@ struct tegra_dc_state {
 	struct clk *clk;
 	unsigned long pclk;
 	unsigned int div;
+
+	u32 planes;
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -64,20 +66,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
 	return NULL;
 }
 
-static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
-{
-	u32 value = WIN_A_ACT_REQ << index;
-
-	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
-}
-
-static void tegra_dc_cursor_commit(struct tegra_dc *dc)
-{
-	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
-	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
-}
-
 /*
  * Reads the active copy of a register. This takes the dc->lock spinlock to
  * prevent races with the VBLANK processing which also needs access to the
@@ -395,8 +383,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
 		break;
 	}
 
-	tegra_dc_window_commit(dc, index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -439,9 +425,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane,
 {
 }
 
+static int tegra_plane_state_add(struct tegra_plane *plane,
+				 struct drm_plane_state *state)
+{
+	struct drm_crtc_state *crtc_state;
+	struct tegra_dc_state *tegra;
+
+	/* Propagate errors from allocation or locking failures. */
+	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	tegra = to_dc_state(crtc_state);
+
+	tegra->planes |= WIN_A_ACT_REQ << plane->index;
+
+	return 0;
+}
+
 static int tegra_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_bo_tiling tiling;
 	int err;
@@ -472,6 +477,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
 		}
 	}
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -538,8 +547,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
 	value &= ~WIN_ENABLE;
 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
-	tegra_dc_window_commit(dc, p->index);
-
 	spin_unlock_irqrestore(&dc->lock, flags);
 }
 
@@ -599,6 +606,9 @@ static const u32 tegra_cursor_plane_formats[] = {
 static int tegra_cursor_atomic_check(struct drm_plane *plane,
 				     struct drm_plane_state *state)
 {
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+	int err;
+
 	/* no need for further checks if the plane is being disabled */
 	if (!state->crtc)
 		return 0;
@@ -616,6 +626,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
 	    state->crtc_w != 128 && state->crtc_w != 256)
 		return -EINVAL;
 
+	err = tegra_plane_state_add(tegra, state);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -680,9 +694,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
 	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
-	/* apply changes */
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static void tegra_cursor_atomic_disable(struct drm_plane *plane,
@@ -700,9 +711,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
 	value &= ~CURSOR_ENABLE;
 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-	tegra_dc_cursor_commit(dc);
-	tegra_dc_commit(dc);
 }
 
 static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
@@ -734,6 +742,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
 	if (!plane)
 		return ERR_PTR(-ENOMEM);
 
+	/*
+	 * We'll treat the cursor as an overlay plane with index 6 here so
+	 * that the update and activation request bits in DC_CMD_STATE_CONTROL
+	 * match up.
+	 */
+	plane->index = 6;
+
 	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
 	formats = tegra_cursor_plane_formats;
 
@@ -1029,7 +1044,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	}
 
 	drm_crtc_vblank_off(crtc);
-	tegra_dc_commit(dc);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1202,10 +1216,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
 
 static void tegra_crtc_commit(struct drm_crtc *crtc)
 {
-	struct tegra_dc *dc = to_tegra_dc(crtc);
-
 	drm_crtc_vblank_on(crtc);
-	tegra_dc_commit(dc);
 }
 
 static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
@@ -1230,6 +1241,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
 {
+	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL);
+	tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL);
 }
 
 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
  2015-01-20 11:10     ` Daniel Vetter
@ 2015-01-23  9:32         ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:32 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 2257 bytes --]

On Tue, Jan 20, 2015 at 12:10:11PM +0100, Daniel Vetter wrote:
> On Tue, Jan 20, 2015 at 11:48:24AM +0100, Thierry Reding wrote:
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > 
> > This function is useful to obtain the state of the CRTC that a plane is
> > being attached to.
> > 
> > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> >  include/drm/drm_atomic.h | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> > 
> > diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> > index 51168a8b723a..ddca107e5cce 100644
> > --- a/include/drm/drm_atomic.h
> > +++ b/include/drm/drm_atomic.h
> > @@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
> >  int __must_check drm_atomic_commit(struct drm_atomic_state *state);
> >  int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
> >  
> > +/**
> > + * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
> > + *     plane is being attached to
> > + * @state: atomic state of plane
> > + *
> > + * Getting the atomic state of the CRTC that a given plane being attached to
> > + * is non-trivial. We need to look at the global atomic state that the plane
> > + * state is part of and extract the CRTC state using the CRTC's index. Using
> > + * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
> > + * locks, too.
> > + *
> > + * RETURNS:
> > + * The atomic state of the CRTC that the plane is being attached to, or an
> > + * ERR_PTR()-encoded negative error code on failure.
> > + */
> > +static inline struct drm_crtc_state *
> > +drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
> > +{
> > +	return drm_atomic_get_crtc_state(state->state, state->crtc);
> 
> Hm, imo open-coding this is totally ok. Actually better since state->crtc
> might be NULL, and hiding that isn't a good idea.
> 
> If you have a atomic_plane_disable hook then ->crtc is guaranteed to be
> non-NULL in atomic_plane_update, but being explicit here is imo a feature.
> So nacked from my side.

I've dropped this patch and open-coded this call in patch 34/36.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state()
@ 2015-01-23  9:32         ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:32 UTC (permalink / raw)
  To: dri-devel, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2199 bytes --]

On Tue, Jan 20, 2015 at 12:10:11PM +0100, Daniel Vetter wrote:
> On Tue, Jan 20, 2015 at 11:48:24AM +0100, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > This function is useful to obtain the state of the CRTC that a plane is
> > being attached to.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  include/drm/drm_atomic.h | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> > 
> > diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> > index 51168a8b723a..ddca107e5cce 100644
> > --- a/include/drm/drm_atomic.h
> > +++ b/include/drm/drm_atomic.h
> > @@ -75,4 +75,25 @@ int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
> >  int __must_check drm_atomic_commit(struct drm_atomic_state *state);
> >  int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
> >  
> > +/**
> > + * drm_atomic_plane_get_crtc_state - obtain atomic state of the CRTC that a
> > + *     plane is being attached to
> > + * @state: atomic state of plane
> > + *
> > + * Getting the atomic state of the CRTC that a given plane being attached to
> > + * is non-trivial. We need to look at the global atomic state that the plane
> > + * state is part of and extract the CRTC state using the CRTC's index. Using
> > + * the drm_atomic_get_crtc_state() helper we make sure to acquire the proper
> > + * locks, too.
> > + *
> > + * RETURNS:
> > + * The atomic state of the CRTC that the plane is being attached to, or an
> > + * ERR_PTR()-encoded negative error code on failure.
> > + */
> > +static inline struct drm_crtc_state *
> > +drm_atomic_plane_get_crtc_state(struct drm_plane_state *state)
> > +{
> > +	return drm_atomic_get_crtc_state(state->state, state->crtc);
> 
> Hm, imo open-coding this is totally ok. Actually better since state->crtc
> might be NULL, and hiding that isn't a good idea.
> 
> If you have a atomic_plane_disable hook then ->crtc is guaranteed to be
> non-NULL in atomic_plane_update, but being explicit here is imo a feature.
> So nacked from my side.

I've dropped this patch and open-coded this call in patch 34/36.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-22 20:25               ` Stephen Boyd
@ 2015-01-23  9:34                 ` Thierry Reding
  -1 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:34 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Mike Turquette, Tomeu Vizoso, linux-kernel, dri-devel,
	linux-tegra, Russell King


[-- Attachment #1.1: Type: text/plain, Size: 2763 bytes --]

On Thu, Jan 22, 2015 at 12:25:46PM -0800, Stephen Boyd wrote:
> On 01/22, Thierry Reding wrote:
> > On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> > > On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > > > From: Thierry Reding <treding@nvidia.com>
> > > >
> > > > This new function is similar to clk_set_parent(), except that it doesn't
> > > > actually change the parent. It merely checks that the given parent clock
> > > > can be a parent for the given clock.
> > > >
> > > > A situation where this is useful is to check that a particular setup is
> > > > valid before switching to it. One specific use-case for this is atomic
> > > > modesetting in the DRM framework where setting a mode is divided into a
> > > > check phase where a given configuration is validated before applying
> > > > changes to the hardware.
> > > >
> > > > Cc: Russell King <linux@arm.linux.org.uk>
> > > > Cc: Mike Turquette <mturquette@linaro.org>
> > > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > ---
> > > 
> > > Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> > > 
> > > This will slightly conflict with Tomeu's  patches for per-user clock
> > > constraints. It would be best if we can take this through the clk tree
> > > to fix up any conflicts
> > 
> > I had hoped to take this through the drm tree to resolve the build-time.
> > Another possibility would be for me to include the clk tree (or a subset
> > thereof) in my pull-request. That way you can still fix things up in the
> > clock tree if there are any conflicts with other work. We could make
> > that work two ways: this patch gets applied to the clk tree and I pull
> > it, or I provide a stable branch that I base my pull request on and that
> > branch can be pulled into the clk tree.
> > 
> > Yet another alternative would be to split out the clk_has_parent()
> > change from the series and not use it for now. That way we're going to
> > miss this check, but we do that anyway currently and it will only be
> > temporary until v3.21.
> > 
> > Perhaps given where we are in the release cycle the latter would make
> > the most sense for now.
> 
> Ok well let's see what Mike wants to do given that he's doing all
> the patch applying right now. I'd think that we could put this
> one patch on a different branch that we can merge into clk-next
> and you can merge into the drm tree. At least that's the typical
> workflow that usually works for everyone.

I will be sending out a pull request today, so I'm not going to include
this patch or the dependent patch. I'll pick it up again after the merge
window and see if I need to rebase it on top of Tomeu's work.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
@ 2015-01-23  9:34                 ` Thierry Reding
  0 siblings, 0 replies; 120+ messages in thread
From: Thierry Reding @ 2015-01-23  9:34 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: dri-devel, linux-tegra, linux-kernel, Russell King,
	Mike Turquette, Tomeu Vizoso

[-- Attachment #1: Type: text/plain, Size: 2763 bytes --]

On Thu, Jan 22, 2015 at 12:25:46PM -0800, Stephen Boyd wrote:
> On 01/22, Thierry Reding wrote:
> > On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> > > On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > > > From: Thierry Reding <treding@nvidia.com>
> > > >
> > > > This new function is similar to clk_set_parent(), except that it doesn't
> > > > actually change the parent. It merely checks that the given parent clock
> > > > can be a parent for the given clock.
> > > >
> > > > A situation where this is useful is to check that a particular setup is
> > > > valid before switching to it. One specific use-case for this is atomic
> > > > modesetting in the DRM framework where setting a mode is divided into a
> > > > check phase where a given configuration is validated before applying
> > > > changes to the hardware.
> > > >
> > > > Cc: Russell King <linux@arm.linux.org.uk>
> > > > Cc: Mike Turquette <mturquette@linaro.org>
> > > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > ---
> > > 
> > > Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> > > 
> > > This will slightly conflict with Tomeu's  patches for per-user clock
> > > constraints. It would be best if we can take this through the clk tree
> > > to fix up any conflicts
> > 
> > I had hoped to take this through the drm tree to resolve the build-time.
> > Another possibility would be for me to include the clk tree (or a subset
> > thereof) in my pull-request. That way you can still fix things up in the
> > clock tree if there are any conflicts with other work. We could make
> > that work two ways: this patch gets applied to the clk tree and I pull
> > it, or I provide a stable branch that I base my pull request on and that
> > branch can be pulled into the clk tree.
> > 
> > Yet another alternative would be to split out the clk_has_parent()
> > change from the series and not use it for now. That way we're going to
> > miss this check, but we do that anyway currently and it will only be
> > temporary until v3.21.
> > 
> > Perhaps given where we are in the release cycle the latter would make
> > the most sense for now.
> 
> Ok well let's see what Mike wants to do given that he's doing all
> the patch applying right now. I'd think that we could put this
> one patch on a different branch that we can merge into clk-next
> and you can merge into the drm tree. At least that's the typical
> workflow that usually works for everyone.

I will be sending out a pull request today, so I'm not going to include
this patch or the dependent patch. I'll pick it up again after the merge
window and see if I need to rebase it on top of Tomeu's work.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2] clk: Introduce clk_has_parent()
  2015-01-23  9:34                 ` Thierry Reding
  (?)
@ 2015-01-25  1:02                 ` Mike Turquette
  -1 siblings, 0 replies; 120+ messages in thread
From: Mike Turquette @ 2015-01-25  1:02 UTC (permalink / raw)
  To: Thierry Reding, Stephen Boyd
  Cc: dri-devel, linux-tegra, linux-kernel, Russell King, Tomeu Vizoso

Quoting Thierry Reding (2015-01-23 01:34:10)
> On Thu, Jan 22, 2015 at 12:25:46PM -0800, Stephen Boyd wrote:
> > On 01/22, Thierry Reding wrote:
> > > On Wed, Jan 21, 2015 at 04:16:05PM -0800, Stephen Boyd wrote:
> > > > On 01/21/2015 08:13 AM, Thierry Reding wrote:
> > > > > From: Thierry Reding <treding@nvidia.com>
> > > > >
> > > > > This new function is similar to clk_set_parent(), except that it doesn't
> > > > > actually change the parent. It merely checks that the given parent clock
> > > > > can be a parent for the given clock.
> > > > >
> > > > > A situation where this is useful is to check that a particular setup is
> > > > > valid before switching to it. One specific use-case for this is atomic
> > > > > modesetting in the DRM framework where setting a mode is divided into a
> > > > > check phase where a given configuration is validated before applying
> > > > > changes to the hardware.
> > > > >
> > > > > Cc: Russell King <linux@arm.linux.org.uk>
> > > > > Cc: Mike Turquette <mturquette@linaro.org>
> > > > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > ---
> > > > 
> > > > Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
> > > > 
> > > > This will slightly conflict with Tomeu's  patches for per-user clock
> > > > constraints. It would be best if we can take this through the clk tree
> > > > to fix up any conflicts
> > > 
> > > I had hoped to take this through the drm tree to resolve the build-time.
> > > Another possibility would be for me to include the clk tree (or a subset
> > > thereof) in my pull-request. That way you can still fix things up in the
> > > clock tree if there are any conflicts with other work. We could make
> > > that work two ways: this patch gets applied to the clk tree and I pull
> > > it, or I provide a stable branch that I base my pull request on and that
> > > branch can be pulled into the clk tree.
> > > 
> > > Yet another alternative would be to split out the clk_has_parent()
> > > change from the series and not use it for now. That way we're going to
> > > miss this check, but we do that anyway currently and it will only be
> > > temporary until v3.21.
> > > 
> > > Perhaps given where we are in the release cycle the latter would make
> > > the most sense for now.
> > 
> > Ok well let's see what Mike wants to do given that he's doing all
> > the patch applying right now. I'd think that we could put this
> > one patch on a different branch that we can merge into clk-next
> > and you can merge into the drm tree. At least that's the typical
> > workflow that usually works for everyone.
> 
> I will be sending out a pull request today, so I'm not going to include
> this patch or the dependent patch. I'll pick it up again after the merge
> window and see if I need to rebase it on top of Tomeu's work.

I've created an immutable branch based on 3.19-rc1 with only this patch
on it and I've merged that branch into the clk tree. Immutable branch:

git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-has-parent

You can see it merged into the clk tree here:

https://git.kernel.org/cgit/linux/kernel/git/clk/linux.git/commit/?h=clk-next&id=57386798f7db7f09d69f3b44fc66570e6db91bba

Regards,
Mike

> 
> Thierry

^ permalink raw reply	[flat|nested] 120+ messages in thread

end of thread, other threads:[~2015-01-25  1:02 UTC | newest]

Thread overview: 120+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-20 10:48 [PATCH 00/36] drm/tegra: Atomic mode-setting conversion Thierry Reding
2015-01-20 10:48 ` Thierry Reding
2015-01-20 10:48 ` [PATCH 01/36] clk: Introduce clk_try_parent() Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 18:02   ` Mike Turquette
2015-01-20 18:16     ` Rob Clark
2015-01-20 18:16       ` Rob Clark
2015-01-21 15:12       ` Thierry Reding
2015-01-21 15:12         ` Thierry Reding
     [not found]   ` <1421750935-4023-2-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-20 19:21     ` Stephen Boyd
2015-01-20 19:21       ` Stephen Boyd
2015-01-21 15:05       ` Thierry Reding
2015-01-21 15:05         ` Thierry Reding
2015-01-21 16:13   ` [PATCH v2] clk: Introduce clk_has_parent() Thierry Reding
2015-01-21 16:13     ` Thierry Reding
     [not found]     ` <1421856780-32103-1-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-22  0:16       ` Stephen Boyd
2015-01-22  0:16         ` Stephen Boyd
     [not found]         ` <54C04145.1010906-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2015-01-22  7:37           ` Thierry Reding
2015-01-22  7:37             ` Thierry Reding
2015-01-22 20:25             ` Stephen Boyd
2015-01-22 20:25               ` Stephen Boyd
2015-01-23  9:34               ` Thierry Reding
2015-01-23  9:34                 ` Thierry Reding
2015-01-25  1:02                 ` Mike Turquette
2015-01-20 10:48 ` [PATCH 02/36] drm/plane: Make ->atomic_update() mandatory Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 11:10   ` Daniel Vetter
2015-01-20 11:10     ` Daniel Vetter
     [not found]   ` <1421750935-4023-3-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-20 13:51     ` Rob Clark
2015-01-20 13:51       ` Rob Clark
2015-01-23  9:18       ` Thierry Reding
2015-01-23  9:18         ` Thierry Reding
2015-01-23  9:26     ` [PATCH v2] " Thierry Reding
2015-01-23  9:26       ` Thierry Reding
2015-01-20 10:48 ` [PATCH 03/36] drm/plane: Add optional ->atomic_disable() callback Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 11:13   ` Daniel Vetter
2015-01-20 11:13     ` Daniel Vetter
2015-01-22 18:57   ` Gustavo Padovan
2015-01-22 18:57     ` Gustavo Padovan
2015-01-23  9:28   ` [PATCH v4] " Thierry Reding
2015-01-23  9:28     ` Thierry Reding
2015-01-20 10:48 ` [PATCH 04/36] drm/atomic: Add ->atomic_check() to encoder helpers Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 11:07   ` Daniel Vetter
2015-01-20 11:07     ` Daniel Vetter
2015-01-20 10:48 ` [PATCH 05/36] drm/atomic: Add drm_atomic_plane_get_crtc_state() Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 11:10   ` Daniel Vetter
2015-01-20 11:10     ` Daniel Vetter
     [not found]     ` <20150120111011.GX10113-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
2015-01-23  9:32       ` Thierry Reding
2015-01-23  9:32         ` Thierry Reding
2015-01-20 10:48 ` [PATCH 06/36] drm/tegra: Use tegra_commit_dc() in output drivers Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 07/36] drm/tegra: Stop CRTC at CRTC disable time Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 08/36] drm/tegra: dc: Wait for idle when disabled Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 09/36] drm/tegra: Move tegra_drm_mode_funcs to the core Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 10/36] drm/tegra: dc: No longer disable planes at CRTC disable Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 11/36] drm/tegra: Convert output midlayer to helpers Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 12/36] drm/tegra: output: Make ->setup_clock() optional Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 13/36] drm/tegra: Add tegra_dc_setup_clock() helper Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 14/36] drm/tegra: rgb: Demidlayer Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 15/36] drm/tegra: hdmi: Demidlayer Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 16/36] drm/tegra: dsi: Demidlayer Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 17/36] drm/tegra: sor: Demidlayer Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 18/36] drm/tegra: debugfs cleanup cannot fail Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 19/36] drm/tegra: Remove remnants of the output midlayer Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 20/36] drm/tegra: Output cleanup functions cannot fail Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 21/36] drm/tegra: dc: Do not needlessly deassert reset Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 22/36] drm/tegra: Atomic conversion, phase 1 Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 23/36] drm/tegra: Atomic conversion, phase 2 Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 24/36] drm/tegra: Atomic conversion, phase 3, step 1 Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 25/36] drm/tegra: dc: Store clock setup in atomic state Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 26/36] drm/tegra: rgb: Implement ->atomic_check() Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 27/36] drm/tegra: dsi: " Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 28/36] drm/tegra: hdmi: " Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 29/36] drm/tegra: sor: " Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 30/36] drm/tegra: dc: Use atomic clock state in modeset Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 31/36] drm/tegra: Atomic conversion, phase 3, step 2 Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 32/36] drm/tegra: Atomic conversion, phase 3, step 3 Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 33/36] drm/tegra: Remove unused ->mode_fixup() callbacks Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 34/36] drm/tegra: Track active planes in CRTC state Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 11:18   ` Daniel Vetter
2015-01-20 11:18     ` Daniel Vetter
2015-01-20 11:43     ` Thierry Reding
2015-01-20 11:43       ` Thierry Reding
     [not found]   ` <1421750935-4023-35-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-23  9:31     ` [PATCH v2] " Thierry Reding
2015-01-23  9:31       ` Thierry Reding
2015-01-20 10:48 ` [PATCH 35/36] drm/tegra: Track tiling and format in plane state Thierry Reding
2015-01-20 10:48   ` Thierry Reding
2015-01-20 10:48 ` [PATCH 36/36] drm/tegra: dc: Unify enabling the display controller Thierry Reding
2015-01-20 10:48   ` Thierry Reding

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.