[北京Xunwei] i.MX6ULLターミネーターLinuxLCDドライバー実験LinuxでのLCDドライバーの紹介

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のようなデバイスを見ることができます。
ここに画像の説明を挿入

図1.1

上図の/ 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関数を簡単に紹介し、他の関数について自分で学ぶことができます。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/BeiJingXunWei/article/details/112258700