2 Copyright © 1995-2015, The AROS Development Team. All rights reserved.
5 Desc: Linux fbdev gfx HIDD for AROS.
17 #include <sys/types.h>
24 #include <sys/ioctl.h>
26 #include <aros/debug.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"
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
)
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
);
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 */
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 */
109 struct TagItem modetags
[] =
111 { aHidd_Gfx_PixFmtTags
, (IPTR
)pftags
},
112 { aHidd_Gfx_SyncTags
, (IPTR
)synctags
},
118 D(bug("[LinuxFB] No file descriptor supplied in New()\n"));
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
=
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
156 synctags
[1].ti_Data
= vsi
.xres
;
157 synctags
[2].ti_Data
= vsi
.yres
;
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
;
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
);
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
;
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
));
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
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
);
237 if (baseaddr
!= MAP_FAILED
)
238 Hidd_UnixIO_MemoryUnMap(fsd
->unixio
, baseaddr
, fsi
.smem_len
, NULL
);
239 Hidd_UnixIO_CloseFile(fsd
->unixio
, fbdev
, 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
);
260 Hidd_Gfx_Switch (msg
->attrID
, idx
)
262 case aoHidd_Gfx_SupportsGamma
:
263 *msg
->storage
= data
->gamma
;
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).
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
=
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
);
329 object
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
332 object
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
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
);
343 struct LinuxFB_staticdata
*fsd
= LSD(cl
);
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
++)
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.
375 Hidd_UnixIO_IOControlFile(fsd
->unixio
, data
->fbdevinfo
.fbdev
, FBIOPUTCMAP
, &col
, NULL
);
383 static BOOL
setup_linuxfb(struct LinuxFB_staticdata
*fsd
, int fbdev
, struct fb_fix_screeninfo
*fsi
, struct fb_var_screeninfo
*vsi
)
387 r1
= Hidd_UnixIO_IOControlFile(fsd
->unixio
, fbdev
, FBIOGET_FSCREENINFO
, fsi
, NULL
);
388 r2
= Hidd_UnixIO_IOControlFile(fsd
->unixio
, fbdev
, FBIOGET_VSCREENINFO
, vsi
, NULL
);
392 D(kprintf("!!! COULD NOT GET FIXED SCREEN INFO !!!\n"));
398 D(kprintf("!!! COULD NOT GET FIXED SCREEN INFO !!!\n"));
402 D(kprintf("FB: Width: %d, height: %d, line length=%d\n",
403 vsi
->xres
, vsi
->yres
, fsi
->line_length
));
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
)
417 return ((1L << (bf
->offset
)) - 1) - ((1L << (bf
->offset
- bf
->length
)) - 1);
419 return ((1L << bf
->length
) - 1) << bf
->offset
;
424 static ULONG
bitfield2shift(struct fb_bitfield
*bf
)
428 shift
= 32 - (bf
->offset
+ bf
->length
);
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
);
445 #define print_bitfield(color, bf)
449 static BOOL
get_pixfmt(struct TagItem
*pftags
, struct fb_fix_screeninfo
*fsi
, struct fb_var_screeninfo
*vsi
)
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
);
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
;
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 */
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 */
498 /* case FB_VISUAL_MONO01:
499 case FB_VISUAL_MONO10: */
501 D(kprintf("!!! FB: UNHANDLED GRAPHTYPE :%d !!!\n", fsi
->visual
));
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
));
511 case FB_TYPE_PACKED_PIXELS
:
512 pftags
[15].ti_Data
= vHidd_BitMapType_Chunky
;
516 pftags
[15].ti_Data
= vHidd_BitMapType_Planar
;
519 case FB_TYPE_INTERLEAVED_PLANES
:
520 pftags
[15].ti_Data
= vHidd_BitMapType_InterleavedPlanar
;
524 D(kprintf("!!! UNSUPPORTED FRAMEBUFFER TYPE: %d !!!\n", fsi
->type
));