162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* sbuslib.c: Helper library for SBUS framebuffer drivers. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2003 David S. Miller (davem@redhat.com) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/compat.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/fb.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/fbio.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "sbuslib.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid sbusfb_fill_var(struct fb_var_screeninfo *var, struct device_node *dp, 2162306a36Sopenharmony_ci int bpp) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci memset(var, 0, sizeof(*var)); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci var->xres = of_getintprop_default(dp, "width", 1152); 2662306a36Sopenharmony_ci var->yres = of_getintprop_default(dp, "height", 900); 2762306a36Sopenharmony_ci var->xres_virtual = var->xres; 2862306a36Sopenharmony_ci var->yres_virtual = var->yres; 2962306a36Sopenharmony_ci var->bits_per_pixel = bpp; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciEXPORT_SYMBOL(sbusfb_fill_var); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned long sbusfb_mmapsize(long size, unsigned long fbsize) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci if (size == SBUS_MMAP_EMPTY) return 0; 3762306a36Sopenharmony_ci if (size >= 0) return size; 3862306a36Sopenharmony_ci return fbsize * (-size); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint sbusfb_mmap_helper(struct sbus_mmap_map *map, 4262306a36Sopenharmony_ci unsigned long physbase, 4362306a36Sopenharmony_ci unsigned long fbsize, 4462306a36Sopenharmony_ci unsigned long iospace, 4562306a36Sopenharmony_ci struct vm_area_struct *vma) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci unsigned int size, page, r, map_size; 4862306a36Sopenharmony_ci unsigned long map_offset = 0; 4962306a36Sopenharmony_ci unsigned long off; 5062306a36Sopenharmony_ci int i; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci size = vma->vm_end - vma->vm_start; 5662306a36Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 5762306a36Sopenharmony_ci return -EINVAL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci off = vma->vm_pgoff << PAGE_SHIFT; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Each page, see which map applies */ 6662306a36Sopenharmony_ci for (page = 0; page < size; ){ 6762306a36Sopenharmony_ci map_size = 0; 6862306a36Sopenharmony_ci for (i = 0; map[i].size; i++) 6962306a36Sopenharmony_ci if (map[i].voff == off+page) { 7062306a36Sopenharmony_ci map_size = sbusfb_mmapsize(map[i].size, fbsize); 7162306a36Sopenharmony_ci#ifdef __sparc_v9__ 7262306a36Sopenharmony_ci#define POFF_MASK (PAGE_MASK|0x1UL) 7362306a36Sopenharmony_ci#else 7462306a36Sopenharmony_ci#define POFF_MASK (PAGE_MASK) 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci map_offset = (physbase + map[i].poff) & POFF_MASK; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci if (!map_size) { 8062306a36Sopenharmony_ci page += PAGE_SIZE; 8162306a36Sopenharmony_ci continue; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci if (page + map_size > size) 8462306a36Sopenharmony_ci map_size = size - page; 8562306a36Sopenharmony_ci r = io_remap_pfn_range(vma, 8662306a36Sopenharmony_ci vma->vm_start + page, 8762306a36Sopenharmony_ci MK_IOSPACE_PFN(iospace, 8862306a36Sopenharmony_ci map_offset >> PAGE_SHIFT), 8962306a36Sopenharmony_ci map_size, 9062306a36Sopenharmony_ci vma->vm_page_prot); 9162306a36Sopenharmony_ci if (r) 9262306a36Sopenharmony_ci return -EAGAIN; 9362306a36Sopenharmony_ci page += map_size; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(sbusfb_mmap_helper); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg, 10162306a36Sopenharmony_ci struct fb_info *info, 10262306a36Sopenharmony_ci int type, int fb_depth, unsigned long fb_size) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci switch(cmd) { 10562306a36Sopenharmony_ci case FBIOGTYPE: { 10662306a36Sopenharmony_ci struct fbtype __user *f = (struct fbtype __user *) arg; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (put_user(type, &f->fb_type) || 10962306a36Sopenharmony_ci put_user(info->var.yres, &f->fb_height) || 11062306a36Sopenharmony_ci put_user(info->var.xres, &f->fb_width) || 11162306a36Sopenharmony_ci put_user(fb_depth, &f->fb_depth) || 11262306a36Sopenharmony_ci put_user(0, &f->fb_cmsize) || 11362306a36Sopenharmony_ci put_user(fb_size, &f->fb_cmsize)) 11462306a36Sopenharmony_ci return -EFAULT; 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci case FBIOPUTCMAP_SPARC: { 11862306a36Sopenharmony_ci struct fbcmap __user *c = (struct fbcmap __user *) arg; 11962306a36Sopenharmony_ci struct fb_cmap cmap; 12062306a36Sopenharmony_ci u16 red, green, blue; 12162306a36Sopenharmony_ci u8 red8, green8, blue8; 12262306a36Sopenharmony_ci unsigned char __user *ured; 12362306a36Sopenharmony_ci unsigned char __user *ugreen; 12462306a36Sopenharmony_ci unsigned char __user *ublue; 12562306a36Sopenharmony_ci unsigned int index, count, i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (get_user(index, &c->index) || 12862306a36Sopenharmony_ci get_user(count, &c->count) || 12962306a36Sopenharmony_ci get_user(ured, &c->red) || 13062306a36Sopenharmony_ci get_user(ugreen, &c->green) || 13162306a36Sopenharmony_ci get_user(ublue, &c->blue)) 13262306a36Sopenharmony_ci return -EFAULT; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci cmap.len = 1; 13562306a36Sopenharmony_ci cmap.red = &red; 13662306a36Sopenharmony_ci cmap.green = &green; 13762306a36Sopenharmony_ci cmap.blue = &blue; 13862306a36Sopenharmony_ci cmap.transp = NULL; 13962306a36Sopenharmony_ci for (i = 0; i < count; i++) { 14062306a36Sopenharmony_ci int err; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (get_user(red8, &ured[i]) || 14362306a36Sopenharmony_ci get_user(green8, &ugreen[i]) || 14462306a36Sopenharmony_ci get_user(blue8, &ublue[i])) 14562306a36Sopenharmony_ci return -EFAULT; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci red = red8 << 8; 14862306a36Sopenharmony_ci green = green8 << 8; 14962306a36Sopenharmony_ci blue = blue8 << 8; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci cmap.start = index + i; 15262306a36Sopenharmony_ci err = fb_set_cmap(&cmap, info); 15362306a36Sopenharmony_ci if (err) 15462306a36Sopenharmony_ci return err; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci case FBIOGETCMAP_SPARC: { 15962306a36Sopenharmony_ci struct fbcmap __user *c = (struct fbcmap __user *) arg; 16062306a36Sopenharmony_ci unsigned char __user *ured; 16162306a36Sopenharmony_ci unsigned char __user *ugreen; 16262306a36Sopenharmony_ci unsigned char __user *ublue; 16362306a36Sopenharmony_ci struct fb_cmap *cmap = &info->cmap; 16462306a36Sopenharmony_ci unsigned int index, count, i; 16562306a36Sopenharmony_ci u8 red, green, blue; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (get_user(index, &c->index) || 16862306a36Sopenharmony_ci get_user(count, &c->count) || 16962306a36Sopenharmony_ci get_user(ured, &c->red) || 17062306a36Sopenharmony_ci get_user(ugreen, &c->green) || 17162306a36Sopenharmony_ci get_user(ublue, &c->blue)) 17262306a36Sopenharmony_ci return -EFAULT; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (index > cmap->len || count > cmap->len - index) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 17862306a36Sopenharmony_ci red = cmap->red[index + i] >> 8; 17962306a36Sopenharmony_ci green = cmap->green[index + i] >> 8; 18062306a36Sopenharmony_ci blue = cmap->blue[index + i] >> 8; 18162306a36Sopenharmony_ci if (put_user(red, &ured[i]) || 18262306a36Sopenharmony_ci put_user(green, &ugreen[i]) || 18362306a36Sopenharmony_ci put_user(blue, &ublue[i])) 18462306a36Sopenharmony_ci return -EFAULT; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ciEXPORT_SYMBOL(sbusfb_ioctl_helper); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 19562306a36Sopenharmony_ciint sbusfb_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci switch (cmd) { 19862306a36Sopenharmony_ci case FBIOGTYPE: 19962306a36Sopenharmony_ci case FBIOSATTR: 20062306a36Sopenharmony_ci case FBIOGATTR: 20162306a36Sopenharmony_ci case FBIOSVIDEO: 20262306a36Sopenharmony_ci case FBIOGVIDEO: 20362306a36Sopenharmony_ci case FBIOSCURSOR32: 20462306a36Sopenharmony_ci case FBIOGCURSOR32: /* This is not implemented yet. 20562306a36Sopenharmony_ci Later it should be converted... */ 20662306a36Sopenharmony_ci case FBIOSCURPOS: 20762306a36Sopenharmony_ci case FBIOGCURPOS: 20862306a36Sopenharmony_ci case FBIOGCURMAX: 20962306a36Sopenharmony_ci return info->fbops->fb_ioctl(info, cmd, arg); 21062306a36Sopenharmony_ci case FBIOPUTCMAP32: 21162306a36Sopenharmony_ci case FBIOPUTCMAP_SPARC: { 21262306a36Sopenharmony_ci struct fbcmap32 c; 21362306a36Sopenharmony_ci struct fb_cmap cmap; 21462306a36Sopenharmony_ci u16 red, green, blue; 21562306a36Sopenharmony_ci u8 red8, green8, blue8; 21662306a36Sopenharmony_ci unsigned char __user *ured; 21762306a36Sopenharmony_ci unsigned char __user *ugreen; 21862306a36Sopenharmony_ci unsigned char __user *ublue; 21962306a36Sopenharmony_ci unsigned int i; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (copy_from_user(&c, compat_ptr(arg), sizeof(c))) 22262306a36Sopenharmony_ci return -EFAULT; 22362306a36Sopenharmony_ci ured = compat_ptr(c.red); 22462306a36Sopenharmony_ci ugreen = compat_ptr(c.green); 22562306a36Sopenharmony_ci ublue = compat_ptr(c.blue); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci cmap.len = 1; 22862306a36Sopenharmony_ci cmap.red = &red; 22962306a36Sopenharmony_ci cmap.green = &green; 23062306a36Sopenharmony_ci cmap.blue = &blue; 23162306a36Sopenharmony_ci cmap.transp = NULL; 23262306a36Sopenharmony_ci for (i = 0; i < c.count; i++) { 23362306a36Sopenharmony_ci int err; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (get_user(red8, &ured[i]) || 23662306a36Sopenharmony_ci get_user(green8, &ugreen[i]) || 23762306a36Sopenharmony_ci get_user(blue8, &ublue[i])) 23862306a36Sopenharmony_ci return -EFAULT; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci red = red8 << 8; 24162306a36Sopenharmony_ci green = green8 << 8; 24262306a36Sopenharmony_ci blue = blue8 << 8; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci cmap.start = c.index + i; 24562306a36Sopenharmony_ci err = fb_set_cmap(&cmap, info); 24662306a36Sopenharmony_ci if (err) 24762306a36Sopenharmony_ci return err; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci case FBIOGETCMAP32: { 25262306a36Sopenharmony_ci struct fbcmap32 c; 25362306a36Sopenharmony_ci unsigned char __user *ured; 25462306a36Sopenharmony_ci unsigned char __user *ugreen; 25562306a36Sopenharmony_ci unsigned char __user *ublue; 25662306a36Sopenharmony_ci struct fb_cmap *cmap = &info->cmap; 25762306a36Sopenharmony_ci unsigned int index, i; 25862306a36Sopenharmony_ci u8 red, green, blue; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (copy_from_user(&c, compat_ptr(arg), sizeof(c))) 26162306a36Sopenharmony_ci return -EFAULT; 26262306a36Sopenharmony_ci index = c.index; 26362306a36Sopenharmony_ci ured = compat_ptr(c.red); 26462306a36Sopenharmony_ci ugreen = compat_ptr(c.green); 26562306a36Sopenharmony_ci ublue = compat_ptr(c.blue); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (index > cmap->len || c.count > cmap->len - index) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < c.count; i++) { 27162306a36Sopenharmony_ci red = cmap->red[index + i] >> 8; 27262306a36Sopenharmony_ci green = cmap->green[index + i] >> 8; 27362306a36Sopenharmony_ci blue = cmap->blue[index + i] >> 8; 27462306a36Sopenharmony_ci if (put_user(red, &ured[i]) || 27562306a36Sopenharmony_ci put_user(green, &ugreen[i]) || 27662306a36Sopenharmony_ci put_user(blue, &ublue[i])) 27762306a36Sopenharmony_ci return -EFAULT; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci return -ENOIOCTLCMD; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ciEXPORT_SYMBOL(sbusfb_compat_ioctl); 28662306a36Sopenharmony_ci#endif 287