Драйверы кадрового буфера

Предыдущая  Содержание  Следующая V*D*V

Теперь, когда вы понимаете идею в API кадрового буфера и как он обеспечивает аппаратную независимость, давайте изучим архитектуру низкоуровнего драйвера устройства с кадровым буфером на примере навигационной системы.

Пример устройства: Навигационная система

Рисунок 12.6 показывает видео операции на примере навигационной системы машины, построенной на встроенном процессоре. Приёмник GPS передаёт потоки координат на процессор через интерфейс UART. Приложение рисует графику на основе полученной информации о местоположении и обновляет кадровый буфер в системной памяти. Драйвер кадрового буфера через DMA передаёт данные изображения в буферы дисплея, которые являются частью LCD контроллера процессора. Контроллер передаёт пиксельные данные для отображения на ЖК панель QVGA.

 

Рисунок 12.6. Отображение на навигационном устройстве с Linux.

Рисунок 12.6. Отображение на навигационном устройстве с Linux.

 

Наша цель заключается в разработке программного обеспечения для этой системы, работающего с видео. Давайте предположим, что Linux поддерживает процессор, используемый в этом навигационном устройстве, и что все зависящие от архитектуры интерфейсы, такие как DMA, поддерживаются ядром.

 

Одна из возможных аппаратных реализаций устройства, показанного на Рисунке 12.6, заключается в использовании процессоре Freescale i.MX21. Ядром процессора в этом случае является ядро ARM9, а встроенным видеоконтроллером является контроллер жидкокристаллического дисплея Liquid Crystal Display Controller (LCDC). Микропроцессоры, как правило, имеют высокопроизводительную внутреннюю шину, которая подключена к таким контроллерам, как DRAM и видео. В случае iMX.21 эта шина называется Улучшенная высокопроизводительная шина, Advanced High-Performance Bus (AHB). LCDC подключается к AHB.

 

Программное обеспечение навигационной системы для работы с видео построено в целом как приложение GPS, работающее через низкоуровневый драйвер кадрового буфера для контроллера ЖК-дисплея. Приложение получает координат местоположения от приёмника GPS читая /dev/ttySX, где X - это номер UART, подключённого к ресиверу. Затем оно переводит зафиксированную географическую информацию в картинку и записывает пиксельные данные в кадровый буфер, связанных с контроллером ЖК-дисплея. Это делается по аналогии с Распечаткой 12.1, за исключением того, что данные изображения отличаются от нулей для очистки экрана.

 

Далее в этом разделе основное внимание уделяется только низкоуровневому драйверу кадрового буфера. Как и многие другие драйверные  подсистемы, полный набор объектов, режимов и опций, предлагаемых уровнем ядра кадрового буфера, является сложным и может быть изучен только при получении опыта кодирования. Драйвер кадрового буфера для примера системы навигации является относительно простым и является лишь отправной точкой для более глубоких исследований.

 

Таблица 12.1 описывает регистровую модель LCD контроллера показанного на Рисунке 12.6. Драйвер кадрового буфера в Распечатке 12.2 работает с этими регистрами.

 

Таблица 12.1. Назначение регистров контроллера LCD, показанного на Рисунке 12.6

 

Имя регистра

Используется для конфигурации

SIZE_REG

Максимальные размеры ЖК панели по X и Y

HSYNC_REG

Длительность HSYNC

VSYNC_REG

Длительность VSYNC

CONF_REG

Число битов на пиксель, полярность пикселей, делители частоты для генерации pixclock, режим цветной/монохромный и так далее

CTRL_REG

Включение/выключение LCD контроллера, частоты и DMA

DMA_REG

Начальный адрес для DMA кадрового буфера, размер и размеры меток

STATUS_REG

Статусные значения

CONTRAST_REG

Уровень контрастности

 

Наш драйвер кадрового буфера (названный myfb) реализован в Распечатке 12.2 в виде драйвера платформы. Как вы узнали в Главе 6, платформа представляет собой псевдо шину, обычно используемую для подключения простых устройств, интегрированных на кристалле, к модели устройств ядра. Архитектурно-зависимый код настройки (в arch/your-arch/your-platform/) добавляет платформу, используя platform_device_add(); но для простоты, метод probe() драйвера myfb выполняет это до регистрации себя в качестве  драйвера платформы. Обратитесь к разделу "Пример устройства: Сотовый телефон" в Главе 6, где описана общая архитектура драйвера платформы и соответствующие точки входа.

