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