From 0281c4149021376123b4ccdb1548692a3f6e70bd Mon Sep 17 00:00:00 2001
From: Thierry Reding <treding@nvidia.com>
Date: Tue, 28 Nov 2017 11:20:40 +0100
Subject: drm/tegra: hub: Use private object for global state

Rather than subclass the global atomic state to store the hub display
clock and rate, create a private object and store this data in its
state.

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

(limited to 'drivers/gpu/drm/tegra/hub.c')

diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index 094324daa917..9a3f23d4780f 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -573,6 +573,89 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
 	return p;
 }
 
+static struct drm_private_state *
+tegra_display_hub_duplicate_state(struct drm_private_obj *obj)
+{
+	struct tegra_display_hub_state *state;
+
+	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+	return &state->base;
+}
+
+static void tegra_display_hub_destroy_state(struct drm_private_obj *obj,
+					    struct drm_private_state *state)
+{
+	struct tegra_display_hub_state *hub_state =
+		to_tegra_display_hub_state(state);
+
+	kfree(hub_state);
+}
+
+static const struct drm_private_state_funcs tegra_display_hub_state_funcs = {
+	.atomic_duplicate_state = tegra_display_hub_duplicate_state,
+	.atomic_destroy_state = tegra_display_hub_destroy_state,
+};
+
+static struct tegra_display_hub_state *
+tegra_display_hub_get_state(struct tegra_display_hub *hub,
+			    struct drm_atomic_state *state)
+{
+	struct drm_device *drm = dev_get_drvdata(hub->client.parent);
+	struct drm_private_state *priv;
+
+	WARN_ON(!drm_modeset_is_locked(&drm->mode_config.connection_mutex));
+
+	priv = drm_atomic_get_private_obj_state(state, &hub->base);
+	if (IS_ERR(priv))
+		return ERR_CAST(priv);
+
+	return to_tegra_display_hub_state(priv);
+}
+
+int tegra_display_hub_atomic_check(struct drm_device *drm,
+				   struct drm_atomic_state *state)
+{
+	struct tegra_drm *tegra = drm->dev_private;
+	struct tegra_display_hub_state *hub_state;
+	struct drm_crtc_state *old, *new;
+	struct drm_crtc *crtc;
+	unsigned int i;
+
+	if (!tegra->hub)
+		return 0;
+
+	hub_state = tegra_display_hub_get_state(tegra->hub, state);
+	if (IS_ERR(hub_state))
+		return PTR_ERR(hub_state);
+
+	/*
+	 * The display hub display clock needs to be fed by the display clock
+	 * with the highest frequency to ensure proper functioning of all the
+	 * displays.
+	 *
+	 * Note that this isn't used before Tegra186, but it doesn't hurt and
+	 * conditionalizing it would make the code less clean.
+	 */
+	for_each_oldnew_crtc_in_state(state, crtc, old, new, i) {
+		struct tegra_dc_state *dc = to_dc_state(new);
+
+		if (new->active) {
+			if (!hub_state->clk || dc->pclk > hub_state->rate) {
+				hub_state->dc = to_tegra_dc(dc->base.crtc);
+				hub_state->clk = hub_state->dc->clk;
+				hub_state->rate = dc->pclk;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static void tegra_display_hub_update(struct tegra_dc *dc)
 {
 	u32 value;
@@ -598,26 +681,28 @@ static void tegra_display_hub_update(struct tegra_dc *dc)
 void tegra_display_hub_atomic_commit(struct drm_device *drm,
 				     struct drm_atomic_state *state)
 {
-	struct tegra_atomic_state *s = to_tegra_atomic_state(state);
 	struct tegra_drm *tegra = drm->dev_private;
 	struct tegra_display_hub *hub = tegra->hub;
+	struct tegra_display_hub_state *hub_state;
 	struct device *dev = hub->client.dev;
 	int err;
 
-	if (s->clk_disp) {
-		err = clk_set_rate(s->clk_disp, s->rate);
+	hub_state = tegra_display_hub_get_state(hub, state);
+
+	if (hub_state->clk) {
+		err = clk_set_rate(hub_state->clk, hub_state->rate);
 		if (err < 0)
 			dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
-				s->clk_disp, s->rate);
+				hub_state->clk, hub_state->rate);
 
-		err = clk_set_parent(hub->clk_disp, s->clk_disp);
+		err = clk_set_parent(hub->clk_disp, hub_state->clk);
 		if (err < 0)
 			dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
-				hub->clk_disp, s->clk_disp, err);
+				hub->clk_disp, hub_state->clk, err);
 	}
 
-	if (s->dc)
-		tegra_display_hub_update(s->dc);
+	if (hub_state->dc)
+		tegra_display_hub_update(hub_state->dc);
 }
 
 static int tegra_display_hub_init(struct host1x_client *client)
@@ -625,6 +710,14 @@ static int tegra_display_hub_init(struct host1x_client *client)
 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
 	struct drm_device *drm = dev_get_drvdata(client->parent);
 	struct tegra_drm *tegra = drm->dev_private;
+	struct tegra_display_hub_state *state;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	drm_atomic_private_obj_init(&hub->base, &state->base,
+				    &tegra_display_hub_state_funcs);
 
 	tegra->hub = hub;
 
@@ -636,6 +729,7 @@ static int tegra_display_hub_exit(struct host1x_client *client)
 	struct drm_device *drm = dev_get_drvdata(client->parent);
 	struct tegra_drm *tegra = drm->dev_private;
 
+	drm_atomic_private_obj_fini(&tegra->hub->base);
 	tegra->hub = NULL;
 
 	return 0;
-- 
cgit