[Controlador de Linux]----Controlador de pantalla LCD

Tabla de contenido

1. Marco del controlador LCD

1.1Descripción general del controlador LCD

1.2Análisis de Fbmem.c

1.3 Analizar el controlador de hardware subyacente

3. Operaciones relacionadas con el hardware

3.1 Pasadores de hardware

3.2 Configurar pines de hardware

2. Mapeo de dirección física: ioremap()

3. Pines de configuración

3.3 Paleta

5. Enlaces de referencia


1. Marco del controlador LCD

1.1Descripción general del controlador LCD

hipótesis

aplicación: open("/dev/fb0", ...) Número de dispositivo principal: 29, número de dispositivo menor: 0

-------------------------------------------------- ------------

núcleo:

fb_abierto

int fbidx = iminor(inodo);

struct fb_info *info = = registrado_fb[0];

aplicación: leer()

-------------------------------------------------- -------------

núcleo:

fb_read

int fbidx = iminor(inodo);

estructura fb_info *info = registrado_fb[fbidx];

si (info->fbops->fb_read)

devolver información->fbops->fb_read(info, buf, count, ppos);

src = (u32 __iomem *) (información->screen_base + p);

dst = búfer;

*dst++ = fb_readl(src++);

copiar_a_usuario(buf, búfer, c)

 

1.2Análisis de Fbmem.c

"Función de entrada"—— fbmem_init:

fbmem_init(void)
{
    create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);

    if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  //创建设备
        printk("unable to get major %d for fb devs\n", FB_MAJOR);

    fb_class = class_create(THIS_MODULE, "graphics");//创建类
    if (IS_ERR(fb_class)) {
        printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
        fb_class = NULL;
    }
    return 0;
}

"fbmem.c" es un archivo general, por lo que no puede usar directamente .read y otras funciones en la estructura file_operatives. La clase de creación no crea dispositivos bajo la clase de dispositivo. Los dispositivos se crean en esta clase solo cuando hay hardware real . . El dispositivo creado se puede ver en "register_framebuffer()".

fb_info->dev = dispositivo_create(fb_class, fb_info->dispositivo,

        MKDEV(FB_MAJOR, i), "fb%d", i);

fb_open:

APP:open(): Si quieres saber qué hay en la memoria, mira la estructura file_operatives fb_fops

“.read = fb_open”

static int
fb_open(struct inode *inode, struct file *file)
{
    int fbidx = iminor(inode); //次设备号
    struct fb_info *info;
    int res = 0;
    .........
    if (!(info = registered_fb[fbidx]))  //从registered_fb数组中赋值给info
        return -ENODEV;
    ......
    if (info->fbops->fb_open) {
        res = info->fbops->fb_open(info,1);
        if (res)
            module_put(info->fbops->owner);
    }
    return res;
}

fb_read:

APLICACIÓN:read(): Si quieres saber qué hay en la memoria, mira la estructura file_operatives fb_fops

“.read = fb_read”

static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    int fbidx = iminor(inode);
    struct fb_info *info = registered_fb[fbidx];
    ......
    if (info->fbops->fb_read)
        return info->fbops->fb_read(info, buf, count, ppos);
    /*没有read函数*/
    total_size = info->screen_size; //屏幕大小
    .....
    
    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
             GFP_KERNEL); //分配缓冲区
    if (!buffer)
        return -ENOMEM;

    src = (u32 __iomem *) (info->screen_base + p);  /*screen_base 是指
    显存的基地址。这里是读源 src 等于显存的*/

    while (count) {
        .....
        dst = buffer;
        for (i = c >> 2; i--; )
            *dst++ = fb_readl(src++);/*读源(从显存基地址+P 偏移)那里读到一 
个数据放到目标“*dst++”里。dst 是 buffer,buffer 是 kmalloc()上面分 
配的空间。*/
        ..........
        if (copy_to_user(buf, buffer, c)) { //把数据拷贝到用户空间
            err = -EFAULT;
            break;
        }
        ......
    }

    kfree(buffer);

    return (err) ? err : cnt;
}

De lo anterior, sabemos que ".open" y ".read" dependen de una estructura, la estructura fb_info. Esta estructura es una estructura fd_info obtenida de la matriz "registered_fd[]" con el "número de dispositivo menor" como subíndice.

