blob: 1c9048e35d90fabcc1fcfe448ce5281394547ce2 [file] [log] [blame]
Thomas Gleixnercaab2772019-06-03 07:44:50 +02001// SPDX-License-Identifier: GPL-2.0-only
Rob Clarkcd5351f2011-11-12 12:09:40 -06002/*
Andrew F. Davisbb5cdf82017-12-05 14:29:31 -06003 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
Rob Clarkcd5351f2011-11-12 12:09:40 -06004 * Author: Rob Clark <rob@ti.com>
Rob Clarkcd5351f2011-11-12 12:09:40 -06005 */
6
Arnd Bergmann2d802452016-05-11 18:01:45 +02007#include <linux/seq_file.h>
8
Laurent Pinchart2d278f52015-03-05 21:31:37 +02009#include <drm/drm_crtc.h>
Daniel Vetterfcd70cd2019-01-17 22:03:34 +010010#include <drm/drm_modeset_helper.h>
Daniel Stone3e442552018-03-30 15:11:20 +010011#include <drm/drm_gem_framebuffer_helper.h>
Rob Clarkcd5351f2011-11-12 12:09:40 -060012
Laurent Pinchart2d278f52015-03-05 21:31:37 +020013#include "omap_dmm_tiler.h"
14#include "omap_drv.h"
Rob Clarkcd5351f2011-11-12 12:09:40 -060015
Rob Clarkcd5351f2011-11-12 12:09:40 -060016/*
17 * framebuffer funcs
18 */
19
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +030020static const u32 formats[] = {
Rob Clarkae43d7c2012-01-16 12:51:15 -060021 /* 16bpp [A]RGB: */
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +030022 DRM_FORMAT_RGB565, /* RGB16-565 */
23 DRM_FORMAT_RGBX4444, /* RGB12x-4444 */
24 DRM_FORMAT_XRGB4444, /* xRGB12-4444 */
25 DRM_FORMAT_RGBA4444, /* RGBA12-4444 */
26 DRM_FORMAT_ARGB4444, /* ARGB16-4444 */
27 DRM_FORMAT_XRGB1555, /* xRGB15-1555 */
28 DRM_FORMAT_ARGB1555, /* ARGB16-1555 */
Rob Clarkae43d7c2012-01-16 12:51:15 -060029 /* 24bpp RGB: */
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +030030 DRM_FORMAT_RGB888, /* RGB24-888 */
Rob Clarkae43d7c2012-01-16 12:51:15 -060031 /* 32bpp [A]RGB: */
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +030032 DRM_FORMAT_RGBX8888, /* RGBx24-8888 */
33 DRM_FORMAT_XRGB8888, /* xRGB24-8888 */
34 DRM_FORMAT_RGBA8888, /* RGBA32-8888 */
35 DRM_FORMAT_ARGB8888, /* ARGB32-8888 */
Rob Clarkae43d7c2012-01-16 12:51:15 -060036 /* YUV: */
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +030037 DRM_FORMAT_NV12,
38 DRM_FORMAT_YUYV,
39 DRM_FORMAT_UYVY,
Rob Clarkae43d7c2012-01-16 12:51:15 -060040};
41
Rob Clark9a0774e2012-01-16 12:51:17 -060042/* per-plane info for the fb: */
43struct plane {
Laurent Pinchart16869082017-04-21 00:33:51 +030044 dma_addr_t dma_addr;
Rob Clark9a0774e2012-01-16 12:51:17 -060045};
46
Rob Clarkcd5351f2011-11-12 12:09:40 -060047#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
48
49struct omap_framebuffer {
50 struct drm_framebuffer base;
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +000051 int pin_count;
Laurent Pinchartc9028b32016-06-07 00:45:35 +030052 const struct drm_format_info *format;
Laurent Pinchart4d20dfc2016-03-26 19:24:01 +020053 struct plane planes[2];
Laurent Pinchart16869082017-04-21 00:33:51 +030054 /* lock for pinning (pin_count and planes.dma_addr) */
Tomi Valkeinenf524ab72015-06-04 10:56:33 +030055 struct mutex lock;
Rob Clarkcd5351f2011-11-12 12:09:40 -060056};
57
Rob Clarkcd5351f2011-11-12 12:09:40 -060058static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
Daniel Stone3e442552018-03-30 15:11:20 +010059 .create_handle = drm_gem_fb_create_handle,
60 .destroy = drm_gem_fb_destroy,
Rob Clarkcd5351f2011-11-12 12:09:40 -060061};
62
Daniel Stone2ecceeb2018-03-30 15:11:21 +010063static u32 get_linear_addr(struct drm_framebuffer *fb,
Laurent Pinchartc9028b32016-06-07 00:45:35 +030064 const struct drm_format_info *format, int n, int x, int y)
Rob Clark3c810c62012-08-15 15:18:01 -050065{
Daniel Stone2ecceeb2018-03-30 15:11:21 +010066 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
67 struct plane *plane = &omap_fb->planes[n];
Laurent Pinchartdfe9cfc2018-02-11 15:07:33 +020068 u32 offset;
Rob Clark3c810c62012-08-15 15:18:01 -050069
Daniel Stone2ecceeb2018-03-30 15:11:21 +010070 offset = fb->offsets[n]
Laurent Pinchartc9028b32016-06-07 00:45:35 +030071 + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub))
Daniel Stone2ecceeb2018-03-30 15:11:21 +010072 + (y * fb->pitches[n] / (n == 0 ? 1 : format->vsub));
Rob Clark3c810c62012-08-15 15:18:01 -050073
Laurent Pinchart16869082017-04-21 00:33:51 +030074 return plane->dma_addr + offset;
Rob Clark3c810c62012-08-15 15:18:01 -050075}
76
Tomi Valkeinenbfeece52015-08-27 13:09:22 +030077bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb)
78{
Daniel Stone3e442552018-03-30 15:11:20 +010079 return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED;
Tomi Valkeinenbfeece52015-08-27 13:09:22 +030080}
81
Tomi Valkeinen8958aeb2017-05-15 12:57:46 +030082/* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */
Laurent Pinchartdfe9cfc2018-02-11 15:07:33 +020083static u32 drm_rotation_to_tiler(unsigned int drm_rot)
Tomi Valkeinen8958aeb2017-05-15 12:57:46 +030084{
Laurent Pinchartdfe9cfc2018-02-11 15:07:33 +020085 u32 orient;
Tomi Valkeinen8958aeb2017-05-15 12:57:46 +030086
87 switch (drm_rot & DRM_MODE_ROTATE_MASK) {
88 default:
89 case DRM_MODE_ROTATE_0:
90 orient = 0;
91 break;
92 case DRM_MODE_ROTATE_90:
93 orient = MASK_XY_FLIP | MASK_X_INVERT;
94 break;
95 case DRM_MODE_ROTATE_180:
96 orient = MASK_X_INVERT | MASK_Y_INVERT;
97 break;
98 case DRM_MODE_ROTATE_270:
99 orient = MASK_XY_FLIP | MASK_Y_INVERT;
100 break;
101 }
102
103 if (drm_rot & DRM_MODE_REFLECT_X)
104 orient ^= MASK_X_INVERT;
105
106 if (drm_rot & DRM_MODE_REFLECT_Y)
107 orient ^= MASK_Y_INVERT;
108
109 return orient;
110}
111
Rob Clark9a0774e2012-01-16 12:51:17 -0600112/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
113 */
Rob Clark3c810c62012-08-15 15:18:01 -0500114void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300115 struct drm_plane_state *state, struct omap_overlay_info *info)
Rob Clark9a0774e2012-01-16 12:51:17 -0600116{
117 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300118 const struct drm_format_info *format = omap_fb->format;
Rob Clark9a0774e2012-01-16 12:51:17 -0600119 struct plane *plane = &omap_fb->planes[0];
Laurent Pinchartdfe9cfc2018-02-11 15:07:33 +0200120 u32 x, y, orient = 0;
Rob Clark9a0774e2012-01-16 12:51:17 -0600121
Tomi Valkeinen41aff422017-05-04 11:31:56 +0300122 info->fourcc = fb->format->format;
Rob Clark9a0774e2012-01-16 12:51:17 -0600123
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300124 info->pos_x = state->crtc_x;
125 info->pos_y = state->crtc_y;
126 info->out_width = state->crtc_w;
127 info->out_height = state->crtc_h;
128 info->width = state->src_w >> 16;
129 info->height = state->src_h >> 16;
Rob Clark3c810c62012-08-15 15:18:01 -0500130
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300131 /* DSS driver wants the w & h in rotated orientation */
132 if (drm_rotation_90_or_270(state->rotation))
133 swap(info->width, info->height);
134
135 x = state->src_x >> 16;
136 y = state->src_y >> 16;
Rob Clark3c810c62012-08-15 15:18:01 -0500137
Daniel Stone3e442552018-03-30 15:11:20 +0100138 if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED) {
Laurent Pinchartdfe9cfc2018-02-11 15:07:33 +0200139 u32 w = state->src_w >> 16;
140 u32 h = state->src_h >> 16;
Rob Clark3c810c62012-08-15 15:18:01 -0500141
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300142 orient = drm_rotation_to_tiler(state->rotation);
Rob Clark3c810c62012-08-15 15:18:01 -0500143
Tomi Valkeinenc4df6e42017-05-15 11:09:25 +0300144 /*
145 * omap_gem_rotated_paddr() wants the x & y in tiler units.
146 * Usually tiler unit size is the same as the pixel size, except
147 * for YUV422 formats, for which the tiler unit size is 32 bits
148 * and pixel size is 16 bits.
149 */
150 if (fb->format->format == DRM_FORMAT_UYVY ||
151 fb->format->format == DRM_FORMAT_YUYV) {
152 x /= 2;
153 w /= 2;
154 }
155
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300156 /* adjust x,y offset for invert: */
Rob Clark3c810c62012-08-15 15:18:01 -0500157 if (orient & MASK_Y_INVERT)
158 y += h - 1;
159 if (orient & MASK_X_INVERT)
160 x += w - 1;
161
Tomi Valkeinenc4df6e42017-05-15 11:09:25 +0300162 /* Note: x and y are in TILER units, not pixels */
Daniel Stone3e442552018-03-30 15:11:20 +0100163 omap_gem_rotated_dma_addr(fb->obj[0], orient, x, y,
Laurent Pinchart16869082017-04-21 00:33:51 +0300164 &info->paddr);
Rob Clark3c810c62012-08-15 15:18:01 -0500165 info->rotation_type = OMAP_DSS_ROT_TILER;
Tomi Valkeinen16f9ede2017-05-16 12:48:58 +0300166 info->rotation = state->rotation ?: DRM_MODE_ROTATE_0;
Tomi Valkeinenc4df6e42017-05-15 11:09:25 +0300167 /* Note: stride in TILER units, not pixels */
Daniel Stone3e442552018-03-30 15:11:20 +0100168 info->screen_width = omap_gem_tiled_stride(fb->obj[0], orient);
Rob Clark3c810c62012-08-15 15:18:01 -0500169 } else {
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300170 switch (state->rotation & DRM_MODE_ROTATE_MASK) {
Tomi Valkeinen5ac96342014-04-08 16:18:41 +0300171 case 0:
Robert Fossc2c446a2017-05-19 16:50:17 -0400172 case DRM_MODE_ROTATE_0:
Tomi Valkeinen5ac96342014-04-08 16:18:41 +0300173 /* OK */
174 break;
175
176 default:
177 dev_warn(fb->dev->dev,
178 "rotation '%d' ignored for non-tiled fb\n",
Tomi Valkeinen218ed532017-05-15 13:40:08 +0300179 state->rotation);
Tomi Valkeinen5ac96342014-04-08 16:18:41 +0300180 break;
181 }
182
Daniel Stone2ecceeb2018-03-30 15:11:21 +0100183 info->paddr = get_linear_addr(fb, format, 0, x, y);
Tomi Valkeinen517a8a952017-05-03 14:14:27 +0300184 info->rotation_type = OMAP_DSS_ROT_NONE;
Tomi Valkeinen16f9ede2017-05-16 12:48:58 +0300185 info->rotation = DRM_MODE_ROTATE_0;
Daniel Stone2ecceeb2018-03-30 15:11:21 +0100186 info->screen_width = fb->pitches[0];
Rob Clark3c810c62012-08-15 15:18:01 -0500187 }
188
189 /* convert to pixels: */
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300190 info->screen_width /= format->cpp[0];
Rob Clark9a0774e2012-01-16 12:51:17 -0600191
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +0300192 if (fb->format->format == DRM_FORMAT_NV12) {
Rob Clark9a0774e2012-01-16 12:51:17 -0600193 plane = &omap_fb->planes[1];
Rob Clark3c810c62012-08-15 15:18:01 -0500194
195 if (info->rotation_type == OMAP_DSS_ROT_TILER) {
Daniel Stone3e442552018-03-30 15:11:20 +0100196 WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED));
197 omap_gem_rotated_dma_addr(fb->obj[1], orient, x/2, y/2,
Laurent Pinchart16869082017-04-21 00:33:51 +0300198 &info->p_uv_addr);
Rob Clark3c810c62012-08-15 15:18:01 -0500199 } else {
Daniel Stone2ecceeb2018-03-30 15:11:21 +0100200 info->p_uv_addr = get_linear_addr(fb, format, 1, x, y);
Rob Clark3c810c62012-08-15 15:18:01 -0500201 }
Rob Clark9a0774e2012-01-16 12:51:17 -0600202 } else {
203 info->p_uv_addr = 0;
204 }
205}
206
Rob Clark5833bd22013-08-07 13:41:21 -0400207/* pin, prepare for scanout: */
208int omap_framebuffer_pin(struct drm_framebuffer *fb)
Rob Clarkb33f34d2012-03-05 10:48:35 -0600209{
Rob Clark5833bd22013-08-07 13:41:21 -0400210 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
Ville Syrjäläbcb0b462016-12-14 23:30:22 +0200211 int ret, i, n = fb->format->num_planes;
Rob Clarkb33f34d2012-03-05 10:48:35 -0600212
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300213 mutex_lock(&omap_fb->lock);
214
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +0000215 if (omap_fb->pin_count > 0) {
216 omap_fb->pin_count++;
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300217 mutex_unlock(&omap_fb->lock);
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +0000218 return 0;
219 }
220
Rob Clark5833bd22013-08-07 13:41:21 -0400221 for (i = 0; i < n; i++) {
222 struct plane *plane = &omap_fb->planes[i];
Daniel Stone3e442552018-03-30 15:11:20 +0100223 ret = omap_gem_pin(fb->obj[i], &plane->dma_addr);
Rob Clark5833bd22013-08-07 13:41:21 -0400224 if (ret)
225 goto fail;
Daniel Stone3e442552018-03-30 15:11:20 +0100226 omap_gem_dma_sync_buffer(fb->obj[i], DMA_TO_DEVICE);
Rob Clarkb33f34d2012-03-05 10:48:35 -0600227 }
228
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +0000229 omap_fb->pin_count++;
230
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300231 mutex_unlock(&omap_fb->lock);
232
Rob Clark5833bd22013-08-07 13:41:21 -0400233 return 0;
234
235fail:
236 for (i--; i >= 0; i--) {
237 struct plane *plane = &omap_fb->planes[i];
Daniel Stone3e442552018-03-30 15:11:20 +0100238 omap_gem_unpin(fb->obj[i]);
Laurent Pinchart16869082017-04-21 00:33:51 +0300239 plane->dma_addr = 0;
Rob Clarkb33f34d2012-03-05 10:48:35 -0600240 }
241
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300242 mutex_unlock(&omap_fb->lock);
243
Rob Clarkb33f34d2012-03-05 10:48:35 -0600244 return ret;
245}
246
Rob Clark5833bd22013-08-07 13:41:21 -0400247/* unpin, no longer being scanned out: */
Tomi Valkeinen9c368502015-04-28 14:01:35 +0300248void omap_framebuffer_unpin(struct drm_framebuffer *fb)
Rob Clark5833bd22013-08-07 13:41:21 -0400249{
250 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
Ville Syrjäläbcb0b462016-12-14 23:30:22 +0200251 int i, n = fb->format->num_planes;
Rob Clark5833bd22013-08-07 13:41:21 -0400252
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300253 mutex_lock(&omap_fb->lock);
254
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +0000255 omap_fb->pin_count--;
256
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300257 if (omap_fb->pin_count > 0) {
258 mutex_unlock(&omap_fb->lock);
Tomi Valkeinen9c368502015-04-28 14:01:35 +0300259 return;
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300260 }
Tomi Valkeinenf36eb5a2014-09-03 19:25:54 +0000261
Rob Clark5833bd22013-08-07 13:41:21 -0400262 for (i = 0; i < n; i++) {
263 struct plane *plane = &omap_fb->planes[i];
Daniel Stone3e442552018-03-30 15:11:20 +0100264 omap_gem_unpin(fb->obj[i]);
Laurent Pinchart16869082017-04-21 00:33:51 +0300265 plane->dma_addr = 0;
Rob Clark5833bd22013-08-07 13:41:21 -0400266 }
267
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300268 mutex_unlock(&omap_fb->lock);
Rob Clark5833bd22013-08-07 13:41:21 -0400269}
270
Rob Clarkf6b60362012-03-05 10:48:36 -0600271#ifdef CONFIG_DEBUG_FS
272void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
273{
Ville Syrjäläbcb0b462016-12-14 23:30:22 +0200274 int i, n = fb->format->num_planes;
Rob Clarkf6b60362012-03-05 10:48:36 -0600275
276 seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height,
Ville Syrjälä438b74a2016-12-14 23:32:55 +0200277 (char *)&fb->format->format);
Rob Clarkf6b60362012-03-05 10:48:36 -0600278
279 for (i = 0; i < n; i++) {
Rob Clarkf6b60362012-03-05 10:48:36 -0600280 seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
Daniel Stone2ecceeb2018-03-30 15:11:21 +0100281 i, fb->offsets[n], fb->pitches[i]);
Daniel Stone3e442552018-03-30 15:11:20 +0100282 omap_gem_describe(fb->obj[i], m);
Rob Clarkf6b60362012-03-05 10:48:36 -0600283 }
284}
285#endif
286
Rob Clarkcd5351f2011-11-12 12:09:40 -0600287struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
Ville Syrjälä1eb834512015-11-11 19:11:29 +0200288 struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600289{
Laurent Pincharta39c94e2016-03-26 19:51:57 +0200290 unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format);
Rob Clarkae43d7c2012-01-16 12:51:15 -0600291 struct drm_gem_object *bos[4];
Rob Clarkcd5351f2011-11-12 12:09:40 -0600292 struct drm_framebuffer *fb;
Laurent Pincharta39c94e2016-03-26 19:51:57 +0200293 int i;
Rob Clarkae43d7c2012-01-16 12:51:15 -0600294
Laurent Pincharta39c94e2016-03-26 19:51:57 +0200295 for (i = 0; i < num_planes; i++) {
296 bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
297 if (!bos[i]) {
298 fb = ERR_PTR(-ENOENT);
299 goto error;
300 }
301 }
Rob Clarkae43d7c2012-01-16 12:51:15 -0600302
303 fb = omap_framebuffer_init(dev, mode_cmd, bos);
Laurent Pincharta39c94e2016-03-26 19:51:57 +0200304 if (IS_ERR(fb))
305 goto error;
306
307 return fb;
308
309error:
Tomi Valkeinen95552192017-08-04 12:20:03 +0300310 while (--i >= 0)
Thomas Zimmermanne64d0222018-06-18 15:07:26 +0200311 drm_gem_object_put_unlocked(bos[i]);
Laurent Pincharta39c94e2016-03-26 19:51:57 +0200312
Rob Clarkcd5351f2011-11-12 12:09:40 -0600313 return fb;
314}
315
316struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
Ville Syrjälä1eb834512015-11-11 19:11:29 +0200317 const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600318{
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300319 const struct drm_format_info *format = NULL;
Tomi Valkeinen925e4942014-09-25 19:24:27 +0000320 struct omap_framebuffer *omap_fb = NULL;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600321 struct drm_framebuffer *fb = NULL;
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200322 unsigned int pitch = mode_cmd->pitches[0];
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300323 int ret, i;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600324
Rob Clarkae43d7c2012-01-16 12:51:15 -0600325 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
Rob Clarkcd5351f2011-11-12 12:09:40 -0600326 dev, mode_cmd, mode_cmd->width, mode_cmd->height,
Rob Clarkae43d7c2012-01-16 12:51:15 -0600327 (char *)&mode_cmd->pixel_format);
328
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300329 format = drm_format_info(mode_cmd->pixel_format);
330
Rob Clarkae43d7c2012-01-16 12:51:15 -0600331 for (i = 0; i < ARRAY_SIZE(formats); i++) {
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +0300332 if (formats[i] == mode_cmd->pixel_format)
Rob Clarkae43d7c2012-01-16 12:51:15 -0600333 break;
Rob Clarkae43d7c2012-01-16 12:51:15 -0600334 }
335
Tomi Valkeinenc2e52e32017-05-04 11:29:58 +0300336 if (!format || i == ARRAY_SIZE(formats)) {
Laurent Pincharta078a3d2016-03-26 20:02:49 +0200337 dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n",
338 (char *)&mode_cmd->pixel_format);
Rob Clarkae43d7c2012-01-16 12:51:15 -0600339 ret = -EINVAL;
340 goto fail;
341 }
Rob Clarkcd5351f2011-11-12 12:09:40 -0600342
Rob Clarkcd5351f2011-11-12 12:09:40 -0600343 omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
344 if (!omap_fb) {
Rob Clarkae43d7c2012-01-16 12:51:15 -0600345 ret = -ENOMEM;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600346 goto fail;
347 }
348
349 fb = &omap_fb->base;
Rob Clark9a0774e2012-01-16 12:51:17 -0600350 omap_fb->format = format;
Tomi Valkeinenf524ab72015-06-04 10:56:33 +0300351 mutex_init(&omap_fb->lock);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600352
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200353 /*
354 * The code below assumes that no format use more than two planes, and
355 * that the two planes of multiplane formats need the same number of
356 * bytes per pixel.
357 */
358 if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) {
Laurent Pincharta078a3d2016-03-26 20:02:49 +0200359 dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n");
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200360 ret = -EINVAL;
361 goto fail;
362 }
363
364 if (pitch % format->cpp[0]) {
Laurent Pincharta078a3d2016-03-26 20:02:49 +0200365 dev_dbg(dev->dev,
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200366 "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n",
367 pitch, format->cpp[0]);
368 ret = -EINVAL;
369 goto fail;
370 }
371
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300372 for (i = 0; i < format->num_planes; i++) {
Rob Clark9a0774e2012-01-16 12:51:17 -0600373 struct plane *plane = &omap_fb->planes[i];
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300374 unsigned int vsub = i == 0 ? 1 : format->vsub;
375 unsigned int size;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600376
Laurent Pinchartc9028b32016-06-07 00:45:35 +0300377 size = pitch * mode_cmd->height / vsub;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600378
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200379 if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) {
Laurent Pincharta078a3d2016-03-26 20:02:49 +0200380 dev_dbg(dev->dev,
Tomi Valkeinen2150c192017-02-21 09:57:12 +0200381 "provided buffer object is too small! %zu < %d\n",
Laurent Pinchart6941e3d2016-03-26 20:01:13 +0200382 bos[i]->size - mode_cmd->offsets[i], size);
Tomi Valkeinenbe4f2352016-01-08 15:51:02 +0200383 ret = -EINVAL;
384 goto fail;
385 }
386
Daniel Stone3e442552018-03-30 15:11:20 +0100387 fb->obj[i] = bos[i];
Laurent Pinchart16869082017-04-21 00:33:51 +0300388 plane->dma_addr = 0;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600389 }
390
Ville Syrjäläa3f913c2016-12-14 22:48:59 +0200391 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600392
Daniel Vetterc7d73f62012-12-13 23:38:38 +0100393 ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
394 if (ret) {
395 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
396 goto fail;
397 }
398
399 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
400
Rob Clarkcd5351f2011-11-12 12:09:40 -0600401 return fb;
402
403fail:
Tomi Valkeinen925e4942014-09-25 19:24:27 +0000404 kfree(omap_fb);
YAMANE Toshiakiddcd49e2012-11-14 19:32:17 +0900405
Rob Clarkae43d7c2012-01-16 12:51:15 -0600406 return ERR_PTR(ret);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600407}