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