[punctual atom linux serial] Chapter 59 Linux LCD driver experiment - extracted from [punctual atom] I.MX6U embedded Linux Driver Development Guide V1.0

1) Experimental platform: punctual atom alpha Linux development board
2) Platform purchase address: https://item.taobao.com/item.htm?id=603672744434
2) Full set of experimental source code + manual + video download address: http://www.openedv.com/thread-300792-1-1.html
3) Students interested in punctual atomic Linux can add group discussion: 935446741
4) pay attention to the official account of the dot atom and get updated information.

Chapter 59 Linux LCD driver experiment

LCD Is a very common peripheral. In the bare metal chapter, we explained how to write it LCD Bare metal drive, in Linux lower LCD Is more widely used in collocation QT In this way GUI You can make very exquisite under the library UI Interface. In this chapter, we will learn how to Linux Lower drive LCD Screen.

Brief analysis of LCD driver under 59.1 Linux
59.1.1 Framebuffer device
Let's first review how LCD drivers are written in bare metal. The writing process of bare metal LCD drivers is as follows:
① Initialize the eLCDIF controller of I.MX6U, focusing on LCD screen width, height, hspw, hbp, hfp, vspw, vbp, vfp and other information.
② Initialize LCD pixel clock.
③ Set RGBLCD video memory.
④ The application program directly operates the LCD by operating the display memory to display characters, pictures and other information on the LCD.
In Linux, the application program finally displays characters, pictures and other information on the LCD by operating the display memory of RGB LCD. In the bare metal machine, we can freely allocate the video memory, but in the Linux system, the memory management is very strict. The video memory needs to be applied for, not what you want to use. Moreover, because of the existence of virtual memory, the video memory set by the driver and the video memory accessed by the application should be the same piece of physical memory.
In order to solve the above problems, framebuffer was born. Framebuffer is translated as frame buffer, abbreviated as FB. Therefore, if you see "framebuffer" or "FB" in future linux learning, your first reaction should think of RGBLCD or display device. FB is a mechanism that gathers all the hardware and software related to display in the system to create a virtual FB device. After we write the LCD driver, a device named / dev/fbX(X=0~n) will be generated. The application can access the LCD by accessing / dev/fbX. The LCD driver is enabled by default in the official Linux kernel of NXP, so we can see a device like / dev/fb0, as shown in figure

Figure /dev/fb0 device file
In figure, / dev/fb0 is the device file corresponding to the LCD, / dev/fb0 is a character device, so there must be a file_operations operation set, FB's file_ The operations operation set is defined in the drivers/video/fbdev/core/fbmem.c file as follows:

Example code fb Set of operations for the device
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,
1507    .get_unmapped_area = get_fb_unmapped_area,
1508 #endif
1510    .fsync 	= fb_deferred_io_fsync,
1511 #endif
1512    .llseek 	= default_llseek,
1513 };
about fb In this chapter, we focus on driving ALPHA On the development board LCD. 

59.1.2 brief analysis of LCD driver
LCD bare metal routine is mainly divided into two parts:
① . obtain the screen parameters of LCD.
② Initialize the eLCDIF interface controller according to the screen parameter information.
For LCD screens with different resolutions, the driver code of the eLCDIF controller is the same. You only need to modify the corresponding screen parameters. Screen parameter information belongs to the content of screen device information, which must be put into the device tree. Therefore, the main work of our experiment in this chapter is to modify the device tree. The official device tree of NXP has added an LCD device node, but the LCD screen information of this node is written for the 4.3-inch 480 * 272 used by the official EVK development board of NXP, We need to change it to the screen parameters we use.
Let's take a brief look at the LCD driver under Linux officially written by NXP, open imx6ull.dtsi, and then find the lcdif node content, as shown below:

