El dispositivo framebuffer

0. Introducción

El dispositivo frame buffer proporciona una capa de abstracción para el hardware gráfico. Esto representa la memoria intermedia del hardware de vídeo y permite al software acceder al hardware gráfico a través de una interfaz bien definida. Por eso el software no necesita conocer nada en absoluto acerca de las características de bajo nivel del hardware.

Al dispositivo se accede a través de los nodos especiales del dispositivo, generalmente localizados en el directorio /dev (/dev/fb*)

1. /dev/fb* desde la perspectiva del usuario

Desde el punto de vista del usuario el dispositivo frame buffer aparece como cualquier otro dispositivo en /dev. Es un dispositivo I/O (character device) que utiliza los nodos mayor 29 y menor 0. El menor especifica el número de frame buffer.

Por convención se usan los siguientes nodos de dispositivo.

  • 0 = /dev/fb0 Primer frame buffer
  • 1 = /dev/fb1 Segundo frame buffer
  • ...
  • 31 = /dev/fb31 Trigésimo segundo frame buffer

Por compatibilidad se podrían crear los siguientes enlaces simbólicos

/dev/fb0current -> fb0
/dev/fb1current -> fb1

etcétera...

Los dispositivos frame buffer también son dispositivos de memoria "normales", esto es, se pueden leer y escribir. Por ejemplo se puede sacar una captura de pantalla con:

cp /dev/fb0 micaptura

Pueden haber más de un frame buffer al mismo tiempo, por ejemplo, si tenemos una gráfica además de la integrada en la placa base. Los correspondientes frame buffer (/dev/fb0 y /dev/fb1 etc..) trabajarán independientemente.

Las aplicaciones que usan el dispositivo frame buffer ( por ejemplo el sevidor X ) utilizan /dev/fb0 ( el software más antiguo usa /dev/fbcurrent ). Se puede especificar un dispositivo alternativo estableciendo la variable de entorno $FRAMEBUFFER a la ruta del dispositivo deseado. Por ejemplo ( para los usuarios de sh/bash ):

export FRAMEBUFFER=/dev/fb1

o (para los usuarios de csh ):

setenv FRAMEBUFFER /dev/fb1

Después de esto el servidor de X utilizará el segundo frame buffer.

2. /dev/fb* desde la perspectiva del programador

Como es sabido un frame buffer es un dispositivo de memoria como /dev/mem y tiene las mismas características. Se puede leer, escribir, buscar algo en él y mmap()earlo. La diferencia reside en que la memoria que aparece en ese archivo especial no es toda la memoria sino el frame buffer ( marco, buffer ) de algún hardware de vídeo.

/dev/fb* también permite realizar múltiples ioctls ( I/O controls) en él, mediante los cuales se puede consultar y establecer un montón de información del hardware. El mapa de colores también se maneja via ioctls. En linux/fb.h hay más información acerca de las llamadas al sistema (ioctls) disponibles y las estructuras de datos que funcionan. Una pequeña aproximación:

  • Se puede solicitar información indeleble sobre el hardware, como el nombre, organización de la memoria de la pantalla (planos, píxeles empaquetados, ...) y la dirección y longitud de la memoria de la pantalla.
  • Se puede solicitar y cambiar información del hardware como la geometría visible y virtual, la profundidad, el formato del mapa de colores, sincronización, etc... Si se intenta cambiar esa información, puede que el driver redondee (al alza) algunos valores para alcanzar las capacidades del hardware (o devolver EINVAL(?) si eso no es posible).
  • Se puede obtener y establecer partes del mapa de colores. La comunicación se hace con 16 bits para cada parte de color (rojo,verde,azul,transparencia) para dar soporte a todo el hardware existente. El driver hará todos los cálculos necesarios para aplicarlos al hardware (redondeará hacia abajo, quizá sacrifique la transparencia).
  • Toda esta abstracción resulta en una implementación de programas más sencilla a la par que portable. Por ejemplo, el servidor de X trabaja completamente en /dev/fb* y así no tiene necesidad de saber, por ejemplo, como se organizan los registros de color en un hardware en concreto. XF86_FBDev es un servidor X general para hardware de video de mapas de bits sin aceleración. Lo único que hay que tener en cuenta en la programación de aplicaciones es la organización de la pantalla (planos de bits o pixels "chunky" etc...) porque trabaja directamente en los datos de la imagen del frame buffer.
  • En el futuro se planea que los drivers de frame buffer para las tarjetas gráficas puedan ser implementados como módulos del kernel que se carguen en tiempo de ejecución. Como un driver que solo tenga que llamar a register_framebuffer() y proporcionar algunas funciones. Escribir y distribuir dichos drivers independientemente del kernel evitará muchos problemas.

