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