blob: c730cab5a1569bd5d4cc4f6cd5bed55571910f6e [file] [log] [blame]
/*
* PDP Scaled Video Framebuffer
*
* Copyright (c) 2008-2012 Imagination Technologies Ltd.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <asm/soc-chorus2/pdp.h>
#include <video/pdpfb.h>
#include "pdpfb.h"
#include "pdpfb_regs.h"
#include "pdpfb_vid.h"
/* warn if scale coefficients aren't normalised */
/*#define CHECK_SCALE_COEFFS*/
/*#define DEBUG_SCALE_COEFFS*/
#define THIS_STREAM (&pdpfb_vid_stream.stream)
#define THIS_PRIV (&pdpfb_vid_stream)
static struct pdpfb_vid_stream_priv {
struct pdpfb_stream stream;
/* horizontal scaler */
int hdecimation;
int hs_taps;
int hscoeffs[33];
int hs_oldsize, hs_oldres;
/* vertical scaler */
int vs_taps;
int vscoeffs[17];
int vs_oldsize, vs_oldres;
/* pixel format info */
struct pdpfb_vid_csc csc;
int planar_override;
int planar_ov_virtres[2];
int planar_ov_pixfmt;
struct pdpfb_vid_planar planar;
int pixfmt;
int nonstd2pixfmt[PDP_VID_PIXFMT_MAX];
/* fields to update on vevent */
#ifdef PDP_SHARED_BASE
spinlock_t base_addr_lock;
unsigned int base_addr_y;
unsigned int base_addr_u;
unsigned int base_addr_v;
unsigned int hskip;
unsigned int vskip;
#endif
} pdpfb_vid_stream;
static struct pdpfb_vid_csc_coefs pdpfb_vid_csc_presets[] = {
[PDP_VID_CSCPRESET_HDTV] = {
.ry = 298, .rv = 459, .ru = 0,
.gy = 298, .gv = -137, .gu = -55,
.by = 298, .bv = 0, .bu = 541,
},
[PDP_VID_CSCPRESET_SDTV] = {
.ry = 298, .rv = 409, .ru = 0,
.gy = 298, .gv = -208, .gu = -100,
.by = 298, .bv = 0, .bu = 517,
},
[PDP_VID_CSCPRESET_LEGACYHDTV] = {
.ry = 298, .rv = 459, .ru = 0,
.gy = 298, .gv = -139, .gu = -66,
.by = 298, .bv = 0, .bu = 532,
},
[PDP_VID_CSCPRESET_LEGACYSDTV] = {
.ry = 298, .rv = 409, .ru = 0,
.gy = 298, .gv = -207, .gu = -97,
.by = 298, .bv = 0, .bu = 519,
},
};
/* fixed point arithmetic */
static inline int fix(int p, int a)
{
return a << p;
}
static inline int fix_div(int p, int n, int d)
{
return (n << p) / d;
}
static inline int fix_divl(int p, int n, int d)
{
return ((u64)n << p) / d;
}
static inline int fix_mul(int p, int a, int b)
{
return (a * b) >> p;
}
static inline int fix_sin(int p, int x)
{
/* tailor expansion */
int pow;
int sum;
/* in [-pi,pi] */
int pi2 = fix_divl(p, 6283185, 1000000);
int pi = pi2/2;
x = x % pi2;
if (x > pi)
x -= pi2;
else if (x < -pi)
x += pi2;
sum = x;
/* x represents x^2 from now on */
x = fix_mul(p, x, x);
/* -x^3/3! */
pow = fix_mul(p, sum, x);
sum -= pow/6;
/* +x^5/5! */
pow = fix_mul(p, pow, x);
sum += pow/120;
/* -x^7/7! */
pow = fix_mul(p, pow, x);
sum -= pow/5040;
/* +x^9/9! */
pow = fix_mul(p, pow, x);
sum += pow/362880;
return sum;
}
static inline int fix_cos(int p, int x)
{
/* tailor expansion */
int pow;
int sum = fix(p, 1);
/* in [-pi,pi] */
int pi2 = fix_divl(p, 6283185, 1000000);
int pi = pi2/2;
x = x % pi2;
if (x > pi)
x -= pi2;
else if (x < -pi)
x += pi2;
/* x represents x^2 from now on */
x = fix_mul(p, x, x);
/* -x^2/2! */
pow = x;
sum -= pow/2;
/* +x^4/4! */
pow = fix_mul(p, pow, x);
sum += pow/24;
/* -x^6/6! */
pow = fix_mul(p, pow, x);
sum -= pow/720;
/* +x^8/8! */
pow = fix_mul(p, pow, x);
sum += pow/40320;
/* -x^10/10! */
pow = fix_mul(p, pow, x);
sum -= pow/3628800;
return sum;
}
/* returns negative for invalid nonstd */
static int pdpfb_vid_nonstd_to_pixfmt(u32 nonstd)
{
if (nonstd <= 0 || nonstd >= ARRAY_SIZE(THIS_PRIV->nonstd2pixfmt))
return -1;
return THIS_PRIV->nonstd2pixfmt[nonstd];
}
static int pdpfb_vid_scale_coeffs_stale(u32 new_res, u32 old_res,
u32 new_size, u32 old_size,
int new_T, int old_T,
int new_I, int old_I)
{
/* has number of taps/interpolation points changed? */
if (new_T != old_T || new_I != old_I)
return 1;
/* scale factor clamped to max of 1 */
if (new_size >= new_res && old_size >= old_res)
return 0;
/* has scale factor changed? */
return new_size * old_res != old_size * new_res;
}
static int pdpfb_vid_calc_scale_coeffs(u32 res, u32 size,
int T, /* taps */
int I, /* interpolation points */
int *coeffs, int count)
{
int midpoint = T*I/2;
#define SCALE_PRECISION 8
#define RESULT_PRECISION 6
#define DIV(a, b) fix_div(SCALE_PRECISION, a, b)
#define DIVL(a, b) fix_divl(SCALE_PRECISION, a, b)
#define MUL(a, b) fix_mul(SCALE_PRECISION, a, b)
#define SIN(a) fix_sin(SCALE_PRECISION, a)
#define COS(a) fix_cos(SCALE_PRECISION, a)
#define FIX(a) fix(SCALE_PRECISION, a)
#define RESULT(a) ((a) >> (SCALE_PRECISION - RESULT_PRECISION))
int fpi = DIVL(3141593, 1000000);
int fS = min(FIX(1), /* scale factor clamped to 1 */
DIV(size, res));
int fA = DIV(54, 100);
int c, i;
int fpiS = MUL(fpi, fS);
int f2piSoT = fpiS*2/T;
int f1mA = FIX(1) - fA;
int fT = FIX(T);
int ftotal = 0;
/* Calculate coefficients */
for (c = 0; c <= midpoint; ++c) {
/* t + i/I */
int ftpioI = FIX(c)/I;
int fx = MUL(fpiS, fT/2 - ftpioI);
int fco = fA - MUL(f1mA, COS(MUL(f2piSoT, ftpioI)));
if (fx > DIV(1, 10))
fco = MUL(DIV(SIN(fx), fx), fco);
ftotal += fco;
coeffs[c] = fco;
}
/*
* Interpolation points I/2+1 to I-1 have same values as 1 to I/2-1,
* but in opposite order, so we only need to normalise the first half
* of the interpolation points taking mirroring into account.
*/
for (i = 0; i <= I/2; ++i) {
int sum, err, dir = 0;
/*
* Normalise coefficients in each interpolation point and
* convert into result format.
*/
sum = 0;
for (c = i; c <= midpoint; c += I)
sum += coeffs[c];
for (; c < midpoint*2; c += I)
sum += coeffs[midpoint*2 - c];
/* Careful not to modify a coefficient twice */
for (c = i; c <= midpoint; c += I)
coeffs[c] = RESULT(DIV(coeffs[c], sum));
if (i & (I/2 - 1))
for (c = I-i; c <= midpoint; c += I)
coeffs[c] = RESULT(DIV(coeffs[c], sum));
/*
* Find fixed point error from normalisation.
*/
sum = 0;
for (c = i; c <= midpoint; c += I)
sum += coeffs[c];
for (; c < midpoint*2; c += I)
sum += coeffs[midpoint*2 - c];
err = sum - (1 << RESULT_PRECISION);
if (err > 0)
dir = -1;
else if (err < 0)
dir = 1;
else
continue;
#ifdef DEBUG_SCALE_COEFFS
printk(KERN_DEBUG "pdp: i=%d, err=%d\n", i, err);
#define SCALE_DEBUG(C, N) \
printk(KERN_DEBUG "pdp: adj*%d [%d] to 0x%x (err=%d)\n", \
(N), (C), coeffs[(C)], err)
#else
#define SCALE_DEBUG(C, N) do {} while (0)
#endif
/*
* Distribute the error over the tap coefficients, preferring
* to change central coefficients.
*/
if (i == 0) {
/*
* Special case: contains midpoint. Work out from
* center until no error or an odd value (which can be
* fixed by adjusting midpoint again).
*/
while (err > 1 || err < -1) {
coeffs[midpoint] += dir;
err += dir;
SCALE_DEBUG(midpoint, 1);
for (c = midpoint - I;
(err > 1 || err < -1) && c >= I;
c -= I) {
coeffs[c] += dir;
err += dir*2;
SCALE_DEBUG(c, 2);
}
}
if (err & 1) {
coeffs[midpoint] += dir;
err += dir;
SCALE_DEBUG(midpoint, 1);
}
} else if (i == I/2) {
/*
* Special case: tap coeffs mirror. This also means err
* is always even with this interpolation point.
*/
int last = i + I*(T/2-1);
#ifdef CHECK_SCALE_COEFFS
WARN(err & 1, "err (=%d) should be even for i=%d\n",
err, i);
#endif
while (err) {
for (c = last; err && c >= 0; c -= I) {
coeffs[c] += dir;
err += dir*2;
SCALE_DEBUG(c, 2);
}
}
} else {
/*
* No tap coefficient mirrors another. Work out from
* center adjusting values.
*/
int last = midpoint - i;
int offset = i*2 - I;
while (err) {
for (c = last; err && c >= 0; c -= I) {
coeffs[c] += dir;
err += dir;
SCALE_DEBUG(c, 1);
if (!err)
break;
coeffs[c+offset] += dir;
err += dir;
SCALE_DEBUG(c+offset, 1);
}
}
}
#ifdef CHECK_SCALE_COEFFS
sum = 0;
for (c = i; c <= midpoint; c += I)
sum += coeffs[c];
for (; c < midpoint*2; c += I)
sum += coeffs[midpoint*2 - c];
err = sum - (1 << RESULT_PRECISION);
WARN_ONCE(err, "Scale coefficients not normalised"
" (i=%d, err=%d)\n", i, err);
#endif
}
/* reflect about midpoint in spare coeffs */
if (count > midpoint*2)
count = midpoint*2;
for (c = midpoint+1; c < count; ++c)
coeffs[c] = coeffs[2*midpoint - c];
return 0;
#undef DIV
#undef MUL
#undef SIN
#undef COS
#undef FIX
#undef RESULT
}
static int pdpfb_vid_calc_hscale(struct pdpfb_priv *priv)
{
u32 geomw = THIS_STREAM->geom.w;
if (!geomw)
geomw = THIS_STREAM->info.var.xres;
/* don't recalculate coefficients unless something has changed */
if (!pdpfb_vid_scale_coeffs_stale(THIS_STREAM->info.var.xres,
THIS_PRIV->hs_oldres,
geomw,
THIS_PRIV->hs_oldsize,
8, THIS_PRIV->hs_taps,
8, 8))
return 0;
THIS_PRIV->hs_taps = 8;
THIS_PRIV->hs_oldres = THIS_STREAM->info.var.xres;
THIS_PRIV->hs_oldsize = geomw;
return pdpfb_vid_calc_scale_coeffs(THIS_STREAM->info.var.xres,
geomw,
THIS_PRIV->hs_taps,
8,
THIS_PRIV->hscoeffs,
ARRAY_SIZE(THIS_PRIV->hscoeffs));
}
#ifdef PDP_VID_VSCALE
static int pdpfb_vid_calc_vscale(struct pdpfb_priv *priv)
{
struct pdp_info *pdata = pdpfb_get_platform_data(priv);
int vs_taps;
/* 2-tap (bilinear) filtering when scaling down beyond threshold */
if ((u32)THIS_STREAM->geom.h * pdata->vpitch_bilinear_threshold
< ((u32)THIS_STREAM->info.var.yres << PDPFB_PDATA_FIX_SHIFT))
vs_taps = 2;
else
vs_taps = 4;
/* don't recalculate coefficients unless something has changed */
if (!pdpfb_vid_scale_coeffs_stale(THIS_STREAM->info.var.yres,
THIS_PRIV->vs_oldres,
THIS_STREAM->geom.h,
THIS_PRIV->vs_oldsize,
vs_taps, THIS_PRIV->vs_taps,
8, 8))
return 0;
THIS_PRIV->vs_taps = vs_taps;
THIS_PRIV->vs_oldres = THIS_STREAM->info.var.yres;
THIS_PRIV->vs_oldsize = THIS_STREAM->geom.h;
return pdpfb_vid_calc_scale_coeffs(THIS_STREAM->info.var.yres,
THIS_STREAM->geom.h,
vs_taps,
8,
THIS_PRIV->vscoeffs,
ARRAY_SIZE(THIS_PRIV->vscoeffs));
}
#else
static inline int pdpfb_vid_calc_vscale(struct pdpfb_priv *priv)
{
return 0;
}
#endif
static int pdpfb_vid_set_bpp(struct fb_var_screeninfo *var)
{
if (pdpfb_vid_nonstd_to_pixfmt(var->nonstd) < 0)
var->nonstd = 0;
switch (var->nonstd) {
case PDP_VID_PIXFMT_420_PL8:
case PDP_VID_PIXFMT_420_PL8IVU:
case PDP_VID_PIXFMT_420_PL8IUV:
var->bits_per_pixel = 12;
break;
case PDP_VID_PIXFMT_422_UY0VY1_8888:
case PDP_VID_PIXFMT_422_VY0UY1_8888:
case PDP_VID_PIXFMT_422_Y0UY1V_8888:
case PDP_VID_PIXFMT_422_Y0VY1U_8888:
var->bits_per_pixel = 16;
break;
default:
var->bits_per_pixel = 16;
var->nonstd = PDP_VID_PIXFMT_422_UY0VY1_8888;
break;
}
var->red.offset = 0;
var->red.length = 0;
var->green.offset = 0;
var->green.length = 0;
var->blue.offset = 0;
var->blue.length = 0;
var->transp.offset = 0;
var->transp.length = 0;
var->red.msb_right = 0;
var->green.msb_right = 0;
var->blue.msb_right = 0;
var->transp.msb_right = 0;
return 0;
}
static int pdpfb_vid_check_geom(struct pdpfb_priv *priv,
struct pdpfb_geom *geom)
{
struct fb_info *info = &THIS_STREAM->info;
if (!geom->w) {
THIS_PRIV->hdecimation = 0;
} else if (geom->w == info->var.xres) {
geom->w = 0;
} else if (geom->w > info->var.xres*8) {
THIS_PRIV->hdecimation = 0;
geom->w = info->var.xres*8;
} else if (geom->w*4 < info->var.xres) {
THIS_PRIV->hdecimation = 1;
if (geom->w*8 < info->var.xres)
geom->w = (info->var.xres+7)/8;
#if PDP_REV < 0x010001
++geom->w; /* round up */
#endif
} else {
THIS_PRIV->hdecimation = 0;
}
#if PDP_REV < 0x010001
/* geom->w must be even because it's in YUV */
geom->w &= ~0x1;
/* pan granularity depends on horizontal decimation */
if (THIS_PRIV->hdecimation)
info->fix.xpanstep = 4;
else
info->fix.xpanstep = 2;
#else
info->fix.xpanstep = 1;
#endif
/* avoid HSKIP problems by restricting pan step */
if (info->var.nonstd == PDP_VID_PIXFMT_420_PL8IVU ||
info->var.nonstd == PDP_VID_PIXFMT_420_PL8IUV)
info->fix.xpanstep = 16;
if (geom->h) {
#ifndef PDP_VID_VSCALE
/* only doubling and halving */
if (geom->h*4 <= info->var.yres*3)
geom->h = info->var.yres / 2;
else if (geom->h*2 >= info->var.yres*3)
geom->h = info->var.yres * 2;
else
geom->h = 0;
#else
if (geom->h == info->var.yres)
geom->h = 0;
else if (geom->h > info->var.yres*8)
geom->h = info->var.yres*8;
else if (geom->h*8 < info->var.yres)
geom->h = (info->var.yres+7)/8;
#endif
}
return 0;
}
static int pdpfb_vid_set_geom(struct pdpfb_priv *priv)
{
struct pdpfb_geom *geom = &THIS_STREAM->geom;
pdpfb_vid_calc_hscale(priv);
if (geom->h)
pdpfb_vid_calc_vscale(priv);
return 0;
}
static unsigned long pdpfb_vid_required_mem(struct fb_var_screeninfo *var)
{
switch (var->nonstd) {
case PDP_VID_PIXFMT_420_PL8:
case PDP_VID_PIXFMT_420_PL8IVU:
case PDP_VID_PIXFMT_420_PL8IUV:
return 3 * pdpfb_get_line_length(var->xres_virtual/2, 8)
* var->yres_virtual;
default:
return pdpfb_get_line_length(var->xres_virtual,
var->bits_per_pixel)
* var->yres_virtual;
};
}
static int pdpfb_vid_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
if (!var->xres)
var->xres = 1;
if (!var->yres)
var->yres = 1;
#if PDP_REV < 0x010001
/* xres must be even, round up */
var->xres = (var->xres + 1) & ~0x1;
#endif
if (var->xres > var->xres_virtual)
var->xres_virtual = var->xres;
if (var->yres > var->yres_virtual)
var->yres_virtual = var->yres;
if (var->xres_virtual < var->xoffset + var->xres)
var->xres_virtual = var->xoffset + var->xres;
if (var->yres_virtual < var->yoffset + var->yres)
var->yres_virtual = var->yoffset + var->yres;
pdpfb_vid_set_bpp(var);
/* Memory limit */
if (pdpfb_vid_required_mem(var) > THIS_STREAM->videomem_len)
return -ENOMEM;
return 0;
}
static int pdpfb_vid_configure_csc(struct pdpfb_priv *priv)
{
struct pdpfb_vid_csc_coefs *coefs = &THIS_PRIV->csc.coefs;
pdpfb_write(priv, PDP_CSCCOEFF0,
PLACE_FIELD(PDP_CSCCOEFF0_RU, coefs->ru) |
PLACE_FIELD(PDP_CSCCOEFF0_RY, coefs->ry));
pdpfb_write(priv, PDP_CSCCOEFF1,
PLACE_FIELD(PDP_CSCCOEFF1_GY, coefs->gy) |
PLACE_FIELD(PDP_CSCCOEFF1_RV, coefs->rv));
pdpfb_write(priv, PDP_CSCCOEFF2,
PLACE_FIELD(PDP_CSCCOEFF2_GV, coefs->gv) |
PLACE_FIELD(PDP_CSCCOEFF2_GU, coefs->gu));
pdpfb_write(priv, PDP_CSCCOEFF3,
PLACE_FIELD(PDP_CSCCOEFF3_BU, coefs->bu) |
PLACE_FIELD(PDP_CSCCOEFF3_BY, coefs->by));
pdpfb_write(priv, PDP_CSCCOEFF4,
PLACE_FIELD(PDP_CSCCOEFF4_BV, coefs->bv));
return 0;
}
static int pdpfb_vid_check_csc(struct pdpfb_vid_csc_coefs *csc)
{
#define CSC_COEF_INVALID(x) (((x) >= (1<<10)) || ((x) < (-1<<10)))
return CSC_COEF_INVALID(csc->ry) ||
CSC_COEF_INVALID(csc->rv) ||
CSC_COEF_INVALID(csc->ru) ||
CSC_COEF_INVALID(csc->gy) ||
CSC_COEF_INVALID(csc->gv) ||
CSC_COEF_INVALID(csc->gu) ||
CSC_COEF_INVALID(csc->by) ||
CSC_COEF_INVALID(csc->bv) ||
CSC_COEF_INVALID(csc->bu);
#undef CSC_COEF_INVALID
}
static int pdpfb_vid_check_planar(struct fb_info *info,
struct pdpfb_vid_planar *pl)
{
int extra;
/* check alignment */
if (unlikely(pl->y_offset & PDP_YADDR_ALIGNMASK)) {
pl->y_offset &= ~PDP_YADDR_ALIGNMASK;
return -EINVAL;
}
if (unlikely(pl->u_offset & PDP_UADDR_ALIGNMASK)) {
pl->u_offset &= ~PDP_UADDR_ALIGNMASK;
return -EINVAL;
}
if (unlikely(pl->v_offset & PDP_VADDR_ALIGNMASK)) {
pl->v_offset &= ~PDP_VADDR_ALIGNMASK;
return -EINVAL;
}
if (unlikely(pl->y_line_length & PDP_YSTRIDE_ALIGNMASK)) {
/* round up */
pl->y_line_length = pdpfb_get_line_length(pl->y_line_length, 8);
return -EINVAL;
}
/* u/v strides must be the same */
if (unlikely(pl->u_line_length != pl->v_line_length)) {
pl->v_line_length = pl->u_line_length;
return -EINVAL;
}
/* strides must be in range */
if (unlikely(pl->y_line_length > PDP_YSTRIDE_MAX)) {
pl->y_line_length = PDP_YSTRIDE_MAX;
return -EINVAL;
}
/* u/v strides must be half or equal to y stride */
if (unlikely(pl->u_line_length != pl->y_line_length &&
pl->u_line_length*2 != pl->y_line_length)) {
if (pl->u_line_length < pl->y_line_length)
pl->u_line_length = pl->y_line_length/2;
else
pl->u_line_length = pl->y_line_length;
pl->v_line_length = pl->u_line_length;
return -EINVAL;
}
/* offsets must be in range (don't want to overflow later) */
if (unlikely(pl->y_offset > PDP_YADDR_MAX ||
pl->y_offset >= THIS_STREAM->videomem_len)) {
pl->y_offset = 0;
return -ENOMEM;
}
if (unlikely(pl->u_offset > PDP_UADDR_MAX ||
pl->u_offset >= THIS_STREAM->videomem_len)) {
pl->u_offset = 0;
return -ENOMEM;
}
if (unlikely(pl->v_offset > PDP_VADDR_MAX ||
pl->v_offset >= THIS_STREAM->videomem_len)) {
pl->v_offset = 0;
return -ENOMEM;
}
/* does y plane fit in memory? */
extra = info->var.xres_virtual - pl->y_line_length;
if (extra < 0)
extra = 0;
if (pl->y_offset + pl->y_line_length*info->var.yres_virtual + extra
> THIS_STREAM->videomem_len) {
pl->y_offset = 0;
return -ENOMEM;
}
/* does u plane fit in memory? */
if (info->var.nonstd == PDP_VID_PIXFMT_420_PL8)
extra = info->var.xres_virtual/2 - pl->u_line_length;
else /* byte interleaved chroma formats */
extra = info->var.xres_virtual - pl->u_line_length;
if (extra < 0)
extra = 0;
if (pl->u_offset + pl->u_line_length*info->var.yres_virtual/2 + extra
> THIS_STREAM->videomem_len) {
pl->u_offset = 0;
return -ENOMEM;
}
/* does v plane fit in memory? (same extra as u plane) */
if (pl->v_offset + pl->v_line_length*info->var.yres_virtual/2 + extra
> THIS_STREAM->videomem_len) {
pl->v_offset = 0;
return -ENOMEM;
}
return 0;
}
static void pdpfb_vid_update_planar(struct pdpfb_priv *priv,
struct fb_info *info)
{
int uv_planar = 1;
/* handle planar YUV */
switch (info->var.nonstd) {
case PDP_VID_PIXFMT_420_PL8IVU:
case PDP_VID_PIXFMT_420_PL8IUV:
uv_planar = 0;
/* fall through */
case PDP_VID_PIXFMT_420_PL8:
/* The following 2 pixel formats are unimplemented */
case PDP_VID_PIXFMT_420_T88CP:
case PDP_VID_PIXFMT_422_T88CP:
if (THIS_PRIV->planar_override) {
/* if overridden planar setup is still ok, leave it */
if (THIS_PRIV->planar_ov_virtres[0]
== info->var.xres_virtual &&
THIS_PRIV->planar_ov_virtres[1]
== info->var.yres_virtual &&
THIS_PRIV->planar_ov_pixfmt == info->var.nonstd)
break;
}
THIS_PRIV->planar_override = 0;
THIS_PRIV->planar.y_line_length
= pdpfb_get_line_length(info->var.xres_virtual, 8);
THIS_PRIV->planar.u_line_length
= THIS_PRIV->planar.v_line_length
= info->var.xres_virtual >> uv_planar;
THIS_PRIV->planar.y_offset = 0;
THIS_PRIV->planar.u_offset = THIS_PRIV->planar.y_offset
+ THIS_PRIV->planar.y_line_length
* info->var.yres_virtual;
if (uv_planar)
THIS_PRIV->planar.v_offset = THIS_PRIV->planar.u_offset
+ THIS_PRIV->planar.u_line_length
* info->var.yres_virtual/2;
else
THIS_PRIV->planar.v_offset = THIS_PRIV->planar.u_offset;
break;
case PDP_VID_PIXFMT_422_UY0VY1_8888:
case PDP_VID_PIXFMT_422_VY0UY1_8888:
case PDP_VID_PIXFMT_422_Y0UY1V_8888:
case PDP_VID_PIXFMT_422_Y0VY1U_8888:
default:
THIS_PRIV->planar_override = 0;
THIS_PRIV->planar.y_line_length
= THIS_PRIV->planar.u_line_length
= THIS_PRIV->planar.v_line_length
= pdpfb_get_line_length(info->var.xres_virtual,
info->var.bits_per_pixel);
THIS_PRIV->planar.y_offset = 0;
THIS_PRIV->planar.u_offset = 0;
THIS_PRIV->planar.v_offset = 0;
break;
}
}
static int pdpfb_vid_change_mode(struct pdpfb_priv *priv)
{
struct fb_info *info = &THIS_STREAM->info;
u32 str2posn;
/* handle planar YUV */
switch (info->var.nonstd) {
case PDP_VID_PIXFMT_420_PL8:
case PDP_VID_PIXFMT_420_PL8IVU:
case PDP_VID_PIXFMT_420_PL8IUV:
/* The following 2 pixel formats are unimplemented */
case PDP_VID_PIXFMT_420_T88CP:
case PDP_VID_PIXFMT_422_T88CP:
info->fix.type = FB_TYPE_PLANES;
break;
case PDP_VID_PIXFMT_422_UY0VY1_8888:
case PDP_VID_PIXFMT_422_VY0UY1_8888:
case PDP_VID_PIXFMT_422_Y0UY1V_8888:
case PDP_VID_PIXFMT_422_Y0VY1U_8888:
default:
info->fix.type = FB_TYPE_PACKED_PIXELS;
break;
}
info->fix.line_length = THIS_PRIV->planar.y_line_length;
str2posn = pdpfb_read(priv, PDP_STR2POSN);
SET_FIELD(str2posn, PDP_STRXPOSN_SRCSTRIDE,
(info->fix.line_length >> PDP_YSTRIDE_ALIGN) - 1);
pdpfb_write(priv, PDP_STR2POSN, str2posn);
return 0;
}
static int pdpfb_vid_configure_hscale(struct pdpfb_priv *priv)
{
u32 i;
u32 scale_ctrl, hsinit, scale_size;
u32 xres = THIS_STREAM->info.var.xres;
u32 geomw = THIS_STREAM->geom.w;
u32 hpitch;
int en = (geomw && geomw != xres);
#if PDP_REV >= 0x010001
/* we enable the scalar even with 1:1 to get fine control over HSKIP */
en = 1;
#endif
scale_ctrl = pdpfb_read(priv, PDP_SCALECTRL);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_HSCALEBP, !en);
pdpfb_write(priv, PDP_SCALECTRL, scale_ctrl);
if (!geomw)
geomw = xres;
scale_size = pdpfb_read(priv, PDP_SCALESIZE);
SET_FIELD(scale_size, PDP_SCALESIZE_SCALEDWIDTH, geomw - 1);
pdpfb_write(priv, PDP_SCALESIZE, scale_size);
if (!en)
return 0;
hpitch = (fix_div(12, xres, geomw) + 1) >> 1;
if (THIS_PRIV->hdecimation)
hpitch /= 2;
hsinit = pdpfb_read(priv, PDP_HSINIT);
SET_FIELD(hsinit, PDP_HSINIT_HINITIAL,
fix_div(11, THIS_PRIV->hs_taps, 2));
SET_FIELD(hsinit, PDP_HSINIT_HDECIM, THIS_PRIV->hdecimation);
SET_FIELD(hsinit, PDP_HSINIT_HPITCH, hpitch);
pdpfb_write(priv, PDP_HSINIT, hsinit);
for (i = 0; i < 8; ++i) {
u32 val = (0xFF & THIS_PRIV->hscoeffs[i<<2])
| ((0xFF & THIS_PRIV->hscoeffs[(i<<2) + 1]) << 8)
| ((0xFF & THIS_PRIV->hscoeffs[(i<<2) + 2]) << 16)
| ((0xFF & THIS_PRIV->hscoeffs[(i<<2) + 3]) << 24);
pdpfb_write(priv, PDP_HSCOEFF0 + (i<<2), val);
}
#if PDP_REV < 0x010001
/* the odd one out, index 32 goes in most significant byte */
pdpfb_write(priv, PDP_HSCOEFF8,
(0xFF & THIS_PRIV->hscoeffs[i<<2]) << 24);
#else
pdpfb_write(priv, PDP_HSCOEFF8, 0xFF & THIS_PRIV->hscoeffs[i<<2]);
#endif
return 0;
}
#ifndef PDP_VID_VSCALE
static int pdpfb_vid_configure_vscale(struct pdpfb_priv *priv)
{
struct fb_info *info = &THIS_STREAM->info;
struct pdpfb_geom *geom = &THIS_STREAM->geom;
u32 blend2;
u32 line_double = 0;
u32 line_halve = 0;
if (geom->h) {
line_double = (geom->h > info->var.yres);
line_halve = (geom->h < info->var.yres);
}
blend2 = pdpfb_read(priv, THIS_STREAM->regs.blend2);
SET_FIELD(blend2, PDP_STRXBLEND2_LINEDOUBLE, line_double);
SET_FIELD(blend2, PDP_STRXBLEND2_LINEHALVE, line_halve);
pdpfb_write(priv, THIS_STREAM->regs.blend2, blend2);
return 0;
}
#else
static int pdpfb_vid_configure_vscale(struct pdpfb_priv *priv)
{
struct pdp_info *pdata = pdpfb_get_platform_data(priv);
u32 i;
u32 scale_ctrl, scale_size, vsinit;
u32 yres = THIS_STREAM->info.var.yres;
u32 geomh = THIS_STREAM->geom.h;
u32 vpitch = fix_div(11, yres, geomh);
int en = (geomh && geomh != yres);
int hs_before_vs = (THIS_STREAM->info.var.yres > pdata->linestore_len);
scale_ctrl = pdpfb_read(priv, PDP_SCALECTRL);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_VSCALEBP, !en);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_HSBEFOREVS, hs_before_vs);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_VSURUNCTRL, 1);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_VORDER, THIS_PRIV->vs_taps-1);
SET_FIELD(scale_ctrl, PDP_SCALECTRL_VPITCH, vpitch);
pdpfb_write(priv, PDP_SCALECTRL, scale_ctrl);
if (!geomh)
geomh = yres;
scale_size = pdpfb_read(priv, PDP_SCALESIZE);
SET_FIELD(scale_size, PDP_SCALESIZE_SCALEDHEIGHT, geomh - 1);
pdpfb_write(priv, PDP_SCALESIZE, scale_size);
if (!en)
return 0;
vsinit = pdpfb_read(priv, PDP_VSINIT);
SET_FIELD(vsinit, PDP_VSINIT_INITIAL1,
fix_div(11, THIS_PRIV->vs_taps, 2));
pdpfb_write(priv, PDP_VSINIT, vsinit);
for (i = 0; i < 4; ++i) {
u32 val = (0xFF & THIS_PRIV->vscoeffs[i<<2])
| ((0xFF & THIS_PRIV->vscoeffs[(i<<2) + 1]) << 8)
| ((0xFF & THIS_PRIV->vscoeffs[(i<<2) + 2]) << 16)
| ((0xFF & THIS_PRIV->vscoeffs[(i<<2) + 3]) << 24);
pdpfb_write(priv, PDP_VSCOEFF0 + (i<<2), val);
}
pdpfb_write(priv, PDP_VSCOEFF4, 0xFF & THIS_PRIV->vscoeffs[i<<2]);
return 0;
}
#endif
#ifdef PDP_SHARED_BASE
static void pdpfb_vid_apply_addr(void *arg, u32 mask)
{
struct pdpfb_priv *priv = (struct pdpfb_priv *)arg;
u32 tmp;
spin_lock(&THIS_PRIV->base_addr_lock);
tmp = pdpfb_read(priv, PDP_STR2CTRL);
SET_FIELD(tmp, PDP_STRXCTRL_BASEADDR,
THIS_PRIV->base_addr_y >> PDP_YADDR_ALIGN);
pdpfb_write(priv, PDP_STR2CTRL, tmp);
tmp = pdpfb_read(priv, PDP_STR2UADDR);
SET_FIELD(tmp, PDP_STR2UADDR_UBASEADDR,
THIS_PRIV->base_addr_u >> PDP_UADDR_ALIGN);
pdpfb_write(priv, PDP_STR2UADDR, tmp);
tmp = pdpfb_read(priv, PDP_STR2VADDR);
SET_FIELD(tmp, PDP_STR2VADDR_VBASEADDR,
THIS_PRIV->base_addr_v >> PDP_VADDR_ALIGN);
pdpfb_write(priv, PDP_STR2VADDR, tmp);
pdpfb_unregister_isr(pdpfb_vid_apply_addr, priv,
PDPFB_IRQ_VEVENT0);
tmp = pdpfb_read(priv, PDP_SKIPCTRL);
SET_FIELD(tmp, PDP_SKIPCTRL_HSKIP, THIS_PRIV->hskip);
#if PDP_REV >= 0x010001
SET_FIELD(tmp, PDP_SKIPCTRL_VSKIP, THIS_PRIV->vskip);
#endif
pdpfb_write(priv, PDP_SKIPCTRL, tmp);
spin_unlock(&THIS_PRIV->base_addr_lock);
}
#endif
static int pdpfb_vid_configure_addr(struct pdpfb_priv *priv)
{
struct fb_info *info = &THIS_STREAM->info;
u32 ybuf_offset, ybuf_start;
u32 ubuf_offset, ubuf_start;
u32 vbuf_offset, vbuf_start;
u32 hskip, xoff, vskip, yoff;
u32 str_ctrl;
u32 uv_half_stride = THIS_PRIV->planar.u_line_length
< THIS_PRIV->planar.y_line_length;
#ifdef PDP_SHARED_BASE
u32 u_addr;
unsigned long flags;
#else
u32 skip_ctrl;
#endif
str_ctrl = pdpfb_read(priv, PDP_STR2CTRL);
#ifndef PDP_SHARED_BASE
SET_FIELD(str_ctrl, PDP_STR2CTRL_UVHALFSTR, uv_half_stride);
#endif
pdpfb_write(priv, PDP_STR2CTRL, str_ctrl);
switch (info->var.nonstd) {
case PDP_VID_PIXFMT_420_PL8:
hskip = info->var.xoffset & 0x1f;
xoff = info->var.xoffset - hskip;
vskip = info->var.yoffset & 0x1;
yoff = info->var.yoffset - vskip;
ybuf_offset = xoff;
ubuf_offset = xoff >> 1;
vbuf_offset = ubuf_offset;
ybuf_offset += THIS_PRIV->planar.y_line_length * yoff ;
ubuf_offset += THIS_PRIV->planar.u_line_length * (yoff >> 1);
vbuf_offset += THIS_PRIV->planar.v_line_length * (yoff >> 1);
break;
case PDP_VID_PIXFMT_420_PL8IVU:
case PDP_VID_PIXFMT_420_PL8IUV:
hskip = info->var.xoffset & 0xf;
xoff = info->var.xoffset - hskip;
vskip = info->var.yoffset & 0x1;
yoff = info->var.yoffset - vskip;
ybuf_offset = xoff + THIS_PRIV->planar.y_line_length * yoff;
ubuf_offset = xoff +
THIS_PRIV->planar.y_line_length * (yoff >> 1);
vbuf_offset = ubuf_offset;
break;
default:
xoff = info->var.xoffset * info->var.bits_per_pixel / 8;
hskip = (xoff & 0xf) * 8 / info->var.bits_per_pixel;
vskip = 0;
yoff = info->var.yoffset;
ybuf_offset = xoff + info->fix.line_length * yoff;
ubuf_offset = ybuf_offset;
vbuf_offset = ybuf_offset;
break;
}
ybuf_start = THIS_PRIV->planar.y_offset + ybuf_offset;
ubuf_start = THIS_PRIV->planar.u_offset + ubuf_offset;
vbuf_start = THIS_PRIV->planar.v_offset + vbuf_offset;
#if PDP_REV < 0x010001
if (THIS_PRIV->hdecimation)
hskip &= ~0x3;
else
hskip &= ~0x1;
#endif
#ifndef PDP_SHARED_BASE
ybuf_start += info->fix.smem_start;
ubuf_start += info->fix.smem_start;
vbuf_start += info->fix.smem_start;
pdpfb_write(priv, PDP_STR2ADDR, ybuf_start >> PDP_YADDR_ALIGN);
pdpfb_write(priv, PDP_STR2UADDR, ubuf_start >> PDP_UADDR_ALIGN);
pdpfb_write(priv, PDP_STR2VADDR, vbuf_start >> PDP_VADDR_ALIGN);
skip_ctrl = pdpfb_read(priv, PDP_SKIPCTRL);
SET_FIELD(skip_ctrl, PDP_SKIPCTRL_HSKIP, hskip);
#if PDP_REV >= 0x010001
SET_FIELD(skip_ctrl, PDP_SKIPCTRL_VSKIP, vskip);
#endif
pdpfb_write(priv, PDP_SKIPCTRL, skip_ctrl);
#else
ybuf_start += THIS_STREAM->videomem_offset;
ubuf_start += THIS_STREAM->videomem_offset;
vbuf_start += THIS_STREAM->videomem_offset;
spin_lock_irqsave(&THIS_PRIV->base_addr_lock, flags);
u_addr = pdpfb_read(priv, PDP_STR2UADDR);
SET_FIELD(u_addr, PDP_STR2UADDR_UVHALFSTR, uv_half_stride);
pdpfb_write(priv, PDP_STR2UADDR, u_addr);
THIS_PRIV->base_addr_y = ybuf_start;
THIS_PRIV->base_addr_u = ubuf_start;
THIS_PRIV->base_addr_v = vbuf_start;
THIS_PRIV->hskip = hskip;
THIS_PRIV->vskip = vskip;
pdpfb_register_isr(pdpfb_vid_apply_addr, priv,
PDPFB_IRQ_VEVENT0);
spin_unlock_irqrestore(&THIS_PRIV->base_addr_lock, flags);
#endif
return 0;
}
static int pdpfb_vid_configure(struct pdpfb_priv *priv)
{
struct fb_info *info = &THIS_STREAM->info;
u32 str2surf;
str2surf = pdpfb_read(priv, PDP_STR2SURF);
SET_FIELD(str2surf, PDP_STRXSURF_WIDTH, info->var.xres - 1);
SET_FIELD(str2surf, PDP_STRXSURF_HEIGHT, info->var.yres - 1);
SET_FIELD(str2surf, PDP_STR2SURF_PIXFMT, THIS_PRIV->pixfmt);
SET_FIELD(str2surf, PDP_STR2SURF_USECSC, THIS_PRIV->csc.enable);
SET_FIELD(str2surf, PDP_STR2SURF_COSITED, THIS_PRIV->csc.cosited);
pdpfb_write(priv, PDP_STR2SURF, str2surf);
pdpfb_vid_change_mode(priv);
pdpfb_vid_configure_hscale(priv);
pdpfb_vid_configure_vscale(priv);
return 0;
}
static int pdpfb_vid_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct pdpfb_priv *priv = dev_get_drvdata(info->device);
void __user *argp = (void __user *)arg;
struct pdpfb_vid_csc csc;
struct pdpfb_vid_planar pl;
int err;
switch (cmd) {
/* get colour space conversion settings */
case PDPIO_GETCSC:
if (copy_to_user(argp, &THIS_PRIV->csc,
sizeof(THIS_PRIV->csc)))
return -EFAULT;
return 0;
/* set colour space conversion settings */
case PDPIO_SETCSC:
if (copy_from_user(&csc, argp, sizeof(csc)))
return -EFAULT;
csc.enable = (csc.enable != 0);
csc.cosited = (csc.cosited != 0);
if (csc.preset) {
if (csc.preset < ARRAY_SIZE(pdpfb_vid_csc_presets))
csc.coefs = pdpfb_vid_csc_presets[csc.preset];
else
return -EINVAL;
} else if (pdpfb_vid_check_csc(&csc.coefs))
return -EINVAL;
THIS_PRIV->csc = csc;
pdpfb_vid_configure_csc(priv);
if (copy_to_user(argp, &THIS_PRIV->csc,
sizeof(THIS_PRIV->csc)))
return -EFAULT;
return 0;
/* get planar YUV information */
case PDPIO_GETPLANAR:
if (copy_to_user(argp, &THIS_PRIV->planar,
sizeof(THIS_PRIV->planar)))
return -EFAULT;
return 0;
/* set planar YUV information */
case PDPIO_SETPLANAR:
if (copy_from_user(&pl, argp, sizeof(pl)))
return -EFAULT;
/* pixel format must be planar */
if (unlikely(info->fix.type != FB_TYPE_PLANES))
return -EINVAL;
err = pdpfb_vid_check_planar(info, &pl);
if (copy_to_user(argp, &pl, sizeof(pl)))
return -EFAULT;
if (err < 0)
return err;
/* override the planar setup */
THIS_PRIV->planar = pl;
THIS_PRIV->planar_override = 1;
THIS_PRIV->planar_ov_virtres[0] = info->var.xres_virtual;
THIS_PRIV->planar_ov_virtres[1] = info->var.yres_virtual;
THIS_PRIV->planar_ov_pixfmt = info->var.nonstd;
/* start using the new setup */
pdpfb_vid_update_planar(priv, info);
pdpfb_vid_change_mode(priv);
pdpfb_vid_configure_addr(priv);
return 0;
default:
return pdpfb_str_ioctl(priv, THIS_STREAM, cmd, arg);
}
return 0;
}
static int pdpfb_vid_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct pdpfb_priv *priv;
if (var->xoffset + info->var.xres > info->var.xres_virtual ||
var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
priv = dev_get_drvdata(info->device);
pdpfb_vid_configure_addr(priv);
return 0;
}
static int pdpfb_vid_set_par(struct fb_info *info)
{
struct pdpfb_priv *priv = dev_get_drvdata(info->device);
/* resolution may have changed, so scaling may need alteration */
pdpfb_vid_check_geom(priv, &THIS_STREAM->geom);
pdpfb_vid_set_geom(priv);
/* pixel format maps from nonstd, so may need updating */
THIS_PRIV->pixfmt = pdpfb_vid_nonstd_to_pixfmt(info->var.nonstd);
pdpfb_vid_update_planar(priv, info);
pdpfb_vid_configure(priv);
pdpfb_vid_configure_addr(priv);
pdpfb_update_margins(priv, THIS_STREAM);
return 0;
}
static struct fb_fix_screeninfo pdpfb_vid_fix = {
.id = "pdp_vid",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
#if PDP_REV < 0x010001
.xpanstep = 2,
.ypanstep = 2,
#else
.xpanstep = 1,
.ypanstep = 1,
#endif
.accel = FB_ACCEL_IMG_PDP_1,
};
static struct fb_ops pdpfb_vid_ops = {
.fb_setcolreg = pdpfb_setcolreg,
.fb_blank = pdpfb_blank,
.fb_pan_display = pdpfb_vid_pan_display,
.fb_check_var = pdpfb_vid_check_var,
.fb_set_par = pdpfb_vid_set_par,
.fb_ioctl = pdpfb_vid_ioctl,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static void pdpfb_vid_probe_caps(struct pdpfb_priv *priv)
{
int *m = THIS_PRIV->nonstd2pixfmt;
#ifdef PDP_GAMMA
THIS_STREAM->caps.gamma = PDP_GAMMA;
#endif
/* pixfmt values vary between revisions */
m[0] = -1;
m[PDP_VID_PIXFMT_420_PL8] = PDP_STR2SURF_PIXFMT_420_PL8;
m[PDP_VID_PIXFMT_422_UY0VY1_8888] = PDP_STR2SURF_PIXFMT_422_UY0VY1_8888;
m[PDP_VID_PIXFMT_422_VY0UY1_8888] = PDP_STR2SURF_PIXFMT_422_VY0UY1_8888;
m[PDP_VID_PIXFMT_422_Y0UY1V_8888] = PDP_STR2SURF_PIXFMT_422_Y0UY1V_8888;
m[PDP_VID_PIXFMT_422_Y0VY1U_8888] = PDP_STR2SURF_PIXFMT_422_Y0VY1U_8888;
#if PDP_REV < 0x010001
m[PDP_VID_PIXFMT_420_PL8IVU] = -1;
m[PDP_VID_PIXFMT_420_PL8IUV] = -1;
m[PDP_VID_PIXFMT_420_T88CP] = PDP_STR2SURF_PIXFMT_420_T88CP;
m[PDP_VID_PIXFMT_422_T88CP] = PDP_STR2SURF_PIXFMT_422_T88CP;
#else
m[PDP_VID_PIXFMT_420_PL8IVU] = PDP_STR2SURF_PIXFMT_420_PL8IVU;
m[PDP_VID_PIXFMT_420_PL8IUV] = PDP_STR2SURF_PIXFMT_420_PL8IUV;
m[PDP_VID_PIXFMT_420_T88CP] = -1;
m[PDP_VID_PIXFMT_422_T88CP] = -1;
#endif
}
static int pdpfb_vid_probe(struct pdpfb_priv *priv,
struct platform_device *pdev,
const struct fb_videomode *mode)
{
struct fb_info *info = &THIS_STREAM->info;
struct pdp_info *pdata = pdpfb_get_platform_data(priv);
int error;
pdpfb_vid_probe_caps(priv);
info->device = &pdev->dev;
info->fbops = &pdpfb_vid_ops;
info->var.xres = info->var.xres_virtual = (mode->xres/2);
info->var.yres = info->var.yres_virtual = (mode->yres/2);
info->var.width = pdata->lcd_size_cfg.width;
info->var.height = pdata->lcd_size_cfg.height;
info->var.activate = FB_ACTIVATE_NOW;
info->var.nonstd = PDP_VID_PIXFMT_422_Y0VY1U_8888;
error = pdpfb_vid_set_bpp(&info->var);
if (error)
goto err0;
THIS_PRIV->pixfmt = pdpfb_vid_nonstd_to_pixfmt(info->var.nonstd);
pdpfb_vid_update_planar(priv, info);
info->var.hsync_len = mode->hsync_len;
info->var.left_margin = mode->left_margin;
info->var.right_margin = mode->right_margin;
info->var.vsync_len = mode->vsync_len;
info->var.upper_margin = mode->upper_margin;
info->var.lower_margin = mode->lower_margin;
info->fix = pdpfb_vid_fix;
error = pdpfb_str_videomem_alloc(priv, THIS_STREAM);
if (error)
goto err0;
if (pdpfb_vid_required_mem(&info->var) > THIS_STREAM->videomem_len) {
error = -ENOMEM;
goto err1;
}
info->pseudo_palette = pdpfb_get_pseudo_palette(priv);
info->flags = FBINFO_FLAG_DEFAULT
| FBINFO_HWACCEL_XPAN
| FBINFO_HWACCEL_YPAN;
THIS_PRIV->csc.coefs = pdpfb_vid_csc_presets[PDP_VID_CSCPRESET_SDTV];
error = register_framebuffer(info);
if (error < 0)
goto err1;
dev_info(&pdev->dev, "registered video framebuffer (len=0x%lx)\n",
THIS_STREAM->videomem_len);
return 0;
err1:
pdpfb_str_videomem_free(THIS_STREAM);
err0:
return error;
}
static int pdpfb_vid_remove(struct pdpfb_priv *priv,
struct platform_device *pdev)
{
struct fb_info *info = &THIS_STREAM->info;
unregister_framebuffer(info);
pdpfb_str_videomem_free(THIS_STREAM);
return 0;
}
static struct pdpfb_vid_stream_priv pdpfb_vid_stream = {
.stream = {
.mem_pool = PDPFB_MEMPOOL_VIDMEM,
.videomem_len = 0,
.enable = 0,
.ckey = {
.ckey = 0x000000,
.mask = 0xFFFFFF,
},
.global_alpha = 0xFF,
.ops = {
.probe = pdpfb_vid_probe,
.remove = pdpfb_vid_remove,
.check_geom = pdpfb_vid_check_geom,
.set_geom = pdpfb_vid_set_geom,
.configure = pdpfb_vid_configure,
.configure_addr = pdpfb_vid_configure_addr,
},
.regs = {
.surf = PDP_STR2SURF,
.blend = PDP_STR2BLEND,
.blend2 = PDP_STR2BLEND2,
.ctrl = PDP_STR2CTRL,
.posn = PDP_STR2POSN,
#if PDP_REV >= 0x010001
.gamma = PDP_YUVGAMMA0,
.gamma_stride = PDP_YUVGAMMA_STRIDE,
#endif
},
},
.csc = {
.enable = 1,
.preset = PDP_VID_CSCPRESET_SDTV,
.cosited = 1,
},
#ifdef PDP_SHARED_BASE
.base_addr_lock = __SPIN_LOCK_UNLOCKED(pdpfb_vid_stream.base_addr_lock),
#endif
};
struct pdpfb_stream *pdpfb_vid_get_stream(void)
{
return THIS_STREAM;
}