2 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
3 * Author: Rob Clark <rob@ti.com>
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.
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
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/>.
18 #include <linux/seq_file.h>
20 #include <drm/drm_crtc.h>
21 #include <drm/drm_crtc_helper.h>
23 #include "omap_dmm_tiler.h"
30 static const u32 formats
[] = {
32 DRM_FORMAT_RGB565
, /* RGB16-565 */
33 DRM_FORMAT_RGBX4444
, /* RGB12x-4444 */
34 DRM_FORMAT_XRGB4444
, /* xRGB12-4444 */
35 DRM_FORMAT_RGBA4444
, /* RGBA12-4444 */
36 DRM_FORMAT_ARGB4444
, /* ARGB16-4444 */
37 DRM_FORMAT_XRGB1555
, /* xRGB15-1555 */
38 DRM_FORMAT_ARGB1555
, /* ARGB16-1555 */
40 DRM_FORMAT_RGB888
, /* RGB24-888 */
42 DRM_FORMAT_RGBX8888
, /* RGBx24-8888 */
43 DRM_FORMAT_XRGB8888
, /* xRGB24-8888 */
44 DRM_FORMAT_RGBA8888
, /* RGBA32-8888 */
45 DRM_FORMAT_ARGB8888
, /* ARGB32-8888 */
52 /* per-plane info for the fb: */
54 struct drm_gem_object
*bo
;
60 #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
62 struct omap_framebuffer
{
63 struct drm_framebuffer base
;
65 const struct drm_format_info
*format
;
66 struct plane planes
[2];
67 /* lock for pinning (pin_count and planes.dma_addr) */
71 static int omap_framebuffer_create_handle(struct drm_framebuffer
*fb
,
72 struct drm_file
*file_priv
,
75 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
76 return drm_gem_handle_create(file_priv
,
77 omap_fb
->planes
[0].bo
, handle
);
80 static void omap_framebuffer_destroy(struct drm_framebuffer
*fb
)
82 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
83 int i
, n
= fb
->format
->num_planes
;
85 DBG("destroy: FB ID: %d (%p)", fb
->base
.id
, fb
);
87 drm_framebuffer_cleanup(fb
);
89 for (i
= 0; i
< n
; i
++) {
90 struct plane
*plane
= &omap_fb
->planes
[i
];
92 drm_gem_object_unreference_unlocked(plane
->bo
);
98 static const struct drm_framebuffer_funcs omap_framebuffer_funcs
= {
99 .create_handle
= omap_framebuffer_create_handle
,
100 .destroy
= omap_framebuffer_destroy
,
103 static uint32_t get_linear_addr(struct plane
*plane
,
104 const struct drm_format_info
*format
, int n
, int x
, int y
)
108 offset
= plane
->offset
109 + (x
* format
->cpp
[n
] / (n
== 0 ? 1 : format
->hsub
))
110 + (y
* plane
->pitch
/ (n
== 0 ? 1 : format
->vsub
));
112 return plane
->dma_addr
+ offset
;
115 bool omap_framebuffer_supports_rotation(struct drm_framebuffer
*fb
)
117 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
118 struct plane
*plane
= &omap_fb
->planes
[0];
120 return omap_gem_flags(plane
->bo
) & OMAP_BO_TILED
;
123 /* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */
124 static uint32_t drm_rotation_to_tiler(unsigned int drm_rot
)
128 switch (drm_rot
& DRM_MODE_ROTATE_MASK
) {
130 case DRM_MODE_ROTATE_0
:
133 case DRM_MODE_ROTATE_90
:
134 orient
= MASK_XY_FLIP
| MASK_X_INVERT
;
136 case DRM_MODE_ROTATE_180
:
137 orient
= MASK_X_INVERT
| MASK_Y_INVERT
;
139 case DRM_MODE_ROTATE_270
:
140 orient
= MASK_XY_FLIP
| MASK_Y_INVERT
;
144 if (drm_rot
& DRM_MODE_REFLECT_X
)
145 orient
^= MASK_X_INVERT
;
147 if (drm_rot
& DRM_MODE_REFLECT_Y
)
148 orient
^= MASK_Y_INVERT
;
153 /* update ovl info for scanout, handles cases of multi-planar fb's, etc.
155 void omap_framebuffer_update_scanout(struct drm_framebuffer
*fb
,
156 struct drm_plane_state
*state
, struct omap_overlay_info
*info
)
158 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
159 const struct drm_format_info
*format
= omap_fb
->format
;
160 struct plane
*plane
= &omap_fb
->planes
[0];
161 uint32_t x
, y
, orient
= 0;
163 info
->fourcc
= fb
->format
->format
;
165 info
->pos_x
= state
->crtc_x
;
166 info
->pos_y
= state
->crtc_y
;
167 info
->out_width
= state
->crtc_w
;
168 info
->out_height
= state
->crtc_h
;
169 info
->width
= state
->src_w
>> 16;
170 info
->height
= state
->src_h
>> 16;
172 /* DSS driver wants the w & h in rotated orientation */
173 if (drm_rotation_90_or_270(state
->rotation
))
174 swap(info
->width
, info
->height
);
176 x
= state
->src_x
>> 16;
177 y
= state
->src_y
>> 16;
179 if (omap_gem_flags(plane
->bo
) & OMAP_BO_TILED
) {
180 uint32_t w
= state
->src_w
>> 16;
181 uint32_t h
= state
->src_h
>> 16;
183 orient
= drm_rotation_to_tiler(state
->rotation
);
186 * omap_gem_rotated_paddr() wants the x & y in tiler units.
187 * Usually tiler unit size is the same as the pixel size, except
188 * for YUV422 formats, for which the tiler unit size is 32 bits
189 * and pixel size is 16 bits.
191 if (fb
->format
->format
== DRM_FORMAT_UYVY
||
192 fb
->format
->format
== DRM_FORMAT_YUYV
) {
197 /* adjust x,y offset for invert: */
198 if (orient
& MASK_Y_INVERT
)
200 if (orient
& MASK_X_INVERT
)
203 /* Note: x and y are in TILER units, not pixels */
204 omap_gem_rotated_dma_addr(plane
->bo
, orient
, x
, y
,
206 info
->rotation_type
= OMAP_DSS_ROT_TILER
;
207 info
->rotation
= state
->rotation
?: DRM_MODE_ROTATE_0
;
208 /* Note: stride in TILER units, not pixels */
209 info
->screen_width
= omap_gem_tiled_stride(plane
->bo
, orient
);
211 switch (state
->rotation
& DRM_MODE_ROTATE_MASK
) {
213 case DRM_MODE_ROTATE_0
:
218 dev_warn(fb
->dev
->dev
,
219 "rotation '%d' ignored for non-tiled fb\n",
224 info
->paddr
= get_linear_addr(plane
, format
, 0, x
, y
);
225 info
->rotation_type
= OMAP_DSS_ROT_NONE
;
226 info
->rotation
= DRM_MODE_ROTATE_0
;
227 info
->screen_width
= plane
->pitch
;
230 /* convert to pixels: */
231 info
->screen_width
/= format
->cpp
[0];
233 if (fb
->format
->format
== DRM_FORMAT_NV12
) {
234 plane
= &omap_fb
->planes
[1];
236 if (info
->rotation_type
== OMAP_DSS_ROT_TILER
) {
237 WARN_ON(!(omap_gem_flags(plane
->bo
) & OMAP_BO_TILED
));
238 omap_gem_rotated_dma_addr(plane
->bo
, orient
, x
/2, y
/2,
241 info
->p_uv_addr
= get_linear_addr(plane
, format
, 1, x
, y
);
248 /* pin, prepare for scanout: */
249 int omap_framebuffer_pin(struct drm_framebuffer
*fb
)
251 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
252 int ret
, i
, n
= fb
->format
->num_planes
;
254 mutex_lock(&omap_fb
->lock
);
256 if (omap_fb
->pin_count
> 0) {
257 omap_fb
->pin_count
++;
258 mutex_unlock(&omap_fb
->lock
);
262 for (i
= 0; i
< n
; i
++) {
263 struct plane
*plane
= &omap_fb
->planes
[i
];
264 ret
= omap_gem_pin(plane
->bo
, &plane
->dma_addr
);
267 omap_gem_dma_sync_buffer(plane
->bo
, DMA_TO_DEVICE
);
270 omap_fb
->pin_count
++;
272 mutex_unlock(&omap_fb
->lock
);
277 for (i
--; i
>= 0; i
--) {
278 struct plane
*plane
= &omap_fb
->planes
[i
];
279 omap_gem_unpin(plane
->bo
);
283 mutex_unlock(&omap_fb
->lock
);
288 /* unpin, no longer being scanned out: */
289 void omap_framebuffer_unpin(struct drm_framebuffer
*fb
)
291 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
292 int i
, n
= fb
->format
->num_planes
;
294 mutex_lock(&omap_fb
->lock
);
296 omap_fb
->pin_count
--;
298 if (omap_fb
->pin_count
> 0) {
299 mutex_unlock(&omap_fb
->lock
);
303 for (i
= 0; i
< n
; i
++) {
304 struct plane
*plane
= &omap_fb
->planes
[i
];
305 omap_gem_unpin(plane
->bo
);
309 mutex_unlock(&omap_fb
->lock
);
312 /* iterate thru all the connectors, returning ones that are attached
315 struct drm_connector
*omap_framebuffer_get_next_connector(
316 struct drm_framebuffer
*fb
, struct drm_connector
*from
)
318 struct drm_device
*dev
= fb
->dev
;
319 struct list_head
*connector_list
= &dev
->mode_config
.connector_list
;
320 struct drm_connector
*connector
= from
;
323 return list_first_entry_or_null(connector_list
, typeof(*from
),
326 list_for_each_entry_from(connector
, connector_list
, head
) {
327 if (connector
!= from
) {
328 struct drm_encoder
*encoder
= connector
->encoder
;
329 struct drm_crtc
*crtc
= encoder
? encoder
->crtc
: NULL
;
330 if (crtc
&& crtc
->primary
->fb
== fb
)
339 #ifdef CONFIG_DEBUG_FS
340 void omap_framebuffer_describe(struct drm_framebuffer
*fb
, struct seq_file
*m
)
342 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
343 int i
, n
= fb
->format
->num_planes
;
345 seq_printf(m
, "fb: %dx%d@%4.4s\n", fb
->width
, fb
->height
,
346 (char *)&fb
->format
->format
);
348 for (i
= 0; i
< n
; i
++) {
349 struct plane
*plane
= &omap_fb
->planes
[i
];
350 seq_printf(m
, " %d: offset=%d pitch=%d, obj: ",
351 i
, plane
->offset
, plane
->pitch
);
352 omap_gem_describe(plane
->bo
, m
);
357 struct drm_framebuffer
*omap_framebuffer_create(struct drm_device
*dev
,
358 struct drm_file
*file
, const struct drm_mode_fb_cmd2
*mode_cmd
)
360 unsigned int num_planes
= drm_format_num_planes(mode_cmd
->pixel_format
);
361 struct drm_gem_object
*bos
[4];
362 struct drm_framebuffer
*fb
;
365 for (i
= 0; i
< num_planes
; i
++) {
366 bos
[i
] = drm_gem_object_lookup(file
, mode_cmd
->handles
[i
]);
368 fb
= ERR_PTR(-ENOENT
);
373 fb
= omap_framebuffer_init(dev
, mode_cmd
, bos
);
381 drm_gem_object_unreference_unlocked(bos
[i
]);
386 struct drm_framebuffer
*omap_framebuffer_init(struct drm_device
*dev
,
387 const struct drm_mode_fb_cmd2
*mode_cmd
, struct drm_gem_object
**bos
)
389 const struct drm_format_info
*format
= NULL
;
390 struct omap_framebuffer
*omap_fb
= NULL
;
391 struct drm_framebuffer
*fb
= NULL
;
392 unsigned int pitch
= mode_cmd
->pitches
[0];
395 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
396 dev
, mode_cmd
, mode_cmd
->width
, mode_cmd
->height
,
397 (char *)&mode_cmd
->pixel_format
);
399 format
= drm_format_info(mode_cmd
->pixel_format
);
401 for (i
= 0; i
< ARRAY_SIZE(formats
); i
++) {
402 if (formats
[i
] == mode_cmd
->pixel_format
)
406 if (!format
|| i
== ARRAY_SIZE(formats
)) {
407 dev_dbg(dev
->dev
, "unsupported pixel format: %4.4s\n",
408 (char *)&mode_cmd
->pixel_format
);
413 omap_fb
= kzalloc(sizeof(*omap_fb
), GFP_KERNEL
);
420 omap_fb
->format
= format
;
421 mutex_init(&omap_fb
->lock
);
424 * The code below assumes that no format use more than two planes, and
425 * that the two planes of multiplane formats need the same number of
428 if (format
->num_planes
== 2 && pitch
!= mode_cmd
->pitches
[1]) {
429 dev_dbg(dev
->dev
, "pitches differ between planes 0 and 1\n");
434 if (pitch
% format
->cpp
[0]) {
436 "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n",
437 pitch
, format
->cpp
[0]);
442 for (i
= 0; i
< format
->num_planes
; i
++) {
443 struct plane
*plane
= &omap_fb
->planes
[i
];
444 unsigned int vsub
= i
== 0 ? 1 : format
->vsub
;
447 size
= pitch
* mode_cmd
->height
/ vsub
;
449 if (size
> omap_gem_mmap_size(bos
[i
]) - mode_cmd
->offsets
[i
]) {
451 "provided buffer object is too small! %zu < %d\n",
452 bos
[i
]->size
- mode_cmd
->offsets
[i
], size
);
458 plane
->offset
= mode_cmd
->offsets
[i
];
459 plane
->pitch
= pitch
;
463 drm_helper_mode_fill_fb_struct(dev
, fb
, mode_cmd
);
465 ret
= drm_framebuffer_init(dev
, fb
, &omap_framebuffer_funcs
);
467 dev_err(dev
->dev
, "framebuffer init failed: %d\n", ret
);
471 DBG("create: FB ID: %d (%p)", fb
->base
.id
, fb
);