diff options
Diffstat (limited to 'src/u8500_video.c')
-rw-r--r-- | src/u8500_video.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/src/u8500_video.c b/src/u8500_video.c new file mode 100644 index 0000000..b55f5ef --- /dev/null +++ b/src/u8500_video.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2010 Instituto Nokia de Tecnologia + * Copyright (c) 2010 STE + * + * Authors: + * Ricardo Salveti de Araujo <ricardo.salveti@openbossa.org> + * Aloisio Almeida <aloisio.almeida@openbossa.org> + * + * This adaptor code is based on the draft released by STE, using xkdrive. + * + * The driver implements the XVideo adaptor using a video overlay. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mali_fbdev.h" +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fourcc.h> + +#include <blt_api.h> +#include <linux/hwmem.h> +#include <X11/extensions/Xv.h> + +#include "mali_exa.h" +#include "exa.h" +#include "damage.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define NUM_OVERLAY_PORTS 1 +#define ALIGN_VALUE(a) ((a + 15) & ~15) + +#define ENTER() DebugF("Enter %s\n", __FUNCTION__) +#define LEAVE() DebugF("Leave %s\n", __FUNCTION__) + +typedef struct { + /** Port attributes Global settings */ + int color_key; + int autopaint_colorkey; + + /** YUV framerate */ + int src_x, src_y, + src_w, src_h, + dst_x, dst_y, + dst_w, dst_h, + img_width, img_height; + + /*int fd_fb;*/ + int blt_handle; + int hwmem_fd; + int hwmem_size; + int buffer_handle; + void* vaddr; + + Bool hwmem_buffer_initialized; + RegionRec clip; + DrawablePtr pDraw; + int color_format; /* FOURCC */ +} U8500PortPrivRec, *U8500PortPrivPtr; + +static XF86VideoEncodingRec DummyEncoding = { + 0, "XV_IMAGE", VIDEO_IMAGE_MAX_WIDTH, VIDEO_IMAGE_MAX_HEIGHT, {1, 1}, +}; + +static XF86VideoFormatRec Formats[] = +{ + { 15, TrueColor }, + { 16, TrueColor }, + { 24, TrueColor }, +}; + +static XF86AttributeRec OverlayAttributes[] = { + { XvSettable | XvGettable, 0, 65535, "XV_COLORKEY" }, + { XvSettable | XvGettable, 0, 1, "XV_AUTOPAINT_COLORKEY" }, +}; + +static XF86ImageRec Images[] = +{ + XVIMAGE_YV12, + XVIMAGE_I420, + XVIMAGE_UYVY, + XVIMAGE_YUY2, + XVIMAGE_YUMB, + XVIMAGE_STE0, +}; + +static int +U8500SetupPrivate(ScreenPtr screen, U8500PortPrivPtr pPriv) +{ + pPriv->hwmem_buffer_initialized = FALSE; + pPriv->color_key = -1; + pPriv->autopaint_colorkey = 0; + pPriv->pDraw = NULL; + pPriv->hwmem_size = 0; + pPriv->buffer_handle = -1; + pPriv->vaddr = 0; + pPriv->hwmem_fd = (MALIPTR(xf86Screens[screen->myNum]))->hwmem_fd; + + REGION_INIT(screen, &pPriv->clip, NullBox, 0); + + pPriv->blt_handle = blt_open(); + if(pPriv->blt_handle < 0) { + return -1; + } + + if(pPriv->hwmem_fd < 0) { + blt_close(pPriv->blt_handle); + return -1; + } + + return 0; +} + +static void free_hwmem(U8500PortPrivPtr pPriv) +{ + munmap(pPriv->vaddr,pPriv->hwmem_size); + (void)ioctl(pPriv->hwmem_fd, HWMEM_RELEASE_IOC, pPriv->buffer_handle); + pPriv->vaddr = 0; +} + +static int alloc_hwmem(U8500PortPrivPtr pPriv, int size) +{ + struct hwmem_alloc_request allocReq; + allocReq.size = size; + allocReq.flags = HWMEM_ALLOC_HINT_CACHED; + allocReq.default_access = HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE | HWMEM_ACCESS_IMPORT; + allocReq.mem_type = HWMEM_MEM_CONTIGUOUS_SYS; + pPriv->buffer_handle = ioctl(pPriv->hwmem_fd, HWMEM_ALLOC_IOC, &allocReq); + pPriv->hwmem_size = allocReq.size; + + if (pPriv->buffer_handle < 0) { + ErrorF("Failed to allocate buffer, error code: %d\n", pPriv->buffer_handle); + return -1; + } + + pPriv->vaddr = mmap(NULL, pPriv->hwmem_size, PROT_READ | PROT_WRITE , MAP_SHARED, pPriv->hwmem_fd, pPriv->buffer_handle); + if (pPriv->vaddr == MAP_FAILED) { + ErrorF("Failed to mmap buffer %d\n", pPriv->buffer_handle); + (void)ioctl(pPriv->hwmem_fd, HWMEM_RELEASE_IOC, pPriv->buffer_handle); + return -1; + } + + return 0; +} + +static int getColorFormat(int bitsPerPixel) +{ + switch (bitsPerPixel) { + case 15: + return BLT_FMT_16_BIT_ARGB1555; + case 16: + return BLT_FMT_16_BIT_RGB565; + case 24: + return BLT_FMT_24_BIT_RGB888; + case 32: + return BLT_FMT_32_BIT_ARGB8888; + default: + ErrorF("Undefined bit depth = %d\n", bitsPerPixel); + break; + } + return 0; +} + +void +U8500overlayFreeAdaptor(MaliPtr fbdev, XF86VideoAdaptorPtr adapt) +{ + U8500PortPrivPtr pPriv; + int i; + + if (adapt && adapt->pPortPrivates) { + for (i = 0; i < adapt->nPorts; i++) { + pPriv = adapt->pPortPrivates[i].ptr; + if (pPriv) { + blt_close(pPriv->blt_handle); + free_hwmem(pPriv); + pPriv->hwmem_fd = 0; + } + free(pPriv); + } + free(adapt->pPortPrivates); + } + free(adapt); +} + +static int +U8500overlayPutImage(ScrnInfoPtr screen, short src_x, short src_y, + short dst_x, short dst_y, short src_w, short src_h, + short dst_w, short dst_h, int id, unsigned char *buf, + short width, short height, Bool sync, + RegionPtr clip_boxes, pointer data, + DrawablePtr drawable) +{ + U8500PortPrivPtr pPriv = data; + st_yuvmb_frame_desc* Data = (st_yuvmb_frame_desc *) buf; + struct blt_req bltreq = {0}; + PixmapPtr pPixmap; + MaliPtr fPtr; + int copy_size = 0; + + ENTER(); + + pPriv->pDraw = drawable; + if (dst_w > VIDEO_IMAGE_MAX_WIDTH) dst_w = VIDEO_IMAGE_MAX_WIDTH; + if (dst_h > VIDEO_IMAGE_MAX_HEIGHT) dst_h = VIDEO_IMAGE_MAX_HEIGHT; + + /* in case of full screen mode x and y should be 0 */ + if ((dst_w == VIDEO_IMAGE_MAX_WIDTH) && + (dst_h == VIDEO_IMAGE_MAX_HEIGHT)) { + if (dst_x || dst_y) { + dst_x = dst_y = 0; + } + } + + if (pPriv->pDraw->type == DRAWABLE_PIXMAP) { + pPixmap = (PixmapPtr)pPriv->pDraw; + } + else { + pPixmap = pPriv->pDraw->pScreen->GetWindowPixmap((WindowPtr) pPriv->pDraw); + } + + PrivPixmap *privPixmap = (PrivPixmap *)exaGetPixmapDriverPrivate(pPixmap); + + /*Check if resize, cropping and clipping settings changed.*/ + + if ((pPriv->src_x != src_x) || (pPriv->src_y != src_y) + || (pPriv->src_w != src_w) + || (pPriv->src_h != src_h) + || (pPriv->dst_x != dst_x) + || (pPriv->dst_y != dst_y) + || (pPriv->dst_w != dst_w) + || (pPriv->dst_h != dst_h)) { + + if(((pPriv->color_format != FOURCC_YUMB) && (pPriv->color_format != FOURCC_STE0)) + && pPriv->hwmem_buffer_initialized) { + free_hwmem(pPriv); + pPriv->hwmem_buffer_initialized = FALSE; + } + } + + /*Populate pPriv with information about the current yuv frame.*/ + pPriv->src_x = src_x; + pPriv->src_y = src_y; + pPriv->src_w = src_w; + pPriv->src_h = src_h; + pPriv->dst_x = dst_x; + pPriv->dst_y = dst_y; + pPriv->dst_w = dst_w; + pPriv->dst_h = dst_h; + pPriv->img_width = width; + pPriv->img_height = height; + pPriv->color_format = id; + + /*Set the source format*/ + bltreq.src_img.fmt = BLT_FMT_YUV420_PACKED_PLANAR; + switch (pPriv->color_format) { + case FOURCC_YV12: /* GUID_YV12_PLANAR */ + case FOURCC_I420: /* GUID_I420_PLANAR */ + bltreq.src_img.fmt = BLT_FMT_YUV420_PACKED_PLANAR; + copy_size = width*height*3/2; + break; + case FOURCC_UYVY: + bltreq.src_img.fmt = BLT_FMT_CB_Y_CR_Y; + copy_size = width*height*2; + break; + case FOURCC_YUY2: + bltreq.src_img.fmt = BLT_FMT_Y_CB_Y_CR; + copy_size = width*height*2; + break; + case FOURCC_YUMB: + bltreq.src_img.fmt = BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE; + /* supports zero-copy */ + break; + case FOURCC_STE0: + bltreq.src_img.fmt = BLT_FMT_CB_Y_CR_Y; + /* supports zero-copy */ + break; + default: + ErrorF("Unknown format = %x\n", pPriv->color_format); + break; + } + + /*Setup hwmem buffer*/ + if (((pPriv->color_format != FOURCC_YUMB) && (pPriv->color_format != FOURCC_STE0)) && !pPriv->hwmem_buffer_initialized) { + if(alloc_hwmem(pPriv, copy_size) < 0) + return 0; + + pPriv->hwmem_buffer_initialized = TRUE; + } + + bltreq.size = sizeof(struct blt_req); + bltreq.transform = BLT_TRANSFORM_NONE; + + bltreq.src_img.buf.type = BLT_PTR_PHYSICAL; + bltreq.src_img.buf.offset = Data->physicaladdress; + bltreq.src_img.buf.bits = 0; + bltreq.src_img.width = ALIGN_VALUE(src_w); + bltreq.src_img.height = ALIGN_VALUE(src_h); + bltreq.src_img.pitch = 0; + + bltreq.src_mask.fmt = BLT_FMT_UNUSED; + bltreq.src_mask.buf.type = BLT_PTR_NONE; + + bltreq.dst_img.fmt = getColorFormat(pPriv->pDraw->bitsPerPixel); + bltreq.dst_img.pitch = pPixmap->devKind; + bltreq.dst_img.buf.type = BLT_PTR_HWMEM_BUF_NAME_OFFSET; + bltreq.dst_img.buf.hwmem_buf_name = privPixmap->mem_info->hwmem_global_name; + bltreq.dst_img.buf.offset = 0; + bltreq.dst_img.buf.bits = 0; + + bltreq.src_rect.x = src_x; + bltreq.src_rect.y = src_y; + bltreq.src_rect.width = src_w; + bltreq.src_rect.height = src_h; + + bltreq.dst_rect.x = dst_x; + bltreq.dst_rect.y = dst_y; + bltreq.dst_rect.width = dst_w; + bltreq.dst_rect.height = dst_h; + + if(privPixmap->isFrameBuffer) + { + bltreq.dst_img.width = screen->pScreen->width; + bltreq.dst_img.height = screen->pScreen->height; + + bltreq.dst_clip_rect.x = 0; + bltreq.dst_clip_rect.y = 0; + bltreq.dst_clip_rect.width = screen->pScreen->width; + bltreq.dst_clip_rect.height = screen->pScreen->height; + } + else + { + // Compositioning window manager case. + bltreq.dst_rect.x = 0; + bltreq.dst_rect.y = 0; + + bltreq.dst_img.width = pPriv->pDraw->width; + bltreq.dst_img.height = pPriv->pDraw->height; + + bltreq.dst_clip_rect.x = 0; + bltreq.dst_clip_rect.y = 0; + bltreq.dst_clip_rect.width = dst_w; + bltreq.dst_clip_rect.height = dst_h; + } + + bltreq.global_alpha = 255; + bltreq.prio = 4; + bltreq.flags = BLT_FLAG_ASYNCH | BLT_FLAG_DESTINATION_CLIP; + + if(pPriv->color_format != FOURCC_YUMB && pPriv->color_format != FOURCC_STE0) + { + bltreq.src_img.buf.type = BLT_PTR_HWMEM_BUF_NAME_OFFSET; + bltreq.src_img.buf.hwmem_buf_name = ioctl(pPriv->hwmem_fd, HWMEM_EXPORT_IOC, pPriv->buffer_handle); + bltreq.src_img.buf.offset = 0; + memcpy((void *) pPriv->vaddr, (void *) buf, copy_size); + } + + int status = blt_request(pPriv->blt_handle, &bltreq); + if(status < 0) + { + ErrorF("Blit request failed: %d\n", status); + return 0; + } + + (void)blt_synch(pPriv->blt_handle, status); + + fPtr = MALIPTR(screen); + if (privPixmap->isFrameBuffer) { + fPtr->fb_lcd_var.yoffset = 0; + fPtr->fb_lcd_var.activate |= FB_ACTIVATE_FORCE; + if ( ioctl( fPtr->fb_lcd_fd, FBIOPUT_VSCREENINFO, &fPtr->fb_lcd_var ) < 0 ) + { + xf86DrvMsg(screen->scrnIndex, X_WARNING, "[%s:%d] failed in FBIOPUT_VSCREENINFO (offset: %i)\n", __FUNCTION__, __LINE__, fPtr->fb_lcd_var.yoffset ); + } + } + + DamageDamageRegion(drawable, clip_boxes); + + LEAVE(); + return Success; +} + +void +U8500StopVideo(ScrnInfoPtr screen, pointer data, Bool exit) +{ + ENTER(); + LEAVE(); +} + +static int +U8500overlayGetPortAttribute(ScrnInfoPtr screen, Atom attribute, + INT32 * value, pointer data) +{ + ENTER(); + LEAVE(); + return Success; +} + +static int +U8500overlaySetPortAttribute(ScrnInfoPtr screen, Atom attribute, + INT32 value, pointer data) +{ + ENTER(); + LEAVE(); + return Success; +} + +static void +U8500QueryBestSize(ScrnInfoPtr screen, Bool motion, + short vid_w, short vid_h, short dst_w, + short dst_h, unsigned int *p_w, + unsigned int *p_h, pointer data) +{ + *p_w = ALIGN_VALUE(dst_w); + *p_h = ALIGN_VALUE(dst_h); +} + +static int +U8500QueryImageAttributes(ScrnInfoPtr screen, int id, unsigned short *w, + unsigned short *h, int *pitches, int *offsets) +{ + int size = 0, tmp = 0; + + ENTER(); + + DebugF("%s: id:%d, w:%#x, h:%#x, pitches:%#x, offsets:%#x\n", + __func__, id, w, h, pitches, offsets); + + /* Check if width and height are crossing maximum supported + * sizes and roll back if required */ + if (w && (*w > VIDEO_IMAGE_MAX_WIDTH)) + *w = VIDEO_IMAGE_MAX_WIDTH; + if (h && (*h > VIDEO_IMAGE_MAX_HEIGHT)) + *h = VIDEO_IMAGE_MAX_HEIGHT; + + if (w) + *w = ALIGN_VALUE(*w); + if (h) + *h = ALIGN_VALUE(*h); + + if (offsets) offsets[0] = 0; + + DebugF("%s: id:%x, w:%d, h:%d\n", __func__, id, *w, *h); + + switch (id) { + case FOURCC_YUMB: + size = (*w * *h * 3)/2; + break; + case FOURCC_STE0: + size = *w * *h * 2; + break; + case FOURCC_YV12: + case FOURCC_I420: + if (h) { + *h = (*h + 1) & ~1; + } + size = (*w + 3) & ~3; + if (pitches) pitches[0] = size; + size *= *h; + if (offsets) offsets[1] = size; + tmp = ((*w >> 1) + 3) & ~3; + if (pitches) pitches[1] = pitches[2] = tmp; + tmp *= (*h >> 1); + size += tmp; + if (offsets) offsets[2] = size; + size += tmp; + break; + case FOURCC_UYVY: + case FOURCC_YUY2: + default: + size = *w << 1; + if (pitches) pitches[0] = size; + size *= *h; + break; + } + + LEAVE(); + return size; +} + +XF86VideoAdaptorPtr +U8500overlaySetupImageVideo(ScreenPtr screen) +{ + ScrnInfoPtr xf86screen = xf86Screens[screen->myNum]; + MaliPtr fbdev = xf86screen->driverPrivate; + XF86VideoAdaptorPtr adapt; + U8500PortPrivPtr pPriv; + int i; + + xf86DrvMsg(xf86screen, X_INFO, "U8500overlaySetupImageVideo\n" ); + + if (!(adapt = calloc(1, sizeof(XF86VideoAdaptorRec)))) + return NULL; + + adapt->type = XvWindowMask | XvInputMask | XvImageMask; + adapt->flags = VIDEO_CLIP_TO_VIEWPORT | VIDEO_OVERLAID_IMAGES; + adapt->name = "B2R2 Overlay Video Accelerator"; + adapt->nEncodings = 1; + adapt->pEncodings = &DummyEncoding; + + adapt->nFormats = ARRAY_SIZE(Formats); + adapt->pFormats = Formats; + + adapt->nAttributes = ARRAY_SIZE(OverlayAttributes); + adapt->pAttributes = OverlayAttributes; + + adapt->nImages = ARRAY_SIZE(Images); + adapt->pImages = Images; + + adapt->pPortPrivates = (DevUnion *) + calloc(NUM_OVERLAY_PORTS, sizeof(DevUnion)); + if (!adapt->pPortPrivates) + goto unwind; + + for (i = 0; i < NUM_OVERLAY_PORTS; i++) { + pPriv = calloc(1, sizeof(U8500PortPrivRec)); + if (!pPriv) + goto unwind; + + adapt->pPortPrivates[i].ptr = (pointer) pPriv; + adapt->nPorts++; + + if (U8500SetupPrivate(screen, pPriv) < 0) + goto unwind; + } + + adapt->PutVideo = NULL; + adapt->PutStill = NULL; + adapt->GetVideo = NULL; + adapt->GetStill = NULL; + adapt->PutImage = U8500overlayPutImage; + adapt->ReputImage = NULL; + adapt->StopVideo = U8500StopVideo; + adapt->GetPortAttribute = U8500overlayGetPortAttribute; + adapt->SetPortAttribute = U8500overlaySetPortAttribute; + adapt->QueryBestSize = U8500QueryBestSize; + adapt->QueryImageAttributes = U8500QueryImageAttributes; + adapt->ClipNotify = NULL; + + fbdev->overlay_adaptor = adapt; + + return adapt; + +unwind: + U8500overlayFreeAdaptor(fbdev, adapt); + + return NULL; +} |