Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fbcmap.c
Go to the documentation of this file.
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 <[email protected]>
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/fb.h>
17 #include <linux/slab.h>
18 #include <linux/uaccess.h>
19 
20 static u16 red2[] __read_mostly = {
21  0x0000, 0xaaaa
22 };
23 static u16 green2[] __read_mostly = {
24  0x0000, 0xaaaa
25 };
26 static u16 blue2[] __read_mostly = {
27  0x0000, 0xaaaa
28 };
29 
30 static u16 red4[] __read_mostly = {
31  0x0000, 0xaaaa, 0x5555, 0xffff
32 };
33 static u16 green4[] __read_mostly = {
34  0x0000, 0xaaaa, 0x5555, 0xffff
35 };
36 static u16 blue4[] __read_mostly = {
37  0x0000, 0xaaaa, 0x5555, 0xffff
38 };
39 
40 static u16 red8[] __read_mostly = {
41  0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
42 };
43 static u16 green8[] __read_mostly = {
44  0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa
45 };
46 static u16 blue8[] __read_mostly = {
47  0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
48 };
49 
50 static u16 red16[] __read_mostly = {
51  0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
52  0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
53 };
54 static u16 green16[] __read_mostly = {
55  0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa,
56  0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
57 };
58 static u16 blue16[] __read_mostly = {
59  0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
60  0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
61 };
62 
63 static const struct fb_cmap default_2_colors = {
64  .len=2, .red=red2, .green=green2, .blue=blue2
65 };
66 static const struct fb_cmap default_8_colors = {
67  .len=8, .red=red8, .green=green8, .blue=blue8
68 };
69 static const struct fb_cmap default_4_colors = {
70  .len=4, .red=red4, .green=green4, .blue=blue4
71 };
72 static const struct fb_cmap default_16_colors = {
73  .len=16, .red=red16, .green=green16, .blue=blue16
74 };
75 
76 
77 
92 int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
93 {
94  int size = len * sizeof(u16);
95  int ret = -ENOMEM;
96 
97  if (cmap->len != len) {
98  fb_dealloc_cmap(cmap);
99  if (!len)
100  return 0;
101 
102  cmap->red = kmalloc(size, flags);
103  if (!cmap->red)
104  goto fail;
105  cmap->green = kmalloc(size, flags);
106  if (!cmap->green)
107  goto fail;
108  cmap->blue = kmalloc(size, flags);
109  if (!cmap->blue)
110  goto fail;
111  if (transp) {
112  cmap->transp = kmalloc(size, flags);
113  if (!cmap->transp)
114  goto fail;
115  } else {
116  cmap->transp = NULL;
117  }
118  }
119  cmap->start = 0;
120  cmap->len = len;
121  ret = fb_copy_cmap(fb_default_cmap(len), cmap);
122  if (ret)
123  goto fail;
124  return 0;
125 
126 fail:
127  fb_dealloc_cmap(cmap);
128  return ret;
129 }
130 
131 int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
132 {
133  return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
134 }
135 
145 void fb_dealloc_cmap(struct fb_cmap *cmap)
146 {
147  kfree(cmap->red);
148  kfree(cmap->green);
149  kfree(cmap->blue);
150  kfree(cmap->transp);
151 
152  cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
153  cmap->len = 0;
154 }
155 
164 int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
165 {
166  int tooff = 0, fromoff = 0;
167  int size;
168 
169  if (to->start > from->start)
170  fromoff = to->start - from->start;
171  else
172  tooff = from->start - to->start;
173  size = to->len - tooff;
174  if (size > (int) (from->len - fromoff))
175  size = from->len - fromoff;
176  if (size <= 0)
177  return -EINVAL;
178  size *= sizeof(u16);
179 
180  memcpy(to->red+tooff, from->red+fromoff, size);
181  memcpy(to->green+tooff, from->green+fromoff, size);
182  memcpy(to->blue+tooff, from->blue+fromoff, size);
183  if (from->transp && to->transp)
184  memcpy(to->transp+tooff, from->transp+fromoff, size);
185  return 0;
186 }
187 
188 int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
189 {
190  int tooff = 0, fromoff = 0;
191  int size;
192 
193  if (to->start > from->start)
194  fromoff = to->start - from->start;
195  else
196  tooff = from->start - to->start;
197  size = to->len - tooff;
198  if (size > (int) (from->len - fromoff))
199  size = from->len - fromoff;
200  if (size <= 0)
201  return -EINVAL;
202  size *= sizeof(u16);
203 
204  if (copy_to_user(to->red+tooff, from->red+fromoff, size))
205  return -EFAULT;
206  if (copy_to_user(to->green+tooff, from->green+fromoff, size))
207  return -EFAULT;
208  if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
209  return -EFAULT;
210  if (from->transp && to->transp)
211  if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
212  return -EFAULT;
213  return 0;
214 }
215 
227 int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
228 {
229  int i, start, rc = 0;
230  u16 *red, *green, *blue, *transp;
231  u_int hred, hgreen, hblue, htransp = 0xffff;
232 
233  red = cmap->red;
234  green = cmap->green;
235  blue = cmap->blue;
236  transp = cmap->transp;
237  start = cmap->start;
238 
239  if (start < 0 || (!info->fbops->fb_setcolreg &&
240  !info->fbops->fb_setcmap))
241  return -EINVAL;
242  if (info->fbops->fb_setcmap) {
243  rc = info->fbops->fb_setcmap(cmap, info);
244  } else {
245  for (i = 0; i < cmap->len; i++) {
246  hred = *red++;
247  hgreen = *green++;
248  hblue = *blue++;
249  if (transp)
250  htransp = *transp++;
251  if (info->fbops->fb_setcolreg(start++,
252  hred, hgreen, hblue,
253  htransp, info))
254  break;
255  }
256  }
257  if (rc == 0)
258  fb_copy_cmap(cmap, &info->cmap);
259 
260  return rc;
261 }
262 
263 int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
264 {
265  int rc, size = cmap->len * sizeof(u16);
266  struct fb_cmap umap;
267 
268  if (size < 0 || size < cmap->len)
269  return -E2BIG;
270 
271  memset(&umap, 0, sizeof(struct fb_cmap));
272  rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
273  GFP_KERNEL);
274  if (rc)
275  return rc;
276  if (copy_from_user(umap.red, cmap->red, size) ||
277  copy_from_user(umap.green, cmap->green, size) ||
278  copy_from_user(umap.blue, cmap->blue, size) ||
279  (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
280  rc = -EFAULT;
281  goto out;
282  }
283  umap.start = cmap->start;
284  if (!lock_fb_info(info)) {
285  rc = -ENODEV;
286  goto out;
287  }
288  if (cmap->start < 0 || (!info->fbops->fb_setcolreg &&
289  !info->fbops->fb_setcmap)) {
290  rc = -EINVAL;
291  goto out1;
292  }
293  rc = fb_set_cmap(&umap, info);
294 out1:
295  unlock_fb_info(info);
296 out:
297  fb_dealloc_cmap(&umap);
298  return rc;
299 }
300 
312 const struct fb_cmap *fb_default_cmap(int len)
313 {
314  if (len <= 2)
315  return &default_2_colors;
316  if (len <= 4)
317  return &default_4_colors;
318  if (len <= 8)
319  return &default_8_colors;
320  return &default_16_colors;
321 }
322 
323 
331 void fb_invert_cmaps(void)
332 {
333  u_int i;
334 
335  for (i = 0; i < ARRAY_SIZE(red2); i++) {
336  red2[i] = ~red2[i];
337  green2[i] = ~green2[i];
338  blue2[i] = ~blue2[i];
339  }
340  for (i = 0; i < ARRAY_SIZE(red4); i++) {
341  red4[i] = ~red4[i];
342  green4[i] = ~green4[i];
343  blue4[i] = ~blue4[i];
344  }
345  for (i = 0; i < ARRAY_SIZE(red8); i++) {
346  red8[i] = ~red8[i];
347  green8[i] = ~green8[i];
348  blue8[i] = ~blue8[i];
349  }
350  for (i = 0; i < ARRAY_SIZE(red16); i++) {
351  red16[i] = ~red16[i];
352  green16[i] = ~green16[i];
353  blue16[i] = ~blue16[i];
354  }
355 }
356 
357 
358  /*
359  * Visible symbols for modules
360  */
361