Структуры данных

Давайте посмотрим на основные структуры данных и методы, связанные с драйверами кадрового буфера, а затем рассмотрим myfb. Основными структурами являются следующие две:

 

1.Структура fb_info является центральной структурой данных драйверов кадрового буфера. Эта структура определена в include/linux/fb.h следующим образом:
 
struct fb_info {
   /* ... */
   struct fb_var_screeninfo var; /* Изменяемая информация экрана. Обсуждалась ранее. */
   struct fb_fix_screeninfo fix; /* Неизменяемая информация экрана. Обсуждалась ранее. */
   /* ... */
   struct fb_cmap cmap;          /* Таблица цветов. Обсуждалась ранее. */
   /* ... */
   struct fb_ops *fbops;         /* Операции драйвера. Обсуждается далее. */
   /* ... */
   char __iomem *screen_base;    /* Виртуальный адрес кадрового буфера */
   unsigned long screen_size;    /* Размер кадрового буфера */
   /* ... */
   /* Остальное зависит от устройства */
   void *par; /* Private area */
};
 
Память для fb_info выделяется через framebuffer_alloc(), библиотечная процедура предоставляется ядром кадрового буфера. Эта функция также имеет в качестве аргумента размер выделяемой области и добавляет в конец выделенной памяти для fb_info. Обратиться к этой области можно используя указатель par структуры fb_info. Значения полей fb_info, таких как fb_var_screeninfo и fb_fix_screeninfo были обсуждены в разделе "API кадрового буфера".
 

2.Структура fb_ops содержит адреса всех точек входа, предоставляемых низкоуровневым драйвером кадрового буфера. Первые несколько методов в fb_ops необходимы для функционирования драйвера, а остальные являются необязательными, обеспечивающими ускорение графики. Ответственность каждой функции кратко объясняется комментариями:
 
struct fb_ops {
   struct module *owner;
   /* Открытие драйвера */
   int (*fb_open)(struct fb_info *info, int user);
   /* Закрытие драйвера */
   int (*fb_release)(struct fb_info *info, int user);
   /* ... */
   /* Проверка видео параметров на здравый смысл */
   int (*fb_check_var)(struct fb_var_screeninfo *var,
                       struct fb_info *info);
   /* Конфигурирование регистров видео контроллера */
   int (*fb_set_par)(struct fb_info *info);
   /* Создание карты палитры псевдо цветов */
   int (*fb_setcolreg)(unsigned regno, unsigned red,
                       unsigned green, unsigned blue,
                       unsigned transp, struct fb_info *info);
   /* Включение/отключение дисплея */
   int (*fb_blank)(int blank, struct fb_info *info);
   /* ... */
   /* Метод ускорения для заполнения прямоугольника линиями пикселей */
   void (*fb_fillrect)(struct fb_info *info,
                       const struct fb_fillrect *rect);
   /* Метод ускорения копирования одной прямоугольной области в другую */
   void (*fb_copyarea)(struct fb_info *info,
               const struct fb_copyarea *region);
   /* Метод ускорения рисования на экране картинки */
   void (*fb_imageblit)(struct fb_info *info,
                   const struct fb_image *image);
   /* Метод ускорения для поворота изображения */
   void (*fb_rotate)(struct fb_info *info, int angle);
   /* Интерфейс ioctl для поддержки зависимых от устройства команд */
   int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
                   unsigned long arg);
   /* ... */
};

 

Давайте теперь посмотрим на методы драйвера, реализованные для драйвера myfb в Распечатке 12.2.

Проверка и установка параметров

Проверку разумности переменных, таких как разрешение по X, разрешение по Y и число битов на пиксель, выполняет метод fb_check_var(). Таким образом, если вы используете fbset для установки разрешения по X менее минимального, поддерживаемого контроллером LCD (64 в нашем примере), то эта функция будет ограничивать его до минимального значения, позволяемого оборудованием.

 