Example code imx6ull.dtsi In the file lcdif Node content
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 };
Example code Medium lcdif Node information is used by all users I.MX6ULL The boards of the chip are not complete lcdif Node information. Screen parameters need to be added according to different hardware platforms, such as imx6ull-alientek-emmc.dts Medium lcdif Add other attribute information to the node. From sample code It can be seen that lcdif Nodal compatible The attribute value is“ fsl,imx6ul-lcdif"And“ fsl,imx28-lcdif",So in Linux Search these two strings in the source code to find them I.MX6ULL of LCD Driver file, this file is drivers/video/fbdev/mxsfb.c,mxsfb.c namely I.MX6ULL of LCD Driver file, in which the following contents can be found:
Example code platform Lower LCD drive
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 };
1637 module_platform_driver(mxsfb_driver);
From sample code It can be seen that this is a standard platform Drive, when the drive matches the device mxsfb_probe The function will execute. Look mxsfb_probe Function, let's take a brief look at it Linux lower Framebuffer Driven writing process, Linux The kernel will all Framebuffer Abstract as a fb_info The structure of, fb_info The structure contains Framebuffer The complete set of properties and operations of the device, so each Framebuffer Every device must have one fb_info. In other words, LCD The driver of is to build fb_info,And register with the system fb_info The process of. fb_info Structure defined in include/linux/fb.h In the document, the contents are as follows(Omit conditional compilation): 
Example code fb_info structural morphology
448 struct fb_info {
449     atomic_t count;
450     int node;
451     int flags;
452     struct mutex lock;          		/* mutex  			*/
453     struct mutex mm_lock;       		/* Mutex for fb_mmap and smem_ * domains*/
454     struct fb_var_screeninfo var;	/* Current variable parameter 		*/
455     struct fb_fix_screeninfo fix; 	/* Current fixed parameter 		*/
456     struct fb_monspecs monspecs;  	/* Current display characteristics 	*/
457     struct work_struct queue;       	/* Frame buffer event queue 	*/
458     struct fb_pixmap pixmap;        	/* Image hardware mapping		*/
459     struct fb_pixmap sprite;        	/* Cursor hardware mapping 		*/
460     struct fb_cmap cmap;            	/* Current palette 		*/
461     struct list_head modelist;      	/* Current mode list 		*/
462     struct fb_videomode *mode;      	/* Current video mode 		*/
464 #ifdef CONFIG_FB_BACKLIGHT           	/*  If the LCD supports backlight*/
465     /* assigned backlight device */
466     /* set before framebuffer registration, 
467        remove after unregister */
468     struct backlight_device *bl_dev;	/* Backlight equipment 		*/
470     /* Backlight level curve */
471     struct mutex bl_curve_mutex;    
472     u8 bl_curve[FB_BACKLIGHT_LEVELS];
473 #endif
479     struct fb_ops *fbops;       		/* Frame buffer operation function set	*/  
480     struct device *device;      		/* Parent device 			*/
481     struct device *dev;         		/* Current fb device 		*/
482     int class_flag;             		/* Private sysfs flag 	*/
486     char __iomem *screen_base;  		/* Virtual memory base address (screen memory) 		*/
487     unsigned long screen_size;  		/* Virtual memory size (screen memory size) 	*/ 
488     void *pseudo_palette;       		/* Pseudo 16 bit palette  				*/ 
507 };
fb_info There are many member variables in the structure, which we focus on var,fix,fbops,screen_base,screen_size and pseudo_palette. mxsfb_probe The main work contents of the function are:

① Apply for fb_info.
② . initialize each member variable in fb_info structure.
③ . initialize the eLCDIF controller.
④ . use the register_framebuffer function to register the initialized fb_info with the Linux kernel. The prototype of the register_framebuffer function is as follows:
int register_framebuffer(struct fb_info *fb_info)
Function parameters and return values have the following meanings:
Fb_info: fb_info to be reported.
Return value: 0, success; negative value, failure.
Next, let's take a brief look at the mxsfb_probe function. The function contents are as follows (with reduction):

