linux/drivers/gpu/drm/udl/udl_fb.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Red Hat
   3 *
   4 * based in parts on udlfb.c:
   5 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
   6 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
   7 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License v2. See the file COPYING in the main directory of this archive for
  11 * more details.
  12 */
  13#include <linux/module.h>
  14#include <linux/slab.h>
  15#include <linux/fb.h>
  16#include <linux/dma-buf.h>
  17#include <linux/mem_encrypt.h>
  18
  19#include <drm/drmP.h>
  20#include <drm/drm_crtc.h>
  21#include <drm/drm_crtc_helper.h>
  22#include "udl_drv.h"
  23
  24#include <drm/drm_fb_helper.h>
  25
  26#define DL_DEFIO_WRITE_DELAY    (HZ/20) /* fb_deferred_io.delay in jiffies */
  27
  28static int fb_defio = 0;  /* Optionally enable experimental fb_defio mmap support */
  29static int fb_bpp = 16;
  30
  31module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
  32module_param(fb_defio, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
  33
  34struct udl_fbdev {
  35        struct drm_fb_helper helper;
  36        struct udl_framebuffer ufb;
  37        int fb_count;
  38};
  39
  40#define DL_ALIGN_UP(x, a) ALIGN(x, a)
  41#define DL_ALIGN_DOWN(x, a) ALIGN_DOWN(x, a)
  42
  43/** Read the red component (0..255) of a 32 bpp colour. */
  44#define DLO_RGB_GETRED(col) (uint8_t)((col) & 0xFF)
  45
  46/** Read the green component (0..255) of a 32 bpp colour. */
  47#define DLO_RGB_GETGRN(col) (uint8_t)(((col) >> 8) & 0xFF)
  48
  49/** Read the blue component (0..255) of a 32 bpp colour. */
  50#define DLO_RGB_GETBLU(col) (uint8_t)(((col) >> 16) & 0xFF)
  51
  52/** Return red/green component of a 16 bpp colour number. */
  53#define DLO_RG16(red, grn) (uint8_t)((((red) & 0xF8) | ((grn) >> 5)) & 0xFF)
  54
  55/** Return green/blue component of a 16 bpp colour number. */
  56#define DLO_GB16(grn, blu) (uint8_t)(((((grn) & 0x1C) << 3) | ((blu) >> 3)) & 0xFF)
  57
  58/** Return 8 bpp colour number from red, green and blue components. */
  59#define DLO_RGB8(red, grn, blu) ((((red) << 5) | (((grn) & 3) << 3) | ((blu) & 7)) & 0xFF)
  60
  61#if 0
  62static uint8_t rgb8(uint32_t col)
  63{
  64        uint8_t red = DLO_RGB_GETRED(col);
  65        uint8_t grn = DLO_RGB_GETGRN(col);
  66        uint8_t blu = DLO_RGB_GETBLU(col);
  67
  68        return DLO_RGB8(red, grn, blu);
  69}
  70
  71static uint16_t rgb16(uint32_t col)
  72{
  73        uint8_t red = DLO_RGB_GETRED(col);
  74        uint8_t grn = DLO_RGB_GETGRN(col);
  75        uint8_t blu = DLO_RGB_GETBLU(col);
  76
  77        return (DLO_RG16(red, grn) << 8) + DLO_GB16(grn, blu);
  78}
  79#endif
  80
  81int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
  82                      int width, int height)
  83{
  84        struct drm_device *dev = fb->base.dev;
  85        struct udl_device *udl = dev->dev_private;
  86        int i, ret;
  87        char *cmd;
  88        cycles_t start_cycles, end_cycles;
  89        int bytes_sent = 0;
  90        int bytes_identical = 0;
  91        struct urb *urb;
  92        int aligned_x;
  93        int bpp = fb->base.format->cpp[0];
  94
  95        if (!fb->active_16)
  96                return 0;
  97
  98        if (!fb->obj->vmapping) {
  99                ret = udl_gem_vmap(fb->obj);
 100                if (ret == -ENOMEM) {
 101                        DRM_ERROR("failed to vmap fb\n");
 102                        return 0;
 103                }
 104                if (!fb->obj->vmapping) {
 105                        DRM_ERROR("failed to vmapping\n");
 106                        return 0;
 107                }
 108        }
 109
 110        aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
 111        width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
 112        x = aligned_x;
 113
 114        if ((width <= 0) ||
 115            (x + width > fb->base.width) ||
 116            (y + height > fb->base.height))
 117                return -EINVAL;
 118
 119        start_cycles = get_cycles();
 120
 121        urb = udl_get_urb(dev);
 122        if (!urb)
 123                return 0;
 124        cmd = urb->transfer_buffer;
 125
 126        for (i = y; i < y + height ; i++) {
 127                const int line_offset = fb->base.pitches[0] * i;
 128                const int byte_offset = line_offset + (x * bpp);
 129                const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp);
 130                if (udl_render_hline(dev, bpp, &urb,
 131                                     (char *) fb->obj->vmapping,
 132                                     &cmd, byte_offset, dev_byte_offset,
 133                                     width * bpp,
 134                                     &bytes_identical, &bytes_sent))
 135                        goto error;
 136        }
 137
 138        if (cmd > (char *) urb->transfer_buffer) {
 139                /* Send partial buffer remaining before exiting */
 140                int len = cmd - (char *) urb->transfer_buffer;
 141                ret = udl_submit_urb(dev, urb, len);
 142                bytes_sent += len;
 143        } else
 144                udl_urb_completion(urb);
 145
 146error:
 147        atomic_add(bytes_sent, &udl->bytes_sent);
 148        atomic_add(bytes_identical, &udl->bytes_identical);
 149        atomic_add(width*height*bpp, &udl->bytes_rendered);
 150        end_cycles = get_cycles();
 151        atomic_add(((unsigned int) ((end_cycles - start_cycles)
 152                    >> 10)), /* Kcycles */
 153                   &udl->cpu_kcycles_used);
 154
 155        return 0;
 156}
 157
 158static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 159{
 160        unsigned long start = vma->vm_start;
 161        unsigned long size = vma->vm_end - vma->vm_start;
 162        unsigned long offset;
 163        unsigned long page, pos;
 164
 165        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
 166                return -EINVAL;
 167
 168        offset = vma->vm_pgoff << PAGE_SHIFT;
 169
 170        if (offset > info->fix.smem_len || size > info->fix.smem_len - offset)
 171                return -EINVAL;
 172
 173        pos = (unsigned long)info->fix.smem_start + offset;
 174
 175        pr_notice("mmap() framebuffer addr:%lu size:%lu\n",
 176                  pos, size);
 177
 178        /* We don't want the framebuffer to be mapped encrypted */
 179        vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
 180
 181        while (size > 0) {
 182                page = vmalloc_to_pfn((void *)pos);
 183                if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
 184                        return -EAGAIN;
 185
 186                start += PAGE_SIZE;
 187                pos += PAGE_SIZE;
 188                if (size > PAGE_SIZE)
 189                        size -= PAGE_SIZE;
 190                else
 191                        size = 0;
 192        }
 193
 194        /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
 195        return 0;
 196}
 197
 198/*
 199 * It's common for several clients to have framebuffer open simultaneously.
 200 * e.g. both fbcon and X. Makes things interesting.
 201 * Assumes caller is holding info->lock (for open and release at least)
 202 */
 203static int udl_fb_open(struct fb_info *info, int user)
 204{
 205        struct udl_fbdev *ufbdev = info->par;
 206        struct drm_device *dev = ufbdev->ufb.base.dev;
 207        struct udl_device *udl = dev->dev_private;
 208
 209        /* If the USB device is gone, we don't accept new opens */
 210        if (drm_dev_is_unplugged(udl->ddev))
 211                return -ENODEV;
 212
 213        ufbdev->fb_count++;
 214
 215#ifdef CONFIG_DRM_FBDEV_EMULATION
 216        if (fb_defio && (info->fbdefio == NULL)) {
 217                /* enable defio at last moment if not disabled by client */
 218
 219                struct fb_deferred_io *fbdefio;
 220
 221                fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);
 222
 223                if (fbdefio) {
 224                        fbdefio->delay = DL_DEFIO_WRITE_DELAY;
 225                        fbdefio->deferred_io = drm_fb_helper_deferred_io;
 226                }
 227
 228                info->fbdefio = fbdefio;
 229                fb_deferred_io_init(info);
 230        }
 231#endif
 232
 233        pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
 234                  info->node, user, info, ufbdev->fb_count);
 235
 236        return 0;
 237}
 238
 239
 240/*
 241 * Assumes caller is holding info->lock mutex (for open and release at least)
 242 */
 243static int udl_fb_release(struct fb_info *info, int user)
 244{
 245        struct udl_fbdev *ufbdev = info->par;
 246
 247        ufbdev->fb_count--;
 248
 249#ifdef CONFIG_DRM_FBDEV_EMULATION
 250        if ((ufbdev->fb_count == 0) && (info->fbdefio)) {
 251                fb_deferred_io_cleanup(info);
 252                kfree(info->fbdefio);
 253                info->fbdefio = NULL;
 254                info->fbops->fb_mmap = udl_fb_mmap;
 255        }
 256#endif
 257
 258        pr_warn("released /dev/fb%d user=%d count=%d\n",
 259                info->node, user, ufbdev->fb_count);
 260
 261        return 0;
 262}
 263
 264static struct fb_ops udlfb_ops = {
 265        .owner = THIS_MODULE,
 266        DRM_FB_HELPER_DEFAULT_OPS,
 267        .fb_fillrect = drm_fb_helper_sys_fillrect,
 268        .fb_copyarea = drm_fb_helper_sys_copyarea,
 269        .fb_imageblit = drm_fb_helper_sys_imageblit,
 270        .fb_mmap = udl_fb_mmap,
 271        .fb_open = udl_fb_open,
 272        .fb_release = udl_fb_release,
 273};
 274
 275static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
 276                                      struct drm_file *file,
 277                                      unsigned flags, unsigned color,
 278                                      struct drm_clip_rect *clips,
 279                                      unsigned num_clips)
 280{
 281        struct udl_framebuffer *ufb = to_udl_fb(fb);
 282        int i;
 283        int ret = 0;
 284
 285        drm_modeset_lock_all(fb->dev);
 286
 287        if (!ufb->active_16)
 288                goto unlock;
 289
 290        if (ufb->obj->base.import_attach) {
 291                ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf,
 292                                               DMA_FROM_DEVICE);
 293                if (ret)
 294                        goto unlock;
 295        }
 296
 297        for (i = 0; i < num_clips; i++) {
 298                ret = udl_handle_damage(ufb, clips[i].x1, clips[i].y1,
 299                                  clips[i].x2 - clips[i].x1,
 300                                  clips[i].y2 - clips[i].y1);
 301                if (ret)
 302                        break;
 303        }
 304
 305        if (ufb->obj->base.import_attach) {
 306                ret = dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf,
 307                                             DMA_FROM_DEVICE);
 308        }
 309
 310 unlock:
 311        drm_modeset_unlock_all(fb->dev);
 312
 313        return ret;
 314}
 315
 316static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)
 317{
 318        struct udl_framebuffer *ufb = to_udl_fb(fb);
 319
 320        if (ufb->obj)
 321                drm_gem_object_put_unlocked(&ufb->obj->base);
 322
 323        drm_framebuffer_cleanup(fb);
 324        kfree(ufb);
 325}
 326
 327static const struct drm_framebuffer_funcs udlfb_funcs = {
 328        .destroy = udl_user_framebuffer_destroy,
 329        .dirty = udl_user_framebuffer_dirty,
 330};
 331
 332
 333static int
 334udl_framebuffer_init(struct drm_device *dev,
 335                     struct udl_framebuffer *ufb,
 336                     const struct drm_mode_fb_cmd2 *mode_cmd,
 337                     struct udl_gem_object *obj)
 338{
 339        int ret;
 340
 341        ufb->obj = obj;
 342        drm_helper_mode_fill_fb_struct(dev, &ufb->base, mode_cmd);
 343        ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);
 344        return ret;
 345}
 346
 347
 348static int udlfb_create(struct drm_fb_helper *helper,
 349                        struct drm_fb_helper_surface_size *sizes)
 350{
 351        struct udl_fbdev *ufbdev =
 352                container_of(helper, struct udl_fbdev, helper);
 353        struct drm_device *dev = ufbdev->helper.dev;
 354        struct fb_info *info;
 355        struct drm_framebuffer *fb;
 356        struct drm_mode_fb_cmd2 mode_cmd;
 357        struct udl_gem_object *obj;
 358        uint32_t size;
 359        int ret = 0;
 360
 361        if (sizes->surface_bpp == 24)
 362                sizes->surface_bpp = 32;
 363
 364        mode_cmd.width = sizes->surface_width;
 365        mode_cmd.height = sizes->surface_height;
 366        mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
 367
 368        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 369                                                          sizes->surface_depth);
 370
 371        size = mode_cmd.pitches[0] * mode_cmd.height;
 372        size = ALIGN(size, PAGE_SIZE);
 373
 374        obj = udl_gem_alloc_object(dev, size);
 375        if (!obj)
 376                goto out;
 377
 378        ret = udl_gem_vmap(obj);
 379        if (ret) {
 380                DRM_ERROR("failed to vmap fb\n");
 381                goto out_gfree;
 382        }
 383
 384        info = drm_fb_helper_alloc_fbi(helper);
 385        if (IS_ERR(info)) {
 386                ret = PTR_ERR(info);
 387                goto out_gfree;
 388        }
 389        info->par = ufbdev;
 390
 391        ret = udl_framebuffer_init(dev, &ufbdev->ufb, &mode_cmd, obj);
 392        if (ret)
 393                goto out_gfree;
 394
 395        fb = &ufbdev->ufb.base;
 396
 397        ufbdev->helper.fb = fb;
 398
 399        strcpy(info->fix.id, "udldrmfb");
 400
 401        info->screen_base = ufbdev->ufb.obj->vmapping;
 402        info->fix.smem_len = size;
 403        info->fix.smem_start = (unsigned long)ufbdev->ufb.obj->vmapping;
 404
 405        info->fbops = &udlfb_ops;
 406        drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth);
 407        drm_fb_helper_fill_var(info, &ufbdev->helper, sizes->fb_width, sizes->fb_height);
 408
 409        DRM_DEBUG_KMS("allocated %dx%d vmal %p\n",
 410                      fb->width, fb->height,
 411                      ufbdev->ufb.obj->vmapping);
 412
 413        return ret;
 414out_gfree:
 415        drm_gem_object_put_unlocked(&ufbdev->ufb.obj->base);
 416out:
 417        return ret;
 418}
 419
 420static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
 421        .fb_probe = udlfb_create,
 422};
 423
 424static void udl_fbdev_destroy(struct drm_device *dev,
 425                              struct udl_fbdev *ufbdev)
 426{
 427        drm_fb_helper_unregister_fbi(&ufbdev->helper);
 428        drm_fb_helper_fini(&ufbdev->helper);
 429        drm_framebuffer_unregister_private(&ufbdev->ufb.base);
 430        drm_framebuffer_cleanup(&ufbdev->ufb.base);
 431        drm_gem_object_put_unlocked(&ufbdev->ufb.obj->base);
 432}
 433
 434int udl_fbdev_init(struct drm_device *dev)
 435{
 436        struct udl_device *udl = dev->dev_private;
 437        int bpp_sel = fb_bpp;
 438        struct udl_fbdev *ufbdev;
 439        int ret;
 440
 441        ufbdev = kzalloc(sizeof(struct udl_fbdev), GFP_KERNEL);
 442        if (!ufbdev)
 443                return -ENOMEM;
 444
 445        udl->fbdev = ufbdev;
 446
 447        drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
 448
 449        ret = drm_fb_helper_init(dev, &ufbdev->helper, 1);
 450        if (ret)
 451                goto free;
 452
 453        ret = drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
 454        if (ret)
 455                goto fini;
 456
 457        /* disable all the possible outputs/crtcs before entering KMS mode */
 458        drm_helper_disable_unused_functions(dev);
 459
 460        ret = drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
 461        if (ret)
 462                goto fini;
 463
 464        return 0;
 465
 466fini:
 467        drm_fb_helper_fini(&ufbdev->helper);
 468free:
 469        kfree(ufbdev);
 470        return ret;
 471}
 472
 473void udl_fbdev_cleanup(struct drm_device *dev)
 474{
 475        struct udl_device *udl = dev->dev_private;
 476        if (!udl->fbdev)
 477                return;
 478
 479        udl_fbdev_destroy(dev, udl->fbdev);
 480        kfree(udl->fbdev);
 481        udl->fbdev = NULL;
 482}
 483
 484void udl_fbdev_unplug(struct drm_device *dev)
 485{
 486        struct udl_device *udl = dev->dev_private;
 487        struct udl_fbdev *ufbdev;
 488        if (!udl->fbdev)
 489                return;
 490
 491        ufbdev = udl->fbdev;
 492        drm_fb_helper_unlink_fbi(&ufbdev->helper);
 493}
 494
 495struct drm_framebuffer *
 496udl_fb_user_fb_create(struct drm_device *dev,
 497                   struct drm_file *file,
 498                   const struct drm_mode_fb_cmd2 *mode_cmd)
 499{
 500        struct drm_gem_object *obj;
 501        struct udl_framebuffer *ufb;
 502        int ret;
 503        uint32_t size;
 504
 505        obj = drm_gem_object_lookup(file, mode_cmd->handles[0]);
 506        if (obj == NULL)
 507                return ERR_PTR(-ENOENT);
 508
 509        size = mode_cmd->pitches[0] * mode_cmd->height;
 510        size = ALIGN(size, PAGE_SIZE);
 511
 512        if (size > obj->size) {
 513                DRM_ERROR("object size not sufficient for fb %d %zu %d %d\n", size, obj->size, mode_cmd->pitches[0], mode_cmd->height);
 514                return ERR_PTR(-ENOMEM);
 515        }
 516
 517        ufb = kzalloc(sizeof(*ufb), GFP_KERNEL);
 518        if (ufb == NULL)
 519                return ERR_PTR(-ENOMEM);
 520
 521        ret = udl_framebuffer_init(dev, ufb, mode_cmd, to_udl_bo(obj));
 522        if (ret) {
 523                kfree(ufb);
 524                return ERR_PTR(-EINVAL);
 525        }
 526        return &ufb->base;
 527}
 528