/*
 * Video driver for PXA27x(Bulverde) LCD Controller (bvdd)
 * by AGAWA Koji <i (AT) atty (DOT) jp>
 * (C) 2004
 */
/* English in this source code is written by machine translation.
   Meaning also not leading, permitting. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <linux/fb.h>

#include "config.h"
#include "video_out.h"
#include "video_out_internal.h"
#include "sub.h"
#include "aspect.h"
#include "mp_msg.h"

#include "drivers/bvdd/bvdd.h"

#define UNUSED(v) ((void)(v))

static vo_info_t info = {
    "PXA27x(Bulverde) LCD Controller",
    "bvdd",
    "AGAWA Koji <i (AT) atty (DOT) jp>",
    "for Sharp Linux Zaurus SL-C3000/SL-C1000"
};

LIBVO_EXTERN(bvdd);

// ----------------------------------------------------------------
#define MAX_FRAMES 3
typedef struct vidix_yuv_s
{
	unsigned y,u,v;
}vidix_yuv_t;

typedef struct vidix_rect_s
{
	unsigned x,y,w,h;	/* in pixels */
	vidix_yuv_t pitch;	/* line-align in bytes */
}vidix_rect_t;

typedef struct w100_yuv_planes_s {
	uint8_t *y;
	uint8_t *u;
	uint8_t *v;
} yuv_planes_t;

typedef struct yuv_plane_s {
    bvdd_vram_plane_t y;
    bvdd_vram_plane_t u;
    bvdd_vram_plane_t v;
} yuv_plane_t;

typedef struct w100_priv_s {
    uint32_t format;

    /* w100 info */
    int vram_size[2];		/*  */
    void *vram_addr[2];		/* address */
    yuv_planes_t frame_addrs[MAX_FRAMES];
    yuv_planes_t frame_offsets[MAX_FRAMES];
    int is_graphic_window_enabled;
    int eq_brightness;		/* for mplayer */
    int display_brightness;	/* for w100 */

    /* overlay info */
    uint16_t overlay_handle;
    int overlay_pos_x;
    int overlay_pos_y;
    int overlay_expand_h;
    int overlay_expand_v;
    int overlay_pitch_y;
    int overlay_pitch_u;
    int overlay_pitch_v;
} w100_priv_t;

static char *st_bvdd_devfile = "/dev/bvdd";
static int st_bvdd_fd = -1;
static uint32_t st_format;
static int st_src_width;
static int st_src_height;
static int st_current_frame;	/* current frame to display */
static yuv_plane_t st_planes[MAX_FRAMES];
static vidix_yuv_t dstrides;
static bvdd_frame_t st_bvdd_frame;
static int st_overlay_pos_x;
static int st_overlay_pos_y;
static int st_overlay_pitch_y;
static int st_overlay_pitch_u;
static int st_overlay_pitch_v;

/* ---------------------------------------------------------------- */
#define LCD_SHARP_QVGA 0
#define LCD_SHARP_VGA  1
static int st_lcd_mode = LCD_SHARP_VGA;

static int change_resolution(int mode)
{
    int xres, yres;
    int fd;

    struct fb_var_screeninfo vinfo;

    switch (mode) {
    case LCD_SHARP_QVGA:
	xres = 240;
	yres = 320;
	break;
    case LCD_SHARP_VGA:
	xres = 480;
	yres = 640;
	break;
    default:
	return;
    }

    fd = open("/dev/fb0", O_RDWR);
    if (fd < 0)
	return;

    ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
    vinfo.xres = xres;
    vinfo.xres_virtual = xres;
    vinfo.yres = yres;
    vinfo.yres_virtual = yres;
    ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo);

    close(fd);

    st_lcd_mode = mode;
}

