2 * drivers/staging/omapdrm/omap_fb.c
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
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.
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
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/>.
23 #include "drm_crtc_helper.h"
29 /* per-format info: */
31 enum omap_color_mode dss_format
;
32 uint32_t pixel_format
;
34 int stride_bpp
; /* this times width is stride */
35 int sub_y
; /* sub-sample in y dimension */
40 static const struct format formats
[] = {
42 { OMAP_DSS_COLOR_RGB16
, DRM_FORMAT_RGB565
, {{2, 1}}, false }, /* RGB16-565 */
43 { OMAP_DSS_COLOR_RGB12U
, DRM_FORMAT_RGBX4444
, {{2, 1}}, false }, /* RGB12x-4444 */
44 { OMAP_DSS_COLOR_RGBX16
, DRM_FORMAT_XRGB4444
, {{2, 1}}, false }, /* xRGB12-4444 */
45 { OMAP_DSS_COLOR_RGBA16
, DRM_FORMAT_RGBA4444
, {{2, 1}}, false }, /* RGBA12-4444 */
46 { OMAP_DSS_COLOR_ARGB16
, DRM_FORMAT_ARGB4444
, {{2, 1}}, false }, /* ARGB16-4444 */
47 { OMAP_DSS_COLOR_XRGB16_1555
, DRM_FORMAT_XRGB1555
, {{2, 1}}, false }, /* xRGB15-1555 */
48 { OMAP_DSS_COLOR_ARGB16_1555
, DRM_FORMAT_ARGB1555
, {{2, 1}}, false }, /* ARGB16-1555 */
50 { OMAP_DSS_COLOR_RGB24P
, DRM_FORMAT_RGB888
, {{3, 1}}, false }, /* RGB24-888 */
52 { OMAP_DSS_COLOR_RGBX32
, DRM_FORMAT_RGBX8888
, {{4, 1}}, false }, /* RGBx24-8888 */
53 { OMAP_DSS_COLOR_RGB24U
, DRM_FORMAT_XRGB8888
, {{4, 1}}, false }, /* xRGB24-8888 */
54 { OMAP_DSS_COLOR_RGBA32
, DRM_FORMAT_RGBA8888
, {{4, 1}}, false }, /* RGBA32-8888 */
55 { OMAP_DSS_COLOR_ARGB32
, DRM_FORMAT_ARGB8888
, {{4, 1}}, false }, /* ARGB32-8888 */
57 { OMAP_DSS_COLOR_NV12
, DRM_FORMAT_NV12
, {{1, 1}, {1, 2}}, true },
58 { OMAP_DSS_COLOR_YUV2
, DRM_FORMAT_YUYV
, {{2, 1}}, true },
59 { OMAP_DSS_COLOR_UYVY
, DRM_FORMAT_UYVY
, {{2, 1}}, true },
62 /* per-plane info for the fb: */
64 struct drm_gem_object
*bo
;
70 #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
72 struct omap_framebuffer
{
73 struct drm_framebuffer base
;
74 const struct format
*format
;
75 struct plane planes
[4];
78 static int omap_framebuffer_create_handle(struct drm_framebuffer
*fb
,
79 struct drm_file
*file_priv
,
82 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
83 return drm_gem_handle_create(file_priv
,
84 omap_fb
->planes
[0].bo
, handle
);
87 static void omap_framebuffer_destroy(struct drm_framebuffer
*fb
)
89 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
90 int i
, n
= drm_format_num_planes(omap_fb
->format
->pixel_format
);
92 DBG("destroy: FB ID: %d (%p)", fb
->base
.id
, fb
);
94 drm_framebuffer_cleanup(fb
);
96 for (i
= 0; i
< n
; i
++) {
97 struct plane
*plane
= &omap_fb
->planes
[i
];
99 drm_gem_object_unreference_unlocked(plane
->bo
);
105 static int omap_framebuffer_dirty(struct drm_framebuffer
*fb
,
106 struct drm_file
*file_priv
, unsigned flags
, unsigned color
,
107 struct drm_clip_rect
*clips
, unsigned num_clips
)
111 for (i
= 0; i
< num_clips
; i
++) {
112 omap_framebuffer_flush(fb
, clips
[i
].x1
, clips
[i
].y1
,
113 clips
[i
].x2
- clips
[i
].x1
,
114 clips
[i
].y2
- clips
[i
].y1
);
120 static const struct drm_framebuffer_funcs omap_framebuffer_funcs
= {
121 .create_handle
= omap_framebuffer_create_handle
,
122 .destroy
= omap_framebuffer_destroy
,
123 .dirty
= omap_framebuffer_dirty
,
126 /* pins buffer in preparation for scanout */
127 int omap_framebuffer_pin(struct drm_framebuffer
*fb
)
129 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
130 int ret
, i
, n
= drm_format_num_planes(omap_fb
->format
->pixel_format
);
132 for (i
= 0; i
< n
; i
++) {
133 struct plane
*plane
= &omap_fb
->planes
[i
];
134 ret
= omap_gem_get_paddr(plane
->bo
, &plane
->paddr
, true);
143 struct plane
*plane
= &omap_fb
->planes
[i
];
144 omap_gem_put_paddr(plane
->bo
);
149 /* releases buffer when done with scanout */
150 void omap_framebuffer_unpin(struct drm_framebuffer
*fb
)
152 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
153 int i
, n
= drm_format_num_planes(omap_fb
->format
->pixel_format
);
155 for (i
= 0; i
< n
; i
++) {
156 struct plane
*plane
= &omap_fb
->planes
[i
];
157 omap_gem_put_paddr(plane
->bo
);
161 /* update ovl info for scanout, handles cases of multi-planar fb's, etc.
163 void omap_framebuffer_update_scanout(struct drm_framebuffer
*fb
, int x
, int y
,
164 struct omap_overlay_info
*info
)
166 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
167 const struct format
*format
= omap_fb
->format
;
168 struct plane
*plane
= &omap_fb
->planes
[0];
171 offset
= plane
->offset
+
172 (x
* format
->planes
[0].stride_bpp
) +
173 (y
* plane
->pitch
/ format
->planes
[0].sub_y
);
175 info
->color_mode
= format
->dss_format
;
176 info
->paddr
= plane
->paddr
+ offset
;
177 info
->screen_width
= plane
->pitch
/ format
->planes
[0].stride_bpp
;
179 if (format
->dss_format
== OMAP_DSS_COLOR_NV12
) {
180 plane
= &omap_fb
->planes
[1];
181 offset
= plane
->offset
+
182 (x
* format
->planes
[1].stride_bpp
) +
183 (y
* plane
->pitch
/ format
->planes
[1].sub_y
);
184 info
->p_uv_addr
= plane
->paddr
+ offset
;
190 struct drm_gem_object
*omap_framebuffer_bo(struct drm_framebuffer
*fb
, int p
)
192 struct omap_framebuffer
*omap_fb
= to_omap_framebuffer(fb
);
193 if (p
>= drm_format_num_planes(omap_fb
->format
->pixel_format
))
195 return omap_fb
->planes
[p
].bo
;
198 /* iterate thru all the connectors, returning ones that are attached
201 struct drm_connector
*omap_framebuffer_get_next_connector(
202 struct drm_framebuffer
*fb
, struct drm_connector
*from
)
204 struct drm_device
*dev
= fb
->dev
;
205 struct list_head
*connector_list
= &dev
->mode_config
.connector_list
;
206 struct drm_connector
*connector
= from
;
209 return list_first_entry(connector_list
, typeof(*from
), head
);
212 list_for_each_entry_from(connector
, connector_list
, head
) {
213 if (connector
!= from
) {
214 struct drm_encoder
*encoder
= connector
->encoder
;
215 struct drm_crtc
*crtc
= encoder
? encoder
->crtc
: NULL
;
216 if (crtc
&& crtc
->fb
== fb
) {
225 /* flush an area of the framebuffer (in case of manual update display that
226 * is not automatically flushed)
228 void omap_framebuffer_flush(struct drm_framebuffer
*fb
,
229 int x
, int y
, int w
, int h
)
231 struct drm_connector
*connector
= NULL
;
233 VERB("flush: %d,%d %dx%d, fb=%p", x
, y
, w
, h
, fb
);
235 while ((connector
= omap_framebuffer_get_next_connector(fb
, connector
))) {
236 /* only consider connectors that are part of a chain */
237 if (connector
->encoder
&& connector
->encoder
->crtc
) {
238 /* TODO: maybe this should propagate thru the crtc who
239 * could do the coordinate translation..
241 struct drm_crtc
*crtc
= connector
->encoder
->crtc
;
242 int cx
= max(0, x
- crtc
->x
);
243 int cy
= max(0, y
- crtc
->y
);
244 int cw
= w
+ (x
- crtc
->x
) - cx
;
245 int ch
= h
+ (y
- crtc
->y
) - cy
;
247 omap_connector_flush(connector
, cx
, cy
, cw
, ch
);
252 struct drm_framebuffer
*omap_framebuffer_create(struct drm_device
*dev
,
253 struct drm_file
*file
, struct drm_mode_fb_cmd2
*mode_cmd
)
255 struct drm_gem_object
*bos
[4];
256 struct drm_framebuffer
*fb
;
259 ret
= objects_lookup(dev
, file
, mode_cmd
->pixel_format
,
260 bos
, mode_cmd
->handles
);
264 fb
= omap_framebuffer_init(dev
, mode_cmd
, bos
);
266 int i
, n
= drm_format_num_planes(mode_cmd
->pixel_format
);
267 for (i
= 0; i
< n
; i
++)
268 drm_gem_object_unreference_unlocked(bos
[i
]);
274 struct drm_framebuffer
*omap_framebuffer_init(struct drm_device
*dev
,
275 struct drm_mode_fb_cmd2
*mode_cmd
, struct drm_gem_object
**bos
)
277 struct omap_framebuffer
*omap_fb
;
278 struct drm_framebuffer
*fb
= NULL
;
279 const struct format
*format
= NULL
;
280 int ret
, i
, n
= drm_format_num_planes(mode_cmd
->pixel_format
);
282 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
283 dev
, mode_cmd
, mode_cmd
->width
, mode_cmd
->height
,
284 (char *)&mode_cmd
->pixel_format
);
286 for (i
= 0; i
< ARRAY_SIZE(formats
); i
++) {
287 if (formats
[i
].pixel_format
== mode_cmd
->pixel_format
) {
288 format
= &formats
[i
];
294 dev_err(dev
->dev
, "unsupported pixel format: %4.4s\n",
295 (char *)&mode_cmd
->pixel_format
);
300 omap_fb
= kzalloc(sizeof(*omap_fb
), GFP_KERNEL
);
302 dev_err(dev
->dev
, "could not allocate fb\n");
308 ret
= drm_framebuffer_init(dev
, fb
, &omap_framebuffer_funcs
);
310 dev_err(dev
->dev
, "framebuffer init failed: %d\n", ret
);
314 DBG("create: FB ID: %d (%p)", fb
->base
.id
, fb
);
316 omap_fb
->format
= format
;
318 for (i
= 0; i
< n
; i
++) {
319 struct plane
*plane
= &omap_fb
->planes
[i
];
320 int size
, pitch
= mode_cmd
->pitches
[i
];
322 if (pitch
< (mode_cmd
->width
* format
->planes
[i
].stride_bpp
)) {
323 dev_err(dev
->dev
, "provided buffer pitch is too small! %d < %d\n",
324 pitch
, mode_cmd
->width
* format
->planes
[i
].stride_bpp
);
329 size
= pitch
* mode_cmd
->height
/ format
->planes
[i
].sub_y
;
331 if (size
> (bos
[i
]->size
- mode_cmd
->offsets
[i
])) {
332 dev_err(dev
->dev
, "provided buffer object is too small! %d < %d\n",
333 bos
[i
]->size
- mode_cmd
->offsets
[i
], size
);
339 plane
->offset
= mode_cmd
->offsets
[i
];
340 plane
->pitch
= mode_cmd
->pitches
[i
];
341 plane
->paddr
= pitch
;
344 drm_helper_mode_fill_fb_struct(fb
, mode_cmd
);
350 omap_framebuffer_destroy(fb
);