| 1 | |
| 2 | |
| 3 | * Copyright (C) 2012-2013 Avionic Design GmbH
|
| 4 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
|
| 5 | *
|
| 6 | * Based on the KMS/FB DMA helpers
|
| 7 | * Copyright (C) 2012 Analog Devices Inc.
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/console.h>
|
| 11 | #include <linux/fb.h>
|
| 12 | #include <linux/vmalloc.h>
|
| 13 |
|
| 14 | #include <drm/drm_drv.h>
|
| 15 | #include <drm/drm_crtc_helper.h>
|
| 16 | #include <drm/drm_fb_helper.h>
|
| 17 | #include <drm/drm_fourcc.h>
|
| 18 | #include <drm/drm_framebuffer.h>
|
| 19 | #include <drm/drm_gem_framebuffer_helper.h>
|
| 20 | #include <drm/drm_modeset_helper.h>
|
| 21 |
|
| 22 | #include "drm.h"
|
| 23 | #include "gem.h"
|
| 24 |
|
| 25 | static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
| 26 | {
|
| 27 | struct drm_fb_helper *helper = info->par;
|
| 28 | struct tegra_bo *bo;
|
| 29 | int err;
|
| 30 |
|
| 31 | bo = tegra_fb_get_plane(helper->fb, 0);
|
| 32 |
|
| 33 | err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma);
|
| 34 | if (err < 0)
|
| 35 | return err;
|
| 36 |
|
| 37 | return __tegra_gem_mmap(&bo->gem, vma);
|
| 38 | }
|
| 39 |
|
| 40 | static void tegra_fbdev_fb_destroy(struct fb_info *info)
|
| 41 | {
|
| 42 | struct drm_fb_helper *helper = info->par;
|
| 43 | struct tegra_bo *bo = tegra_fb_get_plane(helper->fb, 0);
|
| 44 |
|
| 45 | drm_fb_helper_fini(helper);
|
| 46 |
|
| 47 |
|
| 48 | if (bo->pages) {
|
| 49 | vunmap(bo->vaddr);
|
| 50 | bo->vaddr = NULL;
|
| 51 | }
|
| 52 |
|
| 53 | drm_client_buffer_delete(helper->buffer);
|
| 54 | drm_client_release(&helper->client);
|
| 55 | }
|
| 56 |
|
| 57 | static const struct fb_ops tegra_fb_ops = {
|
| 58 | .owner = THIS_MODULE,
|
| 59 | __FB_DEFAULT_DMAMEM_OPS_RDWR,
|
| 60 | DRM_FB_HELPER_DEFAULT_OPS,
|
| 61 | __FB_DEFAULT_DMAMEM_OPS_DRAW,
|
| 62 | .fb_mmap = tegra_fb_mmap,
|
| 63 | .fb_destroy = tegra_fbdev_fb_destroy,
|
| 64 | };
|
| 65 |
|
| 66 | static const struct drm_fb_helper_funcs tegra_fbdev_helper_funcs = {
|
| 67 | };
|
| 68 |
|
| 69 | int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
|
| 70 | struct drm_fb_helper_surface_size *sizes)
|
| 71 | {
|
| 72 | struct drm_client_dev *client = &helper->client;
|
| 73 | struct drm_device *drm = client->dev;
|
| 74 | struct drm_file *file = client->file;
|
| 75 | struct tegra_drm *tegra = drm->dev_private;
|
| 76 | struct fb_info *info = helper->info;
|
| 77 | u32 fourcc, pitch;
|
| 78 | u64 size;
|
| 79 | const struct drm_format_info *format;
|
| 80 | struct tegra_bo *bo;
|
| 81 | struct drm_gem_object *gem;
|
| 82 | u32 handle;
|
| 83 | struct drm_client_buffer *buffer;
|
| 84 | int err;
|
| 85 |
|
| 86 | fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
|
| 87 | format = drm_get_format_info(drm, fourcc, DRM_FORMAT_MOD_LINEAR);
|
| 88 | pitch = round_up(drm_format_info_min_pitch(format, 0, sizes->surface_width),
|
| 89 | tegra->pitch_align);
|
| 90 | size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE);
|
| 91 |
|
| 92 | bo = tegra_bo_create(drm, size, 0);
|
| 93 | if (IS_ERR(bo))
|
| 94 | return PTR_ERR(bo);
|
| 95 | gem = &bo->gem;
|
| 96 |
|
| 97 | err = drm_gem_handle_create(file, gem, &handle);
|
| 98 | if (err)
|
| 99 | goto err_drm_gem_object_put;
|
| 100 |
|
| 101 | buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height,
|
| 102 | fourcc, handle, pitch);
|
| 103 | if (IS_ERR(buffer)) {
|
| 104 | err = PTR_ERR(buffer);
|
| 105 | goto err_drm_gem_handle_delete;
|
| 106 | }
|
| 107 |
|
| 108 | helper->funcs = &tegra_fbdev_helper_funcs;
|
| 109 | helper->buffer = buffer;
|
| 110 | helper->fb = buffer->fb;
|
| 111 |
|
| 112 | info->fbops = &tegra_fb_ops;
|
| 113 |
|
| 114 | drm_fb_helper_fill_info(info, helper, sizes);
|
| 115 |
|
| 116 | if (bo->pages) {
|
| 117 | bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
|
| 118 | pgprot_writecombine(PAGE_KERNEL));
|
| 119 | if (!bo->vaddr) {
|
| 120 | dev_err(drm->dev, "failed to vmap() framebuffer\n");
|
| 121 | err = -ENOMEM;
|
| 122 | goto err_drm_client_buffer_delete;
|
| 123 | }
|
| 124 | }
|
| 125 |
|
| 126 | info->flags |= FBINFO_VIRTFB;
|
| 127 | info->screen_buffer = bo->vaddr;
|
| 128 | info->screen_size = gem->size;
|
| 129 | info->fix.smem_start = (unsigned long)(bo->iova);
|
| 130 | info->fix.smem_len = gem->size;
|
| 131 |
|
| 132 |
|
| 133 | drm_gem_handle_delete(file, handle);
|
| 134 |
|
| 135 |
|
| 136 | drm_gem_object_put(gem);
|
| 137 |
|
| 138 | return 0;
|
| 139 |
|
| 140 | err_drm_client_buffer_delete:
|
| 141 | drm_client_buffer_delete(buffer);
|
| 142 | err_drm_gem_handle_delete:
|
| 143 | drm_gem_handle_delete(file, handle);
|
| 144 | err_drm_gem_object_put:
|
| 145 | drm_gem_object_put(gem);
|
| 146 | return err;
|
| 147 | }
|