/* * Mesa 3-D graphics library * Version: 7.9 * * Copyright (C) 2010 LunarG Inc. * * 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. * * Authors: * Chia-I Wu <olv@lunarg.com> */ #include <sys/mman.h> #include <sys/ioctl.h> #include <linux/fb.h> #include "pipe/p_compiler.h" #include "util/u_format.h" #include "util/u_math.h" #include "util/u_memory.h" #include "state_tracker/sw_winsys.h" #include "fbdev_sw_winsys.h" struct fbdev_sw_displaytarget { enum pipe_format format; unsigned width; unsigned height; unsigned stride; void *data; void *mapped; }; struct fbdev_sw_winsys { struct sw_winsys base; int fd; struct fb_fix_screeninfo finfo; unsigned rows; unsigned stride; }; static INLINE struct fbdev_sw_displaytarget * fbdev_sw_displaytarget(struct sw_displaytarget *dt) { return (struct fbdev_sw_displaytarget *) dt; } static INLINE struct fbdev_sw_winsys * fbdev_sw_winsys(struct sw_winsys *ws) { return (struct fbdev_sw_winsys *) ws; } static void fbdev_displaytarget_display(struct sw_winsys *ws, struct sw_displaytarget *dt, void *winsys_private) { struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws); struct fbdev_sw_displaytarget *src = fbdev_sw_displaytarget(dt); const struct fbdev_sw_drawable *dst = (const struct fbdev_sw_drawable *) winsys_private; unsigned height, row_offset, row_len, i; void *fbmem; /* FIXME format conversion */ if (dst->format != src->format) { assert(0); return; } height = dst->height; if (dst->y + dst->height > fbdev->rows) { /* nothing to copy */ if (dst->y >= fbdev->rows) return; height = fbdev->rows - dst->y; } row_offset = util_format_get_stride(dst->format, dst->x); row_len = util_format_get_stride(dst->format, dst->width); if (row_offset + row_len > fbdev->stride) { /* nothing to copy */ if (row_offset >= fbdev->stride) return; row_len = fbdev->stride - row_offset; } fbmem = mmap(0, fbdev->finfo.smem_len, PROT_WRITE, MAP_SHARED, fbdev->fd, 0); if (fbmem == MAP_FAILED) return; for (i = 0; i < height; i++) { char *from = (char *) src->data + src->stride * i; char *to = (char *) fbmem + fbdev->stride * (dst->y + i) + row_offset; memcpy(to, from, row_len); } munmap(fbmem, fbdev->finfo.smem_len); } static void fbdev_displaytarget_unmap(struct sw_winsys *ws, struct sw_displaytarget *dt) { struct fbdev_sw_displaytarget *fbdt = fbdev_sw_displaytarget(dt); fbdt->mapped = NULL; } static void * fbdev_displaytarget_map(struct sw_winsys *ws, struct sw_displaytarget *dt, unsigned flags) { struct fbdev_sw_displaytarget *fbdt = fbdev_sw_displaytarget(dt); fbdt->mapped = fbdt->data; return fbdt->mapped; } static void fbdev_displaytarget_destroy(struct sw_winsys *ws, struct sw_displaytarget *dt) { struct fbdev_sw_displaytarget *fbdt = fbdev_sw_displaytarget(dt); if (fbdt->data) align_free(fbdt->data); FREE(fbdt); } static struct sw_displaytarget * fbdev_displaytarget_create(struct sw_winsys *ws, unsigned tex_usage, enum pipe_format format, unsigned width, unsigned height, unsigned alignment, unsigned *stride) { struct fbdev_sw_displaytarget *fbdt; unsigned nblocksy, size, format_stride; fbdt = CALLOC_STRUCT(fbdev_sw_displaytarget); if (!fbdt) return NULL; fbdt->format = format; fbdt->width = width; fbdt->height = height; format_stride = util_format_get_stride(format, width); fbdt->stride = align(format_stride, alignment); nblocksy = util_format_get_nblocksy(format, height); size = fbdt->stride * nblocksy; fbdt->data = align_malloc(size, alignment); if (!fbdt->data) { FREE(fbdt); return NULL; } *stride = fbdt->stride; return (struct sw_displaytarget *) fbdt; } static boolean fbdev_is_displaytarget_format_supported(struct sw_winsys *ws, unsigned tex_usage, enum pipe_format format) { return TRUE; } static void fbdev_destroy(struct sw_winsys *ws) { struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws); FREE(fbdev); } struct sw_winsys * fbdev_create_sw_winsys(int fd) { struct fbdev_sw_winsys *fbdev; fbdev = CALLOC_STRUCT(fbdev_sw_winsys); if (!fbdev) return NULL; fbdev->fd = fd; if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->finfo)) { FREE(fbdev); return NULL; } fbdev->rows = fbdev->finfo.smem_len / fbdev->finfo.line_length; fbdev->stride = fbdev->finfo.line_length; fbdev->base.destroy = fbdev_destroy; fbdev->base.is_displaytarget_format_supported = fbdev_is_displaytarget_format_supported; fbdev->base.displaytarget_create = fbdev_displaytarget_create; fbdev->base.displaytarget_destroy = fbdev_displaytarget_destroy; fbdev->base.displaytarget_map = fbdev_displaytarget_map; fbdev->base.displaytarget_unmap = fbdev_displaytarget_unmap; fbdev->base.displaytarget_display = fbdev_displaytarget_display; return &fbdev->base; }