/* ---------------------------------------------------------------- */
static void draw_alpha(int x0,int y0, int w,int h,
		       unsigned char* src, unsigned char *srca, int stride)
{
#if 1
    uint8_t *psrc, *psrca, *pdst;
    pdst = st_planes[st_current_frame].y.addr.virt;
    pdst += (x0 * dstrides.y) + (dstrides.y - 1 - y0);
    psrc = src;
    psrca = srca;
    while (h--) {
	int j;
	for (j = 0; j < w; ++j) {
	    if (psrca[j])
		pdst[j * dstrides.y] =
		    ((pdst[j * dstrides.y] * psrca[j]) >> 8) + psrc[j];
	}
	psrc += stride;
	psrca += stride;
	pdst -= 1;
    }
#else
    uint32_t apitch, bespitch;
    void *lvo_mem;
    lvo_mem = st_frame_addrs[st_current_frame].y;
    apitch = st_overlay_pitch_y - 1;
    switch (st_format) {
    case IMGFMT_YV12:
    case IMGFMT_IYUV:
    case IMGFMT_I420:
    case IMGFMT_YVU9:
    case IMGFMT_IF09:
    case IMGFMT_Y8:
    case IMGFMT_Y800:
	bespitch = (st_src_width + apitch) & (~apitch);
	vo_draw_alpha_yv12(w,h,src,srca,stride,lvo_mem+bespitch*y0+x0,bespitch);
	break;
#if 0
    case IMGFMT_YUY2:
	bespitch = (priv->src_width*2 + apitch) & (~apitch);
	vo_draw_alpha_yuy2(w,h,src,srca,stride,lvo_mem+bespitch*y0+2*x0,bespitch);
	break;
    case IMGFMT_UYVY:
	bespitch = (priv->src_width*2 + apitch) & (~apitch);
	vo_draw_alpha_yuy2(w,h,src,srca,stride,lvo_mem+bespitch*y0+2*x0+1,bespitch);
	break;
    case IMGFMT_RGB32:
    case IMGFMT_BGR32:
	bespitch = (priv->src_width*4 + apitch) & (~apitch);
	vo_draw_alpha_rgb32(w,h,src,srca,stride,lvo_mem+y0*bespitch+4*x0,bespitch);
	break;
    case IMGFMT_RGB24:
    case IMGFMT_BGR24:
	bespitch = (priv->src_width*3 + apitch) & (~apitch);
	vo_draw_alpha_rgb24(w,h,src,srca,stride,lvo_mem+y0*bespitch+3*x0,bespitch);
	break;
    case IMGFMT_RGB16:
    case IMGFMT_BGR16:
	bespitch = (priv->src_width*2 + apitch) & (~apitch);
	vo_draw_alpha_rgb16(w,h,src,srca,stride,lvo_mem+y0*bespitch+2*x0,bespitch);
	break;
    case IMGFMT_RGB15:
    case IMGFMT_BGR15:
	bespitch = (priv->src_width*2 + apitch) & (~apitch);
	vo_draw_alpha_rgb15(w,h,src,srca,stride,lvo_mem+y0*bespitch+2*x0,bespitch);
	break;
#endif
    default:
	return;
    }
#endif
}

/*
RL_XQ_480x640_1500_128.avi
image: 0x401f1e40 0x4024e3c0 0x4023b7c0 stride: 0x1e0 0xf0 0xf0 w:480 h: 16 x:  0 y: 16
image: 0x401f3c40 0x4024eb40 0x4023bf40 stride: 0x1e0 0xf0 0xf0 w:480 h: 16 x:  0 y: 32
 */
