diff options
Diffstat (limited to 'drivers/video/omap2/dsscomp/base.c')
-rw-r--r-- | drivers/video/omap2/dsscomp/base.c | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/drivers/video/omap2/dsscomp/base.c b/drivers/video/omap2/dsscomp/base.c new file mode 100644 index 0000000..ad7ade6 --- /dev/null +++ b/drivers/video/omap2/dsscomp/base.c @@ -0,0 +1,504 @@ +/* + * 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); +} |