fb_check_var() также устанавливает соответствующий формат RGB. Наш пример использует 16 бит на пиксель, и контроллер связывает каждое слово данных в кадровом буфере с часто используемым кодом RGB565: 5 бит для красного, 6 бит для зелёного и 5 бит для синего. Смещения в словах данных для каждого из трёх цветов также устанавливаются соответственно.

 

Метод fb_set_par() настраивает регистры контроллера LCD в зависимости от значений, указанных в fb_info.var. Это включает в себя настройку

 

Длительности горизонтальной синхронизации, отступ слева и справа в HSYNC_REG;

Длительности вертикальной синхронизации, отступ сверху и снизу в VSYNC_REG;

Видимое разрешение по X и Y в SIZE_REG;

Параметры DMA в DMA_REG.

 

Предположим, что приложение GPS пытается изменить разрешение QVGA дисплея на 50x50. Ниже приводится цепь событий:

 

1.Дисплей проинициализирован в разрешение QVGA:
 
bash> fbset
mode "320x240-76"
   # D: 5.830 MHz, H: 18.219 kHz, V: 75.914 Hz
   geometry 320 240 320 240 16
   timings 171521 0 0 0 0 0 0
   rgba 5/11,6/5,5/0,0/0
endmode
 

2.Приложение делает следующее:
 
struct fb_var_screeninfo vinfo;
fbfd = open("/dev/fb0", O_RDWR);
vinfo.xres = 50;
vinfo.yres = 50;
vinfo.bits_per_pixel = 8;
 
ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo);
 
Заметим, что это эквивалент команды
 
fbset -xres 50 -yres 50 -depth 8
 

3.ioctl FBIOPUT_VSCREENINFO на предыдущем этапе вызывает срабатывание myfb_check_var(). Этот метод драйвера выражает недовольство и округляет запрошенное разрешение до минимума, поддерживаемого оборудованием, которое в этом случае 64x64.

 

4.из ядра кадрового буфера вызывается myfb_set_par(), которая программирует новые параметры отображения в регистры контроллера LCD.
 

5.fbset теперь выводит новые параметры:
 
bash> fbset
mode "64x64-1423"
   # D: 5.830 MHz, H: 91.097 kHz, V: 1423.386 Hz
   geometry 64 64 320 240 16
   timings 171521 0 0 0 0 0 0
   rgba 5/11,6/5,5/0,0/0
endmode
 

Цветовые режимы

Обычные цветовые режимы, поддерживаемые видео оборудованием, включают псевдо-цвет и естественные цвета (true color). В первом случае индексные номера преобразуются в пиксели формата RGB путём кодировок. Выбрав подмножество доступных цветов и с помощью индексирования соответствующих цветов вместо значения самого пикселя, вы можете снизить требования к памяти кадрового буфера. Однако, используемое оборудование должно поддерживать эту схему изменения цветового набора (или палитры).

 

В режиме true color (который есть то, что поддерживает наш пример LCD контроллера), изменение палитр не являются актуальным. Тем не менее, вам всё равно придётся удовлетворить потребности консольного драйвера кадрового буфера, который использует только 16 цветов. Для этого вам необходимо создать псевдо палитру кодированием соответствующих 16-ти необработанных значений RGB в биты, которые могут быть напрямую переданы оборудованию. Эта псевдо палитра хранится в поле pseudo_palette структуры fb_info. В Распечатке 12.2, myfb_setcolreg() заполняет её следующим образом:

 

((u32*)(info->pseudo_palette))[color_index] =

            (red << info->var.red.offset)     |

            (green << info->var.green.offset) |

            (blue << info->var.blue.offset)   |

            (transp << info->var.transp.offset);

 

Наш LCD контроллер использует 16 бит на пиксель и формат RGB565, и как вы видели ранее, метод fb_check_var() гарантирует, что  значения красного, зелёного и синего имеют смещения 11, 5 и 0, соответственно. В дополнение к индексу цвета и значениям красного, синего и зелёного, fb_setcolreg() принимает аргумент transp, чтобы указать желаемые эффекты прозрачности. Этот механизм, называемый альфа-канал, сочетает указанное значение пикселя с цветом фона. LCD контроллер в данном примере не поддерживает альфа-смешивание, так что myfb_check_var() устанавливает смещение и размер transp в ноль.

 