3. Mantenimiento de la resolución Frame Buffer

Las resoluciones de frame buffer se mantienen usando la utilidad "fbset". Se pueden cambiar las propiedades del modo de vídeo o de un dispositivo frame buffer. Su principal uso es cambiar el modo de vídeo actual, por ejemplo durante el arranque en uno de los archivos /etc/rc.* o /etc/ini.d/*.

fbset utiliza una base de datos almacenada en un fichero de configuración, por eso se pueden añadir fácilmente los propios modos de vídeo y hacer referencia a ellos con un simple identificador.

4. El servidor X

El servidor X es la principal aplicación para el dispositivo frame buffer. Desde la versión 3.2 de XFree86, el servidor X es parte de XFree86 y tiene dos modos:

  • Si la subsección "Display" para el driver "fbdev" en el fichero /etc/XF86Config contiene una línea
    Modes "default"

    el servidor X utilizará el esquema descrito más arriba, por ejemplo arrancará con la resolución determinada por /dev/fb0 (o $FRAMEBUFFER, si está establecida. Todavía hay que especificar la profundidad de color (usando la clave Depth) y la resolución virtual (usando la clave Virtual). Es la configuración más simple, pero tiene algunas limitaciones.

  • Por lo tanto también es posible especificar resoluciones en el archivo /etc/XF86Config. Esto permite cambiar las resoluciones al vuelo mientras se mantiene la misma resolución virtual. El dispositivo framebuffer que se usa sigue siendo /dev/fb0current (o $FRAMEBUFFER), pero las resoluciones disponibles están definidas ahora por /etc/XF86Config. La desventaja es que hay que especificar las tasas de refresco en un formato diferente ("fbset -x" puede ayudar).

Para sintonizar un modo de vídeo se puede usar fbset o xvidtune. Darse cuenta de que xvidtune no trabaja 100% con XF86_FBDev: los valores de reloj reportados siempre son incorrectos.

5. Tasas de refresco del modo de vídeo

Un monitor dibuja una imagen en la pantalla usando un haz de electrones (3 rayos o haces para los modelos de color). El frente del monitor se cubre por un patrón de fósforos coloreados (pixels). Si a un fósforo se le "golpea" con un electrón, este emite un fotón y así se hace visible.

El haz de electrones dibuja líneas horizontales (scanlines) de derecha a izquierda y desde arriba hacia abajo. Modificando la intensidad del haz de electrones se muestran los pixels con diferentes colores e intensidades.

Después de cada barrido el haz de electrones retorna al lado izquierdo de la pantalla y baja a la siguiente línea: a esto se le llama "horizontal retrace". Después de que se ha pintado la pantalla entera (frame), el haz retorna a la esquina superior izquierda ("vertical retrace"). Durante ambos "retraces", vertical y horizontal, el haz de electrones se apaga.

La velocidad a la que el haz de electrones pinta los píxels es determinada por la frecuencia de reloj de la tarjeta gráfica. Para una frecuencia de reloj de, por ejemplo, 28.37516 Mhz (millones de ciclos por segundo), cada píxel dura 35242 ps (picosegundos)

      1/(28.37516E6 Hz) = 35.242E-9 s

Si la resolución de la pantalla es de 640x480 entonces

      640*35.242E-9 s = 22.555E-6 s

para pintar los 640 píxels de la línea de barrido (xres).

Pero el retorno horizontal también toma algo de tiempo (por ejemplo 272 "píxels"), por eso un barrido horizontal completo se lleva

      (640+272)*35.242E-9 s = 32.141E-6 s

Digamos que el ratio horizontal está en 31 Khz:

       1/(32.141E-6 s) = 31.113E3 Hz

La pantalla completa cuenta 480 líneas (yres), pero también hay que considerar el retorno vertical (por ejemplo 49 "líneas"). Por eso una pantalla completa llevará.

       (480+49)*32.141E-6 s = 17.002E-3 s

El ratio vertical estará sobre los 59 Hz:

       1/(17.002E-3 s) = 58.815 Hz

Esto quiere decir que los datos de la pantalla se refrescan alrededor de 59 veces por segundo. Para tener una imagen estable sin parpadeo visible, VESA recomienda un ratio de escaneado de al menos 72 Hz. Pero el parpadeo perceptible depende del ojo humano: algunas personas pueden usar 50 Hz mientras que otras percibirán ese parpadeo mientras ese ratio sea menor de 80 Hz.

Como el monitor no sabe cuando empieza un nuevo escaneado de línea, la tarjeta gráfica proporcionará un pulso de sincronización (horizontal sync o hsync) para cada escaneado de línea. De forma similar proporciona un pulso de sincronización para cada nuevo marco (vertical sync o vsync). La posición de la imagen en la pantalla está influenciada por los tiempos en los que ocurren los pulsos de sincronización.

La siguiente figura sumariza todos los tiempos. El retorno horizontal es la suma del margen izquierdo, el margen derecho y la longitud de hsync mientras que el retorno horizontal es la suma del margen superior, el inferior y la longitud de vsync.

    +----------+---------------------------------------------+----------+-------+
    |          |                ↑                            |          |       |
    |          |                |upper_margin                |          |       |
    |          |                ↓                            |          |       |
    +----------###############################################----------+-------+
    |          #                ↑                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |   left   #                |                            #  right   | hsync |
    |  margin  #                |       xres                 #  margin  |  len  |
    |<-------->#<---------------+--------------------------->#<-------->|<----->|
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |yres                        #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                |                            #          |       |
    |          #                ↓                            #          |       |
    +----------###############################################----------+-------+
    |          |                ↑                            |          |       |
    |          |                |lower_margin                |          |       |
    |          |                ↓                            |          |       |
    +----------+---------------------------------------------+----------+-------+
    |          |                ↑                            |          |       |
    |          |                |vsync_len                   |          |       |
    |          |                ↓                            |          |       |
    +----------+---------------------------------------------+----------+-------+


El dispositivo framebuffer espera todos los tiempos horizontales en número de pulsos de reloj en picosegundos ( por ej. 1E-12 s ) y los tiempos verticales en número de escaneos de línea.

6. Equivalencias de valores de refresco XFree86 y dispositivo frame buffer

Una "modeline" XFree86 consiste en los siguientes campos:

   "800x600"     50      800  856  976 1040    600  637  643  666
   < nombre >   DCF       HR  SH1  SH2  HFL     VR  SV1  SV2  VFL

El dispositivo frame buffer utiliza los siguientes campos:

  • pixclock: reloj de pixel en ps (picosegundos)
  • left_margin: tiempo desde sync a picture
  • right_margin: tiempo desde picture a sync
  • upper_margin: tiempo desde sync a picture
  • lower_margin: tiempo desde picture a sync
  • hsync_len: longitud de horizontal sync
  • vsync_len: longitud de vertical sync

  1. Pixelclock:
    • xfree: en MHz
    • fb: en picoseconds (ps)
    • pixclock = 1000000 / DCF
  2. horizontal timings:
    • left_margin = HFL - SH2
    • right_margin = SH1 - HR
    • hsync_len = SH2 - SH1
  3. vertical timings:
    • upper_margin = VFL - SV2
    • lower_margin = SV1 - VR
    • vsync_len = SV2 - SV1


Se pueden encontrar buenos ejemplos de tasas de refresco de VESA en el árbol de las fuentes XFree86, "xc/programs/Xserver/hw/xfree86/doc/modeDB.txt".

7. Referencias

Para información más específica sobre el dispositivo frame buffer y sus aplicaciones visitar la web de Linux-fbdev:

http://linux-fbdev.sourceforge.net/

y la siguiente documentación:

  • Páginas de manual de fbset: fbset(8), fb.modes(5)
  • Páginas de manual de XFree86: XF68_FBDev(1), XF86Config(4/5)
  • Las fuentes del poderoso kernel:
    • linux/drivers/video/
    • linux/include/linux/fb.h
    • linux/include/video/

8. Lista de correo

Lista de correo en kernel.org:
linux-fbdev@vger.kernel.org.

http://sourceforge.net/projects/linux-fbdev/ para información de suscripción.

9. Descargas

Todos los archivos necesarios así como la última versión de fbset pueden encontrarse en sus repositorios habituales.

10. Créditos

This readme was written by Geert Uytterhoeven, partly based on the original
`X-framebuffer.README' by Roman Hodek and Martin Schaller. Section 6 was
provided by Frank Neumann.

The frame buffer device abstraction was designed by Martin Schaller.