static uint32_t bvdd_draw_slice_420(uint8_t *image[], int stride[],
				     int w, int h, int x, int y)
{
#if 1
    uint8_t *src;
    uint8_t *dest;
    int i;

    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: bvdd_draw_slice_420: frame=%d\n", st_current_frame);
    /* Plane Y */
    dest = st_planes[st_current_frame].y.addr.virt;
    dest += dstrides.y * y + x;
    src = image[0];
#if 0
    __asm__ __volatile__ (
			  " \n\t"
			 "1: \n\t"
			  "mov r0, %[w] \n\t"
			 "2: \n\t"
			  "wldrd wr0, [ %[src] ] \n\t"
			  "pld [%[src], #32] \n\t"
			  "wldrd wr1, [ %[src], #8] \n\t"
			  "subs r0, r0, #1 \n\t"
			  "wldrd wr2, [ %[src], #16] \n\t"
			  "wldrd wr3, [ %[src], #24] \n\t"
			  "add %[src], %[src], #32 \n\t"
			  "wstrd wr0, [ %[dest]] \n\t"
			  "wstrd wr1, [ %[dest], #8] \n\t"
			  "wstrd wr2, [ %[dest], #16] \n\t"
			  "wstrd wr3, [ %[dest], #24] \n\t"
			  "add %[dest], %[dest], #32 \n\t"
			  "bne 2b \n\t"
			  "add %[src], %[src], %[srcdiff] \n\t"
			  "add %[dest], %[dest], %[destdiff] \n\t"
			  "subs %[h], %[h], #1 \n\t"
			  "bne 1b \n\t"
			  " \n\t"
			  " \n\t"
			  " \n\t"
			  " \n\t"
			  :
			  : [src]"r"(src), [dest]"r"(dest), [h]"r"(h), [w]"r"(w / 32),
			  [srcdiff]"r"(stride[0] - ((w >> 5) << 5)), [destdiff]"r"(dstrides.y - ((w >> 5) << 5))
			  : "memory", "r0");
#else
    for (i = 0; i < h; ++i) {
	memcpy(dest, src, w);
	src += stride[0];
	dest += dstrides.y;
    }
#endif

    /* Plane V */
    dest = st_planes[st_current_frame].u.addr.virt;
    dest += (dstrides.y / 2) * (y / 2) + x;
    src = image[1];
    for (i = 0; i < h / 2; ++i) {
	memcpy(dest, src, w / 2);
	src += stride[1];
	dest += dstrides.y / 2;
    }

    /* Plane U */
    dest = st_planes[st_current_frame].v.addr.virt;
    dest += (dstrides.y / 2) * (y / 2) + x;
/*     dest += dstrides.u * y / 4 + x; */
    src = image[2];
    for (i = 0; i < h / 2; ++i) {
	memcpy(dest, src, w / 2);
	src += stride[2];
	dest += dstrides.y / 2;
    }

#endif
    return 0;
}

#if 0
static uint32_t bvdd_draw_slice_420(uint8_t *image[], int stride[],
				     int w, int h, int x, int y)
{
    uint8_t *src;
    uint8_t *dest;
    int i;

    /* Plane Y */
    dest = st_frame_addrs[st_current_frame].y;
    dest += dstrides.y * y + x;
    src = image[0];
    for (i = 0; i < h; ++i) {
	memcpy(dest, src, w);
	src += stride[0];
	dest += dstrides.y;
    }

    /* Plane V */
    dest = st_frame_addrs[st_current_frame].u;
    dest += dstrides.v * y / 4 + x;
    src = image[1];
    for (i = 0; i < h / 2; ++i) {
	memcpy(dest, src, w / 2);
	src += stride[1];
	dest += dstrides.v / 2;
    }

    /* Plane U */
    dest = st_frame_addrs[st_current_frame].v;
    dest += dstrides.u * y / 4 + x;
    src = image[2];
    for (i = 0; i < h / 2; ++i) {
	memcpy(dest, src, w / 2);
	src += stride[2];
	dest += dstrides.u / 2;
    }

    return 0;
}
#endif

