Added a test for MUIA_Listview_SelectChange.
[AROS.git] / arch / all-linux / hidd / linuxfb / gfxclass.c
blob6bc55eb2e143e4042a048a5af3fe321d98d037a9
1 /*
2 Copyright © 1995-2015, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Linux fbdev gfx HIDD for AROS.
6 Lang: English.
7 */
9 #define DEBUG 1
10 #define DEBUG_PF
12 #include <stddef.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <sys/mman.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <linux/kd.h>
20 #include <fcntl.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <sys/ioctl.h>
26 #include <aros/debug.h>
27 #include <oop/oop.h>
28 #include <hidd/hidd.h>
29 #include <hidd/graphics.h>
30 #include <proto/exec.h>
31 #include <proto/oop.h>
32 #include <proto/utility.h>
33 #include <hidd/unixio.h>
35 #include "linuxfb_intern.h"
36 #include "bitmap.h"
38 #define CURSOR_IMAGE_BPP (4)
40 #include LC_LIBDEFS_FILE
42 static BOOL setup_linuxfb(struct LinuxFB_staticdata *fsd, int fbdev,
43 struct fb_fix_screeninfo *fsi, struct fb_var_screeninfo *vsi);
44 static VOID cleanup_linuxfb(struct LinuxFB_data *data, struct LinuxFB_staticdata *fsd);
45 static BOOL get_pixfmt(struct TagItem *pftags, struct fb_fix_screeninfo *fsi, struct fb_var_screeninfo *vsi);
47 static AROS_INTH1(ResetHandler, struct LinuxFB_data *, data)
49 AROS_INTFUNC_INIT
51 if (data->confd != -1)
53 /* Enable console and restore keyboard mode */
54 Hidd_UnixIO_IOControlFile(data->unixio, data->confd, KDSETMODE, (void *)KD_TEXT, NULL);
55 Hidd_UnixIO_IOControlFile(data->unixio, data->confd, KDSKBMODE, (void *)data->kbmode, NULL);
58 return FALSE;
60 AROS_INTFUNC_EXIT
63 /***************** FBGfx::New() ***********************/
65 OOP_Object *LinuxFB__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
67 struct LinuxFB_staticdata *fsd = LSD(cl);
68 struct fb_fix_screeninfo fsi;
69 struct fb_var_screeninfo vsi;
70 int fbdev = GetTagData(aHidd_LinuxFB_File, -1, msg->attrList);
71 char *baseaddr = MAP_FAILED;
73 struct TagItem pftags[] =
75 { aHidd_PixFmt_RedShift , 0 }, /* 0 */
76 { aHidd_PixFmt_GreenShift , 0 }, /* 1 */
77 { aHidd_PixFmt_BlueShift , 0 }, /* 2 */
78 { aHidd_PixFmt_AlphaShift , 0 }, /* 3 */
79 { aHidd_PixFmt_RedMask , 0 }, /* 4 */
80 { aHidd_PixFmt_GreenMask , 0 }, /* 5 */
81 { aHidd_PixFmt_BlueMask , 0 }, /* 6 */
82 { aHidd_PixFmt_AlphaMask , 0 }, /* 7 */
83 { aHidd_PixFmt_ColorModel , 0 }, /* 8 */
84 { aHidd_PixFmt_Depth , 0 }, /* 9 */
85 { aHidd_PixFmt_BytesPerPixel, 0 }, /* 10 */
86 { aHidd_PixFmt_BitsPerPixel , 0 }, /* 11 */
87 { aHidd_PixFmt_StdPixFmt , 0 }, /* 12 */
88 { aHidd_PixFmt_CLUTShift , 0 }, /* 13 */
89 { aHidd_PixFmt_CLUTMask , 0 }, /* 14 */
90 { aHidd_PixFmt_BitMapType , 0 }, /* 15 */
91 { TAG_DONE , 0 }
94 struct TagItem synctags[] =
96 {aHidd_Sync_Description, (IPTR)"FBDev:%hx%v"}, /* 0 */
97 {aHidd_Sync_HDisp , 0 }, /* 1 */
98 {aHidd_Sync_VDisp , 0 }, /* 2 */
99 {aHidd_Sync_LeftMargin , 0 }, /* 3 */
100 {aHidd_Sync_RightMargin, 0 }, /* 4 */
101 {aHidd_Sync_HSyncLength, 0 }, /* 5 */
102 {aHidd_Sync_UpperMargin, 0 }, /* 6 */
103 {aHidd_Sync_LowerMargin, 0 }, /* 7 */
104 {aHidd_Sync_VSyncLength, 0 }, /* 8 */
105 {aHidd_Sync_PixelTime , 0 }, /* 9 */
106 {TAG_DONE , 0 }
109 struct TagItem modetags[] =
111 { aHidd_Gfx_PixFmtTags , (IPTR)pftags },
112 { aHidd_Gfx_SyncTags , (IPTR)synctags },
113 { TAG_DONE , 0 }
116 if (fbdev == -1)
118 D(bug("[LinuxFB] No file descriptor supplied in New()\n"));
119 return NULL;
122 /* Do GfxHidd initalization here */
123 if (setup_linuxfb(LSD(cl), fbdev, &fsi, &vsi))
125 if (get_pixfmt(pftags, &fsi, &vsi))
128 /* Memorymap the framebuffer using mmap() */
129 baseaddr = Hidd_UnixIO_MemoryMap(fsd->unixio, NULL, fsi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev, 0, NULL);
131 D(bug("[LinuxFB] Mapped at 0x%p\n", baseaddr));
132 if (baseaddr != MAP_FAILED)
134 /* Register gfxmodes */
135 struct TagItem mytags[] =
137 { aHidd_Gfx_ModeTags , (IPTR)modetags },
138 { aHidd_Gfx_FrameBufferType, vHidd_FrameBuffer_Mirrored },
139 { TAG_MORE , (IPTR)msg->attrList }
142 struct pRoot_New mymsg =
144 msg->mID,
145 mytags
149 * Set the gfxmode info.
150 * Some devices report all zeroes for clock information (LCD on AspireOne).
151 * This makes sync class crash with division by zero error, additionally
152 * this would create garbage SpecialMonitor structure.
153 * Here we attempt to detect such devices by checking pixclock against
154 * being zero.
156 synctags[1].ti_Data = vsi.xres;
157 synctags[2].ti_Data = vsi.yres;
159 if (vsi.pixclock)
161 synctags[3].ti_Data = vsi.left_margin;
162 synctags[4].ti_Data = vsi.right_margin;
163 synctags[5].ti_Data = vsi.hsync_len;
164 synctags[6].ti_Data = vsi.upper_margin;
165 synctags[7].ti_Data = vsi.lower_margin;
166 synctags[8].ti_Data = vsi.vsync_len;
167 synctags[9].ti_Data = vsi.pixclock;
169 else
171 /* Do not supply analog signal information, we have no analog signals */
172 synctags[3].ti_Tag = TAG_DONE;
175 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, &mymsg.mID);
176 if (NULL != o)
178 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
180 data->basebm = OOP_FindClass(CLID_Hidd_BitMap);
182 data->fbdevinfo.fbdev = fbdev;
183 data->fbdevinfo.fbtype = pftags[8].ti_Data;
184 data->fbdevinfo.baseaddr = baseaddr;
185 data->fbdevinfo.pitch = fsi.line_length;
186 data->mem_len = fsi.smem_len;
187 data->fbdevinfo.bpp = ((vsi.bits_per_pixel - 1) / 8) + 1;
188 data->fbdevinfo.xres = vsi.xres;
189 data->fbdevinfo.yres = vsi.yres;
191 data->confd = -1;
192 data->unixio = fsd->unixio;
193 data->gamma = (fsi.visual == FB_VISUAL_DIRECTCOLOR) ? TRUE : FALSE;
194 D(bug("[LinuxFB] Gamma support: %d\n", data->gamma));
196 if (data->gamma)
199 * Some explanations of this magic.
200 * In 16-bit modes we have different number of gradations per component.
201 * For example, R5G6B5 layout assumes that we have 64 gradations for
202 * G and only 32 gradations for R and B.
203 * Consequently, gamma table for G is twice longer. But framebuffer API
204 * allows to load only complete triplets.
205 * Hence we have to scale down our gamma table supplied by the OS, which
206 * always contains 256 values three times, with different scale factor for
207 * each color.
209 UBYTE bits = 0;
211 if (vsi.red.length > bits) bits = vsi.red.length;
212 if (vsi.green.length > bits) bits = vsi.green.length;
213 if (vsi.blue.length > bits) bits = vsi.blue.length;
215 D(bug("Initializing gamma table scaler down to %d bits\n", bits));
216 /* Determine how many triplets we will load (the longest table) */
217 data->scale_size = 1 << bits;
219 /* And then - scale factor for each component */
220 data->r_step = 0x100 >> vsi.red.length;
221 data->g_step = 0x100 >> vsi.green.length;
222 data->b_step = 0x100 >> vsi.blue.length;
224 D(bug("Steps R G B: %d %d %d\n", data->r_step, data->g_step, data->b_step));
227 data->resetHandler.is_Code = (VOID_FUNC)ResetHandler;
228 data->resetHandler.is_Data = data;
229 AddResetCallback(&data->resetHandler);
231 return o;
237 if (baseaddr != MAP_FAILED)
238 Hidd_UnixIO_MemoryUnMap(fsd->unixio, baseaddr, fsi.smem_len, NULL);
239 Hidd_UnixIO_CloseFile(fsd->unixio, fbdev, NULL);
241 return NULL;
244 /********** FBGfx::Dispose() ******************************/
245 VOID LinuxFB__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
247 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
249 RemResetCallback(&data->resetHandler);
250 cleanup_linuxfb(data, LSD(cl));
252 OOP_DoSuperMethod(cl, o, msg);
255 BOOL LinuxFB__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg)
257 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
258 ULONG idx;
260 Hidd_Gfx_Switch (msg->attrID, idx)
262 case aoHidd_Gfx_SupportsGamma:
263 *msg->storage = data->gamma;
264 return TRUE;
267 return OOP_DoSuperMethod(cl, o, &msg->mID);
270 /********** FBGfx::CreateObject() ****************************/
271 OOP_Object *LinuxFB__Hidd_Gfx__CreateObject(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_CreateObject *msg)
273 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
274 OOP_Object *object = NULL;
276 if (msg->cl == data->basebm)
278 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
279 BOOL fb = GetTagData(aHidd_BitMap_FrameBuffer, FALSE, msg->attrList);
281 D(bug("[LinuxFB] CreateObject, framebuffer=%d\n", fb));
283 * Our framebuffer is a chunky bitmap at predetermined address.
284 * We don't care about friends etc here, because even if our
285 * class is selected for friend bitmap, it's completely safe
286 * because it will not get FBDevInfo. Storage overhead per
287 * bitmap is extremely small here (just 8 bytes).
289 if (fb)
291 struct TagItem tags[] =
293 {aHidd_BitMap_ClassPtr , (IPTR)LSD(cl)->bmclass },
294 {aHidd_BitMap_BytesPerRow , data->fbdevinfo.pitch },
295 {aHidd_ChunkyBM_Buffer , (IPTR)data->fbdevinfo.baseaddr},
296 {aHidd_LinuxFBBitmap_FBDevInfo, -1 },
297 {TAG_MORE , (IPTR)msg->attrList }
299 struct pHidd_Gfx_CreateObject p =
301 msg->mID,
302 msg->cl,
303 tags
306 if (data->fbdevinfo.fbtype == vHidd_ColorModel_Palette)
308 tags[3].ti_Data = data->fbdevinfo.fbdev;
311 if (data->confd == -1)
314 * Switch console into gfx mode, no more console output
315 * FIXME: How to determine which console is connected to this framebuffer ?
317 data->confd = Hidd_UnixIO_OpenFile(data->unixio, "/dev/tty0", O_RDWR, 0, NULL);
318 if (data->confd != -1)
320 Hidd_UnixIO_IOControlFile(data->unixio, data->confd, KDGKBMODE, &data->kbmode, NULL);
321 Hidd_UnixIO_IOControlFile(data->unixio, data->confd, KDSETMODE, (void *)KD_GRAPHICS, NULL);
322 Hidd_UnixIO_IOControlFile(data->unixio, data->confd, KDSKBMODE, K_RAW, NULL);
326 object = (OOP_Object *)OOP_DoSuperMethod(cl, o, &p.mID);
328 else
329 object = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
331 else
332 object = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
334 return object;
337 BOOL LinuxFB__Hidd_Gfx__SetGamma(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_Gamma *msg)
339 struct LinuxFB_data *data = OOP_INST_DATA(cl, o);
341 if (data->gamma)
343 struct LinuxFB_staticdata *fsd = LSD(cl);
344 UWORD r, g, b, i;
345 struct fb_cmap col =
347 0, 1, &r, &g, &b, NULL
349 UBYTE ri = data->r_step - 1;
350 UBYTE gi = data->g_step - 1;
351 UBYTE bi = data->b_step - 1;
353 D(bug("[LinuxFB] N R G B gamma tables:\n"));
354 for (i = 0; i < data->scale_size; i++)
356 col.start = i;
358 r = msg->Red[ri] << 8;
359 g = msg->Green[gi] << 8;
360 b = msg->Blue[bi] << 8;
361 D(bug("[LinuxFB] %02X %02X %02X %02X\n", i, msg->Red[ri], msg->Green[gi], msg->Blue[bi]));
364 * Wraparound here is intentional. It prevents from buffer overflow.
365 * Example: RGB 565 format. In this case we load 64 triplets. But
366 * only G channel will actually use 64 values, R and B will use only
367 * 32 values. So, we scale down 256 values to 32, but the rest 32
368 * unused values also need to be picked up from somewhere.
369 * Alternatively we could add some conditions, but it would be slower.
371 ri += data->r_step;
372 gi += data->g_step;
373 bi += data->b_step;
375 Hidd_UnixIO_IOControlFile(fsd->unixio, data->fbdevinfo.fbdev, FBIOPUTCMAP, &col, NULL);
377 return TRUE;
380 return FALSE;
383 static BOOL setup_linuxfb(struct LinuxFB_staticdata *fsd, int fbdev, struct fb_fix_screeninfo *fsi, struct fb_var_screeninfo *vsi)
385 int r1, r2;
387 r1 = Hidd_UnixIO_IOControlFile(fsd->unixio, fbdev, FBIOGET_FSCREENINFO, fsi, NULL);
388 r2 = Hidd_UnixIO_IOControlFile(fsd->unixio, fbdev, FBIOGET_VSCREENINFO, vsi, NULL);
390 if (r1 == -1)
392 D(kprintf("!!! COULD NOT GET FIXED SCREEN INFO !!!\n"));
393 return FALSE;
396 if (r2 == -1)
398 D(kprintf("!!! COULD NOT GET FIXED SCREEN INFO !!!\n"));
399 return FALSE;
402 D(kprintf("FB: Width: %d, height: %d, line length=%d\n",
403 vsi->xres, vsi->yres, fsi->line_length));
405 return TRUE;
408 static VOID cleanup_linuxfb(struct LinuxFB_data *data, struct LinuxFB_staticdata *fsd)
410 Hidd_UnixIO_MemoryUnMap(fsd->unixio, data->fbdevinfo.baseaddr, data->mem_len, NULL);
411 Hidd_UnixIO_CloseFile(fsd->unixio, data->fbdevinfo.fbdev, NULL);
414 static HIDDT_Pixel bitfield2mask(struct fb_bitfield *bf)
416 #if 0
417 return ((1L << (bf->offset)) - 1) - ((1L << (bf->offset - bf->length)) - 1);
418 #else
419 return ((1L << bf->length) - 1) << bf->offset;
420 #endif
424 static ULONG bitfield2shift(struct fb_bitfield *bf)
426 int shift;
428 shift = 32 - (bf->offset + bf->length);
429 if (shift == 32)
430 shift = 0;
432 return shift;
435 #ifdef DEBUG_PF
437 static void print_bitfield(const char *color, struct fb_bitfield *bf)
439 kprintf("FB: Bitfield %s: %d, %d, %d\n"
440 , color, bf->offset, bf->length, bf->msb_right);
443 #else
445 #define print_bitfield(color, bf)
447 #endif
449 static BOOL get_pixfmt(struct TagItem *pftags, struct fb_fix_screeninfo *fsi, struct fb_var_screeninfo *vsi)
451 BOOL success = TRUE;
453 pftags[9 ].ti_Data = vsi->bits_per_pixel; /* Depth */
454 pftags[10].ti_Data = ((vsi->bits_per_pixel - 1) / 8) + 1; /* Bytes per pixel */
455 pftags[11].ti_Data = vsi->bits_per_pixel; /* Size */
457 print_bitfield("red", &vsi->red);
458 print_bitfield("green", &vsi->green);
459 print_bitfield("blue", &vsi->blue);
460 print_bitfield("transp", &vsi->transp);
462 switch (fsi->visual)
464 case FB_VISUAL_TRUECOLOR:
465 case FB_VISUAL_DIRECTCOLOR:
466 pftags[0].ti_Data = bitfield2shift(&vsi->red); /* Shifts: R, G, B, A */
467 pftags[1].ti_Data = bitfield2shift(&vsi->green);
468 pftags[2].ti_Data = bitfield2shift(&vsi->blue);
469 pftags[3].ti_Data = bitfield2shift(&vsi->transp);
471 pftags[4].ti_Data = bitfield2mask(&vsi->red); /* Masks: R, G, B, A */
472 pftags[5].ti_Data = bitfield2mask(&vsi->green);
473 pftags[6].ti_Data = bitfield2mask(&vsi->blue);
474 pftags[7].ti_Data = bitfield2mask(&vsi->transp);
476 pftags[8].ti_Data = vHidd_ColorModel_TrueColor;
477 break;
479 case FB_VISUAL_PSEUDOCOLOR:
480 pftags[4 ].ti_Data = bitfield2mask(&vsi->red); /* Masks: R, G, B, A */
481 pftags[5 ].ti_Data = bitfield2mask(&vsi->green);
482 pftags[6 ].ti_Data = bitfield2mask(&vsi->blue);
484 pftags[8 ].ti_Data = vHidd_ColorModel_Palette;
485 pftags[13].ti_Data = 0; /* LUT shift */
486 pftags[14].ti_Data = 0xFF; /* LUT mask */
487 break;
489 case FB_VISUAL_STATIC_PSEUDOCOLOR:
490 pftags[4 ].ti_Data = bitfield2mask(&vsi->red); /* Masks: R, G, B, A */
491 pftags[5 ].ti_Data = bitfield2mask(&vsi->green);
492 pftags[6 ].ti_Data = bitfield2mask(&vsi->blue);
493 pftags[8 ].ti_Data = vHidd_ColorModel_StaticPalette;
494 pftags[13].ti_Data = 0; /* LUT shift */
495 pftags[14].ti_Data = 0xFF; /* LUT mask */
496 break;
498 /* case FB_VISUAL_MONO01:
499 case FB_VISUAL_MONO10: */
500 default:
501 D(kprintf("!!! FB: UNHANDLED GRAPHTYPE :%d !!!\n", fsi->visual));
502 return FALSE;
505 D(kprintf("FB; mask: (%p, %p, %p, %p), shift: (%ld, %ld, %ld, %ld)\n",
506 pftags[4].ti_Data, pftags[5].ti_Data, pftags[6].ti_Data, pftags[7].ti_Data,
507 pftags[0].ti_Data, pftags[1].ti_Data, pftags[2].ti_Data, pftags[3].ti_Data));
509 switch (fsi->type)
511 case FB_TYPE_PACKED_PIXELS:
512 pftags[15].ti_Data = vHidd_BitMapType_Chunky;
513 break;
515 case FB_TYPE_PLANES:
516 pftags[15].ti_Data = vHidd_BitMapType_Planar;
517 break;
519 case FB_TYPE_INTERLEAVED_PLANES:
520 pftags[15].ti_Data = vHidd_BitMapType_InterleavedPlanar;
521 break;
523 default:
524 D(kprintf("!!! UNSUPPORTED FRAMEBUFFER TYPE: %d !!!\n", fsi->type));
525 success = FALSE;
526 break;
529 return success;