1// SPDX-License-Identifier: GPL-2.0
2/* sbuslib.c: Helper library for SBUS framebuffer drivers.
3 *
4 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
5 */
6
7#include <linux/compat.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/string.h>
11#include <linux/fb.h>
12#include <linux/mm.h>
13#include <linux/uaccess.h>
14#include <linux/of.h>
15
16#include <asm/fbio.h>
17
18#include "sbuslib.h"
19
20void sbusfb_fill_var(struct fb_var_screeninfo *var, struct device_node *dp,
21		     int bpp)
22{
23	memset(var, 0, sizeof(*var));
24
25	var->xres = of_getintprop_default(dp, "width", 1152);
26	var->yres = of_getintprop_default(dp, "height", 900);
27	var->xres_virtual = var->xres;
28	var->yres_virtual = var->yres;
29	var->bits_per_pixel = bpp;
30}
31
32EXPORT_SYMBOL(sbusfb_fill_var);
33
34static unsigned long sbusfb_mmapsize(long size, unsigned long fbsize)
35{
36	if (size == SBUS_MMAP_EMPTY) return 0;
37	if (size >= 0) return size;
38	return fbsize * (-size);
39}
40
41int sbusfb_mmap_helper(struct sbus_mmap_map *map,
42		       unsigned long physbase,
43		       unsigned long fbsize,
44		       unsigned long iospace,
45		       struct vm_area_struct *vma)
46{
47	unsigned int size, page, r, map_size;
48	unsigned long map_offset = 0;
49	unsigned long off;
50	int i;
51
52	if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE)))
53		return -EINVAL;
54
55	size = vma->vm_end - vma->vm_start;
56	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
57		return -EINVAL;
58
59	off = vma->vm_pgoff << PAGE_SHIFT;
60
61	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
62
63	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
64	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
65
66	/* Each page, see which map applies */
67	for (page = 0; page < size; ){
68		map_size = 0;
69		for (i = 0; map[i].size; i++)
70			if (map[i].voff == off+page) {
71				map_size = sbusfb_mmapsize(map[i].size, fbsize);
72#ifdef __sparc_v9__
73#define POFF_MASK	(PAGE_MASK|0x1UL)
74#else
75#define POFF_MASK	(PAGE_MASK)
76#endif
77				map_offset = (physbase + map[i].poff) & POFF_MASK;
78				break;
79			}
80		if (!map_size) {
81			page += PAGE_SIZE;
82			continue;
83		}
84		if (page + map_size > size)
85			map_size = size - page;
86		r = io_remap_pfn_range(vma,
87					vma->vm_start + page,
88					MK_IOSPACE_PFN(iospace,
89						map_offset >> PAGE_SHIFT),
90					map_size,
91					vma->vm_page_prot);
92		if (r)
93			return -EAGAIN;
94		page += map_size;
95	}
96
97	return 0;
98}
99EXPORT_SYMBOL(sbusfb_mmap_helper);
100
101int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
102			struct fb_info *info,
103			int type, int fb_depth, unsigned long fb_size)
104{
105	switch(cmd) {
106	case FBIOGTYPE: {
107		struct fbtype __user *f = (struct fbtype __user *) arg;
108
109		if (put_user(type, &f->fb_type) ||
110		    put_user(info->var.yres, &f->fb_height) ||
111		    put_user(info->var.xres, &f->fb_width) ||
112		    put_user(fb_depth, &f->fb_depth) ||
113		    put_user(0, &f->fb_cmsize) ||
114		    put_user(fb_size, &f->fb_cmsize))
115			return -EFAULT;
116		return 0;
117	}
118	case FBIOPUTCMAP_SPARC: {
119		struct fbcmap __user *c = (struct fbcmap __user *) arg;
120		struct fb_cmap cmap;
121		u16 red, green, blue;
122		u8 red8, green8, blue8;
123		unsigned char __user *ured;
124		unsigned char __user *ugreen;
125		unsigned char __user *ublue;
126		unsigned int index, count, i;
127
128		if (get_user(index, &c->index) ||
129		    get_user(count, &c->count) ||
130		    get_user(ured, &c->red) ||
131		    get_user(ugreen, &c->green) ||
132		    get_user(ublue, &c->blue))
133			return -EFAULT;
134
135		cmap.len = 1;
136		cmap.red = &red;
137		cmap.green = &green;
138		cmap.blue = &blue;
139		cmap.transp = NULL;
140		for (i = 0; i < count; i++) {
141			int err;
142
143			if (get_user(red8, &ured[i]) ||
144			    get_user(green8, &ugreen[i]) ||
145			    get_user(blue8, &ublue[i]))
146				return -EFAULT;
147
148			red = red8 << 8;
149			green = green8 << 8;
150			blue = blue8 << 8;
151
152			cmap.start = index + i;
153			err = fb_set_cmap(&cmap, info);
154			if (err)
155				return err;
156		}
157		return 0;
158	}
159	case FBIOGETCMAP_SPARC: {
160		struct fbcmap __user *c = (struct fbcmap __user *) arg;
161		unsigned char __user *ured;
162		unsigned char __user *ugreen;
163		unsigned char __user *ublue;
164		struct fb_cmap *cmap = &info->cmap;
165		unsigned int index, count, i;
166		u8 red, green, blue;
167
168		if (get_user(index, &c->index) ||
169		    get_user(count, &c->count) ||
170		    get_user(ured, &c->red) ||
171		    get_user(ugreen, &c->green) ||
172		    get_user(ublue, &c->blue))
173			return -EFAULT;
174
175		if (index > cmap->len || count > cmap->len - index)
176			return -EINVAL;
177
178		for (i = 0; i < count; i++) {
179			red = cmap->red[index + i] >> 8;
180			green = cmap->green[index + i] >> 8;
181			blue = cmap->blue[index + i] >> 8;
182			if (put_user(red, &ured[i]) ||
183			    put_user(green, &ugreen[i]) ||
184			    put_user(blue, &ublue[i]))
185				return -EFAULT;
186		}
187		return 0;
188	}
189	default:
190		return -EINVAL;
191	}
192}
193EXPORT_SYMBOL(sbusfb_ioctl_helper);
194
195#ifdef CONFIG_COMPAT
196int sbusfb_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
197{
198	switch (cmd) {
199	case FBIOGTYPE:
200	case FBIOSATTR:
201	case FBIOGATTR:
202	case FBIOSVIDEO:
203	case FBIOGVIDEO:
204	case FBIOSCURSOR32:
205	case FBIOGCURSOR32:	/* This is not implemented yet.
206				   Later it should be converted... */
207	case FBIOSCURPOS:
208	case FBIOGCURPOS:
209	case FBIOGCURMAX:
210		return info->fbops->fb_ioctl(info, cmd, arg);
211	case FBIOPUTCMAP32:
212	case FBIOPUTCMAP_SPARC: {
213		struct fbcmap32 c;
214		struct fb_cmap cmap;
215		u16 red, green, blue;
216		u8 red8, green8, blue8;
217		unsigned char __user *ured;
218		unsigned char __user *ugreen;
219		unsigned char __user *ublue;
220		unsigned int i;
221
222		if (copy_from_user(&c, compat_ptr(arg), sizeof(c)))
223			return -EFAULT;
224		ured = compat_ptr(c.red);
225		ugreen = compat_ptr(c.green);
226		ublue = compat_ptr(c.blue);
227
228		cmap.len = 1;
229		cmap.red = &red;
230		cmap.green = &green;
231		cmap.blue = &blue;
232		cmap.transp = NULL;
233		for (i = 0; i < c.count; i++) {
234			int err;
235
236			if (get_user(red8, &ured[i]) ||
237			    get_user(green8, &ugreen[i]) ||
238			    get_user(blue8, &ublue[i]))
239				return -EFAULT;
240
241			red = red8 << 8;
242			green = green8 << 8;
243			blue = blue8 << 8;
244
245			cmap.start = c.index + i;
246			err = fb_set_cmap(&cmap, info);
247			if (err)
248				return err;
249		}
250		return 0;
251	}
252	case FBIOGETCMAP32: {
253		struct fbcmap32 c;
254		unsigned char __user *ured;
255		unsigned char __user *ugreen;
256		unsigned char __user *ublue;
257		struct fb_cmap *cmap = &info->cmap;
258		unsigned int index, i;
259		u8 red, green, blue;
260
261		if (copy_from_user(&c, compat_ptr(arg), sizeof(c)))
262			return -EFAULT;
263		index = c.index;
264		ured = compat_ptr(c.red);
265		ugreen = compat_ptr(c.green);
266		ublue = compat_ptr(c.blue);
267
268		if (index > cmap->len || c.count > cmap->len - index)
269			return -EINVAL;
270
271		for (i = 0; i < c.count; i++) {
272			red = cmap->red[index + i] >> 8;
273			green = cmap->green[index + i] >> 8;
274			blue = cmap->blue[index + i] >> 8;
275			if (put_user(red, &ured[i]) ||
276			    put_user(green, &ugreen[i]) ||
277			    put_user(blue, &ublue[i]))
278				return -EFAULT;
279		}
280		return 0;
281	}
282	default:
283		return -ENOIOCTLCMD;
284	}
285}
286EXPORT_SYMBOL(sbusfb_compat_ioctl);
287#endif
288