LCD FrameBuffer driver based on STM32MP157

LCD driver based on FrameBuffer frame of STM32MP157

  1. What is FrameBuffer?

    Framebuffer is translated into Chinese, that is, frame buffer. Frame is the smallest unit in image animation. A frame is a still picture, and continuous frames form animation. Then frame buffer is used to cache the data of a frame. In the linux kernel, it is a program interface that abstracts the display device into a frame buffer. The frame buffer generally determines the buffer size according to the resolution of the display device and image format (ARGB888, RGB888, RGB565, etc.), and applies for an area from the memory. When we want to display a picture, We can write the data of the picture to the frame buffer.

  2. FrameBuffer Driver Framework

    FrameFBuffer driver is a character driver, so it is based on character devices. What about the general framework of character driver?

    1. Character device operation process

      The process of a user operating a hardware is shown in the figure above. For linux, everything is a file, and every device on the hardware is abstracted as a file. Our device files are stored in the / dev directory under the root directory

      As shown in the figure, there are character drivers GPIO and I2c, block drivers mmcblk1 and mmcblk1boot0, among which fb0 is the lcd framebuffer driver on my development board. You can view the driver information through ls -la /dev/fb0: this c means that it is a character device, and the primary device number is 29 and the secondary device number is 0

    2. Analysis of LCD FrameBuffer Driver Framework

      For an ordinary character device, there are usually five steps:

      • Step 1: get the main equipment number major, which can be automatically assigned or selected by yourself
      • Step 2: create a file_operation structure, initializing member variables open,. release,. read,. Member variables such as write
      • Step 3: register character device driver
        • register_chrdev(major,name,file_operation); According to the main equipment number and file_ Operation register device driver
        • class_creat(); Dynamically create the logical class of the device, complete the initialization of some fields, and then add them to the kernel. The created logical class is located in / sys/class
        • device_create(); Create the corresponding device node in the / dev directory
      • Step 4: enter the function and use module_init() macro is modified, the driver will perform this function when loading, and the function of the third part is called in the entrance function.
      • Step 5: exit function, using module_exit() macro. This function will be executed when the driver is unloaded. In the exit function:
        • unregister_chrdev(); Unregister character device driver
        • device_destroy(); Delete device
        • class_destroy(); Delete class

      However, this series of operation kernels of character operation have been implemented for us. The following operations are implemented in drivers / video / fbdev / core / fbmem C file. The implementation process is as follows:

      • Step 1: main equipment No. FB_MAJOR is in include / UAPI / Linux / major H is defined as 29

      • Step 2: file_operation creation and initialization
      static const struct file_operations fb_fops = {
      	.owner =	THIS_MODULE,
      	.read =		fb_read,
      	.write =	fb_write,
      	.unlocked_ioctl = fb_ioctl,
      #ifdef CONFIG_COMPAT
      	.compat_ioctl = fb_compat_ioctl,
      #endif
      	.mmap =		fb_mmap,
      	.open =		fb_open,
      	.release =	fb_release,
      #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
      	(defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
      	 !defined(CONFIG_MMU))
      	.get_unmapped_area = get_fb_unmapped_area,
      #endif
      #ifdef CONFIG_FB_DEFERRED_IO
      	.fsync =	fb_deferred_io_fsync,
      #endif
      	.llseek =	default_llseek,
      };
      
      • Step 3: enter the function to execute the registered character driven operation, etc
      static int __init  fbmem_init(void)
      {
      	int ret;
      
      	if (!proc_create_seq("fb", 0, NULL, &proc_fb_seq_ops))
      		return -ENOMEM;
      
          /*Register character device, FB_MAJOR is the main equipment number, file_ The operation structure is fb_fops*/
      	ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
      	if (ret) {
      		printk("unable to get major %d for fb devs\n", FB_MAJOR);
      		goto err_chrdev;
      	}
          /* Dynamically create the logical class of the device, located in the / sys/class / directory and named graphics*/
      	fb_class = class_create(THIS_MODULE, "graphics");
      	if (IS_ERR(fb_class)) {
      		ret = PTR_ERR(fb_class);
      		pr_warn("Unable to create fb class; errno = %d\n", ret);
      		fb_class = NULL;
      		goto err_class;
      	}
      
      	fb_console_init();
      	return 0;
      
      err_class:
      	unregister_chrdev(FB_MAJOR, "fb");
      err_chrdev:
      	remove_proc_entry("fb", NULL);
      	return ret;
      }
      module_init(fbmem_init);/* Entry function */
      
      • Step 4: exit function, logout character drive operation, etc
      static void __exit
      fbmem_exit(void)
      {
      	fb_console_exit();
      
      	remove_proc_entry("fb", NULL);
      	class_destroy(fb_class);/* Delete fb_class class */
      	unregister_chrdev(FB_MAJOR, "fb");/* Unregister character driver */
      }
      module_exit(fbmem_exit);
      

      Then let's briefly analyze FB_ In FOPS structure Open function fb_open()

      static int fb_open(struct inode *inode, struct file *file)
      __acquires(&info->lock)
      __releases(&info->lock)
      {
          /* Obtain the secondary equipment number. The equipment number of the equipment is stored in the inode structure. The secondary equipment number can be obtained through the equipment number */
      	int fbidx = iminor(inode);
      	struct fb_info *info;
      	int res = 0;
      
          /* Get the FB corresponding to the device_ When registering the device driver of info structure, FB will be registered according to the secondary device number_ Info structure */
      	info = get_fb_info(fbidx);
      	if (!info) {
      		request_module("fb%d", fbidx);
      		info = get_fb_info(fbidx);
      		if (!info)
      			return -ENODEV;
      	}
      	if (IS_ERR(info))
      		return PTR_ERR(info);
      
      	lock_fb_info(info);
      	if (!try_module_get(info->fbops->owner)) {
      		res = -ENODEV;
      		goto out;
      	}
      	file->private_data = info;
      	if (info->fbops->fb_open) {
      		res = info->fbops->fb_open(info,1);/* Call FB_ FB under fbops in info structure_ Open function */
      		if (res)
      			module_put(info->fbops->owner);
      	}
      #ifdef CONFIG_FB_DEFERRED_IO
      	if (info->fbdefio)
      		fb_deferred_io_open(info, inode, file);
      #endif
      out:
      	unlock_fb_info(info);
      	if (res)
      		put_fb_info(info);
      	return res;
      }
      

      In FB_ iminor(inode) in the open () function obtains the secondary device number according to the device number in the inode node of the device file, get_ fb_ The info() function obtains an FB according to the secondary device number_ Info structure info, and finally call info - > fbops - > fb_open() function.

      Same reason analysis fb_release() function, and finally call info - > fbops - > fb_release() function, so the kernel provides us with a framebuffer framework, and we only need to implement an fb_info structure, which can be found through the device number, and initialize fbops in the structure. After simple analysis, we can basically conclude that the lcd FrameBuffer framework process is as follows:

      Through the above simple analysis, it can be seen that the framebuffer framework driver in the kernel finally calls fb_info structure to control the lcd, so we mainly implement FB when writing the lcd framebuffer driver_ Info structure, so that the framebuffer driver can find the FB through the secondary device number_ Info structure.

    3. fb_info structure

      fb_ The info structure is defined in include / Linux / FB H, as follows

      struct fb_info {
      	atomic_t count;
      	int node;
      	int flags;
      	/*
      	 * -1 by default, set to a FB_ROTATE_* value by the driver, if it knows
      	 * a lcd is not mounted upright and fbcon should rotate to compensate.
      	 */
      	int fbcon_rotate_hint; /* Screen rotation parameters */
      	struct mutex lock;		/* Lock for open/release/ioctl funcs */
      	struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
      	struct fb_var_screeninfo var;	/* Current var Screen variable information*/
      	struct fb_fix_screeninfo fix;	/* Current fix Screen fixed information*/
      	struct fb_monspecs monspecs;	/* Current Monitor specs */
      	struct work_struct queue;	/* Framebuffer event queue */
      	struct fb_pixmap pixmap;	/* Image hardware mapper */
      	struct fb_pixmap sprite;	/* Cursor hardware mapper */
      	struct fb_cmap cmap;		/* Current cmap */
      	struct list_head modelist;      /* mode list */
      	struct fb_videomode *mode;	/* current mode */
      
      #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
      	/* assigned backlight device */
      	/* set before framebuffer registration, 
      	   remove after unregister */
      	struct backlight_device *bl_dev;
      
      	/* Backlight level curve */
      	struct mutex bl_curve_mutex;	
      	u8 bl_curve[FB_BACKLIGHT_LEVELS];
      #endif
      #ifdef CONFIG_FB_DEFERRED_IO
      	struct delayed_work deferred_work;
      	struct fb_deferred_io *fbdefio;
      #endif
      
      	struct fb_ops *fbops;   /* Operation functions (read, write, open, close) */
      	struct device *device;		/* This is the parent */
      	struct device *dev;		/* This is this fb device */
      	int class_flag;                    /* private sysfs flags */
      #ifdef CONFIG_FB_TILEBLITTING
      	struct fb_tile_ops *tileops;    /* Tile Blitting */
      #endif
      	union {
      		char __iomem *screen_base;	/* Virtual address */
      		char *screen_buffer;
      	};
      	unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 */ 
      	void *pseudo_palette;		/* Fake palette of 16 colors */ 
      #define FBINFO_STATE_RUNNING	0
      #define FBINFO_STATE_SUSPENDED	1
      	u32 state;			/* Hardware state i.e suspend */
      	void *fbcon_par;                /* fbcon use-only private area */
      	/* From here on everything is device dependent */
      	void *par;/* Private data of the device can be accessed through the framebuffer_ The first parameter of the alloc () function to allocate the specified size of memory */
      	/* we need the PCI or similar aperture base/size not
      	   smem_start/size as smem_start may just be an object
      	   allocated inside the aperture so may not actually overlap */
      	struct apertures_struct {
      		unsigned int count;
      		struct aperture {
      			resource_size_t base;
      			resource_size_t size;
      		} ranges[0];
      	} *apertures;
      
      	bool skip_vt_switch; /* no VT switch on suspend/resume required */
      };
      

      In FB_ There are several important parameters in the info structure:

      • struct fb_var_screeninfo var*; Screen variable information, include / UAPI / Linux / FB H file, which defines the resolution, image format, pixel clock and other hardware related parameters of the display

        struct fb_var_screeninfo {
        	__u32 xres;			/* visible resolution Visual resolution X axis		*/
        	__u32 yres;			/* visible resolution Visual resolution Y axis		*/
        	__u32 xres_virtual;		/* virtual resolution Virtual resolution X axis		*/
        	__u32 yres_virtual;		/* virtual resolution Virtual resolution Y axis		*/
        	__u32 xoffset;			/* offset from virtual to visible Offset from virtual to visual resolution */
        	__u32 yoffset;			/* resolution			*/
        
        	__u32 bits_per_pixel;		/* Number of data bits required per pixel */
        	__u32 grayscale;		/* 0 = color, 1 = grayscale,	*/
        					/* >1 = FOURCC			*/
        	struct fb_bitfield red;		/* RGB What is the position and length of r in a pixel data*/
        	struct fb_bitfield green;	/* RGB What is the position and length of g in a pixel data*/
        	struct fb_bitfield blue;/* RGB What is the position and length of b in a pixel data*/
        	struct fb_bitfield transp;	/* transparency			*/	
        
        	__u32 nonstd;			/* != 0 Non standard pixel format */
        
        	__u32 activate;			/* see FB_ACTIVATE_*		*/
        
        	__u32 height;			/* height of picture in mm    */
        	__u32 width;			/* width of picture in mm     */
        
        	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */
        
        	/* Timing: All values in pixclocks, except pixclock (of course) */
        	__u32 pixclock;			/* pixel clock in ps (pico seconds)  Pixel clock*/
        	__u32 left_margin;		/* time from sync to picture	*/
        	__u32 right_margin;		/* time from picture to sync	*/
        	__u32 upper_margin;		/* time from sync to picture	*/
        	__u32 lower_margin;
        	__u32 hsync_len;		/* length of horizontal sync	*/
        	__u32 vsync_len;		/* length of vertical sync	*/
        	__u32 sync;			/* see FB_SYNC_*		*/
        	__u32 vmode;			/* see FB_VMODE_*		*/
        	__u32 rotate;			/* angle we rotate counter clockwise */
        	__u32 colorspace;		/* colorspace for FOURCC-based modes */
        	__u32 reserved[4];		/* Reserved for future compatibility */
        };
        
      • struct fb_fix_screeninfo fix; Fixed screen information, include / UAPI / Linux / FB H file, which defines the start address, length, FB type and other parameters of the video memory

        struct fb_fix_screeninfo {
        	char id[16];			/* identification string eg "TT Builtin" */
        	unsigned long smem_start;	/* Start of frame buffer mem buf start address of video memory */
        					/* (physical address) */
        	__u32 smem_len;			/* Length of frame buffer mem buf length of video memory  */
        	__u32 type;			/* see FB_TYPE_*		*/
        	__u32 type_aux;			/* Interleave for interleaved Planes */
        	__u32 visual;			/* see FB_VISUAL_*		*/ 
        	__u16 xpanstep;			/* zero if no hardware panning  */
        	__u16 ypanstep;			/* zero if no hardware panning  */
        	__u16 ywrapstep;		/* zero if no hardware ywrap    */
        	__u32 line_length;		/* length of a line in bytes    */
        	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */
        					/* (physical address) */
        	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
        	__u32 accel;			/* Indicate to driver which	*/
        					/*  specific chip/card we have	*/
        	__u16 capabilities;		/* see FB_CAP_*			*/
        	__u16 reserved[2];		/* Reserved for future compatibility */
        };
        
      • struct fb_ops fbops; In include / Linux / FB H file, FB is defined_ read,fb_write,fb_open,fb_close and other operation functions.

        struct fb_ops {
        	/* open/release and usage marking */
        	struct module *owner;
        	int (*fb_open)(struct fb_info *info, int user);
        	int (*fb_release)(struct fb_info *info, int user);
        
        	/* For framebuffers with strange non linear layouts or that do not
        	 * work with normal memory mapped access
        	 */
        	ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
        			   size_t count, loff_t *ppos);
        	ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
        			    size_t count, loff_t *ppos);
        
        	/* checks var and eventually tweaks it to something supported,
        	 * DO NOT MODIFY PAR */
        	int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
        
        	/* set the video mode according to info->var */
        	int (*fb_set_par)(struct fb_info *info);
        
        	/* set color register */
        	int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
        			    unsigned blue, unsigned transp, struct fb_info *info);
        
        	/* set color registers in batch */
        	int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
        
        	/* blank display */
        	int (*fb_blank)(int blank, struct fb_info *info);
        
        	/* pan display */
        	int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
        
        	/* Draws a rectangle */
        	void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
        	/* Copy data from area to another */
        	void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
        	/* Draws a image to the display */
        	void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
        
        	/* Draws cursor */
        	int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
        
        	/* wait for blit idle, optional */
        	int (*fb_sync)(struct fb_info *info);
        
        	/* perform fb specific ioctl (optional) */
        	int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
        			unsigned long arg);
        
        	/* Handle 32bit compat ioctl (optional) */
        	int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
        			unsigned long arg);
        
        	/* perform fb specific mmap */
        	int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
        
        	/* get capability given var */
        	void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
        			    struct fb_var_screeninfo *var);
        
        	/* teardown any resources to do with this framebuffer */
        	void (*fb_destroy)(struct fb_info *info);
        
        	/* called at KDB enter and leave time to prepare the console */
        	int (*fb_debug_enter)(struct fb_info *info);
        	int (*fb_debug_leave)(struct fb_info *info);
        };
        
    4. Analysis s3c2410fb C driver file

      The path is drivers / video / fbdev / s3c2410fb c. To analyze the driver file, first analyze its entry function:

      int __init s3c2410fb_init(void)
      {
          /* Register platform driver */
      	int ret = platform_driver_register(&s3c2410fb_driver);
      
      	if (ret == 0)
      		ret = platform_driver_register(&s3c2412fb_driver);
      
      	return ret;
      }
      
      static void __exit s3c2410fb_cleanup(void)
      {
          /* Unregister platform driver */
      	platform_driver_unregister(&s3c2410fb_driver);
      	platform_driver_unregister(&s3c2412fb_driver);
      }
      

      Through the entry function, it can be judged that the s3c2410 FB driver is a platform driver. There are two models of s3c2410 and s3c2412 drivers. We analyze one s3c2410fb_driver

      static struct platform_driver s3c2410fb_driver = {
      	.probe		= s3c2410fb_probe,
      	.remove		= s3c2410fb_remove,
      	.suspend	= s3c2410fb_suspend,
      	.resume		= s3c2410fb_resume,
      	.driver		= {
      		.name	= "s3c2410-lcd",
      	},
      };
      
      static int s3c2410fb_probe(struct platform_device *pdev)
      {
      	return s3c24xxfb_probe(pdev, DRV_S3C2410);
      }
      
      static int s3c24xxfb_probe(struct platform_device *pdev,
      			   enum s3c_drv_type drv_type)
      {
      	struct s3c2410fb_info *info;
      	struct s3c2410fb_display *display;
      	struct fb_info *fbinfo; /* Declare a fhinfo structure pointer */
      	struct s3c2410fb_mach_info *mach_info;
      	struct resource *res;
      	int ret;
      	int irq;
      	int i;
      	int size;
      	u32 lcdcon1;
      
      	mach_info = dev_get_platdata(&pdev->dev);
      	if (mach_info == NULL) {
      		dev_err(&pdev->dev,
      			"no platform data for lcd, cannot attach\n");
      		return -EINVAL;
      	}
      
      	if (mach_info->default_display >= mach_info->num_displays) {
      		dev_err(&pdev->dev, "default is %d but only %d displays\n",
      			mach_info->default_display, mach_info->num_displays);
      		return -EINVAL;
      	}
      
      	display = mach_info->displays + mach_info->default_display;
      
      	irq = platform_get_irq(pdev, 0);
      	if (irq < 0) {
      		dev_err(&pdev->dev, "no irq for device\n");
      		return -ENOENT;
      	}
      
          /* Assign fbinfo structure
            * The first parameter sizeof(struct s3c2410fb_info) will allocate more sizeof(struct s3c2410fb_info) and divide 		  Put the allocated memory base address in FB_ par in info structure
            */
      	fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
      	if (!fbinfo)
      		return -ENOMEM;
      
      	platform_set_drvdata(pdev, fbinfo);
      
      	info = fbinfo->par;
      	info->dev = &pdev->dev;
      	info->drv_type = drv_type;
      
      	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
      	if (res == NULL) {
      		dev_err(&pdev->dev, "failed to get memory registers\n");
      		ret = -ENXIO;
      		goto dealloc_fb;
      	}
      
      	size = resource_size(res);
      	info->mem = request_mem_region(res->start, size, pdev->name);
      	if (info->mem == NULL) {
      		dev_err(&pdev->dev, "failed to get memory region\n");
      		ret = -ENOENT;
      		goto dealloc_fb;
      	}
      
      	info->io = ioremap(res->start, size);
      	if (info->io == NULL) {
      		dev_err(&pdev->dev, "ioremap() of registers failed\n");
      		ret = -ENXIO;
      		goto release_mem;
      	}
      
      	if (drv_type == DRV_S3C2412)
      		info->irq_base = info->io + S3C2412_LCDINTBASE;
      	else
      		info->irq_base = info->io + S3C2410_LCDINTBASE;
      
      	dprintk("devinit\n");
      
      	strcpy(fbinfo->fix.id, driver_name);
      
      	/* Stop the video */
      	lcdcon1 = readl(info->io + S3C2410_LCDCON1);
      	writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
         /* fbinfo Structure initialization */
      	fbinfo->fix.type	    = FB_TYPE_PACKED_PIXELS;
      	fbinfo->fix.type_aux	    = 0;
      	fbinfo->fix.xpanstep	    = 0;
      	fbinfo->fix.ypanstep	    = 0;
      	fbinfo->fix.ywrapstep	    = 0;
      	fbinfo->fix.accel	    = FB_ACCEL_NONE;
      
      	fbinfo->var.nonstd	    = 0;
      	fbinfo->var.activate	    = FB_ACTIVATE_NOW;
      	fbinfo->var.accel_flags     = 0;
      	fbinfo->var.vmode	    = FB_VMODE_NONINTERLACED;
      
      	fbinfo->fbops		    = &s3c2410fb_ops;
      	fbinfo->flags		    = FBINFO_FLAG_DEFAULT;
      	fbinfo->pseudo_palette      = &info->pseudo_pal;
      
      	for (i = 0; i < 256; i++)
      		info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
      
      	ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);
      	if (ret) {
      		dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
      		ret = -EBUSY;
      		goto release_regs;
      	}
      
      	info->clk = clk_get(NULL, "lcd");
      	if (IS_ERR(info->clk)) {
      		dev_err(&pdev->dev, "failed to get lcd clock source\n");
      		ret = PTR_ERR(info->clk);
      		goto release_irq;
      	}
      
      	clk_prepare_enable(info->clk);
      	dprintk("got and enabled clock\n");
      
      	usleep_range(1000, 1100);
      
      	info->clk_rate = clk_get_rate(info->clk);
      
      	/* find maximum required memory size for display */
      	for (i = 0; i < mach_info->num_displays; i++) {
      		unsigned long smem_len = mach_info->displays[i].xres;
      
      		smem_len *= mach_info->displays[i].yres;
      		smem_len *= mach_info->displays[i].bpp;
      		smem_len >>= 3;
      		if (fbinfo->fix.smem_len < smem_len)
      			fbinfo->fix.smem_len = smem_len;
      	}
      
      	/* Initialize video memory */
      	ret = s3c2410fb_map_video_memory(fbinfo);
      	if (ret) {
      		dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
      		ret = -ENOMEM;
      		goto release_clock;
      	}
      
      	dprintk("got video memory\n");
      
      	fbinfo->var.xres = display->xres;
      	fbinfo->var.yres = display->yres;
      	fbinfo->var.bits_per_pixel = display->bpp;
      
      	s3c2410fb_init_registers(fbinfo);
      
      	s3c2410fb_check_var(&fbinfo->var, fbinfo);
      
      	ret = s3c2410fb_cpufreq_register(info);
      	if (ret < 0) {
      		dev_err(&pdev->dev, "Failed to register cpufreq\n");
      		goto free_video_memory;
      	}
          
          /* Register fb_info structure */
      	ret = register_framebuffer(fbinfo);
      	if (ret < 0) {
      		dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n",
      			ret);
      		goto free_cpufreq;
      	}
      
      	/* create device files */
      	ret = device_create_file(&pdev->dev, &dev_attr_debug);
      	if (ret)
      		dev_err(&pdev->dev, "failed to add debug attribute\n");
      
      	dev_info(&pdev->dev, "fb%d: %s frame buffer device\n",
      		fbinfo->node, fbinfo->fix.id);
      
      	return 0;
      
       free_cpufreq:
      	s3c2410fb_cpufreq_deregister(info);
      free_video_memory:
      	s3c2410fb_unmap_video_memory(fbinfo);
      release_clock:
      	clk_disable_unprepare(info->clk);
      	clk_put(info->clk);
      release_irq:
      	free_irq(irq, info);
      release_regs:
      	iounmap(info->io);
      release_mem:
      	release_mem_region(res->start, size);
      dealloc_fb:
      	framebuffer_release(fbinfo);
      	return ret;
      }
      
      

      When the device name is S3C2410 LCD, it will match the driver and execute the probe function. In the probe function, we can analyze the following steps:

      • Create fb_info structure: through framebuffer_alloc() implementation
      • fb_info struct member initialization
      • Register fb_info structure: through register_framebuffer() implementation

      Therefore, when we write an lcd driver, we mainly need to implement the above three steps. First, we briefly analyze the framebuffer_alloc()

      struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
      {
      #define BYTES_PER_LONG (BITS_PER_LONG/8)
      #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
      	int fb_info_size = sizeof(struct fb_info);/* Calculate FB_ Size of info structure */
      	struct fb_info *info;
      	char *p;
      
      	if (size)
      		fb_info_size += PADDING;/* If the processor is 64 bits, it is 8 bytes. If it is 32 bits, it is 4 bytes */
      
      	p = kzalloc(fb_info_size + size, GFP_KERNEL);/* Assign fb_info structure memory and memory of the passed in parameter size*/
      
      	if (!p)
      		return NULL;
      
      	info = (struct fb_info *) p;
      
      	if (size)
      		info->par = p + fb_info_size;/* fb_info par in the structure points to FB_ End of info structure */
      
      	info->device = dev; /* fb_info devive in the struct is assigned to the passed in parameter devices */
          /* fbcon_rotate_hint:Screen rotation direction
           * This value can be taken as:
          #define FB_ROTATE_UR       0 : Rotate up
          #define FB_ROTATE_CW      1 :  Clockwise rotation
          #define FB_ROTATE_UD       2 : Rotate down
          #define FB_ROTATE_CCW    3 : Counterclockwise rotation
          At / usr / include / Linux / FB H defined in the document 
           */
      	info->fbcon_rotate_hint = -1;/* fb_info Fbcon in_ rotate_ Hint defaults to - 1*/
      
      #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
      	mutex_init(&info->bl_curve_mutex);
      #endif
      
      	return info;
      #undef PADDING
      #undef BYTES_PER_LONG
      }
      

      By analyzing * * framebuffer_alloc(size_t size, struct device *dev) * * function, we can conclude that it will eventually allocate a block of memory (sizeof(struct fb_info)(4 bytes for it or 8 bytes for it)) + (size), put the memory given to size at the end, and store the first address in FB_ Element par, FB in info structure_ Info to store the second parameter dev

      So framebuffer_ The alloc (size_t size, struct device * DEV) function is mainly FB_ The info structure allocates memory. In addition, you can allocate more memory with the specified size, and put the memory base address into fb_info - > par

      Next, analyze the following registers_ framebuffer(struct fb_info *fb_info); In drivers / video / fbdev / core / fbmem C is defined in the document.

      struct fb_info *registered_fb[FB_MAX] __read_mostly;
      
      int register_framebuffer(struct fb_info *fb_info)
      {
      	int ret;
      	mutex_lock(&registration_lock);
      	ret = do_register_framebuffer(fb_info);
      	mutex_unlock(&registration_lock);
      	return ret;
      }
      
      static int do_register_framebuffer(struct fb_info *fb_info)
      {
      	int i, ret;
      	struct fb_videomode mode;
      
      	if (fb_check_foreignness(fb_info))
      		return -ENOSYS;
      
      	do_remove_conflicting_framebuffers(fb_info->apertures,
      					   fb_info->fix.id,
      					   fb_is_primary_device(fb_info));
      
      	if (num_registered_fb == FB_MAX)
      		return -ENXIO;
      
      	num_registered_fb++;
          /* registered_fb[]Array from 0 to FB_MAX starts searching, and exits if any is empty*/
      	for (i = 0 ; i < FB_MAX; i++)
      		if (!registered_fb[i])
      			break;
      	fb_info->node = i;/* fb_info The node item in the structure is used to store the fb in registered_ Index in fb array */
      	atomic_set(&fb_info->count, 1);
      	mutex_init(&fb_info->lock);
      	mutex_init(&fb_info->mm_lock);
      	/* 
      	  * Create fb(n) equipment, and the main equipment number of the equipment is FB_ The major secondary equipment number is i, so the secondary equipment number is registered_fb
      	  * The index of the array relative to fb 
      	   */
      	fb_info->dev = device_create(fb_class, fb_info->device,
      				     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
      	if (IS_ERR(fb_info->dev)) {
      		/* Not fatal */
      		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
      		fb_info->dev = NULL;
      	} else
      		fb_init_device(fb_info);
      
      	if (fb_info->pixmap.addr == NULL) {
      		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
      		if (fb_info->pixmap.addr) {
      			fb_info->pixmap.size = FBPIXMAPSIZE;
      			fb_info->pixmap.buf_align = 1;
      			fb_info->pixmap.scan_align = 1;
      			fb_info->pixmap.access_align = 32;
      			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
      		}
      	}	
      	fb_info->pixmap.offset = 0;
      
      	if (!fb_info->pixmap.blit_x)
      		fb_info->pixmap.blit_x = ~(u32)0;
      
      	if (!fb_info->pixmap.blit_y)
      		fb_info->pixmap.blit_y = ~(u32)0;
      
      	if (!fb_info->modelist.prev || !fb_info->modelist.next)
      		INIT_LIST_HEAD(&fb_info->modelist);
      
      	if (fb_info->skip_vt_switch)
      		pm_vt_switch_required(fb_info->dev, false);
      	else
      		pm_vt_switch_required(fb_info->dev, true);
      
      	fb_var_to_videomode(&mode, &fb_info->var);
      	fb_add_videomode(&mode, &fb_info->modelist);
      	registered_fb[i] = fb_info;
      
      #ifdef CONFIG_GUMSTIX_AM200EPD
      	{
      		struct fb_event event;
      		event.info = fb_info;
      		fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
      	}
      #endif
      
      	if (!lockless_register_fb)
      		console_lock();
      	else
      		atomic_inc(&ignore_console_lock_warning);
      	lock_fb_info(fb_info);
          /* The registration event of the frame buffer hardware device fb. After this operation, the lcd can display the logo and the terminal ttyn for printing */
      	ret = fbcon_fb_registered(fb_info);
      	unlock_fb_info(fb_info);
      
      	if (!lockless_register_fb)
      		console_unlock();
      	else
      		atomic_dec(&ignore_console_lock_warning);
      	return ret;
      }
      
      

      As can be seen from the above code, registered is defined in_ FB [fb_max], is an fb_info type structure pointer array, that is, the time FB stored in the array_ The address of the info structure. We are registering fb_info structure, we will put the structure pointer we want to register into the array. And we can see fb_info - > node stores the registered fb_info structure in registered_fb [] the index in the array, in addition to using device_create() creates a device whose main device number is MKDEV(FB_MAJOR, i) and the main device number is FB_ Major, as mentioned earlier, is fixed to 29. The device number is i, and i is the registered fb_info structure in registered_fb [] the index in the array, and the created device file name is "fb%d", i; Therefore, a device of fbn will be created after registering FB_ After the info structure, we can view fb0, fb1 and other device files in the / dev / directory, and the data followed by FB is the FB corresponding to the device file_ Info structure in registered_ The index in FB [] array is also the secondary device number and fb_info - > node, static int FB in the FrameBuffer framework driver mentioned earlier_ Open (struct inode *inode, struct file * file) and other functions will first find the secondary device number of the device according to the struct inode *inode. The struct inode *inode structure saves the metadata of the device file, which contains the device number of the device. The secondary device number can be found according to the device number and the primary device number, and then can be registered_ Corresponding FB found in FB [] array_ Info structure.

      Finally, fbcon_fb_registered(fb_info); It realizes the process of how to print logo on lcd, which is also the first process of boot screen display. For specific analysis, please refer to this article:

      Analysis of startup screen display process of Android system

Keywords: Single-Chip Microcomputer stm32 ARM

Added by ibelimb on Fri, 14 Jan 2022 01:31:35 +0200