static uint32_t bvdd_draw_slice_packed(uint8_t *image[], int stride[],
					int w, int h, int x, int y)
{
#if 0
    uint8_t *src;
    uint8_t *dest;
    int i;

    dest = st_w100_mem + vidix_play.offsets[st_next_frame] + vidix_play.offset.y;
    dest += dstrides.y * y + x;
    src = image[0];
    for (i = 0; i < h; ++i) {
	memcpy(dest, src, w * st_image_bpp);
	src += stride[0];
	dest += dstrides.y;
    }
#endif
    return 0;
}

static uint32_t bvdd_get_image(mp_image_t *mpi)
{
#if 0
    mp_msg(MSGT_VO, MSGL_V, "vo_w100: w100_get_image called.\n");

    if (mpi->type == MP_IMGTYPE_STATIC && st_num_frames > 1)
	return VO_FALSE;
    if (mpi->flags & MP_IMGFLAG_READABLE)
	return VO_FALSE; /* slow video ram */
    if (((mpi->stride[0] == dstrides.y &&
	  (!(mpi->flags & MP_IMGFLAG_PLANAR) ||
	   (mpi->stride[1] == dstrides.u && mpi->stride[2]==dstrides.v)))
	 || (mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))) &&
	(!(vidix_play.flags & VID_PLAY_INTERLEAVED_UV))) {
	if (mpi->flags & MP_IMGFLAG_ACCEPT_WIDTH) {
	    // check if only width is enough to represent strides:
	    if (mpi->flags & MP_IMGFLAG_PLANAR) {
		if ((dstrides.y >> 1) != dstrides.v || dstrides.v != dstrides.u)
		    return VO_FALSE;
	    } else {
		if (dstrides.y % (mpi->bpp / 8))
		    return VO_FALSE;
	    }
	}
	mpi->planes[0] = st_w100_mem + vidix_play.offsets[st_next_frame]
	    + vidix_play.offset.y;
	mpi->width = mpi->stride[0] = dstrides.y;
	if (mpi->flags & MP_IMGFLAG_PLANAR) {
	    mpi->planes[1] = st_w100_mem + vidix_play.offsets[st_next_frame]
		+ vidix_play.offset.v;
	    mpi->stride[1] = dstrides.v >> mpi->chroma_x_shift;
	    mpi->planes[2] = st_w100_mem + vidix_play.offsets[st_next_frame]
		+ vidix_play.offset.u;
	    mpi->stride[2] = dstrides.u >> mpi->chroma_x_shift;
	} else
	    mpi->width /= mpi->bpp / 8;
	mpi->flags |= MP_IMGFLAG_DIRECT;
	return VO_TRUE;
    }
#endif
    return VO_FALSE;
}

// ---------------------------------------------------------------- interfaces
/*
 * Preinitializes driver (real INITIALIZATION)
 *   arg - currently it's vo_subdevice
 *   returns: zero on successful initialization, non-zero on error.
 */
static uint32_t preinit(const char *vo_subdevice)
{
    UNUSED(vo_subdevice);

    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: preinit() was called\n");

    return 0;
}

static int alloc_plane(bvdd_vram_plane_t *vp, int type, int size, uint8_t c)
{
    vp->area_type = type;
    vp->size = size;
    if (ioctl(st_bvdd_fd, BVDD_IOCTL_ALLOC_PLANE, vp) < 0) {
	mp_msg(MSGT_VO, MSGL_FATAL, "vo_bvdd: mmap failed.\n");
	return -1;
    }
    vp->addr.virt = mmap(0, vp->size, PROT_READ|PROT_WRITE, MAP_SHARED, st_bvdd_fd, vp->addr.phys);
    if (vp->addr.virt == MAP_FAILED) {
	mp_msg(MSGT_VO, MSGL_FATAL, "vo_bvdd: mmap failed.\n");
	return -1;
    }
    memset(vp->addr.virt, c, vp->size);
    return 1;
}

