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 = dispc_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 dispc_ovl_enable(priv->dispc, omap_plane->id, false); 82 return; 83 } 84 85 dispc_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 dispc_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 = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 244 u32 i; 245 246 for (i = 0; formats[i]; i++) 247 if (formats[i] == DRM_FORMAT_YUYV || 248 formats[i] == DRM_FORMAT_UYVY || 249 formats[i] == DRM_FORMAT_NV12) 250 return true; 251 252 return false; 253 } 254 255 static const char *plane_id_to_name[] = { 256 [OMAP_DSS_GFX] = "gfx", 257 [OMAP_DSS_VIDEO1] = "vid1", 258 [OMAP_DSS_VIDEO2] = "vid2", 259 [OMAP_DSS_VIDEO3] = "vid3", 260 }; 261 262 static const enum omap_plane_id plane_idx_to_id[] = { 263 OMAP_DSS_GFX, 264 OMAP_DSS_VIDEO1, 265 OMAP_DSS_VIDEO2, 266 OMAP_DSS_VIDEO3, 267 }; 268 269 /* initialize plane */ 270 struct drm_plane *omap_plane_init(struct drm_device *dev, 271 int idx, enum drm_plane_type type, 272 u32 possible_crtcs) 273 { 274 struct omap_drm_private *priv = dev->dev_private; 275 unsigned int num_planes = dispc_get_num_ovls(priv->dispc); 276 struct drm_plane *plane; 277 struct omap_plane *omap_plane; 278 enum omap_plane_id id; 279 int ret; 280 u32 nformats; 281 const u32 *formats; 282 283 if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) 284 return ERR_PTR(-EINVAL); 285 286 id = plane_idx_to_id[idx]; 287 288 DBG("%s: type=%d", plane_id_to_name[id], type); 289 290 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 291 if (!omap_plane) 292 return ERR_PTR(-ENOMEM); 293 294 formats = dispc_ovl_get_color_modes(priv->dispc, id); 295 for (nformats = 0; formats[nformats]; ++nformats) 296 ; 297 omap_plane->id = id; 298 omap_plane->name = plane_id_to_name[id]; 299 300 plane = &omap_plane->base; 301 302 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 303 &omap_plane_funcs, formats, 304 nformats, NULL, type, NULL); 305 if (ret < 0) 306 goto error; 307 308 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 309 310 omap_plane_install_properties(plane, &plane->base); 311 drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1); 312 drm_plane_create_alpha_property(plane); 313 drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | 314 BIT(DRM_MODE_BLEND_COVERAGE)); 315 316 if (omap_plane_supports_yuv(plane)) 317 drm_plane_create_color_properties(plane, 318 BIT(DRM_COLOR_YCBCR_BT601) | 319 BIT(DRM_COLOR_YCBCR_BT709), 320 BIT(DRM_COLOR_YCBCR_FULL_RANGE) | 321 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), 322 DRM_COLOR_YCBCR_BT601, 323 DRM_COLOR_YCBCR_FULL_RANGE); 324 325 return plane; 326 327 error: 328 dev_err(dev->dev, "%s(): could not create plane: %s\n", 329 __func__, plane_id_to_name[id]); 330 331 kfree(omap_plane); 332 return NULL; 333 } 334