struct fb_info *info = registered_fb[fbidx];

P: ¿Dónde está configuradologging_fb?

registro_framebuffer

Analizar Register_framebuffer:

int
register_framebuffer(struct fb_info *fb_info)
{
    int i;
    struct fb_event event;
    struct fb_videomode mode;
    ............
    for (i = 0 ; i < FB_MAX; i++) //FB_MAX:32
        if (!registered_fb[i]) //找出一个空项
            break;
    fb_info->node = i;

    fb_info->dev = device_create(fb_class, fb_info->device,
                     MKDEV(FB_MAJOR, i), "fb%d", i);/*在fb_class类下
   创建设备*/
      ..........
}

1.3 Analizar el controlador de hardware subyacente

ruta:/drivers/video/s3c2440.c como ejemplo (de la función de entrada)

int __devinit s3c2410fb_init(void)
{
    //注册一个平台驱动(总线设备驱动模型)
    return platform_driver_register(&s3c2410fb_driver);
}

s3c2410fb_probe:

static int __init s3c2410fb_probe(struct platform_device *pdev)
{
    struct s3c2410fb_info *info;
    struct fb_info     *fbinfo;
    struct s3c2410fb_hw *mregs;
    int ret;
    int irq;
    int i;
    u32 lcdcon1;

    mach_info = pdev->dev.platform_data;//根据pdev获得"mach_info"信息

    mregs = &mach_info->regs;

    irq = platform_get_irq(pdev, 0);//根据pdev获得"irq "中断信息

    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), 
                                &pdev->dev);
    if (!fbinfo) {
        return -ENOMEM;
    }
     /*硬件相关设置*/
    info = fbinfo->par;
    info->fb = fbinfo;
    info->dev = &pdev->dev;

    ......

    /* Stop the video and unset ENVID if set */
    info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
    lcdcon1 = readl(S3C2410_LCDCON1);
    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
    /*硬件相关的操作:*/
    s3c2410_gpio_setpin(S3C2410_GPB0, 0); // back light control 

    info->mach_info         = pdev->dev.platform_data;

    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.height      = mach_info->height;
    fbinfo->var.width       = mach_info->width;
    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;

    fbinfo->var.xres        = mach_info->xres.defval;
    fbinfo->var.xres_virtual    = mach_info->xres.defval;
    fbinfo->var.yres        = mach_info->yres.defval;
    fbinfo->var.yres_virtual    = mach_info->yres.defval;
    fbinfo->var.bits_per_pixel  = mach_info->bpp.defval;

    fbinfo->var.upper_margin    = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1;
    fbinfo->var.lower_margin    = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1;
    fbinfo->var.vsync_len       = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;

    fbinfo->var.left_margin     = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
    fbinfo->var.right_margin    = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
    fbinfo->var.hsync_len       = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;

    fbinfo->var.red.offset      = 11;
    fbinfo->var.green.offset    = 5;
    fbinfo->var.blue.offset     = 0;
    fbinfo->var.transp.offset   = 0;
    fbinfo->var.red.length      = 5;
    fbinfo->var.green.length    = 6;
    fbinfo->var.blue.length     = 5;
    fbinfo->var.transp.length   = 0;
    fbinfo->fix.smem_len        =   mach_info->xres.max *
                    mach_info->yres.max *
                    mach_info->bpp.max / 8;
    ........

    /* Initialize video memory */
    ret = s3c2410fb_map_video_memory(info);
    if (ret) {
        printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);
        ret = -ENOMEM;
        goto release_clock;
    }
    .........
    return 0;
    .........
release_clock:
    clk_disable(info->clk);
    clk_put(info->clk);
}

Para obtener la información de resolución de la pantalla LCD: mire de arriba a abajo (fbmem.c)

fbmem.c mira ".ioctl"

fbmem_init->fb_ops->ioctl(fb_ioctl)

static int 
fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
     unsigned long arg)
{
        int fbidx = iminor(inode);
        struct fb_info *info = registered_fb[fbidx];
        ......
        case FBIOGET_VSCREENINFO:/*GET 获得.V(var)可变的.SCREEN 屏幕.INFO 信 
息*/
        return copy_to_user(argp, &info->var,
                    sizeof(var)) ? -EFAULT : 0;
        ......
}