/*
 * Initialize (means CONFIGURE) the display driver.
 * params:
 *   src_width,srcheight: image source size
 *   dst_width,dst_height: size of the requested window size, just a hint
 *   fullscreen: flag, 0=windowd 1=fullscreen, just a hint
 *   title: window title, if available
 *   format: fourcc of pixel format
 * returns : zero on successful initialization, non-zero on error.
 */
static uint32_t config(uint32_t src_width, uint32_t src_height,
		       uint32_t dst_width, uint32_t dst_height, uint32_t flags,
		       char *title, uint32_t format)
{
    int fs = flags & VOFLAG_FULLSCREEN;
    int vm = flags & VOFLAG_MODESWITCHING;
    int zoom = flags & VOFLAG_SWSCALE;
    int y_pitch, uv_pitch;
    int x_res = 480, y_res = 640;
    uint32_t apitch;
    int vram_size;
    int i;

    mp_msg(MSGT_VO, MSGL_DBG2, "vo_bvdd: config() was called\n");
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: src_width:%d, src_height:%d, dst_width:%d, dst_height:%d\n",
	   src_width, src_height, dst_width, dst_height);

    if (!query_format(format)) {
	printf("vo_bvdd: unsupported fourcc for this w100 driver: %x (%s)\n",
	       format, vo_format_name(format));
	return -1;
    }
    st_format = format;

    if (st_bvdd_fd >= 0) {
	return -1;
    }

    if (vm && (src_width <= 240 && src_height <= 320)) {
	printf("vo_bvdd: Change QVGA mode.\n");
	change_resolution(LCD_SHARP_QVGA);
	x_res = 240;
	y_res = 320;
    }

    st_bvdd_fd = open(st_bvdd_devfile, O_RDWR|O_SYNC);
    if (st_bvdd_fd < 0) {
	mp_msg(MSGT_VO, MSGL_FATAL, "vo_bvdd: couldn't open %s, reason %s.\n",
	       st_bvdd_devfile, strerror(errno));
	return -1;
    }

    ioctl(st_bvdd_fd, BVDD_IOCTL_SET_IWMMXT_STATE, 1);

    // 表示領域をセンタリング
    st_overlay_pos_x = (x_res - src_width) / 2;
    st_overlay_pos_y = (y_res - src_height) / 2;

    geometry(&st_overlay_pos_x, &st_overlay_pos_y,
	     &src_width, &src_height, x_res, y_res);

    st_src_width = src_width;
    st_src_height = src_height;

    switch (format) {
    case IMGFMT_YV12:
    case IMGFMT_IYUV:
    case IMGFMT_I420:
    case IMGFMT_YVU9:
    case IMGFMT_IF09:
    case IMGFMT_Y8:
    case IMGFMT_Y800:
	y_pitch = (src_width + 15) & ~15;
	uv_pitch = y_pitch / 2;
	break;
    default:
	return -1;
    }

    if (alloc_plane(&st_planes[0].y, BVDD_VRAM_AREA_BVDD, y_pitch * src_height, 0) < 0) /* Y */
	return -1;
    if (alloc_plane(&st_planes[0].u, BVDD_VRAM_AREA_BVDD, uv_pitch * (src_height / 2), 0x80) < 0) /* U */
	return -1;
    if (alloc_plane(&st_planes[0].v, BVDD_VRAM_AREA_BVDD, uv_pitch * (src_height / 2), 0x80) < 0) /* V */
	return -1;
    if (vo_doublebuffering) {
	for (i = 1; i < 3; ++i) {
	    if (alloc_plane(&st_planes[i].y, BVDD_VRAM_AREA_BVDD, y_pitch * src_height, 0) < 0) /* Y */
		return -1;
	    if (alloc_plane(&st_planes[i].u, BVDD_VRAM_AREA_BVDD, uv_pitch * (src_height / 2), 0x80) < 0) /* U */
		return -1;
	    if (alloc_plane(&st_planes[i].v, BVDD_VRAM_AREA_BVDD, uv_pitch * (src_height / 2), 0x80) < 0) /* V */
		return -1;
	}
    }

    /* select first frame */
    st_current_frame = vo_doublebuffering ? 1 : 0;

    st_bvdd_frame.x = st_overlay_pos_x;
    st_bvdd_frame.y = st_overlay_pos_y;
    st_bvdd_frame.width = src_width;
    st_bvdd_frame.height = src_height;
    st_bvdd_frame.format = BVDD_FORMAT_YUV420;
    st_bvdd_frame.plane_id = st_planes[st_current_frame].y.id;
    st_bvdd_frame.plane_id_u = st_planes[st_current_frame].u.id;
    st_bvdd_frame.plane_id_v = st_planes[st_current_frame].v.id;

    ioctl(st_bvdd_fd, BVDD_IOCTL_SETUP_OVERLAY2_FRAME, &st_bvdd_frame);

    switch (format) {
    case IMGFMT_YV12:
    case IMGFMT_I420:
    case IMGFMT_IYUV:
    case IMGFMT_YVU9:
    case IMGFMT_IF09:
    case IMGFMT_Y800:
    case IMGFMT_Y8:
	dstrides.y = y_pitch;
	dstrides.v = uv_pitch;
	dstrides.u = uv_pitch;
	break;
    case IMGFMT_RGB32:
    case IMGFMT_BGR32:
	apitch = st_overlay_pitch_y - 1;
	dstrides.y = (src_width * 4 + apitch) & ~apitch;
	dstrides.u = dstrides.v = 0;
/* 	st_image_bpp = 4; */
	break;
    case IMGFMT_RGB24:
    case IMGFMT_BGR24:
	apitch = st_overlay_pitch_y - 1;
	dstrides.y = (src_width * 3 + apitch) & ~apitch;
	dstrides.u = dstrides.v = 0;
/* 	st_image_bpp = 3; */
	break;
    default:
	apitch = st_overlay_pitch_y - 1;
	dstrides.y = (src_width * 2 + apitch) & ~apitch;
	dstrides.u = dstrides.v = 0;
/* 	st_image_bpp = 2; */
	break;
    }

    if (format == IMGFMT_YV12 || format == IMGFMT_I420 || format == IMGFMT_IYUV)
	video_out_bvdd.draw_slice = bvdd_draw_slice_420;
    /* 	else if (format == IMGFMT_YVU9 || format == IMGFMT_IF09) */
    /* 	    vo_server->draw_slice = w100_draw_slice_410; */
    else
	video_out_bvdd.draw_slice = bvdd_draw_slice_packed;

    return 0;
}

