1 /* 2 * drivers/gpu/drm/omapdrm/omap_plane.c 3 * 4 * Copyright (C) 2011 Texas Instruments 5 * Author: Rob Clark <rob.clark@linaro.org> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <drm/drm_atomic.h> 21 #include <drm/drm_atomic_helper.h> 22 #include <drm/drm_plane_helper.h> 23 24 #include "omap_dmm_tiler.h" 25 #include "omap_drv.h" 26 27 /* 28 * plane funcs 29 */ 30 31 #define to_omap_plane(x) container_of(x, struct omap_plane, base) 32 33 struct omap_plane { 34 struct drm_plane base; 35 enum omap_plane_id id; 36 const char *name; 37 }; 38 39 static int omap_plane_prepare_fb(struct drm_plane *plane, 40 struct drm_plane_state *new_state) 41 { 42 if (!new_state->fb) 43 return 0; 44 45 return omap_framebuffer_pin(new_state->fb); 46 } 47 48 static void omap_plane_cleanup_fb(struct drm_plane *plane, 49 struct drm_plane_state *old_state) 50 { 51 if (old_state->fb) 52 omap_framebuffer_unpin(old_state->fb); 53 } 54 55 static void omap_plane_atomic_update(struct drm_plane *plane, 56 struct drm_plane_state *old_state) 57 { 58 struct omap_drm_private *priv = plane->dev->dev_private; 59 struct omap_plane *omap_plane = to_omap_plane(plane); 60 struct drm_plane_state *state = plane->state; 61 struct omap_overlay_info info; 62 int ret; 63 64 DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); 65 66 memset(&info, 0, sizeof(info)); 67 info.rotation_type = OMAP_DSS_ROT_NONE; 68 info.rotation = DRM_MODE_ROTATE_0; 69 info.global_alpha = 0xff; 70 info.zorder = state->zpos; 71 72 /* update scanout: */ 73 omap_framebuffer_update_scanout(state->fb, state, &info); 74 75 DBG("%dx%d -> %dx%d (%d)", info.width, info.height, 76 info.out_width, info.out_height, 77 info.screen_width); 78 DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, 79 &info.paddr, &info.p_uv_addr); 80 81 /* and finally, update omapdss: */ 82 ret = priv->dispc_ops->ovl_setup(omap_plane->id, &info, 83 omap_crtc_timings(state->crtc), false, 84 omap_crtc_channel(state->crtc)); 85 if (ret) { 86 dev_err(plane->dev->dev, "Failed to setup plane %s\n", 87 omap_plane->name); 88 priv->dispc_ops->ovl_enable(omap_plane->id, false); 89 return; 90 } 91 92 priv->dispc_ops->ovl_enable(omap_plane->id, true); 93 } 94 95 static void omap_plane_atomic_disable(struct drm_plane *plane, 96 struct drm_plane_state *old_state) 97 { 98 struct omap_drm_private *priv = plane->dev->dev_private; 99 struct omap_plane *omap_plane = to_omap_plane(plane); 100 101 plane->state->rotation = DRM_MODE_ROTATE_0; 102 plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY 103 ? 0 : omap_plane->id; 104 105 priv->dispc_ops->ovl_enable(omap_plane->id, false); 106 } 107 108 static int omap_plane_atomic_check(struct drm_plane *plane, 109 struct drm_plane_state *state) 110 { 111 struct drm_crtc_state *crtc_state; 112 113 if (!state->fb) 114 return 0; 115 116 /* crtc should only be NULL when disabling (i.e., !state->fb) */ 117 if (WARN_ON(!state->crtc)) 118 return 0; 119 120 crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); 121 /* we should have a crtc state if the plane is attached to a crtc */ 122 if (WARN_ON(!crtc_state)) 123 return 0; 124 125 if (!crtc_state->enable) 126 return 0; 127 128 if (state->crtc_x < 0 || state->crtc_y < 0) 129 return -EINVAL; 130 131 if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) 132 return -EINVAL; 133 134 if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) 135 return -EINVAL; 136 137 if (state->rotation != DRM_MODE_ROTATE_0 && 138 !omap_framebuffer_supports_rotation(state->fb)) 139 return -EINVAL; 140 141 return 0; 142 } 143 144 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { 145 .prepare_fb = omap_plane_prepare_fb, 146 .cleanup_fb = omap_plane_cleanup_fb, 147 .atomic_check = omap_plane_atomic_check, 148 .atomic_update = omap_plane_atomic_update, 149 .atomic_disable = omap_plane_atomic_disable, 150 }; 151 152 static void omap_plane_destroy(struct drm_plane *plane) 153 { 154 struct omap_plane *omap_plane = to_omap_plane(plane); 155 156 DBG("%s", omap_plane->name); 157 158 drm_plane_cleanup(plane); 159 160 kfree(omap_plane); 161 } 162 163 /* helper to install properties which are common to planes and crtcs */ 164 void omap_plane_install_properties(struct drm_plane *plane, 165 struct drm_mode_object *obj) 166 { 167 struct drm_device *dev = plane->dev; 168 struct omap_drm_private *priv = dev->dev_private; 169 170 if (priv->has_dmm) { 171 if (!plane->rotation_property) 172 drm_plane_create_rotation_property(plane, 173 DRM_MODE_ROTATE_0, 174 DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | 175 DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 | 176 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); 177 178 /* Attach the rotation property also to the crtc object */ 179 if (plane->rotation_property && obj != &plane->base) 180 drm_object_attach_property(obj, plane->rotation_property, 181 DRM_MODE_ROTATE_0); 182 } 183 184 drm_object_attach_property(obj, priv->zorder_prop, 0); 185 } 186 187 static void omap_plane_reset(struct drm_plane *plane) 188 { 189 struct omap_plane *omap_plane = to_omap_plane(plane); 190 191 drm_atomic_helper_plane_reset(plane); 192 if (!plane->state) 193 return; 194 195 /* 196 * Set the zpos default depending on whether we are a primary or overlay 197 * plane. 198 */ 199 plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY 200 ? 0 : omap_plane->id; 201 } 202 203 static int omap_plane_atomic_set_property(struct drm_plane *plane, 204 struct drm_plane_state *state, 205 struct drm_property *property, 206 uint64_t val) 207 { 208 struct omap_drm_private *priv = plane->dev->dev_private; 209 210 if (property == priv->zorder_prop) 211 state->zpos = val; 212 else 213 return -EINVAL; 214 215 return 0; 216 } 217 218 static int omap_plane_atomic_get_property(struct drm_plane *plane, 219 const struct drm_plane_state *state, 220 struct drm_property *property, 221 uint64_t *val) 222 { 223 struct omap_drm_private *priv = plane->dev->dev_private; 224 225 if (property == priv->zorder_prop) 226 *val = state->zpos; 227 else 228 return -EINVAL; 229 230 return 0; 231 } 232 233 static const struct drm_plane_funcs omap_plane_funcs = { 234 .update_plane = drm_atomic_helper_update_plane, 235 .disable_plane = drm_atomic_helper_disable_plane, 236 .reset = omap_plane_reset, 237 .destroy = omap_plane_destroy, 238 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 239 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 240 .atomic_set_property = omap_plane_atomic_set_property, 241 .atomic_get_property = omap_plane_atomic_get_property, 242 }; 243 244 static const char *plane_id_to_name[] = { 245 [OMAP_DSS_GFX] = "gfx", 246 [OMAP_DSS_VIDEO1] = "vid1", 247 [OMAP_DSS_VIDEO2] = "vid2", 248 [OMAP_DSS_VIDEO3] = "vid3", 249 }; 250 251 static const enum omap_plane_id plane_idx_to_id[] = { 252 OMAP_DSS_GFX, 253 OMAP_DSS_VIDEO1, 254 OMAP_DSS_VIDEO2, 255 OMAP_DSS_VIDEO3, 256 }; 257 258 /* initialize plane */ 259 struct drm_plane *omap_plane_init(struct drm_device *dev, 260 int idx, enum drm_plane_type type, 261 u32 possible_crtcs) 262 { 263 struct omap_drm_private *priv = dev->dev_private; 264 unsigned int num_planes = priv->dispc_ops->get_num_ovls(); 265 struct drm_plane *plane; 266 struct omap_plane *omap_plane; 267 enum omap_plane_id id; 268 int ret; 269 u32 nformats; 270 const u32 *formats; 271 272 if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) 273 return ERR_PTR(-EINVAL); 274 275 id = plane_idx_to_id[idx]; 276 277 DBG("%s: type=%d", plane_id_to_name[id], type); 278 279 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 280 if (!omap_plane) 281 return ERR_PTR(-ENOMEM); 282 283 formats = priv->dispc_ops->ovl_get_color_modes(id); 284 for (nformats = 0; formats[nformats]; ++nformats) 285 ; 286 omap_plane->id = id; 287 omap_plane->name = plane_id_to_name[id]; 288 289 plane = &omap_plane->base; 290 291 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 292 &omap_plane_funcs, formats, 293 nformats, NULL, type, NULL); 294 if (ret < 0) 295 goto error; 296 297 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 298 299 omap_plane_install_properties(plane, &plane->base); 300 drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1); 301 302 return plane; 303 304 error: 305 dev_err(dev->dev, "%s(): could not create plane: %s\n", 306 __func__, plane_id_to_name[id]); 307 308 kfree(omap_plane); 309 return NULL; 310 } 311