VdpStatus vdp_presentation_queue_display(VdpPresentationQueue presentation_queue,
        VdpOutputSurface surface,
        uint32_t clip_width,
        uint32_t clip_height,
        VdpTime earliest_presentation_time)
{
    queue_ctx_t *q = handle_get(presentation_queue);
    if (!q)
        return VDP_STATUS_INVALID_HANDLE;

    output_surface_ctx_t *os = handle_get(surface);
    if (!os)
        return VDP_STATUS_INVALID_HANDLE;

    if (earliest_presentation_time != 0)
        VDPAU_DBG_ONCE("Presentation time not supported");

    Window c;
    int x,y;
    XTranslateCoordinates(q->device->display, q->target->drawable, RootWindow(q->device->display, q->device->screen), 0, 0, &x, &y, &c);
    XClearWindow(q->device->display, q->target->drawable);

    if (os->vs)
    {
        // VIDEO layer
        __disp_layer_info_t layer_info;
        memset(&layer_info, 0, sizeof(layer_info));
        layer_info.pipe = q->device->osd_enabled ? 0 : 1;
        layer_info.mode = DISP_LAYER_WORK_MODE_SCALER;
        layer_info.fb.format = DISP_FORMAT_YUV420;
        layer_info.fb.seq = DISP_SEQ_UVUV;
        switch (os->vs->source_format) {
        case VDP_YCBCR_FORMAT_YUYV:
            layer_info.fb.mode = DISP_MOD_INTERLEAVED;
            layer_info.fb.format = DISP_FORMAT_YUV422;
            layer_info.fb.seq = DISP_SEQ_YUYV;
            break;
        case VDP_YCBCR_FORMAT_UYVY:
            layer_info.fb.mode = DISP_MOD_INTERLEAVED;
            layer_info.fb.format = DISP_FORMAT_YUV422;
            layer_info.fb.seq = DISP_SEQ_UYVY;
            break;
        case VDP_YCBCR_FORMAT_NV12:
            layer_info.fb.mode = DISP_MOD_NON_MB_UV_COMBINED;
            break;
        case VDP_YCBCR_FORMAT_YV12:
            layer_info.fb.mode = DISP_MOD_NON_MB_PLANAR;
            break;
        default:
        case INTERNAL_YCBCR_FORMAT:
            layer_info.fb.mode = DISP_MOD_MB_UV_COMBINED;
            break;
        }
        layer_info.fb.br_swap = 0;
        layer_info.fb.addr[0] = ve_virt2phys(os->vs->data) + 0x40000000;
        layer_info.fb.addr[1] = ve_virt2phys(os->vs->data + os->vs->plane_size) + 0x40000000;
        layer_info.fb.addr[2] = ve_virt2phys(os->vs->data + os->vs->plane_size + os->vs->plane_size / 4) + 0x40000000;

        layer_info.fb.cs_mode = DISP_BT601;
        layer_info.fb.size.width = os->vs->width;
        layer_info.fb.size.height = os->vs->height;
        layer_info.src_win.x = os->video_src_rect.x0;
        layer_info.src_win.y = os->video_src_rect.y0;
        layer_info.src_win.width = os->video_src_rect.x1 - os->video_src_rect.x0;
        layer_info.src_win.height = os->video_src_rect.y1 - os->video_src_rect.y0;
        layer_info.scn_win.x = x + os->video_dst_rect.x0;
        layer_info.scn_win.y = y + os->video_dst_rect.y0;
        layer_info.scn_win.width = os->video_dst_rect.x1 - os->video_dst_rect.x0;
        layer_info.scn_win.height = os->video_dst_rect.y1 - os->video_dst_rect.y0;
        layer_info.ck_enable = q->device->osd_enabled ? 0 : 1;

        if (layer_info.scn_win.y < 0)
        {
            int cutoff = -(layer_info.scn_win.y);
            layer_info.src_win.y += cutoff;
            layer_info.src_win.height -= cutoff;
            layer_info.scn_win.y = 0;
            layer_info.scn_win.height -= cutoff;
        }

        uint32_t args[4] = { 0, q->target->layer, (unsigned long)(&layer_info), 0 };
        ioctl(q->target->fd, DISP_CMD_LAYER_SET_PARA, args);

        ioctl(q->target->fd, DISP_CMD_LAYER_OPEN, args);
        // Note: might be more reliable (but slower and problematic when there
        // are driver issues and the GET functions return wrong values) to query the
        // old values instead of relying on our internal csc_change.
        // Since the driver calculates a matrix out of these values after each
        // set doing this unconditionally is costly.
        if (os->csc_change) {
            ioctl(q->target->fd, DISP_CMD_LAYER_ENHANCE_OFF, args);
            args[2] = 0xff * os->brightness + 0x20;
            ioctl(q->target->fd, DISP_CMD_LAYER_SET_BRIGHT, args);
            args[2] = 0x20 * os->contrast;
            ioctl(q->target->fd, DISP_CMD_LAYER_SET_CONTRAST, args);
            args[2] = 0x20 * os->saturation;
            ioctl(q->target->fd, DISP_CMD_LAYER_SET_SATURATION, args);
            // hue scale is randomly chosen, no idea how it maps exactly
            args[2] = (32 / 3.14) * os->hue + 0x20;
            ioctl(q->target->fd, DISP_CMD_LAYER_SET_HUE, args);
            ioctl(q->target->fd, DISP_CMD_LAYER_ENHANCE_ON, args);
            os->csc_change = 0;
        }
    }
    else
    {
        uint32_t args[4] = { 0, q->target->layer, 0, 0 };
        ioctl(q->target->fd, DISP_CMD_LAYER_CLOSE, args);
    }

    if (!q->device->osd_enabled)
        return VDP_STATUS_OK;

    if (os->rgba.flags & RGBA_FLAG_NEEDS_CLEAR)
        rgba_clear(&os->rgba);

    if (os->rgba.flags & RGBA_FLAG_DIRTY)
    {
        // TOP layer
        rgba_flush(&os->rgba);

        __disp_layer_info_t layer_info;
        memset(&layer_info, 0, sizeof(layer_info));
        layer_info.pipe = 1;
        layer_info.mode = DISP_LAYER_WORK_MODE_NORMAL;
        layer_info.fb.mode = DISP_MOD_INTERLEAVED;
        layer_info.fb.format = DISP_FORMAT_ARGB8888;
        layer_info.fb.seq = DISP_SEQ_ARGB;
        switch (os->rgba.format)
        {
        case VDP_RGBA_FORMAT_R8G8B8A8:
            layer_info.fb.br_swap = 1;
            break;
        case VDP_RGBA_FORMAT_B8G8R8A8:
        default:
            layer_info.fb.br_swap = 0;
            break;
        }
        layer_info.fb.addr[0] = ve_virt2phys(os->rgba.data) + 0x40000000;
        layer_info.fb.cs_mode = DISP_BT601;
        layer_info.fb.size.width = os->rgba.width;
        layer_info.fb.size.height = os->rgba.height;
        layer_info.src_win.x = os->rgba.dirty.x0;
        layer_info.src_win.y = os->rgba.dirty.y0;
        layer_info.src_win.width = os->rgba.dirty.x1 - os->rgba.dirty.x0;
        layer_info.src_win.height = os->rgba.dirty.y1 - os->rgba.dirty.y0;
        layer_info.scn_win.x = x + os->rgba.dirty.x0;
        layer_info.scn_win.y = y + os->rgba.dirty.y0;
        layer_info.scn_win.width = min_nz(clip_width, os->rgba.dirty.x1) - os->rgba.dirty.x0;
        layer_info.scn_win.height = min_nz(clip_height, os->rgba.dirty.y1) - os->rgba.dirty.y0;

        uint32_t args[4] = { 0, q->target->layer_top, (unsigned long)(&layer_info), 0 };
        ioctl(q->target->fd, DISP_CMD_LAYER_SET_PARA, args);

        ioctl(q->target->fd, DISP_CMD_LAYER_OPEN, args);
    }
    else
    {
        uint32_t args[4] = { 0, q->target->layer_top, 0, 0 };
        ioctl(q->target->fd, DISP_CMD_LAYER_CLOSE, args);
    }

    return VDP_STATUS_OK;
}
Ejemplo n.º 2
0
static int sunxi_disp1_5_set_video_layer(struct sunxi_disp *sunxi_disp, int x, int y, int width, int height, output_surface_ctx_t *surface)
{
	struct sunxi_disp1_5_private *disp = (struct sunxi_disp1_5_private *)sunxi_disp;

	disp_window src = { .x = surface->video_src_rect.x0, .y = surface->video_src_rect.y0,
			    .width = surface->video_src_rect.x1 - surface->video_src_rect.x0,
			    .height = surface->video_src_rect.y1 - surface->video_src_rect.y0 };
	disp_window scn = { .x = x + surface->video_dst_rect.x0, .y = y + surface->video_dst_rect.y0,
			    .width = surface->video_dst_rect.x1 - surface->video_dst_rect.x0,
			    .height = surface->video_dst_rect.y1 - surface->video_dst_rect.y0 };

	if (scn.y < 0)
	{
		int scn_clip = -scn.y;
		int src_clip = scn_clip * src.height / scn.height;
		scn.y = 0;
		scn.height -= scn_clip;
		src.y += src_clip;
		src.height -= src_clip;
	}
	if (scn.x < 0)
	{
		int scn_clip = -scn.x;
		int src_clip = scn_clip * src.width / scn.width;
		scn.x = 0;
		scn.width -= scn_clip;
		src.x += src_clip;
		src.width -= src_clip;
	}
	if (scn.x + scn.width > disp->screen_width)
	{
		int scn_clip = scn.x + scn.width - disp->screen_width;
		int src_clip = scn_clip * src.width / scn.width;
		scn.width -= scn_clip;
		src.width -= src_clip;
	}

	unsigned long args[4] = { 0, disp->video_layer, (unsigned long)(&disp->video_info) };
	switch (surface->vs->source_format)
	{
	case VDP_YCBCR_FORMAT_YUYV:
		disp->video_info.fb.format = DISP_FORMAT_YUV422_I_YUYV;
		break;
	case VDP_YCBCR_FORMAT_UYVY:
		disp->video_info.fb.format = DISP_FORMAT_YUV422_I_UYVY;
		break;
	case VDP_YCBCR_FORMAT_NV12:
		disp->video_info.fb.format = DISP_FORMAT_YUV420_SP_UVUV;
		break;
	case INTERNAL_YCBCR_FORMAT:
		disp->video_info.fb.format = DISP_FORMAT_YUV420_SP_TILE_UVUV;
		break;
	case VDP_YCBCR_FORMAT_YV12:
	default:
		disp->video_info.fb.format = DISP_FORMAT_YUV420_P;
		break;
	}

	disp->video_info.fb.addr[0] = cedrus_mem_get_phys_addr(surface->yuv->data);
	disp->video_info.fb.addr[1] = cedrus_mem_get_phys_addr(surface->yuv->data) + surface->vs->luma_size;
	disp->video_info.fb.addr[2] = cedrus_mem_get_phys_addr(surface->yuv->data) + surface->vs->luma_size + surface->vs->chroma_size / 2;

	disp->video_info.fb.size.width = surface->vs->width;
	disp->video_info.fb.size.height = surface->vs->height;
	disp->video_info.fb.src_win = src;
	disp->video_info.screen_win = scn;
	disp->video_info.fb.pre_multiply = 1;

	if (ioctl(disp->fd, DISP_CMD_LAYER_ENABLE, args))
		return -EINVAL;

	if (ioctl(disp->fd, DISP_CMD_LAYER_SET_INFO, args))
		return -EINVAL;

	return 0;
}

