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