Example code mxsfb_probe function
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;
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    }
1401    host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), 
1402    if (!host) {
1403        dev_err(&pdev->dev, "Failed to allocate IO resource\n");
1404        return -ENOMEM;
1405    }
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;
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    }
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    }
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    }
1469    INIT_LIST_HEAD(&fb_info->modelist);
1471    pm_runtime_enable(&host->pdev->dev);
1473    ret = mxsfb_init_fbinfo(host);
1474    if (ret != 0)
1475        goto fb_pm_runtime_disable;
1477    mxsfb_dispdrv_init(pdev, fb_info);
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    }
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    }
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 }
Line 1374, host Structure pointer variable, representing I.MX6ULL of LCD Main control interface of, mxsfb_info Structure is NXP Defined for I.MX series SOC of Framebuffer Equipment structure. That is, the equipment structure we have been talking about earlier. This structure includes I.MX series SOC of Framebuffer Device details, such as clock eLCDIF Controller register base address fb_info Wait.
Line 1395, get from the device tree eLCDIF Register header address of the interface controller, in the device tree lcdif The node has been set eLCDIF The first address of the register is 0 X021C8000,therefore res=0X021C8000. 
Line 1401, here you are host Request memory, host by mxsfb_info Type structure pointer.
Line 1407, here you are fb_info Apply for memory, that is, apply for memory fb_info. 
1413rd~1414 Rows, setting host of fb_info The member variable is fb_info,set up fb_info of par The member variable is host. Through this step, the previously applied host and fb_info Connected.
Line 1416, apply for interrupt, and the interrupt service function is mxsfb_irq_handler. 
Line 1425, the first address of the register obtained from the device tree(res)Carry out memory mapping to get the virtual address and save it to host of base Member variables. So by visiting host of base Members can access I.MX6ULL The whole of eLCDIF Register. Actually mxsfb.c Already defined in eLCDIF The offset value of each register from the base address is as follows:
Example code eLCDIF Offset value of each register
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
You can compare< I.MX6ULL In the reference manual eLCDIF Check example code 59 in this section.1.2.4 There are no errors in these registers.
Continue back to example code Medium mxsfb_probe Function, line 1462, here fb_info Medium pseudo_palette Request memory.
Line 1473, call mxsfb_init_fbinfo Function initialization fb_info,The point is fb_info of var,fix,fbops,screen_base and screen_size. among fbops yes Framebuffer Operation set of the device, NXP Provided fbops by mxsfb_ops,The contents are as follows:
Example code mxsfb_ops Operation set
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 };
about mxsfb_ops The operation functions inside are not introduced in detail here. mxsfb_init_fbinfo Function by calling mxsfb_init_fbinfo_dt Function from the device tree LCD Parameter information of. last, mxsfb_init_fbinfo The function will call mxsfb_map_videomem Function application LCD Frame buffer memory(That is, video memory). 
1489th~1490 Rows, setting eLCDIF Corresponding registers of the controller.
The 1494th line, the last call. register_framebuffer Function direction Linux Kernel registration fb_info. 
mxsfb.c The file is large, and there are other important functions, such as mxsfb_remove,mxsfb_shutdown Wait, let's briefly introduce it here mxsfb_probe Function, as for other functions, you can refer to them yourself.

59.2 hardware schematic analysis
Refer to section 24.2 for the schematic diagram of experimental hardware in this chapter.
59.3 LCD driver programming
As mentioned earlier, the eLCDIF interface driver NXP of 6ULL has been written, so we don't need to modify the LCD driver. What we need to do is modify the device tree according to the LCD used. Three key points should be paid attention to:
① IO configuration used by LCD.
② Modify the LCD screen node, modify the corresponding attribute value and replace it with the LCD screen parameters we use.
③ For LCD backlight node information modification, the corresponding device node information shall be modified according to the backlight IO actually used.
Next, let's take a look at how to modify the above two nodes in turn:
1. LCD screen IO configuration
First, check the IO configuration used by the LCD in the device tree. In fact, NXP has been written for us and does not need to be modified, but we still need to take a look. Open the imx6ull-alientek-emmc.dts file and find the following in the iomuxc node:

