[go: nahoru, domu]

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