diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_overlay.c')
| -rw-r--r-- | drivers/gpu/drm/omapdrm/omap_overlay.c | 212 | 
1 files changed, 212 insertions, 0 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c new file mode 100644 index 000000000000..10730c9b2752 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_overlay.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/ + * Author: Benoit Parrot <bparrot@ti.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> + +#include "omap_dmm_tiler.h" +#include "omap_drv.h" + +/* + * overlay funcs + */ +static const char * const overlay_id_to_name[] = { +	[OMAP_DSS_GFX] = "gfx", +	[OMAP_DSS_VIDEO1] = "vid1", +	[OMAP_DSS_VIDEO2] = "vid2", +	[OMAP_DSS_VIDEO3] = "vid3", +}; + +/* + * Find a free overlay with the required caps and supported fourcc + */ +static struct omap_hw_overlay * +omap_plane_find_free_overlay(struct drm_device *dev, struct drm_plane *hwoverlay_to_plane[], +			     u32 caps, u32 fourcc) +{ +	struct omap_drm_private *priv = dev->dev_private; +	int i; + +	DBG("caps: %x fourcc: %x", caps, fourcc); + +	for (i = 0; i < priv->num_ovls; i++) { +		struct omap_hw_overlay *cur = priv->overlays[i]; + +		DBG("%d: id: %d cur->caps: %x", +		    cur->idx, cur->id, cur->caps); + +		/* skip if already in-use */ +		if (hwoverlay_to_plane[cur->idx]) +			continue; + +		/* skip if doesn't support some required caps: */ +		if (caps & ~cur->caps) +			continue; + +		/* check supported format */ +		if (!dispc_ovl_color_mode_supported(priv->dispc, +						    cur->id, fourcc)) +			continue; + +		return cur; +	} + +	DBG("no match"); +	return NULL; +} + +/* + * Assign a new overlay to a plane with the required caps and supported fourcc + * If a plane need a new overlay, the previous one should have been released + * with omap_overlay_release() + * This should be called from the plane atomic_check() in order to prepare the + * next global overlay_map to be enabled when atomic transaction is valid. + */ +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, +			u32 caps, u32 fourcc, struct omap_hw_overlay **overlay, +			struct omap_hw_overlay **r_overlay) +{ +	/* Get the global state of the current atomic transaction */ +	struct omap_global_state *state = omap_get_global_state(s); +	struct drm_plane **overlay_map = state->hwoverlay_to_plane; +	struct omap_hw_overlay *ovl, *r_ovl; + +	ovl = omap_plane_find_free_overlay(s->dev, overlay_map, caps, fourcc); +	if (!ovl) +		return -ENOMEM; + +	overlay_map[ovl->idx] = plane; +	*overlay = ovl; + +	if (r_overlay) { +		r_ovl = omap_plane_find_free_overlay(s->dev, overlay_map, +						     caps, fourcc); +		if (!r_ovl) { +			overlay_map[r_ovl->idx] = NULL; +			*overlay = NULL; +			return -ENOMEM; +		} + +		overlay_map[r_ovl->idx] = plane; +		*r_overlay = r_ovl; +	} + +	DBG("%s: assign to plane %s caps %x", ovl->name, plane->name, caps); + +	if (r_overlay) { +		DBG("%s: assign to right of plane %s caps %x", +		    r_ovl->name, plane->name, caps); +	} + +	return 0; +} + +/* + * Release an overlay from a plane if the plane gets not visible or the plane + * need a new overlay if overlay caps changes. + * This should be called from the plane atomic_check() in order to prepare the + * next global overlay_map to be enabled when atomic transaction is valid. + */ +void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay) +{ +	/* Get the global state of the current atomic transaction */ +	struct omap_global_state *state = omap_get_global_state(s); +	struct drm_plane **overlay_map = state->hwoverlay_to_plane; + +	if (!overlay) +		return; + +	if (WARN_ON(!overlay_map[overlay->idx])) +		return; + +	DBG("%s: release from plane %s", overlay->name, overlay_map[overlay->idx]->name); + +	overlay_map[overlay->idx] = NULL; +} + +/* + * Update an overlay state that was attached to a plane before the current atomic state. + * This should be called from the plane atomic_update() or atomic_disable(), + * where an overlay association to a plane could have changed between the old and current + * atomic state. + */ +void omap_overlay_update_state(struct omap_drm_private *priv, +			       struct omap_hw_overlay *overlay) +{ +	struct omap_global_state *state = omap_get_existing_global_state(priv); +	struct drm_plane **overlay_map = state->hwoverlay_to_plane; + +	/* Check if this overlay is not used anymore, then disable it */ +	if (!overlay_map[overlay->idx]) { +		DBG("%s: disabled", overlay->name); + +		/* disable the overlay */ +		dispc_ovl_enable(priv->dispc, overlay->id, false); +	} +} + +static void omap_overlay_destroy(struct omap_hw_overlay *overlay) +{ +	kfree(overlay); +} + +static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id, +						 enum omap_overlay_caps caps) +{ +	struct omap_hw_overlay *overlay; + +	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); +	if (!overlay) +		return ERR_PTR(-ENOMEM); + +	overlay->name = overlay_id_to_name[overlay_id]; +	overlay->id = overlay_id; +	overlay->caps = caps; + +	return overlay; +} + +int omap_hwoverlays_init(struct omap_drm_private *priv) +{ +	static const enum omap_plane_id hw_plane_ids[] = { +			OMAP_DSS_GFX, OMAP_DSS_VIDEO1, +			OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3, +	}; +	u32 num_overlays = dispc_get_num_ovls(priv->dispc); +	enum omap_overlay_caps caps; +	int i, ret; + +	for (i = 0; i < num_overlays; i++) { +		struct omap_hw_overlay *overlay; + +		caps = dispc_ovl_get_caps(priv->dispc, hw_plane_ids[i]); +		overlay = omap_overlay_init(hw_plane_ids[i], caps); +		if (IS_ERR(overlay)) { +			ret = PTR_ERR(overlay); +			dev_err(priv->dev, "failed to construct overlay for %s (%d)\n", +				overlay_id_to_name[i], ret); +			omap_hwoverlays_destroy(priv); +			return ret; +		} +		overlay->idx = priv->num_ovls; +		priv->overlays[priv->num_ovls++] = overlay; +	} + +	return 0; +} + +void omap_hwoverlays_destroy(struct omap_drm_private *priv) +{ +	int i; + +	for (i = 0; i < priv->num_ovls; i++) { +		omap_overlay_destroy(priv->overlays[i]); +		priv->overlays[i] = NULL; +	} + +	priv->num_ovls = 0; +}  | 
