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                if (stride == linesize && chunk_stride == stride) {
 140                        remain = linesize * height;
 141                        page = 0;
 142                        i_data = (void *)data;
 143
 144                        while (remain > 0) {
 145                                ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT);
 146
 147                                if (page == 0) {
 148                                        chunk = ptr;
 149                                        k_data = chunk->data;
 150                                        size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data);
 151                                } else {
 152                                        k_data = ptr;
 153                                        size = PAGE_SIZE;
 154                                }
 155                                size = min(size, remain);
 156
 157                                memcpy(k_data, i_data, size);
 158
 159                                qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 160                                i_data += size;
 161                                remain -= size;
 162                                page++;
 163                        }
 164                } else {
 165                        unsigned page_base, page_offset, out_offset;
 166                        for (i = 0 ; i < height ; ++i) {
 167                                i_data = (void *)data + i * stride;
 168                                remain = linesize;
 169                                out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride;
 170
 171                                while (remain > 0) {
 172                                        page_base = out_offset & PAGE_MASK;
 173                                        page_offset = offset_in_page(out_offset);
 174                                        size = min((int)(PAGE_SIZE - page_offset), remain);
 175
 176                                        ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
 177                                        k_data = ptr + page_offset;
 178                                        memcpy(k_data, i_data, size);
 179                                        qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 180                                        remain -= size;
 181                                        i_data += size;
 182                                        out_offset += size;
 183                                }
 184                        }
 185                }
 186        }
 187        qxl_bo_kunmap(chunk_bo);
 188
 189        image_bo = dimage->bo;
 190        ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
 191        image = ptr;
 192
 193        image->descriptor.id = 0;
 194        image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
 195
 196        image->descriptor.flags = 0;
 197        image->descriptor.width = width;
 198        image->descriptor.height = height;
 199
 200        switch (depth) {
 201        case 1:
 202                /* TODO: BE? check by arch? */
 203                image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE;
 204                break;
 205        case 24:
 206                image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT;
 207                break;
 208        case 32:
 209                image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT;
 210                break;
 211        default:
 212                DRM_ERROR("unsupported image bit depth\n");
 213                return -EINVAL; /* TODO: cleanup */
 214        }
 215        image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
 216        image->u.bitmap.x = width;
 217        image->u.bitmap.y = height;
 218        image->u.bitmap.stride = chunk_stride;
 219        image->u.bitmap.palette = 0;
 220        image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
 221
 222        qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
 223
 224        return 0;
 225}
 226
 227int qxl_image_init(struct qxl_device *qdev,
 228                     struct qxl_release *release,
 229                     struct qxl_drm_image *dimage,
 230                     const uint8_t *data,
 231                     int x, int y, int width, int height,
 232                     int depth, int stride)
 233{
 234        data += y * stride + x * (depth / 8);
 235        return qxl_image_init_helper(qdev, release, dimage, data,
 236                                       width, height, depth, 0, stride);
 237}
 238