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 uint32_t nformats; 39 uint32_t formats[32]; 40 }; 41 42 struct omap_plane_state { 43 struct drm_plane_state base; 44 45 unsigned int zorder; 46 }; 47 48 static inline struct omap_plane_state * 49 to_omap_plane_state(struct drm_plane_state *state) 50 { 51 return container_of(state, struct omap_plane_state, base); 52 } 53 54 static int omap_plane_prepare_fb(struct drm_plane *plane, 55 struct drm_plane_state *new_state) 56 { 57 if (!new_state->fb) 58 return 0; 59 60 return omap_framebuffer_pin(new_state->fb); 61 } 62 63 static void omap_plane_cleanup_fb(struct drm_plane *plane, 64 struct drm_plane_state *old_state) 65 { 66 if (old_state->fb) 67 omap_framebuffer_unpin(old_state->fb); 68 } 69 70 static void omap_plane_atomic_update(struct drm_plane *plane, 71 struct drm_plane_state *old_state) 72 { 73 struct omap_drm_private *priv = plane->dev->dev_private; 74 struct omap_plane *omap_plane = to_omap_plane(plane); 75 struct drm_plane_state *state = plane->state; 76 struct omap_plane_state *omap_state = to_omap_plane_state(state); 77 struct omap_overlay_info info; 78 struct omap_drm_window win; 79 int ret; 80 81 DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); 82 83 memset(&info, 0, sizeof(info)); 84 info.rotation_type = OMAP_DSS_ROT_DMA; 85 info.rotation = OMAP_DSS_ROT_0; 86 info.global_alpha = 0xff; 87 info.mirror = 0; 88 info.zorder = omap_state->zorder; 89 90 memset(&win, 0, sizeof(win)); 91 win.rotation = state->rotation; 92 win.crtc_x = state->crtc_x; 93 win.crtc_y = state->crtc_y; 94 win.crtc_w = state->crtc_w; 95 win.crtc_h = state->crtc_h; 96 97 /* 98 * src values are in Q16 fixed point, convert to integer. 99 * omap_framebuffer_update_scanout() takes adjusted src. 100 */ 101 win.src_x = state->src_x >> 16; 102 win.src_y = state->src_y >> 16; 103 104 if (drm_rotation_90_or_270(state->rotation)) { 105 win.src_w = state->src_h >> 16; 106 win.src_h = state->src_w >> 16; 107 } else { 108 win.src_w = state->src_w >> 16; 109 win.src_h = state->src_h >> 16; 110 } 111 112 /* update scanout: */ 113 omap_framebuffer_update_scanout(state->fb, &win, &info); 114 115 DBG("%dx%d -> %dx%d (%d)", info.width, info.height, 116 info.out_width, info.out_height, 117 info.screen_width); 118 DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, 119 &info.paddr, &info.p_uv_addr); 120 121 priv->dispc_ops->ovl_set_channel_out(omap_plane->id, 122 omap_crtc_channel(state->crtc)); 123 124 /* and finally, update omapdss: */ 125 ret = priv->dispc_ops->ovl_setup(omap_plane->id, &info, 126 omap_crtc_timings(state->crtc), false); 127 if (ret) { 128 dev_err(plane->dev->dev, "Failed to setup plane %s\n", 129 omap_plane->name); 130 priv->dispc_ops->ovl_enable(omap_plane->id, false); 131 return; 132 } 133 134 priv->dispc_ops->ovl_enable(omap_plane->id, true); 135 } 136 137 static void omap_plane_atomic_disable(struct drm_plane *plane, 138 struct drm_plane_state *old_state) 139 { 140 struct omap_drm_private *priv = plane->dev->dev_private; 141 struct omap_plane_state *omap_state = to_omap_plane_state(plane->state); 142 struct omap_plane *omap_plane = to_omap_plane(plane); 143 144 plane->state->rotation = DRM_ROTATE_0; 145 omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY 146 ? 0 : omap_plane->id; 147 148 priv->dispc_ops->ovl_enable(omap_plane->id, false); 149 } 150 151 static int omap_plane_atomic_check(struct drm_plane *plane, 152 struct drm_plane_state *state) 153 { 154 struct drm_crtc_state *crtc_state; 155 156 if (!state->fb) 157 return 0; 158 159 /* crtc should only be NULL when disabling (i.e., !state->fb) */ 160 if (WARN_ON(!state->crtc)) 161 return 0; 162 163 crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); 164 /* we should have a crtc state if the plane is attached to a crtc */ 165 if (WARN_ON(!crtc_state)) 166 return 0; 167 168 if (!crtc_state->enable) 169 return 0; 170 171 if (state->crtc_x < 0 || state->crtc_y < 0) 172 return -EINVAL; 173 174 if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) 175 return -EINVAL; 176 177 if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) 178 return -EINVAL; 179 180 if (state->rotation != DRM_ROTATE_0 && 181 !omap_framebuffer_supports_rotation(state->fb)) 182 return -EINVAL; 183 184 return 0; 185 } 186 187 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { 188 .prepare_fb = omap_plane_prepare_fb, 189 .cleanup_fb = omap_plane_cleanup_fb, 190 .atomic_check = omap_plane_atomic_check, 191 .atomic_update = omap_plane_atomic_update, 192 .atomic_disable = omap_plane_atomic_disable, 193 }; 194 195 static void omap_plane_destroy(struct drm_plane *plane) 196 { 197 struct omap_plane *omap_plane = to_omap_plane(plane); 198 199 DBG("%s", omap_plane->name); 200 201 drm_plane_cleanup(plane); 202 203 kfree(omap_plane); 204 } 205 206 /* helper to install properties which are common to planes and crtcs */ 207 void omap_plane_install_properties(struct drm_plane *plane, 208 struct drm_mode_object *obj) 209 { 210 struct drm_device *dev = plane->dev; 211 struct omap_drm_private *priv = dev->dev_private; 212 213 if (priv->has_dmm) { 214 if (!plane->rotation_property) 215 drm_plane_create_rotation_property(plane, 216 DRM_ROTATE_0, 217 DRM_ROTATE_0 | DRM_ROTATE_90 | 218 DRM_ROTATE_180 | DRM_ROTATE_270 | 219 DRM_REFLECT_X | DRM_REFLECT_Y); 220 221 /* Attach the rotation property also to the crtc object */ 222 if (plane->rotation_property && obj != &plane->base) 223 drm_object_attach_property(obj, plane->rotation_property, 224 DRM_ROTATE_0); 225 } 226 227 drm_object_attach_property(obj, priv->zorder_prop, 0); 228 } 229 230 static struct drm_plane_state * 231 omap_plane_atomic_duplicate_state(struct drm_plane *plane) 232 { 233 struct omap_plane_state *state; 234 struct omap_plane_state *copy; 235 236 if (WARN_ON(!plane->state)) 237 return NULL; 238 239 state = to_omap_plane_state(plane->state); 240 copy = kmemdup(state, sizeof(*state), GFP_KERNEL); 241 if (copy == NULL) 242 return NULL; 243 244 __drm_atomic_helper_plane_duplicate_state(plane, ©->base); 245 246 return ©->base; 247 } 248 249 static void omap_plane_atomic_destroy_state(struct drm_plane *plane, 250 struct drm_plane_state *state) 251 { 252 __drm_atomic_helper_plane_destroy_state(state); 253 kfree(to_omap_plane_state(state)); 254 } 255 256 static void omap_plane_reset(struct drm_plane *plane) 257 { 258 struct omap_plane *omap_plane = to_omap_plane(plane); 259 struct omap_plane_state *omap_state; 260 261 if (plane->state) { 262 omap_plane_atomic_destroy_state(plane, plane->state); 263 plane->state = NULL; 264 } 265 266 omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); 267 if (omap_state == NULL) 268 return; 269 270 /* 271 * Set defaults depending on whether we are a primary or overlay 272 * plane. 273 */ 274 omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY 275 ? 0 : omap_plane->id; 276 omap_state->base.rotation = DRM_ROTATE_0; 277 278 plane->state = &omap_state->base; 279 plane->state->plane = plane; 280 } 281 282 static int omap_plane_atomic_set_property(struct drm_plane *plane, 283 struct drm_plane_state *state, 284 struct drm_property *property, 285 uint64_t val) 286 { 287 struct omap_drm_private *priv = plane->dev->dev_private; 288 struct omap_plane_state *omap_state = to_omap_plane_state(state); 289 290 if (property == priv->zorder_prop) 291 omap_state->zorder = val; 292 else 293 return -EINVAL; 294 295 return 0; 296 } 297 298 static int omap_plane_atomic_get_property(struct drm_plane *plane, 299 const struct drm_plane_state *state, 300 struct drm_property *property, 301 uint64_t *val) 302 { 303 struct omap_drm_private *priv = plane->dev->dev_private; 304 const struct omap_plane_state *omap_state = 305 container_of(state, const struct omap_plane_state, base); 306 307 if (property == priv->zorder_prop) 308 *val = omap_state->zorder; 309 else 310 return -EINVAL; 311 312 return 0; 313 } 314 315 static const struct drm_plane_funcs omap_plane_funcs = { 316 .update_plane = drm_atomic_helper_update_plane, 317 .disable_plane = drm_atomic_helper_disable_plane, 318 .reset = omap_plane_reset, 319 .destroy = omap_plane_destroy, 320 .set_property = drm_atomic_helper_plane_set_property, 321 .atomic_duplicate_state = omap_plane_atomic_duplicate_state, 322 .atomic_destroy_state = omap_plane_atomic_destroy_state, 323 .atomic_set_property = omap_plane_atomic_set_property, 324 .atomic_get_property = omap_plane_atomic_get_property, 325 }; 326 327 static const char *plane_id_to_name[] = { 328 [OMAP_DSS_GFX] = "gfx", 329 [OMAP_DSS_VIDEO1] = "vid1", 330 [OMAP_DSS_VIDEO2] = "vid2", 331 [OMAP_DSS_VIDEO3] = "vid3", 332 }; 333 334 static const enum omap_plane_id plane_idx_to_id[] = { 335 OMAP_DSS_GFX, 336 OMAP_DSS_VIDEO1, 337 OMAP_DSS_VIDEO2, 338 OMAP_DSS_VIDEO3, 339 }; 340 341 /* initialize plane */ 342 struct drm_plane *omap_plane_init(struct drm_device *dev, 343 int idx, enum drm_plane_type type, 344 u32 possible_crtcs) 345 { 346 struct omap_drm_private *priv = dev->dev_private; 347 struct drm_plane *plane; 348 struct omap_plane *omap_plane; 349 enum omap_plane_id id; 350 int ret; 351 352 if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) 353 return ERR_PTR(-EINVAL); 354 355 id = plane_idx_to_id[idx]; 356 357 DBG("%s: type=%d", plane_id_to_name[id], type); 358 359 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 360 if (!omap_plane) 361 return ERR_PTR(-ENOMEM); 362 363 omap_plane->nformats = omap_framebuffer_get_formats( 364 omap_plane->formats, ARRAY_SIZE(omap_plane->formats), 365 priv->dispc_ops->ovl_get_color_modes(id)); 366 omap_plane->id = id; 367 omap_plane->name = plane_id_to_name[id]; 368 369 plane = &omap_plane->base; 370 371 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 372 &omap_plane_funcs, omap_plane->formats, 373 omap_plane->nformats, type, NULL); 374 if (ret < 0) 375 goto error; 376 377 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 378 379 omap_plane_install_properties(plane, &plane->base); 380 381 return plane; 382 383 error: 384 dev_err(dev->dev, "%s(): could not create plane: %s\n", 385 __func__, plane_id_to_name[id]); 386 387 kfree(omap_plane); 388 return NULL; 389 } 390