diff options
author | Thierry Reding <treding@nvidia.com> | 2017-11-28 13:20:40 +0300 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2018-03-17 02:03:36 +0300 |
commit | 0281c4149021376123b4ccdb1548692a3f6e70bd (patch) | |
tree | e2d1031c16f495b117a96f2f6035736561455b8e /drivers/gpu/drm/tegra/hub.c | |
parent | 4ae4b5c0dbaa499f2fd9215caac6e474c8dd477f (diff) | |
download | linux-0281c4149021376123b4ccdb1548692a3f6e70bd.tar.xz |
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>
Diffstat (limited to 'drivers/gpu/drm/tegra/hub.c')
-rw-r--r-- | drivers/gpu/drm/tegra/hub.c | 110 |
1 files changed, 102 insertions, 8 deletions
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; |