diff --git a/src/host1x.h b/src/host1x.h index 29a48af..b9a32e1 100644 --- a/src/host1x.h +++ b/src/host1x.h @@ -25,18 +25,6 @@ #ifndef HOST1X_H #define HOST1X_H -#define FLOAT_TO_FIXED_6_12(fp) \ - (((int32_t) (fp * 4096.0f + 0.5f)) & ((1 << 18) - 1)) - -#define FLOAT_TO_FIXED_s2_7(fp) \ - (((fp < 0.0f) << 9) | (((int32_t) (fabs(fp) * 128.0f)) & ((1 << 9) - 1))) - -#define FLOAT_TO_FIXED_s1_7(fp) \ - (((fp < 0.0f) << 8) | (((int32_t) (fabs(fp) * 128.0f)) & ((1 << 8) - 1))) - -#define FLOAT_TO_FIXED_0_8(fp) \ - (((int32_t) (fp * 256.0f + 0.5f)) & ((1 << 8) - 1)) - #define HOST1X_OPCODE_SETCL(offset, classid, mask) \ ((0x0 << 28) | (((offset) & 0xfff) << 16) | (((classid) & 0x3ff) << 6) | ((mask) & 0x3f)) #define HOST1X_OPCODE_INCR(offset, count) \ diff --git a/src/presentation_queue_target.c b/src/presentation_queue_target.c index 887fb5f..a6517ad 100644 --- a/src/presentation_queue_target.c +++ b/src/presentation_queue_target.c @@ -383,6 +383,8 @@ static void pqt_display_xv(tegra_pqt *pqt, tegra_surface *surf, bool block) surf->shared->dst_y0, surf->shared->dst_width, surf->shared->dst_height); + + tegra_xv_apply_csc(dev, &surf->shared->csc); } else if (surf->xv_img) { DebugMsg("surface %u RGB overlay\n", surf->surface_id); @@ -462,6 +464,7 @@ static void transit_display_to_dri(tegra_pqt *pqt) XvStopVideo(dev->display, dev->xv_port, pqt->drawable); memset(&pqt->bg_old_state, 0, sizeof(pqt->bg_old_state)); + tegra_xv_reset_csc(dev); pqt->disp_state = TEGRA_PQT_DRI; } @@ -519,7 +522,7 @@ static void pqt_update_dri_buffer(tegra_pqt *pqt, tegra_surface *surf) ret = host1x_gr2d_surface_blit(&surf->stream_2d, surf->shared->video->pixbuf, pqt->dri_pixbuf, - &surf->shared->csc, + &surf->shared->csc.gr2d, surf->shared->src_x0, surf->shared->src_y0, surf->shared->src_width, diff --git a/src/surface_mixer.c b/src/surface_mixer.c index f7fb492..b742111 100644 --- a/src/surface_mixer.c +++ b/src/surface_mixer.c @@ -44,17 +44,51 @@ static bool custom_csc(VdpCSCMatrix const csc_matrix) return false; } +static bool mixer_apply_vdp_csc_to_xv(tegra_mixer *mix, + VdpCSCMatrix const cscmat) +{ + tegra_device *dev = mix->dev; + uint32_t val0, val1; + + if (!tegra_xv_initialize_csc(dev)) + return false; + + val0 = mix->csc.gr2d.yos & 0xff; + val1 = FLOAT_TO_FIXED_s2_8( CLAMP(cscmat[0][0], 0.0f, 1.98f) ); + + mix->csc.xv.yof_kyrgb = (val1 << 16) | val0; + + val0 = FLOAT_TO_FIXED_s2_8( CLAMP(cscmat[0][1], -3.98f, 3.98f) ); + val1 = FLOAT_TO_FIXED_s2_8( CLAMP(cscmat[0][2], -3.98f, 3.98f) ); + + mix->csc.xv.kur_kvr = (val1 << 16) | val0; + + val0 = FLOAT_TO_FIXED_s1_8( CLAMP(cscmat[1][1], -1.98f, 1.98f) ); + val1 = FLOAT_TO_FIXED_s1_8( CLAMP(cscmat[1][2], -1.98f, 1.98f) ); + + mix->csc.xv.kug_kvg = (val1 << 16) | val0; + + val0 = FLOAT_TO_FIXED_s2_8( CLAMP(cscmat[2][1], -3.98f, 3.98f) ); + val1 = FLOAT_TO_FIXED_s2_8( CLAMP(cscmat[2][2], -3.98f, 3.98f) ); + + mix->csc.xv.kub_kvb = (val1 << 16) | val0; + + return true; +} + static void mixer_apply_vdp_csc(tegra_mixer *mix, VdpCSCMatrix const cscmat) { - mix->csc.yos = -16; - mix->csc.cvr = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[0][2], -3.98f, 3.98f) ); - mix->csc.cub = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[2][1], -3.98f, 3.98f) ); - mix->csc.cyx = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[0][0], -1.98f, 1.98f) ); - mix->csc.cur = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[0][1], -3.98f, 3.98f) ); - mix->csc.cug = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[1][1], -1.98f, 1.98f) ); - mix->csc.cvb = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[2][2], -3.98f, 3.98f) ); - mix->csc.cvg = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[1][2], -1.98f, 1.98f) ); - mix->custom_csc = custom_csc(cscmat); + mix->csc.gr2d.yos = -16; + mix->csc.gr2d.cyx = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[0][0], -1.98f, 1.98f) ); + mix->csc.gr2d.cur = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[0][1], -3.98f, 3.98f) ); + mix->csc.gr2d.cvr = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[0][2], -3.98f, 3.98f) ); + mix->csc.gr2d.cug = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[1][1], -1.98f, 1.98f) ); + mix->csc.gr2d.cvg = FLOAT_TO_FIXED_s1_7( CLAMP(cscmat[1][2], -1.98f, 1.98f) ); + mix->csc.gr2d.cub = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[2][1], -3.98f, 3.98f) ); + mix->csc.gr2d.cvb = FLOAT_TO_FIXED_s2_7( CLAMP(cscmat[2][2], -3.98f, 3.98f) ); + + mix->custom_csc = (!mixer_apply_vdp_csc_to_xv(mix, cscmat) && + custom_csc(cscmat)); } VdpStatus vdp_video_mixer_query_feature_support(VdpDevice device, @@ -676,7 +710,7 @@ VdpStatus vdp_video_mixer_render( ret = host1x_gr2d_surface_blit(&dest_surf->stream_2d, video_surf->pixbuf, dest_surf->pixbuf, - &mix->csc, + &mix->csc.gr2d, src_vid_x0, src_vid_y0, src_vid_width, diff --git a/src/surface_output.c b/src/surface_output.c index aedbc04..8d403d5 100644 --- a/src/surface_output.c +++ b/src/surface_output.c @@ -516,7 +516,7 @@ static VdpStatus surface_render_bitmap_surface( if (shared) { ret = rotate_surface_gr2d(shared->video, dst_surf, - &shared->csc, + &shared->csc.gr2d, rotate, 0, 0, shared->src_width, @@ -688,7 +688,7 @@ static VdpStatus surface_render_bitmap_surface( if (shared) { ret = rotate_surface_gr2d(shared->video, dst_surf, - &shared->csc, + &shared->csc.gr2d, rotate, 0, 0, shared->src_width, diff --git a/src/surface_shared.c b/src/surface_shared.c index c6a1f33..40fe850 100644 --- a/src/surface_shared.c +++ b/src/surface_shared.c @@ -83,7 +83,7 @@ static XvImage * create_video_xv(tegra_surface *video) tegra_shared_surface *create_shared_surface(tegra_surface *disp, tegra_surface *video, - struct host1x_csc_params *csc, + tegra_csc *csc, uint32_t src_x0, uint32_t src_y0, uint32_t src_width, @@ -310,7 +310,7 @@ int shared_surface_transfer_video(tegra_surface *disp) ret = host1x_gr2d_surface_blit(&disp->stream_2d, video->pixbuf, disp->pixbuf, - &shared->csc, + &shared->csc.gr2d, shared->src_x0, shared->src_y0, shared->src_width, diff --git a/src/vdpau_tegra.c b/src/vdpau_tegra.c index e8c5c40..6df5a60 100644 --- a/src/vdpau_tegra.c +++ b/src/vdpau_tegra.c @@ -21,6 +21,8 @@ pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t xv_lock = PTHREAD_MUTEX_INITIALIZER; + bool tegra_vdpau_debug; bool tegra_vdpau_force_xv; bool tegra_vdpau_force_dri; @@ -70,6 +72,152 @@ bool tegra_check_xv_atom(tegra_device *dev, char const *atom_name) return i < count; } +static bool __tegra_xv_apply_csc(tegra_device *dev, tegra_csc *csc) +{ + int32_t val, ret; + + if (dev->xv_csc.applied) { + if (memcmp(&dev->xv_csc.old.xv, &csc->xv, sizeof(csc->xv)) == 0) { + return true; + } + } + + dev->xv_csc.applied = false; + + ret = XvSetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_YOF_KYRGB, + csc->xv.yof_kyrgb); + if (ret != Success) { + ErrorMsg("failed to set XV_TEGRA_YOF_KYRGB %d\n", ret); + return false; + } + + ret = XvSetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_KUR_KVR, + csc->xv.kur_kvr); + if (ret != Success) { + ErrorMsg("failed to set XV_TEGRA_KUR_KVR %d\n", ret); + return false; + } + + ret = XvSetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_KUG_KVG, + csc->xv.kug_kvg); + if (ret != Success) { + ErrorMsg("failed to set XV_TEGRA_KUG_KVG %d\n", ret); + return false; + } + + ret = XvSetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_KUB_KVB, + csc->xv.kub_kvb); + if (ret != Success) { + ErrorMsg("failed to set XV_TEGRA_KUB_KVB %d\n", ret); + return false; + } + + ret = XvSetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_update, + 1); + if (ret != Success) { + ErrorMsg("failed to set XV_TEGRA_CSC_UPDATE %d\n", ret); + dev->xv_csc.ready = false; + return false; + } + + ret = XvGetPortAttribute(dev->display, dev->xv_port, + dev->xv_csc.xvCSC_update, &val); + if (ret != Success || !val) { + ErrorMsg("failed to get XV_TEGRA_CSC_UPDATE %d val %d\n", ret, val); + dev->xv_csc.ready = false; + return false; + } + + dev->xv_csc.old.xv = csc->xv; + dev->xv_csc.applied = true; + + return true; +} + +bool tegra_xv_initialize_csc(tegra_device *dev) +{ + pthread_mutex_lock(&xv_lock); + + if (!dev->xv_csc.inited && dev->xv_ready) { + if (tegra_check_xv_atom(dev, "XV_TEGRA_YOF_KYRGB")) + dev->xv_csc.xvCSC_YOF_KYRGB = XInternAtom(dev->display, + "XV_TEGRA_YOF_KYRGB", + false); + + if (tegra_check_xv_atom(dev, "XV_TEGRA_KUR_KVR")) + dev->xv_csc.xvCSC_KUR_KVR = XInternAtom(dev->display, + "XV_TEGRA_KUR_KVR", + false); + + if (tegra_check_xv_atom(dev, "XV_TEGRA_KUG_KVG")) + dev->xv_csc.xvCSC_KUG_KVG = XInternAtom(dev->display, + "XV_TEGRA_KUG_KVG", + false); + + if (tegra_check_xv_atom(dev, "XV_TEGRA_KUB_KVB")) + dev->xv_csc.xvCSC_KUB_KVB = XInternAtom(dev->display, + "XV_TEGRA_KUB_KVB", + false); + + if (tegra_check_xv_atom(dev, "XV_TEGRA_CSC_UPDATE")) + dev->xv_csc.xvCSC_update = XInternAtom(dev->display, + "XV_TEGRA_CSC_UPDATE", + false); + if (dev->xv_csc.xvCSC_YOF_KYRGB != None && + dev->xv_csc.xvCSC_KUR_KVR != None && + dev->xv_csc.xvCSC_KUG_KVG != None && + dev->xv_csc.xvCSC_KUB_KVB != None && + dev->xv_csc.xvCSC_update != None) + { + tegra_csc default_csc = { + .xv = { + .yof_kyrgb = 0x012a00f0, + .kur_kvr = 0x01980000, + .kug_kvg = 0x032f039b, + .kub_kvb = 0x00000204, + }, + }; + + dev->xv_csc.ready = __tegra_xv_apply_csc(dev, &default_csc); + } + + if (!dev->xv_csc.ready) { + ErrorMsg("XV colorspace conversion not available, update Opentegra Xorg driver and/or Linux kernel to get video overlay CSC support\n"); + } + + dev->xv_csc.inited = true; + } + + pthread_mutex_unlock(&xv_lock); + + return dev->xv_csc.ready; +} + +void tegra_xv_reset_csc(tegra_device *dev) +{ + pthread_mutex_lock(&xv_lock); + dev->xv_csc.applied = false; + pthread_mutex_unlock(&xv_lock); +} + +bool tegra_xv_apply_csc(tegra_device *dev, tegra_csc *csc) +{ + int ret = false; + + pthread_mutex_lock(&xv_lock); + if (dev->xv_csc.ready) { + ret = __tegra_xv_apply_csc(dev, csc); + } + pthread_mutex_unlock(&xv_lock); + + return ret; +} + VdpTime get_time(void) { struct timespec tp; diff --git a/src/vdpau_tegra.h b/src/vdpau_tegra.h index 0a99414..951f2a4 100644 --- a/src/vdpau_tegra.h +++ b/src/vdpau_tegra.h @@ -128,6 +128,24 @@ do { \ #define CLAMP(_v, _vmin, _vmax) \ (((_v) < (_vmin) ? (_vmin) : (((_v) > (_vmax)) ? (_vmax) : (_v)))) +#define FLOAT_TO_FIXED_6_12(fp) \ + (((int32_t) (fp * 4096.0f + 0.5f)) & ((1 << 18) - 1)) + +#define FLOAT_TO_FIXED_s2_8(fp) \ + (((int32_t) (fp * 256.0f + 0.5f)) & ((1 << 11) - 1)) + +#define FLOAT_TO_FIXED_s1_8(fp) \ + (((int32_t) (fp * 256.0f + 0.5f)) & ((1 << 10) - 1)) + +#define FLOAT_TO_FIXED_s2_7(fp) \ + (((fp < 0.0f) << 9) | (((int32_t) (fabs(fp) * 128.0f + 0.5f)) & ((1 << 9) - 1))) + +#define FLOAT_TO_FIXED_s1_7(fp) \ + (((fp < 0.0f) << 8) | (((int32_t) (fabs(fp) * 128.0f + 0.5f)) & ((1 << 8) - 1))) + +#define FLOAT_TO_FIXED_0_8(fp) \ + (((int32_t) (fp * 256.0f + 0.5f)) & ((1 << 8) - 1)) + typedef union TegraXvVdpauInfo { struct { unsigned int visible : 1; @@ -151,6 +169,17 @@ typedef struct { extern pthread_mutex_t global_lock; +typedef struct tegra_csc { + struct host1x_csc_params gr2d; + + struct xv_csc { + uint32_t yof_kyrgb; + uint32_t kur_kvr; + uint32_t kug_kvg; + uint32_t kub_kvb; + } xv; +} tegra_csc; + typedef struct tegra_device { struct drm_tegra *drm; struct drm_tegra_channel *gr3d; @@ -170,6 +199,18 @@ typedef struct tegra_device { int drm_fd; Atom xvVdpauInfo; + + struct xv_csc_controls { + Atom xvCSC_YOF_KYRGB; + Atom xvCSC_KUR_KVR; + Atom xvCSC_KUG_KVG; + Atom xvCSC_KUB_KVB; + Atom xvCSC_update; + tegra_csc old; + bool applied; + bool inited; + bool ready; + } xv_csc; } tegra_device; struct tegra_surface; @@ -178,7 +219,7 @@ typedef struct tegra_shared_surface { atomic_t refcnt; struct tegra_surface *video; struct tegra_surface *disp; - struct host1x_csc_params csc; + struct tegra_csc csc; uint32_t src_x0, src_y0, src_width, src_height; uint32_t dst_x0, dst_y0, dst_width, dst_height; XvImage *xv_img; @@ -251,7 +292,7 @@ typedef struct tegra_decoder { } tegra_decoder; typedef struct tegra_mixer { - struct host1x_csc_params csc; + struct tegra_csc csc; pthread_mutex_t lock; atomic_t refcnt; VdpColor bg_color; @@ -386,7 +427,7 @@ int sync_dmabuf_read_end(int dmabuf_fd); tegra_shared_surface *create_shared_surface(tegra_surface *disp, tegra_surface *video, - struct host1x_csc_params *csc, + tegra_csc *csc, uint32_t src_x0, uint32_t src_y0, uint32_t src_width, @@ -403,6 +444,9 @@ void shared_surface_kill_disp(tegra_surface *disp); tegra_shared_surface * shared_surface_get(tegra_surface *disp); bool tegra_check_xv_atom(tegra_device *dev, char const *atom_name); +bool tegra_xv_initialize_csc(tegra_device *dev); +void tegra_xv_reset_csc(tegra_device *dev); +bool tegra_xv_apply_csc(tegra_device *dev, tegra_csc *csc); int rotate_surface_gr2d(tegra_surface *src_surf, tegra_surface *dst_surf,