Абстракция кадрового буфера достаточно мощна, чтобы изолировать приложения от характеристик панели дисплея, будь это RGB или BGR, или что-то другое. Смещения красного, синего и зелёного, установленные fb_check_var(), передаются в пользовательское пространство с помощью структуры fb_var_screeninfo, получаемой через ioctl() FBIOGET_VSCREENINFO. Поскольку такие приложения, как X Windows совместимы с кадровым буфером, они рисуют пиксели в кадровом буфере в зависимости от смещения цветов, возвращаемых этим ioctl().

 

Размеры битовых полей, используемые кодировкой RGB (5+6+5 = 16 в данном случае) называются глубиной цвета, которая используется консольным драйвером кадрового буфера для выбора файла логотипа для отображения во время загрузки (смотрите раздел "Логотип при загрузке").

Гашение экрана

Поддержку для гашения и включения дисплея обеспечивает метод fb_blank(). Это используется в основном для управления питанием. Для гашения дисплея навигационной системы после 10-ти минутного периода бездействия сделайте следующее:

 

bash> setterm -blank 10

 

Эта команда проходит вниз по уровням до уровня кадрового буфера и в результате вызывает myfb_blank(), который программирует соответствующие биты в CTRL_REG.

Методы ускорения

Если ваш пользовательский интерфейс должен выполнять тяжёлые операции, таких как смешивание, изменение размера, двигать рисунки, или генерировать динамический градиент, вам, вероятно, потребуется ускорение графики, чтобы получить приемлемый уровень производительности. Давайте кратко рассмотрим методы fb_ops, которые можно использовать, если ваша видеокарта поддерживает ускорение графики.

 

Метод fb_imageblit() рисует на дисплее изображение. Эта точка входа даёт возможность драйверу использовать любые специальные возможности, которыми может обладать ваш видео контроллер, чтобы ускорить эту операцию. cfb_imageblit() является универсальной функцией библиотеки, предоставляемой ядром кадрового буфера, чтобы выполнять это, если у оборудование не имеет ускорителя. Она используется, например, для вывода логотипа на экран во время загрузки. fb_copyarea() копирует прямоугольную область из одной области экрана в другую. cfb_copyarea() представляет собой оптимальный способ сделать это, если ваш графический контроллер не обладает магией, чтобы ускорить эту операцию. Метод fb_fillrect() быстро заполняет прямоугольник пиксельными линиями. cfb_fillrect() предлагает общий способ достижения этой цели без ускорителя. Контроллер LCD в нашей навигационной системы не предоставляет ускорения, так что драйвер в примере заполняет эти методы используя универсальные оптимизированные процедуры, предлагаемые ядром кадрового буфера.

 

 

DirectFB

 

DirectFB (www.directfb.org) является библиотекой, построенной поверх интерфейса кадрового буфера, которая предоставляет основу простого оконного менеджера и способы для аппаратного ускорения графики и виртуальные интерфейсы, которые позволяют сосуществовать нескольким приложениям, работающим с кадровым буфером. DirectFB, наряду с поддерживающими ускорение драйверами кадрового буфера внизу и использующим DirectFB движком рендеринга, таким как Cairo (www.cairographics.org) поверх, иногда используется в интенсивно работающих с графикой встроенных устройствах вместо более традиционных решений, таких как X Windows.

 

 

DMA из кадрового буфера

LCD контроллер в навигационной системе содержит механизм DMA, который получает кадры изображения из системной памяти. Контроллер отправляет полученные графические данные на панель отображения. Скорость DMA поддерживает частоту обновления экрана. Некэшируемый кадровый буфер, подходящий для согласованного доступа, выделяется с использованием dma_alloc_coherent() в myfb_probe(). (Мы обсудили согласованного отображение DMA в Главе 10, "Соединение компонентов периферии".) myfb_set_par() записывает этот выделенный адрес DMA в регистр DMA_REG LCD контроллера.

 