Hay información de pantalla en info->var;

 

 2. Configuración de parámetros de hardware LCD

Antes de comenzar oficialmente a escribir el controlador, primero comprenda las operaciones específicas del hardware, de la siguiente manera;

 

Al configurar el controlador LCD, el "VCLK" de diferentes LCD es diferente. Es necesario ajustar el controlador LCD para configurar la frecuencia rápida o lenta. El proceso de operación del hardware es:

1. Configure el controlador LCD según el manual del LCD. (parámetros del reloj, etc.)

2. Asigne memoria de video e indique al controlador LCD la dirección de la memoria de video. Explique también el formato de color. Cuántos bytes se utilizan para representar un píxel, etc.

3. Operaciones relacionadas con 2440: configurar pines para LCD. Los pines del chip se pueden configurar como entrada y salida, y también se pueden configurar como pines de LCD.

static struct fb_info *s3c_led;

static void lcd_setting(void)
{
    /* 2.1 设置固定的参数 */
    strcpy(s3c_led->fix.id,"mylcd");       //名字
    s3c_led->fix.smem_len = 240*320*16/8;  //显存的长度
    s3c_led->fix.typ      = FB_TYPE_PACKED_PIXELS;
    s3c_led->fix.visual   = FB_VISUAL_TRUECOLOR;  /*TFT*/
    s3c_led->fix.line_length = 240*16/8; //固定信息里面,一行的长度

    /* 2.2 设置可变的参数 */
    s3c_led->var.xres           = 240; //x方向的分辨率240
    s3c_led->var.yres           = 320; //y方向的分辨率240
    s3c_led->var.xres_virtual   = 240; //x方向的虚拟分辨率240
    s3c_led->var.yres_virtual   = 320; //y方向的虚拟分辨率240
    s3c_led->bits_per_pixel     = 16;  //BPP

    /* RGB:565 */
    s3c_lcd->var.red.offset     = 11; //RGB---565:红色的偏移值从bit11开始
    s3c_lcd->var.red.length     = 5;  //RGB---565,红色占5位
    
    s3c_lcd->var.green.offset   = 5; //RGB---565:红色的偏移值从bit5开始
    s3c_lcd->var.green.length   = 6; //RGB---565,红色占6位

    s3c_lcd->var.blue.offset    = 0; //RGB---565:红色的偏移值从bit0开始
    s3c_lcd->var.blue.length    = 5; //RGB---565,红色占5位
    
    s3c_lcd->var.activate = FB_ACTIVATE_NOW;  //默认值
    /* 2.3 设置操作函数 */
    s3c_lcd->fbops        = &s3c_lcdfb_ops; 
    /* 2.4 其他的设置 */
    //s3c_lcd->pseudo_palette =; //
    //s3c_lcd->screen_base  = ;  /* 显存的虚拟地址 */ 
    s3c_lcd->screen_size   = 240*324*16/8;  //显存的大小
}

3. Operaciones relacionadas con el hardware

3.1 Pasadores de hardware

La interfaz utilizada por la pantalla es la siguiente:

 

 El diagrama esquemático anterior muestra que los registros utilizados por la pantalla LCD son: GPCx GPGx GPDx, y se utilizan los pines de configuración.

Circuito de retroiluminación:

Pin "TECLADO": TECLADO --- J6 TOUT0/GPB0 (GPB0 debe configurarse como pin de salida: GPBCON |=

GPB0_out;)

3.2 Configurar pines de hardware

1. Definir pines de mapeo

static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;

2. Mapeo de dirección física: ioremap()

void * __ioremap(unsigned long phys_addr, unsigned long size)

ioremap() asigna un espacio de direcciones IO al espacio de direcciones virtuales del kernel para facilitar el acceso;

    gpbcon = ioremap(0x56000010, 8);
    gpbdat = gpbcon+1;
    gpccon = ioremap(0x56000020, 4);
    gpdcon = ioremap(0x56000030, 4);
    gpgcon = ioremap(0x56000060, 4);

3. Pines de configuración

    *gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
    *gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */
    
    *gpbcon &= ~(3);  /* GPB0设置为输出引脚 */
    *gpbcon |= 1;
    *gpbdat &= ~1;     /* 输出低电平,不开启背光 */

    *gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */

