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; }
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); }