diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_drv.c')
| -rw-r--r-- | drivers/gpu/drm/omapdrm/omap_drv.c | 196 | 
1 files changed, 192 insertions, 4 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index c05d3975cb31..2720a58ccd90 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -117,6 +117,102 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state)  	dispc_runtime_put(priv->dispc);  } +static int drm_atomic_state_normalized_zpos_cmp(const void *a, const void *b) +{ +	const struct drm_plane_state *sa = *(struct drm_plane_state **)a; +	const struct drm_plane_state *sb = *(struct drm_plane_state **)b; + +	if (sa->normalized_zpos != sb->normalized_zpos) +		return sa->normalized_zpos - sb->normalized_zpos; +	else +		return sa->plane->base.id - sb->plane->base.id; +} + +/* + * This replaces the drm_atomic_normalize_zpos to handle the dual overlay case. + * + * Since both halves need to be 'appear' side by side the zpos is + * recalculated when dealing with dual overlay cases so that the other + * planes zpos is consistent. + */ +static int omap_atomic_update_normalize_zpos(struct drm_device *dev, +					     struct drm_atomic_state *state) +{ +	struct drm_crtc *crtc; +	struct drm_crtc_state *old_state, *new_state; +	struct drm_plane *plane; +	int c, i, n, inc; +	int total_planes = dev->mode_config.num_total_plane; +	struct drm_plane_state **states; +	int ret = 0; + +	states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL); +	if (!states) +		return -ENOMEM; + +	for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, c) { +		if (old_state->plane_mask == new_state->plane_mask && +		    !new_state->zpos_changed) +			continue; + +		/* Reset plane increment and index value for every crtc */ +		n = 0; + +		/* +		 * Normalization process might create new states for planes +		 * which normalized_zpos has to be recalculated. +		 */ +		drm_for_each_plane_mask(plane, dev, new_state->plane_mask) { +			struct drm_plane_state *plane_state = +				drm_atomic_get_plane_state(new_state->state, +							   plane); +			if (IS_ERR(plane_state)) { +				ret = PTR_ERR(plane_state); +				goto done; +			} +			states[n++] = plane_state; +		} + +		sort(states, n, sizeof(*states), +		     drm_atomic_state_normalized_zpos_cmp, NULL); + +		for (i = 0, inc = 0; i < n; i++) { +			plane = states[i]->plane; + +			states[i]->normalized_zpos = i + inc; +			DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n", +					 plane->base.id, plane->name, +					 states[i]->normalized_zpos); + +			if (is_omap_plane_dual_overlay(states[i])) +				inc++; +		} +		new_state->zpos_changed = true; +	} + +done: +	kfree(states); +	return ret; +} + +static int omap_atomic_check(struct drm_device *dev, +			     struct drm_atomic_state *state) +{ +	int ret; + +	ret = drm_atomic_helper_check(dev, state); +	if (ret) +		return ret; + +	if (dev->mode_config.normalize_zpos) { +		ret = omap_atomic_update_normalize_zpos(dev, state); +		if (ret) +			return ret; +	} + +	return 0; +} +  static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = {  	.atomic_commit_tail = omap_atomic_commit_tail,  }; @@ -124,10 +220,86 @@ static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs =  static const struct drm_mode_config_funcs omap_mode_config_funcs = {  	.fb_create = omap_framebuffer_create,  	.output_poll_changed = drm_fb_helper_output_poll_changed, -	.atomic_check = drm_atomic_helper_check, +	.atomic_check = omap_atomic_check,  	.atomic_commit = drm_atomic_helper_commit,  }; +/* Global/shared object state funcs */ + +/* + * This is a helper that returns the private state currently in operation. + * Note that this would return the "old_state" if called in the atomic check + * path, and the "new_state" after the atomic swap has been done. + */ +struct omap_global_state * +omap_get_existing_global_state(struct omap_drm_private *priv) +{ +	return to_omap_global_state(priv->glob_obj.state); +} + +/* + * This acquires the modeset lock set aside for global state, creates + * a new duplicated private object state. + */ +struct omap_global_state *__must_check +omap_get_global_state(struct drm_atomic_state *s) +{ +	struct omap_drm_private *priv = s->dev->dev_private; +	struct drm_private_state *priv_state; + +	priv_state = drm_atomic_get_private_obj_state(s, &priv->glob_obj); +	if (IS_ERR(priv_state)) +		return ERR_CAST(priv_state); + +	return to_omap_global_state(priv_state); +} + +static struct drm_private_state * +omap_global_duplicate_state(struct drm_private_obj *obj) +{ +	struct omap_global_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 omap_global_destroy_state(struct drm_private_obj *obj, +				      struct drm_private_state *state) +{ +	struct omap_global_state *omap_state = to_omap_global_state(state); + +	kfree(omap_state); +} + +static const struct drm_private_state_funcs omap_global_state_funcs = { +	.atomic_duplicate_state = omap_global_duplicate_state, +	.atomic_destroy_state = omap_global_destroy_state, +}; + +static int omap_global_obj_init(struct drm_device *dev) +{ +	struct omap_drm_private *priv = dev->dev_private; +	struct omap_global_state *state; + +	state = kzalloc(sizeof(*state), GFP_KERNEL); +	if (!state) +		return -ENOMEM; + +	drm_atomic_private_obj_init(dev, &priv->glob_obj, &state->base, +				    &omap_global_state_funcs); +	return 0; +} + +static void omap_global_obj_fini(struct omap_drm_private *priv) +{ +	drm_atomic_private_obj_fini(&priv->glob_obj); +} +  static void omap_disconnect_pipelines(struct drm_device *ddev)  {  	struct omap_drm_private *priv = ddev->dev_private; @@ -231,8 +403,6 @@ static int omap_modeset_init(struct drm_device *dev)  	if (!omapdss_stack_is_ready())  		return -EPROBE_DEFER; -	drm_mode_config_init(dev); -  	ret = omap_modeset_init_properties(dev);  	if (ret < 0)  		return ret; @@ -583,10 +753,20 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)  	omap_gem_init(ddev); +	drm_mode_config_init(ddev); + +	ret = omap_global_obj_init(ddev); +	if (ret) +		goto err_gem_deinit; + +	ret = omap_hwoverlays_init(priv); +	if (ret) +		goto err_free_priv_obj; +  	ret = omap_modeset_init(ddev);  	if (ret) {  		dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret); -		goto err_gem_deinit; +		goto err_free_overlays;  	}  	/* Initialize vblank handling, start with all CRTCs disabled. */ @@ -618,7 +798,12 @@ err_cleanup_helpers:  	omap_fbdev_fini(ddev);  err_cleanup_modeset:  	omap_modeset_fini(ddev); +err_free_overlays: +	omap_hwoverlays_destroy(priv); +err_free_priv_obj: +	omap_global_obj_fini(priv);  err_gem_deinit: +	drm_mode_config_cleanup(ddev);  	omap_gem_deinit(ddev);  	destroy_workqueue(priv->wq);  	omap_disconnect_pipelines(ddev); @@ -642,6 +827,9 @@ static void omapdrm_cleanup(struct omap_drm_private *priv)  	drm_atomic_helper_shutdown(ddev);  	omap_modeset_fini(ddev); +	omap_hwoverlays_destroy(priv); +	omap_global_obj_fini(priv); +	drm_mode_config_cleanup(ddev);  	omap_gem_deinit(ddev);  	destroy_workqueue(priv->wq);  |