aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/omapdrm/omap_fb.c
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-02-11 12:43:09 -0500
committerRob Clark <robdclark@gmail.com>2013-02-16 17:38:06 -0500
commit8bb0daffb0b8e45188066255b4203446eae181f1 (patch)
treec1a324b863df57becdfab54c9325231bbb853b56 /drivers/gpu/drm/omapdrm/omap_fb.c
parenta4462f246c8821f625f45bce52c7ca7e0207dffe (diff)
drm/omap: move out of staging
Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_fb.c')
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
new file mode 100644
index 000000000000..9d5f6f696c72
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -0,0 +1,472 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_fb.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21#include "omap_dmm_tiler.h"
22
23#include "drm_crtc.h"
24#include "drm_crtc_helper.h"
25
26/*
27 * framebuffer funcs
28 */
29
30/* per-format info: */
31struct format {
32 enum omap_color_mode dss_format;
33 uint32_t pixel_format;
34 struct {
35 int stride_bpp; /* this times width is stride */
36 int sub_y; /* sub-sample in y dimension */
37 } planes[4];
38 bool yuv;
39};
40
41static const struct format formats[] = {
42 /* 16bpp [A]RGB: */
43 { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */
44 { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */
45 { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */
46 { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */
47 { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */
48 { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */
49 { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */
50 /* 24bpp RGB: */
51 { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */
52 /* 32bpp [A]RGB: */
53 { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */
54 { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */
55 { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */
56 { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */
57 /* YUV: */
58 { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true },
59 { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true },
60 { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true },
61};
62
63/* convert from overlay's pixel formats bitmask to an array of fourcc's */
64uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
65 uint32_t max_formats, enum omap_color_mode supported_modes)
66{
67 uint32_t nformats = 0;
68 int i = 0;
69
70 for (i = 0; i < ARRAY_SIZE(formats) && nformats < max_formats; i++)
71 if (formats[i].dss_format & supported_modes)
72 pixel_formats[nformats++] = formats[i].pixel_format;
73
74 return nformats;
75}
76
77/* per-plane info for the fb: */
78struct plane {
79 struct drm_gem_object *bo;
80 uint32_t pitch;
81 uint32_t offset;
82 dma_addr_t paddr;
83};
84
85#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
86
87struct omap_framebuffer {
88 struct drm_framebuffer base;
89 const struct format *format;
90 struct plane planes[4];
91};
92
93static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
94 struct drm_file *file_priv,
95 unsigned int *handle)
96{
97 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
98 return drm_gem_handle_create(file_priv,
99 omap_fb->planes[0].bo, handle);
100}
101
102static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
103{
104 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
105 int i, n = drm_format_num_planes(fb->pixel_format);
106
107 DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
108
109 drm_framebuffer_cleanup(fb);
110
111 for (i = 0; i < n; i++) {
112 struct plane *plane = &omap_fb->planes[i];
113 if (plane->bo)
114 drm_gem_object_unreference_unlocked(plane->bo);
115 }
116
117 kfree(omap_fb);
118}
119
120static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
121 struct drm_file *file_priv, unsigned flags, unsigned color,
122 struct drm_clip_rect *clips, unsigned num_clips)
123{
124 int i;
125
126 for (i = 0; i < num_clips; i++) {
127 omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
128 clips[i].x2 - clips[i].x1,
129 clips[i].y2 - clips[i].y1);
130 }
131
132 return 0;
133}
134
135static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
136 .create_handle = omap_framebuffer_create_handle,
137 .destroy = omap_framebuffer_destroy,
138 .dirty = omap_framebuffer_dirty,
139};
140
141static uint32_t get_linear_addr(struct plane *plane,
142 const struct format *format, int n, int x, int y)
143{
144 uint32_t offset;
145
146 offset = plane->offset +
147 (x * format->planes[n].stride_bpp) +
148 (y * plane->pitch / format->planes[n].sub_y);
149
150 return plane->paddr + offset;
151}
152
153/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
154 */
155void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
156 struct omap_drm_window *win, struct omap_overlay_info *info)
157{
158 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
159 const struct format *format = omap_fb->format;
160 struct plane *plane = &omap_fb->planes[0];
161 uint32_t x, y, orient = 0;
162
163 info->color_mode = format->dss_format;
164
165 info->pos_x = win->crtc_x;
166 info->pos_y = win->crtc_y;
167 info->out_width = win->crtc_w;
168 info->out_height = win->crtc_h;
169 info->width = win->src_w;
170 info->height = win->src_h;
171
172 x = win->src_x;
173 y = win->src_y;
174
175 if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) {
176 uint32_t w = win->src_w;
177 uint32_t h = win->src_h;
178
179 switch (win->rotation & 0xf) {
180 default:
181 dev_err(fb->dev->dev, "invalid rotation: %02x",
182 (uint32_t)win->rotation);
183 /* fallthru to default to no rotation */
184 case 0:
185 case BIT(DRM_ROTATE_0):
186 orient = 0;
187 break;
188 case BIT(DRM_ROTATE_90):
189 orient = MASK_XY_FLIP | MASK_X_INVERT;
190 break;
191 case BIT(DRM_ROTATE_180):
192 orient = MASK_X_INVERT | MASK_Y_INVERT;
193 break;
194 case BIT(DRM_ROTATE_270):
195 orient = MASK_XY_FLIP | MASK_Y_INVERT;
196 break;
197 }
198
199 if (win->rotation & BIT(DRM_REFLECT_X))
200 orient ^= MASK_X_INVERT;
201
202 if (win->rotation & BIT(DRM_REFLECT_Y))
203 orient ^= MASK_Y_INVERT;
204
205 /* adjust x,y offset for flip/invert: */
206 if (orient & MASK_XY_FLIP)
207 swap(w, h);
208 if (orient & MASK_Y_INVERT)
209 y += h - 1;
210 if (orient & MASK_X_INVERT)
211 x += w - 1;
212
213 omap_gem_rotated_paddr(plane->bo, orient, x, y, &info->paddr);
214 info->rotation_type = OMAP_DSS_ROT_TILER;
215 info->screen_width = omap_gem_tiled_stride(plane->bo, orient);
216 } else {
217 info->paddr = get_linear_addr(plane, format, 0, x, y);
218 info->rotation_type = OMAP_DSS_ROT_DMA;
219 info->screen_width = plane->pitch;
220 }
221
222 /* convert to pixels: */
223 info->screen_width /= format->planes[0].stride_bpp;
224
225 if (format->dss_format == OMAP_DSS_COLOR_NV12) {
226 plane = &omap_fb->planes[1];
227
228 if (info->rotation_type == OMAP_DSS_ROT_TILER) {
229 WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED));
230 omap_gem_rotated_paddr(plane->bo, orient,
231 x/2, y/2, &info->p_uv_addr);
232 } else {
233 info->p_uv_addr = get_linear_addr(plane, format, 1, x, y);
234 }
235 } else {
236 info->p_uv_addr = 0;
237 }
238}
239
240/* Call for unpin 'a' (if not NULL), and pin 'b' (if not NULL). Although
241 * buffers to unpin are just pushed to the unpin fifo so that the
242 * caller can defer unpin until vblank.
243 *
244 * Note if this fails (ie. something went very wrong!), all buffers are
245 * unpinned, and the caller disables the overlay. We could have tried
246 * to revert back to the previous set of pinned buffers but if things are
247 * hosed there is no guarantee that would succeed.
248 */
249int omap_framebuffer_replace(struct drm_framebuffer *a,
250 struct drm_framebuffer *b, void *arg,
251 void (*unpin)(void *arg, struct drm_gem_object *bo))
252{
253 int ret = 0, i, na, nb;
254 struct omap_framebuffer *ofba = to_omap_framebuffer(a);
255 struct omap_framebuffer *ofbb = to_omap_framebuffer(b);
256 uint32_t pinned_mask = 0;
257
258 na = a ? drm_format_num_planes(a->pixel_format) : 0;
259 nb = b ? drm_format_num_planes(b->pixel_format) : 0;
260
261 for (i = 0; i < max(na, nb); i++) {
262 struct plane *pa, *pb;
263
264 pa = (i < na) ? &ofba->planes[i] : NULL;
265 pb = (i < nb) ? &ofbb->planes[i] : NULL;
266
267 if (pa)
268 unpin(arg, pa->bo);
269
270 if (pb && !ret) {
271 ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true);
272 if (!ret) {
273 omap_gem_dma_sync(pb->bo, DMA_TO_DEVICE);
274 pinned_mask |= (1 << i);
275 }
276 }
277 }
278
279 if (ret) {
280 /* something went wrong.. unpin what has been pinned */
281 for (i = 0; i < nb; i++) {
282 if (pinned_mask & (1 << i)) {
283 struct plane *pb = &ofba->planes[i];
284 unpin(arg, pb->bo);
285 }
286 }
287 }
288
289 return ret;
290}
291
292struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p)
293{
294 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
295 if (p >= drm_format_num_planes(fb->pixel_format))
296 return NULL;
297 return omap_fb->planes[p].bo;
298}
299
300/* iterate thru all the connectors, returning ones that are attached
301 * to the same fb..
302 */
303struct drm_connector *omap_framebuffer_get_next_connector(
304 struct drm_framebuffer *fb, struct drm_connector *from)
305{
306 struct drm_device *dev = fb->dev;
307 struct list_head *connector_list = &dev->mode_config.connector_list;
308 struct drm_connector *connector = from;
309
310 if (!from)
311 return list_first_entry(connector_list, typeof(*from), head);
312
313 list_for_each_entry_from(connector, connector_list, head) {
314 if (connector != from) {
315 struct drm_encoder *encoder = connector->encoder;
316 struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
317 if (crtc && crtc->fb == fb)
318 return connector;
319
320 }
321 }
322
323 return NULL;
324}
325
326/* flush an area of the framebuffer (in case of manual update display that
327 * is not automatically flushed)
328 */
329void omap_framebuffer_flush(struct drm_framebuffer *fb,
330 int x, int y, int w, int h)
331{
332 struct drm_connector *connector = NULL;
333
334 VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
335
336 while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
337 /* only consider connectors that are part of a chain */
338 if (connector->encoder && connector->encoder->crtc) {
339 /* TODO: maybe this should propagate thru the crtc who
340 * could do the coordinate translation..
341 */
342 struct drm_crtc *crtc = connector->encoder->crtc;
343 int cx = max(0, x - crtc->x);
344 int cy = max(0, y - crtc->y);
345 int cw = w + (x - crtc->x) - cx;
346 int ch = h + (y - crtc->y) - cy;
347
348 omap_connector_flush(connector, cx, cy, cw, ch);
349 }
350 }
351}
352
353#ifdef CONFIG_DEBUG_FS
354void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
355{
356 struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
357 int i, n = drm_format_num_planes(fb->pixel_format);
358
359 seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height,
360 (char *)&fb->pixel_format);
361
362 for (i = 0; i < n; i++) {
363 struct plane *plane = &omap_fb->planes[i];
364 seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
365 i, plane->offset, plane->pitch);
366 omap_gem_describe(plane->bo, m);
367 }
368}
369#endif
370
371struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
372 struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd)
373{
374 struct drm_gem_object *bos[4];
375 struct drm_framebuffer *fb;
376 int ret;
377
378 ret = objects_lookup(dev, file, mode_cmd->pixel_format,
379 bos, mode_cmd->handles);
380 if (ret)
381 return ERR_PTR(ret);
382
383 fb = omap_framebuffer_init(dev, mode_cmd, bos);
384 if (IS_ERR(fb)) {
385 int i, n = drm_format_num_planes(mode_cmd->pixel_format);
386 for (i = 0; i < n; i++)
387 drm_gem_object_unreference_unlocked(bos[i]);
388 return fb;
389 }
390 return fb;
391}
392
393struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
394 struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
395{
396 struct omap_framebuffer *omap_fb;
397 struct drm_framebuffer *fb = NULL;
398 const struct format *format = NULL;
399 int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
400
401 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
402 dev, mode_cmd, mode_cmd->width, mode_cmd->height,
403 (char *)&mode_cmd->pixel_format);
404
405 for (i = 0; i < ARRAY_SIZE(formats); i++) {
406 if (formats[i].pixel_format == mode_cmd->pixel_format) {
407 format = &formats[i];
408 break;
409 }
410 }
411
412 if (!format) {
413 dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
414 (char *)&mode_cmd->pixel_format);
415 ret = -EINVAL;
416 goto fail;
417 }
418
419 omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
420 if (!omap_fb) {
421 dev_err(dev->dev, "could not allocate fb\n");
422 ret = -ENOMEM;
423 goto fail;
424 }
425
426 fb = &omap_fb->base;
427 omap_fb->format = format;
428
429 for (i = 0; i < n; i++) {
430 struct plane *plane = &omap_fb->planes[i];
431 int size, pitch = mode_cmd->pitches[i];
432
433 if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) {
434 dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n",
435 pitch, mode_cmd->width * format->planes[i].stride_bpp);
436 ret = -EINVAL;
437 goto fail;
438 }
439
440 size = pitch * mode_cmd->height / format->planes[i].sub_y;
441
442 if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
443 dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
444 bos[i]->size - mode_cmd->offsets[i], size);
445 ret = -EINVAL;
446 goto fail;
447 }
448
449 plane->bo = bos[i];
450 plane->offset = mode_cmd->offsets[i];
451 plane->pitch = pitch;
452 plane->paddr = 0;
453 }
454
455 drm_helper_mode_fill_fb_struct(fb, mode_cmd);
456
457 ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
458 if (ret) {
459 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
460 goto fail;
461 }
462
463 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
464
465 return fb;
466
467fail:
468 if (fb)
469 omap_framebuffer_destroy(fb);
470
471 return ERR_PTR(ret);
472}