#include "smi.h"
#include "smi_video.h"
#undef MIN
#undef ABS
#undef CLAMP
#undef ENTRIES
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define ABS(n) (((n) < 0) ? -(n) : (n))
#define CLAMP(v, min, max) (((v) < (min)) ? (min) : MIN(v, max))
#define ENTRIES(array) (sizeof(array) / sizeof((array)[0]))
#define nElems(x) (sizeof(x) / sizeof(x[0]))
#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
#if SMI_USE_VIDEO
#include "dixstruct.h"
static int SMI_AddEncoding(XF86VideoEncodingPtr enc, int i,
int norm, int input, int channel);
static void SMI_BuildEncodings(SMI_PortPtr p);
static XF86VideoAdaptorPtr SMI_SetupVideo(ScreenPtr pScreen);
static void SMI_ResetVideo(ScrnInfoPtr pScrn);
#if SMI_USE_CAPTURE
static int SMI_PutVideo(ScrnInfoPtr pScrn,
short vid_x, short vid_y, short drw_x, short drw_y,
short vid_w, short vid_h, short drw_w, short drw_h,
RegionPtr clipBoxes, pointer data);
#endif
static void SMI_StopVideo(ScrnInfoPtr pScrn, pointer data, Bool shutdown);
static int SMI_SetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
INT32 value, pointer data);
static int SMI_GetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
INT32 *value, pointer data);
static void SMI_QueryBestSize(ScrnInfoPtr pScrn, Bool motion,
short vid_w, short vid_h, short drw_w, short drw_h,
unsigned int *p_w, unsigned int *p_h, pointer data);
static int SMI_PutImage(ScrnInfoPtr pScrn,
short src_x, short src_y, short drw_x, short drw_y,
short src_w, short src_h, short drw_w, short drw_h,
int id, unsigned char *buf, short width, short height, Bool sync,
RegionPtr clipBoxes, pointer data);
static int SMI_QueryImageAttributes(ScrnInfoPtr pScrn,
int id, unsigned short *width, unsigned short *height,
int *picthes, int *offsets);
static Bool SMI_ClipVideo(ScrnInfoPtr pScrn, BoxPtr dst,
INT32 *x1, INT32 *y1, INT32 *x2, INT32 *y2,
RegionPtr reg, INT32 width, INT32 height);
static void SMI_DisplayVideo(ScrnInfoPtr pScrn, int id, int offset,
short width, short height, int pitch, int x1, int y1, int x2, int y2,
BoxPtr dstBox, short vid_w, short vid_h, short drw_w, short drw_h);
static void SMI_DisplayVideo0730(ScrnInfoPtr pScrn, int id, int offset,
short width, short height, int pitch, int x1, int y1, int x2, int y2,
BoxPtr dstBox, short vid_w, short vid_h, short drw_w, short drw_h);
static void SMI_BlockHandler(int i, pointer blockData, pointer pTimeout,
pointer pReadMask);
#if 0
static void SMI_WaitForSync(ScrnInfoPtr pScrn);
#endif
static void SMI_InitOffscreenImages(ScreenPtr pScreen);
static FBAreaPtr SMI_AllocateMemory(ScrnInfoPtr pScrn, FBAreaPtr area,
int numLines);
static int SMI_AllocSurface(ScrnInfoPtr pScrn,
int id, unsigned short width, unsigned short height,
XF86SurfacePtr surface);
static int SMI_FreeSurface(XF86SurfacePtr surface);
static int SMI_DisplaySurface(XF86SurfacePtr surface,
short vid_x, short vid_y, short drw_x, short drw_y,
short vid_w, short vid_h, short drw_w, short drw_h,
RegionPtr clipBoxes);
static int SMI_StopSurface(XF86SurfacePtr surface);
static int SMI_GetSurfaceAttribute(ScrnInfoPtr pScrn, Atom attr, INT32 *value);
static int SMI_SetSurfaceAttribute(ScrnInfoPtr pScrn, Atom attr, INT32 value);
static int SetAttr(ScrnInfoPtr pScrn, int i, int value);
static int SetAttrSAA7110(ScrnInfoPtr pScrn, int i, int value);
static int SetAttrSAA7111(ScrnInfoPtr pScrn, int i, int value);
static void SetKeyReg(SMIPtr pSmi, int reg, int value);
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,0,0)
static Bool RegionsEqual(RegionPtr A, RegionPtr B);
#endif
static Atom xvColorKey;
static Atom xvEncoding;
static Atom xvBrightness,xvCapBrightness, xvContrast, xvSaturation, xvHue;
static Atom xvInterlaced;
#define N_COMPOSITE_CHANNELS 4
#define N_SVIDEO_CHANNELS 2
#define N_VIDEO_INPUTS 2
typedef enum _VideoInput { COMPOSITE, SVIDEO } VideoInput;
typedef struct _VideoInputDataRec {
char* name;
} VideoInputDataRec;
static VideoInputDataRec VideoInputs[] = {
{ "composite" },
{ "svideo" }
};
#define N_VIDEO_NORMS 3
typedef enum _VideoNorm { PAL, NTSC, SECAM } VideoNorm;
typedef struct _VideoNormDataRec {
char* name;
unsigned long Wt;
unsigned long Wa;
unsigned long Ht;
unsigned long Ha;
unsigned long HStart;
unsigned long VStart;
XvRationalRec rate;
} VideoNormDataRec;
static VideoNormDataRec VideoNorms[] =
{
{"pal", 864, 704, 625, 576, 16, 16, { 1, 50 }},
{"ntsc", 858, 704, 525, 480, 21, 8, { 1001, 60000 }},
{"secam", 864, 7040, 625, 576, 31, 16, { 1, 50 }},
};
#define N_ENCODINGS ((N_VIDEO_NORMS) * (N_COMPOSITE_CHANNELS + N_SVIDEO_CHANNELS))
static XF86VideoFormatRec SMI_VideoFormats[] =
{
{ 15, TrueColor },
{ 16, TrueColor },
{ 24, TrueColor },
};
#define XV_ENCODING_NAME "XV_ENCODING"
#define XV_BRIGHTNESS_NAME "XV_BRIGHTNESS"
#define XV_CAPTURE_BRIGHTNESS_NAME "XV_CAPTURE_BRIGHTNESS"
#define XV_CONTRAST_NAME "XV_CONTRAST"
#define XV_SATURATION_NAME "XV_SATURATION"
#define XV_HUE_NAME "XV_HUE"
#define XV_COLORKEY_NAME "XV_COLORKEY"
#define XV_INTERLACED_NAME "XV_INTERLACED"
static XF86AttributeRec SMI_VideoAttributesSAA711x[N_ATTRS] = {
{XvSettable | XvGettable, 0, N_ENCODINGS-1, XV_ENCODING_NAME},
{XvSettable | XvGettable, 0, 255, XV_BRIGHTNESS_NAME},
{XvSettable | XvGettable, 0, 255, XV_CAPTURE_BRIGHTNESS_NAME},
{XvSettable | XvGettable, 0, 127, XV_CONTRAST_NAME},
{XvSettable | XvGettable, 0, 127, XV_SATURATION_NAME},
{XvSettable | XvGettable, -128, 127, XV_HUE_NAME},
{XvSettable | XvGettable, 0x000000, 0xFFFFFF, XV_COLORKEY_NAME},
{XvSettable | XvGettable, 0, 1, XV_INTERLACED_NAME},
};
static XF86AttributeRec SMI_VideoAttributes[2] = {
{XvSettable | XvGettable, 0, 255, XV_BRIGHTNESS_NAME},
{XvSettable | XvGettable, 0x000000, 0xFFFFFF, XV_COLORKEY_NAME},
};
static XF86ImageRec SMI_VideoImages[] =
{
XVIMAGE_YUY2,
XVIMAGE_YV12,
XVIMAGE_I420,
{
FOURCC_RV15,
XvRGB,
LSBFirst,
{ 'R', 'V' ,'1', '5',
0x00, '5', 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 },
16,
XvPacked,
1,
15,
0x001F, 0x03E0, 0x7C00,
0, 0, 0,
0, 0, 0,
0, 0, 0,
{ 'R', 'V', 'B' },
XvTopToBottom
},
{
FOURCC_RV16,
XvRGB,
LSBFirst,
{ 'R', 'V' ,'1', '6',
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 },
16,
XvPacked,
1,
16,
0x001F, 0x07E0, 0xF800,
0, 0, 0,
0, 0, 0,
0, 0, 0,
{ 'R', 'V', 'B' },
XvTopToBottom
},
{
FOURCC_RV24,
XvRGB,
LSBFirst,
{ 'R', 'V' ,'2', '4',
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 },
24,
XvPacked,
1,
24,
0x0000FF, 0x00FF00, 0xFF0000,
0, 0, 0,
0, 0, 0,
0, 0, 0,
{ 'R', 'V', 'B' },
XvTopToBottom
},
{
FOURCC_RV32,
XvRGB,
LSBFirst,
{ 'R', 'V' ,'3', '2',
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 },
32,
XvPacked,
1,
24,
0x0000FF, 0x00FF00, 0xFF0000,
0, 0, 0,
0, 0, 0,
0, 0, 0,
{ 'R', 'V', 'B' },
XvTopToBottom
},
};
static I2CByte SAA7111CompositeChannelSelect[N_COMPOSITE_CHANNELS][4] = {
{ 0x02, 0xC0, 0x09, 0x4A},
{ 0x02, 0xC1, 0x09, 0x4A},
{ 0x02, 0xC2, 0x09, 0x4A},
{ 0x02, 0xC3, 0x09, 0x4A},
};
static I2CByte SAA7111SVideoChannelSelect[N_SVIDEO_CHANNELS][4] = {
{ 0x02, 0xC6, 0x09, 0xCA},
{ 0x02, 0xC7, 0x09, 0xCA},
};
static I2CByte SAA7111VideoStd[3][8] = {
{0x06, 108, 0x07, 108, 0x08, 0x09, 0x0E, 0x01},
{0x06, 107, 0x07, 107, 0x08, 0x49, 0x0E, 0x01},
{0x06, 108, 0x07, 108, 0x08, 0x01, 0x0E, 0x51}
};
#if 0
static I2CByte SAA7110InitData[] =
{
0x00, 0x4C, 0x01, 0x3C, 0x02, 0x00, 0x03, 0xEF,
0x04, 0xBD, 0x05, 0xE2, 0x06, 0x00, 0x07, 0x00,
0x08, 0xF8, 0x09, 0xF8, 0x0A, 0x60, 0x0B, 0x60,
0x0C, 0x00, 0x0D, 0x80, 0x0E, 0x18, 0x0F, 0xD9,
0x10, 0x00, 0x11, 0x2B, 0x12, 0x40, 0x13, 0x40,
0x14, 0x42, 0x15, 0x1A, 0x16, 0xFF, 0x17, 0xDA,
0x18, 0xE6, 0x19, 0x90, 0x20, 0xD9, 0x21, 0x16,
0x22, 0x40, 0x23, 0x40, 0x24, 0x80, 0x25, 0x40,
0x26, 0x80, 0x27, 0x4F, 0x28, 0xFE, 0x29, 0x01,
0x2A, 0xCF, 0x2B, 0x0F, 0x2C, 0x03, 0x2D, 0x01,
0x2E, 0x83, 0x2F, 0x03, 0x30, 0x40, 0x31, 0x35,
0x32, 0x02, 0x33, 0x8C, 0x34, 0x03,
0x11, 0x2B, 0x0F, 0xD9,
0x06, 0x00, 0x0E, 0x18, 0x20, 0xD9, 0x21, 0x16,
0x22, 0x40, 0x2C, 0x03,
};
#endif
static I2CByte SAA7111InitData[] =
{
0x11, 0x1D,
0x02, 0xC0,
0x03, 0x23,
0x04, 0x00,
0x05, 0x00,
0x06, 108,
0x07, 108,
0x08, 0x88,
0x09, 0x41,
0x0A, 0x80,
0x0B, 0x40,
0x0C, 0x40,
0x0D, 0x00,
0x0E, 0x01,
0x10, 0x48,
0x12, 0x00,
0x13, 0x00,
0x15, 0x00,
0x16, 0x00,
0x17, 0x00,
};
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,0,0)
static Bool
RegionsEqual(
RegionPtr A,
RegionPtr B
)
{
int *dataA, *dataB;
int num;
ENTER_PROC("RegionsEqual");
num = REGION_NUM_RECTS(A);
if (num != REGION_NUM_RECTS(B))
{
LEAVE_PROC("RegionsEqual");
return(FALSE);
}
if ( (A->extents.x1 != B->extents.x1)
|| (A->extents.y1 != B->extents.y1)
|| (A->extents.x2 != B->extents.x2)
|| (A->extents.y2 != B->extents.y2)
)
{
LEAVE_PROC("RegionsEqual");
return(FALSE);
}
dataA = (int*) REGION_RECTS(A);
dataB = (int*) REGION_RECTS(B);
while (num--)
{
if ((dataA[0] != dataB[0]) || (dataA[1] != dataB[1]))
{
return(FALSE);
}
dataA += 2;
dataB += 2;
}
LEAVE_PROC("RegionsEqual");
return(TRUE);
}
#endif
static int
SMI_AddEncoding(XF86VideoEncodingPtr enc, int i,
int norm, int input, int channel)
{
char* norm_string;
char* input_string;
char channel_string[20];
ENTER_PROC("SMI_AddEncoding");
norm_string = VideoNorms[norm].name;
input_string = VideoInputs[input].name;
sprintf(channel_string, "%d", channel);
enc[i].id = i;
enc[i].name = xalloc(strlen(norm_string) +
strlen(input_string) +
strlen(channel_string)+3);
if (NULL == enc[i].name) {
LEAVE_PROC("SMI_AddEncoding");
return -1;
}
enc[i].width = VideoNorms[norm].Wa;
enc[i].height = VideoNorms[norm].Ha;
enc[i].rate = VideoNorms[norm].rate;
sprintf(enc[i].name,"%s-%s-%s", norm_string, input_string, channel_string);
LEAVE_PROC("SMI_AddEncoding");
return 0;
}
static void
SMI_BuildEncodings(SMI_PortPtr p)
{
int ch, n;
ENTER_PROC("SMI_BuildEncodings");
p->enc = xalloc(sizeof(XF86VideoEncodingRec) * N_ENCODINGS);
if (NULL == p->enc)
goto fail;
memset(p->enc,0,sizeof(XF86VideoEncodingRec) * N_ENCODINGS);
p->norm = xalloc(sizeof(int) * N_ENCODINGS);
if (NULL == p->norm)
goto fail;
memset(p->norm,0,sizeof(int) * N_ENCODINGS);
p->input = xalloc(sizeof(int) * N_ENCODINGS);
if (NULL == p->input)
goto fail;
memset(p->input,0,sizeof(int) * N_ENCODINGS);
p->channel = xalloc(sizeof(int) * N_ENCODINGS);
if (NULL == p->channel)
goto fail;
memset(p->channel,0,sizeof(int) * N_ENCODINGS);
p->nenc = 0;
for (ch = 0; ch < N_COMPOSITE_CHANNELS; ch++) {
for (n = 0; n < N_VIDEO_NORMS; n++) {
SMI_AddEncoding(p->enc, p->nenc, n, COMPOSITE, ch);
p->norm[p->nenc] = n;
p->input[p->nenc] = COMPOSITE;
p->channel[p->nenc] = ch;
p->nenc++;
}
}
for (ch = 0; ch < N_SVIDEO_CHANNELS; ch++) {
for (n = 0; n < N_VIDEO_NORMS; n++) {
SMI_AddEncoding(p->enc, p->nenc, n, SVIDEO, ch);
p->norm[p->nenc] = n;
p->input[p->nenc] = SVIDEO;
p->channel[p->nenc] = ch;
p->nenc++;
}
}
LEAVE_PROC("SMI_BuildEncodings");
return;
fail:
if (p->input) xfree(p->input);
p->input = NULL;
if (p->norm) xfree(p->norm);
p->norm = NULL;
if (p->channel) xfree(p->channel);
p->channel = NULL;
if (p->enc) xfree(p->enc);
p->enc = NULL;
p->nenc = 0;
LEAVE_PROC("SMI_BuildEncodings");
}
void
SMI_InitVideo(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
SMIPtr psmi = SMIPTR(pScrn);
XF86VideoAdaptorPtr *ptrAdaptors, *newAdaptors = NULL;
XF86VideoAdaptorPtr newAdaptor = NULL;
int numAdaptors;
ENTER_PROC("SMI_InitVideo");
numAdaptors = xf86XVListGenericAdaptors(pScrn, &ptrAdaptors);
DEBUG((VERBLEV, "numAdaptors=%d\n", numAdaptors));
if (psmi->rotate == 0)
{
newAdaptor = SMI_SetupVideo(pScreen);
DEBUG((VERBLEV, "newAdaptor=%p\n", newAdaptor));
SMI_InitOffscreenImages(pScreen);
}
if (newAdaptor != NULL)
{
if (numAdaptors == 0)
{
numAdaptors = 1;
ptrAdaptors = &newAdaptor;
}
else
{
newAdaptors = xalloc((numAdaptors + 1) *
sizeof(XF86VideoAdaptorPtr*));
if (newAdaptors != NULL)
{
memcpy(newAdaptors, ptrAdaptors,
numAdaptors * sizeof(XF86VideoAdaptorPtr));
newAdaptors[numAdaptors++] = newAdaptor;
ptrAdaptors = newAdaptors;
}
}
}
if (numAdaptors != 0)
{
DEBUG((VERBLEV, "ScreenInit %i\n",numAdaptors));
xf86XVScreenInit(pScreen, ptrAdaptors, numAdaptors);
}
if (newAdaptors != NULL)
{
xfree(newAdaptors);
}
LEAVE_PROC("SMI_InitVideo");
}
#if 0
static int
Scale(int i, int value, int min, int max)
{
return min + (value - SMI_VideoAttributes[i].min_value) * (max - min) /
(SMI_VideoAttributes[i].max_value - SMI_VideoAttributes[i].min_value);
}
#endif
static int
SetAttr(ScrnInfoPtr pScrn, int i, int value)
{
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
if (i < XV_ENCODING || i > XV_HUE)
return BadMatch;
value = CLAMP(value, SMI_VideoAttributes[i].min_value,
SMI_VideoAttributes[i].max_value);
if (i == XV_BRIGHTNESS) {
int my_value = (value <= 128? value + 128 : value - 128);
SetKeyReg(pSmi, 0x5C, 0xEDEDED | (my_value << 24));
} else if (pPort->I2CDev.SlaveAddr == SAA7110) {
return SetAttrSAA7110(pScrn, i, value);
}
else if (pPort->I2CDev.SlaveAddr == SAA7111) {
return SetAttrSAA7111(pScrn, i, value);
}
#if 0
else {
return XvBadAlloc;
}
#endif
return Success;
}
static int
SetAttrSAA7110(ScrnInfoPtr pScrn, int i, int value)
{
return XvBadAlloc;
}
static int
SetAttrSAA7111(ScrnInfoPtr pScrn, int i, int value)
{
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
if (i == XV_ENCODING) {
int norm;
int input;
int channel;
norm = pPort->norm[value];
input = pPort->input[value];
channel = pPort->channel[value];
DEBUG((VERBLEV, "SetAttribute XV_ENCODING: %d. norm=%d input=%d channel=%d\n",
value, norm, input, channel));
if (!xf86I2CWriteVec(&(pPort->I2CDev), SAA7111VideoStd[norm],
ENTRIES(SAA7111VideoStd[norm]) / 2)) {
return XvBadAlloc;
}
if (input == COMPOSITE) {
if (!xf86I2CWriteVec(&(pPort->I2CDev),
SAA7111CompositeChannelSelect[channel],
ENTRIES(SAA7111CompositeChannelSelect[channel]) / 2)) {
return XvBadAlloc;
}
}
else {
if (!xf86I2CWriteVec(&(pPort->I2CDev),
SAA7111SVideoChannelSelect[channel],
ENTRIES(SAA7111SVideoChannelSelect[channel]) / 2)) {
return XvBadAlloc;
}
}
}
else if (i >= XV_CAPTURE_BRIGHTNESS && i <= XV_HUE) {
int slave_adr = 0;
switch (i) {
case XV_CAPTURE_BRIGHTNESS:
DEBUG((VERBLEV, "SetAttribute XV_BRIGHTNESS: %d\n", value));
slave_adr = 0x0a;
break;
case XV_CONTRAST:
DEBUG((VERBLEV, "SetAttribute XV_CONTRAST: %d\n", value));
slave_adr = 0x0b;
break;
case XV_SATURATION:
DEBUG((VERBLEV, "SetAttribute XV_SATURATION: %d\n", value));
slave_adr = 0x0c;
break;
case XV_HUE:
DEBUG((VERBLEV, "SetAttribute XV_HUE: %d\n", value));
slave_adr = 0x0d;
break;
default:
return XvBadAlloc;
}
if (!xf86I2CWriteByte(&(pPort->I2CDev), slave_adr, (value & 0xff)))
return XvBadAlloc;
}
else {
return BadMatch;
}
{
I2CByte i2c_bytes[32];
int i;
xf86I2CReadBytes(&(pPort->I2CDev), 0, i2c_bytes, 32);
DEBUG((VERBLEV, "SAA7111 Registers\n"));
for (i=0; i<32; i++) {
DEBUG((VERBLEV, "%02X=%02X ", i, i2c_bytes[i]));
if ((i&7) == 7) DEBUG((VERBLEV, "\n"));
}
}
return Success;
}
static XF86VideoAdaptorPtr
SMI_SetupVideo(
ScreenPtr pScreen
)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr smiPortPtr;
XF86VideoAdaptorPtr ptrAdaptor;
ENTER_PROC("SMI_SetupVideo");
ptrAdaptor = xcalloc(1, sizeof(XF86VideoAdaptorRec) +
sizeof(DevUnion) + sizeof(SMI_PortRec));
if (ptrAdaptor == NULL)
{
LEAVE_PROC("SMI_SetupVideo");
return(NULL);
}
ptrAdaptor->type = XvInputMask
#if SMI_USE_CAPTURE
| XvOutputMask
| XvVideoMask
#endif
| XvImageMask
| XvWindowMask
;
ptrAdaptor->flags = VIDEO_OVERLAID_IMAGES
| VIDEO_CLIP_TO_VIEWPORT
;
ptrAdaptor->name = "Silicon Motion Lynx Series Video Engine";
ptrAdaptor->nPorts = 1;
ptrAdaptor->pPortPrivates = (DevUnion*) &ptrAdaptor[1];
ptrAdaptor->pPortPrivates[0].ptr = (pointer) &ptrAdaptor->pPortPrivates[1];
smiPortPtr = (SMI_PortPtr) ptrAdaptor->pPortPrivates[0].ptr;
SMI_BuildEncodings(smiPortPtr);
ptrAdaptor->nEncodings = smiPortPtr->nenc;
ptrAdaptor->pEncodings = smiPortPtr->enc;
#if 0
for (i = 0; i < nElems(SMI_VideoEncodings); i++)
{
SMI_VideoEncodings[i].width = pSmi->lcdWidth;
SMI_VideoEncodings[i].height = pSmi->lcdHeight;
}
#endif
ptrAdaptor->nFormats = nElems(SMI_VideoFormats);
ptrAdaptor->pFormats = SMI_VideoFormats;
ptrAdaptor->nAttributes = nElems(SMI_VideoAttributes);
ptrAdaptor->pAttributes = SMI_VideoAttributes;
ptrAdaptor->nImages = nElems(SMI_VideoImages);
ptrAdaptor->pImages = SMI_VideoImages;
#if SMI_USE_CAPTURE
if (pSmi->Chipset == SMI_COUGAR3DR)
ptrAdaptor->PutVideo = NULL;
else
ptrAdaptor->PutVideo = SMI_PutVideo;
ptrAdaptor->PutStill = NULL;
ptrAdaptor->GetVideo = NULL;
ptrAdaptor->GetStill = NULL;
#else
ptrAdaptor->PutVideo = NULL;
ptrAdaptor->PutStill = NULL;
ptrAdaptor->GetVideo = NULL;
ptrAdaptor->GetStill = NULL;
#endif
ptrAdaptor->StopVideo = SMI_StopVideo;
ptrAdaptor->SetPortAttribute = SMI_SetPortAttribute;
ptrAdaptor->GetPortAttribute = SMI_GetPortAttribute;
ptrAdaptor->QueryBestSize = SMI_QueryBestSize;
ptrAdaptor->PutImage = SMI_PutImage;
ptrAdaptor->QueryImageAttributes = SMI_QueryImageAttributes;
smiPortPtr->Attribute[XV_COLORKEY] = pSmi->videoKey;
smiPortPtr->Attribute[XV_INTERLACED] = pSmi->interlaced;
smiPortPtr->videoStatus = 0;
#if 0
if (xf86I2CProbeAddress(pSmi->I2C, SAA7111))
{
LEAVE_PROC("SMI_SetupVideo");
return(NULL);
}
DEBUG((VERBLEV, "SAA7111 detected\n"));
#endif
smiPortPtr->I2CDev.DevName = "SAA 7111A";
smiPortPtr->I2CDev.SlaveAddr = SAA7111;
smiPortPtr->I2CDev.pI2CBus = pSmi->I2C;
if (xf86I2CDevInit(&(smiPortPtr->I2CDev)))
{
if (xf86I2CWriteVec(&(smiPortPtr->I2CDev), SAA7111InitData,
ENTRIES(SAA7111InitData) / 2)) {
xvEncoding = MAKE_ATOM(XV_ENCODING_NAME);
xvHue = MAKE_ATOM(XV_HUE_NAME);
xvSaturation = MAKE_ATOM(XV_SATURATION_NAME);
xvContrast = MAKE_ATOM(XV_CONTRAST_NAME);
xvInterlaced = MAKE_ATOM(XV_INTERLACED_NAME);
DEBUG((VERBLEV, "SAA7111 intialized\n"));
} else {
xf86DestroyI2CDevRec(&(smiPortPtr->I2CDev),FALSE);
smiPortPtr->I2CDev.SlaveAddr = 0;
}
} else
smiPortPtr->I2CDev.SlaveAddr = 0;
#if defined(REGION_NULL)
REGION_NULL(pScreen, &smiPortPtr->clip);
#else
REGION_INIT(pScreen, &smiPortPtr->clip, NullBox, 0);
#endif
pSmi->ptrAdaptor = ptrAdaptor;
pSmi->BlockHandler = pScreen->BlockHandler;
pScreen->BlockHandler = SMI_BlockHandler;
xvColorKey = MAKE_ATOM(XV_COLORKEY_NAME);
xvBrightness = MAKE_ATOM(XV_BRIGHTNESS_NAME);
xvCapBrightness = MAKE_ATOM(XV_CAPTURE_BRIGHTNESS_NAME);
SMI_ResetVideo(pScrn);
LEAVE_PROC("SMI_SetupVideo");
return(ptrAdaptor);
}
static void
SMI_ResetVideo(
ScrnInfoPtr pScrn
)
{
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
int r, g, b;
ENTER_PROC("SMI_ResetVideo");
SetAttr(pScrn, XV_ENCODING, 0);
SetAttr(pScrn, XV_BRIGHTNESS, 128);
SetAttr(pScrn, XV_CAPTURE_BRIGHTNESS, 128);
SetAttr(pScrn, XV_CONTRAST, 71);
SetAttr(pScrn, XV_SATURATION, 64);
SetAttr(pScrn, XV_HUE, 0);
switch (pScrn->depth)
{
case 8:
SetKeyReg(pSmi, FPR04, pPort->Attribute[XV_COLORKEY] & 0x00FF);
SetKeyReg(pSmi, FPR08, 0);
break;
case 15:
case 16:
SetKeyReg(pSmi, FPR04, pPort->Attribute[XV_COLORKEY] & 0xFFFF);
SetKeyReg(pSmi, FPR08, 0);
break;
default:
r = (pPort->Attribute[XV_COLORKEY] & pScrn->mask.red) >> pScrn->offset.red;
g = (pPort->Attribute[XV_COLORKEY] & pScrn->mask.green) >> pScrn->offset.green;
b = (pPort->Attribute[XV_COLORKEY] & pScrn->mask.blue) >> pScrn->offset.blue;
SetKeyReg(pSmi, FPR04, ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3));
SetKeyReg(pSmi, FPR08, 0);
break;
}
SetKeyReg(pSmi, FPR5C, 0xEDEDED | (pPort->Attribute[XV_BRIGHTNESS] << 24));
LEAVE_PROC("SMI_ResetVideo");
}
#if SMI_USE_CAPTURE
static int
SMI_PutVideo(
ScrnInfoPtr pScrn,
short vid_x,
short vid_y,
short drw_x,
short drw_y,
short vid_w,
short vid_h,
short drw_w,
short drw_h,
RegionPtr clipBoxes,
pointer data
)
{
SMI_PortPtr pPort = (SMI_PortPtr) data;
SMIPtr pSmi = SMIPTR(pScrn);
CARD32 vid_pitch, vid_address;
CARD32 vpr00, cpr00;
int xscale, yscale;
BoxRec dstBox;
INT32 x1, y1, x2, y2;
int norm;
int areaHeight, width, height, fbPitch;
int top, left;
ENTER_PROC("SMI_PutVideo");
DEBUG((VERBLEV, "Interlaced Video %d\n", pPort->Attribute[XV_INTERLACED]));
if (!pPort->Attribute[XV_INTERLACED]) {
vid_h /= 2;
}
norm = pPort->norm[pPort->Attribute[XV_ENCODING]];
vid_x += VideoNorms[norm].HStart;
vid_y += VideoNorms[norm].VStart;
vid_x &= ~1;
DEBUG((VERBLEV, "vid_x=%d vid_y=%d drw_x=%d drw_y=%d "
"vid_w=%d vid_h=%d drw_w=%d drw_h=%d\n",
vid_x, vid_y, drw_x, drw_y, vid_w, vid_h, drw_w, drw_h));
x1 = vid_x;
y1 = vid_y;
x2 = vid_x + vid_w;
y2 = vid_y + vid_h;
width = vid_w;
height = vid_h;
dstBox.x1 = drw_x;
dstBox.y1 = drw_y;
dstBox.x2 = drw_x + drw_w;
dstBox.y2 = drw_y + drw_h;
#if 1
if (!SMI_ClipVideo(pScrn, &dstBox, &x1, &y1, &x2, &y2, clipBoxes, width,
height))
#else
if (!xf86XVClipVideoHelper(&dstBox, &x1, &y1, &x2, &y2, clipBoxes, width,
height))
#endif
{
LEAVE_PROC("SMI_PutVideo");
return(Success);
}
DEBUG((VERBLEV, "Clip: x1=%d y1=%d x2=%d y2=%d\n", x1 >> 16, y1 >> 16, x2 >> 16, y2 >> 16));
dstBox.x1 -= pScrn->frameX0;
dstBox.y1 -= pScrn->frameY0;
dstBox.x2 -= pScrn->frameX0;
dstBox.y2 -= pScrn->frameY0;
vid_pitch = (vid_w * 2 + 7) & ~7;
vpr00 = READ_VPR(pSmi, 0x00) & ~0x0FF000FF;
cpr00 = READ_CPR(pSmi, 0x00) & ~0x000FFF00;
if (pPort->Attribute[XV_INTERLACED]) {
vpr00 |= 0x0010000E;
}
else {
vpr00 |= 0x0130000E;
}
if (pPort->Attribute[XV_INTERLACED]) {
cpr00 |= 0x01000601;
}
else {
cpr00 |= 0x01000801;
}
if (pSmi->ByteSwap)
cpr00 |= 0x00004000;
fbPitch = pSmi->Stride;
if (pSmi->Bpp != 3)
{
fbPitch *= pSmi->Bpp;
}
if (vid_w <= drw_w)
{
xscale = (256 * vid_w / drw_w) & 0xFF;
}
else if (vid_w / 2 <= drw_w)
{
xscale = (128 * vid_w / drw_w) & 0xFF;
width /= 2;
vid_pitch /= 2;
cpr00 |= 0x00010000;
}
else if (vid_w / 4 <= drw_w)
{
xscale = (64 * vid_w / drw_w) & 0xFF;
width /= 4;
vid_pitch /= 4;
cpr00 |= 0x00020000;
}
else
{
xscale = 0;
width /= 4;
vid_pitch /= 4;
cpr00 |= 0x00020000;
}
if (vid_h <= drw_h)
{
yscale = (256 * vid_h / drw_h) & 0xFF;
}
else if (vid_h / 2 <= drw_h)
{
yscale = (128 * vid_h / drw_h) & 0xFF;
height /= 2;
cpr00 |= 0x00040000;
}
else if (vid_h / 4 <= drw_h)
{
yscale = (64 * vid_h / drw_h) & 0xFF;
height /= 4;
cpr00 |= 0x00080000;
}
else
{
yscale = 0;
height /= 4;
cpr00 |= 0x00080000;
}
do
{
areaHeight = (vid_pitch * height + fbPitch - 1) / fbPitch;
DEBUG((VERBLEV, "SMI_AllocateMemory: vid_pitch=%d height=%d fbPitch=%d areaHeight=%d\n",
vid_pitch, height, fbPitch, areaHeight));
pPort->area = SMI_AllocateMemory(pScrn, pPort->area, areaHeight);
if (pPort->area == NULL)
{
if ((cpr00 & 0x000C0000) == 0)
{
yscale = (128 * vid_h / drw_h) & 0xFF;
height = vid_h / 2;
cpr00 |= 0x00040000;
}
else if (cpr00 & 0x00040000)
{
yscale = (64 * vid_h / drw_h) & 0xFF;
height = vid_h / 4;
cpr00 ^= 0x000C0000;
}
else
{
if ((cpr00 & 0x00030000) == 0)
{
xscale = (128 * vid_w / drw_w) & 0xFF;
width = vid_w / 2;
cpr00 |= 0x00010000;
}
else if (cpr00 & 0x00010000)
{
xscale = (64 * vid_w / drw_w) & 0xFF;
width = vid_w / 4;
cpr00 ^= 0x00030000;
}
else
{
DEBUG((VERBLEV, "allocate error\n"));
LEAVE_PROC("SMI_PutVideo");
return(BadAlloc);
}
}
}
}
while (pPort->area == NULL);
DEBUG((VERBLEV, "xscale==%d yscale=%d width=%d height=%d\n",
xscale, yscale, width, height));
vid_address = (pPort->area->box.y1 * fbPitch);
DEBUG((VERBLEV, "test RegionsEqual\n"));
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,0,0)
if (!RegionsEqual(&pPort->clip, clipBoxes))
#else
if (!REGION_EQUAL(pScrn->pScreen, &pPort->clip, clipBoxes))
#endif
{
DEBUG((VERBLEV, "RegionCopy\n"));
REGION_COPY(pScrn->pScreen, &pPort->clip, clipBoxes);
DEBUG((VERBLEV, "FillKey\n"));
xf86XVFillKeyHelper(pScrn->pScreen, pPort->Attribute[XV_COLORKEY], clipBoxes);
}
left = x1 >> 16;
top = y1 >> 16;
width = (x2 - x1) >> 16;
height = (y2 - y1) >> 16;
OUT_SEQ(pSmi, 0x21, IN_SEQ(pSmi, 0x21) & ~0x04);
WRITE_VPR(pSmi, 0x54, READ_VPR(pSmi, 0x54) | 0x00200000);
#if 0
SMI_WaitForSync(pScrn);
#endif
WRITE_VPR(pSmi, 0x14, dstBox.x1 + (dstBox.y1 << 16));
WRITE_VPR(pSmi, 0x18, dstBox.x2 + (dstBox.y2 << 16));
WRITE_VPR(pSmi, 0x20, (vid_pitch / 8) + ((vid_pitch / 8) << 16));
WRITE_VPR(pSmi, 0x24, (xscale << 8) + yscale);
if (pPort->Attribute[XV_INTERLACED]) {
WRITE_VPR(pSmi, 0x28, dstBox.x1 + (dstBox.y1 << 16));
WRITE_VPR(pSmi, 0x2C, dstBox.x2 + (dstBox.y2 << 16));
WRITE_VPR(pSmi, 0x34, (vid_pitch / 8) + ((vid_pitch / 8) << 16));
WRITE_VPR(pSmi, 0x38, (xscale << 8) + yscale);
WRITE_VPR(pSmi, 0x1C, vid_address / 8);
WRITE_VPR(pSmi, 0x30, vid_address / 8);
WRITE_VPR(pSmi, 0x48, vid_address / 8);
WRITE_VPR(pSmi, 0x4C, vid_address / 8 + vid_pitch / 8);
WRITE_CPR(pSmi, 0x04, left + ((top/2) << 16));
WRITE_CPR(pSmi, 0x08, width + ((height/2) << 16));
WRITE_CPR(pSmi, 0x0C, vid_address / 8);
WRITE_CPR(pSmi, 0x10, vid_address / 8 + vid_pitch / 8);
WRITE_CPR(pSmi, 0x14, 2*(vid_pitch / 8) + ((2*(vid_pitch / 8)) << 16));
}
else {
WRITE_CPR(pSmi, 0x04, left + (top << 16));
WRITE_CPR(pSmi, 0x08, width + (height << 16));
WRITE_CPR(pSmi, 0x0C, vid_address / 8);
WRITE_CPR(pSmi, 0x10, vid_address / 8);
WRITE_CPR(pSmi, 0x14, (vid_pitch / 8) + ((vid_pitch / 8) << 16));
}
WRITE_CPR(pSmi, 0x00, cpr00);
WRITE_VPR(pSmi, 0x00, vpr00);
pPort->videoStatus = CLIENT_VIDEO_ON;
DEBUG((VERBLEV, "SMI_PutVideo success\n"));
LEAVE_PROC("SMI_PutVideo");
return(Success);
}
#endif
static void
SMI_StopVideo(
ScrnInfoPtr pScrn,
pointer data,
Bool shutdown
)
{
SMI_PortPtr pPort = (SMI_PortPtr) data;
SMIPtr pSmi = SMIPTR(pScrn);
ENTER_PROC("SMI_StopVideo");
REGION_EMPTY(pScrn->pScreen, &pPort->clip);
if (shutdown)
{
if (pPort->videoStatus & CLIENT_VIDEO_ON)
{
if (pSmi->Chipset == SMI_COUGAR3DR)
{
WRITE_FPR(pSmi, FPR00, READ_FPR(pSmi, 0x00) & ~(FPR00_VWIENABLE));
}
else
{
WRITE_VPR(pSmi, 0x00, READ_VPR(pSmi, 0x00) & ~0x01000008);
}
#if SMI_USE_CAPTURE
if (pSmi->Chipset != SMI_COUGAR3DR)
{
WRITE_CPR(pSmi, 0x00, READ_CPR(pSmi, 0x00) & ~0x00000001);
WRITE_VPR(pSmi, 0x54, READ_VPR(pSmi, 0x54) & ~0x00F00000);
}
#endif
}
if (pPort->area != NULL)
{
xf86FreeOffscreenArea(pPort->area);
pPort->area = NULL;
}
pPort->videoStatus = 0;
}
else
{
if (pPort->videoStatus & CLIENT_VIDEO_ON)
{
pPort->videoStatus |= OFF_TIMER;
pPort->offTime = currentTime.milliseconds + OFF_DELAY;
}
}
LEAVE_PROC("SMI_StopVideo");
}
static int
SMI_SetPortAttribute(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 value,
pointer data
)
{
int res;
SMI_PortPtr pPort = (SMI_PortPtr) data;
SMIPtr pSmi = SMIPTR(pScrn);
ENTER_PROC("SMI_SetPortAttribute");
if (attribute == xvColorKey) {
int r, g, b;
pPort->Attribute[XV_COLORKEY] = value;
switch (pScrn->depth)
{
case 8:
SetKeyReg(pSmi, FPR04, value & 0x00FF);
break;
case 15:
case 16:
SetKeyReg(pSmi, FPR04, value & 0xFFFF);
break;
default:
r = (value & pScrn->mask.red) >> pScrn->offset.red;
g = (value & pScrn->mask.green) >> pScrn->offset.green;
b = (value & pScrn->mask.blue) >> pScrn->offset.blue;
SetKeyReg(pSmi, FPR04,
((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3));
break;
}
res = Success;
}
else if (attribute == xvInterlaced) {
pPort->Attribute[XV_INTERLACED] = (value != 0);
res = Success;
}
else if (attribute == xvEncoding) {
res = SetAttr(pScrn, XV_ENCODING, value);
}
else if (attribute == xvBrightness) {
res = SetAttr(pScrn, XV_BRIGHTNESS, value);
}
else if (attribute == xvCapBrightness) {
res = SetAttr(pScrn, XV_CAPTURE_BRIGHTNESS, value);
}
else if (attribute == xvContrast) {
res = SetAttr(pScrn, XV_CONTRAST, value);
}
else if (attribute == xvSaturation) {
res = SetAttr(pScrn, XV_SATURATION, value);
}
else if (attribute == xvHue) {
res = SetAttr(pScrn, XV_HUE, value);
}
else {
res = BadMatch;
}
LEAVE_PROC("SMI_SetPortAttribute");
return(res);
}
static int
SMI_GetPortAttribute(
ScrnInfoPtr pScrn,
Atom attribute,
INT32 *value,
pointer data
)
{
SMI_PortPtr pPort = (SMI_PortPtr) data;
ENTER_PROC("SMI_GetPortAttribute");
if (attribute == xvEncoding)
*value = pPort->Attribute[XV_ENCODING];
else if (attribute == xvBrightness)
*value = pPort->Attribute[XV_BRIGHTNESS];
else if (attribute == xvCapBrightness)
*value = pPort->Attribute[XV_CAPTURE_BRIGHTNESS];
else if (attribute == xvContrast)
*value = pPort->Attribute[XV_CONTRAST];
else if (attribute == xvSaturation)
*value = pPort->Attribute[XV_SATURATION];
else if (attribute == xvHue)
*value = pPort->Attribute[XV_HUE];
else if (attribute == xvColorKey)
*value = pPort->Attribute[XV_COLORKEY];
else
{
LEAVE_PROC("SMI_GetPortAttribute");
return(BadMatch);
}
LEAVE_PROC("SMI_GetPortAttribute");
return(Success);
}
static void
SMI_QueryBestSize(
ScrnInfoPtr pScrn,
Bool motion,
short vid_w,
short vid_h,
short drw_w,
short drw_h,
unsigned int *p_w,
unsigned int *p_h,
pointer data
)
{
SMIPtr pSmi = SMIPTR(pScrn);
ENTER_PROC("SMI_QueryBestSize");
*p_w = min(drw_w, pSmi->lcdWidth);
*p_h = min(drw_h, pSmi->lcdHeight);
LEAVE_PROC("SMI_QueryBestSize");
}
static int
SMI_PutImage(
ScrnInfoPtr pScrn,
short src_x,
short src_y,
short drw_x,
short drw_y,
short src_w,
short src_h,
short drw_w,
short drw_h,
int id,
unsigned char *buf,
short width,
short height,
Bool sync,
RegionPtr clipBoxes,
pointer data
)
{
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
INT32 x1, y1, x2, y2;
int bpp = 0;
int fbPitch, srcPitch, srcPitch2 = 0, dstPitch, areaHeight;
BoxRec dstBox;
CARD32 offset, offset2 = 0, offset3 = 0, tmp;
int left, top, nPixels, nLines;
unsigned char *dstStart;
ENTER_PROC("SMI_PutImage");
x1 = src_x;
y1 = src_y;
x2 = src_x + src_w;
y2 = src_y + src_h;
dstBox.x1 = drw_x;
dstBox.y1 = drw_y;
dstBox.x2 = drw_x + drw_w;
dstBox.y2 = drw_y + drw_h;
if (!SMI_ClipVideo(pScrn, &dstBox, &x1, &y1, &x2, &y2, clipBoxes, width,
height))
{
LEAVE_PROC("SMI_PutImage");
return(Success);
}
dstBox.x1 -= pScrn->frameX0;
dstBox.y1 -= pScrn->frameY0;
dstBox.x2 -= pScrn->frameX0;
dstBox.y2 -= pScrn->frameY0;
if (pSmi->Bpp == 3)
{
fbPitch = pSmi->Stride;
}
else
{
fbPitch = pSmi->Stride * pSmi->Bpp;
}
switch (id)
{
case FOURCC_YV12:
srcPitch = (width + 3) & ~3;
offset2 = srcPitch * height;
srcPitch2 = ((width >> 1) + 3) & ~3;
offset3 = offset2 + (srcPitch2 * (height >> 1));
dstPitch = ((width << 1) + 15) & ~15;
break;
case FOURCC_I420:
srcPitch = (width + 3) & ~3;
offset3 = srcPitch * height;
srcPitch2 = ((width >> 1) + 3) & ~3;
offset2 = offset3 + (srcPitch2 * (height >> 1));
dstPitch = ((width << 1) + 15) & ~15;
break;
case FOURCC_RV24:
bpp = 3;
srcPitch = width * bpp;
dstPitch = (srcPitch + 15) & ~15;
break;
case FOURCC_RV32:
bpp = 4;
srcPitch = width * bpp;
dstPitch = (srcPitch + 15) & ~15;
break;
case FOURCC_YUY2:
case FOURCC_RV15:
case FOURCC_RV16:
default:
bpp = 2;
srcPitch = width * bpp;
dstPitch = (srcPitch + 15) & ~15;
break;
}
areaHeight = ((dstPitch * height) + fbPitch - 1) / fbPitch;
pPort->area = SMI_AllocateMemory(pScrn, pPort->area, areaHeight);
if (pPort->area == NULL)
{
LEAVE_PROC("SMI_PutImage");
return(BadAlloc);
}
top = y1 >> 16;
left = (x1 >> 16) & ~1;
nPixels = ((((x2 + 0xFFFF) >> 16) + 1) & ~1) - left;
left *= bpp;
offset = (pPort->area->box.y1 * fbPitch) + (top * dstPitch);
dstStart = pSmi->FBBase + offset + left;
switch(id) {
case FOURCC_YV12:
case FOURCC_I420:
top &= ~1;
tmp = ((top >> 1) * srcPitch2) + (left >> 2);
offset2 += tmp;
offset3 += tmp;
if(id == FOURCC_I420) {
tmp = offset2;
offset2 = offset3;
offset3 = tmp;
}
nLines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top;
xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + (left >> 1),
buf + offset2, buf + offset3, dstStart,
srcPitch, srcPitch2, dstPitch, nLines, nPixels);
break;
case FOURCC_UYVY:
case FOURCC_YUY2:
default:
buf += (top * srcPitch) + left;
nLines = ((y2 + 0xffff) >> 16) - top;
xf86XVCopyPacked(buf, dstStart, srcPitch, dstPitch, nLines, nPixels);
break;
}
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,0,0)
if (!RegionsEqual(&pPort->clip, clipBoxes))
#else
if (!REGION_EQUAL(pScrn->pScreen, &pPort->clip, clipBoxes))
#endif
{
REGION_COPY(pScrn->pScreen, &pPort->clip, clipBoxes);
xf86XVFillKeyHelper(pScrn->pScreen, pPort->Attribute[XV_COLORKEY],
clipBoxes);
}
if (pSmi->Chipset != SMI_COUGAR3DR)
SMI_DisplayVideo(pScrn, id, offset, width, height, dstPitch, x1, y1, x2, y2,
&dstBox, src_w, src_h, drw_w, drw_h);
else
SMI_DisplayVideo0730(pScrn, id, offset, width, height, dstPitch, x1, y1, x2, y2,
&dstBox, src_w, src_h, drw_w, drw_h);
pPort->videoStatus = CLIENT_VIDEO_ON;
LEAVE_PROC("SMI_PutImage");
return(Success);
}
static int
SMI_QueryImageAttributes(
ScrnInfoPtr pScrn,
int id,
unsigned short *width,
unsigned short *height,
int *pitches,
int *offsets
)
{
SMIPtr pSmi = SMIPTR(pScrn);
int size, tmp;
ENTER_PROC("SMI_QueryImageAttributes");
if (*width > pSmi->lcdWidth)
{
*width = pSmi->lcdWidth;
}
if (*height > pSmi->lcdHeight)
{
*height = pSmi->lcdHeight;
}
*width = (*width + 1) & ~1;
if (offsets != NULL)
{
offsets[0] = 0;
}
switch (id)
{
case FOURCC_YV12:
case FOURCC_I420:
*height = (*height + 1) & ~1;
size = (*width + 3) & ~3;
if (pitches != NULL)
{
pitches[0] = size;
}
size *= *height;
if (offsets != NULL)
{
offsets[1] = size;
}
tmp = ((*width >> 1) + 3) & ~3;
if (pitches != NULL)
{
pitches[1] = pitches[2] = tmp;
}
tmp *= (*height >> 1);
size += tmp;
if (offsets != NULL)
{
offsets[2] = size;
}
size += tmp;
break;
case FOURCC_YUY2:
case FOURCC_RV15:
case FOURCC_RV16:
default:
size = *width * 2;
if (pitches != NULL)
{
pitches[0] = size;
}
size *= *height;
break;
case FOURCC_RV24:
size = *width * 3;
if (pitches != NULL)
{
pitches[0] = size;
}
size *= *height;
break;
case FOURCC_RV32:
size = *width * 4;
if (pitches != NULL)
{
pitches[0] = size;
}
size *= *height;
break;
}
LEAVE_PROC("SMI_QueryImageAttributes");
return(size);
}
#if 0
static void
SMI_WaitForSync(
ScrnInfoPtr pScrn
)
{
SMIPtr pSmi = SMIPTR(pScrn);
vgaHWPtr hwp = VGAHWPTR(pScrn);
int vgaIOBase = hwp->IOBase;
int vgaCRIndex = vgaIOBase + VGA_CRTC_INDEX_OFFSET;
int vgaCRData = vgaIOBase + VGA_CRTC_DATA_OFFSET;
VerticalRetraceWait();
}
#endif
static Bool
SMI_ClipVideo(
ScrnInfoPtr pScrn,
BoxPtr dst,
INT32 *x1,
INT32 *y1,
INT32 *x2,
INT32 *y2,
RegionPtr reg,
INT32 width,
INT32 height
)
{
ScreenPtr pScreen = pScrn->pScreen;
INT32 vscale, hscale;
BoxPtr extents = REGION_EXTENTS(pScreen, reg);
int diff;
ENTER_PROC("SMI_ClipVideo");
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
extents->x1 = max(extents->x1, pScrn->frameX0);
extents->y1 = max(extents->y1, pScrn->frameY0);
hscale = ((*x2 - *x1) << 16) / (dst->x2 - dst->x1);
vscale = ((*y2 - *y1) << 16) / (dst->y2 - dst->y1);
*x1 <<= 16; *y1 <<= 16;
*x2 <<= 16; *y2 <<= 16;
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
diff = extents->x1 - dst->x1;
if (diff > 0)
{
dst->x1 = extents->x1;
*x1 += diff * hscale;
}
diff = extents->y1 - dst->y1;
if (diff > 0)
{
dst->y1 = extents->y1;
*y1 += diff * vscale;
}
diff = dst->x2 - extents->x2;
if (diff > 0)
{
dst->x2 = extents->x2;
*x2 -= diff * hscale;
}
diff = dst->y2 - extents->y2;
if (diff > 0)
{
dst->y2 = extents->y2;
*y2 -= diff * vscale;
}
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
if (*x1 < 0)
{
diff = (-*x1 + hscale - 1) / hscale;
dst->x1 += diff;
*x1 += diff * hscale;
}
if (*y1 < 0)
{
diff = (-*y1 + vscale - 1) / vscale;
dst->y1 += diff;
*y1 += diff * vscale;
}
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
#if 0
delta = *x2 - (width << 16);
if (delta > 0)
{
diff = (delta + hscale - 1) / hscale;
dst->x2 -= diff;
*x2 -= diff * hscale;
}
delta = *y2 - (height << 16);
if (delta > 0)
{
diff = (delta + vscale - 1) / vscale;
dst->y2 -= diff;
*y2 -= diff * vscale;
}
#endif
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
if ((*x1 >= *x2) || (*y1 >= *y2))
{
LEAVE_PROC("SMI_ClipVideo");
return(FALSE);
}
if ( (dst->x1 != extents->x1) || (dst->y1 != extents->y1)
|| (dst->x2 != extents->x2) || (dst->y2 != extents->y2)
)
{
RegionRec clipReg;
REGION_INIT(pScreen, &clipReg, dst, 1);
REGION_INTERSECT(pScreen, reg, reg, &clipReg);
REGION_UNINIT(pScreen, &clipReg);
}
DEBUG((VERBLEV, "ClipVideo(%d): x1=%d y1=%d x2=%d y2=%d\n", __LINE__, *x1 >> 16, *y1 >> 16, *x2 >> 16, *y2 >> 16));
LEAVE_PROC("SMI_ClipVideo");
return(TRUE);
}
static void
SMI_DisplayVideo(
ScrnInfoPtr pScrn,
int id,
int offset,
short width,
short height,
int pitch,
int x1,
int y1,
int x2,
int y2,
BoxPtr dstBox,
short vid_w,
short vid_h,
short drw_w,
short drw_h
)
{
SMIPtr pSmi = SMIPTR(pScrn);
CARD32 vpr00;
int hstretch, vstretch;
ENTER_PROC("SMI_DisplayVideo");
vpr00 = READ_VPR(pSmi, 0x00) & ~0x0CB800FF;
switch (id)
{
case FOURCC_YV12:
case FOURCC_I420:
case FOURCC_YUY2:
vpr00 |= 0x6;
break;
case FOURCC_RV15:
vpr00 |= 0x1;
break;
case FOURCC_RV16:
vpr00 |= 0x2;
break;
case FOURCC_RV24:
vpr00 |= 0x4;
break;
case FOURCC_RV32:
vpr00 |= 0x3;
break;
}
if (drw_w > vid_w)
{
hstretch = (2560 * vid_w / drw_w + 5) / 10;
}
else
{
hstretch = 0;
}
if (drw_h > vid_h)
{
vstretch = (2560 * vid_h / drw_h + 5) / 10;
vpr00 |= 1 << 21;
}
else
{
vstretch = 0;
}
#if 0
SMI_WaitForSync(pScrn);
#endif
WRITE_VPR(pSmi, 0x00, vpr00 | (1 << 3) | (1 << 20));
WRITE_VPR(pSmi, 0x14, (dstBox->x1) | (dstBox->y1 << 16));
WRITE_VPR(pSmi, 0x18, (dstBox->x2) | (dstBox->y2 << 16));
WRITE_VPR(pSmi, 0x1C, offset >> 3);
WRITE_VPR(pSmi, 0x20, (pitch >> 3) | ((pitch >> 3) << 16));
WRITE_VPR(pSmi, 0x24, (hstretch << 8) | vstretch);
LEAVE_PROC("SMI_DisplayVideo");
}
static void
SMI_DisplayVideo0730(
ScrnInfoPtr pScrn,
int id,
int offset,
short width,
short height,
int pitch,
int x1,
int y1,
int x2,
int y2,
BoxPtr dstBox,
short vid_w,
short vid_h,
short drw_w,
short drw_h
)
{
SMIPtr pSmi = SMIPTR(pScrn);
CARD32 fpr00;
int hstretch, vstretch;
ENTER_PROC("SMI_DisplayVideo0730");
fpr00 = READ_FPR(pSmi, 0x00) & ~(FPR00_MASKBITS);
switch (id)
{
case FOURCC_YV12:
case FOURCC_I420:
case FOURCC_YUY2:
fpr00 |= FPR00_FMT_YUV422;
break;
case FOURCC_RV15:
fpr00 |= FPR00_FMT_15P;
break;
case FOURCC_RV16:
fpr00 |= FPR00_FMT_16P;
break;
case FOURCC_RV24:
fpr00 |= FPR00_FMT_24P;
break;
case FOURCC_RV32:
fpr00 |= FPR00_FMT_32P;
break;
}
if (drw_w > vid_w)
{
hstretch = (8192 * vid_w / drw_w);
}
else
{
hstretch = 0;
}
if (drw_h > vid_h)
{
vstretch = (8192 * vid_h / drw_h);
}
else
{
vstretch = 0;
}
WRITE_FPR(pSmi, FPR00, fpr00 | FPR00_VWIENABLE | FPR00_VWIKEYENABLE);
WRITE_FPR(pSmi, FPR14, (dstBox->x1) | (dstBox->y1 << 16));
WRITE_FPR(pSmi, FPR18, (dstBox->x2) | (dstBox->y2 << 16));
WRITE_FPR(pSmi, FPR1C, offset >> 3);
WRITE_FPR(pSmi, FPR20, (pitch >> 3) | ((pitch >> 3) << 16));
WRITE_FPR(pSmi, FPR24, (hstretch & 0xFF00) | ((vstretch & 0xFF00)>>8));
WRITE_FPR(pSmi, FPR68, ((hstretch & 0x00FF)<<8) | (vstretch & 0x00FF));
LEAVE_PROC("SMI_DisplayVideo0730");
}
static void
SMI_BlockHandler(
int i,
pointer blockData,
pointer pTimeout,
pointer pReadMask
)
{
ScreenPtr pScreen = screenInfo.screens[i];
ScrnInfoPtr pScrn = xf86Screens[i];
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
pScreen->BlockHandler = pSmi->BlockHandler;
(*pScreen->BlockHandler)(i, blockData, pTimeout, pReadMask);
pScreen->BlockHandler = SMI_BlockHandler;
if (pPort->videoStatus & TIMER_MASK)
{
UpdateCurrentTime();
if (pPort->videoStatus & OFF_TIMER)
{
if (pPort->offTime < currentTime.milliseconds)
{
if (pSmi->Chipset == SMI_COUGAR3DR)
{
WRITE_FPR(pSmi, FPR00, READ_FPR(pSmi, 0x00) & ~(FPR00_VWIENABLE));
}
else
{
WRITE_VPR(pSmi, 0x00, READ_VPR(pSmi, 0x00) & ~0x00000008);
}
pPort->videoStatus = FREE_TIMER;
pPort->freeTime = currentTime.milliseconds + FREE_DELAY;
}
}
else
{
if (pPort->freeTime < currentTime.milliseconds)
{
xf86FreeOffscreenArea(pPort->area);
pPort->area = NULL;
}
pPort->videoStatus = 0;
}
}
}
#if 0
static int
SMI_SendI2C(
ScrnInfoPtr pScrn,
CARD8 device,
char *devName,
SMI_I2CDataPtr i2cData
)
{
SMIPtr pSmi = SMIPTR(pScrn);
I2CDevPtr dev;
int status = Success;
ENTER_PROC("SMI_SendI2C");
if (pSmi->I2C == NULL)
{
LEAVE_PROC("SMI_SendI2C");
return(BadAlloc);
}
dev = xf86CreateI2CDevRec();
if (dev == NULL)
{
LEAVE_PROC("SMI_SendI2C");
return(BadAlloc);
}
dev->DevName = devName;
dev->SlaveAddr = device;
dev->pI2CBus = pSmi->I2C;
if (!xf86I2CDevInit(dev))
{
status = BadAlloc;
}
else
{
while (i2cData->address != 0xFF || i2cData->data != 0xFF)
{
if (!xf86I2CWriteByte(dev, i2cData->address, i2cData->data))
{
status = BadAlloc;
break;
}
i2cData++;
}
}
xf86DestroyI2CDevRec(dev, TRUE);
LEAVE_PROC("SMI_SendI2C");
return(status);
}
#endif
static void
SMI_InitOffscreenImages(
ScreenPtr pScreen
)
{
XF86OffscreenImagePtr offscreenImages;
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
SMIPtr pSmi = SMIPTR(pScrn);
SMI_PortPtr pPort = (SMI_PortPtr) pSmi->ptrAdaptor->pPortPrivates[0].ptr;
ENTER_PROC("SMI_InitOffscreenImages");
offscreenImages = xalloc(sizeof(XF86OffscreenImageRec));
if (offscreenImages == NULL)
{
LEAVE_PROC("SMI_InitOffscreenImages");
return;
}
offscreenImages->image = SMI_VideoImages;
offscreenImages->flags = VIDEO_OVERLAID_IMAGES
| VIDEO_CLIP_TO_VIEWPORT;
offscreenImages->alloc_surface = SMI_AllocSurface;
offscreenImages->free_surface = SMI_FreeSurface;
offscreenImages->display = SMI_DisplaySurface;
offscreenImages->stop = SMI_StopSurface;
offscreenImages->getAttribute = SMI_GetSurfaceAttribute;
offscreenImages->setAttribute = SMI_SetSurfaceAttribute;
offscreenImages->max_width = pSmi->lcdWidth;
offscreenImages->max_height = pSmi->lcdHeight;
if (!pPort->I2CDev.SlaveAddr) {
offscreenImages->num_attributes = nElems(SMI_VideoAttributes);
offscreenImages->attributes = SMI_VideoAttributes;
} else {
offscreenImages->num_attributes =
nElems(SMI_VideoAttributesSAA711x);
offscreenImages->attributes = SMI_VideoAttributesSAA711x;
}
xf86XVRegisterOffscreenImages(pScreen, offscreenImages, 1);
LEAVE_PROC("SMI_InitOffscreenImages");
}
static FBAreaPtr
SMI_AllocateMemory(
ScrnInfoPtr pScrn,
FBAreaPtr area,
int numLines
)
{
ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex];
ENTER_PROC("SMI_AllocateMemory");
if (area != NULL)
{
if ((area->box.y2 - area->box.y1) >= numLines)
{
LEAVE_PROC("SMI_AllocateMemory (area->box.y2 - area->box.y1) >= numLines ok");
return(area);
}
if (xf86ResizeOffscreenArea(area, pScrn->displayWidth, numLines))
{
LEAVE_PROC("SMI_AllocateMemory xf86ResizeOffscreenArea ok");
return(area);
}
xf86FreeOffscreenArea(area);
}
area = xf86AllocateOffscreenArea(pScreen, pScrn->displayWidth, numLines, 0,
NULL, NULL, NULL);
if (area == NULL)
{
int maxW, maxH;
xf86QueryLargestOffscreenArea(pScreen, &maxW, &maxH, 0,
FAVOR_WIDTH_THEN_AREA, PRIORITY_EXTREME);
DEBUG((VERBLEV, "QueryLargestOffscreenArea maxW=%d maxH=%d displayWidth=%d numlines=%d\n",
maxW, maxH, pScrn->displayWidth, numLines));
if ((maxW < pScrn->displayWidth) || (maxH < numLines))
{
LEAVE_PROC("SMI_AllocateMemory (maxW < pScrn->displayWidth) || (maxH < numLines)");
return(NULL);
}
xf86PurgeUnlockedOffscreenAreas(pScreen);
area = xf86AllocateOffscreenArea(pScreen, pScrn->displayWidth, numLines,
0, NULL, NULL, NULL);
}
DEBUG((VERBLEV, "area = %p\n", area));
LEAVE_PROC("SMI_AllocateMemory");
return(area);
}
static int
SMI_AllocSurface(
ScrnInfoPtr pScrn,
int id,
unsigned short width,
unsigned short height,
XF86SurfacePtr surface
)
{
SMIPtr pSmi = SMIPTR(pScrn);
int numLines, pitch, fbPitch, bpp;
SMI_OffscreenPtr ptrOffscreen;
FBAreaPtr area;
ENTER_PROC("SMI_AllocSurface");
if ((width > pSmi->lcdWidth) || (height > pSmi->lcdHeight))
{
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
if (pSmi->Bpp == 3)
{
fbPitch = pSmi->Stride;
}
else
{
fbPitch = pSmi->Stride * pSmi->Bpp;
}
width = (width + 1) & ~1;
switch (id)
{
case FOURCC_YV12:
case FOURCC_I420:
case FOURCC_YUY2:
case FOURCC_RV15:
case FOURCC_RV16:
bpp = 2;
break;
case FOURCC_RV24:
bpp = 3;
break;
case FOURCC_RV32:
bpp = 4;
break;
default:
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
pitch = (width * bpp + 15) & ~15;
numLines = ((height * pitch) + fbPitch - 1) / fbPitch;
area = SMI_AllocateMemory(pScrn, NULL, numLines);
if (area == NULL)
{
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
surface->pitches = xalloc(sizeof(int));
if (surface->pitches == NULL)
{
xf86FreeOffscreenArea(area);
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
surface->offsets = xalloc(sizeof(int));
if (surface->offsets == NULL)
{
xfree(surface->pitches);
xf86FreeOffscreenArea(area);
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
ptrOffscreen = xalloc(sizeof(SMI_OffscreenRec));
if (ptrOffscreen == NULL)
{
xfree(surface->offsets);
xfree(surface->pitches);
xf86FreeOffscreenArea(area);
LEAVE_PROC("SMI_AllocSurface");
return(BadAlloc);
}
surface->pScrn = pScrn;
surface->id = id;
surface->width = width;
surface->height = height;
surface->pitches[0] = pitch;
surface->offsets[0] = area->box.y1 * fbPitch;
surface->devPrivate.ptr = (pointer) ptrOffscreen;
ptrOffscreen->area = area;
ptrOffscreen->isOn = FALSE;
LEAVE_PROC("SMI_AllocSurface");
return(Success);
}
static int
SMI_FreeSurface(
XF86SurfacePtr surface
)
{
SMI_OffscreenPtr ptrOffscreen = (SMI_OffscreenPtr) surface->devPrivate.ptr;
ENTER_PROC("SMI_FreeSurface");
if (ptrOffscreen->isOn)
{
SMI_StopSurface(surface);
}
xf86FreeOffscreenArea(ptrOffscreen->area);
xfree(surface->pitches);
xfree(surface->offsets);
xfree(surface->devPrivate.ptr);
LEAVE_PROC("SMI_FreeSurface");
return(Success);
}
static int
SMI_DisplaySurface(
XF86SurfacePtr surface,
short vid_x,
short vid_y,
short drw_x,
short drw_y,
short vid_w,
short vid_h,
short drw_w,
short drw_h,
RegionPtr clipBoxes
)
{
SMI_OffscreenPtr ptrOffscreen = (SMI_OffscreenPtr) surface->devPrivate.ptr;
SMIPtr pSmi = SMIPTR(surface->pScrn);
SMI_PortPtr pPort = pSmi->ptrAdaptor->pPortPrivates[0].ptr;
INT32 x1, y1, x2, y2;
BoxRec dstBox;
ENTER_PROC("SMI_DisplaySurface");
x1 = vid_x;
x2 = vid_x + vid_w;
y1 = vid_y;
y2 = vid_y + vid_h;
dstBox.x1 = drw_x;
dstBox.x2 = drw_x + drw_w;
dstBox.y1 = drw_y;
dstBox.y2 = drw_y + drw_h;
if (!SMI_ClipVideo(surface->pScrn, &dstBox, &x1, &y1, &x2, &y2, clipBoxes,
surface->width, surface->height))
{
LEAVE_PROC("SMI_DisplaySurface");
return(Success);
}
dstBox.x1 -= surface->pScrn->frameX0;
dstBox.y1 -= surface->pScrn->frameY0;
dstBox.x2 -= surface->pScrn->frameX0;
dstBox.y2 -= surface->pScrn->frameY0;
xf86XVFillKeyHelper(surface->pScrn->pScreen,
pPort->Attribute[XV_COLORKEY], clipBoxes);
if (pSmi->Chipset != SMI_COUGAR3DR)
{
SMI_ResetVideo(surface->pScrn);
SMI_DisplayVideo(surface->pScrn, surface->id, surface->offsets[0],
surface->width, surface->height, surface->pitches[0], x1, y1, x2,
y2, &dstBox, vid_w, vid_h, drw_w, drw_h);
}
else
{
SMI_ResetVideo(surface->pScrn);
SMI_DisplayVideo0730(surface->pScrn, surface->id, surface->offsets[0],
surface->width, surface->height, surface->pitches[0], x1, y1, x2,
y2, &dstBox, vid_w, vid_h, drw_w, drw_h);
}
ptrOffscreen->isOn = TRUE;
if (pPort->videoStatus & CLIENT_VIDEO_ON)
{
REGION_EMPTY(surface->pScrn->pScreen, &pPort->clip);
UpdateCurrentTime();
pPort->videoStatus = FREE_TIMER;
pPort->freeTime = currentTime.milliseconds + FREE_DELAY;
}
LEAVE_PROC("SMI_DisplaySurface");
return(Success);
}
static int
SMI_StopSurface(
XF86SurfacePtr surface
)
{
SMI_OffscreenPtr ptrOffscreen = (SMI_OffscreenPtr) surface->devPrivate.ptr;
ENTER_PROC("SMI_StopSurface");
if (ptrOffscreen->isOn)
{
SMIPtr pSmi = SMIPTR(surface->pScrn);
if (pSmi->Chipset == SMI_COUGAR3DR)
{
WRITE_FPR(pSmi, FPR00, READ_FPR(pSmi, 0x00) & ~(FPR00_VWIENABLE));
}
else
{
WRITE_VPR(pSmi, 0x00, READ_VPR(pSmi, 0x00) & ~0x00000008);
}
ptrOffscreen->isOn = FALSE;
}
LEAVE_PROC("SMI_StopSurface");
return(Success);
}
static int
SMI_GetSurfaceAttribute(
ScrnInfoPtr pScrn,
Atom attr,
INT32 *value
)
{
SMIPtr pSmi = SMIPTR(pScrn);
return(SMI_GetPortAttribute(pScrn, attr, value,
(pointer) pSmi->ptrAdaptor->pPortPrivates[0].ptr));
}
static int
SMI_SetSurfaceAttribute(
ScrnInfoPtr pScrn,
Atom attr,
INT32 value
)
{
SMIPtr pSmi = SMIPTR(pScrn);
return(SMI_SetPortAttribute(pScrn, attr, value,
(pointer) pSmi->ptrAdaptor->pPortPrivates[0].ptr));
}
static void
SetKeyReg(SMIPtr pSmi, int reg, int value)
{
if (pSmi->Chipset == SMI_COUGAR3DR)
{
WRITE_FPR(pSmi, reg, value);
}
else
{
WRITE_VPR(pSmi, reg, value);
}
}
#else
void SMI_InitVideo(ScreenPtr pScreen) {}
#endif