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 
fb_alloc_cmap(struct fb_cmap * cmap,int len,int transp)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 	if (cmap->red)
98 	    kfree(cmap->red);
99 	if (cmap->green)
100 	    kfree(cmap->green);
101 	if (cmap->blue)
102 	    kfree(cmap->blue);
103 	if (cmap->transp)
104 	    kfree(cmap->transp);
105 	cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
106 	cmap->len = 0;
107 	if (!len)
108 	    return 0;
109 	if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
110 	    return -1;
111 	if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
112 	    return -1;
113 	if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
114 	    return -1;
115 	if (transp) {
116 	    if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
117 		return -1;
118 	} else
119 	    cmap->transp = NULL;
120     }
121     cmap->start = 0;
122     cmap->len = len;
123     fb_copy_cmap(fb_default_cmap(len), cmap, 0);
124     return 0;
125 }
126 
127 
128 /**
129  *	fb_copy_cmap - copy a colormap
130  *	@from: frame buffer colormap structure
131  *	@to: frame buffer colormap structure
132  *	@fsfromto: determine copy method
133  *
134  *	Copy contents of colormap from @from to @to.
135  *
136  *	@fsfromto accepts the following integer parameters:
137  *	0: memcpy function
138  *	1: copy_from_user() function to copy from userspace
139  *	2: copy_to_user() function to copy to userspace
140  *
141  */
142 
fb_copy_cmap(struct fb_cmap * from,struct fb_cmap * to,int fsfromto)143 void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
144 {
145     int size;
146     int tooff = 0, fromoff = 0;
147 
148     if (to->start > from->start)
149 	fromoff = to->start-from->start;
150     else
151 	tooff = from->start-to->start;
152     size = to->len-tooff;
153     if (size > from->len-fromoff)
154 	size = from->len-fromoff;
155     if (size < 0)
156 	return;
157     size *= sizeof(u16);
158 
159     switch (fsfromto) {
160     case 0:
161 	memcpy(to->red+tooff, from->red+fromoff, size);
162 	memcpy(to->green+tooff, from->green+fromoff, size);
163 	memcpy(to->blue+tooff, from->blue+fromoff, size);
164 	if (from->transp && to->transp)
165 	    memcpy(to->transp+tooff, from->transp+fromoff, size);
166         break;
167     case 1:
168 	copy_from_user(to->red+tooff, from->red+fromoff, size);
169 	copy_from_user(to->green+tooff, from->green+fromoff, size);
170 	copy_from_user(to->blue+tooff, from->blue+fromoff, size);
171 	if (from->transp && to->transp)
172             copy_from_user(to->transp+tooff, from->transp+fromoff, size);
173 	break;
174     case 2:
175 	copy_to_user(to->red+tooff, from->red+fromoff, size);
176 	copy_to_user(to->green+tooff, from->green+fromoff, size);
177 	copy_to_user(to->blue+tooff, from->blue+fromoff, size);
178 	if (from->transp && to->transp)
179 	    copy_to_user(to->transp+tooff, from->transp+fromoff, size);
180 	break;
181     }
182 }
183 
184 
185 /**
186  *	fb_get_cmap - get a colormap
187  *	@cmap: frame buffer colormap
188  *	@kspc: boolean, 0 copy local, 1 put_user() function
189  *	@getcolreg: pointer to a function to get a color register
190  *	@info: frame buffer info structure
191  *
192  *	Get a colormap @cmap for a screen of device @info.
193  *
194  *	Returns negative errno on error, or zero on success.
195  *
196  */
197 
fb_get_cmap(struct fb_cmap * cmap,int kspc,int (* getcolreg)(u_int,u_int *,u_int *,u_int *,u_int *,struct fb_info *),struct fb_info * info)198 int fb_get_cmap(struct fb_cmap *cmap, int kspc,
199     	    	int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *,
200 				 struct fb_info *),
201 		struct fb_info *info)
202 {
203     int i, start;
204     u16 *red, *green, *blue, *transp;
205     u_int hred, hgreen, hblue, htransp;
206 
207     red = cmap->red;
208     green = cmap->green;
209     blue = cmap->blue;
210     transp = cmap->transp;
211     start = cmap->start;
212     if (start < 0)
213 	return -EINVAL;
214     for (i = 0; i < cmap->len; i++) {
215 	if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp, info))
216 	    return 0;
217 	if (kspc) {
218 	    *red = hred;
219 	    *green = hgreen;
220 	    *blue = hblue;
221 	    if (transp)
222 		*transp = htransp;
223 	} else {
224 	    put_user(hred, red);
225 	    put_user(hgreen, green);
226 	    put_user(hblue, blue);
227 	    if (transp)
228 		put_user(htransp, transp);
229 	}
230 	red++;
231 	green++;
232 	blue++;
233 	if (transp)
234 	    transp++;
235     }
236     return 0;
237 }
238 
239 
240 /**
241  *	fb_set_cmap - set the colormap
242  *	@cmap: frame buffer colormap structure
243  *	@kspc: boolean, 0 copy local, 1 get_user() function
244  *	@info: frame buffer info structure
245  *
246  *	Sets the colormap @cmap for a screen of device @info.
247  *
248  *	Returns negative errno on error, or zero on success.
249  *
250  */
251 
fb_set_cmap(struct fb_cmap * cmap,int kspc,int (* setcolreg)(u_int,u_int,u_int,u_int,u_int,struct fb_info *),struct fb_info * info)252 int fb_set_cmap(struct fb_cmap *cmap, int kspc,
253     	    	int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
254 				 struct fb_info *),
255 		struct fb_info *info)
256 {
257     int i, start;
258     u16 *red, *green, *blue, *transp;
259     u_int hred, hgreen, hblue, htransp;
260 
261     red = cmap->red;
262     green = cmap->green;
263     blue = cmap->blue;
264     transp = cmap->transp;
265     start = cmap->start;
266 
267     if (start < 0)
268 	return -EINVAL;
269     for (i = 0; i < cmap->len; i++) {
270 	if (kspc) {
271 	    hred = *red;
272 	    hgreen = *green;
273 	    hblue = *blue;
274 	    htransp = transp ? *transp : 0xffff;
275 	} else {
276 	    get_user(hred, red);
277 	    get_user(hgreen, green);
278 	    get_user(hblue, blue);
279 	    if (transp)
280 		get_user(htransp, transp);
281 	    else
282 		htransp = 0xffff;
283 	}
284 	red++;
285 	green++;
286 	blue++;
287 	if (transp)
288 	    transp++;
289 	if (setcolreg(start++, hred, hgreen, hblue, htransp, info))
290 	    return 0;
291     }
292     return 0;
293 }
294 
295 
296 /**
297  *	fb_default_cmap - get default colormap
298  *	@len: size of palette for a depth
299  *
300  *	Gets the default colormap for a specific screen depth.  @len
301  *	is the size of the palette for a particular screen depth.
302  *
303  *	Returns pointer to a frame buffer colormap structure.
304  *
305  */
306 
fb_default_cmap(int len)307 struct fb_cmap *fb_default_cmap(int len)
308 {
309     if (len <= 2)
310 	return &default_2_colors;
311     if (len <= 4)
312 	return &default_4_colors;
313     if (len <= 8)
314 	return &default_8_colors;
315     return &default_16_colors;
316 }
317 
318 
319 /**
320  *	fb_invert_cmaps - invert all defaults colormaps
321  *
322  *	Invert all default colormaps.
323  *
324  */
325 
fb_invert_cmaps(void)326 void fb_invert_cmaps(void)
327 {
328     u_int i;
329 
330     for (i = 0; i < 2; i++) {
331 	red2[i] = ~red2[i];
332 	green2[i] = ~green2[i];
333 	blue2[i] = ~blue2[i];
334     }
335     for (i = 0; i < 4; i++) {
336 	red4[i] = ~red4[i];
337 	green4[i] = ~green4[i];
338 	blue4[i] = ~blue4[i];
339     }
340     for (i = 0; i < 8; i++) {
341 	red8[i] = ~red8[i];
342 	green8[i] = ~green8[i];
343 	blue8[i] = ~blue8[i];
344     }
345     for (i = 0; i < 16; i++) {
346 	red16[i] = ~red16[i];
347 	green16[i] = ~green16[i];
348 	blue16[i] = ~blue16[i];
349     }
350 }
351 
352 
353     /*
354      *  Visible symbols for modules
355      */
356 
357 EXPORT_SYMBOL(fb_alloc_cmap);
358 EXPORT_SYMBOL(fb_copy_cmap);
359 EXPORT_SYMBOL(fb_get_cmap);
360 EXPORT_SYMBOL(fb_set_cmap);
361 EXPORT_SYMBOL(fb_default_cmap);
362 EXPORT_SYMBOL(fb_invert_cmaps);
363