1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ 4 * Author: Benoit Parrot <bparrot@ti.com> 5 */ 6 7 #include <drm/drm_atomic.h> 8 #include <drm/drm_atomic_helper.h> 9 10 #include "omap_dmm_tiler.h" 11 #include "omap_drv.h" 12 13 /* 14 * overlay funcs 15 */ 16 static const char * const overlay_id_to_name[] = { 17 [OMAP_DSS_GFX] = "gfx", 18 [OMAP_DSS_VIDEO1] = "vid1", 19 [OMAP_DSS_VIDEO2] = "vid2", 20 [OMAP_DSS_VIDEO3] = "vid3", 21 }; 22 23 /* 24 * Find a free overlay with the required caps and supported fourcc 25 */ 26 static struct omap_hw_overlay * 27 omap_plane_find_free_overlay(struct drm_device *dev, struct drm_plane *hwoverlay_to_plane[], 28 u32 caps, u32 fourcc) 29 { 30 struct omap_drm_private *priv = dev->dev_private; 31 int i; 32 33 DBG("caps: %x fourcc: %x", caps, fourcc); 34 35 for (i = 0; i < priv->num_ovls; i++) { 36 struct omap_hw_overlay *cur = priv->overlays[i]; 37 38 DBG("%d: id: %d cur->caps: %x", 39 cur->idx, cur->id, cur->caps); 40 41 /* skip if already in-use */ 42 if (hwoverlay_to_plane[cur->idx]) 43 continue; 44 45 /* skip if doesn't support some required caps: */ 46 if (caps & ~cur->caps) 47 continue; 48 49 /* check supported format */ 50 if (!dispc_ovl_color_mode_supported(priv->dispc, 51 cur->id, fourcc)) 52 continue; 53 54 return cur; 55 } 56 57 DBG("no match"); 58 return NULL; 59 } 60 61 /* 62 * Assign a new overlay to a plane with the required caps and supported fourcc 63 * If a plane need a new overlay, the previous one should have been released 64 * with omap_overlay_release() 65 * This should be called from the plane atomic_check() in order to prepare the 66 * next global overlay_map to be enabled when atomic transaction is valid. 67 */ 68 int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, 69 u32 caps, u32 fourcc, struct omap_hw_overlay **overlay, 70 struct omap_hw_overlay **r_overlay) 71 { 72 /* Get the global state of the current atomic transaction */ 73 struct omap_global_state *state = omap_get_global_state(s); 74 struct drm_plane **overlay_map = state->hwoverlay_to_plane; 75 struct omap_hw_overlay *ovl, *r_ovl; 76 77 ovl = omap_plane_find_free_overlay(s->dev, overlay_map, caps, fourcc); 78 if (!ovl) 79 return -ENOMEM; 80 81 overlay_map[ovl->idx] = plane; 82 *overlay = ovl; 83 84 if (r_overlay) { 85 r_ovl = omap_plane_find_free_overlay(s->dev, overlay_map, 86 caps, fourcc); 87 if (!r_ovl) { 88 overlay_map[ovl->idx] = NULL; 89 *overlay = NULL; 90 return -ENOMEM; 91 } 92 93 overlay_map[r_ovl->idx] = plane; 94 *r_overlay = r_ovl; 95 } 96 97 DBG("%s: assign to plane %s caps %x", ovl->name, plane->name, caps); 98 99 if (r_overlay) { 100 DBG("%s: assign to right of plane %s caps %x", 101 r_ovl->name, plane->name, caps); 102 } 103 104 return 0; 105 } 106 107 /* 108 * Release an overlay from a plane if the plane gets not visible or the plane 109 * need a new overlay if overlay caps changes. 110 * This should be called from the plane atomic_check() in order to prepare the 111 * next global overlay_map to be enabled when atomic transaction is valid. 112 */ 113 void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay) 114 { 115 /* Get the global state of the current atomic transaction */ 116 struct omap_global_state *state = omap_get_global_state(s); 117 struct drm_plane **overlay_map = state->hwoverlay_to_plane; 118 119 if (!overlay) 120 return; 121 122 if (WARN_ON(!overlay_map[overlay->idx])) 123 return; 124 125 DBG("%s: release from plane %s", overlay->name, overlay_map[overlay->idx]->name); 126 127 overlay_map[overlay->idx] = NULL; 128 } 129 130 /* 131 * Update an overlay state that was attached to a plane before the current atomic state. 132 * This should be called from the plane atomic_update() or atomic_disable(), 133 * where an overlay association to a plane could have changed between the old and current 134 * atomic state. 135 */ 136 void omap_overlay_update_state(struct omap_drm_private *priv, 137 struct omap_hw_overlay *overlay) 138 { 139 struct omap_global_state *state = omap_get_existing_global_state(priv); 140 struct drm_plane **overlay_map = state->hwoverlay_to_plane; 141 142 /* Check if this overlay is not used anymore, then disable it */ 143 if (!overlay_map[overlay->idx]) { 144 DBG("%s: disabled", overlay->name); 145 146 /* disable the overlay */ 147 dispc_ovl_enable(priv->dispc, overlay->id, false); 148 } 149 } 150 151 static void omap_overlay_destroy(struct omap_hw_overlay *overlay) 152 { 153 kfree(overlay); 154 } 155 156 static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id, 157 enum omap_overlay_caps caps) 158 { 159 struct omap_hw_overlay *overlay; 160 161 overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); 162 if (!overlay) 163 return ERR_PTR(-ENOMEM); 164 165 overlay->name = overlay_id_to_name[overlay_id]; 166 overlay->id = overlay_id; 167 overlay->caps = caps; 168 169 return overlay; 170 } 171 172 int omap_hwoverlays_init(struct omap_drm_private *priv) 173 { 174 static const enum omap_plane_id hw_plane_ids[] = { 175 OMAP_DSS_GFX, OMAP_DSS_VIDEO1, 176 OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3, 177 }; 178 u32 num_overlays = dispc_get_num_ovls(priv->dispc); 179 enum omap_overlay_caps caps; 180 int i, ret; 181 182 for (i = 0; i < num_overlays; i++) { 183 struct omap_hw_overlay *overlay; 184 185 caps = dispc_ovl_get_caps(priv->dispc, hw_plane_ids[i]); 186 overlay = omap_overlay_init(hw_plane_ids[i], caps); 187 if (IS_ERR(overlay)) { 188 ret = PTR_ERR(overlay); 189 dev_err(priv->dev, "failed to construct overlay for %s (%d)\n", 190 overlay_id_to_name[i], ret); 191 omap_hwoverlays_destroy(priv); 192 return ret; 193 } 194 overlay->idx = priv->num_ovls; 195 priv->overlays[priv->num_ovls++] = overlay; 196 } 197 198 return 0; 199 } 200 201 void omap_hwoverlays_destroy(struct omap_drm_private *priv) 202 { 203 int i; 204 205 for (i = 0; i < priv->num_ovls; i++) { 206 omap_overlay_destroy(priv->overlays[i]); 207 priv->overlays[i] = NULL; 208 } 209 210 priv->num_ovls = 0; 211 } 212