Когда драйвер включает DMA вызывая myfb_enable_controller(), контроллер начинает переправлять пиксельные данные из кадрового буфера на дисплей с помощью синхронного DMA. Таким образом, когда приложение GPS отображает кадровый буфер (с помощью mmap()) и записывает в неё информацию о местоположении, на LCD закрашиваются пиксели.

Контраст и подсветка

LCD контроллер навигационной системы поддерживает управление контрастом с помощью регистра CONTRAST_REG. Драйвер экспортирует это в пространство пользователя с помощью myfb_ioctl(). Приложение GPS управляет контрастом следующим образом:

 

unsigned int my_fd, desired_contrast_level = 100;

/* Открываем кадровый буфер */

my_fd = open("/dev/fb0", O_RDWR);

ioctl(my_fd, MYFB_SET_BRIGHTNESS, &desired_contrast_level);

 

ЖК панель навигационной системы подсвечивается с помощью задней подсветки. Процессор управляет инвертером подсветки через выводы GPIO, поэтому вы можете включить или отключить свет управляя соответствующими контактами. Ядро абстрагирует универсальный интерфейс подсветки через узлы sysfs. Чтобы связаться с этим интерфейсом, ваш драйвер должен заполнить структуру backlight_ops методами получения и обновления настроек подсветки, и зарегистрировать её в ядре с помощью backlight_device_register(). Загляните в drivers/video/backlight/ для поиска исходников интерфейса подсветки и поищите в дереве drivers/ по названию backlight_device_register() видео драйверы, использующие этот интерфейс. В Распечатке 12.2 операции управления подсветкой не реализованы.

 

Распечатка 12.2. Драйвер кадрового буфера для навигационной системы

 

Код:

#include <linux/fb.h>

#include <linux/dma-mapping.h>

#include <linux/platform_device.h>

 

/* Карта адресов регистров LCD контроллера */

#define LCD_CONTROLLER_BASE 0x01000D00

#define SIZE_REG     (*(volatile u32 *)(LCD_CONTROLLER_BASE))

#define HSYNC_REG    (*(volatile u32 *)(LCD_CONTROLLER_BASE + 4))

#define VSYNC_REG    (*(volatile u32 *)(LCD_CONTROLLER_BASE + 8))

#define CONF_REG     (*(volatile u32 *)(LCD_CONTROLLER_BASE + 12))

#define CTRL_REG     (*(volatile u32 *)(LCD_CONTROLLER_BASE + 16))

#define DMA_REG      (*(volatile u32 *)(LCD_CONTROLLER_BASE + 20))

#define STATUS_REG   (*(volatile u32 *)(LCD_CONTROLLER_BASE + 24))

#define CONTRAST_REG (*(volatile u32 *)(LCD_CONTROLLER_BASE + 28))

#define LCD_CONTROLLER_SIZE 32

 

/* Ресурсы для LCD контроллера как устройства платформы */

static struct resource myfb_resources[] = {

    [0] = {

        .start = LCD_CONTROLLER_BASE,

        .end   = LCD_CONTROLLER_SIZE,

        .flags = IORESOURCE_MEM,

    },

};

 

/* Определение устройства платформы */

static struct platform_device myfb_device = {

    .name = "myfb",

    .id = 0,

    .dev = {

        .coherent_dma_mask = 0xffffffff,

    },

    .num_resources = ARRAY_SIZE(myfb_resources),

    .resource = myfb_resources,

};

 

/* Установка параметров LCD контроллера */

static int

myfb_set_par(struct fb_info *info)

