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 *new_plane_state) 103 { 104 struct drm_crtc_state *crtc_state; 105 106 if (!new_plane_state->fb) 107 return 0; 108 109 /* crtc should only be NULL when disabling (i.e., !new_plane_state->fb) */ 110 if (WARN_ON(!new_plane_state->crtc)) 111 return 0; 112 113 crtc_state = drm_atomic_get_existing_crtc_state(new_plane_state->state, 114 new_plane_state->crtc); 115 /* we should have a crtc state if the plane is attached to a crtc */ 116 if (WARN_ON(!crtc_state)) 117 return 0; 118 119 if (!crtc_state->enable) 120 return 0; 121 122 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) 123 return -EINVAL; 124 125 if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) 126 return -EINVAL; 127 128 if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) 129 return -EINVAL; 130 131 if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && 132 !omap_framebuffer_supports_rotation(new_plane_state->fb)) 133 return -EINVAL; 134 135 return 0; 136 } 137 138 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { 139 .prepare_fb = omap_plane_prepare_fb, 140 .cleanup_fb = omap_plane_cleanup_fb, 141 .atomic_check = omap_plane_atomic_check, 142 .atomic_update = omap_plane_atomic_update, 143 .atomic_disable = omap_plane_atomic_disable, 144 }; 145 146 static void omap_plane_destroy(struct drm_plane *plane) 147 { 148 struct omap_plane *omap_plane = to_omap_plane(plane); 149 150 DBG("%s", omap_plane->name); 151 152 drm_plane_cleanup(plane); 153 154 kfree(omap_plane); 155 } 156 157 /* helper to install properties which are common to planes and crtcs */ 158 void omap_plane_install_properties(struct drm_plane *plane, 159 struct drm_mode_object *obj) 160 { 161 struct drm_device *dev = plane->dev; 162 struct omap_drm_private *priv = dev->dev_private; 163 164 if (priv->has_dmm) { 165 if (!plane->rotation_property) 166 drm_plane_create_rotation_property(plane, 167 DRM_MODE_ROTATE_0, 168 DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | 169 DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 | 170 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); 171 172 /* Attach the rotation property also to the crtc object */ 173 if (plane->rotation_property && obj != &plane->base) 174 drm_object_attach_property(obj, plane->rotation_property, 175 DRM_MODE_ROTATE_0); 176 } 177 178 drm_object_attach_property(obj, priv->zorder_prop, 0); 179 } 180 181 static void omap_plane_reset(struct drm_plane *plane) 182 { 183 struct omap_plane *omap_plane = to_omap_plane(plane); 184 185 drm_atomic_helper_plane_reset(plane); 186 if (!plane->state) 187 return; 188 189 /* 190 * Set the zpos default depending on whether we are a primary or overlay 191 * plane. 192 */ 193 plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY 194 ? 0 : omap_plane->id; 195 plane->state->color_encoding = DRM_COLOR_YCBCR_BT601; 196 plane->state->color_range = DRM_COLOR_YCBCR_FULL_RANGE; 197 } 198 199 static int omap_plane_atomic_set_property(struct drm_plane *plane, 200 struct drm_plane_state *state, 201 struct drm_property *property, 202 u64 val) 203 { 204 struct omap_drm_private *priv = plane->dev->dev_private; 205 206 if (property == priv->zorder_prop) 207 state->zpos = val; 208 else 209 return -EINVAL; 210 211 return 0; 212 } 213 214 static int omap_plane_atomic_get_property(struct drm_plane *plane, 215 const struct drm_plane_state *state, 216 struct drm_property *property, 217 u64 *val) 218 { 219 struct omap_drm_private *priv = plane->dev->dev_private; 220 221 if (property == priv->zorder_prop) 222 *val = state->zpos; 223 else 224 return -EINVAL; 225 226 return 0; 227 } 228 229 static const struct drm_plane_funcs omap_plane_funcs = { 230 .update_plane = drm_atomic_helper_update_plane, 231 .disable_plane = drm_atomic_helper_disable_plane, 232 .reset = omap_plane_reset, 233 .destroy = omap_plane_destroy, 234 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 235 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 236 .atomic_set_property = omap_plane_atomic_set_property, 237 .atomic_get_property = omap_plane_atomic_get_property, 238 }; 239 240 static bool omap_plane_supports_yuv(struct drm_plane *plane) 241 { 242 struct omap_drm_private *priv = plane->dev->dev_private; 243 struct omap_plane *omap_plane = to_omap_plane(plane); 244 const u32 *formats = dispc_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 = dispc_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 = dispc_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