| 1 | |
| 2 | |
| 3 | * Copyright (c) 2007-2011, Intel Corporation.
|
| 4 | * All Rights Reserved.
|
| 5 | *
|
| 6 | *************************************************************************
|
| 7 |
|
| 8 | #include <linux/fb.h>
|
| 9 |
|
| 10 | #include <drm/drm_crtc_helper.h>
|
| 11 | #include <drm/drm_drv.h>
|
| 12 | #include <drm/drm_fb_helper.h>
|
| 13 | #include <drm/drm_framebuffer.h>
|
| 14 |
|
| 15 | #include "gem.h"
|
| 16 | #include "psb_drv.h"
|
| 17 |
|
| 18 | |
| 19 | * VM area struct
|
| 20 |
|
| 21 |
|
| 22 | static vm_fault_t psb_fbdev_vm_fault(struct vm_fault *vmf)
|
| 23 | {
|
| 24 | struct vm_area_struct *vma = vmf->vma;
|
| 25 | struct fb_info *info = vma->vm_private_data;
|
| 26 | unsigned long address = vmf->address - (vmf->pgoff << PAGE_SHIFT);
|
| 27 | unsigned long pfn = info->fix.smem_start >> PAGE_SHIFT;
|
| 28 | vm_fault_t err = VM_FAULT_SIGBUS;
|
| 29 | unsigned long page_num = vma_pages(vma);
|
| 30 | unsigned long i;
|
| 31 |
|
| 32 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
| 33 |
|
| 34 | for (i = 0; i < page_num; ++i) {
|
| 35 | err = vmf_insert_mixed(vma, address, pfn);
|
| 36 | if (unlikely(err & VM_FAULT_ERROR))
|
| 37 | break;
|
| 38 | address += PAGE_SIZE;
|
| 39 | ++pfn;
|
| 40 | }
|
| 41 |
|
| 42 | return err;
|
| 43 | }
|
| 44 |
|
| 45 | static const struct vm_operations_struct psb_fbdev_vm_ops = {
|
| 46 | .fault = psb_fbdev_vm_fault,
|
| 47 | };
|
| 48 |
|
| 49 | |
| 50 | * struct fb_ops
|
| 51 |
|
| 52 |
|
| 53 | static int psb_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
| 54 | {
|
| 55 | if (vma->vm_pgoff != 0)
|
| 56 | return -EINVAL;
|
| 57 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
| 58 | return -EINVAL;
|
| 59 |
|
| 60 | |
| 61 | * If this is a GEM object then info->screen_base is the virtual
|
| 62 | * kernel remapping of the object. FIXME: Review if this is
|
| 63 | * suitable for our mmap work
|
| 64 |
|
| 65 | vma->vm_ops = &psb_fbdev_vm_ops;
|
| 66 | vma->vm_private_data = info;
|
| 67 | vm_flags_set(vma, VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP);
|
| 68 |
|
| 69 | return 0;
|
| 70 | }
|
| 71 |
|
| 72 | static void psb_fbdev_fb_destroy(struct fb_info *info)
|
| 73 | {
|
| 74 | struct drm_fb_helper *fb_helper = info->par;
|
| 75 |
|
| 76 | drm_fb_helper_fini(fb_helper);
|
| 77 |
|
| 78 | drm_client_buffer_delete(fb_helper->buffer);
|
| 79 | drm_client_release(&fb_helper->client);
|
| 80 | }
|
| 81 |
|
| 82 | static const struct fb_ops psb_fbdev_fb_ops = {
|
| 83 | .owner = THIS_MODULE,
|
| 84 | __FB_DEFAULT_IOMEM_OPS_RDWR,
|
| 85 | DRM_FB_HELPER_DEFAULT_OPS,
|
| 86 | __FB_DEFAULT_IOMEM_OPS_DRAW,
|
| 87 | .fb_mmap = psb_fbdev_fb_mmap,
|
| 88 | .fb_destroy = psb_fbdev_fb_destroy,
|
| 89 | };
|
| 90 |
|
| 91 | static const struct drm_fb_helper_funcs psb_fbdev_fb_helper_funcs = {
|
| 92 | };
|
| 93 |
|
| 94 | |
| 95 | * struct drm_driver
|
| 96 |
|
| 97 |
|
| 98 | int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
|
| 99 | struct drm_fb_helper_surface_size *sizes)
|
| 100 | {
|
| 101 | struct drm_client_dev *client = &fb_helper->client;
|
| 102 | struct drm_device *dev = client->dev;
|
| 103 | struct drm_file *file = client->file;
|
| 104 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
| 105 | struct pci_dev *pdev = to_pci_dev(dev->dev);
|
| 106 | struct fb_info *info = fb_helper->info;
|
| 107 | u32 fourcc, pitch;
|
| 108 | u64 size;
|
| 109 | const struct drm_format_info *format;
|
| 110 | struct drm_client_buffer *buffer;
|
| 111 | struct psb_gem_object *backing;
|
| 112 | struct drm_gem_object *obj;
|
| 113 | u32 handle;
|
| 114 | int ret;
|
| 115 |
|
| 116 |
|
| 117 | if (sizes->surface_bpp == 24) {
|
| 118 | sizes->surface_bpp = 32;
|
| 119 | sizes->surface_depth = 24;
|
| 120 | }
|
| 121 |
|
| 122 | try_psb_gem_create:
|
| 123 | fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
|
| 124 | format = drm_get_format_info(dev, fourcc, DRM_FORMAT_MOD_LINEAR);
|
| 125 | pitch = ALIGN(drm_format_info_min_pitch(format, 0, sizes->surface_width), SZ_64);
|
| 126 | size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE);
|
| 127 |
|
| 128 |
|
| 129 | backing = psb_gem_create(dev, size, "fb", true, PAGE_SIZE);
|
| 130 | if (IS_ERR(backing)) {
|
| 131 | ret = PTR_ERR(backing);
|
| 132 | if (ret == -EBUSY && sizes->surface_bpp > 16) {
|
| 133 | |
| 134 | * If the mode does not fit in 32 bit then switch to 16 bit to
|
| 135 | * get a console on full resolution. User-space compositors will
|
| 136 | * allocate their own 32-bit framebuffers.
|
| 137 |
|
| 138 | sizes->surface_bpp = 16;
|
| 139 | sizes->surface_depth = 16;
|
| 140 | goto try_psb_gem_create;
|
| 141 | }
|
| 142 | return ret;
|
| 143 | }
|
| 144 | obj = &backing->base;
|
| 145 |
|
| 146 | ret = drm_gem_handle_create(file, obj, &handle);
|
| 147 | if (ret)
|
| 148 | goto err_drm_gem_object_put;
|
| 149 |
|
| 150 | buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height,
|
| 151 | fourcc, handle, pitch);
|
| 152 | if (IS_ERR(buffer)) {
|
| 153 | ret = PTR_ERR(buffer);
|
| 154 | goto err_drm_gem_handle_delete;
|
| 155 | }
|
| 156 |
|
| 157 | fb_helper->funcs = &psb_fbdev_fb_helper_funcs;
|
| 158 | fb_helper->buffer = buffer;
|
| 159 | fb_helper->fb = buffer->fb;
|
| 160 |
|
| 161 | info->fbops = &psb_fbdev_fb_ops;
|
| 162 |
|
| 163 |
|
| 164 | info->screen_base = dev_priv->vram_addr + backing->offset;
|
| 165 | info->screen_size = obj->size;
|
| 166 |
|
| 167 | drm_fb_helper_fill_info(info, fb_helper, sizes);
|
| 168 |
|
| 169 | info->fix.smem_start = dev_priv->stolen_base + backing->offset;
|
| 170 | info->fix.smem_len = obj->size;
|
| 171 | info->fix.ywrapstep = 0;
|
| 172 | info->fix.ypanstep = 0;
|
| 173 | info->fix.mmio_start = pci_resource_start(pdev, 0);
|
| 174 | info->fix.mmio_len = pci_resource_len(pdev, 0);
|
| 175 |
|
| 176 | fb_memset_io(info->screen_base, 0, info->screen_size);
|
| 177 |
|
| 178 |
|
| 179 |
|
| 180 | dev_dbg(dev->dev, "allocated %dx%d fb\n", buffer->fb->width, buffer->fb->height);
|
| 181 |
|
| 182 |
|
| 183 | drm_gem_handle_delete(file, handle);
|
| 184 |
|
| 185 |
|
| 186 | drm_gem_object_put(obj);
|
| 187 |
|
| 188 | return 0;
|
| 189 |
|
| 190 | err_drm_gem_handle_delete:
|
| 191 | drm_gem_handle_delete(file, handle);
|
| 192 | err_drm_gem_object_put:
|
| 193 | drm_gem_object_put(obj);
|
| 194 | return ret;
|
| 195 | }
|