1 /* 2 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 3 * Author: Rob Clark <rob@ti.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <linux/seq_file.h> 19 20 #include <drm/drm_crtc.h> 21 #include <drm/drm_crtc_helper.h> 22 #include <drm/drm_gem_framebuffer_helper.h> 23 24 #include "omap_dmm_tiler.h" 25 #include "omap_drv.h" 26 27 /* 28 * framebuffer funcs 29 */ 30 31 static const u32 formats[] = { 32 /* 16bpp [A]RGB: */ 33 DRM_FORMAT_RGB565, /* RGB16-565 */ 34 DRM_FORMAT_RGBX4444, /* RGB12x-4444 */ 35 DRM_FORMAT_XRGB4444, /* xRGB12-4444 */ 36 DRM_FORMAT_RGBA4444, /* RGBA12-4444 */ 37 DRM_FORMAT_ARGB4444, /* ARGB16-4444 */ 38 DRM_FORMAT_XRGB1555, /* xRGB15-1555 */ 39 DRM_FORMAT_ARGB1555, /* ARGB16-1555 */ 40 /* 24bpp RGB: */ 41 DRM_FORMAT_RGB888, /* RGB24-888 */ 42 /* 32bpp [A]RGB: */ 43 DRM_FORMAT_RGBX8888, /* RGBx24-8888 */ 44 DRM_FORMAT_XRGB8888, /* xRGB24-8888 */ 45 DRM_FORMAT_RGBA8888, /* RGBA32-8888 */ 46 DRM_FORMAT_ARGB8888, /* ARGB32-8888 */ 47 /* YUV: */ 48 DRM_FORMAT_NV12, 49 DRM_FORMAT_YUYV, 50 DRM_FORMAT_UYVY, 51 }; 52 53 /* per-plane info for the fb: */ 54 struct plane { 55 dma_addr_t dma_addr; 56 }; 57 58 #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) 59 60 struct omap_framebuffer { 61 struct drm_framebuffer base; 62 int pin_count; 63 const struct drm_format_info *format; 64 struct plane planes[2]; 65 /* lock for pinning (pin_count and planes.dma_addr) */ 66 struct mutex lock; 67 }; 68 69 static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { 70 .create_handle = drm_gem_fb_create_handle, 71 .destroy = drm_gem_fb_destroy, 72 }; 73 74 static u32 get_linear_addr(struct drm_framebuffer *fb, 75 const struct drm_format_info *format, int n, int x, int y) 76 { 77 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 78 struct plane *plane = &omap_fb->planes[n]; 79 u32 offset; 80 81 offset = fb->offsets[n] 82 + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub)) 83 + (y * fb->pitches[n] / (n == 0 ? 1 : format->vsub)); 84 85 return plane->dma_addr + offset; 86 } 87 88 bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) 89 { 90 return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED; 91 } 92 93 /* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */ 94 static u32 drm_rotation_to_tiler(unsigned int drm_rot) 95 { 96 u32 orient; 97 98 switch (drm_rot & DRM_MODE_ROTATE_MASK) { 99 default: 100 case DRM_MODE_ROTATE_0: 101 orient = 0; 102 break; 103 case DRM_MODE_ROTATE_90: 104 orient = MASK_XY_FLIP | MASK_X_INVERT; 105 break; 106 case DRM_MODE_ROTATE_180: 107 orient = MASK_X_INVERT | MASK_Y_INVERT; 108 break; 109 case DRM_MODE_ROTATE_270: 110 orient = MASK_XY_FLIP | MASK_Y_INVERT; 111 break; 112 } 113 114 if (drm_rot & DRM_MODE_REFLECT_X) 115 orient ^= MASK_X_INVERT; 116 117 if (drm_rot & DRM_MODE_REFLECT_Y) 118 orient ^= MASK_Y_INVERT; 119 120 return orient; 121 } 122 123 /* update ovl info for scanout, handles cases of multi-planar fb's, etc. 124 */ 125 void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, 126 struct drm_plane_state *state, struct omap_overlay_info *info) 127 { 128 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 129 const struct drm_format_info *format = omap_fb->format; 130 struct plane *plane = &omap_fb->planes[0]; 131 u32 x, y, orient = 0; 132 133 info->fourcc = fb->format->format; 134 135 info->pos_x = state->crtc_x; 136 info->pos_y = state->crtc_y; 137 info->out_width = state->crtc_w; 138 info->out_height = state->crtc_h; 139 info->width = state->src_w >> 16; 140 info->height = state->src_h >> 16; 141 142 /* DSS driver wants the w & h in rotated orientation */ 143 if (drm_rotation_90_or_270(state->rotation)) 144 swap(info->width, info->height); 145 146 x = state->src_x >> 16; 147 y = state->src_y >> 16; 148 149 if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED) { 150 u32 w = state->src_w >> 16; 151 u32 h = state->src_h >> 16; 152 153 orient = drm_rotation_to_tiler(state->rotation); 154 155 /* 156 * omap_gem_rotated_paddr() wants the x & y in tiler units. 157 * Usually tiler unit size is the same as the pixel size, except 158 * for YUV422 formats, for which the tiler unit size is 32 bits 159 * and pixel size is 16 bits. 160 */ 161 if (fb->format->format == DRM_FORMAT_UYVY || 162 fb->format->format == DRM_FORMAT_YUYV) { 163 x /= 2; 164 w /= 2; 165 } 166 167 /* adjust x,y offset for invert: */ 168 if (orient & MASK_Y_INVERT) 169 y += h - 1; 170 if (orient & MASK_X_INVERT) 171 x += w - 1; 172 173 /* Note: x and y are in TILER units, not pixels */ 174 omap_gem_rotated_dma_addr(fb->obj[0], orient, x, y, 175 &info->paddr); 176 info->rotation_type = OMAP_DSS_ROT_TILER; 177 info->rotation = state->rotation ?: DRM_MODE_ROTATE_0; 178 /* Note: stride in TILER units, not pixels */ 179 info->screen_width = omap_gem_tiled_stride(fb->obj[0], orient); 180 } else { 181 switch (state->rotation & DRM_MODE_ROTATE_MASK) { 182 case 0: 183 case DRM_MODE_ROTATE_0: 184 /* OK */ 185 break; 186 187 default: 188 dev_warn(fb->dev->dev, 189 "rotation '%d' ignored for non-tiled fb\n", 190 state->rotation); 191 break; 192 } 193 194 info->paddr = get_linear_addr(fb, format, 0, x, y); 195 info->rotation_type = OMAP_DSS_ROT_NONE; 196 info->rotation = DRM_MODE_ROTATE_0; 197 info->screen_width = fb->pitches[0]; 198 } 199 200 /* convert to pixels: */ 201 info->screen_width /= format->cpp[0]; 202 203 if (fb->format->format == DRM_FORMAT_NV12) { 204 plane = &omap_fb->planes[1]; 205 206 if (info->rotation_type == OMAP_DSS_ROT_TILER) { 207 WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED)); 208 omap_gem_rotated_dma_addr(fb->obj[1], orient, x/2, y/2, 209 &info->p_uv_addr); 210 } else { 211 info->p_uv_addr = get_linear_addr(fb, format, 1, x, y); 212 } 213 } else { 214 info->p_uv_addr = 0; 215 } 216 } 217 218 /* pin, prepare for scanout: */ 219 int omap_framebuffer_pin(struct drm_framebuffer *fb) 220 { 221 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 222 int ret, i, n = fb->format->num_planes; 223 224 mutex_lock(&omap_fb->lock); 225 226 if (omap_fb->pin_count > 0) { 227 omap_fb->pin_count++; 228 mutex_unlock(&omap_fb->lock); 229 return 0; 230 } 231 232 for (i = 0; i < n; i++) { 233 struct plane *plane = &omap_fb->planes[i]; 234 ret = omap_gem_pin(fb->obj[i], &plane->dma_addr); 235 if (ret) 236 goto fail; 237 omap_gem_dma_sync_buffer(fb->obj[i], DMA_TO_DEVICE); 238 } 239 240 omap_fb->pin_count++; 241 242 mutex_unlock(&omap_fb->lock); 243 244 return 0; 245 246 fail: 247 for (i--; i >= 0; i--) { 248 struct plane *plane = &omap_fb->planes[i]; 249 omap_gem_unpin(fb->obj[i]); 250 plane->dma_addr = 0; 251 } 252 253 mutex_unlock(&omap_fb->lock); 254 255 return ret; 256 } 257 258 /* unpin, no longer being scanned out: */ 259 void omap_framebuffer_unpin(struct drm_framebuffer *fb) 260 { 261 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 262 int i, n = fb->format->num_planes; 263 264 mutex_lock(&omap_fb->lock); 265 266 omap_fb->pin_count--; 267 268 if (omap_fb->pin_count > 0) { 269 mutex_unlock(&omap_fb->lock); 270 return; 271 } 272 273 for (i = 0; i < n; i++) { 274 struct plane *plane = &omap_fb->planes[i]; 275 omap_gem_unpin(fb->obj[i]); 276 plane->dma_addr = 0; 277 } 278 279 mutex_unlock(&omap_fb->lock); 280 } 281 282 #ifdef CONFIG_DEBUG_FS 283 void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) 284 { 285 int i, n = fb->format->num_planes; 286 287 seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, 288 (char *)&fb->format->format); 289 290 for (i = 0; i < n; i++) { 291 seq_printf(m, " %d: offset=%d pitch=%d, obj: ", 292 i, fb->offsets[n], fb->pitches[i]); 293 omap_gem_describe(fb->obj[i], m); 294 } 295 } 296 #endif 297 298 struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, 299 struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) 300 { 301 unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format); 302 struct drm_gem_object *bos[4]; 303 struct drm_framebuffer *fb; 304 int i; 305 306 for (i = 0; i < num_planes; i++) { 307 bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); 308 if (!bos[i]) { 309 fb = ERR_PTR(-ENOENT); 310 goto error; 311 } 312 } 313 314 fb = omap_framebuffer_init(dev, mode_cmd, bos); 315 if (IS_ERR(fb)) 316 goto error; 317 318 return fb; 319 320 error: 321 while (--i >= 0) 322 drm_gem_object_unreference_unlocked(bos[i]); 323 324 return fb; 325 } 326 327 struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, 328 const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) 329 { 330 const struct drm_format_info *format = NULL; 331 struct omap_framebuffer *omap_fb = NULL; 332 struct drm_framebuffer *fb = NULL; 333 unsigned int pitch = mode_cmd->pitches[0]; 334 int ret, i; 335 336 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", 337 dev, mode_cmd, mode_cmd->width, mode_cmd->height, 338 (char *)&mode_cmd->pixel_format); 339 340 format = drm_format_info(mode_cmd->pixel_format); 341 342 for (i = 0; i < ARRAY_SIZE(formats); i++) { 343 if (formats[i] == mode_cmd->pixel_format) 344 break; 345 } 346 347 if (!format || i == ARRAY_SIZE(formats)) { 348 dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", 349 (char *)&mode_cmd->pixel_format); 350 ret = -EINVAL; 351 goto fail; 352 } 353 354 omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); 355 if (!omap_fb) { 356 ret = -ENOMEM; 357 goto fail; 358 } 359 360 fb = &omap_fb->base; 361 omap_fb->format = format; 362 mutex_init(&omap_fb->lock); 363 364 /* 365 * The code below assumes that no format use more than two planes, and 366 * that the two planes of multiplane formats need the same number of 367 * bytes per pixel. 368 */ 369 if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { 370 dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n"); 371 ret = -EINVAL; 372 goto fail; 373 } 374 375 if (pitch % format->cpp[0]) { 376 dev_dbg(dev->dev, 377 "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", 378 pitch, format->cpp[0]); 379 ret = -EINVAL; 380 goto fail; 381 } 382 383 for (i = 0; i < format->num_planes; i++) { 384 struct plane *plane = &omap_fb->planes[i]; 385 unsigned int vsub = i == 0 ? 1 : format->vsub; 386 unsigned int size; 387 388 size = pitch * mode_cmd->height / vsub; 389 390 if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { 391 dev_dbg(dev->dev, 392 "provided buffer object is too small! %zu < %d\n", 393 bos[i]->size - mode_cmd->offsets[i], size); 394 ret = -EINVAL; 395 goto fail; 396 } 397 398 fb->obj[i] = bos[i]; 399 plane->dma_addr = 0; 400 } 401 402 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 403 404 ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); 405 if (ret) { 406 dev_err(dev->dev, "framebuffer init failed: %d\n", ret); 407 goto fail; 408 } 409 410 DBG("create: FB ID: %d (%p)", fb->base.id, fb); 411 412 return fb; 413 414 fail: 415 kfree(omap_fb); 416 417 return ERR_PTR(ret); 418 } 419