A. Configurar el controlador LCD

Controlador LCD empaquetado:

struct lcd_regs {
    unsigned long   lcdcon1;
    unsigned long   lcdcon2;
    unsigned long   lcdcon3;
    unsigned long   lcdcon4;
    unsigned long   lcdcon5;
    unsigned long   lcdsaddr1;
    unsigned long   lcdsaddr2;
    unsigned long   lcdsaddr3;
    unsigned long   redlut;
    unsigned long   greenlut;
    unsigned long   bluelut;     // 0X4D000028 STN:蓝色查找表
    unsigned long   reserved[9]; //保留字节
    unsigned long   dithmode;    // 0X4D00004C STN:抖动模式
    unsigned long   tpal;
    unsigned long   lcdintpnd;
    unsigned long   lcdsrcpnd;
    unsigned long   lcdintmsk;
    unsigned long   lpcsel;
};
static volatile struct lcd_regs *lcd_regs; //结构体指针
lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));

B. Asignar memoria de vídeo

Asigne memoria de video (framebuffer) de la memoria e indique la dirección al controlador LCD. Después de configurar el controlador LCD, automáticamente tomará un valor de píxel de la memoria de video y enviará los datos de píxeles a la pantalla LCD a través de VD0~VD23. Luego saca el siguiente y comienza de nuevo. Cuando obtienes el último en la memoria de video (la memoria de video es así de grande), vas al principio de la memoria de video. Las direcciones de la memoria de video deben ser consecutivas. No puede usar kmolloc() para asignar memoria de video ->dma_alloc_writecombine()

static inline 
void dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *dma_addr, gfp_t gfp)
{
return dma_alloc_attrs(dev, size, dma_addr, gfp,
                        DMA_ATTR_WRITE_COMBINE);
}
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, 
                    &s3c_lcd->fix.smem_start, GFP_KERNEL);  //分配显存

lcd_regs->lcdsaddr1  = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);//lcdsaddr1显存地址
lcd_regs->lcdsaddr2  = ((s3c_lcd->fix.smem_start + 
                        s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;lcdsaddr2帧缓冲器地址
lcd_regs->lcdsaddr3 = (240*16/16); 

/* 启动LCD */
lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器  */
lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 */
*gpbdat |= 1;     /* 输出高电平, 使能背光 */

3.3 Paleta

La paleta es una pieza de memoria y los datos de 8 bits obtenidos por el controlador LCD de la memoria de video no se envían directamente a la pantalla LCD en este momento. En cambio, se envía a la paleta, que es un bloque de memoria que almacena datos reales de 16 bits.

Después de que el controlador LCD obtiene los datos de 8 bits de la memoria de la pantalla, los usa como índice. Si un valor de 8 bits es 100, entonces encuentra el elemento número 100 de la "paleta" (como una matriz) y toma el 16 bits en el artículo número 100. Salir. Enviado a la pantalla LCD.

La paleta es como una caja de pintura con colores dentro. El color que desea elegir se recupera a través del índice para averiguar qué color está en el cuadro de color de la paleta y lo saca después de encontrarlo. La paleta aquí es un bloque de memoria, con varios colores configurados de antemano. Cuando el controlador LCD necesita usarla, lee datos de 8 bits de la memoria de la pantalla y usa estos datos de 8 bits como un valor de "índice". , de la matriz "paleta" Tome un color real y luego envíe este color real al hardware LCD.

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
                 unsigned int green, unsigned int blue,
                 unsigned int transp, struct fb_info *info)
{
    unsigned int val;
    
    if (regno > 16)
        return 1;

    /* 用red,green,blue三原色构造出val */
    val  = chan_to_field(red,   &info->var.red);
    val |= chan_to_field(green, &info->var.green);
    val |= chan_to_field(blue,  &info->var.blue);
    
    //((u32 *)(info->pseudo_palette))[regno] = val;
    pseudo_palette[regno] = val;
    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;
}

5. Enlaces de referencia

https://gitee.com/change-ly/linux_project

Supongo que te gusta

Origin blog.csdn.net/weixin_45281868/article/details/126736261
Recomendado
Clasificación