Commit 9a0774e0 authored by Rob Clark's avatar Rob Clark Committed by Greg Kroah-Hartman

staging: drm/omap: multiplanar and YUV support

Add support in framebuffer objects for other color formats and multi-
planar YUV (NV12).  Since this requires changing the API between the
plane and fb for getting scanout information (paddr, etc), take
advantage of the opportunity and put in place a way to allow fb's to
be unpinned when they are not being scanned out.  Now, before start
of scanout the plane calls omap_framebuffer_pin() which takes care
to pin all the backing bo's, then omap_framebuffer_update_scanout()
however many times to update the scanout address(es), etc, and then
when finished omap_framebuffer_unpin().
Signed-off-by: default avatarRob Clark <rob@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent bb5c2d9a
...@@ -168,7 +168,7 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, ...@@ -168,7 +168,7 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
omap_crtc->event = event; omap_crtc->event = event;
crtc->fb = fb; crtc->fb = fb;
omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ,
page_flip_cb, crtc); page_flip_cb, crtc);
return 0; return 0;
......
...@@ -90,9 +90,11 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, ...@@ -90,9 +90,11 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd); struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd);
struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p);
int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, int omap_framebuffer_pin(struct drm_framebuffer *fb);
void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); void omap_framebuffer_unpin(struct drm_framebuffer *fb);
void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
struct omap_overlay_info *info);
struct drm_connector *omap_framebuffer_get_next_connector( struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_framebuffer *fb, struct drm_connector *from); struct drm_framebuffer *fb, struct drm_connector *from);
void omap_framebuffer_flush(struct drm_framebuffer *fb, void omap_framebuffer_flush(struct drm_framebuffer *fb,
......
...@@ -59,14 +59,20 @@ static const struct format formats[] = { ...@@ -59,14 +59,20 @@ static const struct format formats[] = {
{ OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true },
}; };
/* per-plane info for the fb: */
struct plane {
struct drm_gem_object *bo;
uint32_t pitch;
uint32_t offset;
dma_addr_t paddr;
};
#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
struct omap_framebuffer { struct omap_framebuffer {
struct drm_framebuffer base; struct drm_framebuffer base;
struct drm_gem_object *bo;
int size;
dma_addr_t paddr;
const struct format *format; const struct format *format;
struct plane planes[4];
}; };
static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
...@@ -74,22 +80,23 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, ...@@ -74,22 +80,23 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
unsigned int *handle) unsigned int *handle)
{ {
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
return drm_gem_handle_create(file_priv, omap_fb->bo, handle); return drm_gem_handle_create(file_priv,
omap_fb->planes[0].bo, handle);
} }
static void omap_framebuffer_destroy(struct drm_framebuffer *fb) static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
{ {
struct drm_device *dev = fb->dev;
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
int i, n = drm_format_num_planes(omap_fb->format->pixel_format);
DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
drm_framebuffer_cleanup(fb); drm_framebuffer_cleanup(fb);
if (omap_fb->bo) { for (i = 0; i < n; i++) {
if (omap_fb->paddr && omap_gem_put_paddr(omap_fb->bo)) struct plane *plane = &omap_fb->planes[i];
dev_err(dev->dev, "could not unmap!\n"); if (plane->bo)
drm_gem_object_unreference_unlocked(omap_fb->bo); drm_gem_object_unreference_unlocked(plane->bo);
} }
kfree(omap_fb); kfree(omap_fb);
...@@ -116,37 +123,76 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { ...@@ -116,37 +123,76 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
.dirty = omap_framebuffer_dirty, .dirty = omap_framebuffer_dirty,
}; };
/* returns the buffer size */ /* pins buffer in preparation for scanout */
int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, int omap_framebuffer_pin(struct drm_framebuffer *fb)
void **vaddr, dma_addr_t *paddr, unsigned int *screen_width)
{ {
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
int bpp = fb->bits_per_pixel / 8; int ret, i, n = drm_format_num_planes(omap_fb->format->pixel_format);
unsigned long offset;
for (i = 0; i < n; i++) {
offset = (x * bpp) + (y * fb->pitches[0]); struct plane *plane = &omap_fb->planes[i];
ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true);
if (vaddr) { if (ret)
void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); goto fail;
/* note: we can only count on having a vaddr for buffers that
* are allocated physically contiguously to begin with (ie.
* dma_alloc_coherent()). But this should be ok because it
* is only used by legacy fbdev
*/
BUG_ON(IS_ERR_OR_NULL(bo_vaddr));
*vaddr = bo_vaddr + offset;
} }
*paddr = omap_fb->paddr + offset; return 0;
*screen_width = fb->pitches[0] / bpp;
return omap_fb->size - offset; fail:
while (--i > 0) {
struct plane *plane = &omap_fb->planes[i];
omap_gem_put_paddr(plane->bo);
}
return ret;
} }
struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb) /* releases buffer when done with scanout */
void omap_framebuffer_unpin(struct drm_framebuffer *fb)
{ {
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
return omap_fb->bo; int i, n = drm_format_num_planes(omap_fb->format->pixel_format);
for (i = 0; i < n; i++) {
struct plane *plane = &omap_fb->planes[i];
omap_gem_put_paddr(plane->bo);
}
}
/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
*/
void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
struct omap_overlay_info *info)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
const struct format *format = omap_fb->format;
struct plane *plane = &omap_fb->planes[0];
unsigned int offset;
offset = plane->offset +
(x * format->planes[0].stride_bpp) +
(y * plane->pitch / format->planes[0].sub_y);
info->color_mode = format->dss_format;
info->paddr = plane->paddr + offset;
info->screen_width = plane->pitch / format->planes[0].stride_bpp;
if (format->dss_format == OMAP_DSS_COLOR_NV12) {
plane = &omap_fb->planes[1];
offset = plane->offset +
(x * format->planes[1].stride_bpp) +
(y * plane->pitch / format->planes[1].sub_y);
info->p_uv_addr = plane->paddr + offset;
} else {
info->p_uv_addr = 0;
}
}
struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
if (p >= drm_format_num_planes(omap_fb->format->pixel_format))
return NULL;
return omap_fb->planes[p].bo;
} }
/* iterate thru all the connectors, returning ones that are attached /* iterate thru all the connectors, returning ones that are attached
...@@ -231,7 +277,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, ...@@ -231,7 +277,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
struct omap_framebuffer *omap_fb; struct omap_framebuffer *omap_fb;
struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fb = NULL;
const struct format *format = NULL; const struct format *format = NULL;
int i, size, ret; int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height, dev, mode_cmd, mode_cmd->width, mode_cmd->height,
...@@ -251,10 +297,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, ...@@ -251,10 +297,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
goto fail; goto fail;
} }
/* in case someone tries to feed us a completely bogus stride: */
mode_cmd->pitches[0] = align_pitch(mode_cmd->pitches[0],
mode_cmd->width, format->planes[0].stride_bpp);
omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
if (!omap_fb) { if (!omap_fb) {
dev_err(dev->dev, "could not allocate fb\n"); dev_err(dev->dev, "could not allocate fb\n");
...@@ -271,21 +313,32 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, ...@@ -271,21 +313,32 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
DBG("create: FB ID: %d (%p)", fb->base.id, fb); DBG("create: FB ID: %d (%p)", fb->base.id, fb);
size = PAGE_ALIGN(mode_cmd->pitches[0] * mode_cmd->height); omap_fb->format = format;
if (size > bos[0]->size) { for (i = 0; i < n; i++) {
dev_err(dev->dev, "provided buffer object is too small!\n"); struct plane *plane = &omap_fb->planes[i];
ret = -EINVAL; int size, pitch = mode_cmd->pitches[i];
goto fail;
}
omap_fb->bo = bos[0]; if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) {
omap_fb->size = size; dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n",
pitch, mode_cmd->width * format->planes[i].stride_bpp);
ret = -EINVAL;
goto fail;
}
ret = omap_gem_get_paddr(bos[0], &omap_fb->paddr, true); size = pitch * mode_cmd->height / format->planes[i].sub_y;
if (ret) {
dev_err(dev->dev, "could not map (paddr)!\n"); if (size > (bos[i]->size - mode_cmd->offsets[i])) {
goto fail; dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
bos[i]->size - mode_cmd->offsets[i], size);
ret = -EINVAL;
goto fail;
}
plane->bo = bos[i];
plane->offset = mode_cmd->offsets[i];
plane->pitch = mode_cmd->pitches[i];
plane->paddr = pitch;
} }
drm_helper_mode_fill_fb_struct(fb, mode_cmd); drm_helper_mode_fill_fb_struct(fb, mode_cmd);
......
...@@ -1034,6 +1034,11 @@ void omap_gem_free_object(struct drm_gem_object *obj) ...@@ -1034,6 +1034,11 @@ void omap_gem_free_object(struct drm_gem_object *obj)
drm_gem_free_mmap_offset(obj); drm_gem_free_mmap_offset(obj);
} }
/* this means the object is still pinned.. which really should
* not happen. I think..
*/
WARN_ON(omap_obj->paddr_cnt > 0);
/* don't free externally allocated backing memory */ /* don't free externally allocated backing memory */
if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) { if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
if (omap_obj->pages) { if (omap_obj->pages) {
......
...@@ -40,6 +40,9 @@ struct omap_plane { ...@@ -40,6 +40,9 @@ struct omap_plane {
* fractional positions: * fractional positions:
*/ */
unsigned int src_x, src_y; unsigned int src_x, src_y;
/* last fb that we pinned: */
struct drm_framebuffer *pinned_fb;
}; };
...@@ -55,7 +58,8 @@ static int commit(struct drm_plane *plane) ...@@ -55,7 +58,8 @@ static int commit(struct drm_plane *plane)
DBG("%s", ovl->name); DBG("%s", ovl->name);
DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
info->out_height, info->screen_width); info->out_height, info->screen_width);
DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
info->paddr, info->p_uv_addr);
/* NOTE: do we want to do this at all here, or just wait /* NOTE: do we want to do this at all here, or just wait
* for dpms(ON) since other CRTC's may not have their mode * for dpms(ON) since other CRTC's may not have their mode
...@@ -133,6 +137,23 @@ static void update_manager(struct drm_plane *plane) ...@@ -133,6 +137,23 @@ static void update_manager(struct drm_plane *plane)
} }
} }
/* update which fb (if any) is pinned for scanout */
static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
int ret = 0;
if (omap_plane->pinned_fb != fb) {
if (omap_plane->pinned_fb)
omap_framebuffer_unpin(omap_plane->pinned_fb);
omap_plane->pinned_fb = fb;
if (fb)
ret = omap_framebuffer_pin(fb);
}
return ret;
}
/* update parameters that are dependent on the framebuffer dimensions and /* update parameters that are dependent on the framebuffer dimensions and
* position within the fb that this plane scans out from. This is called * position within the fb that this plane scans out from. This is called
* when framebuffer or x,y base may have changed. * when framebuffer or x,y base may have changed.
...@@ -140,19 +161,23 @@ static void update_manager(struct drm_plane *plane) ...@@ -140,19 +161,23 @@ static void update_manager(struct drm_plane *plane)
static void update_scanout(struct drm_plane *plane) static void update_scanout(struct drm_plane *plane)
{ {
struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_plane *omap_plane = to_omap_plane(plane);
unsigned int screen_width; /* really means "pitch" */ struct omap_overlay_info *info = &omap_plane->info;
dma_addr_t paddr; int ret;
omap_framebuffer_get_buffer(plane->fb, ret = update_pin(plane, plane->fb);
omap_plane->src_x, omap_plane->src_y, if (ret) {
NULL, &paddr, &screen_width); dev_err(plane->dev->dev,
"could not pin fb: %d\n", ret);
omap_plane->info.enabled = false;
}
DBG("%s: %d,%d: %08x (%d)", omap_plane->ovl->name, omap_framebuffer_update_scanout(plane->fb,
omap_plane->src_x, omap_plane->src_y, omap_plane->src_x, omap_plane->src_y, info);
(u32)paddr, screen_width);
omap_plane->info.paddr = paddr; DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
omap_plane->info.screen_width = screen_width; omap_plane->src_x, omap_plane->src_y,
(u32)info->paddr, (u32)info->p_uv_addr,
info->screen_width);
} }
static int omap_plane_update(struct drm_plane *plane, static int omap_plane_update(struct drm_plane *plane,
...@@ -219,6 +244,7 @@ int omap_plane_dpms(struct drm_plane *plane, int mode) ...@@ -219,6 +244,7 @@ int omap_plane_dpms(struct drm_plane *plane, int mode)
omap_plane->info.enabled = true; omap_plane->info.enabled = true;
} else { } else {
omap_plane->info.enabled = false; omap_plane->info.enabled = false;
update_pin(plane, NULL);
} }
return commit(plane); return commit(plane);
...@@ -291,11 +317,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, ...@@ -291,11 +317,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
else else
omap_plane->info.zorder = 1; omap_plane->info.zorder = 1;
/* TODO color mode should come from fb.. this will come in a
* subsequent patch
*/
omap_plane->info.color_mode = OMAP_DSS_COLOR_RGB24U;
update_manager(plane); update_manager(plane);
return plane; return plane;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment