Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/video/fbcmap.c -- Colormap handling for frame buffer devices | |
3 | * | |
4 | * Created 15 Jun 1997 by Geert Uytterhoeven | |
5 | * | |
6 | * 2001 - Documented with DocBook | |
7 | * - Brad Douglas <brad@neruo.com> | |
8 | * | |
9 | * This file is subject to the terms and conditions of the GNU General Public | |
10 | * License. See the file COPYING in the main directory of this archive for | |
11 | * more details. | |
12 | */ | |
13 | ||
14 | #include <linux/string.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/tty.h> | |
17 | #include <linux/fb.h> | |
18 | #include <linux/slab.h> | |
19 | ||
20 | #include <asm/uaccess.h> | |
21 | ||
22 | static u16 red2[] = { | |
23 | 0x0000, 0xaaaa | |
24 | }; | |
25 | static u16 green2[] = { | |
26 | 0x0000, 0xaaaa | |
27 | }; | |
28 | static u16 blue2[] = { | |
29 | 0x0000, 0xaaaa | |
30 | }; | |
31 | ||
32 | static u16 red4[] = { | |
33 | 0x0000, 0xaaaa, 0x5555, 0xffff | |
34 | }; | |
35 | static u16 green4[] = { | |
36 | 0x0000, 0xaaaa, 0x5555, 0xffff | |
37 | }; | |
38 | static u16 blue4[] = { | |
39 | 0x0000, 0xaaaa, 0x5555, 0xffff | |
40 | }; | |
41 | ||
42 | static u16 red8[] = { | |
43 | 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa | |
44 | }; | |
45 | static u16 green8[] = { | |
46 | 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa | |
47 | }; | |
48 | static u16 blue8[] = { | |
49 | 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa | |
50 | }; | |
51 | ||
52 | static u16 red16[] = { | |
53 | 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa, | |
54 | 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff | |
55 | }; | |
56 | static u16 green16[] = { | |
57 | 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa, | |
58 | 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff | |
59 | }; | |
60 | static u16 blue16[] = { | |
61 | 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, | |
62 | 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff | |
63 | }; | |
64 | ||
65 | static struct fb_cmap default_2_colors = { | |
66 | 0, 2, red2, green2, blue2, NULL | |
67 | }; | |
68 | static struct fb_cmap default_8_colors = { | |
69 | 0, 8, red8, green8, blue8, NULL | |
70 | }; | |
71 | static struct fb_cmap default_4_colors = { | |
72 | 0, 4, red4, green4, blue4, NULL | |
73 | }; | |
74 | static struct fb_cmap default_16_colors = { | |
75 | 0, 16, red16, green16, blue16, NULL | |
76 | }; | |
77 | ||
78 | ||
79 | /** | |
80 | * fb_alloc_cmap - allocate a colormap | |
81 | * @cmap: frame buffer colormap structure | |
82 | * @len: length of @cmap | |
83 | * @transp: boolean, 1 if there is transparency, 0 otherwise | |
84 | * | |
85 | * Allocates memory for a colormap @cmap. @len is the | |
86 | * number of entries in the palette. | |
87 | * | |
88 | * Returns -1 errno on error, or zero on success. | |
89 | * | |
90 | */ | |
91 | ||
92 | int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) | |
93 | { | |
94 | int size = len*sizeof(u16); | |
95 | ||
96 | if (cmap->len != len) { | |
97 | fb_dealloc_cmap(cmap); | |
98 | if (!len) | |
99 | return 0; | |
100 | if (!(cmap->red = kmalloc(size, GFP_ATOMIC))) | |
101 | goto fail; | |
102 | if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) | |
103 | goto fail; | |
104 | if (!(cmap->blue = kmalloc(size, GFP_ATOMIC))) | |
105 | goto fail; | |
106 | if (transp) { | |
107 | if (!(cmap->transp = kmalloc(size, GFP_ATOMIC))) | |
108 | goto fail; | |
109 | } else | |
110 | cmap->transp = NULL; | |
111 | } | |
112 | cmap->start = 0; | |
113 | cmap->len = len; | |
114 | fb_copy_cmap(fb_default_cmap(len), cmap); | |
115 | return 0; | |
116 | ||
117 | fail: | |
118 | fb_dealloc_cmap(cmap); | |
119 | return -1; | |
120 | } | |
121 | ||
122 | /** | |
123 | * fb_dealloc_cmap - deallocate a colormap | |
124 | * @cmap: frame buffer colormap structure | |
125 | * | |
126 | * Deallocates a colormap that was previously allocated with | |
127 | * fb_alloc_cmap(). | |
128 | * | |
129 | */ | |
130 | ||
131 | void fb_dealloc_cmap(struct fb_cmap *cmap) | |
132 | { | |
133 | kfree(cmap->red); | |
134 | kfree(cmap->green); | |
135 | kfree(cmap->blue); | |
136 | kfree(cmap->transp); | |
137 | ||
138 | cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; | |
139 | cmap->len = 0; | |
140 | } | |
141 | ||
142 | /** | |
143 | * fb_copy_cmap - copy a colormap | |
144 | * @from: frame buffer colormap structure | |
145 | * @to: frame buffer colormap structure | |
146 | * | |
147 | * Copy contents of colormap from @from to @to. | |
148 | */ | |
149 | ||
150 | int fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to) | |
151 | { | |
152 | int tooff = 0, fromoff = 0; | |
153 | int size; | |
154 | ||
155 | if (to->start > from->start) | |
156 | fromoff = to->start - from->start; | |
157 | else | |
158 | tooff = from->start - to->start; | |
159 | size = to->len - tooff; | |
160 | if (size > (int) (from->len - fromoff)) | |
161 | size = from->len - fromoff; | |
162 | if (size <= 0) | |
163 | return -EINVAL; | |
164 | size *= sizeof(u16); | |
165 | ||
166 | memcpy(to->red+tooff, from->red+fromoff, size); | |
167 | memcpy(to->green+tooff, from->green+fromoff, size); | |
168 | memcpy(to->blue+tooff, from->blue+fromoff, size); | |
169 | if (from->transp && to->transp) | |
170 | memcpy(to->transp+tooff, from->transp+fromoff, size); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | int fb_cmap_to_user(struct fb_cmap *from, struct fb_cmap_user *to) | |
175 | { | |
176 | int tooff = 0, fromoff = 0; | |
177 | int size; | |
178 | ||
179 | if (to->start > from->start) | |
180 | fromoff = to->start - from->start; | |
181 | else | |
182 | tooff = from->start - to->start; | |
183 | size = to->len - tooff; | |
184 | if (size > (int) (from->len - fromoff)) | |
185 | size = from->len - fromoff; | |
186 | if (size <= 0) | |
187 | return -EINVAL; | |
188 | size *= sizeof(u16); | |
189 | ||
190 | if (copy_to_user(to->red+tooff, from->red+fromoff, size)) | |
191 | return -EFAULT; | |
192 | if (copy_to_user(to->green+tooff, from->green+fromoff, size)) | |
193 | return -EFAULT; | |
194 | if (copy_to_user(to->blue+tooff, from->blue+fromoff, size)) | |
195 | return -EFAULT; | |
196 | if (from->transp && to->transp) | |
197 | if (copy_to_user(to->transp+tooff, from->transp+fromoff, size)) | |
198 | return -EFAULT; | |
199 | return 0; | |
200 | } | |
201 | ||
202 | /** | |
203 | * fb_set_cmap - set the colormap | |
204 | * @cmap: frame buffer colormap structure | |
205 | * @info: frame buffer info structure | |
206 | * | |
207 | * Sets the colormap @cmap for a screen of device @info. | |
208 | * | |
209 | * Returns negative errno on error, or zero on success. | |
210 | * | |
211 | */ | |
212 | ||
213 | int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info) | |
214 | { | |
215 | int i, start; | |
216 | u16 *red, *green, *blue, *transp; | |
217 | u_int hred, hgreen, hblue, htransp = 0xffff; | |
218 | ||
219 | red = cmap->red; | |
220 | green = cmap->green; | |
221 | blue = cmap->blue; | |
222 | transp = cmap->transp; | |
223 | start = cmap->start; | |
224 | ||
71494376 BH |
225 | if (start < 0 || (!info->fbops->fb_setcolreg && |
226 | !info->fbops->fb_setcmap)) | |
1da177e4 | 227 | return -EINVAL; |
71494376 BH |
228 | if (info->fbops->fb_setcmap) |
229 | return info->fbops->fb_setcmap(cmap, info); | |
1da177e4 LT |
230 | for (i = 0; i < cmap->len; i++) { |
231 | hred = *red++; | |
232 | hgreen = *green++; | |
233 | hblue = *blue++; | |
234 | if (transp) | |
235 | htransp = *transp++; | |
236 | if (info->fbops->fb_setcolreg(start++, | |
237 | hred, hgreen, hblue, htransp, | |
238 | info)) | |
239 | break; | |
240 | } | |
241 | return 0; | |
242 | } | |
243 | ||
244 | int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info) | |
245 | { | |
246 | int i, start; | |
247 | u16 __user *red, *green, *blue, *transp; | |
248 | u_int hred, hgreen, hblue, htransp = 0xffff; | |
249 | ||
250 | red = cmap->red; | |
251 | green = cmap->green; | |
252 | blue = cmap->blue; | |
253 | transp = cmap->transp; | |
254 | start = cmap->start; | |
255 | ||
71494376 BH |
256 | if (start < 0 || (!info->fbops->fb_setcolreg && |
257 | !info->fbops->fb_setcmap)) | |
1da177e4 | 258 | return -EINVAL; |
71494376 BH |
259 | |
260 | /* If we can batch, do it */ | |
261 | if (info->fbops->fb_setcmap && cmap->len > 1) { | |
262 | struct fb_cmap umap; | |
263 | int size = cmap->len * sizeof(u16); | |
264 | int rc; | |
265 | ||
266 | memset(&umap, 0, sizeof(struct fb_cmap)); | |
267 | rc = fb_alloc_cmap(&umap, cmap->len, transp != NULL); | |
268 | if (rc) | |
269 | return rc; | |
270 | if (copy_from_user(umap.red, red, size) || | |
271 | copy_from_user(umap.green, green, size) || | |
272 | copy_from_user(umap.blue, blue, size) || | |
273 | (transp && copy_from_user(umap.transp, transp, size))) { | |
274 | rc = -EFAULT; | |
275 | } | |
276 | umap.start = start; | |
277 | if (rc == 0) | |
278 | rc = info->fbops->fb_setcmap(&umap, info); | |
279 | fb_dealloc_cmap(&umap); | |
280 | return rc; | |
281 | } | |
282 | ||
1da177e4 LT |
283 | for (i = 0; i < cmap->len; i++, red++, blue++, green++) { |
284 | if (get_user(hred, red) || | |
285 | get_user(hgreen, green) || | |
286 | get_user(hblue, blue) || | |
287 | (transp && get_user(htransp, transp))) | |
288 | return -EFAULT; | |
289 | if (info->fbops->fb_setcolreg(start++, | |
290 | hred, hgreen, hblue, htransp, | |
291 | info)) | |
292 | return 0; | |
293 | if (transp) | |
294 | transp++; | |
295 | } | |
296 | return 0; | |
297 | } | |
298 | ||
299 | /** | |
300 | * fb_default_cmap - get default colormap | |
301 | * @len: size of palette for a depth | |
302 | * | |
303 | * Gets the default colormap for a specific screen depth. @len | |
304 | * is the size of the palette for a particular screen depth. | |
305 | * | |
306 | * Returns pointer to a frame buffer colormap structure. | |
307 | * | |
308 | */ | |
309 | ||
310 | struct fb_cmap *fb_default_cmap(int len) | |
311 | { | |
312 | if (len <= 2) | |
313 | return &default_2_colors; | |
314 | if (len <= 4) | |
315 | return &default_4_colors; | |
316 | if (len <= 8) | |
317 | return &default_8_colors; | |
318 | return &default_16_colors; | |
319 | } | |
320 | ||
321 | ||
322 | /** | |
323 | * fb_invert_cmaps - invert all defaults colormaps | |
324 | * | |
325 | * Invert all default colormaps. | |
326 | * | |
327 | */ | |
328 | ||
329 | void fb_invert_cmaps(void) | |
330 | { | |
331 | u_int i; | |
332 | ||
333 | for (i = 0; i < 2; i++) { | |
334 | red2[i] = ~red2[i]; | |
335 | green2[i] = ~green2[i]; | |
336 | blue2[i] = ~blue2[i]; | |
337 | } | |
338 | for (i = 0; i < 4; i++) { | |
339 | red4[i] = ~red4[i]; | |
340 | green4[i] = ~green4[i]; | |
341 | blue4[i] = ~blue4[i]; | |
342 | } | |
343 | for (i = 0; i < 8; i++) { | |
344 | red8[i] = ~red8[i]; | |
345 | green8[i] = ~green8[i]; | |
346 | blue8[i] = ~blue8[i]; | |
347 | } | |
348 | for (i = 0; i < 16; i++) { | |
349 | red16[i] = ~red16[i]; | |
350 | green16[i] = ~green16[i]; | |
351 | blue16[i] = ~blue16[i]; | |
352 | } | |
353 | } | |
354 | ||
355 | ||
356 | /* | |
357 | * Visible symbols for modules | |
358 | */ | |
359 | ||
360 | EXPORT_SYMBOL(fb_alloc_cmap); | |
361 | EXPORT_SYMBOL(fb_dealloc_cmap); | |
362 | EXPORT_SYMBOL(fb_copy_cmap); | |
363 | EXPORT_SYMBOL(fb_set_cmap); | |
364 | EXPORT_SYMBOL(fb_default_cmap); | |
365 | EXPORT_SYMBOL(fb_invert_cmaps); |