{

    unsigned long adjusted_fb_start;

    struct fb_var_screeninfo *var = &info->var;

    struct fb_fix_screeninfo *fix = &info->fix;

 

    /* Старшие 16 бит HSYNC_REG содержат длительность HSYNC, следующие 8

       содержат отступ слева, 8 младших содержат отступ справа */

    HSYNC_REG = (var->hsync_len << 16) |

                (var->left_margin << 8)|

                (var->right_margin);

 

    /* Старшие 16 бит VSYNC_REG содержат длительность VSYNC, следующие 8

       содержат отступ сверху, 8 младших содержат отступ снизу */

    VSYNC_REG = (var->vsync_len << 16) |

                (var->upper_margin << 8)|

                (var->lower_margin);

 

    /* Старшие 16 бит SIZE_REG содержат xres, младшие 16 содержат yres */

    SIZE_REG = (var->xres << 16) | (var->yres);

 

    /* Устанавливаем число битов на пиксель, полярность битов, делители частоты

       для pixclock, и режим цвет/монохром в CONF_REG */

    /* ... */

 

    /* Заполняем DMA_REG начальным адресом кадрового буфера

       выделенного когерентно в myfb_probe(). Изменяем этот адрес

       для учёта смещения начала области экрана */

    adjusted_fb_start = fix->smem_start +

        (var->yoffset * var->xres_virtual + var->xoffset) *

        (var->bits_per_pixel) / 8;

    __raw_writel(adjusted_fb_start, (unsigned long *)DMA_REG);

    /* Устанавливаем размер DMA и размер меток в DMA_REG */

    /* ... */

 

    /* Устанавливаем неизменяемую информацию */

    fix->accel = FB_ACCEL_NONE;        /* Аппаратного ускорения нет */

    fix->visual = FB_VISUAL_TRUECOLOR; /* Режим True color */

    fix->line_length = var->xres_virtual * var->bits_per_pixel/8;

    return 0;

}

 

/* Включение LCD контроллера */

static void

myfb_enable_controller(struct fb_info *info)

{

    /* Включение LCD контроллера, старт DMA, запуск тактовых частот

       и включение питания через запись в CTRL_REG */

    /* ... */

}

 

/* Выключение LCD контроллера */

static void

myfb_disable_controller(struct fb_info *info)

{

    /* Включение LCD контроллера, остановка DMA, выключение тактовых частот

       и питания через запись в CTRL_REG */

    /* ... */

}

 

/* Проверка разумности и изменение переменных */

static int

myfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)

{

    /* Округление вверх до минимального разрешения, поддерживаемого

       LCD контроллером */

    if (var->xres < 64) var->xres = 64;

    if (var->yres < 64) var->yres = 64;

 

    /* ... */

    /* Это оборудование поддерживает формат цвета RGB565.

       Для подробностей смотрите раздел "Режимы цвета" */

    if (var->bits_per_pixel == 16) {

        /* Кодирование красного */

        var->red.length = 5;

        var->red.offset = 11;

        /* Кодирование зелёного */

        var->green.length = 6;

        var->green.offset = 5;

        /* Кодирование синего */

        var->blue.length = 5;

        var->blue.offset = 0;

        /* Оборудование не поддерживает альфа канал */

        var->transp.length = 0;

        var->transp.offset = 0;

    }

    return 0;

}

 

/* Включение/выключение экрана */

static int

myfb_blank(int blank_mode, struct fb_info *info)

{

    switch (blank_mode) {

    case FB_BLANK_POWERDOWN:

    case FB_BLANK_VSYNC_SUSPEND:

    case FB_BLANK_HSYNC_SUSPEND:

    case FB_BLANK_NORMAL:

        myfb_disable_controller(info);

        break;

    case FB_BLANK_UNBLANK:

        myfb_enable_controller(info);

        break;

    }

    return 0;

}

 

/* Конфигурирование карты палитры псевдо цветов */

static int

myfb_setcolreg(u_int color_index, u_int red, u_int green,

            u_int blue, u_int transp, struct fb_info *info)

{

    if (info->fix.visual == FB_VISUAL_TRUECOLOR) {

        /* Делаем требуемые преобразования для конвертации красного, синего, зелёного и

           прозрачности в значения, которые могут быть напрямую восприняты оборудованием */

        /* ... */

 

        ((u32 *)(info->pseudo_palette))[color_index] =

                    (red << info->var.red.offset)     |

                    (green << info->var.green.offset) |

                    (blue << info->var.blue.offset)   |

                    (transp << info->var.transp.offset);

    }

    return 0;

}

 

/* Зависимое от устройства определение ioctl */

#define MYFB_SET_BRIGHTNESS _IOW('M', 3, int8_t)

 

/* Зависимая от устройства ioctl */

static int

myfb_ioctl(struct fb_info *info, unsigned int cmd,

unsigned long arg)

