/* * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifdef HAVE_DIX_CONFIG_H #include "dix-config.h" #endif #include "exa.h" #include "xf86.h" #include "driver.h" #include "dumb_bo.h" #include "fbpict.h" #include #ifdef MODESETTING_WITH_RGA #include #include #define RGA_MIN_LINEWIDTH 2 /* See rga_get_pixmap_format */ #define PIXMAP_IS_YUV(pix) ((pix)->drawable.bitsPerPixel == 12) #endif #define ABS(n) ((n) < 0 ? -(n) : (n)) #define ANGLE(n) ((n) < 0 ? (n) + 360 : (n)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) typedef struct { struct dumb_bo *bo; int fd; int pitch; Bool owned; } ms_exa_pixmap_priv; typedef struct { struct { int alu; Pixel planemask; Pixel fg; } solid; struct { PixmapPtr pSrcPixmap; int alu; Pixel planemask; } copy; struct { int op; PicturePtr pSrcPicture; PicturePtr pMaskPicture; PicturePtr pDstPicture; PixmapPtr pSrc; PixmapPtr pMask; PixmapPtr pDst; int rotate; Bool reflect_y; } composite; } ms_exa_prepare_args; typedef struct { ms_exa_pixmap_priv *scratch_pixmap; ms_exa_prepare_args prepare_args; } ms_exa_ctx; #ifdef MODESETTING_WITH_RGA static inline RgaSURF_FORMAT rga_get_pixmap_format(PixmapPtr pPix) { switch (pPix->drawable.bitsPerPixel) { case 32: if (pPix->drawable.depth == 32) return RK_FORMAT_BGRA_8888; return RK_FORMAT_BGRX_8888; case 12: switch (pPix->drawable.depth) { case 12: return RK_FORMAT_YCbCr_420_SP; /* HACK: Special depth for NV12_10 and NV16*/ case 10: return RK_FORMAT_YCbCr_420_SP_10B; default: return RK_FORMAT_YCbCr_422_SP; } default: return RK_FORMAT_UNKNOWN; } } static Bool rga_prepare_info(PixmapPtr pPixmap, rga_info_t *info, int x, int y, int w, int h) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPixmap); RgaSURF_FORMAT format; int pitch, wstride, hstride; memset(info, 0, sizeof(rga_info_t)); info->fd = -1; info->mmuFlag = 1; if (priv && priv->fd) { info->fd = priv->fd; pitch = priv->pitch; } else { info->virAddr = pPixmap->devPrivate.ptr; pitch = pPixmap->devKind; } format = rga_get_pixmap_format(pPixmap); /* rga requires yuv image rect align to 2 */ if (PIXMAP_IS_YUV(pPixmap)) { x = (x + 1) & ~1; y = (y + 1) & ~1; w = w & ~1; h = h & ~1; } /* rga requires image width/height larger than 2 */ if (w <= RGA_MIN_LINEWIDTH || h <= RGA_MIN_LINEWIDTH) return FALSE; wstride = pitch * 8 / pPixmap->drawable.bitsPerPixel; hstride = pPixmap->drawable.height; if (x < 0 || y < 0 || x + w > wstride || y + h > hstride) return FALSE; rga_set_rect(&info->rect, x, y, w, h, wstride, hstride, format); return TRUE; } static Bool rga_check_pixmap(PixmapPtr pPixmap) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPixmap); RgaSURF_FORMAT format; /* rga requires image width/height larger than 2 */ if (pPixmap->drawable.width <= RGA_MIN_LINEWIDTH && pPixmap->drawable.height <= RGA_MIN_LINEWIDTH) return FALSE; format = rga_get_pixmap_format(pPixmap); if (format == RK_FORMAT_UNKNOWN) return FALSE; if (pPixmap->devKind && pPixmap->devPrivate.ptr) return TRUE; return priv && priv->fd; } #endif static void ms_exa_done(PixmapPtr pPixmap) {} Bool ms_exa_prepare_access(PixmapPtr pPix, int index) { ScreenPtr screen = pPix->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPix); if (pPix->devPrivate.ptr) return TRUE; if (!priv) return FALSE; dumb_bo_map(ms->drmmode.fd, priv->bo); pPix->devPrivate.ptr = priv->bo->ptr; return pPix->devPrivate.ptr != NULL; } void ms_exa_finish_access(PixmapPtr pPix, int index) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPix); if (priv && priv->bo) pPix->devPrivate.ptr = NULL; } static Bool ms_exa_prepare_solid(PixmapPtr pPixmap, int alu, Pixel planemask, Pixel fg) { ScreenPtr screen = pPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; #ifdef MODESETTING_WITH_RGA //int rop; if (planemask != ~0U) return FALSE; if (!rga_check_pixmap(pPixmap)) return FALSE; /* TODO: Support rop */ switch (alu) { case GXcopy: break; case GXclear: case GXset: case GXcopyInverted: default: return FALSE; } #endif ctx->prepare_args.solid.alu = alu; ctx->prepare_args.solid.planemask = planemask; ctx->prepare_args.solid.fg = fg; return TRUE; } static void ms_exa_solid_bail(PixmapPtr pPixmap, int x1, int y1, int x2, int y2) { ScreenPtr screen = pPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; ChangeGCVal val[3]; GCPtr gc; gc = GetScratchGC(pPixmap->drawable.depth, screen); val[0].val = ctx->prepare_args.solid.alu; val[1].val = ctx->prepare_args.solid.planemask; val[2].val = ctx->prepare_args.solid.fg; ChangeGC(NullClient, gc, GCFunction | GCPlaneMask | GCForeground, val); ValidateGC(&pPixmap->drawable, gc); ms_exa_prepare_access(pPixmap, 0); fbFill(&pPixmap->drawable, gc, x1, y1, x2 - x1, y2 - y1); ms_exa_finish_access(pPixmap, 0); FreeScratchGC(gc); } static void ms_exa_solid(PixmapPtr pPixmap, int x1, int y1, int x2, int y2) { #ifdef MODESETTING_WITH_RGA ScreenPtr screen = pPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; rga_info_t dst_info = {0}; int width = x2 - x1; int height = y2 - y1; /* skip small images */ if (width * height <= 4096) goto bail; if (!rga_prepare_info(pPixmap, &dst_info, x1, y1, x2 - x1, y2 - y1)) goto bail; dst_info.color = ctx->prepare_args.solid.fg; /* rga only support RGBA8888 for color fill */ if (pPixmap->drawable.bitsPerPixel != 32) goto bail; if (pPixmap->drawable.depth == 24) dst_info.color |= 0xFF << 24; dst_info.rect.format = RK_FORMAT_RGBA_8888; if (c_RkRgaColorFill(&dst_info) < 0) goto bail; return; bail: #endif ms_exa_solid_bail(pPixmap, x1, y1, x2, y2); } static Bool ms_exa_prepare_copy(PixmapPtr pSrcPixmap, PixmapPtr pDstPixmap, int dx, int dy, int alu, Pixel planemask) { ScreenPtr screen = pSrcPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; #ifdef MODESETTING_WITH_RGA //int rop; if (planemask != ~0U) return FALSE; if (!rga_check_pixmap(pSrcPixmap)) return FALSE; if (!rga_check_pixmap(pDstPixmap)) return FALSE; /* TODO: Support rop */ switch (alu) { case GXcopy: break; case GXclear: case GXset: case GXcopyInverted: default: return FALSE; } #endif ctx->prepare_args.copy.pSrcPixmap = pSrcPixmap; ctx->prepare_args.copy.alu = alu; ctx->prepare_args.copy.planemask = planemask; return TRUE; } static void ms_exa_copy_bail(PixmapPtr pDstPixmap, int srcX, int srcY, int dstX, int dstY, int width, int height) { ScreenPtr screen = pDstPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PixmapPtr pSrcPixmap = ctx->prepare_args.copy.pSrcPixmap; ChangeGCVal val[2]; GCPtr gc; gc = GetScratchGC(pDstPixmap->drawable.depth, screen); val[0].val = ctx->prepare_args.copy.alu; val[1].val = ctx->prepare_args.copy.planemask; ChangeGC(NullClient, gc, GCFunction | GCPlaneMask, val); ValidateGC(&pDstPixmap->drawable, gc); ms_exa_prepare_access(pSrcPixmap, 0); ms_exa_prepare_access(pDstPixmap, 0); fbCopyArea(&pSrcPixmap->drawable, &pDstPixmap->drawable, gc, srcX, srcY, width, height, dstX, dstY); ms_exa_finish_access(pDstPixmap, 0); ms_exa_finish_access(pSrcPixmap, 0); FreeScratchGC(gc); } static void ms_exa_copy(PixmapPtr pDstPixmap, int srcX, int srcY, int dstX, int dstY, int width, int height) { #ifdef MODESETTING_WITH_RGA ScreenPtr screen = pDstPixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PixmapPtr pSrcPixmap = ctx->prepare_args.copy.pSrcPixmap; rga_info_t src_info = {0}; rga_info_t dst_info = {0}; rga_info_t tmp_info = {0}; /* skip small images */ if (width * height <= 4096) goto bail; if (!rga_prepare_info(pSrcPixmap, &src_info, srcX, srcY, width, height)) goto bail; if (!rga_prepare_info(pDstPixmap, &dst_info, dstX, dstY, width, height)) goto bail; /* need an extra buffer for overlap copy */ if (pSrcPixmap == pDstPixmap && (ABS(dstX - srcX) < width || ABS(dstY - srcX) < height)) { tmp_info.fd = ctx->scratch_pixmap->fd; tmp_info.mmuFlag = 1; rga_set_rect(&tmp_info.rect, 0, 0, width, height, width, height, rga_get_pixmap_format(pDstPixmap)); if (c_RkRgaBlit(&src_info, &tmp_info, NULL) < 0) goto bail; src_info = tmp_info; } if (c_RkRgaBlit(&src_info, &dst_info, NULL) < 0) goto bail; return; bail: #endif ms_exa_copy_bail(pDstPixmap, srcX, srcY, dstX, dstY, width, height); } static Bool ms_exa_check_composite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture) { #ifdef MODESETTING_WITH_RGA /* TODO: Support other op */ if (op != PictOpSrc && op != PictOpOver) return FALSE; /* TODO: Support mask */ if (pMaskPicture) return FALSE; /* TODO: Multiply transform from src and dst */ if (pDstPicture->transform) return FALSE; #endif if (!pSrcPicture->pDrawable) return FALSE; return TRUE; } static Bool ms_exa_parse_transform(PictTransformPtr t, int *rotate, Bool *reflect_y) { PictVector v; double x, y, dx, dy; int r1, r2; if (!t) { *rotate = 0; *reflect_y = FALSE; return TRUE; } /* Only support affine matrix */ if (t->matrix[2][0] || t->matrix[2][1] || !t->matrix[2][2]) return FALSE; dx = t->matrix[0][2] / (double) t->matrix[2][2]; dy = t->matrix[1][2] / (double) t->matrix[2][2]; v.vector[0] = IntToxFixed(1); v.vector[1] = IntToxFixed(0); v.vector[2] = xFixed1; PictureTransformPoint(t, &v); x = pixman_fixed_to_double(v.vector[0]) - dx; y = pixman_fixed_to_double(v.vector[1]) - dy; r1 = (int) ANGLE(atan2(y, x) * 180 / M_PI); /* Only support 0/90/180/270 rotations */ if (r1 % 90) return FALSE; v.vector[0] = IntToxFixed(0); v.vector[1] = IntToxFixed(1); v.vector[2] = xFixed1; PictureTransformPoint(t, &v); x = pixman_fixed_to_double(v.vector[0]) - dx; y = pixman_fixed_to_double(v.vector[1]) - dy; r2 = (int) ANGLE(atan2(y, x) * 180 / M_PI - 90); *rotate = (360 - r1) % 360; *reflect_y = r1 != r2; return TRUE; } static Bool ms_exa_prepare_composite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, PixmapPtr pSrc, PixmapPtr pMask, PixmapPtr pDst) { ScreenPtr screen = pSrc->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; #ifdef MODESETTING_WITH_RGA PictTransformPtr t = pSrcPicture->transform; if (!rga_check_pixmap(pSrc)) return FALSE; if (!rga_check_pixmap(pDst)) return FALSE; if (pDst == pSrc) return FALSE; /* TODO: Support repeat */ if (pSrcPicture->repeat) return FALSE; /* TODO: Handle pSrcPicture->filter */ if (!ms_exa_parse_transform(t, &ctx->prepare_args.composite.rotate, &ctx->prepare_args.composite.reflect_y)) return FALSE; #endif ctx->prepare_args.composite.op = op; ctx->prepare_args.composite.pSrcPicture = pSrcPicture; ctx->prepare_args.composite.pMaskPicture = pMaskPicture; ctx->prepare_args.composite.pDstPicture = pDstPicture; ctx->prepare_args.composite.pSrc = pSrc; ctx->prepare_args.composite.pMask = pMask; return TRUE; } static inline void ms_exa_composite_fix_offsets(DrawablePtr pDrawable, PixmapPtr pPix, int *xoff, int *yoff) { // Base on fb/fb.h#fbGetDrawablePixmap if (pDrawable->type != DRAWABLE_PIXMAP) { ScreenPtr pScreen = pDrawable->pScreen; pPix = pScreen->GetWindowPixmap((WindowPtr) pDrawable); #ifdef COMPOSITE *xoff -= pPix->drawable.x - pPix->screen_x; *yoff -= pPix->drawable.y - pPix->screen_y; #else *xoff -= pPix->drawable.x; *yoff -= pPix->drawable.y; #endif } else { *xoff -= pDrawable->x; *yoff -= pDrawable->y; } // Based on fb/fbpict.c#create_bits_picture *xoff -= pDrawable->x; *yoff -= pDrawable->y; } static void ms_exa_composite_bail(PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY, int dstX, int dstY, int width, int height) { ScreenPtr screen = pDst->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PicturePtr pSrcPicture = ctx->prepare_args.composite.pSrcPicture; PicturePtr pMaskPicture = ctx->prepare_args.composite.pMaskPicture; PicturePtr pDstPicture = ctx->prepare_args.composite.pDstPicture; PixmapPtr pSrc = ctx->prepare_args.composite.pSrc; PixmapPtr pMask = ctx->prepare_args.composite.pMask; int op = ctx->prepare_args.composite.op; if (pMask) ms_exa_prepare_access(pMask, 0); ms_exa_prepare_access(pSrc, 0); ms_exa_prepare_access(pDst, 0); ms_exa_composite_fix_offsets(pSrcPicture->pDrawable, pSrc, &srcX, &srcY); ms_exa_composite_fix_offsets(pDstPicture->pDrawable, pDst, &dstX, &dstY); if (pMaskPicture && pMask) ms_exa_composite_fix_offsets(pMaskPicture->pDrawable, pMask, &maskX, &maskY); fbComposite(op, pSrcPicture, pMaskPicture, pDstPicture, srcX, srcY, maskX, maskY, dstX, dstY, width, height); ms_exa_finish_access(pDst, 0); ms_exa_finish_access(pSrc, 0); if (pMask) ms_exa_finish_access(pMask, 0); } static void ms_exa_composite(PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY, int dstX, int dstY, int width, int height) { #ifdef MODESETTING_WITH_RGA ScreenPtr screen = pDst->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PictTransformPtr t = ctx->prepare_args.composite.pSrcPicture->transform; PixmapPtr pSrc = ctx->prepare_args.composite.pSrc; BoxRec box = { .x1 = srcX, .y1 = srcY, .x2 = srcX + width, .y2 = srcY + height, }; rga_info_t src_info = {0}; rga_info_t dst_info = {0}; rga_info_t tmp_info = {0}; Bool reflect_y = ctx->prepare_args.composite.reflect_y; int rotate = ctx->prepare_args.composite.rotate; int op = ctx->prepare_args.composite.op; int sw, sh, blend = 0; /* skip small images */ if (width * height <= 4096) goto bail; if (t) pixman_transform_bounds(t, &box); sw = box.x2 - box.x1; sh = box.y2 - box.y1; /* rga has scale limits */ if ((double)sw / width > 16 || (double)width / sw > 16 || (double)sh / height > 16 || (double)height / sh > 16) goto bail; if (!rga_prepare_info(pSrc, &src_info, box.x1, box.y1, sw, sh)) goto bail; if (!rga_prepare_info(pDst, &dst_info, dstX, dstY, width, height)) goto bail; /* dst = src + (1 - src.a) * dst */ if (op == PictOpOver) blend = 0xFF0405; if (rotate == 90) src_info.rotation = HAL_TRANSFORM_ROT_90; else if (rotate == 180) src_info.rotation = HAL_TRANSFORM_ROT_180; else if (rotate == 270) src_info.rotation = HAL_TRANSFORM_ROT_270; /* need an extra buffer for reflect + rotate composite */ if (reflect_y && rotate) { tmp_info.fd = ctx->scratch_pixmap->fd; tmp_info.mmuFlag = 1; rga_set_rect(&tmp_info.rect, 0, 0, width, height, width, height, rga_get_pixmap_format(pDst)); src_info.blend = blend; if (c_RkRgaBlit(&src_info, &tmp_info, NULL) < 0) goto bail; src_info = tmp_info; } if (reflect_y) src_info.rotation = HAL_TRANSFORM_FLIP_V; src_info.blend = blend; if (c_RkRgaBlit(&src_info, &dst_info, NULL) < 0) goto bail; return; bail: #endif ms_exa_composite_bail(pDst, srcX, srcY, maskX, maskY, dstX, dstY, width, height); } static Bool ms_exa_upload_to_screen(PixmapPtr pDst, int x, int y, int w, int h, char *src, int src_pitch) { #ifndef MODESETTING_WITH_RGA return FALSE; #else ScreenPtr screen = pDst->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PixmapPtr pixmap; Bool ret = FALSE; /* rga requires image width/height larger than 2 */ if (w <= RGA_MIN_LINEWIDTH || h <= RGA_MIN_LINEWIDTH) return FALSE; /* skip small images */ if (w * h <= 4096) return FALSE; if (!rga_check_pixmap(pDst)) return FALSE; /* copy the h-1 lines */ h -= 1; pixmap = drmmode_create_pixmap_header(screen, w, h, pDst->drawable.depth, pDst->drawable.bitsPerPixel, src_pitch, NULL); if (!pixmap) goto out; pixmap->devKind = src_pitch; pixmap->devPrivate.ptr = src; ctx->prepare_args.copy.pSrcPixmap = pixmap; ms_exa_copy(pDst, 0, 0, x, y, w, h); screen->DestroyPixmap(pixmap); /* copy the last line separately */ pixmap = drmmode_create_pixmap_header(screen, w, 1, pDst->drawable.depth, pDst->drawable.bitsPerPixel, src_pitch, NULL); if (!pixmap) goto out; pixmap->devKind = w * pDst->drawable.bitsPerPixel / 8; pixmap->devPrivate.ptr = src + src_pitch * h; ctx->prepare_args.copy.pSrcPixmap = pixmap; ms_exa_copy(pDst, 0, 0, x, y + h, w, 1); ret = TRUE; out: if (pixmap) screen->DestroyPixmap(pixmap); return ret; #endif } static Bool ms_exa_download_from_screen(PixmapPtr pSrc, int x, int y, int w, int h, char *dst, int dst_pitch) { #ifndef MODESETTING_WITH_RGA return FALSE; #else ScreenPtr screen = pSrc->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); ms_exa_ctx *ctx = ms->drmmode.exa->priv; PixmapPtr pixmap; Bool ret = FALSE; /* rga requires image width/height larger than 2 */ if (w <= RGA_MIN_LINEWIDTH || h <= RGA_MIN_LINEWIDTH) return FALSE; /* skip small images */ if (w * h <= 4096) return FALSE; if (!rga_check_pixmap(pSrc)) return FALSE; ctx->prepare_args.copy.pSrcPixmap = pSrc; /* copy the h-1 lines */ h -= 1; pixmap = drmmode_create_pixmap_header(screen, w, h, pSrc->drawable.depth, pSrc->drawable.bitsPerPixel, dst_pitch, NULL); if (!pixmap) goto out; pixmap->devKind = dst_pitch; pixmap->devPrivate.ptr = dst; ms_exa_copy(pixmap, x, y, 0, 0, w, h); screen->DestroyPixmap(pixmap); /* copy the last line separately */ pixmap = drmmode_create_pixmap_header(screen, w, 1, pSrc->drawable.depth, pSrc->drawable.bitsPerPixel, dst_pitch, NULL); if (!pixmap) goto out; pixmap->devKind = w * pSrc->drawable.bitsPerPixel / 8; pixmap->devPrivate.ptr = dst + dst_pitch * h; ms_exa_copy(pixmap, x, y + h, 0, 0, w, 1); ret = TRUE; out: if (pixmap) screen->DestroyPixmap(pixmap); return ret; #endif } static void ms_exa_wait_marker(ScreenPtr pScreen, int marker) { // TODO: Use async rga, and sync for specified request here. } static int ms_exa_mark_sync(ScreenPtr pScreen) { // TODO: return latest request(marker). return 0; } static void ms_exa_destroy_pixmap(ScreenPtr pScreen, void *driverPriv) { ScrnInfoPtr scrn = xf86Screens[pScreen->myNum]; modesettingPtr ms = modesettingPTR(scrn); ms_exa_pixmap_priv *priv = driverPriv; if (priv->fd > 0) close(priv->fd); if (priv->owned && priv->bo) dumb_bo_destroy(ms->drmmode.fd, priv->bo); free(priv); } static void * ms_exa_create_pixmap2(ScreenPtr pScreen, int width, int height, int depth, int usage_hint, int bitsPerPixel, int *new_fb_pitch) { ScrnInfoPtr scrn = xf86Screens[pScreen->myNum]; modesettingPtr ms = modesettingPTR(scrn); ms_exa_pixmap_priv *priv; priv = calloc(1, sizeof(ms_exa_pixmap_priv)); if (!priv) return NULL; if (!width && !height) return priv; priv->bo = dumb_bo_create(ms->drmmode.fd, width, height, bitsPerPixel); if (!priv->bo) goto fail; priv->owned = TRUE; priv->fd = dumb_bo_get_fd(ms->drmmode.fd, priv->bo, 0); priv->pitch = priv->bo->pitch; if (new_fb_pitch) *new_fb_pitch = priv->pitch; return priv; fail: free(priv); return NULL; } static Bool ms_exa_pixmap_is_offscreen(PixmapPtr pPixmap) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPixmap); return priv && priv->bo; } Bool ms_exa_set_pixmap_bo(ScrnInfoPtr scrn, PixmapPtr pPixmap, struct dumb_bo *bo, Bool owned) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pPixmap); modesettingPtr ms = modesettingPTR(scrn); if (!ms->drmmode.exa || !priv) return FALSE; if (priv->fd > 0) close(priv->fd); if (priv->owned && priv->bo) dumb_bo_destroy(ms->drmmode.fd, priv->bo); priv->bo = bo; priv->fd = dumb_bo_get_fd(ms->drmmode.fd, priv->bo, 0); priv->pitch = priv->bo->pitch; priv->owned = owned; pPixmap->devPrivate.ptr = NULL; pPixmap->devKind = priv->pitch; return TRUE; } struct dumb_bo * ms_exa_bo_from_pixmap(ScreenPtr screen, PixmapPtr pixmap) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pixmap); ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); if (!ms->drmmode.exa || !priv) return NULL; return priv->bo; } void ms_exa_exchange_buffers(PixmapPtr front, PixmapPtr back) { ms_exa_pixmap_priv *front_priv = exaGetPixmapDriverPrivate(front); ms_exa_pixmap_priv *back_priv = exaGetPixmapDriverPrivate(back); ms_exa_pixmap_priv tmp_priv; tmp_priv = *front_priv; *front_priv = *back_priv; *back_priv = tmp_priv; } Bool ms_exa_back_pixmap_from_fd(PixmapPtr pixmap, int fd, CARD16 width, CARD16 height, CARD16 stride, CARD8 depth, CARD8 bpp) { ScreenPtr screen = pixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); struct dumb_bo *bo; Bool ret; bo = dumb_get_bo_from_fd(ms->drmmode.fd, fd, stride, stride * height); if (!bo) return FALSE; screen->ModifyPixmapHeader(pixmap, width, height, depth, bpp, stride, NULL); ret = ms_exa_set_pixmap_bo(scrn, pixmap, bo, TRUE); if (!ret) dumb_bo_destroy(ms->drmmode.fd, bo); return ret; } int ms_exa_shareable_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, CARD16 *stride, CARD32 *size) { ms_exa_pixmap_priv *priv = exaGetPixmapDriverPrivate(pixmap); ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); if (!ms->drmmode.exa || !priv || !priv->fd) return -1; return priv->fd; } Bool ms_exa_copy_area(PixmapPtr pSrc, PixmapPtr pDst, pixman_f_transform_t *transform, RegionPtr clip) { #ifdef MODESETTING_WITH_RGA ScreenPtr screen = pSrc->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); rga_info_t src_info = {0}; rga_info_t dst_info = {0}; RegionPtr region = NULL; BoxPtr box; int n; if (!ms->drmmode.exa) goto bail; if (!rga_check_pixmap(pSrc)) goto bail; if (!rga_check_pixmap(pDst)) goto bail; /* Fallback to compositor for rotate / reflect */ if (transform) { pixman_transform_t t; Bool reflect_y = FALSE; int rotate = 0; pixman_transform_from_pixman_f_transform(&t, transform); if (!ms_exa_parse_transform(&t, &rotate, &reflect_y)) goto bail; if (rotate || reflect_y) goto bail; } region = RegionDuplicate(clip); n = REGION_NUM_RECTS(region); while(n--) { int sx, sy, sw, sh, dx, dy, dw, dh; box = REGION_RECTS(region) + n; box->x1 = max(box->x1, 0); box->y1 = max(box->y1, 0); box->x2 = min(box->x2, pDst->drawable.width); box->y2 = min(box->y2, pDst->drawable.height); dx = box->x1; dy = box->y1; dw = box->x2 - box->x1; dh = box->y2 - box->y1; if (transform) pixman_f_transform_bounds(transform, box); sx = max(box->x1, 0); sy = max(box->y1, 0); sw = min(box->x2, pSrc->drawable.width) - sx; sh = min(box->y2, pSrc->drawable.height) - sy; /* rga has scale limits */ if ((double)sw / dw > 16 || (double)dw / sw > 16 || (double)sh / dh > 16 || (double)dh / sh > 16) goto err; if (!rga_prepare_info(pSrc, &src_info, sx, sy, sw, sh)) goto err; if (!rga_prepare_info(pDst, &dst_info, dx, dy, dw, dh)) goto err; if (!c_RkRgaBlit(&src_info, &dst_info, NULL)) continue; err: /* HACK: Ignoring errors for YUV, since xserver cannot handle it */ if (!PIXMAP_IS_YUV(pSrc) && !PIXMAP_IS_YUV(pDst)) goto bail; } RegionDestroy(region); return TRUE; bail: if (region) RegionDestroy(region); #endif return ms_copy_area(pSrc, pDst, transform, clip); } static inline void ms_setup_exa(ExaDriverPtr exa) { exa->exa_major = EXA_VERSION_MAJOR; exa->exa_minor = EXA_VERSION_MINOR; exa->pixmapPitchAlign = 8; exa->flags = EXA_OFFSCREEN_PIXMAPS; exa->maxX = 4096; exa->maxY = 4096; exa->PrepareSolid = ms_exa_prepare_solid; exa->Solid = ms_exa_solid; exa->DoneSolid = ms_exa_done; exa->PrepareCopy = ms_exa_prepare_copy; exa->Copy = ms_exa_copy; exa->DoneCopy = ms_exa_done; exa->CheckComposite = ms_exa_check_composite; exa->PrepareComposite = ms_exa_prepare_composite; exa->Composite = ms_exa_composite; exa->DoneComposite = ms_exa_done; /* Disable upload/download, until rga2 crash issue fixed */ exa->UploadToScreen = ms_exa_upload_to_screen; exa->DownloadFromScreen = ms_exa_download_from_screen; exa->WaitMarker = ms_exa_wait_marker; exa->MarkSync = ms_exa_mark_sync; // bo based pixmap ops exa->flags |= EXA_HANDLES_PIXMAPS | EXA_SUPPORTS_PREPARE_AUX; exa->DestroyPixmap = ms_exa_destroy_pixmap; exa->CreatePixmap2 = ms_exa_create_pixmap2; exa->PrepareAccess = ms_exa_prepare_access; exa->FinishAccess = ms_exa_finish_access; exa->PixmapIsOffscreen = ms_exa_pixmap_is_offscreen; } Bool ms_init_exa(ScrnInfoPtr scrn) { modesettingPtr ms = modesettingPTR(scrn); ScreenPtr screen = scrn->pScreen; drmmode_exa *exa; ms_exa_ctx *ctx; if (ms->drmmode.exa) ms_deinit_exa(scrn); #ifdef MODESETTING_WITH_RGA if (c_RkRgaInit() < 0) return FALSE; xf86DrvMsg(scrn->scrnIndex, X_INFO, "Using RGA EXA\n"); #else xf86DrvMsg(scrn->scrnIndex, X_INFO, "Using software EXA\n"); #endif ms->drmmode.exa = calloc(1, sizeof(drmmode_exa)); if (!ms->drmmode.exa) return FALSE; exa = ms->drmmode.exa; exa->driver = exaDriverAlloc(); if (!exa->driver) goto bail; ms_setup_exa(exa->driver); if (!exaDriverInit(screen, exa->driver)) goto bail; exa->priv = calloc(1, sizeof(ms_exa_ctx)); if (!exa->priv) goto bail; ctx = exa->priv; ctx->scratch_pixmap = ms_exa_create_pixmap2(screen, exa->driver->maxX, exa->driver->maxY, scrn->depth, 0, scrn->bitsPerPixel, NULL); if (!ctx->scratch_pixmap) goto bail; return TRUE; bail: ms_deinit_exa(scrn); return FALSE; } void ms_deinit_exa(ScrnInfoPtr scrn) { modesettingPtr ms = modesettingPTR(scrn); ScreenPtr screen = scrn->pScreen; drmmode_exa *exa; ms_exa_ctx *ctx; if (!ms->drmmode.exa) return; exa = ms->drmmode.exa; ctx = exa->priv; if (ctx) { if (ctx->scratch_pixmap) ms_exa_destroy_pixmap(screen, ctx->scratch_pixmap); free(ctx); } if (exa->driver) { exaDriverFini(screen); free(exa->driver); } free(exa); ms->drmmode.exa = NULL; #ifdef MODESETTING_WITH_RGA c_RkRgaDeInit(); #endif }