123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- /*
- * Copyright 2013 Red Hat 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie
- * Alon Levy
- */
- #include <linux/gfp.h>
- #include <linux/slab.h>
- #include "qxl_drv.h"
- #include "qxl_object.h"
- static int
- qxl_allocate_chunk(struct qxl_device *qdev,
- struct qxl_release *release,
- struct qxl_drm_image *image,
- unsigned int chunk_size)
- {
- struct qxl_drm_chunk *chunk;
- int ret;
- chunk = kmalloc(sizeof(struct qxl_drm_chunk), GFP_KERNEL);
- if (!chunk)
- return -ENOMEM;
- ret = qxl_alloc_bo_reserved(qdev, release, chunk_size, &chunk->bo);
- if (ret) {
- kfree(chunk);
- return ret;
- }
- list_add_tail(&chunk->head, &image->chunk_list);
- return 0;
- }
- int
- qxl_image_alloc_objects(struct qxl_device *qdev,
- struct qxl_release *release,
- struct qxl_drm_image **image_ptr,
- int height, int stride)
- {
- struct qxl_drm_image *image;
- int ret;
- image = kmalloc(sizeof(struct qxl_drm_image), GFP_KERNEL);
- if (!image)
- return -ENOMEM;
- INIT_LIST_HEAD(&image->chunk_list);
- ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_image), &image->bo);
- if (ret) {
- kfree(image);
- return ret;
- }
- ret = qxl_allocate_chunk(qdev, release, image, sizeof(struct qxl_data_chunk) + stride * height);
- if (ret) {
- qxl_bo_unref(&image->bo);
- kfree(image);
- return ret;
- }
- *image_ptr = image;
- return 0;
- }
- void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage)
- {
- struct qxl_drm_chunk *chunk, *tmp;
- list_for_each_entry_safe(chunk, tmp, &dimage->chunk_list, head) {
- qxl_bo_unref(&chunk->bo);
- kfree(chunk);
- }
- qxl_bo_unref(&dimage->bo);
- kfree(dimage);
- }
- static int
- qxl_image_init_helper(struct qxl_device *qdev,
- struct qxl_release *release,
- struct qxl_drm_image *dimage,
- const uint8_t *data,
- int width, int height,
- int depth, unsigned int hash,
- int stride)
- {
- struct qxl_drm_chunk *drv_chunk;
- struct qxl_image *image;
- struct qxl_data_chunk *chunk;
- int i;
- int chunk_stride;
- int linesize = width * depth / 8;
- struct qxl_bo *chunk_bo, *image_bo;
- void *ptr;
- /* Chunk */
- /* FIXME: Check integer overflow */
- /* TODO: variable number of chunks */
- drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head);
- chunk_bo = drv_chunk->bo;
- chunk_stride = stride; /* TODO: should use linesize, but it renders
- wrong (check the bitmaps are sent correctly
- first) */
- ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
- chunk = ptr;
- chunk->data_size = height * chunk_stride;
- chunk->prev_chunk = 0;
- chunk->next_chunk = 0;
- qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
- {
- void *k_data, *i_data;
- int remain;
- int page;
- int size;
- if (stride == linesize && chunk_stride == stride) {
- remain = linesize * height;
- page = 0;
- i_data = (void *)data;
- while (remain > 0) {
- ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT);
- if (page == 0) {
- chunk = ptr;
- k_data = chunk->data;
- size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data);
- } else {
- k_data = ptr;
- size = PAGE_SIZE;
- }
- size = min(size, remain);
- memcpy(k_data, i_data, size);
- qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
- i_data += size;
- remain -= size;
- page++;
- }
- } else {
- unsigned page_base, page_offset, out_offset;
- for (i = 0 ; i < height ; ++i) {
- i_data = (void *)data + i * stride;
- remain = linesize;
- out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride;
- while (remain > 0) {
- page_base = out_offset & PAGE_MASK;
- page_offset = offset_in_page(out_offset);
- size = min((int)(PAGE_SIZE - page_offset), remain);
- ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
- k_data = ptr + page_offset;
- memcpy(k_data, i_data, size);
- qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
- remain -= size;
- i_data += size;
- out_offset += size;
- }
- }
- }
- }
- qxl_bo_kunmap(chunk_bo);
- image_bo = dimage->bo;
- ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
- image = ptr;
- image->descriptor.id = 0;
- image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
- image->descriptor.flags = 0;
- image->descriptor.width = width;
- image->descriptor.height = height;
- switch (depth) {
- case 1:
- /* TODO: BE? check by arch? */
- image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE;
- break;
- case 24:
- image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT;
- break;
- case 32:
- image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT;
- break;
- default:
- DRM_ERROR("unsupported image bit depth\n");
- return -EINVAL; /* TODO: cleanup */
- }
- image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
- image->u.bitmap.x = width;
- image->u.bitmap.y = height;
- image->u.bitmap.stride = chunk_stride;
- image->u.bitmap.palette = 0;
- image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
- qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
- return 0;
- }
- int qxl_image_init(struct qxl_device *qdev,
- struct qxl_release *release,
- struct qxl_drm_image *dimage,
- const uint8_t *data,
- int x, int y, int width, int height,
- int depth, int stride)
- {
- data += y * stride + x * (depth / 8);
- return qxl_image_init_helper(qdev, release, dimage, data,
- width, height, depth, 0, stride);
- }
|