18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* sbuslib.c: Helper library for SBUS framebuffer drivers. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2003 David S. Miller (davem@redhat.com) 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/compat.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/fb.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/fbio.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "sbuslib.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_civoid sbusfb_fill_var(struct fb_var_screeninfo *var, struct device_node *dp, 218c2ecf20Sopenharmony_ci int bpp) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci memset(var, 0, sizeof(*var)); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci var->xres = of_getintprop_default(dp, "width", 1152); 268c2ecf20Sopenharmony_ci var->yres = of_getintprop_default(dp, "height", 900); 278c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 288c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 298c2ecf20Sopenharmony_ci var->bits_per_pixel = bpp; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sbusfb_fill_var); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic unsigned long sbusfb_mmapsize(long size, unsigned long fbsize) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci if (size == SBUS_MMAP_EMPTY) return 0; 378c2ecf20Sopenharmony_ci if (size >= 0) return size; 388c2ecf20Sopenharmony_ci return fbsize * (-size); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint sbusfb_mmap_helper(struct sbus_mmap_map *map, 428c2ecf20Sopenharmony_ci unsigned long physbase, 438c2ecf20Sopenharmony_ci unsigned long fbsize, 448c2ecf20Sopenharmony_ci unsigned long iospace, 458c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci unsigned int size, page, r, map_size; 488c2ecf20Sopenharmony_ci unsigned long map_offset = 0; 498c2ecf20Sopenharmony_ci unsigned long off; 508c2ecf20Sopenharmony_ci int i; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci size = vma->vm_end - vma->vm_start; 568c2ecf20Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 578c2ecf20Sopenharmony_ci return -EINVAL; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci off = vma->vm_pgoff << PAGE_SHIFT; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Each page, see which map applies */ 668c2ecf20Sopenharmony_ci for (page = 0; page < size; ){ 678c2ecf20Sopenharmony_ci map_size = 0; 688c2ecf20Sopenharmony_ci for (i = 0; map[i].size; i++) 698c2ecf20Sopenharmony_ci if (map[i].voff == off+page) { 708c2ecf20Sopenharmony_ci map_size = sbusfb_mmapsize(map[i].size, fbsize); 718c2ecf20Sopenharmony_ci#ifdef __sparc_v9__ 728c2ecf20Sopenharmony_ci#define POFF_MASK (PAGE_MASK|0x1UL) 738c2ecf20Sopenharmony_ci#else 748c2ecf20Sopenharmony_ci#define POFF_MASK (PAGE_MASK) 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci map_offset = (physbase + map[i].poff) & POFF_MASK; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci if (!map_size) { 808c2ecf20Sopenharmony_ci page += PAGE_SIZE; 818c2ecf20Sopenharmony_ci continue; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci if (page + map_size > size) 848c2ecf20Sopenharmony_ci map_size = size - page; 858c2ecf20Sopenharmony_ci r = io_remap_pfn_range(vma, 868c2ecf20Sopenharmony_ci vma->vm_start + page, 878c2ecf20Sopenharmony_ci MK_IOSPACE_PFN(iospace, 888c2ecf20Sopenharmony_ci map_offset >> PAGE_SHIFT), 898c2ecf20Sopenharmony_ci map_size, 908c2ecf20Sopenharmony_ci vma->vm_page_prot); 918c2ecf20Sopenharmony_ci if (r) 928c2ecf20Sopenharmony_ci return -EAGAIN; 938c2ecf20Sopenharmony_ci page += map_size; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sbusfb_mmap_helper); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg, 1018c2ecf20Sopenharmony_ci struct fb_info *info, 1028c2ecf20Sopenharmony_ci int type, int fb_depth, unsigned long fb_size) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci switch(cmd) { 1058c2ecf20Sopenharmony_ci case FBIOGTYPE: { 1068c2ecf20Sopenharmony_ci struct fbtype __user *f = (struct fbtype __user *) arg; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (put_user(type, &f->fb_type) || 1098c2ecf20Sopenharmony_ci put_user(info->var.yres, &f->fb_height) || 1108c2ecf20Sopenharmony_ci put_user(info->var.xres, &f->fb_width) || 1118c2ecf20Sopenharmony_ci put_user(fb_depth, &f->fb_depth) || 1128c2ecf20Sopenharmony_ci put_user(0, &f->fb_cmsize) || 1138c2ecf20Sopenharmony_ci put_user(fb_size, &f->fb_cmsize)) 1148c2ecf20Sopenharmony_ci return -EFAULT; 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci case FBIOPUTCMAP_SPARC: { 1188c2ecf20Sopenharmony_ci struct fbcmap __user *c = (struct fbcmap __user *) arg; 1198c2ecf20Sopenharmony_ci struct fb_cmap cmap; 1208c2ecf20Sopenharmony_ci u16 red, green, blue; 1218c2ecf20Sopenharmony_ci u8 red8, green8, blue8; 1228c2ecf20Sopenharmony_ci unsigned char __user *ured; 1238c2ecf20Sopenharmony_ci unsigned char __user *ugreen; 1248c2ecf20Sopenharmony_ci unsigned char __user *ublue; 1258c2ecf20Sopenharmony_ci unsigned int index, count, i; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (get_user(index, &c->index) || 1288c2ecf20Sopenharmony_ci get_user(count, &c->count) || 1298c2ecf20Sopenharmony_ci get_user(ured, &c->red) || 1308c2ecf20Sopenharmony_ci get_user(ugreen, &c->green) || 1318c2ecf20Sopenharmony_ci get_user(ublue, &c->blue)) 1328c2ecf20Sopenharmony_ci return -EFAULT; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci cmap.len = 1; 1358c2ecf20Sopenharmony_ci cmap.red = &red; 1368c2ecf20Sopenharmony_ci cmap.green = &green; 1378c2ecf20Sopenharmony_ci cmap.blue = &blue; 1388c2ecf20Sopenharmony_ci cmap.transp = NULL; 1398c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1408c2ecf20Sopenharmony_ci int err; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (get_user(red8, &ured[i]) || 1438c2ecf20Sopenharmony_ci get_user(green8, &ugreen[i]) || 1448c2ecf20Sopenharmony_ci get_user(blue8, &ublue[i])) 1458c2ecf20Sopenharmony_ci return -EFAULT; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci red = red8 << 8; 1488c2ecf20Sopenharmony_ci green = green8 << 8; 1498c2ecf20Sopenharmony_ci blue = blue8 << 8; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci cmap.start = index + i; 1528c2ecf20Sopenharmony_ci err = fb_set_cmap(&cmap, info); 1538c2ecf20Sopenharmony_ci if (err) 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci case FBIOGETCMAP_SPARC: { 1598c2ecf20Sopenharmony_ci struct fbcmap __user *c = (struct fbcmap __user *) arg; 1608c2ecf20Sopenharmony_ci unsigned char __user *ured; 1618c2ecf20Sopenharmony_ci unsigned char __user *ugreen; 1628c2ecf20Sopenharmony_ci unsigned char __user *ublue; 1638c2ecf20Sopenharmony_ci struct fb_cmap *cmap = &info->cmap; 1648c2ecf20Sopenharmony_ci unsigned int index, count, i; 1658c2ecf20Sopenharmony_ci u8 red, green, blue; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (get_user(index, &c->index) || 1688c2ecf20Sopenharmony_ci get_user(count, &c->count) || 1698c2ecf20Sopenharmony_ci get_user(ured, &c->red) || 1708c2ecf20Sopenharmony_ci get_user(ugreen, &c->green) || 1718c2ecf20Sopenharmony_ci get_user(ublue, &c->blue)) 1728c2ecf20Sopenharmony_ci return -EFAULT; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (index > cmap->len || count > cmap->len - index) 1758c2ecf20Sopenharmony_ci return -EINVAL; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1788c2ecf20Sopenharmony_ci red = cmap->red[index + i] >> 8; 1798c2ecf20Sopenharmony_ci green = cmap->green[index + i] >> 8; 1808c2ecf20Sopenharmony_ci blue = cmap->blue[index + i] >> 8; 1818c2ecf20Sopenharmony_ci if (put_user(red, &ured[i]) || 1828c2ecf20Sopenharmony_ci put_user(green, &ugreen[i]) || 1838c2ecf20Sopenharmony_ci put_user(blue, &ublue[i])) 1848c2ecf20Sopenharmony_ci return -EFAULT; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sbusfb_ioctl_helper); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1958c2ecf20Sopenharmony_cistatic int fbiogetputcmap(struct fb_info *info, unsigned int cmd, unsigned long arg) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct fbcmap32 __user *argp = (void __user *)arg; 1988c2ecf20Sopenharmony_ci struct fbcmap __user *p = compat_alloc_user_space(sizeof(*p)); 1998c2ecf20Sopenharmony_ci u32 addr; 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = copy_in_user(p, argp, 2 * sizeof(int)); 2038c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->red); 2048c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->red); 2058c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->green); 2068c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->green); 2078c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->blue); 2088c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->blue); 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci return -EFAULT; 2118c2ecf20Sopenharmony_ci return info->fbops->fb_ioctl(info, 2128c2ecf20Sopenharmony_ci (cmd == FBIOPUTCMAP32) ? 2138c2ecf20Sopenharmony_ci FBIOPUTCMAP_SPARC : FBIOGETCMAP_SPARC, 2148c2ecf20Sopenharmony_ci (unsigned long)p); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int fbiogscursor(struct fb_info *info, unsigned long arg) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct fbcursor __user *p = compat_alloc_user_space(sizeof(*p)); 2208c2ecf20Sopenharmony_ci struct fbcursor32 __user *argp = (void __user *)arg; 2218c2ecf20Sopenharmony_ci compat_uptr_t addr; 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = copy_in_user(p, argp, 2258c2ecf20Sopenharmony_ci 2 * sizeof (short) + 2 * sizeof(struct fbcurpos)); 2268c2ecf20Sopenharmony_ci ret |= copy_in_user(&p->size, &argp->size, sizeof(struct fbcurpos)); 2278c2ecf20Sopenharmony_ci ret |= copy_in_user(&p->cmap, &argp->cmap, 2 * sizeof(int)); 2288c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->cmap.red); 2298c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->cmap.red); 2308c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->cmap.green); 2318c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->cmap.green); 2328c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->cmap.blue); 2338c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->cmap.blue); 2348c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->mask); 2358c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->mask); 2368c2ecf20Sopenharmony_ci ret |= get_user(addr, &argp->image); 2378c2ecf20Sopenharmony_ci ret |= put_user(compat_ptr(addr), &p->image); 2388c2ecf20Sopenharmony_ci if (ret) 2398c2ecf20Sopenharmony_ci return -EFAULT; 2408c2ecf20Sopenharmony_ci return info->fbops->fb_ioctl(info, FBIOSCURSOR, (unsigned long)p); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint sbusfb_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci switch (cmd) { 2468c2ecf20Sopenharmony_ci case FBIOGTYPE: 2478c2ecf20Sopenharmony_ci case FBIOSATTR: 2488c2ecf20Sopenharmony_ci case FBIOGATTR: 2498c2ecf20Sopenharmony_ci case FBIOSVIDEO: 2508c2ecf20Sopenharmony_ci case FBIOGVIDEO: 2518c2ecf20Sopenharmony_ci case FBIOGCURSOR32: /* This is not implemented yet. 2528c2ecf20Sopenharmony_ci Later it should be converted... */ 2538c2ecf20Sopenharmony_ci case FBIOSCURPOS: 2548c2ecf20Sopenharmony_ci case FBIOGCURPOS: 2558c2ecf20Sopenharmony_ci case FBIOGCURMAX: 2568c2ecf20Sopenharmony_ci return info->fbops->fb_ioctl(info, cmd, arg); 2578c2ecf20Sopenharmony_ci case FBIOPUTCMAP32: 2588c2ecf20Sopenharmony_ci return fbiogetputcmap(info, cmd, arg); 2598c2ecf20Sopenharmony_ci case FBIOGETCMAP32: 2608c2ecf20Sopenharmony_ci return fbiogetputcmap(info, cmd, arg); 2618c2ecf20Sopenharmony_ci case FBIOSCURSOR32: 2628c2ecf20Sopenharmony_ci return fbiogscursor(info, arg); 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sbusfb_compat_ioctl); 2688c2ecf20Sopenharmony_ci#endif 269