/*
 * Control interface
 */
static uint32_t control(uint32_t request, void *data, ...)
{
    switch (request) {
    case VOCTRL_GET_IMAGE:
	return bvdd_get_image(data);
    case VOCTRL_QUERY_FORMAT:
	return query_format(*((uint32_t *)data));
    }

    return VO_NOTIMPL;
}

/*
 * Display a new RGB/BGR frame of the video to the screen.
 * params:
 *   src[0] - pointer to the image
 */
uint32_t draw_frame(uint8_t *src[])
{
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: dummy draw_frame() was called\n");
    return -1;
}

/*
 * Draw a planar YUV slice to the buffer:
 * params:
 *   src[3] = source image planes (Y,U,V)
 *   stride[3] = source image planes line widths (in bytes)
 *   w,h = width*height of area to be copied (in Y pixels)
 *   x,y = position at the destination image (in Y pixels)
 */
uint32_t draw_slice(uint8_t *src[], int stride[], int w,int h, int x,int y)
{
    mp_msg(MSGT_VO, MSGL_DBG2, "vo_bvdd: dummy draw_slice() was called\n");
    return -1;
}


/*
 * Draws OSD to the screen buffer
 */
static void draw_osd(void)
{
/*     mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: draw_osd() was called\n"); */
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: draw_osd: frame=%d\n", st_current_frame);
    vo_draw_text(st_src_height, st_src_width, draw_alpha);
}

