Difference between revisions of "Kernel interface"
m (typo) |
(line counter, minor updates) |
||
(41 intermediate revisions by 9 users not shown) | |||
Line 1: | Line 1: | ||
− | ' | + | {{hint|For apps use higher level libraries/interfaces like SDL, Qt, X or similar for your own good (portability). It may be impossible to retain this layout on some major hardware revision, let's say Pandora 3000, and your program will break. Also this requires some level of Linux programming knowledge.}} |
− | In case you need to write low level code (you can't/don't want to use high level libs like SDL), you can use kernel interface. This is the recommended way to access hardware (as opposed to GP2X style of accessing chip registers by mmap'ing /dev/mem), because in case hardware changes are needed in future, they could be handled by kernel and all programs would still work. It also should allow several programs to work at the same time and should be more stable. | + | In case you need to write low level code (you can't/don't want to use high level libs like [[SDL]]), you can use kernel interface. This is the recommended way to access hardware (as opposed to GP2X style of accessing chip registers by mmap'ing /dev/mem), because in case hardware changes are needed in future, they could be handled by kernel and all programs would still work. It also should allow several programs to work at the same time and should be more stable. |
− | + | ==Input== | |
− | Buttons, keypad, touchscreen and nubs are all exposed through Linux event interface (EVDEV). All devices are represented by /dev/input/eventX files, which can be opened, read and queried (using ioctl calls). The reads can be synchronous (the read will only return when user does something, like presses the button), or asynchronous (the system will report what changed since the last time you asked). | + | Buttons, keypad, touchscreen and [[nubs]] are all exposed through Linux event interface (EVDEV). All devices are represented by '''/dev/input/eventX''' files, which can be opened, read and queried (using ioctl calls). The reads can be synchronous (the read will only return when user does something, like presses the button), or asynchronous (the system will report what changed since the last time you asked). |
− | + | {{warning | |
− | <source lang=" | + | |Don't hardcode device filenames in your program! |
+ | }} | ||
+ | |||
+ | For example, currently /dev/input/event2 represents game buttons, but in future it may become touchscreen. Scan input device names instead, example: | ||
+ | <source lang="c"> | ||
for (i = 0; 1; i++) | for (i = 0; 1; i++) | ||
{ | { | ||
Line 20: | Line 24: | ||
</source> | </source> | ||
− | List of device names: | + | List of device names and events they send: |
− | {| | + | {|class="wikitable" |
|- | |- | ||
!name | !name | ||
!description | !description | ||
+ | !event.type | ||
+ | !event.code | ||
+ | !event.value | ||
|- | |- | ||
− | |||
|keypad | |keypad | ||
+ | |keypad | ||
+ | |EV_KEY | ||
+ | |KEY_0...KEY_Z, KEY_BACKSPACE, KEY_LEFTSHIFT, KEY_SPACE, KEY_ENTER, KEY_COMMA, KEY_DOT, KEY_FN | ||
+ | |0 - released<br> 1 - pressed<br> 2 - autorepeat event | ||
|- | |- | ||
|gpio-keys | |gpio-keys | ||
|game buttons | |game buttons | ||
+ | |EV_KEY | ||
+ | |KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_MENU (Pandora button), KEY_LEFTALT (Start), KEY_LEFTCTRL (Select), KEY_PAGEUP (Y/North), KEY_HOME (A/East), KEY_PAGEDOWN (X/South), KEY_END (B/West), KEY_RIGHTSHIFT (Shoulder L), KEY_RIGHTCTRL (Shoulder R), KEY_KPPLUS (Shoulder L2), KEY_KPMINUS (Shoulder R2), KEY_COFFEE (Hold) | ||
+ | |0 - released<br> 1 - pressed | ||
+ | |- | ||
+ | |gpio-keys | ||
+ | |lid state | ||
+ | |EV_SW | ||
+ | |SW_LID | ||
+ | |0 - closing<br> 1 - opening | ||
|- | |- | ||
− | |||
|touchscreen | |touchscreen | ||
+ | |touchscreen | ||
+ | |EV_ABS | ||
+ | |ABS_X, ABS_Y, ABS_PRESSURE | ||
+ | |varies, use calibration data | ||
|- | |- | ||
− | | | + | |nub0 |
|left nub | |left nub | ||
+ | |EV_ABS | ||
+ | |ABS_X, ABS_Y | ||
+ | | -256 (Left/Up)<br>0<br>+256 (Right/Down) | ||
|- | |- | ||
− | | | + | |nub1 |
|right nub | |right nub | ||
+ | |EV_ABS | ||
+ | |ABS_X, ABS_Y | ||
+ | | -256 (Left/Up))<br>0<br>+256 (Right/Down) | ||
|} | |} | ||
− | Sample code: | + | Sample code:<br> |
[http://beagleboard.googlecode.com/files/evtest.c evtest.c] | [http://beagleboard.googlecode.com/files/evtest.c evtest.c] | ||
− | [http:// | + | [http://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-misc.git;a=blob;f=op_test_inputs.c;hb=HEAD op_test_inputs.c] |
− | ====Touchscreen | + | ===Nubs=== |
+ | Nubs have 3 modes that can be switched during runtime: absolute, mouse, mbuttons; this can be done by writing one of those 3 strings to /proc/pandora/nubX/mode . On mode change /dev/input/event* device reenumeration starts and files belonging to nubs are recreated and must be reopened. | ||
+ | |||
+ | <strong>Warning:</strong> it is not guaranteed that input files will finish recreating after a write to /proc/pandora/nubX/mode finishes, it is application's responsibility to wait until those files are available. | ||
+ | |||
+ | To solve the above problem, the OS provides a helper script that will wait for enumeration to complete: | ||
+ | <source lang="bash"> | ||
+ | /usr/pandora/scripts/op_nubchange.sh <left_num_mode> <right_nub_mode> | ||
+ | </source> | ||
+ | op_nubchange.sh is not available on HF6 or earlier firmwares, to support them, something like this could be used: | ||
+ | <source lang="bash"> | ||
+ | if [ -e /usr/pandora/scripts/op_nubchange.sh ]; then | ||
+ | /usr/pandora/scripts/op_nubchange.sh absolute absolute | ||
+ | else | ||
+ | echo absolute > /proc/pandora/nub0/mode | ||
+ | echo absolute > /proc/pandora/nub1/mode | ||
+ | fi | ||
+ | </source> | ||
+ | |||
+ | ===Touchscreen=== | ||
Event interface returns uncalibrated values directly from driver, so you need to use tslib or manage calibration yourself (using data from /etc/pointercal). | Event interface returns uncalibrated values directly from driver, so you need to use tslib or manage calibration yourself (using data from /etc/pointercal). | ||
+ | ===X11=== | ||
+ | When using the direct kernel api for input, it is also important to stop X processing the events as well. To do this you can create an X window to eat the events. There is also a utility that comes with the Pandora to do this: | ||
+ | <source lang="c"> | ||
+ | op_runfbapp ./yourapp | ||
+ | </source> | ||
− | + | ==Sound== | |
Pandora uses ALSA, but it has OSS emulation enabled too, so GP2X code should work. | Pandora uses ALSA, but it has OSS emulation enabled too, so GP2X code should work. | ||
+ | ==Video== | ||
+ | ===Architecture=== | ||
+ | Framebuffer device (/dev/fbX) is supported. There are 3 framebuffers available ('''/dev/fb0''', /dev/fb1 and /dev/fb2), which represent 3 graphics/video layers on OMAP3 by default (but can be reconfigured). Only /dev/fb0 is enabled by default. | ||
+ | |||
+ | OMAP3 display subsystem is controlled by a driver known as DSS2, which has various controls available on /sys/devices/platform/omapdss/ (but they are not meant to be changed by programs, so they are root writable only). The driver exposes 3 layers (called overlays) and 2 displays. Overlays 1 and 2 can perform hardware scaling on the fly using 5-tap poly-phase filter, overlay0 can not. Displays 0 and 1 represent LCD and TV respectively. By default the 3 framebuffers (/dev/fbX) are redirected to 3 overlays, which all output to the LCD. This configuration is not meant to be changed by programs, only firmware should manage these. | ||
+ | |||
+ | /dev/fb2 is reserved for system use and requires root to access. Programs are free to use /dev/fb0 and /dev/fb1 as they please. | ||
+ | |||
+ | ===Basic usage=== | ||
+ | ====framebuffer interface==== | ||
+ | Framebuffers can be accessed Linux fbdev interface: | ||
+ | <source lang="c"> | ||
+ | fbdev = open("/dev/fb0", O_RDWR); | ||
+ | buffer = mmap(0, 800*480*2, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev, 0); | ||
+ | </source> | ||
+ | (this is basic example, no error checks) | ||
+ | |||
+ | the returned pointer can be used to draw on the screen. | ||
+ | |||
+ | Be sure to #include <linux/fb.h> to get access to the FB device ioctl interface, and <sys/ioctl.h> for access to ioctl itself. | ||
+ | |||
+ | ====double buffering==== | ||
+ | This can be achieved using FBIOPAN_DISPLAY ioctl system call. For this you need to mmap framebuffer of double size | ||
+ | <source lang="c"> | ||
+ | buffer1 = mmap(0, 800*480*2 * 2, PROT_WRITE, MAP_SHARED, fbdev, 0); | ||
+ | buffer2 = (char *)mem + 800*480*2; | ||
+ | </source> | ||
+ | |||
+ | Then to display buffer2 you would call: | ||
+ | <source lang="c"> | ||
+ | struct fb_var_screeninfo fbvar; | ||
+ | ioctl(fbdev, FBIOGET_VSCREENINFO, &fbvar); | ||
+ | fbvar.yoffset = 480; | ||
+ | ioctl(fbdev, FBIOPAN_DISPLAY, &fbvar); | ||
+ | </source> | ||
+ | going back to buffer1 would be repeating above with fbvar.yoffset = 0. Triple or quad buffering can be implemented using the same technique. | ||
+ | |||
+ | ====vertical sync==== | ||
+ | Linux has standard FBIO_WAITFORVSYNC for this: | ||
+ | <source lang="c"> | ||
+ | int arg = 0; | ||
+ | ioctl(fbdev, FBIO_WAITFORVSYNC, &arg); | ||
+ | </source> | ||
+ | be sure to pass argument value 0 or it will not work. | ||
+ | |||
+ | Some toolchains don't have FBIO_WAITFORVSYNC defined in <linux/fb.h>, in which case you can define it in the following manner: | ||
+ | |||
+ | <source lang="c"> | ||
+ | #ifndef FBIO_WAITFORVSYNC | ||
+ | #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) | ||
+ | #endif | ||
+ | </source> | ||
+ | =====adaptive vsync===== | ||
+ | Standard vsync has a problem: if the program takes slightly longer that one frame to update, calling FBIO_WAITFORVSYNC will wait for the whole next frame, wasting time. In the worst case this can halve the framerate, and the CPU ends up being not fully utilized. The program can try to combat this by measuring time since the last wait, but that is not reliable because ioctl may return long after the actual vsync as Linux may need to process other drivers or system threads before giving back control. | ||
+ | |||
+ | To solve the above issue, SZ 1.74 (kernel 3.2.78) has introduced a new ioctl: | ||
+ | <source lang="c"> | ||
+ | #ifndef OMAPFB_WAITFORVSYNC_FRAME | ||
+ | #define OMAPFB_WAITFORVSYNC_FRAME _IOWR('O', 70, unsigned int) | ||
+ | #endif | ||
+ | |||
+ | static unsigned int arg = 0; | ||
+ | ioctl(fbdev, FBIO_WAITFORVSYNC, &arg); | ||
+ | </source> | ||
+ | OMAPFB_WAITFORVSYNC_FRAME has an unsigned int arg which is the current frame number (both an input and output). The ioctl only waits if the passed arg matches the current frame number that's maintained inside the kernel, otherwise it instantly returns. It always updates the arg to the current frame number on return. | ||
− | + | Making 'arg' static is the easiest way to make it work, but you can keep the variable anywhere else as long as it's preserved between ioctl calls. | |
− | |||
+ | ====line counter==== | ||
+ | Since SZ 1.74 (kernel kernel 3.2.78) it's possible to read the LCD line counter: | ||
+ | <source lang="c"> | ||
+ | #ifndef OMAPFB_GET_LINE_STATUS | ||
+ | #define OMAPFB_GET_LINE_STATUS _IOR('O', 71, int) | ||
+ | #endif | ||
+ | |||
+ | int counter; | ||
+ | ioctl(fd, OMAPFB_GET_LINE_STATUS, &counter); | ||
+ | </source> | ||
+ | It returns the current line number (0-479) only during the active display. During VFP, vsync and VBP it returns 0, and when the screen is off (lid closed, etc) the hardware returns 2047. | ||
+ | |||
+ | ====hardware scaling==== | ||
+ | Overlay1 (/dev/fb1) can be used to achieve hardware scaling. Technically overlay2 (fb2) can be used for this too, but it is reserved for TV-out functionality and only usable by root, so don't use it. The overlay is configured using series of standard and OMAP specific ioctl calls, but the system ships with some tools to achieve this from scripts too. This way the framebuffer can be set up for some arbitrary size (say 320x240) and can output to LCD as 800x480 with hardware scaling. | ||
+ | Here is an example script: | ||
+ | <source lang="bash"> | ||
+ | ofbset -fb /dev/fb1 -pos 0 0 -size 800 480 -mem 307200 -en 1 | ||
+ | fbset -fb /dev/fb1 -g 320 240 320 480 16 | ||
+ | |||
+ | ./your_app_here | ||
+ | |||
+ | ofbset -fb /dev/fb1 -pos 0 0 -size 0 0 -mem 0 -en 0 | ||
+ | </source> | ||
+ | |||
+ | What it does: | ||
+ | * allocates OMAP DSS layer, asks video output to be 800x480 at position 0,0 (could set it to 640x480 at 80,0 instead to get centered 2x scaling of 320x240). 307200 bytes of video memory are allocated for 2 320x240 16bpp screens (for doublebuffering). | ||
+ | * sets video mode to 320x240@16bpp, virtual resolution 320x480 for doublebuffering. | ||
+ | * runs your app | ||
+ | * cleans the video layer on exit | ||
+ | |||
+ | Now the program can act as if it works with 320x240 16bpp screen. | ||
+ | |||
+ | For some example code, see how it's implemented in [http://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=sdl_omap.git;a=tree;f=src/video/omapdss;hb=HEAD pandora's SDL]. | ||
+ | |||
+ | ====LCD refresh rate==== | ||
+ | The OS has a dedicated script which can change the LCD refresh rate. It must be ran with sudo: | ||
+ | <source lang="c"> | ||
+ | sudo -n /usr/pandora/scripts/op_lcdrate.sh 50 | ||
+ | </source> | ||
+ | From code, system() function can be used for this. | ||
+ | |||
+ | '''Note''': this doesn't mean your program has to run as root to use this (it never should!), just run the script as shown above. | ||
+ | |||
+ | ====gamma==== | ||
+ | Since SZ 1.52, gamma can be controlled with another script, the same way as LCD refresh: | ||
+ | <source lang="c"> | ||
+ | sudo -n /usr/pandora/scripts/op_gamma.sh 1.0 | ||
+ | </source> | ||
+ | where 1.0 is the gamma level. If supplied gamma level is 0, then user defaults are used (useful to set on exit). There is also an optional -b N argument (to be specified before gamma value), where N controls black level (0-255, useful to combat LCD ghosting). | ||
+ | |||
+ | ===Hardware scaling filter control=== | ||
+ | The hardware scaler in pandora uses poly-phase 5-tap 8-phase filter. It is described in 15.4.2.3.4 and 15.6.1.3 sections of TRM. There are 40 coefficients programmable for both horizontal and vertical resampling (vertical resampling might also use 3 taps/24 coefficients, it depends on available pixel clock). | ||
+ | ====using predefined filters==== | ||
+ | Just run 'sudo /usr/pandora/scripts/op_videofir.sh <name>', where name currently can be: | ||
+ | * default - the default filter from OMAP manual | ||
+ | * none - nearest neighbor | ||
+ | ====using custom filters==== | ||
+ | run 'sudo /usr/pandora/scripts/op_videofir.sh <basename>', where basename* files contains coefficient table. The script will look for <basename>_h, <basename>_v3 and <basename>_v5 files for 5-tap horizontal, 3-tap vertical and 5-tap vertical configurations respectively. Pass absolute or relative path for basename with './', like './something'. The coefficients should be in a form of table like described in TRM, for example: | ||
+ | <source lang="c"> | ||
+ | ---- | ||
+ | 0 0 128 0 0 | ||
+ | -1 13 124 -8 0 | ||
+ | -2 30 112 -11 -1 | ||
+ | -5 51 95 -11 -2 | ||
+ | 0 -9 73 73 -9 | ||
+ | -2 -11 95 51 -5 | ||
+ | -1 -11 112 30 -2 | ||
+ | 0 -8 124 13 -1 | ||
+ | </source> | ||
+ | For 3-tap table, first and last columns should contain zeros. | ||
+ | |||
+ | ==LEDs and Display backlight== | ||
+ | The LEDs can be controlled via '''/sys/class/leds/''', and then a file [http://www.gp32x.com/board/index.php?s=&showtopic=45309&view=findpost&p=673593]: | ||
+ | * pandora::sd1 | ||
+ | * pandora::sd2 | ||
+ | * pandora::charger | ||
+ | * pandora::power | ||
+ | * pandora::bluetooth | ||
+ | * pandora::wifi | ||
+ | * pandora::keypad_bl | ||
+ | Backlight can be controlled via '''/sys/class/backlight/'''. | ||
+ | |||
+ | ==Battery== | ||
+ | There is bq27500 battery monitoring chip onboard, that exports various files at /sys/class/power_supply/bq27500-0/ , for example 'capacity' tells charge percenage left. See Data_provided_by_Battery_and_Power_driver for more details.<br> | ||
+ | There are also similar directories at /sys/class/power_supply/twl4030_* that provide info while charging. | ||
+ | |||
+ | ==Misc== | ||
+ | ===Overclocking=== | ||
+ | Can be performed by running a helper script | ||
+ | <source lang="bash"> | ||
+ | sudo /usr/pandora/scripts/op_cpuspeed.sh X | ||
+ | </source> | ||
+ | In case you want to do it from another program or non-interactively, you can use system() function (or similar) with: | ||
+ | <source lang="bash"> | ||
+ | sudo -n /usr/pandora/scripts/op_cpuspeed.sh -n X | ||
+ | </source> | ||
+ | * Note: you are advised not to change the clock explicitly from a program because optimal clock may differ on different pandora revisions, or somebody might want to run at different clock speed to save battery, CPU life, etc. . Let the user decide about the clock before running the program and set it using the system tools. | ||
+ | * Note: '-n' script argument is not available on pre-SuperZaxxon Final firmwares. | ||
− | + | [[Category:Development]] | |
− | + | [[Category:Kernel]] | |
− | + | [[Category:Hardware]] | |
− | |||
− |
Latest revision as of 16:18, 28 February 2016
For apps use higher level libraries/interfaces like SDL, Qt, X or similar for your own good (portability). It may be impossible to retain this layout on some major hardware revision, let's say Pandora 3000, and your program will break. Also this requires some level of Linux programming knowledge. |
In case you need to write low level code (you can't/don't want to use high level libs like SDL), you can use kernel interface. This is the recommended way to access hardware (as opposed to GP2X style of accessing chip registers by mmap'ing /dev/mem), because in case hardware changes are needed in future, they could be handled by kernel and all programs would still work. It also should allow several programs to work at the same time and should be more stable.
Input
Buttons, keypad, touchscreen and nubs are all exposed through Linux event interface (EVDEV). All devices are represented by /dev/input/eventX files, which can be opened, read and queried (using ioctl calls). The reads can be synchronous (the read will only return when user does something, like presses the button), or asynchronous (the system will report what changed since the last time you asked).
Don't hardcode device filenames in your program! |
For example, currently /dev/input/event2 represents game buttons, but in future it may become touchscreen. Scan input device names instead, example:
for (i = 0; 1; i++)
{
sprintf(name, "/dev/input/event%i", i);
fd = open(name, O_RDONLY);
if (fd < 0) break; /* no more devices */
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
if (strcmp(name, "gpio-keys") == 0)
return fd; /* found the buttons! */
close(fd); /* we don't need this device */
}
List of device names and events they send:
name | description | event.type | event.code | event.value |
---|---|---|---|---|
keypad | keypad | EV_KEY | KEY_0...KEY_Z, KEY_BACKSPACE, KEY_LEFTSHIFT, KEY_SPACE, KEY_ENTER, KEY_COMMA, KEY_DOT, KEY_FN | 0 - released 1 - pressed 2 - autorepeat event |
gpio-keys | game buttons | EV_KEY | KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_MENU (Pandora button), KEY_LEFTALT (Start), KEY_LEFTCTRL (Select), KEY_PAGEUP (Y/North), KEY_HOME (A/East), KEY_PAGEDOWN (X/South), KEY_END (B/West), KEY_RIGHTSHIFT (Shoulder L), KEY_RIGHTCTRL (Shoulder R), KEY_KPPLUS (Shoulder L2), KEY_KPMINUS (Shoulder R2), KEY_COFFEE (Hold) | 0 - released 1 - pressed |
gpio-keys | lid state | EV_SW | SW_LID | 0 - closing 1 - opening |
touchscreen | touchscreen | EV_ABS | ABS_X, ABS_Y, ABS_PRESSURE | varies, use calibration data |
nub0 | left nub | EV_ABS | ABS_X, ABS_Y | -256 (Left/Up) 0 +256 (Right/Down) |
nub1 | right nub | EV_ABS | ABS_X, ABS_Y | -256 (Left/Up)) 0 +256 (Right/Down) |
Sample code:
evtest.c
op_test_inputs.c
Nubs
Nubs have 3 modes that can be switched during runtime: absolute, mouse, mbuttons; this can be done by writing one of those 3 strings to /proc/pandora/nubX/mode . On mode change /dev/input/event* device reenumeration starts and files belonging to nubs are recreated and must be reopened.
Warning: it is not guaranteed that input files will finish recreating after a write to /proc/pandora/nubX/mode finishes, it is application's responsibility to wait until those files are available.
To solve the above problem, the OS provides a helper script that will wait for enumeration to complete:
/usr/pandora/scripts/op_nubchange.sh <left_num_mode> <right_nub_mode>
op_nubchange.sh is not available on HF6 or earlier firmwares, to support them, something like this could be used:
if [ -e /usr/pandora/scripts/op_nubchange.sh ]; then
/usr/pandora/scripts/op_nubchange.sh absolute absolute
else
echo absolute > /proc/pandora/nub0/mode
echo absolute > /proc/pandora/nub1/mode
fi
Touchscreen
Event interface returns uncalibrated values directly from driver, so you need to use tslib or manage calibration yourself (using data from /etc/pointercal).
X11
When using the direct kernel api for input, it is also important to stop X processing the events as well. To do this you can create an X window to eat the events. There is also a utility that comes with the Pandora to do this:
op_runfbapp ./yourapp
Sound
Pandora uses ALSA, but it has OSS emulation enabled too, so GP2X code should work.
Video
Architecture
Framebuffer device (/dev/fbX) is supported. There are 3 framebuffers available (/dev/fb0, /dev/fb1 and /dev/fb2), which represent 3 graphics/video layers on OMAP3 by default (but can be reconfigured). Only /dev/fb0 is enabled by default.
OMAP3 display subsystem is controlled by a driver known as DSS2, which has various controls available on /sys/devices/platform/omapdss/ (but they are not meant to be changed by programs, so they are root writable only). The driver exposes 3 layers (called overlays) and 2 displays. Overlays 1 and 2 can perform hardware scaling on the fly using 5-tap poly-phase filter, overlay0 can not. Displays 0 and 1 represent LCD and TV respectively. By default the 3 framebuffers (/dev/fbX) are redirected to 3 overlays, which all output to the LCD. This configuration is not meant to be changed by programs, only firmware should manage these.
/dev/fb2 is reserved for system use and requires root to access. Programs are free to use /dev/fb0 and /dev/fb1 as they please.
Basic usage
framebuffer interface
Framebuffers can be accessed Linux fbdev interface:
fbdev = open("/dev/fb0", O_RDWR);
buffer = mmap(0, 800*480*2, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev, 0);
(this is basic example, no error checks)
the returned pointer can be used to draw on the screen.
Be sure to #include <linux/fb.h> to get access to the FB device ioctl interface, and <sys/ioctl.h> for access to ioctl itself.
double buffering
This can be achieved using FBIOPAN_DISPLAY ioctl system call. For this you need to mmap framebuffer of double size
buffer1 = mmap(0, 800*480*2 * 2, PROT_WRITE, MAP_SHARED, fbdev, 0);
buffer2 = (char *)mem + 800*480*2;
Then to display buffer2 you would call:
struct fb_var_screeninfo fbvar;
ioctl(fbdev, FBIOGET_VSCREENINFO, &fbvar);
fbvar.yoffset = 480;
ioctl(fbdev, FBIOPAN_DISPLAY, &fbvar);
going back to buffer1 would be repeating above with fbvar.yoffset = 0. Triple or quad buffering can be implemented using the same technique.
vertical sync
Linux has standard FBIO_WAITFORVSYNC for this:
int arg = 0;
ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
be sure to pass argument value 0 or it will not work.
Some toolchains don't have FBIO_WAITFORVSYNC defined in <linux/fb.h>, in which case you can define it in the following manner:
#ifndef FBIO_WAITFORVSYNC
#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
#endif
adaptive vsync
Standard vsync has a problem: if the program takes slightly longer that one frame to update, calling FBIO_WAITFORVSYNC will wait for the whole next frame, wasting time. In the worst case this can halve the framerate, and the CPU ends up being not fully utilized. The program can try to combat this by measuring time since the last wait, but that is not reliable because ioctl may return long after the actual vsync as Linux may need to process other drivers or system threads before giving back control.
To solve the above issue, SZ 1.74 (kernel 3.2.78) has introduced a new ioctl:
#ifndef OMAPFB_WAITFORVSYNC_FRAME
#define OMAPFB_WAITFORVSYNC_FRAME _IOWR('O', 70, unsigned int)
#endif
static unsigned int arg = 0;
ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
OMAPFB_WAITFORVSYNC_FRAME has an unsigned int arg which is the current frame number (both an input and output). The ioctl only waits if the passed arg matches the current frame number that's maintained inside the kernel, otherwise it instantly returns. It always updates the arg to the current frame number on return.
Making 'arg' static is the easiest way to make it work, but you can keep the variable anywhere else as long as it's preserved between ioctl calls.
line counter
Since SZ 1.74 (kernel kernel 3.2.78) it's possible to read the LCD line counter:
#ifndef OMAPFB_GET_LINE_STATUS
#define OMAPFB_GET_LINE_STATUS _IOR('O', 71, int)
#endif
int counter;
ioctl(fd, OMAPFB_GET_LINE_STATUS, &counter);
It returns the current line number (0-479) only during the active display. During VFP, vsync and VBP it returns 0, and when the screen is off (lid closed, etc) the hardware returns 2047.
hardware scaling
Overlay1 (/dev/fb1) can be used to achieve hardware scaling. Technically overlay2 (fb2) can be used for this too, but it is reserved for TV-out functionality and only usable by root, so don't use it. The overlay is configured using series of standard and OMAP specific ioctl calls, but the system ships with some tools to achieve this from scripts too. This way the framebuffer can be set up for some arbitrary size (say 320x240) and can output to LCD as 800x480 with hardware scaling. Here is an example script:
ofbset -fb /dev/fb1 -pos 0 0 -size 800 480 -mem 307200 -en 1
fbset -fb /dev/fb1 -g 320 240 320 480 16
./your_app_here
ofbset -fb /dev/fb1 -pos 0 0 -size 0 0 -mem 0 -en 0
What it does:
- allocates OMAP DSS layer, asks video output to be 800x480 at position 0,0 (could set it to 640x480 at 80,0 instead to get centered 2x scaling of 320x240). 307200 bytes of video memory are allocated for 2 320x240 16bpp screens (for doublebuffering).
- sets video mode to 320x240@16bpp, virtual resolution 320x480 for doublebuffering.
- runs your app
- cleans the video layer on exit
Now the program can act as if it works with 320x240 16bpp screen.
For some example code, see how it's implemented in pandora's SDL.
LCD refresh rate
The OS has a dedicated script which can change the LCD refresh rate. It must be ran with sudo:
sudo -n /usr/pandora/scripts/op_lcdrate.sh 50
From code, system() function can be used for this.
Note: this doesn't mean your program has to run as root to use this (it never should!), just run the script as shown above.
gamma
Since SZ 1.52, gamma can be controlled with another script, the same way as LCD refresh:
sudo -n /usr/pandora/scripts/op_gamma.sh 1.0
where 1.0 is the gamma level. If supplied gamma level is 0, then user defaults are used (useful to set on exit). There is also an optional -b N argument (to be specified before gamma value), where N controls black level (0-255, useful to combat LCD ghosting).
Hardware scaling filter control
The hardware scaler in pandora uses poly-phase 5-tap 8-phase filter. It is described in 15.4.2.3.4 and 15.6.1.3 sections of TRM. There are 40 coefficients programmable for both horizontal and vertical resampling (vertical resampling might also use 3 taps/24 coefficients, it depends on available pixel clock).
using predefined filters
Just run 'sudo /usr/pandora/scripts/op_videofir.sh <name>', where name currently can be:
- default - the default filter from OMAP manual
- none - nearest neighbor
using custom filters
run 'sudo /usr/pandora/scripts/op_videofir.sh <basename>', where basename* files contains coefficient table. The script will look for <basename>_h, <basename>_v3 and <basename>_v5 files for 5-tap horizontal, 3-tap vertical and 5-tap vertical configurations respectively. Pass absolute or relative path for basename with './', like './something'. The coefficients should be in a form of table like described in TRM, for example:
----
0 0 128 0 0
-1 13 124 -8 0
-2 30 112 -11 -1
-5 51 95 -11 -2
0 -9 73 73 -9
-2 -11 95 51 -5
-1 -11 112 30 -2
0 -8 124 13 -1
For 3-tap table, first and last columns should contain zeros.
LEDs and Display backlight
The LEDs can be controlled via /sys/class/leds/, and then a file [1]:
- pandora::sd1
- pandora::sd2
- pandora::charger
- pandora::power
- pandora::bluetooth
- pandora::wifi
- pandora::keypad_bl
Backlight can be controlled via /sys/class/backlight/.
Battery
There is bq27500 battery monitoring chip onboard, that exports various files at /sys/class/power_supply/bq27500-0/ , for example 'capacity' tells charge percenage left. See Data_provided_by_Battery_and_Power_driver for more details.
There are also similar directories at /sys/class/power_supply/twl4030_* that provide info while charging.
Misc
Overclocking
Can be performed by running a helper script
sudo /usr/pandora/scripts/op_cpuspeed.sh X
In case you want to do it from another program or non-interactively, you can use system() function (or similar) with:
sudo -n /usr/pandora/scripts/op_cpuspeed.sh -n X
- Note: you are advised not to change the clock explicitly from a program because optimal clock may differ on different pandora revisions, or somebody might want to run at different clock speed to save battery, CPU life, etc. . Let the user decide about the clock before running the program and set it using the system tools.
- Note: '-n' script argument is not available on pre-SuperZaxxon Final firmwares.