static void sunxi_disp1_5_close_video_layer(struct sunxi_disp *sunxi_disp)
{
	struct sunxi_disp1_5_private *disp = (struct sunxi_disp1_5_private *)sunxi_disp;

	unsigned long args[4] = { 0, disp->video_layer };

	ioctl(disp->fd, DISP_CMD_LAYER_DISABLE, args);
}

static int sunxi_disp1_5_set_osd_layer(struct sunxi_disp *sunxi_disp, int x, int y, int width, int height, output_surface_ctx_t *surface)
{
	struct sunxi_disp1_5_private *disp = (struct sunxi_disp1_5_private *)sunxi_disp;

	unsigned long args[4] = { 0, disp->osd_layer, (unsigned long)(&disp->osd_info) };

	disp_window src = { .x = surface->rgba.dirty.x0, .y = surface->rgba.dirty.y0,
			  .width = surface->rgba.dirty.x1 - surface->rgba.dirty.x0,
			  .height = surface->rgba.dirty.y1 - surface->rgba.dirty.y0 };
	disp_window scn = { .x = x + surface->rgba.dirty.x0, .y = y + surface->rgba.dirty.y0,
			  .width = min_nz(width, surface->rgba.dirty.x1) - surface->rgba.dirty.x0,
			  .height = min_nz(height, surface->rgba.dirty.y1) - surface->rgba.dirty.y0 };

	switch (surface->rgba.format)
	{
	case VDP_RGBA_FORMAT_R8G8B8A8:
		disp->osd_info.fb.format = DISP_FORMAT_ABGR_8888;
		break;
	case VDP_RGBA_FORMAT_B8G8R8A8:
	default:
		disp->osd_info.fb.format = DISP_FORMAT_ARGB_8888;
		break;
	}

	disp->osd_info.fb.addr[0] = cedrus_mem_get_phys_addr(surface->rgba.data);
	disp->osd_info.fb.size.width = surface->rgba.width;
	disp->osd_info.fb.size.height = surface->rgba.height;
	disp->osd_info.fb.src_win = src;
	disp->osd_info.screen_win = scn;

	if (ioctl(disp->fd, DISP_CMD_LAYER_ENABLE, args))
		return -EINVAL;

	if (ioctl(disp->fd, DISP_CMD_LAYER_SET_INFO, args))
		return -EINVAL;

	return 0;
}

static void sunxi_disp1_5_close_osd_layer(struct sunxi_disp *sunxi_disp)
{
	struct sunxi_disp1_5_private *disp = (struct sunxi_disp1_5_private *)sunxi_disp;

	unsigned long args[4] = { 0, disp->osd_layer };

	ioctl(disp->fd, DISP_CMD_LAYER_DISABLE, args);
}