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