linux/drivers/gpu/drm/qxl/qxl_image.c
<<
>>
Prefs
   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
  32static int
  33qxl_allocate_chunk(struct qxl_device *qdev,
  34                   struct qxl_release *release,
  35                   struct qxl_drm_image *image,
  36                   unsigned int chunk_size)
  37{
  38        struct qxl_drm_chunk *chunk;
  39        int ret;
  40
  41        chunk = kmalloc(sizeof(struct qxl_drm_chunk), GFP_KERNEL);
  42        if (!chunk)
  43                return -ENOMEM;
  44
  45        ret = qxl_alloc_bo_reserved(qdev, release, chunk_size, &chunk->bo);
  46        if (ret) {
  47                kfree(chunk);
  48                return ret;
  49        }
  50
  51        list_add_tail(&chunk->head, &image->chunk_list);
  52        return 0;
  53}
  54
  55int
  56qxl_image_alloc_objects(struct qxl_device *qdev,
  57                        struct qxl_release *release,
  58                        struct qxl_drm_image **image_ptr,
  59                        int height, int stride)
  60{
  61        struct qxl_drm_image *image;
  62        int ret;
  63
  64        image = kmalloc(sizeof(struct qxl_drm_image), GFP_KERNEL);
  65        if (!image)
  66                return -ENOMEM;
  67
  68        INIT_LIST_HEAD(&image->chunk_list);
  69
  70        ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_image), &image->bo);
  71        if (ret) {
  72                kfree(image);
  73                return ret;
  74        }
  75
  76        ret = qxl_allocate_chunk(qdev, release, image, sizeof(struct qxl_data_chunk) + stride * height);
  77        if (ret) {
  78                qxl_bo_unref(&image->bo);
  79                kfree(image);
  80                return ret;
  81        }
  82        *image_ptr = image;
  83        return 0;
  84}
  85
  86void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage)
  87{
  88        struct qxl_drm_chunk *chunk, *tmp;
  89
  90        list_for_each_entry_safe(chunk, tmp, &dimage->chunk_list, head) {
  91                qxl_bo_unref(&chunk->bo);
  92                kfree(chunk);
  93        }
  94
  95        qxl_bo_unref(&dimage->bo);
  96        kfree(dimage);
  97}
  98
  99static int
 100qxl_image_init_helper(struct qxl_device *qdev,
 101                      struct qxl_release *release,
 102                      struct qxl_drm_image *dimage,
 103                      const uint8_t *data,
 104                      int width, int height,
 105                      int depth, unsigned int hash,
 106                      int stride)
 107{
 108        struct qxl_drm_chunk *drv_chunk;
 109        struct qxl_image *image;
 110        struct qxl_data_chunk *chunk;
 111        int i;
 112        int chunk_stride;
 113        int linesize = width * depth / 8;
 114        struct qxl_bo *chunk_bo, *image_bo;
 115        void *ptr;
 116        /* Chunk */
 117        /* FIXME: Check integer overflow */
 118        /* TODO: variable number of chunks */
 119
 120        drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head);
 121
 122        chunk_bo = drv_chunk->bo;
 123        chunk_stride = stride; /* TODO: should use linesize, but it renders
 124                                  wrong (check the bitmaps are sent correctly
 125                                  first) */
 126
 127        ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
 128        chunk = ptr;
 129        chunk->data_size = height * chunk_stride;
 130        chunk->prev_chunk = 0;
 131        chunk->next_chunk = 0;
 132        qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 133
 134        {
 135                void *k_data, *i_data;
 136                int remain;
 137                int page;
 138                int size;
 139
 140                if (stride == linesize && chunk_stride == stride) {
 141                        remain = linesize * height;
 142                        page = 0;
 143                        i_data = (void *)data;
 144
 145                        while (remain > 0) {
 146                                ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT);
 147
 148                                if (page == 0) {
 149                                        chunk = ptr;
 150                                        k_data = chunk->data;
 151                                        size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data);
 152                                } else {
 153                                        k_data = ptr;
 154                                        size = PAGE_SIZE;
 155                                }
 156                                size = min(size, remain);
 157
 158                                memcpy(k_data, i_data, size);
 159
 160                                qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 161                                i_data += size;
 162                                remain -= size;
 163                                page++;
 164                        }
 165                } else {
 166                        unsigned int page_base, page_offset, out_offset;
 167
 168                        for (i = 0 ; i < height ; ++i) {
 169                                i_data = (void *)data + i * stride;
 170                                remain = linesize;
 171                                out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride;
 172
 173                                while (remain > 0) {
 174                                        page_base = out_offset & PAGE_MASK;
 175                                        page_offset = offset_in_page(out_offset);
 176                                        size = min((int)(PAGE_SIZE - page_offset), remain);
 177
 178                                        ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
 179                                        k_data = ptr + page_offset;
 180                                        memcpy(k_data, i_data, size);
 181                                        qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 182                                        remain -= size;
 183                                        i_data += size;
 184                                        out_offset += size;
 185                                }
 186                        }
 187                }
 188        }
 189        qxl_bo_vunmap_locked(chunk_bo);
 190
 191        image_bo = dimage->bo;
 192        ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
 193        image = ptr;
 194
 195        image->descriptor.id = 0;
 196        image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
 197
 198        image->descriptor.flags = 0;
 199        image->descriptor.width = width;
 200        image->descriptor.height = height;
 201
 202        switch (depth) {
 203        case 1:
 204                /* TODO: BE? check by arch? */
 205                image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE;
 206                break;
 207        case 24:
 208                image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT;
 209                break;
 210        case 32:
 211                image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT;
 212                break;
 213        default:
 214                DRM_ERROR("unsupported image bit depth\n");
 215                qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
 216                return -EINVAL;
 217        }
 218        image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
 219        image->u.bitmap.x = width;
 220        image->u.bitmap.y = height;
 221        image->u.bitmap.stride = chunk_stride;
 222        image->u.bitmap.palette = 0;
 223        image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
 224
 225        qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
 226
 227        return 0;
 228}
 229
 230int qxl_image_init(struct qxl_device *qdev,
 231                     struct qxl_release *release,
 232                     struct qxl_drm_image *dimage,
 233                     const uint8_t *data,
 234                     int x, int y, int width, int height,
 235                     int depth, int stride)
 236{
 237        data += y * stride + x * (depth / 8);
 238        return qxl_image_init_helper(qdev, release, dimage, data,
 239                                       width, height, depth, 0, stride);
 240}
 241