summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/qxl/qxl_image.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/qxl/qxl_image.c')
-rw-r--r--drivers/gpu/drm/qxl/qxl_image.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/drivers/gpu/drm/qxl/qxl_image.c b/drivers/gpu/drm/qxl/qxl_image.c
new file mode 100644
index 00000000000..258cf8a8636
--- /dev/null
+++ b/drivers/gpu/drm/qxl/qxl_image.c
@@ -0,0 +1,313 @@
+#include <linux/gfp.h>
+#include <linux/slab.h>
+
+#include "qxl_drv.h"
+#include "lookup3.h"
+
+struct image_info
+{
+ struct qxl_image *image;
+ int ref_count;
+ struct image_info *next;
+};
+
+#define HASH_SIZE 4096
+static struct image_info *image_table[HASH_SIZE];
+
+static unsigned int
+hash_and_copy(const uint8_t *src, int src_stride,
+ uint8_t *dest, int dest_stride,
+ int width, int height)
+{
+ int i, j;
+ unsigned int hash = 0;
+
+ for (i = 0; i < height; ++i) {
+ const uint8_t *src_line = src + i * src_stride;
+ uint8_t *dest_line = dest + i * dest_stride;
+
+ for (j = 0; j < width; ++j) {
+ uint32_t *s = (uint32_t *)src_line;
+ uint32_t *d = (uint32_t *)dest_line;
+
+ if (dest)
+ d[j] = s[j];
+ }
+
+ hash = hashlittle(src_line, width * sizeof(uint32_t), hash);
+ }
+
+ return hash;
+}
+
+static struct image_info *
+lookup_image_info(unsigned int hash,
+ int width,
+ int height)
+{
+ struct image_info *info = image_table[hash % HASH_SIZE];
+
+ while (info) {
+ struct qxl_image *image = info->image;
+
+ if (image->descriptor.id == hash &&
+ image->descriptor.width == width &&
+ image->descriptor.height == height) {
+ return info;
+ }
+
+ info = info->next;
+ }
+
+#if 0
+ ErrorF ("lookup of %u failed\n", hash);
+#endif
+
+ return NULL;
+}
+
+static struct image_info *
+insert_image_info(unsigned int hash)
+{
+ struct image_info *info = kmalloc(sizeof(struct image_info), GFP_KERNEL);
+
+ if (!info)
+ return NULL;
+
+ info->next = image_table[hash % HASH_SIZE];
+ image_table[hash % HASH_SIZE] = info;
+
+ return info;
+}
+
+#if 0
+static void
+remove_image_info(struct image_info *info)
+{
+ struct image_info **location = &image_table[info->image->descriptor.id % HASH_SIZE];
+
+ while (*location && (*location) != info)
+ location = &((*location)->next);
+
+ if (*location)
+ *location = info->next;
+
+ kfree(info);
+}
+#endif
+
+#if 0
+void debug_fill(uint8_t *data, int width, int height)
+{
+ int xx, yy;
+ uint32_t *p;
+ static int count = 0;
+
+ for (yy = 0, p = data ; yy < height; ++yy) {
+ for (xx = 0 ; xx < width ; ++xx) {
+ (*p++) = (((count + yy) & 0xff) << 24) |
+ (((count + yy) & 0xff) << 16) |
+ (((count + yy) & 0xff) << 8) |
+ ((count + yy) & 0xff);
+ }
+ }
+ ++count;
+}
+#endif
+
+
+static struct qxl_image *
+qxl_image_create_helper(struct qxl_device *qdev,
+ struct drm_qxl_release *release,
+ struct qxl_bo **image_bo,
+ const uint8_t *data,
+ int width, int height,
+ int depth, unsigned int hash,
+ int stride)
+{
+ struct qxl_image *image;
+ struct qxl_data_chunk *chunk;
+ struct image_info *info;
+ int i;
+ int chunk_stride;
+ int linesize = width * depth / 8;
+ struct qxl_bo *chunk_bo;
+
+#if 0
+ ErrorF ("Must create new image of size %d %d\n", width, height);
+#endif
+ /* Chunk */
+ /* FIXME: Check integer overflow */
+ /* TODO: variable number of chunks */
+ chunk_stride = stride; // TODO: should use linesize, but it renders wrong (check the bitmaps are sent correctly first)
+ chunk = qxl_allocnf(qdev, sizeof(*chunk) + height * chunk_stride, release);
+ chunk_bo = release->bos[release->bo_count - 1];
+
+ chunk->data_size = height * chunk_stride;
+ chunk->prev_chunk = 0;
+ chunk->next_chunk = 0;
+
+ qxl_io_log(qdev, "%s: stride %d, chunk_s %d, linesize %d; "
+ "chunk %p, chunk->data %p, data %p\n",
+ __func__, stride, chunk_stride, linesize, chunk,
+ chunk ? chunk->data : NULL, data);
+ // TODO: put back hashing
+ if (stride == linesize && chunk_stride == stride)
+ memcpy(chunk->data, data, linesize * height);
+ else
+ for (i = 0 ; i < height ; ++i) {
+ memcpy(chunk->data + i*chunk_stride, data + i*stride, linesize);
+ }
+ //memcpy_fromio(chunk->data, data, height * stride);
+ //hash_and_copy(data, stride,
+ // chunk->data, stride,
+ // width, height);
+
+ /* Image */
+ image = qxl_allocnf(qdev, sizeof(*image), release);
+ *image_bo = release->bos[release->bo_count - 1];
+
+ 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:
+ image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE; // TODO: BE? check by arch?
+ 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 NULL; // 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_fb_physical_address(qdev, chunk_bo);
+
+ /* Add to hash table */
+ if (!hash) {
+ QXL_INFO(qdev, "%s: id %d has size %d %d\n", __FUNCTION__,
+ image->descriptor.id, width, height);
+
+ return image;
+ }
+ if((info = insert_image_info(hash))) {
+ info->image = image;
+ info->ref_count = 1;
+
+ image->descriptor.id = hash;
+ image->descriptor.flags = QXL_IMAGE_CACHE;
+
+#if 0
+ ErrorF("added with hash %u\n", hash);
+#endif
+ }
+ QXL_INFO(qdev, "%s: id %d has size %d %d\n", __FUNCTION__,
+ image->descriptor.id, width, height);
+ return image;
+}
+
+struct qxl_image *qxl_image_create(struct qxl_device *qdev,
+ struct drm_qxl_release *release,
+ struct qxl_bo **image_bo,
+ const uint8_t *data,
+ int x, int y, int width, int height,
+ int depth, int stride)
+{
+ unsigned int hash;
+ struct image_info *info;
+
+ data += y * stride + x * (depth / 8);
+ // TODO: TEMP
+ return qxl_image_create_helper(qdev, release, image_bo, data,
+ width, height, depth, 0, stride);
+
+ hash = hash_and_copy(data, stride, NULL, -1, width, height);
+
+ info = lookup_image_info(hash, width, height);
+ if (info) {
+ int i, j;
+ qxl_io_log(qdev, "%s: image found in cache\n", __func__);
+
+#if 0
+ ErrorF ("reusing image %p with hash %u (%d x %d)\n", info->image, hash, width, height);
+#endif
+
+ info->ref_count++;
+
+ for (i = 0; i < height; ++i) {
+ struct qxl_data_chunk *chunk;
+ const uint8_t *src_line = data + i * stride;
+ uint32_t *dest_line;
+
+ chunk = qxl_fb_virtual_address(qdev, info->image->u.bitmap.data);
+
+ dest_line = (uint32_t *)chunk->data + width * i;
+
+ for (j = 0; j < width; ++j) {
+ uint32_t *s = (uint32_t *)src_line;
+ uint32_t *d = (uint32_t *)dest_line;
+
+ if (d[j] != s[j])
+ {
+#if 0
+ ErrorF ("bad collision at (%d, %d)! %d != %d\n", j, i, s[j], d[j]);
+#endif
+ goto out;
+ }
+ }
+ }
+out:
+ return info->image;
+ } else {
+ return qxl_image_create_helper(qdev, release, image_bo, data,
+ width, height, depth, hash, stride);
+ }
+}
+
+void qxl_image_destroy(struct qxl_device *qdev,
+ struct qxl_image *image)
+{
+#if 0
+ struct qxl_data_chunk *chunk;
+ struct image_info *info;
+
+ chunk = qxl_virtual_address(qdev,(void *)image->u.bitmap.data);
+
+ info = lookup_image_info(image->descriptor.id,
+ image->descriptor.width,
+ image->descriptor.height);
+
+ if(info && info->image == image) {
+ --info->ref_count;
+
+ if(info->ref_count != 0)
+ return;
+
+#if 0
+ ErrorF("removed %p from hash table\n", info->image);
+#endif
+
+ remove_image_info(info);
+ }
+
+ qxl_free(qdev->mem, chunk);
+ qxl_free(qdev->mem, image);
+#endif
+}
+
+void qxl_drop_image_cache(struct qxl_device *qdev)
+{
+ memset(image_table, 0, HASH_SIZE * sizeof(struct image_info *));
+}