記事のディレクトリ
1つのフレームバッファデバイス
LCDは、液晶ディスプレイの略語で、液晶ディスプレイと呼ばれることがよくあります。LCDは、カラー画像の表示とビデオの再生をサポートでき、非常に重要な出力デバイスです。システムでGUI(グラフィカルインターフェイスインターフェイス)を使用する必要がある場合は、基盤となるLCDコントローラインターフェイスを操作するだけでなく、LCDデバイスドライバをフラムバッファインターフェイスとして作成する必要があります。
フレームバッファーは、Linuxシステムがディスプレイデバイス用に提供するインターフェイスであり、ディスプレイバッファーを抽象化し、イメージハードウェアの根本的な違いを保護し、上位レベルのアプリケーションがグラフィックモードでディスプレイバッファーを直接操作できるようにします。フレームバッファは、フレームバッファとも呼ばれ、ディスプレイデバイスを操作するためにLinuxが提供するユーザーインターフェイスです。ユーザーアプリケーションは、フレームバッファーを介してさまざまなタイプのディスプレイデバイスに透過的にアクセスできます。この点で、フレームバッファーはハードウェアデバイスのディスプレイバッファーを抽象化したものです。Linuxは、ユーザーアプリケーションが直接読み書きできるフレームバッファーを抽象化します。フレームバッファーの内容を変更することで、LCDディスプレイにすぐに表示できます。
フレームバッファは標準の文字デバイスであり、メジャーデバイス番号は29であり、マイナーデバイス番号はバッファの数によって異なります。フレームバッファは/ dev / fbnデバイスファイルに対応します。グラフィックカードの数に応じて、デバイスファイルは/ dev / fb0、/ dev / fb1などになります。バッファデバイスも通常のメモリデバイスであり、直接読み書きできます。ユーザープログラムの場合、/ devの下にある他のデバイスと同じです。ユーザーは、frameBufferを書き込みと読み取りが可能なメモリの一部と見なすことができます。モニターは、メモリデータに従って対応する画像インターフェースを表示します。これはすべて、LCDコントローラーと対応するドライバーによって行われます。NXPの公式LinuxカーネルはすでにデフォルトでLCDドライバーを有効にしているため、図1.1に示すように、/ dev / fb0のようなデバイスを見ることができます。
上図の/ dev / fb0ファイルはLCDデバイスに対応し、/ dev / fb0は文字デバイスであるため、file_operations操作セットが必要です。fbのfile_operations操作セットはdrivers / video / fbdevで定義されています。 /core/fbmem.cファイル以下に示すように:
1495 static const struct file_operations fb_fops = {
1496 .owner = THIS_MODULE,
1497 .read = fb_read,
1498 .write = fb_write,
1499 .unlocked_ioctl = fb_ioctl,
1500 #ifdef CONFIG_COMPAT
1501 .compat_ioctl = fb_compat_ioctl,
1502 #endif
1503 .mmap = fb_mmap,
1504 .open = fb_open,
1505 .release = fb_release,
1506 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1507 .get_unmapped_area = get_fb_unmapped_area,
1508 #endif
1509 #ifdef CONFIG_FB_DEFERRED_IO
1510 .fsync = fb_deferred_io_fsync,
1511 #endif
1512 .llseek = default_llseek,
1513 };
fbの詳細な処理については、ここでは詳しく説明しません。主なタスクは、開発ボード上のLCDデバイスを駆動することです。
2LCDドライバー分析
LCDは外部デバイスであるため、LCDデバイスを使用する場合は、デバイスツリーにLCD関連のデバイスノードを追加する必要があります。NXPの公式デバイスツリーにはすでにLCDデバイス用のデバイスノードがありますが、開発ボード上のLCDデバイスには適していません。開発ボード上のLCDデバイスが正常に機能するように、パラメーターを変更する必要があります。
まず、imx6ull.dtsiファイルを開いて、LCDに関連するデバイスノードを確認します。
1 lcdif: lcdif@021c8000 {
2 compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
3 reg = <0x021c8000 0x4000>;
4 interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
5 clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
6 <&clks IMX6UL_CLK_LCDIF_APB>,
7 <&clks IMX6UL_CLK_DUMMY>;
8 clock-names = "pix", "axi", "disp_axi";
9 status = "disabled";
10 };
lcdifノードはimx6ull.dtsiファイルにあります。imx6ull.dtsiファイルを含むすべての開発ボードはこのノードを使用します。lcdifノードには同じパラメーターの一部のみが含まれます。LCDデバイスが異なれば、パラメーターも異なります。に追加してください。たとえば、topeet_emmc_4_3.dtsデバイスツリーファイルのデバイスツリーファイルでは、lcdifノードが参照されて他の属性情報が追加されます。
lcdifノードの互換性のある属性値は「fsl、imx6ul-lcdif」と「fsl、imx28-lcdif」であり、これら2つの互換性のある属性値に従って、LCDドライバーファイルはカーネルソースコードにあります。見つかったファイルはdrivers / video /fbdev/mxsfb.c、mxsfb.cはI.MX6ULLのLCDドライバーファイルです。このファイルには次の内容が含まれています。
1362 static const struct of_device_id mxsfb_dt_ids[] = {
1363 {
.compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
1364 {
.compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
1365 {
/* sentinel */ }
1366 };
......
1625 static struct platform_driver mxsfb_driver = {
1626 .probe = mxsfb_probe,
1627 .remove = mxsfb_remove,
1628 .shutdown = mxsfb_shutdown,
1629 .id_table = mxsfb_devtype,
1630 .driver = {
1631 .name = DRIVER_NAME,
1632 .of_match_table = mxsfb_dt_ids,
1633 .pm = &mxsfb_pm_ops,
1634 },
1635 };
1636
1637 module_platform_driver(mxsfb_driver);
まず、LCDドライバーファイルがプラットフォームフレームワークでもあることがわかります。デバイスとドライバーが正常に一致したら、mxsfb_probe関数を実行します。mxsfb_probe関数を見る前に、フレームバッファードライバーの書き込みプロセスを簡単に理解しましょう。 Linuxでは、Linuxカーネルはすべてのフレームバッファーをfb_infoと呼ばれる1つのA構造に抽象化します。fb_info構造には、フレームバッファーデバイスの属性と操作の完全なセットが含まれているため、すべてのフレームバッファーデバイスにfb_infoが必要です。言い換えると、LCDドライバーは、fb_infoを作成し、fb_infoをシステムに登録するプロセスです。fb_info構造体はinclude / linux / fb.hファイルで定義されており、コンテンツの一部は次のとおりです。
448 struct fb_info {
449 atomic_t count;
450 int node;
451 int flags;
452 struct mutex lock; /* 互斥锁 */
453 struct mutex mm_lock; /* 互斥锁,用于 fb_mmap 和 smem_*域*/
454 struct fb_var_screeninfo var; /* 当前可变参数 */
455 struct fb_fix_screeninfo fix; /* 当前固定参数 */
456 struct fb_monspecs monspecs; /* 当前显示器特性 */
457 struct work_struct queue; /* 帧缓冲事件队列 */
458 struct fb_pixmap pixmap; /* 图像硬件映射 */
459 struct fb_pixmap sprite; /* 光标硬件映射 */
460 struct fb_cmap cmap; /* 当前调色板 */
461 struct list_head modelist; /* 当前模式列表 */
462 struct fb_videomode *mode; /* 当前视频模式 */
463
464 #ifdef CONFIG_FB_BACKLIGHT /* 如果 LCD 支持背光的话 */
465 /* assigned backlight device */
466 /* set before framebuffer registration,
467 remove after unregister */
468 struct backlight_device *bl_dev; /* 背光设备 */
469
470 /* Backlight level curve */
471 struct mutex bl_curve_mutex;
472 u8 bl_curve[FB_BACKLIGHT_LEVELS];
473 #endif
......
479 struct fb_ops *fbops; /* 帧缓冲操作函数集 */
480 struct device *device; /* 父设备 */
481 struct device *dev; /* 当前 fb 设备 */
482 int class_flag; /* 私有 sysfs 标志 */
......
486 char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */
487 unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */
488 void *pseudo_palette; /* 伪 16 位调色板 */
......
507 };
fb_info構造には多くのメンバー変数もありますが、そのほとんどは私たちとは関係ありません。注意が必要なのは、var、fix、fbops、screen_base、screen_size、pseudo_paletteです。
mxsfb_probe関数が実装する関数を見てみましょう
。①fb_infoを申請します。
②fb_info構造体の各メンバー変数を初期化します。
③eLCDIFコントローラーを初期化します。
④register_framebuffer関数を使用して、初期化されたfb_infoをLinuxカーネルに登録します。
register_framebuffer関数のプロトタイプは次のとおりです。
int register_framebuffer(struct fb_info *fb_info)
パラメーターfb_infoは、登録される構造体です。
mxsfb_probe関数のコードを簡単に見てみましょう。
1369 static int mxsfb_probe(struct platform_device *pdev)
1370 {
1371 const struct of_device_id *of_id =
1372 of_match_device(mxsfb_dt_ids, &pdev->dev);
1373 struct resource *res;
1374 struct mxsfb_info *host;
1375 struct fb_info *fb_info;
1376 struct pinctrl *pinctrl;
1377 int irq = platform_get_irq(pdev, 0);
1378 int gpio, ret;
1379
......
1394
1395 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1396 if (!res) {
1397 dev_err(&pdev->dev, "Cannot get memory IO resource\n");
1398 return -ENODEV;
1399 }
1400
1401 host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);
1402 if (!host) {
1403 dev_err(&pdev->dev, "Failed to allocate IO resource\n");
1404 return -ENOMEM;
1405 }
1406
1407 fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);
1408 if (!fb_info) {
1409 dev_err(&pdev->dev, "Failed to allocate fbdev\n");
1410 devm_kfree(&pdev->dev, host);
1411 return -ENOMEM;
1412 }
1413 host->fb_info = fb_info;
1414 fb_info->par = host;
1415
1416 ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
1417 dev_name(&pdev->dev), host);
1418 if (ret) {
1419 dev_err(&pdev->dev, "request_irq (%d) failed with
1420 error %d\n", irq, ret);
1421 ret = -ENODEV;
1422 goto fb_release;
1423 }
1424
1425 host->base = devm_ioremap_resource(&pdev->dev, res);
1426 if (IS_ERR(host->base)) {
1427 dev_err(&pdev->dev, "ioremap failed\n");
1428 ret = PTR_ERR(host->base);
1429 goto fb_release;
1430 }
......
1461
1462 fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) *
1463 16, GFP_KERNEL);
1464 if (!fb_info->pseudo_palette) {
1465 ret = -ENOMEM;
1466 goto fb_release;
1467 }
1468
1469 INIT_LIST_HEAD(&fb_info->modelist);
1470
1471 pm_runtime_enable(&host->pdev->dev);
1472
1473 ret = mxsfb_init_fbinfo(host);
1474 if (ret != 0)
1475 goto fb_pm_runtime_disable;
1476
1477 mxsfb_dispdrv_init(pdev, fb_info);
1478
1479 if (!host->dispdrv) {
1480 pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
1481 if (IS_ERR(pinctrl)) {
1482 ret = PTR_ERR(pinctrl);
1483 goto fb_pm_runtime_disable;
1484 }
1485 }
1486
1487 if (!host->enabled) {
1488 writel(0, host->base + LCDC_CTRL);
1489 mxsfb_set_par(fb_info);
1490 mxsfb_enable_controller(fb_info);
1491 pm_runtime_get_sync(&host->pdev->dev);
1492 }
1493
1494 ret = register_framebuffer(fb_info);
1495 if (ret != 0) {
1496 dev_err(&pdev->dev, "Failed to register framebuffer\n");
1497 goto fb_destroy;
1498 }
......
1525 return ret;
1526 }
1374行目で、ホスト構造ポインター変数はI.MX6ULL LCDのメインコントロールインターフェイスを表します。mxsfb_info構造は、I.MXシリーズSOC用にNXPによって定義されたフレームバッファーデバイス構造です。これは、これまで説明してきたデバイス構造です。この構造には、クロック、eLCDIFコントローラーのレジスタベースアドレス、fb_infoなど、I.MXシリーズSOCのフレームバッファーデバイスの詳細情報が含まれています。
1395行目で、eLCDIFインターフェイスコントローラーの最初のレジスタアドレスがデバイスツリーから取得されます。デバイスツリーのlcdifノードがeLCDIFレジスタの最初のアドレスを0X021C8000に設定しているため、res = 0X021C8000です。
1401行目、ホストのメモリを要求します。ホストは、mxsfb_info型の構造体へのポインタです。
1407行目、fb_infoのメモリを申請します。つまり、fb_infoを申請します。
1413〜1414行目で、hostのfb_infoメンバー変数をfb_infoに設定し、fb_infoのparメンバー変数をhostに設定します。この手順により、以前に適用されたホストとfb_infoがリンクされます。
1416行目、割り込みを適用します。割り込みサービス関数はmxsfb_irq_handlerです。
1425行目では、デバイスツリーから取得したレジスタの最初のアドレス(res)に対してメモリマッピングを実行して、仮想アドレスを取得し、ホストのベースメンバー変数に保存します。したがって、ホストのベースメンバーにアクセスすることで、i.MX6ULLのeLCDIFレジスタ全体にアクセスできます。実際、ベースアドレスと比較したeLCDIFの各レジスタのオフセット値は、以下に示すようにmxsfb.cで定義されています。
67 #define LCDC_CTRL 0x00
68 #define LCDC_CTRL1 0x10
69 #define LCDC_V4_CTRL2 0x20
70 #define LCDC_V3_TRANSFER_COUNT 0x20
71 #define LCDC_V4_TRANSFER_COUNT 0x30
......
89 #define LCDC_V4_DEBUG0 0x1d0
90 #define LCDC_V3_DEBUG0 0x1f0
1462行目、fb_infoのpseudo_paletteにメモリを適用します。
1473行目で、mxsfb_init_fbinfo関数を呼び出してfb_infoを初期化し、fb_infoのvar、fix、fbops、screen_base、screen_sizeに注目します。その中で、fbopsはフレームバッファデバイスの操作セットであり、NXPによって提供されるfbopsはmxsfb_opsであり、内容は次のとおりです。
987 static struct fb_ops mxsfb_ops = {
988 .owner = THIS_MODULE,
989 .fb_check_var = mxsfb_check_var,
990 .fb_set_par = mxsfb_set_par,
991 .fb_setcolreg = mxsfb_setcolreg,
992 .fb_ioctl = mxsfb_ioctl,
993 .fb_blank = mxsfb_blank,
994 .fb_pan_display = mxsfb_pan_display,
995 .fb_mmap = mxsfb_mmap,
996 .fb_fillrect = cfb_fillrect,
997 .fb_copyarea = cfb_copyarea,
998 .fb_imageblit = cfb_imageblit,
999 };
mxsfb_opsのさまざまな操作関数についての詳細な紹介はありません。mxsfb_init_fbinfo関数は、mxsfb_init_fbinfo_dt関数を呼び出すことにより、デバイスツリーからLCDのさまざまなパラメータ情報を取得します。最後に、mxsfb_init_fbinfo関数は、mxsfb_map_videomem関数を呼び出して、LCDのフレームバッファメモリ(つまり、ビデオメモリ)を適用します。
1489〜1490行目は、eLCDIFコントローラーの対応するレジスターを設定します。
1494行目で、register_framebuffer関数が呼び出され、fb_infoがLinuxカーネルに登録されます。
mxsfb.cファイルは非常に大きく、mxsfb_remove、mxsfb_shutdownなどの他の重要な関数がいくつかあります。ここでは、mxsfb_probe関数を簡単に紹介し、他の関数について自分で学ぶことができます。