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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    $ y( _& M0 h" O2 s& X上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    / {7 u# b* }1 L. B8 Z$ k  `8 Z/ r  C- j! t% e
    上一节文章:linux lcd设备驱动剖析一4 y- u7 z# o* L  B; Y8 a9 s

    6 P5 |1 w5 Q5 ]* n( ?上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
    + l3 ^" T# @( }
    0 J+ w  {: P0 U; a3 y
    ( ?4 I# ?: b  X& q, Pstatic int __init s3c24xxfb_probe(struct platform_device *pdev,
    8 \9 Q- N) _1 q: @                                  enum s3c_drv_type drv_type)2 n+ F  i+ s4 ^& C2 ^: K) [" c
    {1 I, l; j0 J! C+ l8 k1 t8 _
            struct s3c2410fb_info *info;9 v8 [' V1 n4 Q- ]
            struct s3c2410fb_display *display;' A2 e+ D! n* o, Y% |& l3 B) E
            struct fb_info *fbinfo;6 r, s5 a/ ?3 H7 c
            struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */
    + B/ p8 ^9 X0 a: [* a        struct resource *res;& Z) |/ o, \  a; U+ M
            int ret;
    0 T. x2 j8 Z0 z        int irq;( i! o$ V+ \, `% x
            int i;
    " k9 s5 Z5 D6 Z+ f        int size;
    * v6 o, U* z+ u( `% }        u32 lcdcon1;
    , n8 j5 b, L1 {3 u8 [* a
    , L  h! \7 n5 k, }3 V0 m' B# s( G        /*  s3c24xx_fb_set_platdata()里会设置platform_data8 m( Y% \; }- }3 l
             *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    $ q( K. C  l# M         *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    - U7 M6 V9 W5 T8 A3 j5 W         */* U0 z, ]8 @6 g! m6 v
            mach_info = pdev->dev.platform_data;. G. i7 \- U# [

    # Z' u. |8 U" @8 X4 g2 z5 z4 ]        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    ' |  S7 o# w& n8 V$ w        if (mach_info == NULL) {" U5 S' P# e  g* ~0 C5 s
                    dev_err(&pdev->dev,
    # p5 d1 t7 V& ~4 G! I1 e                        "no platform data for lcd, cannot attach\n");* p% {8 z" {3 i8 v3 Y6 t
                    return -EINVAL;                /* 表示无效的参数 */# h0 e: E9 B' z( I3 x
            }) O1 {1 c2 ~8 ]8 c

    9 {" Z8 t2 ~1 w/ Z        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */! ]8 G) a6 J* J/ B$ ^7 |
            if (mach_info->default_display >= mach_info->num_displays) {9 ]/ x) @3 G# H  ^
                    dev_err(&pdev->dev, "default is %d but only %d displays\n",
    ( X$ v) [* W0 J5 x. D( T# X                        mach_info->default_display, mach_info->num_displays);
    , W6 i1 `  S  t4 y+ r                return -EINVAL;
    / U) M# ]3 M4 ^' ]" g7 ?$ I! M        }
    4 ^# f6 G' I' B6 f % d; r8 i6 R- f* _- T
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    & n, X4 {0 M4 W- r7 s3 ]& f        display = mach_info->displays + mach_info->default_display;' K  Y* l. J7 S( x( _+ D( A
    9 ], R0 d: S; Z: D2 N7 Q
            /* 通过平台设备platform_device获得IRQ+ g' J) A' `. C; M' q
             * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)0 ~: j/ v" Q9 _/ y  G
             */
    1 Q4 V, e- j. |$ ?4 F; D: ^1 p        irq = platform_get_irq(pdev, 0);$ X# Z0 w- ~: I3 g0 O& W
            if (irq < 0) {
    # _1 z/ p3 W1 z) A7 R2 S' ~" V                dev_err(&pdev->dev, "no irq for device\n");
    # i1 l4 A- g8 X                return -ENOENT;
    8 B6 F  p, c) K; y$ S5 M        }, f0 x! U8 T- f# n$ G
    , g; L& e. D& d+ ^
            /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间
    3 K8 W- I, j$ K4 J2 w* p3 W         *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
    2 t6 R, X# L& Z6 D. \         *  比如:clk,resource,io,irq_base,drv_type等额外信息  P0 ^2 u5 ]/ T7 Y8 z
             */
    + {- k* ?  v4 n2 g6 {; S/ I* F3 n        fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    ( `- m: r+ |- e8 c# d" K        if (!fbinfo)
      S' K/ N/ Z; S3 x  N5 U, k/ z3 G                return -ENOMEM;                /* 返回NULL表示失败 */
    ( [& ]/ e( |; U/ X5 G) O- X, S
    3 T% ]& L" I; ?0 k4 g        /* 相当于pdev->dev->driver_data = fbinfo */
    " K, S) |9 P5 ~- u+ l6 w; N! O        platform_set_drvdata(pdev, fbinfo);
    6 X( [4 b. i/ D1 `& q' G
    & Z+ e% M' K& P. U3 x( t' ?* }        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */# }2 ]8 V4 z: U7 L' k
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */! K2 V8 l' z* P) C' w
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    , n7 |$ }# @8 j) {; I4 G! g        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    ' R4 a5 T" A5 `4 f7 c " ?/ g. B: d1 y- e# t& D
            /*  通过平台设备platform_device获得资源(IO) */
    : i; N1 x* r# P# x        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ( V  Y8 w. U/ _( y* _        if (res == NULL) {
    0 S# A# M  U% }8 l- O                dev_err(&pdev->dev, "failed to get memory registers\n");
    8 j0 z5 Y1 ]5 _3 l                ret = -ENXIO;
    ) `5 Y: m' ~" e/ X- b# |                goto dealloc_fb;
    1 P' O% k- V- A/ y. C        }
    7 J( M, ?% L4 {/ U . b7 K0 {) d# \: k- u  d1 y6 e
            size = (res->end - res->start) + 1;                /* 资源的大小 */  P% w9 V$ ?2 q/ C

    4 D3 J" m- E2 J        /* 申请以res->start地址开始大小为size的I/O内存 */
    ) g3 s; d% O3 K; g+ N: n+ N: ~        info->mem = request_mem_region(res->start, size, pdev->name);( ~9 I- D8 a4 k( `- r
            if (info->mem == NULL) {
    8 A) V3 W% p8 b* x% D% k  `$ H& r                dev_err(&pdev->dev, "failed to get memory region\n");/ q7 ]+ @6 J# u7 e! Q$ f% r
                    ret = -ENOENT;0 k0 E2 }+ M) D* z# u
                    goto dealloc_fb;$ d$ k3 _1 ^: b) x" V/ u
            }
    ( v+ V, C, W- i
    ' ^" d* H- _9 }        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    0 P, t8 p& q5 m! D  H- E8 S        info->io = ioremap(res->start, size);
    8 H# b/ h/ V/ O; N, V        if (info->io == NULL) {6 F& ?& @1 F7 I/ Y4 x7 ]
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");
    ' z. x& L& @: v: ~% W: ]                ret = -ENXIO;
    4 F1 S- Z" l8 b! t                goto release_mem;4 Q  O5 X# l+ }5 r: _, [
            }
    $ K( _, _) q/ _ 9 s1 x$ U. S$ J: Z
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    ) W8 L" G$ g  C  X1 Q! x: ?        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    . X  Y% ~5 @+ F- B/ r% B
    6 K5 }' \+ Y# t. e        dprintk("devinit\n");8 q' z( I" s* p& v* W/ J+ E8 v
    & X/ T! F' ^( B% l: m9 o
            /* 驱动名,fbinfo->fix.id = s3c2410fb */- l9 o1 F5 d3 w) c# p) k
            strcpy(fbinfo->fix.id, driver_name);        5 S, a$ G  @2 P
    + f* D) s! S6 x  A4 k+ o3 e
            /* Stop the video */
    ( z& [5 x8 _; n        lcdcon1 = readl(info->io + S3C2410_LCDCON1);) ]$ T4 J! i' h' Z; j
            /* 禁止Video output */8 Z3 V5 }2 V# n6 r* a! ]
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    & f0 V) z# I7 `. M# K% @" d . X5 S4 a) D/ o+ F, M9 c
            /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */4 V4 g! R& M$ N2 ?  ^/ h+ s
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;3 G% O6 P$ ^' K+ A
            fbinfo->fix.type_aux            = 0;
    % e: f9 L5 O& h' \) Y- s' d+ O        fbinfo->fix.xpanstep            = 0;3 D4 P$ \' A& H: K8 ]' @8 n. F
            fbinfo->fix.ypanstep            = 0;3 w4 A0 q5 L$ }: X  x. g) i* k$ k) K
            fbinfo->fix.ywrapstep            = 0;
    / }! P% \# t5 w        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    " {" v2 u' ]% W) r! @3 q3 q 6 ^* o# k) l8 }: E) k# p3 w$ H
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    $ J2 I" g/ U+ d4 M        fbinfo->var.nonstd                    = 0;1 k4 c9 e# Y" [! e0 B0 b
            fbinfo->var.activate            = FB_ACTIVATE_NOW;8 D( x% L2 \$ m9 P! a! d' n
            fbinfo->var.accel_flags     = 0;) Z7 i; [) _6 c3 s* f. F
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;0 |2 E2 W4 E2 g' N
    8 T* K0 g2 H4 O5 H2 X4 R  D) f& I2 ^
            /* 设置fb_ops结构体 */
    + p: D" i6 N& G( ~5 N& }& l( X- X        fbinfo->fbops                            = &s3c2410fb_ops;
    9 e) `  f1 D0 t4 `' Z3 @: q        & z. j% ~: P2 F8 u4 ]# b
            fbinfo->flags                            = FBINFO_FLAG_DEFAULT;' x4 l# M# v' `$ I! M

    0 D0 r4 k! U6 Y: h% i' T        /* 设置假调色板 */
      X4 F9 G2 M* M: c% [* q8 H& h        fbinfo->pseudo_palette      = &info->pseudo_pal;1 g% S  S5 h8 p: P" j
    2 M$ M+ l# a( i' m+ j/ V
            /* palette_buffer = 0x80000000,清空调色板 */* I" K' n0 U$ m5 @
            for (i = 0; i < 256; i++)1 _3 `$ ~5 e' y4 ^0 @: u0 w
                    info->palette_buffer = PALETTE_BUFF_CLEAR;& F3 [" }6 L5 U- e+ j
    & w9 ^! q) a8 \
            /* 申请中断,s3c2410fb_irq是中断处理函数 */5 _) G, @6 y, K5 E' H: w4 s
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);+ Y0 m: W& p* d& `/ p) Z1 n
            if (ret) {( F9 p& i# J2 ^6 i0 {* t
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
    # ?9 a/ x" w+ S. j  b/ \# R                ret = -EBUSY;
    1 D% [3 w$ a! `8 W  @. M                goto release_regs;, i* e  P+ M2 ^7 c
            }% E6 r7 Y0 K% B9 \: g- x
    5 f! W/ V4 a( v$ S
            /* 获取lcd时钟 */
    9 r  A9 o5 |1 D  C        info->clk = clk_get(NULL, "lcd");
    + V4 v3 J+ e- N4 g& g* ~        if (!info->clk || IS_ERR(info->clk)) {
    2 ?# P8 Q" Z2 {4 Q. f  k$ L                printk(KERN_ERR "failed to get lcd clock source\n");
    7 n" m3 m$ p* U. f6 [4 Y$ K# I- A1 @                ret = -ENOENT;- C3 M6 y+ f8 {) k& }
                    goto release_irq;" ]; v% k% U6 A# ]; x$ i
            }
      H$ v( H  M" e% e  u2 G" c4 `' C 4 Y5 I: u1 f! U5 V& D! w) h: t/ o
            /* 使能lcd时钟 */1 b2 C; t" ^3 T
            clk_enable(info->clk);               
    + K) h+ U% ?* P5 k$ Q; [) |        dprintk("got and enabled clock\n");# A8 @" i5 _. C) E& e! w6 ?  j0 r+ D

    0 g8 h7 y9 x+ ^& S        msleep(1);: E0 n7 H8 R+ S5 Z, v3 V
    4 s8 G9 D) A2 j7 d1 V. T7 H
            /* find maximum required memory size for display */8 V7 i( I& S1 `1 n8 R3 w

    $ Q1 K. j0 S7 M        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    " O- u( Y3 y, d5 o1 K/ ?     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    # N5 B: M1 ~; x. C     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。) |' t( u  \* s+ M( g
             */" ]( ^2 f* {/ }8 @; G- @- i
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    ' G" U3 W* r5 L/ j1 T# S0 I3 L                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */0 `$ q. L% V$ |' s! |/ |2 @

    ; N# G" P3 q9 b8 Y                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    9 w2 N2 J; }& M) ]8 m                smem_len *= mach_info->displays.bpp;                                        /* bpp *// C) n) o; A' c
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */
    ; T  z# H2 H% D' B1 P( L0 d                if (fbinfo->fix.smem_len < smem_len)
    ! u' a* \1 H0 Y& j; C                        fbinfo->fix.smem_len = smem_len;
    ) q4 _3 D# R, [' t        }
    & K/ ?& k, a) W
    8 [' L8 S! v9 z( F, N9 s2 @4 i        /* Initialize video memory */
    ; u+ Z$ c& C3 c- ?        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */4 t9 p- F5 ~; W! D$ o
            if (ret) {
    $ v% r8 E% Z; u8 a  g                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);) r3 D5 v9 v2 c
                    ret = -ENOMEM;9 Q5 i) k5 t5 n% S$ K
                    goto release_clock;6 ?9 X7 r0 S! Q. u) Z
            }
    ! u# C" t7 z& ?# h6 h4 }
    ! B" E: a, S' U: @" m' k% h        dprintk("got video memory\n");) k( z- V# u6 B& B) Z/ @$ u4 l
    + {9 N; r- F+ J. L5 `& X. c) n
            /* display指向tq2440_lcd_cfg */1 ^* h& |/ D) l* z
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */0 V3 D4 \9 M8 v3 t' |
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */; S7 {! h" N% n0 \' i+ z
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */  D: j& k  w: |, _/ F4 q3 f* r

    : c- ]* w, X4 j3 s6 m7 V        /* 初始化LCD相关的寄存器 */+ j( z7 C" l8 R: W1 \8 p
            s3c2410fb_init_registers(fbinfo);) c8 _0 m7 Z, r) ?& k

    - V9 m4 L( d; g4 j* c) ~4 B        /* 检查可变参数 */( O  O0 [* Q; H( f' O
            s3c2410fb_check_var(&fbinfo->var, fbinfo);7 b! }/ y8 f" K; e" y
    2 V+ w3 q9 b7 L. `
            /* 注册fb_info结构体 */
    , G! x9 G/ L7 x0 j- y1 z* t+ {# T" b& h        ret = register_framebuffer(fbinfo);0 g1 W: e# Q& ]0 s
            if (ret < 0) {; _( T6 h5 I" s/ Z3 i  E! r
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    ) b4 U2 U9 V8 d) S( J( e9 _, s                        ret);
    6 K3 [7 U- s: ]& e                goto free_video_memory;
    ; [2 N; b- ?3 r/ e        }8 v" I, ~: k( i0 s' D

      R7 u! L  {' ?        /* create device files */
    0 L! H7 H2 d% u" Y        ret = device_create_file(&pdev->dev, &dev_attr_debug);
    1 K$ h1 m% D1 T        if (ret) {" O/ J( `% V  R& V
                    printk(KERN_ERR "failed to add debug attribute\n");
    : c! r; Y0 B  Z' L/ f        }3 y* @! y- n0 y" ]' L

    1 j; l1 v1 g" M. E * v5 p' w7 ]; F( O1 b' x
            /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */
    & m9 A! g( `1 d        printk(KERN_INFO "fb%d: %s frame buffer device\n",
    3 ]5 u6 A% [9 @$ s$ U- r( b: D0 x+ U& f                fbinfo->node, fbinfo->fix.id);
    - B' a# X; d" `5 z! R1 v
    # C3 x' ]/ o* {        return 0;2 k2 ?: K' q7 H; V8 f% p/ e) \
    6 F0 C. |* b  g" _$ i; ^
    free_video_memory:4 d( l5 |. {/ X. K
            s3c2410fb_unmap_video_memory(fbinfo);
    / L$ Y4 s  G1 I7 Prelease_clock:
    2 ]1 c' C: q7 d6 }1 q; X8 I        clk_disable(info->clk);                                /* 禁止lcd时钟 */
    6 L' N& G0 ^* K. g0 \" G        clk_put(info->clk);                                        /* 删除lcd时钟 */
    4 k3 L" X$ M. `release_irq:8 b" [" |& O9 J+ I+ q1 u
            free_irq(irq, info);                                /* 释放IRQ */6 I' x: U- z- v  I( m
    release_regs:
    & y! i- a- y+ C2 e# ~" h$ U        iounmap(info->io);                                        /* 解除映射 */
    + Z5 Z2 P: z6 Y# F4 C% W5 Xrelease_mem:. n. |; g1 e$ j, g, J  h; p
            release_resource(info->mem);                /* 释放资源 */* }2 W6 N& q$ _! ?$ |6 I
            kfree(info->mem);                                        /* 释放刚申请的内存 */
    9 O0 W. y3 l2 C# c3 ^* ?, Gdealloc_fb:* y  N# b4 G" u' q- v3 o8 v
            platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */
    6 b9 I" V5 x+ d2 V' k, j+ o        framebuffer_release(fbinfo);                /* 释放fb_info结构体 */% M% T5 i( |2 t4 W: g
            return ret;
    5 S! p1 O1 b' B}
    6 B6 ]- }) I8 |0 ?拆分详解:
    ; H- q9 T- X* I6 ?( j& Z2 T+ `* i一、获得平台数据: |/ C5 R0 q( n: p5 }, k

    : |. a4 c* f% x
    * g' v8 l8 e4 B7 I- P        mach_info = pdev->dev.platform_data;& L5 v  \% ~& a7 F! o: {% W
      M9 H) Y/ ~* v4 h- {. {* h
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    ) S, W) k$ k3 [% _2 a! Z% E        if (mach_info == NULL) {5 [! W' ~9 f& k6 A$ {1 p
                    dev_err(&pdev->dev,- Q; |/ y: ]1 w: C; T7 |4 A4 _
                            "no platform data for lcd, cannot attach\n");
    3 {1 G- c; z" v                return -EINVAL;                /* 表示无效的参数 */) n1 Y7 z% ]- q7 n
            }
    4 q/ [* ^! J6 d2 r% }$ Os3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);$ o5 ^# |3 ]5 t& G' g
    所以这里传入来的platform_data就是tq2440_fb_info结构体实例。8 F# _' p9 t' Q( S* ]
    * ~2 k+ P" H* J- P9 t, O! d7 J( t$ ^
    static void __init tq2440_machine_init(void)/ C/ l% [5 }, A: S7 n
    {
    ( I0 m( g* ?6 h; O/ _* C7 B        /* 初始化tq2440_fb_info实体结构体 */0 t0 W% Q7 f6 A/ F! p( @; W
            s3c24xx_fb_set_platdata(&tq2440_fb_info);% S* l5 Z- E/ l
            s3c_i2c0_set_platdata(NULL);: m0 ?* v% v7 c; W5 c3 D. z

    ( |- \1 {" F% Q2 R        /* 添加tq2440_devices到内核,它是platform_device类的设备 */
    1 b! g+ G4 p4 h: @  o% `, }  j) B        platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));2 P5 q- p& Q6 O
            EmbedSky_machine_init();
    3 O$ x$ }2 D$ a0 ?( t' ?/ E        s3c2410_gpio_setpin(S3C2410_GPG12, 0);
    $ c; G2 x9 h# H2 X7 j        s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);8 I# O# L& J! [& |/ E
            s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);2 ]- j( G# ]! G
    }
    3 S& A* v% p6 m% r. Ss3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
    ! j8 b- X: |0 W- F1 q; V
    7 j9 @2 A/ w& Tvoid __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)6 h5 @0 y  `0 K1 ?  \- W
    {# ^! E, Z) h3 J% p! D2 i8 A, i
            struct s3c2410fb_mach_info *npd;$ M+ {! d& T) h# F0 k: W9 S
    8 h+ U* y8 P) t
            /* 申请分配s3c2410fb_mach_info大小的内存 */
    1 h2 X, \3 I" P3 M$ d; q0 }* n5 L        npd = kmalloc(sizeof(*npd), GFP_KERNEL);
    3 h, X$ l# G# O! z+ }) y        if (npd) {
    0 T# ]/ [3 W5 l* H5 X                /* 拷贝s3c2410fb_mach_info型实体给npd */
    8 H- s1 b7 H3 x) \" o% v+ x                mEMCpy(npd, pd, sizeof(*npd));
    1 \* O3 f9 E% Z: r % P  Q: E, I" J6 z
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */
    # g7 _+ g& Z! r0 D$ ~2 H9 w                s3c_device_lcd.dev.platform_data = npd;' S! P9 Z" G- I! v7 F8 b
            } else {8 I. I7 Z7 n  x$ r0 V7 F: M+ s3 X
                    printk(KERN_ERR "no memory for LCD platform data\n");
    . e1 w0 V$ q) j6 m% v4 W* ]* s        }7 q  n$ l* n+ F
    }: f5 _' O+ X# ]& m9 a; b, z9 W! j
    二、设置s3c2410fb_display指向tq2440_lcd_cfg
    & |. E, `) E8 V) e1 z/ ~. x& d, A+ d( F3 y8 K, `
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    * w0 g! l% X/ F# D: n/ T  r  B; s        if (mach_info->default_display >= mach_info->num_displays) {
    - y% m# u5 X* H                dev_err(&pdev->dev, "default is %d but only %d displays\n",
    ; f( }; h; Q/ }                        mach_info->default_display, mach_info->num_displays);
    , S7 l* J% r9 A6 Z* Q, \( I                return -EINVAL;8 h, N& G' I& k' N, @
            }
    ) \- a* ^. L7 B% w$ l  M6 a5 C  z   Z) t' s' L+ R
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    & I( S4 V3 B+ r9 i6 q. T        display = mach_info->displays + mach_info->default_display;
    5 b# g( }5 u1 D# i: {4 o! F三、获得IRQ资源
    3 ]# L5 t# s6 F7 m/ \1 }8 v& P' }7 e/ g  n7 p
            irq = platform_get_irq(pdev, 0);3 ^4 a8 \1 H% A* P4 V
            if (irq < 0) {! Y0 q% J0 K* U7 Z! T2 x* L
                    dev_err(&pdev->dev, "no irq for device\n");
    7 v. N& B& S. S; f" j  k                return -ENOENT;
    $ O7 x: X& H' s        }
    7 c# r: U8 e5 X0 V; Z! @' v* Q四、分配fb_info内存5 h. }; S" [; u+ a, P) \3 Q. G
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    / o; Q3 [  }0 R$ {( N( f% }        if (!fbinfo)
    8 H% g- D. N$ F* K  j                return -ENOMEM;                /* 返回NULL表示失败 */1 N* |5 S" l0 d4 M9 S' x' x* |
    framebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。. c# A4 w& Q* x8 d8 s: ~# g
    五、设置s3c2410fb_info结构体, B+ O! i6 g5 o! Q7 d
    1 E) ?% s' n9 F4 r& b
    /* 相当于pdev->dev->driver_data = fbinfo *// [; N* B9 G4 F5 |4 f7 J8 \  q& G
            platform_set_drvdata(pdev, fbinfo);
    ! F, _, f; W" G: T0 k" a 4 \" e  u+ t/ N9 J
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */8 S0 m; q! D  \
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    3 h/ ?% `" I, z8 |1 W        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    , q" }8 `' ?: i) u0 @        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    % p2 U7 ]# E& o3 D7 X- v六、获取IO资源,映射IO
    * h1 i& D# J, o
    # W0 S/ J9 H$ f' ?3 R/*  通过平台设备platform_device获得资源(IO) */
    ; [5 T/ c4 p8 g5 W        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);) v3 c3 G- L+ Y( b
            if (res == NULL) {' _4 ^4 U6 n3 u3 z
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    5 U, Z5 ?( r& d                ret = -ENXIO;5 ~; |9 c. h; e" F: ^/ @8 x
                    goto dealloc_fb;
    ; O3 |. o6 E6 R) |: v        }' @  y3 o4 g, Q
    ) h) @$ v1 W7 x! A
            size = (res->end - res->start) + 1;                /* 资源的大小 */
    " {: X9 \) T/ c0 l6 v4 s3 o5 C
    / `4 ^* Q2 C# i. x5 X0 C# D        /* 申请以res->start地址开始大小为size的I/O内存 */
    2 U4 y; N2 N% E4 r5 r        info->mem = request_mem_region(res->start, size, pdev->name);! h% u6 W9 J) C( K
            if (info->mem == NULL) {
    ( Y' b; a/ m- |                dev_err(&pdev->dev, "failed to get memory region\n");
    ( @% t) \9 l9 \; |                ret = -ENOENT;% v- d4 i: [4 |
                    goto dealloc_fb;# ?: u8 C6 J; J; V
            }
    3 F0 I% d9 _2 g' ~ , t1 K: k1 Z3 l3 T# K8 h
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */5 N) g3 t# s# n9 X# {
            info->io = ioremap(res->start, size);
    # M) [3 p, x' P+ t! ~+ {' f        if (info->io == NULL) {+ K; X8 ~' O3 X
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");
    $ k* l& k. \# P  u6 S% J4 }1 _                ret = -ENXIO;
    . ]  D- q; y/ U, u' A                goto release_mem;
    % _4 x8 I9 g  [: e. N: V        }. {: B  U$ f$ |& L& m4 b6 ~6 R
    ) E4 S6 l5 X( e, j' P
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    , B# q# F& N0 K. a/ G1 P  Z, M8 W        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);0 w4 U! }* s9 j9 B9 Y* M! n8 K2 N4 Z
    七、读写LCDCON1,禁止视频数据输出
    8 L- y0 g& E( C- l2 ^% n- ^
    2 p# J0 X* B$ k/ V0 D6 V        /* Stop the video */
    ' H2 d, J- e- {) U' q/ |, q5 J& S        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    ( G7 j. E3 V! `2 P, J8 V        /* 禁止Video output */7 d6 @6 w& b! G' C# }6 @- @
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    4 }/ V" H' ], o8 ^5 x八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    1 g+ H" D4 e; w( Q! S# n" x  v$ u5 d' w* K+ y3 y
    /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    / R4 e( ^* a& }  [        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;5 c. C4 W7 j- H3 @2 h# P: [
            fbinfo->fix.type_aux            = 0;
    , {/ o2 x! J! J% Z        fbinfo->fix.xpanstep            = 0;) l4 C- o* [2 ^* ?7 |
            fbinfo->fix.ypanstep            = 0;. d' t3 f4 e9 h" U: C; b0 h7 c
            fbinfo->fix.ywrapstep            = 0;# j- _* X2 n% V
            fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */: {% F2 g1 k  ?7 g# f1 `- r5 `

    $ r2 j/ c2 t( V% R' M* n        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    1 z; B/ \6 [. c+ V, c1 K2 n        fbinfo->var.nonstd                    = 0;8 r) p9 V% x( Q
            fbinfo->var.activate            = FB_ACTIVATE_NOW;
    + i" i; G  L! _" u        fbinfo->var.accel_flags     = 0;: L; n3 F- I; f) F
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    ' s5 h7 f  K( s, G5 i  n5 m 7 {# Q2 G( c# l5 l0 T) y0 s
            /* 设置fb_ops结构体 */1 L9 Q/ x/ C6 t! N7 Y# y
            fbinfo->fbops                            = &s3c2410fb_ops;
    ; L3 n- U1 G7 _  V        
    + s, d$ F& s" R6 i# l8 m        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    & c; p+ _+ r4 ]2 B7 f; `
      A1 K4 Y3 q  r% I6 w( ^/ E        /* 设置假调色板 */
    4 q& r" b5 \: H0 k5 ^: a5 S        fbinfo->pseudo_palette      = &info->pseudo_pal;1 `: y8 i" H& T, j6 \

    9 f5 q: X% e- j  m6 [9 @        /* palette_buffer = 0x80000000,清空调色板 */8 A( e9 s, f7 P2 E7 i( C
            for (i = 0; i < 256; i++)
    2 U9 X% K# E2 }, I" u  b, I3 H! e                info->palette_buffer = PALETTE_BUFF_CLEAR;. [9 p9 N0 L+ J) P2 _( c  Q. u
    九、申请中断、获取LCD时钟,使能LCD时钟
    ! Z2 s$ \: T9 c9 E& A        /* 申请中断,s3c2410fb_irq是中断处理函数 */
    9 z- t2 C6 G) B1 O* V" W+ ]        ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    2 r5 A& |" t3 X' s5 {1 Z  S9 G$ ]1 [        if (ret) {3 V/ P7 ?& ?+ l6 ?
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);" ~. j- S* q" `$ _+ R' I! ^
                    ret = -EBUSY;
    # r  C) u) b. [7 {/ n5 V                goto release_regs;! L& c) F7 P  [. u* a* Z9 t
            }
    & q; q6 c3 m" `  a - Q4 B, v6 I9 I8 \: _
            /* 获取lcd时钟 */  w9 I) D$ d* Z
            info->clk = clk_get(NULL, "lcd");
    % I$ R" x% z- l6 P0 t+ w; v( I        if (!info->clk || IS_ERR(info->clk)) {' s, C1 j' s" E/ z6 Z+ ^0 |2 V$ p7 {
                    printk(KERN_ERR "failed to get lcd clock source\n");
    + l+ n; g) d3 W  i                ret = -ENOENT;
    : u4 ~( F1 a7 g3 t) }" J                goto release_irq;' V  E. A: f& f7 H- c* _- A) O
            }; i/ d, h6 l+ a3 l8 l% T2 \6 e
    5 B- t; H0 _  h, h) d
            /* 使能lcd时钟 */+ t* j5 I9 b0 L- Y+ M9 m
            clk_enable(info->clk);               
    / {  }$ h1 H' a! h! h        dprintk("got and enabled clock\n");
    : W$ g! ~; D9 e6 ~; ^7 f十、计算显存大小、分配显存内存8 K6 I$ P  t2 J2 s- O* D- E) |

    / F! h1 y7 d" w& j        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,2 D5 p$ H6 T$ q& Q& J7 q* `3 B
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    7 k0 g  [; A0 e/ O     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    8 N- ?$ f" d  V" O3 G  ~+ p         */' ^5 a$ G3 D- H; B4 R
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    ) F6 T) p9 c6 ^5 _! _+ l% l% U                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */! o& E( w, f+ C8 F0 v

    1 N" o9 H$ E' [& K                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    4 R- n6 l8 p. n8 c. T. Y. q                smem_len *= mach_info->displays.bpp;                                        /* bpp */1 x: F0 X6 P/ q  S" R; U( U
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */" g/ Q/ q- M0 R: t
                    if (fbinfo->fix.smem_len < smem_len)
    3 Z& u; |/ t+ M! L8 l: a- Z0 c$ ?6 [                        fbinfo->fix.smem_len = smem_len;
    % k; X* L4 a' Y. O5 h        }
    : e. o( I4 Q% k) H9 F! Z
    ' G/ G/ J  F6 ]3 j( ?        /* Initialize video memory */
    " n6 J+ z) o; ]; M        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    * j$ Q6 W2 A% T, M+ v3 y9 A9 s% O4 I        if (ret) {
    % m) j9 S# V& `2 Z4 {9 K8 Q                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    9 K3 n2 J' a* A' h' p6 k: N/ p* e7 u                ret = -ENOMEM;
    8 q3 M6 {6 }* X% r! l                goto release_clock;
    # Q# p4 t& H# V# y        }
    8 U" S3 r  I! K" D, Z- H1 ~& |十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    6 D! N: V6 _; k" ]
    - J+ O9 q6 U  X0 H- h4 n        /* display指向tq2440_lcd_cfg */
    - T  ^5 c( K# |. l1 _7 ^        fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */
    / e1 b/ H0 }3 W, T& ~        fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    , j( c0 H5 x  p  ~, T, ?: c  M# T        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */$ o/ [. g; I7 f7 M4 v( d
    十二、LCD相关寄存器的设置和fb_info的可变参数的检测
    ; y0 {9 e  U% ]6 j; B- I; o3 Y4 U
    / E* Z6 ?8 }1 i- i# E* I/* 初始化LCD相关的寄存器 */
    % X4 V) @- I  B# M        s3c2410fb_init_registers(fbinfo);
    1 U, N- W4 V% ]: ?, ?# r
    2 ?! L' o( R1 S! b2 O+ Q        /* 检查可变参数 */- m) |' n$ E2 C$ @: V5 e! v" ]
            s3c2410fb_check_var(&fbinfo->var, fbinfo);
    9 O2 c) u( R/ e7 R7 }十三、注册fb_info结构体. x5 s1 B) s: X. a( l& }: Y$ Q
    ; w& g, c6 ?7 I, \
            /* 注册fb_info结构体 */# F! @' L+ A9 Y9 {" }
            ret = register_framebuffer(fbinfo);* Z2 p2 L# l! \- o) W$ w- B/ N# E- @% a
            if (ret < 0) {$ I( |, Y" ^5 O, c+ v. q+ m* `8 L
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",4 z3 @( X2 b; `3 n& ]$ \; t
                            ret);
    % ]! _1 V  f; Q, }, \+ v4 Y! U                goto free_video_memory;
    5 G- Z5 V& t' t. X( w. p/ L* n        }; p. g; T  i$ k( x" l( b5 r
    $ c2 P1 Y; W3 e

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-7-2 20:08 , Processed in 0.109375 second(s), 23 queries , Gzip On.

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

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

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