Example code 59.3.1 Device tree LCD IO to configure
1  pinctrl_lcdif_dat: lcdifdatgrp {
2      fsl,pins = <
3          MX6UL_PAD_LCD_DATA00__LCDIF_DATA00  0x79
4          MX6UL_PAD_LCD_DATA01__LCDIF_DATA01  0x79
5          MX6UL_PAD_LCD_DATA02__LCDIF_DATA02  0x79
6          MX6UL_PAD_LCD_DATA03__LCDIF_DATA03  0x79
7          MX6UL_PAD_LCD_DATA04__LCDIF_DATA04  0x79
8          MX6UL_PAD_LCD_DATA05__LCDIF_DATA05  0x79
9          MX6UL_PAD_LCD_DATA06__LCDIF_DATA06  0x79
10         MX6UL_PAD_LCD_DATA07__LCDIF_DATA07  0x79
11         MX6UL_PAD_LCD_DATA08__LCDIF_DATA08  0x79
12         MX6UL_PAD_LCD_DATA09__LCDIF_DATA09  0x79
13         MX6UL_PAD_LCD_DATA10__LCDIF_DATA10  0x79
14         MX6UL_PAD_LCD_DATA11__LCDIF_DATA11  0x79
15         MX6UL_PAD_LCD_DATA12__LCDIF_DATA12  0x79
16         MX6UL_PAD_LCD_DATA13__LCDIF_DATA13  0x79
17         MX6UL_PAD_LCD_DATA14__LCDIF_DATA14  0x79
18         MX6UL_PAD_LCD_DATA15__LCDIF_DATA15  0x79
19         MX6UL_PAD_LCD_DATA16__LCDIF_DATA16  0x79
20         MX6UL_PAD_LCD_DATA17__LCDIF_DATA17  0x79
21         MX6UL_PAD_LCD_DATA18__LCDIF_DATA18  0x79
22         MX6UL_PAD_LCD_DATA19__LCDIF_DATA19  0x79
23         MX6UL_PAD_LCD_DATA20__LCDIF_DATA20  0x79
24         MX6UL_PAD_LCD_DATA21__LCDIF_DATA21  0x79
25         MX6UL_PAD_LCD_DATA22__LCDIF_DATA22  0x79
26         MX6UL_PAD_LCD_DATA23__LCDIF_DATA23  0x79
27     >;
28 };
30 pinctrl_lcdif_ctrl: lcdifctrlgrp {
31     fsl,pins = <
32         MX6UL_PAD_LCD_CLK__LCDIF_CLK        	0x79
34         MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC    	0x79
35         MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC    	0x79
36     >;
37 pinctrl_pwm1: pwm1grp {
38     fsl,pins = <
39         MX6UL_PAD_GPIO1_IO08__PWM1_OUT   	0x110b0
40     >;
41 };
second~27 Row, child node pinctrl_lcdif_dat,by RGB LCD Configuration item of 24 data lines.
thirtieth~36 Row, child node pinctrl_lcdif_ctrl,RGB LCD Configuration items of 4 control lines, including CLK,ENABLE,VSYNC and HSYNC. 
thirty-seventh~40 Row, child node pinctrl_pwm1,LCD Backlight PWM Pin configuration item. This pin should be set according to the actual situation. Here, we suggest that you study or work in the future, LCD Backlight IO Try to be consistent with the official development board of semiconductor manufacturers.
Note example code 59.3.1 By default LCD All electrical properties are set to 0 X79,Change them to 0 here X49,That is to say LCD relevant IO The driving capability of the is changed to R0/1,That is to reduce LCD relevant IO Driving ability. Because as I said earlier, it's a punctual atom ALPHA On the development board LCD Three interfaces are used SGM3157 Analog switch. In order to prevent the analog switch from affecting the network, it needs to be reduced here LCD The driving ability of the data line. If the board you use does not use an analog switch, you do not need to set 0 X79 Change to 0 X49. 
2,LCD Screen parameter node information modification
 Continue in imx6ull-alientek-emmc.dts Found in file lcdif Node. The node contents are as follows:
Example code 59.3.2 lcdif Node default information
1  &lcdif {
2      pinctrl-names = "default";
3      pinctrl-0 = <&pinctrl_lcdif_dat      	/* IO used		*/
4               &pinctrl_lcdif_ctrl
5               &pinctrl_lcdif_reset>;
6      display = <&display0>;
7      status = "okay";
9      display0: display {                  	/* LCD Attribute information       	*/
10         bits-per-pixel = <16>;           	/* How many bit s does a pixel occupy	*/
11         bus-width = <24>;                	/* Bus width            	*/
13         display-timings {
14             native-mode = <&timing0>;    	/* timing information            	*/
15             timing0: timing0 {       
16             clock-frequency = <9200000>;	/* LCD Pixel clock in Hz	*/
17             hactive = <480>;             	/* LCD X Number of axis pixels     	*/
18             vactive = <272>;             	/* LCD Y Number of axis pixels     	*/
19             hfront-porch = <8>;          	/* LCD hfp parameter        	*/
20             hback-porch = <4>;           	/* LCD hbp parameter        	*/
21             hsync-len = <41>;            	/* LCD hspw parameter       	*/
22             vback-porch = <2>;           	/* LCD vbp parameter      	*/
23             vfront-porch = <4>;          	/* LCD vfp parameter        	*/
24             vsync-len = <10>;            	/* LCD vspw parameter       	*/
26             hsync-active = <0>;          	/* hsync Data line polarity   	*/
27             vsync-active = <0>;          	/* vsync Data line polarity   	*/
28             de-active = <1>;             	/* de Data line polarity        	*/
29             pixelclk-active = <0>;       	/* clk Data line polarity    	*/
30             };
31         };
32     };
33 };
Example code 59.3.2 Is to imx6ull.dtsi In file lcdif Node, let's take a look at the example code 59 in turn.3.2 What do these attributes mean.
Line 3, pinctrl-0 Properties, LCD Used IO Information, used here pinctrl_lcdif_dat,pinctrl_lcdif_ctrl and pinctrl_lcdif_reset These three IO Related nodes, the first two are in example code 59.3.1 It has been explained in. pinctrl_lcdif_reset yes LCD reset IO Information node, punctual atom I.MX6U-ALPHA Development board LCD No reset is used IO,therefore pinctrl_lcdif_reset You can delete it.
Line 6, display Properties, specifying LCD The child node where the attribute information is located. Here is display0,Here is display0 Child node content.
ninth~32 that 's ok, display0 Child nodes, description LCD Parameter information on line 10 bits-per-pixel Property to indicate the amount of space occupied by a pixel bit The default value is 16 bit. In this tutorial, we will LCD Configure as RGB888 Mode, so one pixel occupies 24 bit,bits-per-pixel The attribute should be changed to 24. On line 11 bus-width Property is used to set the data line width because it is to be configured as RGB888 Mode, therefore bus-width Also set to 24.
thirteenth~30 OK, these lines are very important! Because these lines are set LCD Time sequence parameter information of, NXP Official EVK The development board uses a 4.3 480 inch*272 Screen, so the default here is NXP The official screen parameters are set. The annotation behind the meaning of each attribute has been written in detail. Just read it yourself. These timing parameters are the key to be modified and need to be modified according to the screen we use.
Here, we have a positive point atom ATK7016(7 Inch 1024*600)Take the screen as an example imx6ull-alientek-emmc.dts In file lcdif The node is changed to the following:
Example code 59.3.3 in the light of ATK7016 LCD Modified lcdif Node information
1  &lcdif {
2      pinctrl-names = "default";
3      pinctrl-0 = <&pinctrl_lcdif_dat 		/* IO used 			*/
4               &pinctrl_lcdif_ctrl>;
5      display = <&display0>;
6      status = "okay";
8      display0: display {                  	/* LCD Attribute information      	*/
9          bits-per-pixel = <24>;           	/* One pixel occupies 24bit	*/
10         bus-width = <24>;                	/* Bus width             	*/
12         display-timings {
13             native-mode = <&timing0>;    	/* timing information              	*/
14             timing0: timing0 {       
15             clock-frequency = <51200000>;	/* LCD Pixel clock in Hz	*/
16             hactive = <1024>;             	/* LCD X Number of axis pixels  	*/
17             vactive = <600>;           		/* LCD Y Number of axis pixels    	*/
18             hfront-porch = <160>;         	/* LCD hfp parameter     	*/
19             hback-porch = <140>;        	/* LCD hbp parameter       	*/
20             hsync-len = <20>;           	/* LCD hspw parameter       	*/
21             vback-porch = <20>;          	/* LCD vbp parameter       	*/
22             vfront-porch = <12>;         	/* LCD vfp parameter       	*/
23             vsync-len = <3>;         		/* LCD vspw parameter     	*/
25             hsync-active = <0>;          	/* hsync Data line polarity   	*/
26             vsync-active = <0>;          	/* vsync Data line polarity   	*/
27             de-active = <1>;             	/* de Data line polarity        	*/
28             pixelclk-active = <0>;       	/* clk Data line polarity    	*/
29             };
30         };
31     };
32 };
Line 3, setting LCD Screen used IO,Delete the original pinctrl_lcdif_reset,Because screen reset is not used IO,other IO unchanged.
Line 9, use RGB888 Mode, so a pixel is 24 bit. 
fifteenth~23 that 's ok, ATK7016 Screen timing parameters can be modified according to the screen they use.

3. LCD screen backlight node information
The LCD interface backlight control IO of punctual atom is connected to gpio1 of I.MX6U_ On IO08 pin, GPIO1_IO08 multiplexed to PWM1_OUT controls the brightness of LCD screen backlight through PWM signal, which we have explained in detail in Chapter 29. The LCD backlight pin of punctual atom I.MX6U-ALPHA development board is the same as that of NXP official EVK development board, so the device tree node of backlight does not need to be modified. However, considering that other students may use other development boards or screens, the LCD backlight pin may be different from that of NXP official EVK development board, So let's take a look at how to add backlight node information to the device tree.
The first is GPIO1_IO08 for the IO configuration, find the following in imx6ull-alientek-emmc.dts:

Example code 59.3.4 GPIO1_IO08 Pin configuration
1 pinctrl_pwm1: pwm1grp {
2     fsl,pins = <
3         MX6UL_PAD_GPIO1_IO08__PWM1_OUT   0x110b0
4     >;
5 };
pinctrl_pwm1 Node is GPIO1_IO08 As can be seen from line 3, set GPIO1_IO08 this IO Reuse as PWM1_OUT,And set the electrical property value to 0 x110b0. 
LCD Backlight to use PWM1,Therefore, it should also be set PWM1 Node, in imx6ull.dtsi The following is found in the file:
Example code 59.3.5 imx6ull.dtsi In file pwm1 node
1 pwm1: pwm@02080000 {
2     compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
3     reg = <0x02080000 0x4000>;
4     interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
5     clocks = <&clks IMX6UL_CLK_PWM1>,
6          <&clks IMX6UL_CLK_PWM1>;
7     clock-names = "ipg", "per";
8     #pwm-cells = <2>;
9 };
imx6ull.dtsi In file pwm1 Do not modify the node information. If you want to modify it pwm1 For node content, please imx6ull-alientek-emmc.dts Modify in the file. Throughout Linux Search in source file compatible These two values of the property can be found imx6ull of pwm Driver files, imx6ull of PWM The drive file is drivers/pwm/pwm-imx.c,Here we will not analyze this file in detail. Continue in imx6ull-alientek-emmc.dts Found in file pwm1 The additional contents are as follows:
Example code 59.3.6 towards pwm1 Additional content of node
1 &pwm1 {
2     pinctrl-names = "default";
3     pinctrl-0 = <&pinctrl_pwm1>;
4     status = "okay";
5 };
Line 3, setting pwm1 Used IO by pinctrl_pwm1,That is, sample code 59.3.4 Defined GPIO1_IO08 this IO. 
Line 4, add status Set to okay. 
If the backlight uses other pwm Channels, such as pwm2,Then you need to follow the example code 59.3.6 Content, to pwm2 Add corresponding content to the node. pwm And related IO It's ready, but Linux How does the system know PWM1_OUT It's control LCD What about the backlight? Therefore, we also need a node to LCD Backlight and PWM1_OUT Connect. This node is backlight,backlight Node descriptions can be referenced Documentation/devicetree/indings/video/backlight/pwm-backlight.txt This document explains in detail backlight How to create a node is summarized here:
①,Node name to be“ backlight". 
②,Nodal compatible Property value to be“ pwm-backlight",Therefore, you can Linux Search in kernel“ pwm-backlight"To find PWM Backlight control driver. The driver file is drivers/video/backlight/pwm_bl.c,If you are interested, you can take a look at this driver.
③,pwms Property describes the color used by the backlight PWM as well as PWM Frequency, such as that we will use in this chapter pwm1,pwm The frequency is set to 5 KHz(NXP Official recommended settings). 
④,brightness-levels Property describes the brightness level in the range of 0~255,0 express PWM The duty cycle is 0%,That is, the brightness is the lowest, 255 indicates 100%Duty cycle, that is, the highest brightness. As for setting several levels of brightness, you can fill in this attribute by yourself.
⑤,default-brightness-level Property is the default brightness level.
Set according to the above 5 points backlight Node, this NXP It has been set up for us. Everyone is here imx6ull-alientek-emmc.dts The following is found in the file:
Example code 59.3.7 backlight Node content
1 backlight {
2     compatible = "pwm-backlight";
3     pwms = <&pwm1 0 5000000>;
4     brightness-levels = <0 4 8 16 32 64 128 255>;
5     default-brightness-level = <6>;
6     status = "okay";
7 };
Line 3, setting backlight usage pwm1,PWM Frequency 200 Hz. 
Line 4, set the back 8-level backlight(0~7),They are 0, 4, 8, 16, 32, 64, 128 and 255 respectively, and the corresponding duty cycle is 0%,1.57%,3.13%,6.27%,12.55%,25.1%,50.19%,100%,If not enough, you can add some other backlight level values by yourself.
In line 5, set the default backlight level to 6, that is, 50.19%Brightness.
That's all for the backlight device tree node information. The whole LCD We've finished talking about the node contents of the device tree. You can configure your own development board according to these node contents.

59.4 operational testing
59.4.1 LCD screen Basic test
1. Compile new device tree
We have configured the device tree in the previous section, so we need to enter the following command to recompile the device tree:
make dtbs
Wait for the new imx6ull-alientek-emmc.dtb device tree file to be generated by compilation. Later, start the Linux kernel with the new device tree.
2. Enable Linux logo display
When the Linux kernel starts, you can choose to display the little penguin logo. As long as the little penguin logo display is OK, our LCD driver basically works normally. This logo display needs to be configured, but the Linux kernel generally turns on the logo display by default. However, for the purpose of learning, let's take a look at how to enable the Linux logo display. Open the Linux kernel graphics In the customized configuration interface, press the path to find the corresponding configuration item:
-> Device Drivers
-> Graphics support
-> Bootup logo (LOGO [=y])
-> Standard black and white Linux logo
-> Standard 16-color Linux logo
-> Standard 224-color Linux logo
As shown in figure

Figure logo configuration items
The three options in figure correspond to the logo in black-and-white, 16 bit and 24 bit color format respectively. We select all three and compile them into the Linux kernel. After setting, save and exit, recompile the Linux kernel, and start the system with the newly compiled imx6ull-alientek-emmc.dtb and zImage image image. If the LCD driver works normally, it will be on the LCD screen A colorful little penguin logo appears in the upper left corner of the screen, and the background color of the screen is black, as shown in figure

Figure Linux startup logo display
59.4.2 set LCD as terminal console
We always use SecureCRT as the terminal of the Linux development board, and the development board communicates with SecureCRT through the serial port. Now we have driven the LCD, so we can set the LCD as the terminal, that is, the development board uses its own display device as its own terminal, and then connect the keyboard to directly type commands on the development board and set the LCD as the side of the terminal console The method is as follows:
1. Setting bootargs in uboot
Restart the development board, enter the Linux command line, and reset the console content of the bootargs parameter. The command is as follows:
setenv bootargs 'console=tty1 console=ttymxc0,115200 root=/dev/nfs rw nfsroot=
/home/zuozhongkai/linux/nfs/rootfs ip=
Note that the console is set in the red font. Here we set the console twice. For the first time, set the console=tty1, that is, set the LCD screen as the console. For the second time, set the console = ttymxc0115200, that is, set the serial port as the console. It is equivalent to opening two consoles, one is LCD and the other is serial port. Restart the development board and you will find the LCD and serial port The Linux startup log information will be displayed. However, we cannot use the LCD as the terminal for interaction at this time, because our settings have not been completed.
2. Modify the / etc/inittab file
Open the / etc/inittab file in the root file system of the development board and add the following line:
The contents of the / etc/inittab file after adding are shown in figure

Figure modified / etc/inittab file
After modification, save / etc/inittab and exit, and then restart the development board. After restart, the following line will be displayed on the last line of the LCD screen of the development board:
Please press Enter to activate this console.
The above prompt statement says: press the Enter KEY to enable the current terminal. In Chapter 58, we have registered the KEY on the I.MX6U-ALPHA development board as the Enter KEY, so press the KEY on the development board to enable the LCD terminal. Of course, you can also connect a USB keyboard. The Linux kernel has enabled the USB keyboard driver by default, so you can directly Use the USB keyboard.
So far, we have two sets of terminals, one is SecureCRT based on serial port, and the other is the LCD screen of our development board. However, in order to facilitate debugging, we will focus on SecureCRT in the future. We can output "hello linux" to the LCD screen through the following command line
echo hello linux > /dev/tty1
59.4.3 LCD backlight adjustment
As mentioned in section 59.3, the backlight device tree node has 8 levels of backlight adjustment, which can be set to 0 ~ 7. We can adjust the brightness of LCD backlight by setting the backlight level. Enter the following directory:
The files in this directory are shown in figure

Figure files and subdirectories under directory
Brightness in figure represents the current brightness level and max_bgignness represents the maximum brightness level. The contents of these two files are shown in figure

Figure contents of brightness and max_brightness files
It can be seen from figure that the current screen brightness level is 6. According to the previous analysis, this is 50% brightness. The maximum brightness level of the screen is 7. If we want to modify the screen brightness, we only need to write the screen brightness level to brightness. For example, if the screen brightness level is set to 7, you can use the following commands:
echo 7 > brightness
After entering the above command, you will find that the screen brightness increases. If brightness is set to 0, the LCD backlight will be turned off and the screen will go out.
59.4.4 solution of LCD automatic shutdown
By default, the LCD will turn off after 10 minutes. This is not a code problem, but set by the Linux kernel. Just like we use mobile phones or computers, if we don't operate for a period of time, the screen will turn off to save power. There are many ways to solve this problem. Let's look at it in turn:
1. Press the keyboard to wake up
The simplest is to press the Enter KEY to wake up the screen. In Chapter 58, we registered the KEY key on the I.MX6U-ALPHA development board as the Enter KEY, so press the KEY on the development board to wake up the screen. If there is no KEY on the development board, you can connect an external USB keyboard, and then press the Enter KEY on the USB keyboard to wake up the screen.
2. Turn off the screen shutdown function for 10 minutes
Find the drivers/tty/vt/vt.c file in the Linux source code, and find the blankinterval variable in this file, as shown below:

Example code blankinterval variable
179 static int vesa_blank_mode; 
180 static int vesa_off_interval;
181 static int blankinterval = 10*60;
blankinterval Variable control LCD The closing time is 10 by default*60,That's 10 minutes. take blankinterval Change the value of to 0 to turn off the screen shutdown function for 10 minutes. After modification, it needs to be recompiled Linux Kernel, get new zImage,Then use a new one zImage Launch the development board.
3,Write a APP To turn off the screen out function
 stay ubuntu Create a new one named lcd_always_on.c And then enter the following contents in it:
Example code lcd_always_on.c File code snippet
1  #include <fcntl.h>
2  #include <stdio.h>
3  #include <sys/ioctl.h>
6  int main(int argc, char *argv[])
7  {
8      int fd;
9      fd = open("/dev/tty1", O_RDWR);
10     write(fd, "\033[9;0]", 8);
11     close(fd);
12     return 0;
13 }
Compile with the following command lcd_always_on.c This file:

arm-linux-gnueabihf-gcc lcd_always_on.c -o lcd_always_on
Compile and generate lcd_always_on, copy the executable file to the / usr/bin directory of the root file system of the development board, and then give the executable permission. Set LCD_ always_ The on software starts automatically after startup. Open / etc/init.d/rcS and add the following contents at the end of this file:

Example code lcd_always_on Self start code
1 cd /usr/bin
2 ./lcd_always_on
3 cd ..
Save after modification/etc/init.d/rcS File, and then restart the development board. about Linux Lower LCD Let's stop here.

Keywords: Linux Qt

Added by chipmunken on Thu, 23 Sep 2021 09:43:02 +0300