1 /* 2 * Copyright 2013 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Dave Airlie 23 * Alon Levy 24 */ 25 26 #include <linux/gfp.h> 27 #include <linux/slab.h> 28 29 #include "qxl_drv.h" 30 #include "qxl_object.h" 31 32 static int 33 qxl_image_create_helper(struct qxl_device *qdev, 34 struct qxl_release *release, 35 struct qxl_bo **image_bo, 36 const uint8_t *data, 37 int width, int height, 38 int depth, unsigned int hash, 39 int stride) 40 { 41 struct qxl_image *image; 42 struct qxl_data_chunk *chunk; 43 int i; 44 int chunk_stride; 45 int linesize = width * depth / 8; 46 struct qxl_bo *chunk_bo; 47 int ret; 48 void *ptr; 49 /* Chunk */ 50 /* FIXME: Check integer overflow */ 51 /* TODO: variable number of chunks */ 52 chunk_stride = stride; /* TODO: should use linesize, but it renders 53 wrong (check the bitmaps are sent correctly 54 first) */ 55 ret = qxl_alloc_bo_reserved(qdev, sizeof(*chunk) + height * chunk_stride, 56 &chunk_bo); 57 58 ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0); 59 chunk = ptr; 60 chunk->data_size = height * chunk_stride; 61 chunk->prev_chunk = 0; 62 chunk->next_chunk = 0; 63 qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); 64 65 { 66 void *k_data, *i_data; 67 int remain; 68 int page; 69 int size; 70 if (stride == linesize && chunk_stride == stride) { 71 remain = linesize * height; 72 page = 0; 73 i_data = (void *)data; 74 75 while (remain > 0) { 76 ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT); 77 78 if (page == 0) { 79 chunk = ptr; 80 k_data = chunk->data; 81 size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data); 82 } else { 83 k_data = ptr; 84 size = PAGE_SIZE; 85 } 86 size = min(size, remain); 87 88 memcpy(k_data, i_data, size); 89 90 qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); 91 i_data += size; 92 remain -= size; 93 page++; 94 } 95 } else { 96 unsigned page_base, page_offset, out_offset; 97 for (i = 0 ; i < height ; ++i) { 98 i_data = (void *)data + i * stride; 99 remain = linesize; 100 out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride; 101 102 while (remain > 0) { 103 page_base = out_offset & PAGE_MASK; 104 page_offset = offset_in_page(out_offset); 105 106 size = min((int)(PAGE_SIZE - page_offset), remain); 107 108 ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base); 109 k_data = ptr + page_offset; 110 memcpy(k_data, i_data, size); 111 qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr); 112 remain -= size; 113 i_data += size; 114 out_offset += size; 115 } 116 } 117 } 118 } 119 120 121 qxl_bo_kunmap(chunk_bo); 122 123 /* Image */ 124 ret = qxl_alloc_bo_reserved(qdev, sizeof(*image), image_bo); 125 126 ptr = qxl_bo_kmap_atomic_page(qdev, *image_bo, 0); 127 image = ptr; 128 129 image->descriptor.id = 0; 130 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; 131 132 image->descriptor.flags = 0; 133 image->descriptor.width = width; 134 image->descriptor.height = height; 135 136 switch (depth) { 137 case 1: 138 /* TODO: BE? check by arch? */ 139 image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE; 140 break; 141 case 24: 142 image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT; 143 break; 144 case 32: 145 image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT; 146 break; 147 default: 148 DRM_ERROR("unsupported image bit depth\n"); 149 return -EINVAL; /* TODO: cleanup */ 150 } 151 image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN; 152 image->u.bitmap.x = width; 153 image->u.bitmap.y = height; 154 image->u.bitmap.stride = chunk_stride; 155 image->u.bitmap.palette = 0; 156 image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0); 157 qxl_release_add_res(qdev, release, chunk_bo); 158 qxl_bo_unreserve(chunk_bo); 159 qxl_bo_unref(&chunk_bo); 160 161 qxl_bo_kunmap_atomic_page(qdev, *image_bo, ptr); 162 163 return 0; 164 } 165 166 int qxl_image_create(struct qxl_device *qdev, 167 struct qxl_release *release, 168 struct qxl_bo **image_bo, 169 const uint8_t *data, 170 int x, int y, int width, int height, 171 int depth, int stride) 172 { 173 data += y * stride + x * (depth / 8); 174 return qxl_image_create_helper(qdev, release, image_bo, data, 175 width, height, depth, 0, stride); 176 } 177