Appendix - notro/fbtft-spindle GitHub Wiki
Placeholder for various detailed information.
- X windows multiseat
- Which framebuffer does X windows use?
- X windows backlight script
- X windows and backlight
- Environment variables
- sudo and environment variables
- spidev
- firstboot.sh
- xinput_calibrator and SwapAxes
- rpi_power_switch
- Console blanking
It is possible to have two X windows sessions, on the LCD and on HDMI, at the same time.
However there is currently a problem with input devices (no sharing):
- startx first on HDMI and then on TFT: TFT takes the input devices
startx -- -layout HDMI &
startx -- -layout TFT &
- startx first on TFT and then on HDMI: HDMI takes input devices and TFT goes black
startx -- -layout TFT &
startx -- -layout HDMI &
/etc/X11/xorg.conf.d/50-fbtft.conf
# FBTFT xorg config file
#
# startx -- -layout TFT
# startx -- -layout HDMI
#
# When not specifying the layout, the first is used: TFT
#
Section "ServerLayout"
Identifier "TFT"
Screen 0 "ScreenTFT"
EndSection
Section "ServerLayout"
Identifier "HDMI"
Screen 0 "ScreenHDMI"
EndSection
Section "Screen"
Identifier "ScreenHDMI"
Monitor "MonitorHDMI"
Device "DeviceHDMI"
Endsection
Section "Screen"
Identifier "ScreenTFT"
Monitor "MonitorTFT"
Device "DeviceTFT"
Endsection
Section "Monitor"
Identifier "MonitorHDMI"
Endsection
Section "Monitor"
Identifier "MonitorTFT"
Endsection
Section "Device"
Identifier "DeviceHDMI"
Driver "fbturbo"
Option "fbdev" "/dev/fb0"
Option "SwapbuffersWait" "true"
EndSection
Section "Device"
Identifier "DeviceTFT"
Option "fbdev" "/dev/fb1"
EndSection
Links:
- http://wiki.gentoo.org/wiki/Multiseat
- https://wiki.archlinux.org/index.php/xorg_multiseat
- https://fedoraproject.org/wiki/Input_device_configuration
- http://en.wikibooks.org/wiki/Multiseat_Configuration/evdev
- man xorg.conf
- Build a Six-headed, Six-user Linux System
- Multiseat and LightDM
Order of precedence:
-
XF86Config 'fbdev' option (set in /usr/share/X11/xorg.conf.d/99-fbturbo.conf)
/etc/X11/xorg.conf.d/
/usr/share/X11/xorg.conf.d/ - FRAMEBUFFER environment variable (set in /etc/profile.d/framebuffer.sh)
- /dev/fb0
Source code grep for /dev/fb0:
xorg-server-1.15.0/hw/xfree86/fbdevhw/fbdevhw.c: dev = "/dev/fb0";
xorg-server-1.15.0/hw/kdrive/fbdev/fbdev.c: fbdevDevicePath = "/dev/fb0";
xorg-server-1.15.0/hw/kdrive/fbdev/fbinit.c: ("-fb path Framebuffer device to use. Defaults to /dev/fb0\n");
xorg/xserver/tree/hw/xfree86/fbdevhw/fbdevhw.c
static int
fbdev_open(int scrnIndex, char *dev, char** namep)
{
struct fb_fix_screeninfo fix;
int fd;
/* try argument (from XF86Config) first */
if (dev) {
fd = open(dev,O_RDWR,0);
} else {
/* second: environment variable */
dev = getenv("FRAMEBUFFER");
if ((NULL == dev) || ((fd = open(dev,O_RDWR,0)) == -1)) {
/* last try: default device */
dev = "/dev/fb0";
fd = open(dev,O_RDWR,0);
}
}
Source code browser: http://sourcecodebrowser.com/xorg-server/1.12.1/fbdevhw_8c.html
Workaround to make X windows turn off the backlight instead of just making the screen go black.
Install xscreensaver
$ sudo apt-get install xscreensaver
Screensaver mode
To save CPU, change Mode (while X is running):
$ DISPLAY=:0 xscreensaver-demo
Mode: Blank Screen Only
File->Quit
Changing blanking timeout
On small displays it's difficult to get to all the settings in the xscreensaver-demo window.
Edit ~/.xscreensaver
timeout: 0:10:00
Apply change if X is running (I had to do this even though the manual said it wasn't necessary):
$ DISPLAY=:0 xscreensaver-command -restart
Script
Create /usr/bin/X11/xbacklight.sh
#!/bin/sh
# Make sure there's backlight control
ls /sys/class/backlight/* >/dev/null 2>&1 || exit
process() {
while read line; do
# echo output appears in ~/.xsession-errors when script is autostarted
#echo "$line"
case "$line" in
UNBLANK*)
echo 0 | sudo tee /sys/class/backlight/*/bl_power 1>/dev/null
;;
BLANK*)
echo 4 | sudo tee /sys/class/backlight/*/bl_power 1>/dev/null
;;
esac
done
}
xscreensaver-command -watch | process
Executable
sudo chmod +x /usr/bin/X11/xbacklight.sh
Add to /etc/xdg/lxsession/LXDE/autostart
/usr/bin/X11/xbacklight.sh
Refs:
- Run a program on xscreensaver state change
- man xscreensaver
- man xscreensaver-command
- https://wiki.archlinux.org/index.php/XScreenSaver
X windows can't turn off the backlight on fbdev displays.
- http://www.shallowsky.com/linux/x-screen-blanking.html
- https://wiki.archlinux.org/index.php/backlight
- http://en.wikipedia.org/wiki/VESA_Display_Power_Management_Signaling
xbacklight
$ sudo apt-get install xbacklight
$ DISPLAY=:0 xbacklight -get
No outputs have backlight property
xorg/app/xbacklight/tree/xbacklight.c
backlight_cookie[0] = xcb_intern_atom (conn, 1, strlen("Backlight"), "Backlight");
backlight_cookie[1] = xcb_intern_atom (conn, 1, strlen("BACKLIGHT"), "BACKLIGHT");
backlight_reply = xcb_intern_atom_reply (conn, backlight_cookie[0], &error);
backlight_new = backlight_reply->atom;
backlight_reply = xcb_intern_atom_reply (conn, backlight_cookie[1], &error);
backlight_legacy = backlight_reply->atom;
if (backlight_new == XCB_NONE && backlight_legacy == XCB_NONE) {
fprintf (stderr, "No outputs have backlight property\n");
exit (1);
}
Is this the code that should have implemented the 'Backlight' property in our case?
http://cgit.freedesktop.org/xorg/driver/xf86-video-fbdev/tree/src/fbdev.c
[PATCH 2/2] radeon: add libbacklight support.
- http://lists.x.org/archives/xorg-driver-ati/2013-June/024787.html
- http://cgit.freedesktop.org/libbacklight/tree/libbacklight.c
The FBTFT image exports several touch and framebuffer related environment variables.
They are set with files in the /etc/profile.d directory.
Show variables with:
env
By default sudo only lets through a couple of enviroment variables to the executed program.
The FBTFT image lets through some more variables using files in the /etc/sudoers.d directory.
Show the effective variables with:
sudo env
Enable spidev for userspace SPI access. This can only be done on shields without a touch controller, as the Pi only has 2 available chip selects. Or disable touch by removing it from /etc/modules.
sudo modprobe spi-config devices=bus=0:cs=1:modalias=spidev:speed=500000
dmesg | tail
[ 7721.899378] spi_config_register: device description: bus=0:cs=1:modalias=spidev:speed=500000
[ 7721.899471] spi_config_match_cs: SPI0: check CS=0 to be 1
[ 7721.903949] spi_config_register:spi0.1: registering modalias=spidev with max_speed_hz=500000 mode=0 and no interrupt
ls -l /dev/spi*
crw-rw---T 1 root spi 153, 0 Jan 27 20:43 /dev/spidev0.1
/boot/firstboot.sh is executed on the first boot if it exists. It is run from /etc/rc.local
xinput_calibrator seem to have problems when the touchpanel is rotated 90 degrees left or right.
That's why a workaround is used to swap axes before running the program. This is done in /etc/X11/xorg.conf.d/99-calibration.conf
This module by Adafruit provides support for a Power on/off button.
Add to /etc/modules
rpi_power_switch gpio_pin=23 mode=0
Source code: https://github.com/notro/fbtft_tools/blob/master/rpi_power_switch/rpi_power_switch.c
Adafruit tutorial: Tactile switch as power button
consoleblank is a kernel command line argument. The value can be read back from /sys/module/kernel/parameters/consoleblank.
By default consoleblank = 600 seconds (the kernel variable name is 'blankinterval').
Even if set on the commandline, it can be overwritten later.
setterm
blankinterval can be set with: setterm -blank 10
(value is in minutes)
On Raspian blankinterval is set to 30 minutes by default (setterm and kbd is in the util-linux package).
Excerpts /etc/init.d/kbd
PKG=kbd
if [ -r /etc/$PKG/config ]; then
. /etc/$PKG/config
fi
# screensaver stuff
setterm_args=""
if [ "$BLANK_TIME" ]; then
setterm_args="$setterm_args -blank $BLANK_TIME"
fi
if [ "$setterm_args" ]; then
setterm $setterm_args
fi
The dot . includes the config file.
Excerpt /etc/kbd/config:
# screen blanking timeout. monitor remains on, but the screen is cleared to
# range: 0-60 min (0==never) kernels I've looked at default to 10 minutes.
# (see linux/drivers/char/console.c)
BLANK_TIME=30
Short version:
consoleblank sets blankinterval, or value 600 seconds is used.
kbd uses setterm to set blankinterval (1800 seconds).
console_timer fires after blankinterval expires and calls blank_screen_t().
blank_screen_t() sets blank_timer_expired=1 and schedules console_work which runs console_callback().
console_callback() calls do_blank_screen(), beacuse blank_timer_expired.
do_blank_screen() calls con_blank which is fbcon_blank().
fbcon_blank() calls fb_blank() which returns -EINVAL and therefore calls fbcon_generic_blank().
fbcon_generic_blank() sends a fb notifier: FB_EVENT_CONBLANK.
This is picked up by the backlight subsytem which calls backlight_update_status().
Excerpts from drivers/tty/vt/vt.c
int do_poke_blanked_console;
int console_blanked;
static int blankinterval = 10*60;
core_param(consoleblank, blankinterval, int, 0444);
static int __init con_init(void)
{
if (blankinterval) {
blank_state = blank_normal_wait;
mod_timer(&console_timer, jiffies + (blankinterval * HZ));
}
}
console_initcall(con_init);
static void setterm_command(struct vc_data *vc)
{
switch(vc->vc_par[0]) {
case 9: /* set blanking interval */
blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
poke_blanked_console();
break;
case 13: /* unblank the screen */
poke_blanked_console();
break;
}
}
void poke_blanked_console(void)
{
if (console_blanked)
unblank_screen();
else if (blankinterval) {
mod_timer(&console_timer, jiffies + (blankinterval * HZ));
blank_state = blank_normal_wait;
}
}
void do_blank_screen(int entering_gfx)
{
blank_state = blank_off;
i = vc->vc_sw->con_blank(vc, vesa_off_interval ? 1 : (vesa_blank_mode + 1), 0);
del_timer_sync(&console_timer);
blank_timer_expired = 0;
}
/*
* Called by timer as well as from vt_console_driver
*/
void do_unblank_screen(int leaving_gfx)
{
if (blankinterval) {
mod_timer(&console_timer, jiffies + (blankinterval * HZ));
blank_state = blank_normal_wait;
}
console_blanked = 0;
if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
}
static DEFINE_TIMER(console_timer, blank_screen_t, 0, 0);
/*
* We defer the timer blanking to work queue so it can take the console mutex
* (console operations can still happen at irq time, but only from printk which
* has the console mutex. Not perfect yet, but better than no locking
*/
static void blank_screen_t(unsigned long dummy)
{
blank_timer_expired = 1;
schedule_work(&console_work);
}
static DECLARE_WORK(console_work, console_callback);
static void console_callback(struct work_struct *ignored)
{
if (blank_timer_expired) {
do_blank_screen(0);
blank_timer_expired = 0;
}
}
Excerpts from drivers/video/console/fbcon.c
static const struct consw fb_con = {
.con_blank = fbcon_blank,
};
static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
{
if (fb_blank(info, blank))
fbcon_generic_blank(vc, info, blank);
}
static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
int blank)
{
fb_notifier_call_chain(FB_EVENT_CONBLANK, &event);
}
The backlight subsystem picks up a FB_EVENT_CONBLANK event.
drivers/video/backlight/backlight.c
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
if (fb_blank == FB_BLANK_UNBLANK &&
!bd->fb_bl_on[node]) {
bd->fb_bl_on[node] = true;
if (!bd->use_count++) {
bd->props.state &= ~BL_CORE_FBBLANK;
bd->props.fb_blank = FB_BLANK_UNBLANK;
backlight_update_status(bd);
}
} else if (fb_blank != FB_BLANK_UNBLANK &&
bd->fb_bl_on[node]) {
bd->fb_bl_on[node] = false;
if (!(--bd->use_count)) {
bd->props.state |= BL_CORE_FBBLANK;
bd->props.fb_blank = fb_blank;
backlight_update_status(bd);
}
}
Backlight handling in FBTFT
fb_blank returns -EINVAL on most FBTFT drivers.
int fbtft_backlight_update_status(struct backlight_device *bd)
{
if ((bd->props.power == FB_BLANK_UNBLANK) && (bd->props.fb_blank == FB_BLANK_UNBLANK))
gpio_set_value(par->gpio.led[0], polarity);
else
gpio_set_value(par->gpio.led[0], !polarity);
}