| // 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_print.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_commit *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[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_commit *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_obj(*overlay); | |
| 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; | |
| } |