{

    u32 blevel ;

    switch (cmd) {

    case MYFB_SET_BRIGHTNESS :

        copy_from_user((void *)&blevel, (void *)arg,

                    sizeof(blevel)) ;

        /* Пишем blevel в CONTRAST_REG */

        /* ... */

        break;

    default:

        return –EINVAL;

    }

    return 0;

}

 

/* Структура fb_ops */

static struct fb_ops myfb_ops = {

    .owner        = THIS_MODULE,

    .fb_check_var = myfb_check_var,/* Проверка параметров */

    .fb_set_par   = myfb_set_par,  /* Программирование регистров контроллера */

    .fb_setcolreg = myfb_setcolreg,/* Установка карты цветов */

    .fb_blank     = myfb_blank,    /* Включение/выключение экрана */

    .fb_fillrect  = cfb_fillrect,  /* Универсальная функция для заливки прямоугольника */

    .fb_copyarea  = cfb_copyarea,  /* Универсальная функция для копирования области */

    .fb_imageblit = cfb_imageblit, /* Универсальная функция для рисования */

    .fb_ioctl     = myfb_ioctl,    /* Зависимая от устройства ioctl */

};

 

/* Процедура устройства платформы probe() */

static int __init

myfb_probe(struct platform_device *pdev)

{

    struct fb_info *info;

    struct resource *res;

 

    info = framebuffer_alloc(0, &pdev->dev);

    /* ... */

    /* Получаем соответствующие ресурсы, заданные при регистрации

       данного platform_device */

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    /* Получаем разрешение ядра на использование куска памяти ввода/вывода,

       начиная с LCD_CONTROLLER_BASE и размером в

       LCD_CONTROLLER_SIZE байт */

    res = request_mem_region(res->start, res->end - res->start + 1,

                            pdev->name);

 

    /* Заполняем структуру fb_info постоянными (info->fix) и переменными

      (info->var) значениями, такими как размер кадрового буфера, xres, yres,

      bits_per_pixel, fbops, cmap, и так далее */

    initialize_fb_info(info, pdev); /* Отсутствует */

    info->fbops = &myfb_ops;

    fb_alloc_cmap(&info->cmap, 16, 0);

 

    /* Связываем когерентно память кадрового буфера с DMA. info->screen_base

       содержит адрес процессора для отображаемого буфера,

    info->fix.smem_start carries the associated hardware address */

    info->screen_base = dma_alloc_coherent(0, info->fix.smem_len,

                        (dma_addr_t *)&info->fix.smem_start,

                        GFP_DMA | GFP_KERNEL);

    /* Устанавливаем информацию в info->var для соответствующих

       регистров LCD контроллера */

    myfb_set_par(info);

 

    /* Регистрируемся в ядре кадрового буфера */

    register_framebuffer(info);

    return 0;

}

 

/* Процедура драйвера платформы remove() */

static int

myfb_remove(struct platform_device *pdev)

{

    struct fb_info *info = platform_get_drvdata(pdev);

    struct resource *res;

 

    /* Запрет обновления экрана, выключение DMA,... */

    myfb_disable_controller(info);

 

    /* Отмена регистрации драйвера кадрового буфера */

    unregister_framebuffer(info);

    /* Освобождение карты цветов */

    fb_dealloc_cmap(&info->cmap);

    kfree(info->pseudo_palette);

 

    /* Освобождение кадрового буфера */

    framebuffer_release(info);

    /* Освобождение области памяти */

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    release_mem_region(res->start, res->end - res->start + 1);

    platform_set_drvdata(pdev, NULL);

    return 0;

}

 

/* Структура драйвера платформы */

static struct platform_driver myfb_driver = {

    .probe = myfb_probe,

    .remove = myfb_remove,

    .driver = {

        .name = "myfb",

    },

};

 

/* Инициализация модуля */

int __init

myfb_init(void)

{

    platform_device_add(&myfb_device);

    return platform_driver_register(&myfb_driver);

}

 

/* Выгрузка модуля */

void __exit

myfb_exit(void)

{

    platform_driver_unregister(&myfb_driver);

    platform_device_unregister(&myfb_device);

}

 

module_init(myfb_init);

module_exit(myfb_exit);

 

Предыдущая  Содержание  Следующая