diff options
Diffstat (limited to 'Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c')
-rwxr-xr-x | Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c b/Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c new file mode 100755 index 0000000..890b9bc --- /dev/null +++ b/Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c @@ -0,0 +1,560 @@ +/* + Copyright (c) 2003 Andreas Robinson, All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. +*/ + +#include <config.h> + +#include <direct/messages.h> + +#include "unichrome.h" +#include "uc_overlay.h" +#include "vidregs.h" +#include "mmio.h" +#include <math.h> + +/** + * Map hw settings for vertical scaling. + * + * @param sh source height + * @param dh destination height + * @param zoom will hold vertical setting of zoom register. + * @param mini will hold vertical setting of mini register. + * + * @returns true if successful. + * false if the zooming factor is too large or small. + * + * @note Derived from VIA's V4L driver. + * See ddover.c, DDOVER_HQVCalcZoomHeight() + */ + +bool uc_ovl_map_vzoom(int sh, int dh, u32* zoom, u32* mini) +{ + u32 sh1, tmp, d; + bool zoom_ok = true; + + if (sh == dh) { // No zoom + // Do nothing + } + else if (sh < dh) { // Zoom in + + tmp = (sh * 0x0400) / dh; + zoom_ok = !(tmp > 0x3ff); + + *zoom |= (tmp & 0x3ff) | V1_Y_ZOOM_ENABLE; + *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY; + } + else { // sw > dh - Zoom out + + // Find a suitable divider (1 << d) = {2, 4, 8 or 16} + + sh1 = sh; + for (d = 1; d < 5; d++) { + sh1 >>= 1; + if (sh1 <= dh) break; + } + if (d == 5) { // Too small. + d = 4; + zoom_ok = false; + } + + *mini |= ((d<<1)-1) << 16; // <= {1,3,5,7} << 16 + + // Add scaling + + if (sh1 < dh) { + tmp = (sh1 * 0x400) / dh; + *zoom |= ((tmp & 0x3ff) | V1_Y_ZOOM_ENABLE); + *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY; + } + } + + return zoom_ok; +} + + +/** + * Map hw settings for horizontal scaling. + * + * @param sw source width + * @param dw destination width + * + * @param zoom will hold horizontal setting of zoom register. + * @param mini will hold horizontal setting of mini register. + * @param falign will hold fetch aligment + * @param dcount will hold display count + * + * @returns true if successful. + * false if the zooming factor is too large or small. + * + * @note Derived from VIA's V4L driver. + * See ddover.c, DDOVER_HQVCalcZoomWidth() and DDOver_GetDisplayCount() + */ +bool uc_ovl_map_hzoom(int sw, int dw, u32* zoom, u32* mini, + u32* falign, u32* dcount) +{ + u32 tmp, sw1, d; + int md; // Minify-divider + bool zoom_ok = true; + + md = 1; + *falign = 0; + + if (sw == dw) { // No zoom + // Do nothing + } + else if (sw < dw) { // Zoom in + + tmp = (sw * 0x0800) / dw; + zoom_ok = !(tmp > 0x7ff); + + *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE; + *mini |= V1_X_INTERPOLY; + } + else { // sw > dw - Zoom out + + // Find a suitable divider (1 << d) = {2, 4, 8 or 16} + + sw1 = sw; + for (d = 1; d < 5; d++) { + sw1 >>= 1; + if (sw1 <= dw) break; + } + if (d == 5) { // Too small. + d = 4; + zoom_ok = false; + } + + md = 1 << d; // <= {2,4,8,16} + *falign = ((md<<1)-1) & 0xf; // <= {3,7,15,15} + *mini |= V1_X_INTERPOLY; + *mini |= ((d<<1)-1) << 24; // <= {1,3,5,7} << 24 + + // Add scaling + + if (sw1 < dw) { + //CLE bug + //tmp = sw1*0x0800 / dw; + tmp = (sw1 - 2) * 0x0800 / dw; + *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE; + } + } + + *dcount = sw - md; + + return zoom_ok; +} + + +/** + * @param falign fetch alignment + * @param format overlay pixel format + * @param sw source width + * + * @returns qword pitch register setting + * + * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetFetch() + * @note Only call after uc_ovl_map_hzoom() + */ +u32 uc_ovl_map_qwpitch(int falign, DFBSurfacePixelFormat format, int sw) +{ + int fetch = 0; + + switch (format) { + case DSPF_YV12: + fetch = ALIGN_TO(sw, 32) >> 4; + break; + case DSPF_I420: + fetch = (ALIGN_TO(sw, 16) >> 4) + 1; + break; + case DSPF_UYVY: + case DSPF_YUY2: + fetch = (ALIGN_TO(sw << 1, 16) >> 4) + 1; + break; + case DSPF_ARGB1555: + case DSPF_RGB16: + fetch = (ALIGN_TO(sw << 1, 16) >> 4) + 1; + break; + case DSPF_RGB32: + case DSPF_ARGB: + fetch = (ALIGN_TO(sw << 2, 16) >> 4) + 1; + break; + default: + D_BUG("Unexpected pixelformat!"); + break; + } + + if (fetch < 4) fetch = 4; + + // Note: Unsure if alignment is needed or is in the way. + fetch = ALIGN_TO(fetch, falign + 1); + return fetch << 20; // V12_QWORD_PER_LINE +} + + +/** + * Map pixel format. + * + * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetV1Format() + */ +u32 uc_ovl_map_format(DFBSurfacePixelFormat format) +{ + switch (format) { + case DSPF_YV12: + case DSPF_I420: + return V1_COLORSPACE_SIGN | V1_YUV420; + case DSPF_UYVY: + case DSPF_YUY2: + return V1_COLORSPACE_SIGN | V1_YUV422; + case DSPF_ARGB1555: + return V1_RGB15; + case DSPF_RGB16: + return V1_RGB16; + case DSPF_RGB32: + case DSPF_ARGB: + return V1_RGB32; + default : + D_BUG("Unexpected pixelformat!"); + return V1_YUV422; + } +} + + +/** + * Map overlay window. + * + * @param scrw screen width (eg. 800) + * @param scrh screen height (eg. 600) + * @param win destination window + * @param sw source surface width + * @param sh source surface height + * + * @param win_start will hold window start register setting + * @param win_end will hold window end register setting + * + * @parm ox will hold new leftmost coordinate in source surface + * @parm oy will hold new topmost coordinate in source surface + */ +void uc_ovl_map_window(int scrw, int scrh, DFBRectangle* win, int sw, int sh, + u32* win_start, u32* win_end, int* ox, int* oy) +{ + int x1, y1, x2, y2; + int x,y,dw,dh; // These help making the code readable... + + *ox = 0; + *oy = 0; + *win_start = 0; + *win_end = 0; + + x = win->x; + y = win->y; + dw = win->w; + dh = win->h; + + // For testing the clipping + //scrw -= 100; + //scrh -= 100; + + // Handle invisible case. + if ((x > scrw) || (y > scrh) || (x+dw < 0) || (y+dh < 0)) return; + + // Vertical clipping + + if ((y >= 0) && (y+dh < scrh)) { + // No clipping + y1 = y; + y2 = y+dh-1; + } + else if ((y < 0) && (y+dh < scrh)) { + // Top clip + y1 = 0; + y2 = y+dh-1; + *oy = (int) (((float) (sh * -y)) / ((float) dh) + 0.5); + } + else if ((y >= 0) && (y+dh >= scrh)) { + // Bottom clip + y1 = y; + y2 = scrh-1; + } + else { // if (y < 0) && (y+dh >= scrh) + // Top and bottom clip + y1 = 0; + y2 = scrh-1; + *oy = (int) (((float) (sh * -y)) / ((float) dh) + 0.5); + } + + // Horizontal clipping + + if ((x >= 0) && (x+dw < scrw)) { + // No clipping + x1 = x; + x2 = x+dw-1; + } + else if ((x < 0) && (x+dw < scrw)) { + // Left clip + x1 = 0; + x2 = x+dw-1; + *ox = (int) (((float) (sw * -x)) / ((float) dw) + 0.5); + } + else if ((x >= 0) && (x+dw >= scrw)) { + // Right clip + x1 = x; + x2 = scrw-1; + } + else { // if (x < 0) && (x+dw >= scrw) + // Left and right clip + x1 = 0; + x2 = scrw-1; + *ox = (int) (((float) (sw * -x)) / ((float) dw) + 0.5); + } + + *win_start = (x1 << 16) | y1; + *win_end = (x2 << 16) | y2; + + // For testing the clipping + //*win_start = ((x1+50) << 16) | (y1+50); + //*win_end = ((x2+50) << 16) | (y2+50); +} + + +/** + * Map overlay buffer address. + * + * @param format pixel format + * @param buf Framebuffer address of surface (0 = start of framebuffer) + * @param ox leftmost pixel to show (used when clipping, else set to zero) + * @param oy topmost pixel to show (used when clipping, else set to zero) + * @param w total surface width (does *not* depend on the x parameter) + * @param h total surface height (does *not* depend on the y parameter) + * @param pitch source surface pitch (bytes per pixel) + * + * @param y_start will hold start address of Y(UV) or RGB buffer + * @param u_start will hold start address of Cb buffer (planar modes only) + * @param v_start will hold start address of Cr buffer (planar modes only) + * + * @note Derived from VIA's V4L driver. See ddover.c, + * DDOver_GetSrcStartAddress() and DDOVer_GetYCbCrStartAddress() + */ +void uc_ovl_map_buffer(DFBSurfacePixelFormat format, u32 buf, + int ox, int oy, int sw, int sh, int sp, int field, + u32* y_start, u32* u_start, u32* v_start) +{ + int swap_cb_cr = 0; + + u32 tmp; + u32 y_offset, uv_offset = 0; + + switch (format) { + + case DSPF_YUY2: + case DSPF_UYVY: + y_offset = ((oy * sp) + ((ox << 1) & ~15)); + break; + + case DSPF_YV12: + swap_cb_cr = 1; + case DSPF_I420: + y_offset = ((((oy & ~3) * sp) + ox) & ~31) ; + if (oy > 0) + uv_offset = (((((oy & ~3) >> 1) * sp) + ox) & ~31) >> 1; + else + uv_offset = y_offset >> 1; + break; + + case DSPF_ARGB1555: + case DSPF_RGB16: + y_offset = (oy * sp) + ((ox * 16) >> 3); + break; + + case DSPF_RGB32: + case DSPF_ARGB: + y_offset = (oy * sp) + ((ox * 32) >> 3); + break; + + default: + y_offset = 0; + uv_offset = 0; + D_BUG("Unexpected pixelformat!"); + } + + if (field) { + y_offset += sp; + uv_offset += sp >> 1; + } + + *y_start = buf + y_offset; + + if (u_start && v_start) { + *u_start = buf + sp * sh + uv_offset; + *v_start = buf + sp * sh + sp * (sh >> 2) + uv_offset; + + if (swap_cb_cr) { + tmp = *u_start; + *u_start = *v_start; + *v_start = tmp; + } + } +} + + +/** + * Map alpha mode and opacity. + * + * @param opacity Alpha opacity: 0 = transparent, 255 = opaque. + * -1 = Use alpha from underlying graphics. + * + * @returns alpha control register setting. + * + * @note: Unfortunately, if using alpha from underlying graphics, + * the video is opaque if alpha = 255 and transparent if = 0. + * The inverse would have made more sense ... + * + * @note: The hardware supports a separate alpha plane as well, + * but it is not implemented here. + * + * @note: Derived from ddmpeg.c, VIAAlphaWin() + */ + +u32 uc_ovl_map_alpha(int opacity) +{ + u32 ctrl = 0x00080000; // Not sure what this number is, supposedly + // it is the "expire number divided by 4". + + if (opacity > 255) opacity = 255; + + if (opacity < 0) { + ctrl |= ALPHA_WIN_BLENDING_GRAPHIC; + } + else { + opacity = opacity >> 4; // Throw away bits 0 - 3 + ctrl |= (opacity << 12) | ALPHA_WIN_BLENDING_CONSTANT; + } + + return ctrl; // V_ALPHA_CONTROL +} + +/** + * Calculate V1 control and fifo-control register values + * @param format pixel format + * @param sw source width + * @param hwrev CLE266 hardware revision + * @param extfifo_on set this true if the extended FIFO is enabled + * @param control will hold value for V1_CONTROL + * @param fifo will hold value for V1_FIFO_CONTROL + */ +void uc_ovl_map_v1_control(DFBSurfacePixelFormat format, int sw, + int hwrev, bool extfifo_on, + u32* control, u32* fifo) +{ + *control = V1_BOB_ENABLE | V1_ENABLE | uc_ovl_map_format(format); + + if (hwrev == 0x10) { + *control |= V1_EXPIRE_NUM_F; + } + else { + if (extfifo_on) { + *control |= V1_EXPIRE_NUM_A | V1_FIFO_EXTENDED; + } + else { + *control |= V1_EXPIRE_NUM; + } + } + + if ((format == DSPF_YV12) || (format == DSPF_I420)) { + //Minified video will be skewed without this workaround. + if (sw <= 80) { //Fetch count <= 5 + *fifo = UC_MAP_V1_FIFO_CONTROL(16,0,0); + } + else { + if (hwrev == 0x10) + *fifo = UC_MAP_V1_FIFO_CONTROL(64,56,56); + else + *fifo = UC_MAP_V1_FIFO_CONTROL(16,12,8); + } + } + else { + if (hwrev == 0x10) { + *fifo = UC_MAP_V1_FIFO_CONTROL(64,56,56); // Default rev 0x10 + } + else { + if (extfifo_on) + *fifo = UC_MAP_V1_FIFO_CONTROL(48,40,40); + else + *fifo = UC_MAP_V1_FIFO_CONTROL(32,29,16); // Default + } + } +} + +/** uc_ovl_map_adjustment() helper - clamp x to [lo, hi] */ +static float clamp(float x, float lo, float hi) +{ + return (x < lo) ? lo : ((x > hi) ? hi : x); /* 2 nested if's. */ +} + +/** + * uc_ovl_map_adjustment() helper - format x for the hardware. + * + * @param x The value to format. + * @param ndec Number of binary decimals. + * @param sbit sign bit position. + * =0: use two's complement representation + * >0: use a sign bit + positive value. + * @param mask Bitmask + * @param shift Position in hardware register. + */ +static int fmt(float x, int ndec, int sbit, u32 mask, int shift) +{ + int y = (x * (1 << ndec)); + if (sbit && (y < 0)) y = -y | (1 << sbit); + return (((u32) y) & mask) << shift; +} + +/** + * Map color adjustment to CLE266 hardware. + * + * @param adj DirectFB color adjustment. All fields are assumed valid. + * @param a1 Will hold value for V1_ColorSpaceReg_1 + * @param a2 Will hold value for V1_ColorSpaceReg_2 + */ +void uc_ovl_map_adjustment(DFBColorAdjustment* adj, u32* a1, u32* a2) +{ + float con, sat, bri, hue; + float c, s; + float A, B1, C1, D, B2, C2, B3, C3; + + // Map contrast to [0, 2.0] (preferred: [0, 1.66]), default: 1.0. + con = (float) adj->contrast / 32768.0; + // Map saturation to [0, 2.0], default: 1.0. + sat = (float) adj->saturation / 32768.0; + // Map brightness to [-121, 125], (preferred: [-94, 125.1]), default: 3.97. + bri = (float) (adj->brightness - 31696) / 270.48; + // Map hue to [-pi, pi], default is 0.0. + hue = (float) (adj->hue - 32768) / 10430.378; + // Note: The limits are estimates that need testing. + + // Map parameters to hw registers. + + s = sin(hue) * con * sat; + c = cos(hue) * con * sat; + + A = clamp(1.164*con, 0, 1.9375); + B1 = clamp(-1.596*s, -0.75, 0.75); + C1 = clamp(1.596*c, 1, 2.875); + B2 = clamp( (0.813*s - 0.391*c), 0, -0.875); + C2 = clamp(-(0.813*c + 0.391*s), 0, -1.875); + B3 = clamp(2.018*c, 0, 3.75); + C3 = clamp(2.018*s, -1.25, 1.25); + D = clamp(1.164*(bri-16), -128, 127); + + *a1 = + fmt(A, 4, 0, 0x1f, 24) | fmt(B1, 2, 2, 0x07, 18) | + fmt(C1, 3, 0, 0x1f, 9) | fmt(D, 0, 0, 0xff, 0); + + *a2 = + fmt(B2, 3, 4, 0x7, 25) | fmt(C2, 3, 4, 0xf, 17) | + fmt(B3, 2, 0, 0xf, 10) | fmt(C3, 2, 3, 0xf, 2); +} |