找回密码
 注册
关于网站域名变更的通知
查看: 278|回复: 1
打印 上一主题 下一主题

linux lcd设备驱动剖析二

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2020-4-23 09:42 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    5 T: A8 O( E7 O
    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    3 W8 b. ^) x: j: O
    8 M, Z) P" V: D8 g4 r$ e" ~上一节文章:linux lcd设备驱动剖析一
    - \) R1 m1 u1 G4 X. c5 i2 z
    5 x- G1 w) w/ e+ P上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
    / l, m& e* u  A7 s6 ^0 }. _( k& j2 P  k/ I! a2 M

    " G* k1 l# a% K+ Z  H; \3 e1 t& Tstatic int __init s3c24xxfb_probe(struct platform_device *pdev,
    % x9 g, m1 w! f0 i) R( G  s1 s. D                                  enum s3c_drv_type drv_type)
    & V* C! B2 m2 @# f! u' w{
    3 d& X8 m* V& ?# V7 V        struct s3c2410fb_info *info;
    5 A% L. f0 w" j9 B9 V) Y        struct s3c2410fb_display *display;3 l' M* W  d/ {/ T. I
            struct fb_info *fbinfo;* ~. I# l9 `8 B/ ?
            struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */1 Y; d' _0 F+ ~' m- q7 G; F! s
            struct resource *res;
    / ~) o% @, E- a$ N) ~) u        int ret;1 @7 W( k1 C  `; V& x' t
            int irq;# }, A2 }3 {, @! l$ L
            int i;& V7 C5 _. c7 m7 r' a
            int size;
    $ \' Y. \' K" `1 ~! W* C1 |( K        u32 lcdcon1;; l+ |6 p0 k' V/ L/ ^1 B; m5 L
    0 b( S3 q/ q6 B& {0 W% \4 o
            /*  s3c24xx_fb_set_platdata()里会设置platform_data
    # ?6 I- \9 F0 C5 M/ V         *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    % _, Q2 O6 e* j: F4 [8 x- U4 U% P0 c& I+ \         *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    ! y. e' ^% g, r$ u3 x' p         */5 Q. X, i4 h1 X) e$ D
            mach_info = pdev->dev.platform_data;0 A/ j! S) R3 T; m9 F( ^5 J& }
    7 ?  [' [2 d. R7 l/ U4 U$ g
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */' e% y  n% j; e  j: _. }
            if (mach_info == NULL) {+ L0 ^3 O* b+ l5 `, x
                    dev_err(&pdev->dev,7 `" S+ X7 o! d6 S1 Z
                            "no platform data for lcd, cannot attach\n");
    7 Y. z9 O. \1 t8 |                return -EINVAL;                /* 表示无效的参数 */
    % w6 y/ c5 R4 N& \3 J# A% X$ X( W        }
    8 b) R! y- w+ |; P( [7 S. B " E, G, Y# `3 U$ G8 O# F. |2 M
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    ( A- Y0 O- |2 Q0 I. [( N: q8 k0 H        if (mach_info->default_display >= mach_info->num_displays) {
    ' s7 Z' o, C1 g                dev_err(&pdev->dev, "default is %d but only %d displays\n",7 E) k1 C! J) d* X! N# v
                            mach_info->default_display, mach_info->num_displays);
    - _8 e) e' q* I4 F( T1 t                return -EINVAL;
    5 {  ~4 n& e! Y* k        }  I5 K* C  ^! G) x+ h1 @0 D! m

    6 n8 Z# l1 f8 u4 e: Z$ C: ]6 b9 y        /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    # _/ i4 x  @# ?! k$ c) I        display = mach_info->displays + mach_info->default_display;& ~0 F) _+ ?: L/ A* Y4 E3 x4 ^( h

    8 T; h, `: n5 N7 |5 x        /* 通过平台设备platform_device获得IRQ
    5 N) y4 `0 }) l: e" C* y3 o         * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)5 E3 H% ~" r% j3 R5 Z
             */; f- Y0 R8 _0 z9 O% _' T8 c: S
            irq = platform_get_irq(pdev, 0);: w1 B; G6 r( o7 _
            if (irq < 0) {1 E( q. b" D1 J  R5 }0 U
                    dev_err(&pdev->dev, "no irq for device\n");0 p- C4 V: w* y% R! J( _: A# @, Z
                    return -ENOENT;5 p% O' i, ^% y8 w) K7 x1 n
            }3 u6 Y" e# M1 u' @

    ) B, j" M4 Z7 C& z        /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间2 Z9 C# a2 w8 _) {) R& E7 G
             *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据. W) f$ o4 o& f9 u4 y% T
             *  比如:clk,resource,io,irq_base,drv_type等额外信息
    # f0 S6 F2 @& |, {4 e" M$ w         */7 n: i+ @9 s; T, a& @: b+ v
            fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    , n* e8 b1 X* I        if (!fbinfo): a' F4 b4 ?6 Q2 y; {+ n0 m* h
                    return -ENOMEM;                /* 返回NULL表示失败 */
    8 E/ D0 x8 e, @
    1 {# r! |: x8 i! f8 @        /* 相当于pdev->dev->driver_data = fbinfo */
    , r' n0 ^0 s5 g. z        platform_set_drvdata(pdev, fbinfo);
    ; `2 L1 c; n7 x% y  f' m
    ( q- Q# g/ ]4 ]5 y. v6 \2 Z) i4 t        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */& v$ T; G# C. G% O, o
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */6 }9 @0 B- m$ R# k0 O; t
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    * k/ |' m3 c% Z% s% y. ]% H0 I# a        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    ! X" g9 w: A+ M' o$ G1 N2 V9 I * q# K" P1 |7 J% P  t9 b
            /*  通过平台设备platform_device获得资源(IO) */
    / J* ], h4 M# Y& U- X+ _        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    : h* a: P+ {0 t4 Y: H* j( d        if (res == NULL) {
    9 h" F  ?3 `5 ^' q                dev_err(&pdev->dev, "failed to get memory registers\n");+ r: |8 t2 g. Q' ]* `* t
                    ret = -ENXIO;
    9 H2 Y6 i  @8 a7 Z, _4 I) M                goto dealloc_fb;9 C2 K# |/ ~5 V6 T, j6 S
            }
    ; o5 s0 p2 a% @ + R% _. E6 J( G0 |: e) K; T( `
            size = (res->end - res->start) + 1;                /* 资源的大小 */
    3 n. t4 A5 {" o0 M8 j6 e1 q  Z$ c) C% U
    - y' e0 x' D$ N9 U0 _        /* 申请以res->start地址开始大小为size的I/O内存 */2 a; H8 \3 Y$ C$ y3 X: m) p* r) K
            info->mem = request_mem_region(res->start, size, pdev->name);9 A+ J6 f1 C# \6 r' t5 j
            if (info->mem == NULL) {, S! a' m$ a6 x. a
                    dev_err(&pdev->dev, "failed to get memory region\n");: r" I# r2 ^( W: r" d
                    ret = -ENOENT;
    0 a9 w- M  r; k# x; c, k% b# f                goto dealloc_fb;0 z/ W7 _$ E) }* O
            }& Y6 J  c2 d: y$ C" n* o7 s

    ( a8 Z. T  G* f* N3 e. o7 ]        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */( Y& A% x' @* v/ f; ]9 `7 j
            info->io = ioremap(res->start, size);" ]8 n5 D( ~2 X4 F; [9 T
            if (info->io == NULL) {, \2 ?, D$ b9 y! @: b3 K$ h0 h
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");2 R' c+ U, I1 t; W- B' J! j
                    ret = -ENXIO;8 I1 v1 V; ~: Y+ G- F" j
                    goto release_mem;3 d( T3 E1 W0 g
            }
    ' {4 j/ c( @" E 0 s( ?( ^. l& I( `
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */+ G  j/ i& r- A  p, f
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    4 |0 U, J" m0 D+ r! U( w  e 1 a! c7 P% v( [( E) `
            dprintk("devinit\n");- U# r% W% x( M4 P( T0 F4 W% q6 h+ h: Z/ J

    5 s* Z: G# b7 ?& b7 }        /* 驱动名,fbinfo->fix.id = s3c2410fb */
    2 o/ O0 A- }2 ?  p5 X        strcpy(fbinfo->fix.id, driver_name);        
    & R+ J2 {: X" P& S: \
    7 _; R2 t5 m9 t4 S$ K7 b- B        /* Stop the video */
    - S# f, t  |0 ~, l        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    0 w' \: h$ \) ^& J        /* 禁止Video output */
    # T9 \- H0 V" J2 m+ _! D, ]        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);- `' p' Y# y) Y5 T: ^0 b$ D- G
    " M6 k0 q5 y7 F
            /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    ( k, D2 H, ?. T2 W- L+ N5 n        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;5 S: c! v! c. s; X3 I  a1 Y/ i2 Q
            fbinfo->fix.type_aux            = 0;
    7 X6 B* T* Y5 W$ K        fbinfo->fix.xpanstep            = 0;  G! p7 Z, ]. u1 I- w  c, |
            fbinfo->fix.ypanstep            = 0;( I9 ]- Q  ?. {6 n. @' b
            fbinfo->fix.ywrapstep            = 0;0 a& |  m8 h% R# j6 p
            fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */0 v- {, o/ O5 T9 z. q2 V" y

    / o; M$ H# `4 A2 y* a        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */- G! g7 m- p$ E, n: O
            fbinfo->var.nonstd                    = 0;
    & I7 _! p" b8 R* Z. E# ]7 P; N3 r! x        fbinfo->var.activate            = FB_ACTIVATE_NOW;9 e/ e/ F$ C2 n  Q
            fbinfo->var.accel_flags     = 0;
    / F, E1 G( {7 w! H4 Q) s5 ~        fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;+ l6 W! @: E# y" j; k

    ! K' o5 F2 R. t% Y% k1 n1 Y        /* 设置fb_ops结构体 */$ ]; b) g' E1 X& `$ U
            fbinfo->fbops                            = &s3c2410fb_ops;
      @( ]8 r  |) E        & \: i# Z8 }' {& x
            fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    ) P& Y; m: K4 z! q0 {' Q+ M+ U% |. S $ W$ g1 G" V/ i. ?1 W2 }
            /* 设置假调色板 */
    9 Y* o9 Q- s' S0 u! P  l5 s        fbinfo->pseudo_palette      = &info->pseudo_pal;+ q1 U7 M+ X  _+ a, w
    . w) R) P* f4 U/ l
            /* palette_buffer = 0x80000000,清空调色板 */6 s* d$ ?. T4 w
            for (i = 0; i < 256; i++)7 b, B" Y7 }# O3 u
                    info->palette_buffer = PALETTE_BUFF_CLEAR;( D% _5 V  \( t" c: `- @$ K
    3 o( q8 K3 h3 f% P4 y+ J7 w; G6 O
            /* 申请中断,s3c2410fb_irq是中断处理函数 */" n6 Q6 ~* |, S/ i& U' L
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);: K+ `" o2 U& s: n
            if (ret) {6 T  O$ x7 b7 n0 I# j, F- l0 \
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);3 n% ]: o( C7 i# i) O/ ?
                    ret = -EBUSY;
    / `6 |# H6 Z+ U  f. C1 _                goto release_regs;! e, H( L8 X- j- {
            }) \7 K9 Z' b9 R$ ^( e9 o0 l; H

    ' Y7 l5 {4 O! D% K% ]# G' {        /* 获取lcd时钟 */
    ; Y9 I# P# O' H        info->clk = clk_get(NULL, "lcd");
    ( o, d. e+ k; a: ]8 ^0 z* I        if (!info->clk || IS_ERR(info->clk)) {; A1 }) r; k. @3 A
                    printk(KERN_ERR "failed to get lcd clock source\n");" \  T+ n/ s5 X0 z9 N$ \4 O3 M
                    ret = -ENOENT;
    ) F* U* @2 o7 J5 A                goto release_irq;
    6 G6 @9 Q4 F( \: r! c3 j        }
    : z! g1 C/ l$ N! v5 m3 ^; A - H; g% f% g- n, w
            /* 使能lcd时钟 */
    ' y1 p) D) l2 f        clk_enable(info->clk);               
    ! k, |) `% S$ q8 E) r/ D3 R        dprintk("got and enabled clock\n");
    ( u5 }1 A1 \% c4 `) ]# K
    # d* g& S  v8 f' s  S: x) D$ f        msleep(1);7 S1 r+ Z$ _6 P0 E. ^
      c( _/ l0 L. U7 X+ V
            /* find maximum required memory size for display */1 E+ g# l7 q+ t4 t0 ?

    & I4 y4 s8 n' h1 |' }        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    7 Y# B- L$ l# c+ ?- @* Y( [     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    ; I1 a3 t8 W. l3 R) E1 S     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    $ x, |: C/ ^/ \4 I         */8 B* e( B8 X9 z- X$ W  `
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    1 c: D7 @  L: k: e                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    : N- e5 L/ n' K: b( d
    0 b" ]+ i& r3 x+ P                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 *// p* ^6 R$ I8 A" D: _* W; U, M
                    smem_len *= mach_info->displays.bpp;                                        /* bpp */
    % I0 r4 R0 Y) Q- ^* y                smem_len >>= 3;                                                                                        /* smem_len除以8 */7 ]" F7 ~; F7 @) n4 N
                    if (fbinfo->fix.smem_len < smem_len)
    # y- H: q  B: b                        fbinfo->fix.smem_len = smem_len;
    6 b1 V; I2 `- m% T% R0 p        }
    ( e5 Y6 A* T( ^) F6 S" W $ r- `7 H; s) K: h8 _8 b8 E
            /* Initialize video memory */
    ! c0 `" K+ r' ?) p' Y6 T        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    6 Y& H3 P. d8 Q- `        if (ret) {1 g) r* n! h# w) g1 m. ]
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    , F: Z9 b& D, g+ t( G2 l                ret = -ENOMEM;
    - i: a9 ~2 b; Q; C1 S                goto release_clock;; n4 _' @" v, }- _6 D1 p
            }5 M2 e3 c1 I6 ~

    3 m) b. Z- N4 z+ s0 u' l7 ^9 f        dprintk("got video memory\n");+ ^3 j* I& E2 d6 P) {( L8 S
    ' C4 r+ p+ d; V# J
            /* display指向tq2440_lcd_cfg */( q7 @: e! V" w- g1 C6 |
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */
    8 p+ X$ x' H+ {. H' E  w        fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    . @+ P8 b. o5 X9 ~+ _! g        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    , a2 \/ c) R( a
    ) o8 F  h! I- u1 h' z        /* 初始化LCD相关的寄存器 */
    % \9 N& ^( \; g( C        s3c2410fb_init_registers(fbinfo);) a( a4 l+ J5 }
    # R# C6 C. O3 J5 J
            /* 检查可变参数 */. ~( X3 s/ b3 _" E, v6 i1 f
            s3c2410fb_check_var(&fbinfo->var, fbinfo);6 a! l( {6 }" W4 n( _: E8 o; H' U

    2 ~8 C- r4 b6 I: j; l6 H" B' }. U        /* 注册fb_info结构体 */8 K' _( m/ j( Z
            ret = register_framebuffer(fbinfo);
    2 k2 g  L/ t8 I        if (ret < 0) {
    * Y" O6 F# s* h# |                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    + Z% G8 a; d6 x4 i7 n                        ret);
    # S4 k$ s; E6 Z/ ?  y                goto free_video_memory;
    ! v" g6 `8 G( W, A7 p/ y        }
    9 b1 g# ]4 q. V0 T+ i% }( i% A & Z! z# H# Z. a, }3 p3 ]$ Y" `
            /* create device files */
    3 I6 ?( n$ J: ~6 t' Y        ret = device_create_file(&pdev->dev, &dev_attr_debug);+ c  M, o, K- e. `) J5 ]( J
            if (ret) {
    + y) X' `& y7 I! ]                printk(KERN_ERR "failed to add debug attribute\n");
    - I1 e2 B4 F( o/ E        }( D- X; C5 H0 j) P
    9 W7 `2 M8 V, ?, b) z( D: ~  {6 j

    9 G1 L/ T- U; [# U3 j        /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */# y4 v: y2 t; Z6 ^% a
            printk(KERN_INFO "fb%d: %s frame buffer device\n",
    + [" U5 M/ P' V8 D1 O* x( E2 h                fbinfo->node, fbinfo->fix.id);3 p4 [6 C- T* M( Q+ ]# [8 V  ]
    % ~. u; K- o1 _9 E
            return 0;
    & Q  H* p: |# u: F' s3 n 3 r/ R8 y( F& X" [
    free_video_memory:
    # P* h: P8 \) M( Z        s3c2410fb_unmap_video_memory(fbinfo);
    9 U* w5 V/ q2 w( X7 D0 grelease_clock:2 k, {. A2 ^1 b+ t9 t& @! e, p: D/ d
            clk_disable(info->clk);                                /* 禁止lcd时钟 */( [, w3 h7 ~) n1 ^. U$ [2 i* ]( E
            clk_put(info->clk);                                        /* 删除lcd时钟 */
    , Z! e( F4 S$ Brelease_irq:
    & O# ?: O9 M4 \  G# T. c+ n        free_irq(irq, info);                                /* 释放IRQ */* r: K$ @1 `6 w5 [" C0 {
    release_regs:
    8 C1 A7 `- u! z+ m. a, ?        iounmap(info->io);                                        /* 解除映射 */
    ; T5 ?0 Q0 P1 ]+ Hrelease_mem:+ ?+ v9 G& W2 S' |$ P9 k
            release_resource(info->mem);                /* 释放资源 */
    6 b' d# _+ K& b0 Z        kfree(info->mem);                                        /* 释放刚申请的内存 */6 j$ i3 k& n+ u% s$ h% e
    dealloc_fb:
    7 q8 _5 y8 s9 N& R! i        platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */
    1 d0 n4 Y, ?) B1 C  P( u        framebuffer_release(fbinfo);                /* 释放fb_info结构体 *// t% u8 H: ~1 s+ K; i, P5 A, {1 V& A
            return ret;
    - T1 c* O- i" f; E1 I5 k}
    . z" L2 O0 V3 z1 q6 Z0 I拆分详解:" d/ R/ m! C1 y6 x- h
    一、获得平台数据  s+ ^6 W& a) V6 |; z

    1 f9 @5 p+ P2 [. x! P# z4 v) y- N  O1 X, H
            mach_info = pdev->dev.platform_data;
    ! M" @! Y2 n) ?, q4 p : ~; o+ \& ?2 D8 {
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */8 X! \0 ^) p( W2 b' v$ P  P  t0 v
            if (mach_info == NULL) {
    5 s" U- z+ i3 D% E9 B5 z2 t                dev_err(&pdev->dev,
    ; c7 U2 P3 D; s1 }                        "no platform data for lcd, cannot attach\n");
    6 k7 y  c: ]) ?4 z2 i& @8 E                return -EINVAL;                /* 表示无效的参数 */; _2 ]. x% o/ v, W8 D
            }
    , z; `3 e# [4 f* _: \s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);! U3 S/ Z% `0 s8 M
    所以这里传入来的platform_data就是tq2440_fb_info结构体实例。2 K# Y9 E, H; X2 C

    . O( V- E6 S1 T3 F% ystatic void __init tq2440_machine_init(void)* H" ]' a  K% d  R' S- W& ]
    {+ a: |" k: Z0 f3 R* X
            /* 初始化tq2440_fb_info实体结构体 */0 q5 f1 j  I/ e0 y
            s3c24xx_fb_set_platdata(&tq2440_fb_info);' G; X" v2 n! k9 W0 n
            s3c_i2c0_set_platdata(NULL);& S1 y5 _# ^# B+ c+ f8 J

      H5 C. j3 P1 `2 E1 B& d        /* 添加tq2440_devices到内核,它是platform_device类的设备 */$ \1 T6 s" v4 v& \! D9 K6 G
            platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));) f2 D9 s$ @( d# W3 \/ j
            EmbedSky_machine_init();1 l; T2 T- U% A% H+ Z
            s3c2410_gpio_setpin(S3C2410_GPG12, 0);
    ' u1 F) L8 y, Q+ C8 {: e        s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);* C, j2 N7 q" ~: ]" e* f
            s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);9 B3 w/ R% `* b, t
    }
    ' a; o' Q5 g1 F3 x9 c4 fs3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info1 i/ B% ?; p% Q: P
    ! z7 `. s% s1 B
    void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
    7 L. l9 u% p2 A5 n0 N9 s{$ k1 A1 n) x7 |
            struct s3c2410fb_mach_info *npd;
    ( _( r( I: p1 Q3 ] ; ]0 H  w4 \5 j* B  D" ]# Y5 p/ U% H
            /* 申请分配s3c2410fb_mach_info大小的内存 */, O- V8 w, b: P8 n4 J3 @1 H
            npd = kmalloc(sizeof(*npd), GFP_KERNEL);) N) h# }+ w7 N5 F' C- D( I
            if (npd) {, H6 {& z$ o" M% _6 s
                    /* 拷贝s3c2410fb_mach_info型实体给npd */
    2 _* L* }* \# s) g9 ~                mEMCpy(npd, pd, sizeof(*npd));
    . a' m& r9 j2 E$ b# V 8 ^) e, I- D8 b$ ?
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */# x, f3 r0 W# c" @; X
                    s3c_device_lcd.dev.platform_data = npd;
    ' N) w1 |2 y3 v        } else {' B, h. V# N8 r" t$ F
                    printk(KERN_ERR "no memory for LCD platform data\n");6 Z7 O  J8 P# `
            }+ X+ ]+ F' d  S9 F! P
    }/ W4 @4 G) M2 g. E( }
    二、设置s3c2410fb_display指向tq2440_lcd_cfg
    1 ^: |4 l) o1 T6 v( Q3 k& H; W  z& U+ m! C  u
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */$ {: y" w9 h9 i
            if (mach_info->default_display >= mach_info->num_displays) {' m! ?' Y# ~- u- V7 y
                    dev_err(&pdev->dev, "default is %d but only %d displays\n",
    $ Q) T+ V1 P! H- h" v1 F! @                        mach_info->default_display, mach_info->num_displays);
    / q4 k$ w* I& z                return -EINVAL;9 [4 p9 X3 I. N4 P5 M: ~& |
            }
    3 p+ S6 Y' Y( D  E3 Z5 v& R& D+ d ! G+ e) m5 D9 V% o4 b
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */; ]6 S. y7 l! M6 K
            display = mach_info->displays + mach_info->default_display;  i  N2 e+ S& f& [
    三、获得IRQ资源9 [9 R3 K5 c( g* `4 t1 V, O+ N/ ?

    4 |9 ?( e. C! }) }8 M        irq = platform_get_irq(pdev, 0);0 _- h$ |# ~+ g5 L: W) T! T- U
            if (irq < 0) {: u/ `/ ^" g# d3 @
                    dev_err(&pdev->dev, "no irq for device\n");
    $ b2 N7 ?$ \! L- S1 v2 T9 ]                return -ENOENT;
    4 ?+ \! {( T5 A        }" h. m9 \* n: i0 r$ ]( N
    四、分配fb_info内存' R) b* X: x. `6 Y
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    , x/ ?6 l4 D" M( E# A7 x( f        if (!fbinfo)
    + Y) f! l2 \# i, N+ r1 u( C3 {                return -ENOMEM;                /* 返回NULL表示失败 */
    2 ~' }+ y7 w/ Xframebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。$ z9 p& Q/ c- r( g7 F' m$ g
    五、设置s3c2410fb_info结构体
    5 n. B' L5 v" q* Y( |1 V6 S- I& A$ m" ]
    /* 相当于pdev->dev->driver_data = fbinfo */5 m: `) o7 B$ Y" p' ]- m6 H) k
            platform_set_drvdata(pdev, fbinfo);0 j9 y) @9 s! J( P; a8 E9 D

    . N# R7 H  w  n        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    - v# z6 g2 u4 H% ^8 D! s$ G: X        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */" e% ?' ?$ y0 j1 P( H
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    3 z$ |1 }! a- z; U. E        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    1 q- x. k1 r% s( B六、获取IO资源,映射IO
    / S6 x6 U- |( y! m: [. `
    % [# X$ s: a; |6 x7 o' I/*  通过平台设备platform_device获得资源(IO) */* {; {3 t6 ?  A8 D6 B( q7 o. q
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);! h3 [% K8 C9 B; T7 o# N
            if (res == NULL) {* v3 w5 }8 V& v$ l# j4 y+ O) U
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    2 `+ F$ @6 I/ k) c                ret = -ENXIO;
    7 d, h! A3 p. W5 U9 M                goto dealloc_fb;
    - |2 n/ k& Y4 Q        }
    / H( M; y4 T: ?9 V% U  f2 M- j
    ; L( x; C6 t7 ?& Y5 c& w& t" i        size = (res->end - res->start) + 1;                /* 资源的大小 */" T6 d5 Q7 ?% {  J: w) {4 Y7 W
    / f7 [( B+ A; u+ S
            /* 申请以res->start地址开始大小为size的I/O内存 */
    % `' S( k3 {( B4 b/ I" R        info->mem = request_mem_region(res->start, size, pdev->name);
    + U+ F: I+ W" _        if (info->mem == NULL) {4 W2 a2 g3 S% p) x3 A/ X
                    dev_err(&pdev->dev, "failed to get memory region\n");. ?: H  K) b; L# w
                    ret = -ENOENT;  c4 h, u- i* b
                    goto dealloc_fb;5 c+ j$ @1 q" C/ j& X
            }* ~" c1 i: g4 i; a$ k# b" Y; t

    1 ~; R! e1 X: @" g4 |  e        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */4 s" z% T7 T4 G5 n) h
            info->io = ioremap(res->start, size);( ]0 d7 w6 d+ L/ H$ A3 `
            if (info->io == NULL) {8 t% l1 m5 L. M( _' n* B
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");
    , c3 I( T3 f8 T* K                ret = -ENXIO;
    6 s1 r, R  V: L                goto release_mem;
    ( l9 n' r6 T+ y        }
    ) y" A! H$ ]& [+ z5 |9 q. S+ Y & Q6 h' z/ N) F" K
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */1 ^2 I, G- p6 M$ o! u
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    5 B1 ~( B$ h, z% n8 k+ ^3 ~/ f七、读写LCDCON1,禁止视频数据输出, A6 r* X' W! _' J
    9 n+ ~" P% ?1 n. w' V
            /* Stop the video */
    ( v% \& F! k+ C8 h$ [; e        lcdcon1 = readl(info->io + S3C2410_LCDCON1);0 f' Q/ w: P+ Z- k) ^. k. D% U6 U
            /* 禁止Video output */
    * E8 W  @' B% V: P+ S2 x3 p( P+ @. }        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    , E( ^3 R& g0 w5 N( Y八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    . }( X% O! j  v4 ~; g& W, c! c' L
    3 V, P4 [+ q5 I, j/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */: ]9 g* |( L7 r0 c8 Y: a1 e4 y6 r
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;( Q4 X% _' B) ^6 \0 f1 L
            fbinfo->fix.type_aux            = 0;7 F4 M" O# W# @/ P" j7 m
            fbinfo->fix.xpanstep            = 0;
    5 F8 W, ~# A+ Z' s) J: Z7 P, i        fbinfo->fix.ypanstep            = 0;
    ! l8 T: u% k8 Y7 j        fbinfo->fix.ywrapstep            = 0;
    # y& o  u& X) o5 d2 G7 Y        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    # B5 `! Z3 d4 Z! E1 T; w - |3 {6 u" ~; ~, `: i! i4 s
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    ( K3 g1 S0 f- a/ J' w        fbinfo->var.nonstd                    = 0;- ~! k9 W: n4 d" w0 o7 C4 x. y9 a0 m
            fbinfo->var.activate            = FB_ACTIVATE_NOW;
    ( e5 M" j5 o" Z) g        fbinfo->var.accel_flags     = 0;% H: c) z: t% F7 S4 t
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;: T0 J/ i. ?; W0 O3 e- N. C
    6 r5 x& b- |( b/ D' B6 n
            /* 设置fb_ops结构体 */
    / H- f; N* {1 @" o' K1 V        fbinfo->fbops                            = &s3c2410fb_ops;
    , ^, X: i% d1 V/ W& s# ]( ~        
    ; i, ~, A4 i7 `6 ~        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    # z2 z6 o- ?( F 3 R: n# m& p/ }8 l0 v
            /* 设置假调色板 */4 @- z/ g: ^& B5 `# D$ E: g
            fbinfo->pseudo_palette      = &info->pseudo_pal;- a* J; b) w2 K) @

    ; n: R! ^: X7 h3 g- k8 Z3 C        /* palette_buffer = 0x80000000,清空调色板 */, b- j- s6 p$ E5 M
            for (i = 0; i < 256; i++)- i3 P; `2 [9 Q1 u; T( ?
                    info->palette_buffer = PALETTE_BUFF_CLEAR;
    # n5 c1 v$ M: A& r7 P! t4 Z/ g) a' S2 L九、申请中断、获取LCD时钟,使能LCD时钟4 c- ]  I* i- B  d" {; V* p
            /* 申请中断,s3c2410fb_irq是中断处理函数 */& n3 t! R( D7 k/ I9 |
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);5 o1 D, J5 N. t- S
            if (ret) {
    & p3 S7 t0 z5 }# ~/ g, A* H                dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);& c2 v6 z% U! b2 i
                    ret = -EBUSY;
    1 n4 R5 s' B6 x  N. A& u' d& U3 r                goto release_regs;' b- i$ u" R/ t
            }5 s; t* V# r) M3 y" U
    7 u7 d: I# Z, t4 U" Z! e
            /* 获取lcd时钟 */0 m0 P% r, t! g# R  F4 g
            info->clk = clk_get(NULL, "lcd");
    , e! P/ u; b( }' S) v$ E        if (!info->clk || IS_ERR(info->clk)) {
    ( X$ o4 f# h+ k7 j                printk(KERN_ERR "failed to get lcd clock source\n");/ p9 J! H, u! K. s
                    ret = -ENOENT;" j/ t2 g0 o$ |1 b. `) y3 P* N' D7 Z
                    goto release_irq;
    & u. N* {" z7 ]        }  {2 i  F- R) z$ T7 B: s/ Q/ R1 L
    % S( ^; p+ |2 z' z6 E/ e, S
            /* 使能lcd时钟 */; [6 x, v8 K5 r. _4 f
            clk_enable(info->clk);               
    4 q; B- o0 V- X/ f9 g        dprintk("got and enabled clock\n");5 B1 y# u0 |' e  K
    十、计算显存大小、分配显存内存) D1 ?" o' \. K! `# K) Y

    - ~0 F" C1 z/ j6 ~0 U8 h: h        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    7 d% {' {6 [+ |: g0 f     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。- J# D) s7 P& g8 t3 w6 C
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    0 e+ J) G. f7 r4 o6 }: g         */* `9 i6 Q" G- F3 z& W
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */% R. I' \! t9 ^6 R$ z, r- a8 f" b2 X
                    unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    + z" R* P1 g" J2 I  A& B; I9 }# ]) _. I
    3 l1 U3 h( k5 C: S% N, o( ?- i- G                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    : J, [0 U+ H6 q( B( x5 G3 @* k; e                smem_len *= mach_info->displays.bpp;                                        /* bpp */
    - i; s, @6 E  t" b+ `4 P- d* b2 K& Z                smem_len >>= 3;                                                                                        /* smem_len除以8 */2 X0 O8 p. c7 h
                    if (fbinfo->fix.smem_len < smem_len)0 B' v/ P1 _, `% x0 w2 y# ^
                            fbinfo->fix.smem_len = smem_len;
    ( L: {2 |) W/ L        }
    ! ?* P. C. V) S! e 8 A2 j0 g9 g4 Q$ |9 k
            /* Initialize video memory */0 e) t* h4 u# @8 i% U0 z
            ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */' s' w" ^# p8 u4 ]
            if (ret) {3 k1 f5 y7 A' T' Y; A- C
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    - e/ _5 E& L5 s/ ]7 V6 X. s5 g* K                ret = -ENOMEM;
    0 ^* a9 H' `1 `7 G3 I5 X" h                goto release_clock;
    $ v$ q* I4 R% d  |/ T8 j9 W$ z" r        }
      E% F" w* I1 X* P* a十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    1 q  }7 {# a5 O3 t; {' ?! g* `; t+ K  ]* ~
            /* display指向tq2440_lcd_cfg */, z5 ?" N' H5 [6 a8 o
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */
    ! n0 Y% q4 R1 k7 @) n% Z0 b! d        fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    * A+ m+ S  \- k: S+ q        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */* B( S6 v7 |% D. {; R
    十二、LCD相关寄存器的设置和fb_info的可变参数的检测0 X8 y6 K- J; O. n

    3 G4 }' p; q3 R( t( g. n/* 初始化LCD相关的寄存器 */
    % o# m' E6 e6 N7 d        s3c2410fb_init_registers(fbinfo);4 j4 J. ]: j# R1 o# A

    # O5 U" G, U8 S8 z: @& b        /* 检查可变参数 */
    , ^( e9 k$ a" W  K, Y9 o  B        s3c2410fb_check_var(&fbinfo->var, fbinfo);
    2 r# f0 c0 N  x9 b% M十三、注册fb_info结构体; z0 W+ I% w! K1 c/ ?# H
    , n  ?! k8 S4 D! G4 N
            /* 注册fb_info结构体 */& R  K: E" h* n; y* c$ s+ J
            ret = register_framebuffer(fbinfo);
    : R" c7 I# w3 A" {* H' _" t1 i        if (ret < 0) {: u4 I2 _/ z( E( J0 ?3 }
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    1 ?% q" _. J' m7 @1 w/ `5 |                        ret);
    9 }2 f' X2 h- Q4 J& i                goto free_video_memory;2 z" R) W' W  z9 e% h
            }
    . |0 C5 }5 g5 R4 i7 I3 Q. ]5 z
    4 ~; u6 |; y- y( O

    该用户从未签到

    2#
    发表于 2020-4-23 13:37 | 只看该作者
    linux lcd设备驱动剖析
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-25 23:07 , Processed in 0.187500 second(s), 24 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表