162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci * Author: Rob Clark <rob@ti.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <drm/drm_blend.h> 1062306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h> 1162306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 1262306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1362306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "omap_dmm_tiler.h" 1662306a36Sopenharmony_ci#include "omap_drv.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * framebuffer funcs 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic const u32 formats[] = { 2362306a36Sopenharmony_ci /* 16bpp [A]RGB: */ 2462306a36Sopenharmony_ci DRM_FORMAT_RGB565, /* RGB16-565 */ 2562306a36Sopenharmony_ci DRM_FORMAT_RGBX4444, /* RGB12x-4444 */ 2662306a36Sopenharmony_ci DRM_FORMAT_XRGB4444, /* xRGB12-4444 */ 2762306a36Sopenharmony_ci DRM_FORMAT_RGBA4444, /* RGBA12-4444 */ 2862306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, /* ARGB16-4444 */ 2962306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, /* xRGB15-1555 */ 3062306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, /* ARGB16-1555 */ 3162306a36Sopenharmony_ci /* 24bpp RGB: */ 3262306a36Sopenharmony_ci DRM_FORMAT_RGB888, /* RGB24-888 */ 3362306a36Sopenharmony_ci /* 32bpp [A]RGB: */ 3462306a36Sopenharmony_ci DRM_FORMAT_RGBX8888, /* RGBx24-8888 */ 3562306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, /* xRGB24-8888 */ 3662306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, /* RGBA32-8888 */ 3762306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, /* ARGB32-8888 */ 3862306a36Sopenharmony_ci /* YUV: */ 3962306a36Sopenharmony_ci DRM_FORMAT_NV12, 4062306a36Sopenharmony_ci DRM_FORMAT_YUYV, 4162306a36Sopenharmony_ci DRM_FORMAT_UYVY, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* per-plane info for the fb: */ 4562306a36Sopenharmony_cistruct plane { 4662306a36Sopenharmony_ci dma_addr_t dma_addr; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct omap_framebuffer { 5262306a36Sopenharmony_ci struct drm_framebuffer base; 5362306a36Sopenharmony_ci int pin_count; 5462306a36Sopenharmony_ci const struct drm_format_info *format; 5562306a36Sopenharmony_ci struct plane planes[2]; 5662306a36Sopenharmony_ci /* lock for pinning (pin_count and planes.dma_addr) */ 5762306a36Sopenharmony_ci struct mutex lock; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int omap_framebuffer_dirty(struct drm_framebuffer *fb, 6162306a36Sopenharmony_ci struct drm_file *file_priv, 6262306a36Sopenharmony_ci unsigned flags, unsigned color, 6362306a36Sopenharmony_ci struct drm_clip_rect *clips, 6462306a36Sopenharmony_ci unsigned num_clips) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct drm_crtc *crtc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci drm_modeset_lock_all(fb->dev); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci drm_for_each_crtc(crtc, fb->dev) 7162306a36Sopenharmony_ci omap_crtc_flush(crtc); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci drm_modeset_unlock_all(fb->dev); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs omap_framebuffer_funcs = { 7962306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 8062306a36Sopenharmony_ci .dirty = omap_framebuffer_dirty, 8162306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic u32 get_linear_addr(struct drm_framebuffer *fb, 8562306a36Sopenharmony_ci const struct drm_format_info *format, int n, int x, int y) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 8862306a36Sopenharmony_ci struct plane *plane = &omap_fb->planes[n]; 8962306a36Sopenharmony_ci u32 offset; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci offset = fb->offsets[n] 9262306a36Sopenharmony_ci + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub)) 9362306a36Sopenharmony_ci + (y * fb->pitches[n] / (n == 0 ? 1 : format->vsub)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return plane->dma_addr + offset; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cibool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */ 10462306a36Sopenharmony_cistatic u32 drm_rotation_to_tiler(unsigned int drm_rot) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 orient; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci switch (drm_rot & DRM_MODE_ROTATE_MASK) { 10962306a36Sopenharmony_ci default: 11062306a36Sopenharmony_ci case DRM_MODE_ROTATE_0: 11162306a36Sopenharmony_ci orient = 0; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case DRM_MODE_ROTATE_90: 11462306a36Sopenharmony_ci orient = MASK_XY_FLIP | MASK_X_INVERT; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case DRM_MODE_ROTATE_180: 11762306a36Sopenharmony_ci orient = MASK_X_INVERT | MASK_Y_INVERT; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case DRM_MODE_ROTATE_270: 12062306a36Sopenharmony_ci orient = MASK_XY_FLIP | MASK_Y_INVERT; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (drm_rot & DRM_MODE_REFLECT_X) 12562306a36Sopenharmony_ci orient ^= MASK_X_INVERT; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (drm_rot & DRM_MODE_REFLECT_Y) 12862306a36Sopenharmony_ci orient ^= MASK_Y_INVERT; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return orient; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* update ovl info for scanout, handles cases of multi-planar fb's, etc. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_civoid omap_framebuffer_update_scanout(struct drm_framebuffer *fb, 13662306a36Sopenharmony_ci struct drm_plane_state *state, 13762306a36Sopenharmony_ci struct omap_overlay_info *info, 13862306a36Sopenharmony_ci struct omap_overlay_info *r_info) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 14162306a36Sopenharmony_ci const struct drm_format_info *format = omap_fb->format; 14262306a36Sopenharmony_ci u32 x, y, orient = 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci info->fourcc = fb->format->format; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci info->pos_x = state->crtc_x; 14762306a36Sopenharmony_ci info->pos_y = state->crtc_y; 14862306a36Sopenharmony_ci info->out_width = state->crtc_w; 14962306a36Sopenharmony_ci info->out_height = state->crtc_h; 15062306a36Sopenharmony_ci info->width = state->src_w >> 16; 15162306a36Sopenharmony_ci info->height = state->src_h >> 16; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* DSS driver wants the w & h in rotated orientation */ 15462306a36Sopenharmony_ci if (drm_rotation_90_or_270(state->rotation)) 15562306a36Sopenharmony_ci swap(info->width, info->height); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci x = state->src_x >> 16; 15862306a36Sopenharmony_ci y = state->src_y >> 16; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK) { 16162306a36Sopenharmony_ci u32 w = state->src_w >> 16; 16262306a36Sopenharmony_ci u32 h = state->src_h >> 16; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci orient = drm_rotation_to_tiler(state->rotation); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * omap_gem_rotated_paddr() wants the x & y in tiler units. 16862306a36Sopenharmony_ci * Usually tiler unit size is the same as the pixel size, except 16962306a36Sopenharmony_ci * for YUV422 formats, for which the tiler unit size is 32 bits 17062306a36Sopenharmony_ci * and pixel size is 16 bits. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (fb->format->format == DRM_FORMAT_UYVY || 17362306a36Sopenharmony_ci fb->format->format == DRM_FORMAT_YUYV) { 17462306a36Sopenharmony_ci x /= 2; 17562306a36Sopenharmony_ci w /= 2; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* adjust x,y offset for invert: */ 17962306a36Sopenharmony_ci if (orient & MASK_Y_INVERT) 18062306a36Sopenharmony_ci y += h - 1; 18162306a36Sopenharmony_ci if (orient & MASK_X_INVERT) 18262306a36Sopenharmony_ci x += w - 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Note: x and y are in TILER units, not pixels */ 18562306a36Sopenharmony_ci omap_gem_rotated_dma_addr(fb->obj[0], orient, x, y, 18662306a36Sopenharmony_ci &info->paddr); 18762306a36Sopenharmony_ci info->rotation_type = OMAP_DSS_ROT_TILER; 18862306a36Sopenharmony_ci info->rotation = state->rotation ?: DRM_MODE_ROTATE_0; 18962306a36Sopenharmony_ci /* Note: stride in TILER units, not pixels */ 19062306a36Sopenharmony_ci info->screen_width = omap_gem_tiled_stride(fb->obj[0], orient); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci switch (state->rotation & DRM_MODE_ROTATE_MASK) { 19362306a36Sopenharmony_ci case 0: 19462306a36Sopenharmony_ci case DRM_MODE_ROTATE_0: 19562306a36Sopenharmony_ci /* OK */ 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci default: 19962306a36Sopenharmony_ci dev_warn(fb->dev->dev, 20062306a36Sopenharmony_ci "rotation '%d' ignored for non-tiled fb\n", 20162306a36Sopenharmony_ci state->rotation); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci info->paddr = get_linear_addr(fb, format, 0, x, y); 20662306a36Sopenharmony_ci info->rotation_type = OMAP_DSS_ROT_NONE; 20762306a36Sopenharmony_ci info->rotation = DRM_MODE_ROTATE_0; 20862306a36Sopenharmony_ci info->screen_width = fb->pitches[0]; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* convert to pixels: */ 21262306a36Sopenharmony_ci info->screen_width /= format->cpp[0]; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (fb->format->format == DRM_FORMAT_NV12) { 21562306a36Sopenharmony_ci if (info->rotation_type == OMAP_DSS_ROT_TILER) { 21662306a36Sopenharmony_ci WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED_MASK)); 21762306a36Sopenharmony_ci omap_gem_rotated_dma_addr(fb->obj[1], orient, x/2, y/2, 21862306a36Sopenharmony_ci &info->p_uv_addr); 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci info->p_uv_addr = get_linear_addr(fb, format, 1, x, y); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci info->p_uv_addr = 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (r_info) { 22762306a36Sopenharmony_ci info->width /= 2; 22862306a36Sopenharmony_ci info->out_width /= 2; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci *r_info = *info; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (fb->format->is_yuv) { 23362306a36Sopenharmony_ci if (info->width & 1) { 23462306a36Sopenharmony_ci info->width++; 23562306a36Sopenharmony_ci r_info->width--; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (info->out_width & 1) { 23962306a36Sopenharmony_ci info->out_width++; 24062306a36Sopenharmony_ci r_info->out_width--; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci r_info->pos_x = info->pos_x + info->out_width; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci r_info->paddr = get_linear_addr(fb, format, 0, 24762306a36Sopenharmony_ci x + info->width, y); 24862306a36Sopenharmony_ci if (fb->format->format == DRM_FORMAT_NV12) { 24962306a36Sopenharmony_ci r_info->p_uv_addr = 25062306a36Sopenharmony_ci get_linear_addr(fb, format, 1, 25162306a36Sopenharmony_ci x + info->width, y); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* pin, prepare for scanout: */ 25762306a36Sopenharmony_ciint omap_framebuffer_pin(struct drm_framebuffer *fb) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 26062306a36Sopenharmony_ci int ret, i, n = fb->format->num_planes; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci mutex_lock(&omap_fb->lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (omap_fb->pin_count > 0) { 26562306a36Sopenharmony_ci omap_fb->pin_count++; 26662306a36Sopenharmony_ci mutex_unlock(&omap_fb->lock); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < n; i++) { 27162306a36Sopenharmony_ci struct plane *plane = &omap_fb->planes[i]; 27262306a36Sopenharmony_ci ret = omap_gem_pin(fb->obj[i], &plane->dma_addr); 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci goto fail; 27562306a36Sopenharmony_ci omap_gem_dma_sync_buffer(fb->obj[i], DMA_TO_DEVICE); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci omap_fb->pin_count++; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mutex_unlock(&omap_fb->lock); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cifail: 28562306a36Sopenharmony_ci for (i--; i >= 0; i--) { 28662306a36Sopenharmony_ci struct plane *plane = &omap_fb->planes[i]; 28762306a36Sopenharmony_ci omap_gem_unpin(fb->obj[i]); 28862306a36Sopenharmony_ci plane->dma_addr = 0; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mutex_unlock(&omap_fb->lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* unpin, no longer being scanned out: */ 29762306a36Sopenharmony_civoid omap_framebuffer_unpin(struct drm_framebuffer *fb) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); 30062306a36Sopenharmony_ci int i, n = fb->format->num_planes; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci mutex_lock(&omap_fb->lock); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci omap_fb->pin_count--; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (omap_fb->pin_count > 0) { 30762306a36Sopenharmony_ci mutex_unlock(&omap_fb->lock); 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci for (i = 0; i < n; i++) { 31262306a36Sopenharmony_ci struct plane *plane = &omap_fb->planes[i]; 31362306a36Sopenharmony_ci omap_gem_unpin(fb->obj[i]); 31462306a36Sopenharmony_ci plane->dma_addr = 0; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_unlock(&omap_fb->lock); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 32162306a36Sopenharmony_civoid omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i, n = fb->format->num_planes; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, 32662306a36Sopenharmony_ci (char *)&fb->format->format); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci for (i = 0; i < n; i++) { 32962306a36Sopenharmony_ci seq_printf(m, " %d: offset=%d pitch=%d, obj: ", 33062306a36Sopenharmony_ci i, fb->offsets[n], fb->pitches[i]); 33162306a36Sopenharmony_ci omap_gem_describe(fb->obj[i], m); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci#endif 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistruct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, 33762306a36Sopenharmony_ci struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci const struct drm_format_info *info = drm_get_format_info(dev, 34062306a36Sopenharmony_ci mode_cmd); 34162306a36Sopenharmony_ci unsigned int num_planes = info->num_planes; 34262306a36Sopenharmony_ci struct drm_gem_object *bos[4]; 34362306a36Sopenharmony_ci struct drm_framebuffer *fb; 34462306a36Sopenharmony_ci int i; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < num_planes; i++) { 34762306a36Sopenharmony_ci bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); 34862306a36Sopenharmony_ci if (!bos[i]) { 34962306a36Sopenharmony_ci fb = ERR_PTR(-ENOENT); 35062306a36Sopenharmony_ci goto error; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci fb = omap_framebuffer_init(dev, mode_cmd, bos); 35562306a36Sopenharmony_ci if (IS_ERR(fb)) 35662306a36Sopenharmony_ci goto error; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return fb; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cierror: 36162306a36Sopenharmony_ci while (--i >= 0) 36262306a36Sopenharmony_ci drm_gem_object_put(bos[i]); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return fb; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistruct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, 36862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci const struct drm_format_info *format = NULL; 37162306a36Sopenharmony_ci struct omap_framebuffer *omap_fb = NULL; 37262306a36Sopenharmony_ci struct drm_framebuffer *fb = NULL; 37362306a36Sopenharmony_ci unsigned int pitch = mode_cmd->pitches[0]; 37462306a36Sopenharmony_ci int ret, i; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", 37762306a36Sopenharmony_ci dev, mode_cmd, mode_cmd->width, mode_cmd->height, 37862306a36Sopenharmony_ci (char *)&mode_cmd->pixel_format); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci format = drm_get_format_info(dev, mode_cmd); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 38362306a36Sopenharmony_ci if (formats[i] == mode_cmd->pixel_format) 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!format || i == ARRAY_SIZE(formats)) { 38862306a36Sopenharmony_ci dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", 38962306a36Sopenharmony_ci (char *)&mode_cmd->pixel_format); 39062306a36Sopenharmony_ci ret = -EINVAL; 39162306a36Sopenharmony_ci goto fail; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); 39562306a36Sopenharmony_ci if (!omap_fb) { 39662306a36Sopenharmony_ci ret = -ENOMEM; 39762306a36Sopenharmony_ci goto fail; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci fb = &omap_fb->base; 40162306a36Sopenharmony_ci omap_fb->format = format; 40262306a36Sopenharmony_ci mutex_init(&omap_fb->lock); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * The code below assumes that no format use more than two planes, and 40662306a36Sopenharmony_ci * that the two planes of multiplane formats need the same number of 40762306a36Sopenharmony_ci * bytes per pixel. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { 41062306a36Sopenharmony_ci dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n"); 41162306a36Sopenharmony_ci ret = -EINVAL; 41262306a36Sopenharmony_ci goto fail; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (pitch % format->cpp[0]) { 41662306a36Sopenharmony_ci dev_dbg(dev->dev, 41762306a36Sopenharmony_ci "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", 41862306a36Sopenharmony_ci pitch, format->cpp[0]); 41962306a36Sopenharmony_ci ret = -EINVAL; 42062306a36Sopenharmony_ci goto fail; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci for (i = 0; i < format->num_planes; i++) { 42462306a36Sopenharmony_ci struct plane *plane = &omap_fb->planes[i]; 42562306a36Sopenharmony_ci unsigned int vsub = i == 0 ? 1 : format->vsub; 42662306a36Sopenharmony_ci unsigned int size; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci size = pitch * mode_cmd->height / vsub; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { 43162306a36Sopenharmony_ci dev_dbg(dev->dev, 43262306a36Sopenharmony_ci "provided buffer object is too small! %zu < %d\n", 43362306a36Sopenharmony_ci bos[i]->size - mode_cmd->offsets[i], size); 43462306a36Sopenharmony_ci ret = -EINVAL; 43562306a36Sopenharmony_ci goto fail; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fb->obj[i] = bos[i]; 43962306a36Sopenharmony_ci plane->dma_addr = 0; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); 44562306a36Sopenharmony_ci if (ret) { 44662306a36Sopenharmony_ci dev_err(dev->dev, "framebuffer init failed: %d\n", ret); 44762306a36Sopenharmony_ci goto fail; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci DBG("create: FB ID: %d (%p)", fb->base.id, fb); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return fb; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cifail: 45562306a36Sopenharmony_ci kfree(omap_fb); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return ERR_PTR(ret); 45862306a36Sopenharmony_ci} 459