diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/hub.c')
-rw-r--r-- | drivers/gpu/drm/tegra/hub.c | 125 |
1 files changed, 116 insertions, 9 deletions
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index e10a47d57313..9a3f23d4780f 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -49,6 +49,17 @@ static const u32 tegra_shared_plane_formats[] = { DRM_FORMAT_YUV422, }; +static const u64 tegra_shared_plane_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), + DRM_FORMAT_MOD_INVALID +}; + static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, unsigned int offset) { @@ -527,6 +538,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, unsigned int possible_crtcs = 0x7; struct tegra_shared_plane *plane; unsigned int num_formats; + const u64 *modifiers; struct drm_plane *p; const u32 *formats; int err; @@ -545,10 +557,11 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_shared_plane_formats); formats = tegra_shared_plane_formats; + modifiers = tegra_shared_plane_modifiers; err = drm_universal_plane_init(drm, p, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, type, NULL); + num_formats, modifiers, type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -560,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; @@ -585,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) @@ -612,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; @@ -623,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; |