/*
 * Blit/Flip buffer to the screen. Must be called after each frame!
 */
void flip_page(void)
{
/*     mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: flip_page() was called\n"); */
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: flip_page: frame=%d\n", st_current_frame);
    if (vo_doublebuffering) {
	st_bvdd_frame.plane_id = st_planes[st_current_frame].y.id;
	st_bvdd_frame.plane_id_u = st_planes[st_current_frame].u.id;
	st_bvdd_frame.plane_id_v = st_planes[st_current_frame].v.id;
	ioctl(st_bvdd_fd, BVDD_IOCTL_BRANCH_OVERLAY2_FRAME, &st_bvdd_frame);
	st_current_frame = (st_current_frame + 1) % 3;
    }
}

/*
 * This func is called after every frames to handle keyboard and
 * other events. It's called in PAUSE mode too!
 */
extern int g_sigcont;
void check_events(void)
{
#if 0
    w100_priv_t *priv = &st_w100_priv;

    if (g_sigcont) {
	mp_msg(MSGT_VO, MSGL_INFO, "vo_bvdd: SIGCONT recived.\n");

	/* Immediately after resuming, because kernel modifies the register, it
	   waits for that. */
	usleep(1000 * 500);

	/* re-attach */
#if 0
	/* Hmm... With respect to of context is necessary, but really it
	   fails. It does not release and also there is no problem. */
	if (!AtiCore_ReleaseOverlay(priv->overlay_handle)) {
	    mp_msg(MSGT_VO, MSGL_FATAL,
		   "vo_w100: AtiCore_ReleaseOverlay failed.\n");
	    exit_player(NULL);
	}
#endif
	if (!AtiCore_ProcessDetach()) {
	    mp_msg(MSGT_VO, MSGL_FATAL,
		   "vo_w100: AtiCore_ProcessDetach failed.\n");
	    exit_player(NULL);
	}
	if (!AtiCore_ProcessAttach()) {
	    mp_msg(MSGT_VO, MSGL_FATAL,
		   "vo_w100: AtiCore_ProcessAttach failed.\n");
	    exit_player(NULL);
	}

	/* re-setup */
	if (!w100_setup(priv))
	    exit_player(NULL);

	g_sigcont = 0;
    }
#endif
}

/*
 * Closes driver. Should restore the original state of the system.
 */
static void uninit(void)
{
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: uninit() was called\n");

    ioctl(st_bvdd_fd, BVDD_IOCTL_SET_IWMMXT_STATE, 0);
    if (close(st_bvdd_fd) < 0) {
	mp_msg(MSGT_VO, MSGL_WARN, "vo_bvdd: couldn't close bvdd, reason %s.\n", strerror(errno));
    }
    st_bvdd_fd = -1;

    if (st_lcd_mode == LCD_SHARP_QVGA) {
	change_resolution(LCD_SHARP_VGA);
    }
}


// ----------------------------------------------------------------
static uint32_t query_format(uint32_t format)
{
    mp_msg(MSGT_VO, MSGL_V, "vo_bvdd: query_format was called: %x (%s)\n",
	   format, vo_format_name(format));

    if (IMGFMT_IS_RGB(format)) {
	/* RGB/BGR Formats */
	// TODO
	return 0;

	switch (IMGFMT_RGB_DEPTH(format)) {
	case 16:
	    return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_ACCEPT_STRIDE;
	    break;
	}
    } else {
	/* Planar YUV Formats */
	switch (format) {
	case IMGFMT_YV12:
	case IMGFMT_IYUV:
	case IMGFMT_I420:
	case IMGFMT_YVU9:
	case IMGFMT_IF09:
	case IMGFMT_Y8:
	case IMGFMT_Y800:
	    return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_ACCEPT_STRIDE;
	    break;
	}
    }

    return 0;
}
