blob: ad7ade662237ad0c573d7330fe90d38178952f88 [file] [log] [blame]
/*
* linux/drivers/video/omap2/dsscomp/base.c
*
* DSS Composition basic operation support
*
* Copyright (C) 2011 Texas Instruments, Inc
* Author: Lajos Molnar <molnar@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <mach/tiler.h>
#include <video/omapdss.h>
#include <video/dsscomp.h>
#include <plat/dsscomp.h>
#include "dsscomp.h"
int debug;
module_param(debug, int, 0644);
/* color formats supported - bitfield info is used for truncation logic */
static const struct color_info {
int a_ix, a_bt; /* bitfields */
int r_ix, r_bt;
int g_ix, g_bt;
int b_ix, b_bt;
int x_bt;
enum omap_color_mode mode;
const char *name;
} fmts[2][16] = { {
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, OMAP_DSS_COLOR_CLUT1, "BITMAP1" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 2, OMAP_DSS_COLOR_CLUT2, "BITMAP2" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 4, OMAP_DSS_COLOR_CLUT4, "BITMAP4" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 8, OMAP_DSS_COLOR_CLUT8, "BITMAP8" },
{ 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
{ 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
{ 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
{ 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
"ARGB16-1555" },
{ 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
{ 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
{ 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
{ 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
{ 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
{ 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
{ 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
{ 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
"xRGB15-1555" },
}, {
{ 0, 0, 0, 0, 0, 0, 0, 0, 12, OMAP_DSS_COLOR_NV12, "NV12" },
{ 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
{ 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "invalid" },
{ 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
{ 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
{ 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
{ 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
"ARGB16-1555" },
{ 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
{ 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_YUV2, "YUYV" },
{ 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_UYVY, "UYVY" },
{ 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
{ 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
{ 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
{ 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
"xRGB15-1555" },
} };
static const struct color_info *get_color_info(enum omap_color_mode mode)
{
int i;
for (i = 0; i < sizeof(fmts) / sizeof(fmts[0][0]); i++)
if (fmts[0][i].mode == mode)
return fmts[0] + i;
return NULL;
}
static int color_mode_to_bpp(enum omap_color_mode color_mode)
{
const struct color_info *ci = get_color_info(color_mode);
BUG_ON(!ci);
return ci->a_bt + ci->r_bt + ci->g_bt + ci->b_bt + ci->x_bt;
}
#ifdef CONFIG_DEBUG_FS
const char *dsscomp_get_color_name(enum omap_color_mode m)
{
const struct color_info *ci = get_color_info(m);
return ci ? ci->name : NULL;
}
#endif
union rect {
struct {
s32 x;
s32 y;
s32 w;
s32 h;
};
struct {
s32 xy[2];
s32 wh[2];
};
struct dss2_rect_t r;
};
int crop_to_rect(union rect *crop, union rect *win, union rect *vis,
int rotation, int mirror)
{
int c, swap = rotation & 1;
/* align crop window with display coordinates */
if (swap)
crop->y -= (crop->h = -crop->h);
if (rotation & 2)
crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
if ((!mirror) ^ !(rotation & 2))
crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
for (c = 0; c < 2; c++) {
/* see if complete buffer is outside the vis or it is
fully cropped or scaled to 0 */
if (win->wh[c] <= 0 || vis->wh[c] <= 0 ||
win->xy[c] + win->wh[c] <= vis->xy[c] ||
win->xy[c] >= vis->xy[c] + vis->wh[c] ||
!crop->wh[c ^ swap])
return -ENOENT;
/* crop left/top */
if (win->xy[c] < vis->xy[c]) {
/* correction term */
int a = (vis->xy[c] - win->xy[c]) *
crop->wh[c ^ swap] / win->wh[c];
crop->xy[c ^ swap] += a;
crop->wh[c ^ swap] -= a;
win->wh[c] -= vis->xy[c] - win->xy[c];
win->xy[c] = vis->xy[c];
}
/* crop right/bottom */
if (win->xy[c] + win->wh[c] > vis->xy[c] + vis->wh[c]) {
crop->wh[c ^ swap] = crop->wh[c ^ swap] *
(vis->xy[c] + vis->wh[c] - win->xy[c]) /
win->wh[c];
win->wh[c] = vis->xy[c] + vis->wh[c] - win->xy[c];
}
if (!crop->wh[c ^ swap] || !win->wh[c])
return -ENOENT;
}
/* realign crop window to buffer coordinates */
if (rotation & 2)
crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
if ((!mirror) ^ !(rotation & 2))
crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
if (swap)
crop->y -= (crop->h = -crop->h);
return 0;
}
int set_dss_ovl_info(struct dss2_ovl_info *oi)
{
struct omap_overlay_info info;
struct omap_overlay *ovl;
struct dss2_ovl_cfg *cfg;
union rect crop, win, vis;
int c;
/* check overlay number */
if (!oi || oi->cfg.ix >= omap_dss_get_num_overlays())
return -EINVAL;
cfg = &oi->cfg;
ovl = omap_dss_get_overlay(cfg->ix);
/* just in case there are new fields, we get the current info */
ovl->get_overlay_info(ovl, &info);
info.enabled = cfg->enabled;
if (!cfg->enabled)
goto done;
/* copied params */
info.zorder = cfg->zorder;
if (cfg->zonly)
goto done;
info.global_alpha = cfg->global_alpha;
info.pre_mult_alpha = cfg->pre_mult_alpha;
info.rotation = cfg->rotation;
info.mirror = cfg->mirror;
info.color_mode = cfg->color_mode;
/* crop to screen */
crop.r = cfg->crop;
win.r = cfg->win;
vis.x = vis.y = 0;
vis.w = ovl->manager->device->panel.timings.x_res;
vis.h = ovl->manager->device->panel.timings.y_res;
if (crop_to_rect(&crop, &win, &vis, cfg->rotation, cfg->mirror) ||
vis.w < 2) {
info.enabled = false;
goto done;
}
/* adjust crop to UV pixel boundaries */
for (c = 0; c < (cfg->color_mode == OMAP_DSS_COLOR_NV12 ? 2 :
(cfg->color_mode &
(OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) ? 1 : 0); c++) {
/* keep the output window to avoid trembling edges */
crop.wh[c] += crop.xy[c] & 1; /* round down start */
crop.xy[c] &= ~1;
crop.wh[c] += crop.wh[c] & 1; /* round up end */
/*
* Buffer is aligned on UV pixel boundaries, so no
* worries about extending crop region.
*/
}
info.width = crop.w;
info.height = crop.h;
if (cfg->rotation & 1)
/* DISPC uses swapped height/width for 90/270 degrees */
swap(info.width, info.height);
info.pos_x = win.x;
info.pos_y = win.y;
info.out_width = win.w;
info.out_height = win.h;
/* calculate addresses and cropping */
info.paddr = oi->ba;
info.p_uv_addr = (info.color_mode == OMAP_DSS_COLOR_NV12) ? oi->uv : 0;
info.vaddr = NULL;
/* check for TILER 2D buffer */
if (info.paddr >= 0x60000000 && info.paddr < 0x78000000) {
int bpp = 1 << ((info.paddr >> 27) & 3);
struct tiler_view_t t;
/* crop to top-left */
/*
* DSS supports YUV422 on 32-bit mode, but its technically
* 2 bytes-per-pixel.
* Also RGB24-888 is 3 bytes-per-pixel even though no
* tiler pixel format matches this.
*/
if (cfg->color_mode &
(OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY))
bpp = 2;
else if (cfg->color_mode == OMAP_DSS_COLOR_RGB24P)
bpp = 3;
tilview_create(&t, info.paddr, cfg->width, cfg->height);
info.paddr -= t.tsptr;
tilview_crop(&t, 0, crop.y, cfg->width, crop.h);
info.paddr += t.tsptr + bpp * crop.x;
info.rotation_type = OMAP_DSS_ROT_TILER;
info.screen_width = 0;
/* for NV12 format also crop NV12 */
if (info.color_mode == OMAP_DSS_COLOR_NV12) {
tilview_create(&t, info.p_uv_addr,
cfg->width >> 1, cfg->height >> 1);
info.p_uv_addr -= t.tsptr;
tilview_crop(&t, 0, crop.y >> 1, cfg->width >> 1,
crop.h >> 1);
info.p_uv_addr += t.tsptr + bpp * crop.x;
}
} else {
/* program tiler 1D as SDMA */
int bpp = color_mode_to_bpp(cfg->color_mode);
info.screen_width = cfg->stride * 8 / (bpp == 12 ? 8 : bpp);
info.paddr += crop.x * (bpp / 8) + crop.y * cfg->stride;
/* for NV12 format also crop NV12 */
if (info.color_mode == OMAP_DSS_COLOR_NV12)
info.p_uv_addr += crop.x * (bpp / 8) +
(crop.y >> 1) * cfg->stride;
/* no rotation on DMA buffer */
if (cfg->rotation & 3 || cfg->mirror)
return -EINVAL;
info.rotation_type = OMAP_DSS_ROT_DMA;
}
info.max_x_decim = cfg->decim.max_x ? : 255;
info.max_y_decim = cfg->decim.max_y ? : 255;
info.min_x_decim = cfg->decim.min_x ? : 1;
info.min_y_decim = cfg->decim.min_y ? : 1;
#if 0
info.pic_height = cfg->height;
info.field = 0;
if (cfg->ilace & OMAP_DSS_ILACE_SEQ)
info.field |= OMAP_FLAG_IBUF;
if (cfg->ilace & OMAP_DSS_ILACE_SWAP)
info.field |= OMAP_FLAG_ISWAP;
/*
* Ignore OMAP_DSS_ILACE as there is no real support yet for
* interlaced interleaved vs progressive buffers
*/
if (ovl->manager &&
ovl->manager->device &&
!strcmp(ovl->manager->device->name, "hdmi") &&
is_hdmi_interlaced())
info.field |= OMAP_FLAG_IDEV;
info.out_wb = 0;
#endif
info.cconv = cfg->cconv;
done:
#if 0
pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
"mir=%d col=%x z=%d al=%02x prem=%d pich=%d ilace=%d\n",
ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
info.height, info.screen_width, info.out_width, info.out_height,
info.pos_x, info.pos_y, info.rotation, info.mirror,
info.color_mode, info.zorder, info.global_alpha,
info.pre_mult_alpha, info.pic_height, info.field);
#else
pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
"mir=%d col=%x z=%d al=%02x prem=%d\n",
ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
info.height, info.screen_width, info.out_width, info.out_height,
info.pos_x, info.pos_y, info.rotation, info.mirror,
info.color_mode, info.zorder, info.global_alpha,
info.pre_mult_alpha);
#endif
/* set overlay info */
return ovl->set_overlay_info(ovl, &info);
}
void swap_rb_in_ovl_info(struct dss2_ovl_info *oi)
{
/* we need to swap YUV color matrix if we are swapping R and B */
if (oi->cfg.color_mode &
(OMAP_DSS_COLOR_NV12 | OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) {
swap(oi->cfg.cconv.ry, oi->cfg.cconv.by);
swap(oi->cfg.cconv.rcr, oi->cfg.cconv.bcr);
swap(oi->cfg.cconv.rcb, oi->cfg.cconv.bcb);
}
}
struct omap_overlay_manager *find_dss_mgr(int display_ix)
{
struct omap_overlay_manager *mgr;
char name[32];
int i;
sprintf(name, "display%d", display_ix);
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
mgr = omap_dss_get_overlay_manager(i);
if (mgr->device && !strcmp(name, dev_name(&mgr->device->dev)))
return mgr;
}
return NULL;
}
int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb)
{
struct omap_overlay_manager_info info;
struct omap_overlay_manager *mgr;
if (!mi)
return -EINVAL;
mgr = find_dss_mgr(mi->ix);
if (!mgr)
return -EINVAL;
/* just in case there are new fields, we get the current info */
mgr->get_manager_info(mgr, &info);
info.alpha_enabled = mi->alpha_blending;
info.default_color = mi->default_color;
info.trans_enabled = mi->trans_enabled && !mi->alpha_blending;
info.trans_key = mi->trans_key;
info.trans_key_type = mi->trans_key_type;
info.cpr_coefs = mi->cpr_coefs;
info.cpr_enable = mi->cpr_enabled;
info.cb = *cb;
return mgr->set_manager_info(mgr, &info);
}
void swap_rb_in_mgr_info(struct dss2_mgr_info *mi)
{
const struct omap_dss_cpr_coefs c = { 256, 0, 0, 0, 256, 0, 0, 0, 256 };
/* set default CPR */
if (!mi->cpr_enabled)
mi->cpr_coefs = c;
mi->cpr_enabled = true;
/* swap red and blue */
swap(mi->cpr_coefs.rr, mi->cpr_coefs.br);
swap(mi->cpr_coefs.rg, mi->cpr_coefs.bg);
swap(mi->cpr_coefs.rb, mi->cpr_coefs.bb);
}
/*
* ===========================================================================
* DEBUG METHODS
* ===========================================================================
*/
void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi)
{
struct dss2_ovl_cfg *c = &oi->cfg;
const struct color_info *ci;
if (!(debug & DEBUG_OVERLAYS) ||
!(debug & DEBUG_COMPOSITIONS))
return;
ci = get_color_info(c->color_mode);
if (c->zonly) {
dev_info(DEV(cdev), "ovl%d(%s z%d)\n",
c->ix, c->enabled ? "ON" : "off", c->zorder);
return;
}
dev_info(DEV(cdev), "ovl%d(%s z%d %s%s *%d%% %d*%d:%d,%d+%d,%d rot%d%s"
" => %d,%d+%d,%d %p/%p|%d)\n",
c->ix, c->enabled ? "ON" : "off", c->zorder,
ci->name ? : "(none)",
c->pre_mult_alpha ? " premult" : "",
(c->global_alpha * 100 + 128) / 255,
c->width, c->height, c->crop.x, c->crop.y,
c->crop.w, c->crop.h,
c->rotation, c->mirror ? "+mir" : "",
c->win.x, c->win.y, c->win.w, c->win.h,
(void *) oi->ba, (void *) oi->uv, c->stride);
}
static void print_mgr_info(struct dsscomp_dev *cdev,
struct dss2_mgr_info *mi)
{
printk("(dis%d(%s) alpha=%d col=%08x ilace=%d) ",
mi->ix,
(mi->ix < cdev->num_displays && cdev->displays[mi->ix]) ?
cdev->displays[mi->ix]->name : "NONE",
mi->alpha_blending, mi->default_color,
mi->interlaced);
}
void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
const char *phase)
{
if (!(debug & DEBUG_COMPOSITIONS))
return;
dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
*phase == 'q' ? (void *) d->sync_id : d, phase,
(d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
(d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
(d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
print_mgr_info(cdev, &d->mgr);
printk("n=%d\n", d->num_ovls);
}
void dump_total_comp_info(struct dsscomp_dev *cdev,
struct dsscomp_setup_dispc_data *d,
const char *phase)
{
int i;
if (!(debug & DEBUG_COMPOSITIONS))
return;
dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
*phase == 'q' ? (void *) d->sync_id : d, phase,
(d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
(d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
(d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
for (i = 0; i < d->num_mgrs && i < ARRAY_SIZE(d->mgrs); i++)
print_mgr_info(cdev, d->mgrs + i);
printk("n=%d\n", d->num_ovls);
}