18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/************************************************************************** 38c2ecf20Sopenharmony_ci * Copyright (c) 2007-2011, Intel Corporation. 48c2ecf20Sopenharmony_ci * All Rights Reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci **************************************************************************/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/console.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/pfn_t.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/tty.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <drm/drm.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "framebuffer.h" 278c2ecf20Sopenharmony_ci#include "gtt.h" 288c2ecf20Sopenharmony_ci#include "psb_drv.h" 298c2ecf20Sopenharmony_ci#include "psb_intel_drv.h" 308c2ecf20Sopenharmony_ci#include "psb_intel_reg.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs psb_fb_funcs = { 338c2ecf20Sopenharmony_ci .destroy = drm_gem_fb_destroy, 348c2ecf20Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, 408c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 418c2ecf20Sopenharmony_ci struct fb_info *info) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 448c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 458c2ecf20Sopenharmony_ci uint32_t v; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!fb) 488c2ecf20Sopenharmony_ci return -ENOMEM; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (regno > 255) 518c2ecf20Sopenharmony_ci return 1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci red = CMAP_TOHW(red, info->var.red.length); 548c2ecf20Sopenharmony_ci blue = CMAP_TOHW(blue, info->var.blue.length); 558c2ecf20Sopenharmony_ci green = CMAP_TOHW(green, info->var.green.length); 568c2ecf20Sopenharmony_ci transp = CMAP_TOHW(transp, info->var.transp.length); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci v = (red << info->var.red.offset) | 598c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 608c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 618c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (regno < 16) { 648c2ecf20Sopenharmony_ci switch (fb->format->cpp[0] * 8) { 658c2ecf20Sopenharmony_ci case 16: 668c2ecf20Sopenharmony_ci ((uint32_t *) info->pseudo_palette)[regno] = v; 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci case 24: 698c2ecf20Sopenharmony_ci case 32: 708c2ecf20Sopenharmony_ci ((uint32_t *) info->pseudo_palette)[regno] = v; 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 818c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 828c2ecf20Sopenharmony_ci struct drm_device *dev = fb->dev; 838c2ecf20Sopenharmony_ci struct gtt_range *gtt = to_gtt_range(fb->obj[0]); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * We have to poke our nose in here. The core fb code assumes 878c2ecf20Sopenharmony_ci * panning is part of the hardware that can be invoked before 888c2ecf20Sopenharmony_ci * the actual fb is mapped. In our case that isn't quite true. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (gtt->npage) { 918c2ecf20Sopenharmony_ci /* GTT roll shifts in 4K pages, we need to shift the right 928c2ecf20Sopenharmony_ci number of pages */ 938c2ecf20Sopenharmony_ci int pages = info->fix.line_length >> 12; 948c2ecf20Sopenharmony_ci psb_gtt_roll(dev, gtt, var->yoffset * pages); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic vm_fault_t psbfb_vm_fault(struct vm_fault *vmf) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 1028c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = vma->vm_private_data; 1038c2ecf20Sopenharmony_ci struct drm_device *dev = fb->dev; 1048c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 1058c2ecf20Sopenharmony_ci struct gtt_range *gtt = to_gtt_range(fb->obj[0]); 1068c2ecf20Sopenharmony_ci int page_num; 1078c2ecf20Sopenharmony_ci int i; 1088c2ecf20Sopenharmony_ci unsigned long address; 1098c2ecf20Sopenharmony_ci vm_fault_t ret = VM_FAULT_SIGBUS; 1108c2ecf20Sopenharmony_ci unsigned long pfn; 1118c2ecf20Sopenharmony_ci unsigned long phys_addr = (unsigned long)dev_priv->stolen_base + 1128c2ecf20Sopenharmony_ci gtt->offset; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci page_num = vma_pages(vma); 1158c2ecf20Sopenharmony_ci address = vmf->address - (vmf->pgoff << PAGE_SHIFT); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < page_num; i++) { 1208c2ecf20Sopenharmony_ci pfn = (phys_addr >> PAGE_SHIFT); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = vmf_insert_mixed(vma, address, 1238c2ecf20Sopenharmony_ci __pfn_to_pfn_t(pfn, PFN_DEV)); 1248c2ecf20Sopenharmony_ci if (unlikely(ret & VM_FAULT_ERROR)) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci address += PAGE_SIZE; 1278c2ecf20Sopenharmony_ci phys_addr += PAGE_SIZE; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void psbfb_vm_open(struct vm_area_struct *vma) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void psbfb_vm_close(struct vm_area_struct *vma) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic const struct vm_operations_struct psbfb_vm_ops = { 1418c2ecf20Sopenharmony_ci .fault = psbfb_vm_fault, 1428c2ecf20Sopenharmony_ci .open = psbfb_vm_open, 1438c2ecf20Sopenharmony_ci .close = psbfb_vm_close 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 1498c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (vma->vm_pgoff != 0) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * If this is a GEM object then info->screen_base is the virtual 1588c2ecf20Sopenharmony_ci * kernel remapping of the object. FIXME: Review if this is 1598c2ecf20Sopenharmony_ci * suitable for our mmap work 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci vma->vm_ops = &psbfb_vm_ops; 1628c2ecf20Sopenharmony_ci vma->vm_private_data = (void *)fb; 1638c2ecf20Sopenharmony_ci vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP; 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct fb_ops psbfb_ops = { 1688c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1698c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 1708c2ecf20Sopenharmony_ci .fb_setcolreg = psbfb_setcolreg, 1718c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_cfb_fillrect, 1728c2ecf20Sopenharmony_ci .fb_copyarea = psbfb_copyarea, 1738c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_cfb_imageblit, 1748c2ecf20Sopenharmony_ci .fb_mmap = psbfb_mmap, 1758c2ecf20Sopenharmony_ci .fb_sync = psbfb_sync, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct fb_ops psbfb_roll_ops = { 1798c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1808c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 1818c2ecf20Sopenharmony_ci .fb_setcolreg = psbfb_setcolreg, 1828c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_cfb_fillrect, 1838c2ecf20Sopenharmony_ci .fb_copyarea = drm_fb_helper_cfb_copyarea, 1848c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_cfb_imageblit, 1858c2ecf20Sopenharmony_ci .fb_pan_display = psbfb_pan, 1868c2ecf20Sopenharmony_ci .fb_mmap = psbfb_mmap, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct fb_ops psbfb_unaccel_ops = { 1908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1918c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 1928c2ecf20Sopenharmony_ci .fb_setcolreg = psbfb_setcolreg, 1938c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_cfb_fillrect, 1948c2ecf20Sopenharmony_ci .fb_copyarea = drm_fb_helper_cfb_copyarea, 1958c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_cfb_imageblit, 1968c2ecf20Sopenharmony_ci .fb_mmap = psbfb_mmap, 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/** 2008c2ecf20Sopenharmony_ci * psb_framebuffer_init - initialize a framebuffer 2018c2ecf20Sopenharmony_ci * @dev: our DRM device 2028c2ecf20Sopenharmony_ci * @fb: framebuffer to set up 2038c2ecf20Sopenharmony_ci * @mode_cmd: mode description 2048c2ecf20Sopenharmony_ci * @gt: backing object 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Configure and fill in the boilerplate for our frame buffer. Return 2078c2ecf20Sopenharmony_ci * 0 on success or an error code if we fail. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic int psb_framebuffer_init(struct drm_device *dev, 2108c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 2118c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 2128c2ecf20Sopenharmony_ci struct drm_gem_object *obj) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci const struct drm_format_info *info; 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * Reject unknown formats, YUV formats, and formats with more than 2198c2ecf20Sopenharmony_ci * 4 bytes per pixel. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci info = drm_get_format_info(dev, mode_cmd); 2228c2ecf20Sopenharmony_ci if (!info || !info->depth || info->cpp[0] > 4) 2238c2ecf20Sopenharmony_ci return -EINVAL; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (mode_cmd->pitches[0] & 63) 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 2298c2ecf20Sopenharmony_ci fb->obj[0] = obj; 2308c2ecf20Sopenharmony_ci ret = drm_framebuffer_init(dev, fb, &psb_fb_funcs); 2318c2ecf20Sopenharmony_ci if (ret) { 2328c2ecf20Sopenharmony_ci dev_err(dev->dev, "framebuffer init failed: %d\n", ret); 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/** 2398c2ecf20Sopenharmony_ci * psb_framebuffer_create - create a framebuffer backed by gt 2408c2ecf20Sopenharmony_ci * @dev: our DRM device 2418c2ecf20Sopenharmony_ci * @mode_cmd: the description of the requested mode 2428c2ecf20Sopenharmony_ci * @gt: the backing object 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * Create a framebuffer object backed by the gt, and fill in the 2458c2ecf20Sopenharmony_ci * boilerplate required 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * TODO: review object references 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic struct drm_framebuffer *psb_framebuffer_create 2518c2ecf20Sopenharmony_ci (struct drm_device *dev, 2528c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 2538c2ecf20Sopenharmony_ci struct drm_gem_object *obj) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci fb = kzalloc(sizeof(*fb), GFP_KERNEL); 2598c2ecf20Sopenharmony_ci if (!fb) 2608c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = psb_framebuffer_init(dev, fb, mode_cmd, obj); 2638c2ecf20Sopenharmony_ci if (ret) { 2648c2ecf20Sopenharmony_ci kfree(fb); 2658c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci return fb; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * psbfb_alloc - allocate frame buffer memory 2728c2ecf20Sopenharmony_ci * @dev: the DRM device 2738c2ecf20Sopenharmony_ci * @aligned_size: space needed 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Allocate the frame buffer. In the usual case we get a GTT range that 2768c2ecf20Sopenharmony_ci * is stolen memory backed and life is simple. If there isn't sufficient 2778c2ecf20Sopenharmony_ci * we fail as we don't have the virtual mapping space to really vmap it 2788c2ecf20Sopenharmony_ci * and the kernel console code can't handle non linear framebuffers. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Re-address this as and if the framebuffer layer grows this ability. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct gtt_range *backing; 2858c2ecf20Sopenharmony_ci /* Begin by trying to use stolen memory backing */ 2868c2ecf20Sopenharmony_ci backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE); 2878c2ecf20Sopenharmony_ci if (backing) { 2888c2ecf20Sopenharmony_ci drm_gem_private_object_init(dev, &backing->gem, aligned_size); 2898c2ecf20Sopenharmony_ci return backing; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci return NULL; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * psbfb_create - create a framebuffer 2968c2ecf20Sopenharmony_ci * @fbdev: the framebuffer device 2978c2ecf20Sopenharmony_ci * @sizes: specification of the layout 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Create a framebuffer to the specifications provided 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistatic int psbfb_create(struct drm_fb_helper *fb_helper, 3028c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 3058c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 3068c2ecf20Sopenharmony_ci struct fb_info *info; 3078c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 3088c2ecf20Sopenharmony_ci struct drm_mode_fb_cmd2 mode_cmd; 3098c2ecf20Sopenharmony_ci int size; 3108c2ecf20Sopenharmony_ci int ret; 3118c2ecf20Sopenharmony_ci struct gtt_range *backing; 3128c2ecf20Sopenharmony_ci u32 bpp, depth; 3138c2ecf20Sopenharmony_ci int gtt_roll = 0; 3148c2ecf20Sopenharmony_ci int pitch_lines = 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mode_cmd.width = sizes->surface_width; 3178c2ecf20Sopenharmony_ci mode_cmd.height = sizes->surface_height; 3188c2ecf20Sopenharmony_ci bpp = sizes->surface_bpp; 3198c2ecf20Sopenharmony_ci depth = sizes->surface_depth; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* No 24bit packed */ 3228c2ecf20Sopenharmony_ci if (bpp == 24) 3238c2ecf20Sopenharmony_ci bpp = 32; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci do { 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * Acceleration via the GTT requires pitch to be 3288c2ecf20Sopenharmony_ci * power of two aligned. Preferably page but less 3298c2ecf20Sopenharmony_ci * is ok with some fonts 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096 >> pitch_lines); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci size = mode_cmd.pitches[0] * mode_cmd.height; 3348c2ecf20Sopenharmony_ci size = ALIGN(size, PAGE_SIZE); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Allocate the fb in the GTT with stolen page backing */ 3378c2ecf20Sopenharmony_ci backing = psbfb_alloc(dev, size); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (pitch_lines) 3408c2ecf20Sopenharmony_ci pitch_lines *= 2; 3418c2ecf20Sopenharmony_ci else 3428c2ecf20Sopenharmony_ci pitch_lines = 1; 3438c2ecf20Sopenharmony_ci gtt_roll++; 3448c2ecf20Sopenharmony_ci } while (backing == NULL && pitch_lines <= 16); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* The final pitch we accepted if we succeeded */ 3478c2ecf20Sopenharmony_ci pitch_lines /= 2; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (backing == NULL) { 3508c2ecf20Sopenharmony_ci /* 3518c2ecf20Sopenharmony_ci * We couldn't get the space we wanted, fall back to the 3528c2ecf20Sopenharmony_ci * display engine requirement instead. The HW requires 3538c2ecf20Sopenharmony_ci * the pitch to be 64 byte aligned 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci gtt_roll = 0; /* Don't use GTT accelerated scrolling */ 3578c2ecf20Sopenharmony_ci pitch_lines = 64; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci size = mode_cmd.pitches[0] * mode_cmd.height; 3628c2ecf20Sopenharmony_ci size = ALIGN(size, PAGE_SIZE); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Allocate the framebuffer in the GTT with stolen page backing */ 3658c2ecf20Sopenharmony_ci backing = psbfb_alloc(dev, size); 3668c2ecf20Sopenharmony_ci if (backing == NULL) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci memset(dev_priv->vram_addr + backing->offset, 0, size); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci info = drm_fb_helper_alloc_fbi(fb_helper); 3738c2ecf20Sopenharmony_ci if (IS_ERR(info)) { 3748c2ecf20Sopenharmony_ci ret = PTR_ERR(info); 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci fb = psb_framebuffer_create(dev, &mode_cmd, &backing->gem); 3818c2ecf20Sopenharmony_ci if (IS_ERR(fb)) { 3828c2ecf20Sopenharmony_ci ret = PTR_ERR(fb); 3838c2ecf20Sopenharmony_ci goto out; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci fb_helper->fb = fb; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (dev_priv->ops->accel_2d && pitch_lines > 8) /* 2D engine */ 3898c2ecf20Sopenharmony_ci info->fbops = &psbfb_ops; 3908c2ecf20Sopenharmony_ci else if (gtt_roll) { /* GTT rolling seems best */ 3918c2ecf20Sopenharmony_ci info->fbops = &psbfb_roll_ops; 3928c2ecf20Sopenharmony_ci info->flags |= FBINFO_HWACCEL_YPAN; 3938c2ecf20Sopenharmony_ci } else /* Software */ 3948c2ecf20Sopenharmony_ci info->fbops = &psbfb_unaccel_ops; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci info->fix.smem_start = dev->mode_config.fb_base; 3978c2ecf20Sopenharmony_ci info->fix.smem_len = size; 3988c2ecf20Sopenharmony_ci info->fix.ywrapstep = gtt_roll; 3998c2ecf20Sopenharmony_ci info->fix.ypanstep = 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Accessed stolen memory directly */ 4028c2ecf20Sopenharmony_ci info->screen_base = dev_priv->vram_addr + backing->offset; 4038c2ecf20Sopenharmony_ci info->screen_size = size; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (dev_priv->gtt.stolen_size) { 4068c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = dev->mode_config.fb_base; 4078c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci drm_fb_helper_fill_info(info, fb_helper, sizes); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci info->fix.mmio_start = pci_resource_start(dev->pdev, 0); 4138c2ecf20Sopenharmony_ci info->fix.mmio_len = pci_resource_len(dev->pdev, 0); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "allocated %dx%d fb\n", fb->width, fb->height); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ciout: 4218c2ecf20Sopenharmony_ci psb_gtt_free_range(dev, backing); 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/** 4268c2ecf20Sopenharmony_ci * psb_user_framebuffer_create - create framebuffer 4278c2ecf20Sopenharmony_ci * @dev: our DRM device 4288c2ecf20Sopenharmony_ci * @filp: client file 4298c2ecf20Sopenharmony_ci * @cmd: mode request 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * Create a new framebuffer backed by a userspace GEM object 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_cistatic struct drm_framebuffer *psb_user_framebuffer_create 4348c2ecf20Sopenharmony_ci (struct drm_device *dev, struct drm_file *filp, 4358c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *cmd) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 4388c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * Find the GEM object and thus the gtt range object that is 4428c2ecf20Sopenharmony_ci * to back this space 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci obj = drm_gem_object_lookup(filp, cmd->handles[0]); 4458c2ecf20Sopenharmony_ci if (obj == NULL) 4468c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Let the core code do all the work */ 4498c2ecf20Sopenharmony_ci fb = psb_framebuffer_create(dev, cmd, obj); 4508c2ecf20Sopenharmony_ci if (IS_ERR(fb)) 4518c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return fb; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int psbfb_probe(struct drm_fb_helper *fb_helper, 4578c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 4608c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 4618c2ecf20Sopenharmony_ci unsigned int fb_size; 4628c2ecf20Sopenharmony_ci int bytespp; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci bytespp = sizes->surface_bpp / 8; 4658c2ecf20Sopenharmony_ci if (bytespp == 3) /* no 24bit packed */ 4668c2ecf20Sopenharmony_ci bytespp = 4; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* If the mode will not fit in 32bit then switch to 16bit to get 4698c2ecf20Sopenharmony_ci a console on full resolution. The X mode setting server will 4708c2ecf20Sopenharmony_ci allocate its own 32bit GEM framebuffer */ 4718c2ecf20Sopenharmony_ci fb_size = ALIGN(sizes->surface_width * bytespp, 64) * 4728c2ecf20Sopenharmony_ci sizes->surface_height; 4738c2ecf20Sopenharmony_ci fb_size = ALIGN(fb_size, PAGE_SIZE); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (fb_size > dev_priv->vram_stolen_size) { 4768c2ecf20Sopenharmony_ci sizes->surface_bpp = 16; 4778c2ecf20Sopenharmony_ci sizes->surface_depth = 16; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return psbfb_create(fb_helper, sizes); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic const struct drm_fb_helper_funcs psb_fb_helper_funcs = { 4848c2ecf20Sopenharmony_ci .fb_probe = psbfb_probe, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int psb_fbdev_destroy(struct drm_device *dev, 4888c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci drm_fb_helper_unregister_fbi(fb_helper); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci drm_fb_helper_fini(fb_helper); 4958c2ecf20Sopenharmony_ci drm_framebuffer_unregister_private(fb); 4968c2ecf20Sopenharmony_ci drm_framebuffer_cleanup(fb); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (fb->obj[0]) 4998c2ecf20Sopenharmony_ci drm_gem_object_put(fb->obj[0]); 5008c2ecf20Sopenharmony_ci kfree(fb); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciint psb_fbdev_init(struct drm_device *dev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper; 5088c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 5098c2ecf20Sopenharmony_ci int ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); 5128c2ecf20Sopenharmony_ci if (!fb_helper) { 5138c2ecf20Sopenharmony_ci dev_err(dev->dev, "no memory\n"); 5148c2ecf20Sopenharmony_ci return -ENOMEM; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dev_priv->fb_helper = fb_helper; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci drm_fb_helper_prepare(dev, fb_helper, &psb_fb_helper_funcs); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci ret = drm_fb_helper_init(dev, fb_helper); 5228c2ecf20Sopenharmony_ci if (ret) 5238c2ecf20Sopenharmony_ci goto free; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* disable all the possible outputs/crtcs before entering KMS mode */ 5268c2ecf20Sopenharmony_ci drm_helper_disable_unused_functions(dev); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci ret = drm_fb_helper_initial_config(fb_helper, 32); 5298c2ecf20Sopenharmony_ci if (ret) 5308c2ecf20Sopenharmony_ci goto fini; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cifini: 5358c2ecf20Sopenharmony_ci drm_fb_helper_fini(fb_helper); 5368c2ecf20Sopenharmony_cifree: 5378c2ecf20Sopenharmony_ci kfree(fb_helper); 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void psb_fbdev_fini(struct drm_device *dev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (!dev_priv->fb_helper) 5468c2ecf20Sopenharmony_ci return; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci psb_fbdev_destroy(dev, dev_priv->fb_helper); 5498c2ecf20Sopenharmony_ci kfree(dev_priv->fb_helper); 5508c2ecf20Sopenharmony_ci dev_priv->fb_helper = NULL; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs psb_mode_funcs = { 5548c2ecf20Sopenharmony_ci .fb_create = psb_user_framebuffer_create, 5558c2ecf20Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed, 5568c2ecf20Sopenharmony_ci}; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic void psb_setup_outputs(struct drm_device *dev) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 5618c2ecf20Sopenharmony_ci struct drm_connector *connector; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci drm_mode_create_scaling_mode_property(dev); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* It is ok for this to fail - we just don't get backlight control */ 5668c2ecf20Sopenharmony_ci if (!dev_priv->backlight_property) 5678c2ecf20Sopenharmony_ci dev_priv->backlight_property = drm_property_create_range(dev, 0, 5688c2ecf20Sopenharmony_ci "backlight", 0, 100); 5698c2ecf20Sopenharmony_ci dev_priv->ops->output_init(dev); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci list_for_each_entry(connector, &dev->mode_config.connector_list, 5728c2ecf20Sopenharmony_ci head) { 5738c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder = gma_attached_encoder(connector); 5748c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &gma_encoder->base; 5758c2ecf20Sopenharmony_ci int crtc_mask = 0, clone_mask = 0; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* valid crtcs */ 5788c2ecf20Sopenharmony_ci switch (gma_encoder->type) { 5798c2ecf20Sopenharmony_ci case INTEL_OUTPUT_ANALOG: 5808c2ecf20Sopenharmony_ci crtc_mask = (1 << 0); 5818c2ecf20Sopenharmony_ci clone_mask = (1 << INTEL_OUTPUT_ANALOG); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case INTEL_OUTPUT_SDVO: 5848c2ecf20Sopenharmony_ci crtc_mask = dev_priv->ops->sdvo_mask; 5858c2ecf20Sopenharmony_ci clone_mask = 0; 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci case INTEL_OUTPUT_LVDS: 5888c2ecf20Sopenharmony_ci crtc_mask = dev_priv->ops->lvds_mask; 5898c2ecf20Sopenharmony_ci clone_mask = 0; 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci case INTEL_OUTPUT_MIPI: 5928c2ecf20Sopenharmony_ci crtc_mask = (1 << 0); 5938c2ecf20Sopenharmony_ci clone_mask = 0; 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case INTEL_OUTPUT_MIPI2: 5968c2ecf20Sopenharmony_ci crtc_mask = (1 << 2); 5978c2ecf20Sopenharmony_ci clone_mask = 0; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case INTEL_OUTPUT_HDMI: 6008c2ecf20Sopenharmony_ci crtc_mask = dev_priv->ops->hdmi_mask; 6018c2ecf20Sopenharmony_ci clone_mask = (1 << INTEL_OUTPUT_HDMI); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci case INTEL_OUTPUT_DISPLAYPORT: 6048c2ecf20Sopenharmony_ci crtc_mask = (1 << 0) | (1 << 1); 6058c2ecf20Sopenharmony_ci clone_mask = 0; 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case INTEL_OUTPUT_EDP: 6088c2ecf20Sopenharmony_ci crtc_mask = (1 << 1); 6098c2ecf20Sopenharmony_ci clone_mask = 0; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci encoder->possible_crtcs = crtc_mask; 6128c2ecf20Sopenharmony_ci encoder->possible_clones = 6138c2ecf20Sopenharmony_ci gma_connector_clones(dev, clone_mask); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_civoid psb_modeset_init(struct drm_device *dev) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 6208c2ecf20Sopenharmony_ci struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; 6218c2ecf20Sopenharmony_ci int i; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci drm_mode_config_init(dev); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci dev->mode_config.min_width = 0; 6268c2ecf20Sopenharmony_ci dev->mode_config.min_height = 0; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci dev->mode_config.funcs = &psb_mode_funcs; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* set memory base */ 6318c2ecf20Sopenharmony_ci /* Oaktrail and Poulsbo should use BAR 2*/ 6328c2ecf20Sopenharmony_ci pci_read_config_dword(dev->pdev, PSB_BSM, (u32 *) 6338c2ecf20Sopenharmony_ci &(dev->mode_config.fb_base)); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* num pipes is 2 for PSB but 1 for Mrst */ 6368c2ecf20Sopenharmony_ci for (i = 0; i < dev_priv->num_pipe; i++) 6378c2ecf20Sopenharmony_ci psb_intel_crtc_init(dev, i, mode_dev); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci dev->mode_config.max_width = 4096; 6408c2ecf20Sopenharmony_ci dev->mode_config.max_height = 4096; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci psb_setup_outputs(dev); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (dev_priv->ops->errata) 6458c2ecf20Sopenharmony_ci dev_priv->ops->errata(dev); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci dev_priv->modeset = true; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_civoid psb_modeset_cleanup(struct drm_device *dev) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 6538c2ecf20Sopenharmony_ci if (dev_priv->modeset) { 6548c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(dev); 6558c2ecf20Sopenharmony_ci psb_fbdev_fini(dev); 6568c2ecf20Sopenharmony_ci drm_mode_config_cleanup(dev); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci} 659