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 | |
20 | void 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 | |
32 | EXPORT_SYMBOL(sbusfb_fill_var); |
33 | |
34 | static 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 | |
41 | int 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(size: 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 | addr: vma->vm_start + page, |
88 | pfn: MK_IOSPACE_PFN(iospace, |
89 | map_offset >> PAGE_SHIFT), |
90 | size: map_size, |
91 | prot: vma->vm_page_prot); |
92 | if (r) |
93 | return -EAGAIN; |
94 | page += map_size; |
95 | } |
96 | |
97 | return 0; |
98 | } |
99 | EXPORT_SYMBOL(sbusfb_mmap_helper); |
100 | |
101 | int 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: &cmap, fb_info: 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 | } |
193 | EXPORT_SYMBOL(sbusfb_ioctl_helper); |
194 | |
195 | #ifdef CONFIG_COMPAT |
196 | int 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(to: &c, from: compat_ptr(uptr: arg), n: sizeof(c))) |
223 | return -EFAULT; |
224 | ured = compat_ptr(uptr: c.red); |
225 | ugreen = compat_ptr(uptr: c.green); |
226 | ublue = compat_ptr(uptr: 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: &cmap, fb_info: 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(to: &c, from: compat_ptr(uptr: arg), n: sizeof(c))) |
262 | return -EFAULT; |
263 | index = c.index; |
264 | ured = compat_ptr(uptr: c.red); |
265 | ugreen = compat_ptr(uptr: c.green); |
266 | ublue = compat_ptr(uptr: 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 | } |
286 | EXPORT_SYMBOL(sbusfb_compat_ioctl); |
287 | #endif |
288 | |