drm/tegra: dc: Store clock setup in atomic state

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>
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 4fb9048..b905a4e 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -41,6 +41,22 @@
 	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;
@@ -1004,13 +1020,48 @@
 	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)
@@ -1148,6 +1199,20 @@
 	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);
+
+	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;