1 // SPDX-License-Identifier: GPL-2.0-only 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 /* No 24-bit packed mode */ 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 /* Allocate the framebuffer in the GTT with stolen page backing */ 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 /* Accessed stolen memory directly */ 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 /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ 179 180 dev_dbg(dev->dev, "allocated %dx%d fb\n", buffer->fb->width, buffer->fb->height); 181 182 /* The handle is only needed for creating the framebuffer.*/ 183 drm_gem_handle_delete(file, handle); 184 185 /* The framebuffer still holds a references on the GEM object. */ 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 } 196