|
@@ -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;
|