Linux学习笔记(24)—— LCD驱动程序
时间:2022-12-07 01:00:01
有关LCD接口的相关文档有很多优秀的博文解释,本文不再赘述。以下只是简单列出LCD驱动程序、测试程序等
LCD驱动程序的核心是:
-
分配fb_info
-
设置fb_info
-
注册fb_info
-
硬件相关设置
硬件相关设置可分为三部分:
- 引脚设置
- 时钟设置
- LCD控制器设置
/** * 文件 : lcd_drv.c * 作者 : glen * 描述 : lcd driver文件 */ #include
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct imx6ull_lcdif { volatile unsigned int CTRL; volatile unsigned int CTRL_SET; volatile unsigned int CTRL_CLR; volatile unsigned int CTRL_TOG; volatile unsigned int CTRL1; volatile unsigned int CTRL1_SET; volatile unsigned int CTRL1_CLR; volatile unsigned int CTRL1_TOG; volatile unsigned int CTRL2; volatile unsigned int CTRL2_SET; volatile unsigned int CTRL2_CLR; volatile unsigned int CTRL2_TOG; volatile unsigned int TRANSFER_COUNT; unsigned char RESERVED_0[12]; volatile unsigned int CUR_BUF; unsigned char RESERVED_1[12]; volatile unsigned int NEXT_BUF; unsigned char RESERVED_2[12]; volatile unsigned int TIMING; unsigned char RESERVED_3[12]; volatile unsigned int VDCTRL0; volatile unsigned int VDCTRL0_SET; volatile unsigned int VDCTRL0_CLR; volatile unsigned int VDCTRL0_TOG; volatile unsigned int VDCTRL1; unsigned char RESERVED_4[12]; volatile unsigned int VDCTRL2; unsigned char RESERVED_5[12]; volatile unsigned int VDCTRL3; unsigned char RESERVED_6[12]; volatile unsigned int VDCTRL4; unsigned char RESERVED_7[12]; volatile unsigned int DVICTRL0; unsigned char RESERVED_8[12]; volatile unsigned int DVICTRL1; unsigned char RESERVED_9[12]; volatile unsigned int DVICTRL2; unsigned char RESERVED_10[12]; volatile unsigned int DVICTRL3; unsigned char RESERVED_11[12]; volatile unsigned int DVICTRL4; unsigned char RESERVED_12[12]; volatile unsigned int CSC_COEFF0; unsigned char RESERVED_13[12]; volatile unsigned int CSC_COEFF1; unsigned char RESERVED_14[12]; volatile unsigned int CSC_COEFF2; unsigned char RESERVED_15[12]; volatile unsigned int CSC_COEFF3; unsigned char RESERVED_16[12]; volatile unsigned int CSC_COEFF4; unsigned char RESERVED_17[12]; volatile unsigned int CSC_OFFSET; unsigned char RESERVED_18[12]; volatile unsigned int CSC_LIMIT; unsigned char RESERVED_19[12]; volatile unsigned int DATA; unsigned char RESERVED_20[12]; volatile unsigned int BM_ERROR_STAT; unsigned char RESERVED_21[12]; volatile unsigned int CRC_STAT; unsigned char RESERVED_22[12]; volatile unsigned int STAT; unsigned char RESERVED_23[76]; volatile unsigned int THRES; unsigned char RESERVED_24[12]; volatile unsigned int AS_CTRL; unsigned char RESERVED_25[12]; volatile unsigned int AS_BUF; unsigned char RESERVED_26[12]; volatile unsigned int AS_NEXT_BUF; unsigned char RESERVED_27[12]; volatile unsigned int AS_CLRKEYLOW; unsigned char RESERVED_28[12]; volatile unsigned int AS_CLRKEYHIGH; unsigned char RESERVED_29[12]; volatile unsigned int SYNC_DELAY; }; struct lcd_regs { volatile unsigned int fb_base_phys; volatile unsigned int fb_xres; volatile unsigned int fb_yres; volatile unsigned int fb_bpp; }; static struct lcd_regs *plcd_regs; static struct fb_info *pfb_info; static unsigned int pseudo_palette[32]; static struct gpio_desc *bl_gpio; static struct clk *clk_pix; static struct clk *clk_axi; static void lcd_controller_enable(struct imx6ull_lcdif *lcdif) { lcdif->CTRL |= (1 << 0); } static int lcd_controller_init(struct imx6ull_lcdif *lcdif, struct display_timing *dt, int lcd_bpp, int fb_bpp, unsigned int fb_phy) { int bus_width; int fb_width; int vsync_pol; int hsync_pol; int de_pol; int clk_pol; if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH) hsync_pol = 1; if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH) vsync_pol = 1; if (dt->flags & DISPLAY_FLAGS_DE_HIGH) de_pol = 1; if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) clk_pol = 1; if (lcd_bpp == 24) bus_width = 0x3; else if (lcd_bpp == 18) bus_width = 0x2; else if (lcd_bpp == 8) bus_width = 0x1; else if (lcd_bpp == 16) bus_width = 0x0; else return -1; if (fb_bpp == 24 || fb_bpp == 32) fb_width = 0x3; else if (fb_bpp == 18) fb_width = 0x2; else if (fb_bpp == 8) fb_width = 0x1; else if (fb_bpp == 16) fb_width = 0x0; else return -1; /** * 初始化LCD控制器的CTRL寄存器 * [19] 1 : DOTCLK和DVI modes需要设置为1 * [17] 1 : 设置为1工作在DOTCLK模式 * [15:14] 00 : 输入数据不交换(小端模式)默认就为0,不需设置 * [13:12] 00 : CSC数据不交换(小端模式)默认就为0,不需设置 * [11:10] 11 : 数据总线为24bit * [9:8] 根据显示屏资源文件bpp来设置:8位0x1, 16位0x0, 24位0x3 * [5] 1 : 设置elcdif工作在主机模式 * [1] 0 : 24位数据均是有效数据,默认就为0,不需设置 */ lcdif->CTRL = (1 << 19) | (1 << 17) | (bus_width << 10) | \ (fb_width << 8) | (1 << 5); /** * 设置ELCDIF的寄存器CTRL1 * 根据bpp设置, bpp为24或32才设置 * [19:16]: 111表示传输24位无压缩数据, A通道不传输 */ if (fb_bpp == 24 || fb_bpp == 32) { lcdif->CTRL1 &= ~(0xf << 16); lcdif->CTRL1 |= (0x7 << 16); } else lcdif->CTRL1 |= (0xf << 16); /** * 设置eLCDIF的寄存器TRANSFER_COUNT寄存器 * [31:16] : 垂直方向像素个数 * [15:0] : 水平方向像素个数 */ lcdif->TRANSFER_COUNT = (dt->vactive.typ << 16) | (dt->hactive.typ << 0); /** * 设置eLCDIF的VDCTRL0寄存器 * [29] 0 : VSYNC输出, 默认为0,无需设置 * [28] 1 : 在DOTCLK模式下, 设置1硬件会产生使能ENABLE输出 * [27] 0 : VSYNC低电平有效, 根据屏幕配置文件将其设置为0 * [26] 0 : HSYNC低电平有效, 根据屏幕配置文件将其设置为0 * [25] 1 : DOTCLK下降沿有效, 根据屏幕配置文件将其设置为1 * [24] 1 : ENABLE信号高电平有效, 根据屏幕配置文件将其设置为1 * [21] 1 : 帧同步周期单位, DOTCLK mode设置为1 * [20] 1 : 帧同步脉冲宽度单位, DOTCLK mode设置为1 * [17:0] : vysnc脉冲宽度 */ lcdif->VDCTRL0 = (1 << 28) | (vsync_pol << 27) | (hsync_pol << 26) \ | (clk_pol << 25) | (de_pol << 24) | (1 << 21) | (1 << 20) \ | (dt->vsync_len.typ << 0); /** * 设置eLCDIF的VDCTRL1寄存器 * 设置垂直方向的总周期: 上黑框tvb + 垂直同步脉冲tvp + 垂直有效高度yres + 下黑框tvf */ lcdif->VDCTRL1 = dt->vback_porch.typ + dt->vsync_len.typ + dt->vactive.typ + dt->vfront_porch.typ; /** * 设置eLCDIF的VDCTRL2寄存器 * [31:18] : 水平同步信号脉冲宽度 * [17:0] : 水平方向总周期 * 设置水平方向的总周期: 左黑框thb + 水平同步脉冲thp + 水平有效高度xres + 右黑框thf */ lcdif->VDCTRL2 = (dt->hsync_len.typ << 18) | (dt->hback_porch.typ + dt->hsync_len.typ + dt->hactive.typ + dt->hfront_porch.typ); /** * 设置eLCDIF的VDCTRL3寄存器 * [27:16] : 水平方向等待时钟数 = thb + thp * [15: 0] : 垂直方向等待时钟数 = tvb + tvp */ lcdif->VDCTRL3 = ((dt->hback_porch.typ + dt->hsync_len.typ) << 16) | (dt->vback_porch.typ + dt->vsync_len.typ); /** * 设置eLCDIF的VDCTRL4寄存器 * [18] : 使用VSHYNC、HSYNC、DOTCLK模式此位置1 * [17:0] : 水平方向的宽度 */ lcdif->VDCTRL4 = (1 << 18) | (dt->hactive.typ); /** * 设置eLCDIF的CUR_BUF和NEXT_BUT寄存器 * CUR_BUF : 当前显存地址 * NEXT_BUF : 下一帧显存地址 */ lcdif->CUR_BUF = fb_phy; lcdif->NEXT_BUF = fb_phy; return 0; } static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) { chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset; } static int lcd_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) { unsigned int val; /** * dprintk("setcol:regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */ switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: /* true-color, use pseudo - palette */ if (regno < 16) { u32 *pal = info->pseudo_palette; val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue, &info->var.blue); pal[regno] = val; } break; default: return 1; } return 0; } static struct fb_ops fbops = { .owner = THIS_MODULE, .fb_setcolreg = lcd_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, }; static int lcd_probe(struct platform_device *pdev) { struct device_node *display_np; dma_addr_t phy_addr; int ret; int width; int bpp; struct display_timings *timings = NULL; struct display_timing *dt = NULL; struct imx6ull_lcdif *lcdif; struct resource *res; /** * 从设备树获取LCD显示设备相关信息 */ display_np = of_parse_phandle(pdev->dev.of_node, "display", 0); /* get common info */ ret = of_property_read_u32(display_np, "bus-width", &width); ret = of_property_read_u32(display_np, "bits-per-pixel", &bpp); /* get timming */ timings = of_get_display_timings(display_np); dt = timings->timings[timings->native_mode]; /* get gpio from device tree */ bl_gpio = gpiod_get(&pdev->dev, "backlight", 0); /* config bl_gpio as output */ gpiod_direction_output(bl_gpio, 1); /* set val: gpiod_set_value(bl_gpio, status); */ /* get clk from device tree */ clk_pix = devm_clk_get(&pdev->dev, "pix"); clk_axi = devm_clk_get(&pdev->dev, "axi"); /* set clk rate */ clk_set_rate(clk_pix, dt->pixelclock.typ); /* enable clk */ clk_prepare_enable(clk_pix); clk_prepare_enable(clk_axi); /* 分配fb_info */ pfb_info = framebuffer_alloc(0, NULL); /* 设置fb_info */ /* a. var */ pfb_info->var.xres_virtual = pfb_info->var.xres = dt->hactive.typ; pfb_info->var.yres_virtual = pfb_info->var.yres = dt->vactive.typ; pfb_info->var.bits_per_pixel = 24; pfb_info->var.red.offset = 16; pfb_info->var.red.length = 8; pfb_info->var.green.offset = 8; pfb_info->var.green.length = 8; pfb_info->var.blue.offset = 0; pfb_info->var.blue.length = 8; /* b. fix */ strcpy(pfb_info->fix.id, "fb_lcd"); pfb_info->fix.smem_len = pfb_info->var.xres * pfb_info->var.yres * \ pfb_info->var.bits_per_pixel / 8; if (pfb_info->var.bits_per_pixel == 24) pfb_info->fix.smem_len = pfb_info->var.xres * pfb_info->var.yres * 4; /* fb virtual address */ pfb_info->screen_base = /*dma_alloc_wc(NULL, pfb_info->fix.smem_len, &phy_addr, GFP_KERNEL); */ dma_alloc_writecombine(pfb_info->device, pfb_info->fix.smem_len, (dma_addr_t *)&pfb_info->fix.smem_start, GFP_DMA | GFP_KERNEL); /* pfb_info->fix.smem_start = phy_addr; */ phy_addr = pfb_info->fix.smem_start; pfb_info->fix.type = FB_TYPE_PACKED_PIXELS; pfb_info->fix.visual = FB_VISUAL_TRUECOLOR; pfb_info->fix.line_length = pfb_info->var.xres * pfb_info->var.bits_per_pixel / 8; if (pfb_info->var.bits_per_pixel == 24) pfb_info->fix.line_length = pfb_info->var.xres * 4; /* c. fbops */ pfb_info->fbops = &fbops; pfb_info->pseudo_palette = pseudo_palette; /* 注册fb_info */ register_framebuffer(pfb_info)