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