summaryrefslogtreecommitdiffstats
path: root/src/armada_drm_xv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/armada_drm_xv.c')
-rw-r--r--src/armada_drm_xv.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/src/armada_drm_xv.c b/src/armada_drm_xv.c
new file mode 100644
index 0000000..b8188e0
--- /dev/null
+++ b/src/armada_drm_xv.c
@@ -0,0 +1,907 @@
+/*
+ * Marvell Armada DRM-based Xvideo driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_drm.h"
+#include "xf86Crtc.h"
+#include "xf86xv.h"
+#include "fourcc.h"
+#include <X11/extensions/Xv.h>
+#include <X11/Xatom.h>
+
+#include "armada_ioctl.h"
+
+#define FOURCC_VYUY 0x59555956
+#define XVIMAGE_VYUY \
+ { \
+ FOURCC_VYUY, XvYUV, LSBFirst, \
+ { 'V', 'Y', 'U', 'Y', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPacked, 1, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'V', 'Y', 'U', 'Y' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_I422 0x32323449
+#define XVIMAGE_I422 \
+ { \
+ FOURCC_I422, XvYUV, LSBFirst, \
+ { 'I', '4', '2', '2', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPlanar, 3, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'Y', 'U', 'V' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_YV16 0x36315659
+#define XVIMAGE_YV16 \
+ { \
+ FOURCC_YV16, XvYUV, LSBFirst, \
+ { 'Y', 'V', '1', '6', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPlanar, 3, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'Y', 'V', 'U' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_XVBO 0x4f425658
+#define XVIMAGE_XVBO \
+ { \
+ FOURCC_XVBO, XvYUV, LSBFirst, \
+ { 'X', 'V', 'B', 'O', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPacked, 1, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'U', 'Y', 'V', 'Y' }, \
+ XvTopToBottom, \
+ }
+
+#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
+
+static Atom xvAutoPaintColorKey;
+static Atom xvColorKey;
+static Atom xvBrightness;
+static Atom xvContrast;
+static Atom xvSaturation;
+static Atom xvDeinterlace;
+static Atom xvPipe;
+
+/* Size of physical addresses via BMM */
+typedef uint32_t phys_t;
+
+struct drm_xv {
+ struct armada_drm_info *drm;
+ xf86CrtcPtr desired_crtc;
+ Bool is_bmm;
+ Bool autopaint_colorkey;
+
+ /* Cache the information */
+ int image_fourcc;
+ short image_width;
+ short image_height;
+ const XF86ImageRec *image;
+ uint32_t image_flags;
+ uint32_t image_size;
+ int pitches[3];
+ int offsets[3];
+ int (*cvt)(struct drm_xv *, unsigned char *, struct drm_armada_bo **);
+
+ unsigned bo_idx;
+ struct drm_armada_bo *bo[3];
+ phys_t bo_phys[3];
+
+ RegionRec clipBoxes;
+
+ struct drm_armada_overlay_attrs attrs;
+ struct drm_armada_overlay_put_image arg;
+};
+
+static XF86VideoEncodingRec OverlayEncodings[] = {
+ { 0, "XV_IMAGE", 2048, 2048, { 1, 1 }, },
+};
+
+static XF86VideoFormatRec OverlayFormats[] = {
+ { 8, PseudoColor },
+ { 16, TrueColor },
+ { 24, TrueColor },
+ { 32, TrueColor },
+};
+
+static XF86AttributeRec OverlayAttributes[] = {
+ { XvSettable | XvGettable, 0, 1, "XV_AUTOPAINT_COLORKEY" },
+ { XvSettable | XvGettable, 0, 0x00ffffff, "XV_COLORKEY" },
+ { XvSettable | XvGettable, -128, 127, "XV_BRIGHTNESS" },
+ { XvSettable | XvGettable, 0, 255, "XV_CONTRAST" },
+ { XvSettable | XvGettable, 0, 1023, "XV_SATURATION" },
+ { XvSettable | XvGettable, -1, 0, "XV_PIPE" },
+};
+
+/*
+ * These are in order of preference. The I420/YV12 formats require
+ * conversion within the X server rather than the application, that's
+ * relatively easy to do, and moreover involves reading less data than
+ * I422/YV16. YV16 and VYUY are not common formats (vlc at least does
+ * not have any support for it but does have I422) so these comes at
+ * the very end, to try to avoid vlc complaining about them.
+ */
+static XF86ImageRec OverlayImages[] = {
+ XVIMAGE_I420,
+ XVIMAGE_YV12,
+ XVIMAGE_I422,
+ XVIMAGE_YUY2,
+ XVIMAGE_UYVY,
+ XVIMAGE_VYUY,
+ XVIMAGE_YV16,
+ XVIMAGE_XVBO,
+};
+
+/* It would be nice to be given the image pointer... */
+static const XF86ImageRec *armada_drm_ovl_get_img(int image)
+{
+ const XF86ImageRec *img = NULL;
+ int i;
+
+ for (i = 0; i < sizeof(OverlayImages) / sizeof(XF86ImageRec); i++)
+ if (OverlayImages[i].id == image) {
+ img = &OverlayImages[i];
+ break;
+ }
+
+ return img;
+}
+
+static int armada_drm_ovl_set_img_info(const XF86ImageRec *img,
+ int *pitch, int *offset, short width, short height)
+{
+ int ret = 0;
+
+ if (img->id == FOURCC_XVBO) {
+ /* Our special XVBO format is only two uint32_t */
+ pitch[0] = 2 * sizeof(uint32_t);
+ offset[0] = 0;
+ ret = pitch[0];
+ } else if (img->format == XvPlanar) {
+ int size[3];
+
+ pitch[0] = width / img->horz_y_period;
+ pitch[1] = width / img->horz_u_period;
+ pitch[2] = width / img->horz_v_period;
+ size[0] = (pitch[0] * (height / img->vert_y_period) + 7) & ~7;
+ size[1] = (pitch[1] * (height / img->vert_u_period) + 7) & ~7;
+ size[2] = (pitch[2] * (height / img->vert_v_period) + 7) & ~7;
+ offset[0] = 0;
+ offset[1] = offset[0] + size[0];
+ offset[2] = offset[1] + size[1];
+
+ ret = size[0] + size[1] + size[2];
+ } else if (img->format == XvPacked) {
+ offset[0] = 0;
+ pitch[0] = width * ((img->bits_per_pixel + 7) / 8);
+ ret = offset[0] + pitch[0] * height;
+ }
+
+ return ret;
+}
+
+static void armada_drm_ovl_bo_free(struct drm_xv *drmxv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drmxv->bo); i++)
+ if (drmxv->bo[i]) {
+ drm_armada_bo_put(drmxv->bo[i]);
+ drmxv->bo[i] = NULL;
+ }
+}
+
+static struct drm_armada_bo *armada_drm_ovl_get_bo(struct drm_xv *drmxv,
+ size_t size, size_t width)
+{
+ struct armada_drm_info *drm = drmxv->drm;
+ struct drm_armada_bo *bo;
+
+ bo = drm_armada_bo_dumb_create(drm->bufmgr, width, size / width / 4, 32);
+ if (bo && drm_armada_bo_map(bo)) {
+ drm_armada_bo_put(bo);
+ bo = NULL;
+ }
+ return bo;
+}
+
+static void armada_drm_ovl_StopVideo(ScrnInfoPtr pScrn, pointer data, Bool cleanup)
+{
+ struct drm_xv *drmxv = data;
+ struct drm_armada_overlay_put_image arg;
+ int ret;
+
+ RegionEmpty(&drmxv->clipBoxes);
+
+ memset(&arg, 0, sizeof(arg));
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE, &arg);
+ if (ret)
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] unable to stop overlay: %s\n", strerror(errno));
+
+ if (cleanup) {
+ drmxv->image = NULL;
+ armada_drm_ovl_bo_free(drmxv);
+ }
+}
+
+static int armada_drm_ovl_SetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, INT32 value, pointer data)
+{
+ struct drm_xv *drmxv = data;
+ Bool update_attrs = TRUE;
+ int ret = Success;
+
+ if (attribute == xvAutoPaintColorKey) {
+ drmxv->autopaint_colorkey = !!value;
+ if (value == 0)
+ drmxv->attrs.color_key = 0;
+ } else if (attribute == xvColorKey)
+ drmxv->attrs.color_key = value;
+ else if (attribute == xvBrightness)
+ drmxv->attrs.brightness = value;
+ else if (attribute == xvContrast)
+ drmxv->attrs.contrast = value;
+ else if (attribute == xvSaturation)
+ drmxv->attrs.saturation = value;
+ else if (attribute == xvDeinterlace)
+ update_attrs = FALSE;
+ else if (attribute == xvPipe) {
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+
+ if (value < -1 || value >= config->num_crtc)
+ return BadValue;
+ if (value == -1)
+ drmxv->desired_crtc = NULL;
+ else
+ drmxv->desired_crtc = config->crtc[value];
+
+ update_attrs = FALSE;
+ } else
+ ret = BadMatch;
+
+ if (ret == Success && update_attrs) {
+ struct drm_armada_overlay_attrs arg;
+
+ arg = drmxv->attrs;
+ arg.flags = ARMADA_OVERLAY_UPDATE_ATTRS;
+ drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_ATTRS, &arg);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "SetPortAttribute: attrib %#lx value %#lx ret %u\n",
+ attribute, value, ret);
+
+ return ret;
+}
+
+static int armada_drm_ovl_GetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
+ INT32 *value, pointer data)
+{
+ struct drm_xv *drmxv = data;
+ int ret = Success;
+
+ if (attribute == xvAutoPaintColorKey)
+ *value = drmxv->autopaint_colorkey;
+ else if (attribute == xvColorKey)
+ *value = drmxv->attrs.color_key;
+ else if (attribute == xvBrightness)
+ *value = drmxv->attrs.brightness;
+ else if (attribute == xvContrast)
+ *value = drmxv->attrs.contrast;
+ else if (attribute == xvSaturation)
+ *value = drmxv->attrs.saturation;
+ else if (attribute == xvPipe) {
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for (i = 0; i < config->num_crtc; i++)
+ if (config->crtc[i] == drmxv->desired_crtc)
+ break;
+
+ if (i == config->num_crtc)
+ i = -1;
+
+ *value = i;
+ } else
+ ret = BadMatch;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "GetPortAttribute: attrib %#lx value %#lx ret %u\n",
+ attribute, *value, ret);
+
+ return ret;
+}
+
+static void armada_drm_ovl_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)
+{
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "QueryBestSize: vid %dx%d drw %dx%d\n",
+ vid_w, vid_h, drw_w, drw_h);
+ *p_w = maxt(vid_w, drw_w);
+ *p_h = maxt(vid_h, drw_h);
+}
+
+#define BMM_SHM_MAGIC1 0x13572468
+#define BMM_SHM_MAGIC2 0x24681357
+
+static uint32_t armada_drm_bmm_chk(unsigned char *buf, uint32_t len)
+{
+ uint32_t i, chk, *ptr = (uint32_t *)buf;
+
+ for (chk = i = 0; i < len; i++)
+ chk ^= ptr[i];
+
+ return chk;
+}
+
+static Bool armada_drm_is_bmm(unsigned char *buf)
+{
+ uint32_t *ptr, len;
+
+ if ((uintptr_t)buf & (sizeof(*ptr) - 1))
+ return FALSE;
+
+ ptr = (uint32_t *)buf;
+ if (*ptr != BMM_SHM_MAGIC1)
+ return FALSE;
+
+ len = 2 + ptr[1];
+ return armada_drm_bmm_chk(buf, len) == ptr[len];
+}
+
+static phys_t armada_drm_bmm_getbuf(unsigned char *buf)
+{
+ uint32_t *ptr, len;
+
+ if ((uintptr_t)buf & (sizeof(*ptr) - 1))
+ return (phys_t)-1;
+
+ ptr = (uint32_t *)buf;
+ if (*ptr != BMM_SHM_MAGIC1)
+ return (phys_t)-1;
+
+ len = 2 + ptr[1];
+ if (armada_drm_bmm_chk(buf, len) != ptr[len])
+ return (phys_t)-1;
+
+ return ptr[2];
+}
+
+static void armada_drm_bmm_putbuf(unsigned char *buf, phys_t phys)
+{
+ uint32_t *ptr = (uint32_t *)buf;
+
+ *ptr++ = BMM_SHM_MAGIC2;
+ *ptr++ = 1;
+ *ptr++ = phys;
+ *ptr = armada_drm_bmm_chk(buf, 3);
+}
+
+static int armada_drm_ovl_bmm(struct drm_xv *drmxv, unsigned char *buf, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *old, *bo;
+ phys_t phys;
+
+ /* Marvell Dove special protocol */
+ phys = armada_drm_bmm_getbuf(buf);
+ if (phys == (phys_t)-1)
+ return BadAlloc;
+
+ /* Map the passed buffer into a bo */
+ bo = drm_armada_bo_create_phys(drmxv->drm->bufmgr, phys, drmxv->image_size);
+ if (!bo)
+ return BadAlloc;
+
+ /* Free the old bo, and pass it back */
+ old = drmxv->bo[drmxv->bo_idx];
+ if (old) {
+ drm_armada_bo_put(old);
+ armada_drm_bmm_putbuf(buf, drmxv->bo_phys[drmxv->bo_idx]);
+ }
+ drmxv->bo[drmxv->bo_idx] = bo;
+ drmxv->bo_phys[drmxv->bo_idx] = phys;
+
+ *pbo = bo;
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_cvt(struct drm_xv *drmxv, unsigned char *src, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *bo;
+ unsigned char *dst;
+ size_t size;
+ int i;
+
+ /* Standard XV protocol */
+ bo = drmxv->bo[drmxv->bo_idx];
+ if (!bo)
+ return BadAlloc;
+
+ if (++drmxv->bo_idx >= ARRAY_SIZE(drmxv->bo))
+ drmxv->bo_idx = 0;
+
+ *pbo = bo;
+
+ /* Convert YV12 to YV16 or I420 to I422 */
+ dst = bo->ptr;
+ size = drmxv->offsets[1];
+
+ /* Copy Y */
+ memcpy(dst, src, size);
+
+ dst += size;
+ src += size;
+
+ /* Copy U and V, doubling the number of pixels vertically */
+ size = drmxv->pitches[1];
+ for (i = drmxv->image_height; i > 0; i--) {
+ memcpy(dst, src, size);
+ dst += size;
+ memcpy(dst, src, size);
+ dst += size;
+ src += size;
+ }
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_std(struct drm_xv *drmxv, unsigned char *src, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *bo;
+
+ /* Standard XV protocol */
+ bo = drmxv->bo[drmxv->bo_idx];
+ if (!bo)
+ return BadAlloc;
+
+ if (++drmxv->bo_idx >= ARRAY_SIZE(drmxv->bo))
+ drmxv->bo_idx = 0;
+
+ *pbo = bo;
+
+ memcpy(bo->ptr, src, drmxv->image_size);
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_SetupStd(struct drm_xv *drmxv, int image,
+ short width, short height)
+{
+ int i, size, image2 = image;
+ const XF86ImageRec *img;
+
+ drmxv->image = NULL;
+
+ /*
+ * We can't actually do YV12 or I420 - we convert to YV16
+ * or YV16 with U and V swapped.
+ */
+ if (image2 == FOURCC_YV12)
+ image2 = FOURCC_YV16;
+ if (image2 == FOURCC_I420)
+ image2 = FOURCC_I422;
+
+ /*
+ * Lookup the image type. Note that the server already did this in
+ * Xext/xvdisp.c, but xf86XVPutImage() in hw/xfree86/common/xf86xv.c
+ * threw that information away.
+ */
+ img = armada_drm_ovl_get_img(image2);
+ if (!img)
+ return BadMatch;
+
+ size = armada_drm_ovl_set_img_info(img, drmxv->pitches, drmxv->offsets,
+ width, height);
+ if (!size)
+ return BadAlloc;
+
+ drmxv->arg.flags = ARMADA_OVERLAY_ENABLE;
+ if (img->type == XvYUV) {
+ if (img->vert_u_period == 1)
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV422;
+ else
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV420;
+ }
+
+ if (img->format == XvPlanar) {
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV_PLANAR;
+ for (i = 0; i < img->num_planes; i++) {
+ switch (img->component_order[i]) {
+ case 'Y':
+ drmxv->arg.offset_Y = drmxv->offsets[i];
+ drmxv->arg.stride_Y = drmxv->pitches[i];
+ break;
+ case 'U':
+ drmxv->arg.offset_U = drmxv->offsets[i];
+ drmxv->arg.stride_UV = drmxv->pitches[i];
+ break;
+ case 'V':
+ drmxv->arg.offset_V = drmxv->offsets[i];
+ drmxv->arg.stride_UV = drmxv->pitches[i];
+ break;
+ }
+ }
+ } else {
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV_PACKED;
+ drmxv->arg.offset_Y = drmxv->offsets[0];
+ drmxv->arg.offset_U = drmxv->offsets[0];
+ drmxv->arg.offset_V = drmxv->offsets[0];
+ drmxv->arg.stride_Y = drmxv->pitches[0];
+ drmxv->arg.stride_UV = drmxv->pitches[0];
+
+ switch (image) {
+ case FOURCC_VYUY:
+ drmxv->arg.flags |= ARMADA_OVERLAY_UV_SWAP;
+ case FOURCC_UYVY:
+ drmxv->arg.flags |= ARMADA_OVERLAY_Y_SWAP;
+ case FOURCC_YUY2:
+ break;
+ default:
+ return BadAlloc;
+ }
+ }
+
+ armada_drm_ovl_bo_free(drmxv);
+
+ if (drmxv->is_bmm) {
+ drmxv->cvt = armada_drm_ovl_bmm;
+ } else {
+ if (image != image2)
+ drmxv->cvt = armada_drm_ovl_cvt;
+ else
+ drmxv->cvt = armada_drm_ovl_std;
+
+ for (i = 0; i < ARRAY_SIZE(drmxv->bo); i++) {
+ drmxv->bo[i] = armada_drm_ovl_get_bo(drmxv, size, width);
+ if (!drmxv->bo[i]) {
+ armada_drm_ovl_bo_free(drmxv);
+ return BadAlloc;
+ }
+ }
+ }
+
+ drmxv->image = img;
+ drmxv->image_size = size;
+ drmxv->image_width = width;
+ drmxv->image_height = height;
+ drmxv->image_fourcc = image;
+
+ return Success;
+}
+
+static int armada_drm_ovl_clip(ScrnInfoPtr pScrn, struct drm_xv *drmxv,
+ short src_x, short src_y, short drw_x, short drw_y,
+ short src_w, short src_h, short drw_w, short drw_h,
+ RegionPtr clipBoxes, short width, short height, xf86CrtcPtr *crtcp)
+{
+ struct armada_crtc_info *drmc;
+ xf86CrtcPtr crtc = NULL;
+ INT32 xa, xb, ya, yb;
+ short src_sw, src_sh;
+ BoxRec dst;
+
+ xa = src_x;
+ xb = src_x + src_w;
+ ya = src_y;
+ yb = src_y + src_h;
+
+ dst.x1 = drw_x;
+ dst.x2 = drw_x + drw_w;
+ dst.y1 = drw_y;
+ dst.y2 = drw_y + drw_h;
+
+ if (!xf86_crtc_clip_video_helper(pScrn, &crtc, drmxv->desired_crtc,
+ &dst, &xa, &xb, &ya, &yb, clipBoxes,
+ width, height))
+ return BadAlloc;
+
+ *crtcp = crtc;
+
+ if (!crtc)
+ return Success;
+
+ src_sw = width * ((float)(dst.x2 - dst.x1) / drw_w);
+ src_sh = height * ((float)(dst.y2 - dst.y1) / drw_h);
+
+ /* Convert to coordinates on this CRTC. */
+ drw_w = dst.x2 - dst.x1;
+ drw_h = dst.y2 - dst.y1;
+ drw_x = dst.x1 - crtc->x;
+ drw_y = dst.y1 - crtc->y;
+
+{
+ static int dx, dy, dw, dh, sow, soh, ssw, ssh;
+ if (dx != drw_x || dy != drw_y || dw != drw_w || dh != drw_h || sow != width || soh != height || ssw != src_sw || ssh != src_sh) {
+ dx = drw_x; dy = drw_y; dw = drw_w; dh = drw_h; sow = width; soh = height; ssw = src_sw; ssh = src_sh;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "os[%dx%d] ss[%dx%d] dst[%d,%d %dx%d]\n",
+ sow, soh, ssw, ssh, dx, dy, dw, dh);
+ }
+}
+
+ drmc = crtc->driver_private;
+
+ drmxv->arg.src_width = width;
+ drmxv->arg.src_height = height;
+ drmxv->arg.src_scan_width = src_sw;
+ drmxv->arg.src_scan_height = src_sh;
+ drmxv->arg.crtc_id = drmc->mode_crtc->crtc_id;
+ drmxv->arg.dst_x = drw_x;
+ drmxv->arg.dst_y = drw_y;
+ drmxv->arg.dst_width = drw_w;
+ drmxv->arg.dst_height = drw_h;
+
+ return Success;
+}
+
+static int armada_drm_ovl_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 image, unsigned char *buf, short width, short height,
+ Bool sync, RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
+{
+ xf86CrtcPtr crtc = NULL;
+ struct drm_xv *drmxv = data;
+ struct drm_armada_bo *bo;
+ int ret;
+
+// xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+// "PutImage: s%dx%x+%d+%d d%dx%d+%d+%d image %x %dx%d sync %u\n",
+// src_w, src_h, src_x, src_y, drw_w, drw_h, drw_x, drw_y,
+// image, width, height, sync);
+
+ ret = armada_drm_ovl_clip(pScrn, drmxv, src_x, src_y, drw_x, drw_y,
+ src_w, src_h, drw_w, drw_h, clipBoxes,
+ width, height, &crtc);
+ if (ret != Success)
+ return ret;
+
+ if (!crtc) {
+ armada_drm_ovl_StopVideo(pScrn, data, TRUE);
+ return Success;
+ }
+
+ if (image == FOURCC_XVBO) {
+ /* XVBO support allows applications to prepare the DRM buffer
+ * object themselves, and pass a global name to the X server to
+ * update the hardware with. This is similar to Intel XvMC
+ * support, except we also allow the image format to be
+ * specified via a fourcc as the first word.
+ */
+ uint32_t *p = (uint32_t *)buf;
+ uint32_t fmt = p[0];
+ uint32_t name = p[1];
+
+ if (!drmxv->image || drmxv->image_fourcc != fmt ||
+ drmxv->image_width != width ||
+ drmxv->image_height != height) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "[drm] XVBO: fourcc %08x name %08x\n",
+ fmt, name);
+ drmxv->is_bmm = 1;
+ ret = armada_drm_ovl_SetupStd(drmxv, fmt, width, height);
+ if (ret != Success) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] XVBO: SetupStd failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Convert the global name back to a buffer object, so we have
+ * a handle to pass to the kernel.
+ */
+ bo = drm_armada_bo_create_from_name(drmxv->drm->bufmgr, name);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] XVBO: bo_create_from_name failed: %d\n", ret);
+ return BadAlloc;
+ }
+ } else {
+ if (!drmxv->image || drmxv->image_fourcc != image)
+ drmxv->is_bmm = armada_drm_is_bmm(buf);
+
+ if (!drmxv->image || drmxv->image_fourcc != image ||
+ drmxv->image_width != width || drmxv->image_height != height) {
+ ret = armada_drm_ovl_SetupStd(drmxv, image, width, height);
+ if (ret != Success)
+ return ret;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "[drm] bmm %u image fourcc %08x flags %08x\n",
+ drmxv->is_bmm, image, drmxv->arg.flags);
+ }
+
+ ret = drmxv->cvt(drmxv, buf, &bo);
+ if (ret != Success)
+ return ret;
+ }
+
+ drmxv->arg.bo_handle = bo->handle;
+
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE,
+ &drmxv->arg);
+
+ /*
+ * The kernel will keep a reference internally to the buffer object
+ * while it is being displayed.
+ */
+ drm_armada_bo_put(bo);
+
+ /*
+ * Finally, fill the clip boxes; do this after we've done the ioctl
+ * so we don't impact on latency.
+ */
+ if (drmxv->autopaint_colorkey &&
+ !RegionEqual(&drmxv->clipBoxes, clipBoxes)) {
+ RegionCopy(&drmxv->clipBoxes, clipBoxes);
+
+ xf86XVFillKeyHelper(pScrn->pScreen, drmxv->attrs.color_key, clipBoxes);
+ }
+
+ return Success;
+}
+
+static int armada_drm_ovl_ReputImage(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,
+ RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
+{
+ xf86CrtcPtr crtc = NULL;
+ struct drm_xv *drmxv = data;
+ int ret;
+
+ if (!drmxv->image)
+ return Success;
+
+ ret = armada_drm_ovl_clip(pScrn, drmxv, src_x, src_y, drw_x, drw_y,
+ src_w, src_h, drw_w, drw_h, clipBoxes,
+ drmxv->image_width, drmxv->image_height, &crtc);
+ if (ret)
+ return ret;
+
+ if (!crtc) {
+ armada_drm_ovl_StopVideo(pScrn, data, TRUE);
+ return Success;
+ }
+
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE,
+ &drmxv->arg);
+
+ if (drmxv->autopaint_colorkey &&
+ !RegionEqual(&drmxv->clipBoxes, clipBoxes)) {
+ RegionCopy(&drmxv->clipBoxes, clipBoxes);
+
+ xf86XVFillKeyHelper(pScrn->pScreen, drmxv->attrs.color_key, clipBoxes);
+ }
+
+ return Success;
+}
+
+static int armada_drm_ovl_QueryImageAttributes(ScrnInfoPtr pScrn,
+ int image, unsigned short *width, unsigned short *height,
+ int *pitches, int *offsets)
+{
+ const XF86ImageRec *img;
+ unsigned ret = 0;
+
+ *width = (*width + 1) & ~1;
+ *height = (*height + 1) & ~1;
+
+ img = armada_drm_ovl_get_img(image);
+ if (img) {
+ int pitch[3], offset[3];
+
+ ret = armada_drm_ovl_set_img_info(img, pitch, offset,
+ *width, *height);
+
+ if (ret) {
+ if (pitches)
+ memcpy(pitches, pitch, sizeof(*pitches) * img->num_planes);
+ if (offsets)
+ memcpy(offsets, offset, sizeof(*offsets) * img->num_planes);
+ }
+ }
+ return ret;
+}
+
+Bool armada_drm_XvInit(ScrnInfoPtr pScrn)
+{
+ ScreenPtr scrn = screenInfo.screens[pScrn->scrnIndex];
+ XF86VideoAdaptorPtr xv[1], p;
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_xv *drmxv;
+ DevUnion priv[1];
+ Bool ret;
+
+ if (!xvColorKey) {
+ xvAutoPaintColorKey = MAKE_ATOM("XV_AUTOPAINT_COLORKEY");
+ xvColorKey = MAKE_ATOM("XV_COLORKEY");
+ xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
+ xvContrast = MAKE_ATOM("XV_CONTRAST");
+ xvSaturation = MAKE_ATOM("XV_SATURATION");
+ xvDeinterlace = MAKE_ATOM("XV_DEINTERLACE");
+ xvPipe = MAKE_ATOM("XV_PIPE");
+ }
+
+ drmxv = calloc(1, sizeof *drmxv);
+ if (!drmxv)
+ return FALSE;
+
+ drmxv->drm = drm;
+ drmxv->autopaint_colorkey = TRUE;
+
+ drmIoctl(drm->fd, DRM_IOCTL_ARMADA_OVERLAY_ATTRS, &drmxv->attrs);
+
+ p = xf86XVAllocateVideoAdaptorRec(pScrn);
+ if (!p) {
+ free(drmxv);
+ return FALSE;
+ }
+
+ priv[0].ptr = drmxv;
+
+ p->type = XvWindowMask | XvInputMask | XvImageMask;
+ p->flags = VIDEO_OVERLAID_IMAGES;
+ p->name = "Marvell Armada Overlay Video";
+ p->nEncodings = sizeof(OverlayEncodings) / sizeof(XF86VideoEncodingRec);
+ p->pEncodings = OverlayEncodings;
+ p->nFormats = sizeof(OverlayFormats) / sizeof(XF86VideoFormatRec);
+ p->pFormats = OverlayFormats;
+ p->nPorts = 1;
+ p->pPortPrivates = priv;
+ p->nAttributes = sizeof(OverlayAttributes) / sizeof(XF86AttributeRec);
+ p->pAttributes = OverlayAttributes;
+ p->nImages = sizeof(OverlayImages) / sizeof(XF86ImageRec);
+ p->pImages = OverlayImages;
+ p->StopVideo = armada_drm_ovl_StopVideo;
+ p->SetPortAttribute = armada_drm_ovl_SetPortAttribute;
+ p->GetPortAttribute = armada_drm_ovl_GetPortAttribute;
+ p->QueryBestSize = armada_drm_ovl_QueryBestSize;
+ p->PutImage = armada_drm_ovl_PutImage;
+ p->ReputImage = armada_drm_ovl_ReputImage;
+ p->QueryImageAttributes = armada_drm_ovl_QueryImageAttributes;
+
+ xv[0] = p;
+ ret = xf86XVScreenInit(scrn, xv, 1);
+ free(p);
+
+ if (!ret)
+ free(drmxv);
+
+ return ret;
+}