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