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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    3 R- H" ]* f* y5 v8 Y% S& ~
    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    % C$ |3 c9 Q% ]+ d5 B, h
      H0 a+ C* ]# a2 i0 z& K; B1 O上一节文章:linux lcd设备驱动剖析一
    # ?0 m0 k4 X. y6 E( a$ l9 k, p( S: K# l4 z
    上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
    5 F( K, }1 I  w- }1 F+ u% p5 B1 Z/ ]  k. ~7 @: q9 Q9 R
    * X3 ~9 G6 }- B7 N5 |. u
    static int __init s3c24xxfb_probe(struct platform_device *pdev,! ^4 l" D1 J1 `" G8 R" O8 v" g
                                      enum s3c_drv_type drv_type), e% F# h' X  }+ Z
    {
    ( k2 }9 _+ p% A        struct s3c2410fb_info *info;4 I8 E9 C# c  J+ D2 w
            struct s3c2410fb_display *display;( M, @0 H# c& q4 n- t
            struct fb_info *fbinfo;9 `/ y( x* L% @4 D
            struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */" R4 C" }* ^1 q) i
            struct resource *res;, Z$ n: j' c- u2 e4 w
            int ret;' D; E& S. J& z& W, y
            int irq;
    : K* m! O4 P4 `$ q        int i;
    & \1 J; W  I! P        int size;1 h8 Y7 \  y4 q. p" h5 y2 ^' D
            u32 lcdcon1;6 X* n3 k8 }/ c! p+ V9 w6 {7 V! ]8 }

    5 ~9 m0 x" t+ l4 b" J        /*  s3c24xx_fb_set_platdata()里会设置platform_data
    - R8 S1 m' S& H0 h, C         *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);9 D5 o& M5 l) `5 D) e% `
             *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    & d9 f6 i. q! }* ^/ p( |2 ]5 y         */
    ' d+ A, r" m; Z$ g! i: n5 c        mach_info = pdev->dev.platform_data;
    0 A+ j6 L; R$ M" c
    - W/ v- O5 U& B: q1 m: D- n        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    ! p  h5 U! u* |: u" F        if (mach_info == NULL) {# C8 C* m/ a1 x  s; f
                    dev_err(&pdev->dev,+ H2 I9 O2 n. k2 z! t' Y
                            "no platform data for lcd, cannot attach\n");
    ( B' r/ r" j7 H* e5 o                return -EINVAL;                /* 表示无效的参数 */4 [. ^% t2 |0 t9 k
            }& _6 w5 |, G& o9 G

    1 [" |2 S# J  [) x: A        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    , s- J9 c7 V1 K1 N8 d        if (mach_info->default_display >= mach_info->num_displays) {% `/ N8 ~) R* _! T
                    dev_err(&pdev->dev, "default is %d but only %d displays\n",
    : L4 A: A3 t7 d+ E( }5 ?3 U                        mach_info->default_display, mach_info->num_displays);( E9 d7 m* y: a. R) M# v/ b6 w- w
                    return -EINVAL;; s! W3 H! U9 I2 s# p/ }8 C
            }
    ( k& Q* g/ h4 i" E 9 ?/ s  T: X2 G) K& F) o/ c
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */( P% c7 g2 h) \; F6 w* z0 S2 u* J
            display = mach_info->displays + mach_info->default_display;/ I& J( x2 [  d9 b

    0 i9 v7 X6 w7 ]        /* 通过平台设备platform_device获得IRQ  h2 Z# d, Q) S$ a5 J  o$ K  A
             * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)" j% {- E6 ?# p
             */. [: ?5 p6 Z- d* `  U
            irq = platform_get_irq(pdev, 0);. L0 F$ M: P- m2 O0 b  R
            if (irq < 0) {
      m) O) h: G, A7 B                dev_err(&pdev->dev, "no irq for device\n");
    2 e5 d4 W: p8 J. R5 ?: r& V) K                return -ENOENT;5 ^0 _: ?8 A3 t. ]% I+ I2 G1 t
            }, C; r7 u* V# A
    3 [! L( j9 s4 ]- M! U, t
            /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间1 @( y+ I8 }; x2 h/ T
             *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据! W. t. @' p7 p/ C& U' R, Q  E
             *  比如:clk,resource,io,irq_base,drv_type等额外信息
    ' }& X- v( @+ O; ?* q         */
    0 h, y1 n* ^. B        fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);4 S0 {$ z  V$ m7 L" V& F, ?
            if (!fbinfo)+ c; z( q# J! H( a
                    return -ENOMEM;                /* 返回NULL表示失败 */
      g- a' D  A/ g' s' s- D 7 S& R+ Z  b2 ?+ `9 c8 I, S: ?
            /* 相当于pdev->dev->driver_data = fbinfo */
    1 d5 S. r4 z* C5 n& [        platform_set_drvdata(pdev, fbinfo);0 W$ U2 T! ?3 o* j. a
    . @5 Q# f& o2 k0 ~' K0 [( n3 h
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    & y' l2 c( r' P. F3 _' U0 R) M: ^6 B        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */0 F' v) t% J* y+ c- H
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    : A4 d9 W) w: i) ~        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    0 n% Y6 R) J' Q% r 7 g  X% }" ~4 N' f, ^
            /*  通过平台设备platform_device获得资源(IO) */1 @! F, g# G$ N( u% ]
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);+ ~8 g& Y& f+ |. J1 B, J2 {
            if (res == NULL) {
    ) w- f" {4 _2 I" ?& F, u                dev_err(&pdev->dev, "failed to get memory registers\n");1 n# f0 ]$ x# F+ v1 M! K' q* m
                    ret = -ENXIO;! k! q8 J6 T$ s' \
                    goto dealloc_fb;
    6 M9 h9 A' k8 b/ L, y        }% _0 m5 Q3 o; d$ ]

    8 [  n0 S8 L# f! U! D. u        size = (res->end - res->start) + 1;                /* 资源的大小 */
    5 Q  ]; t8 {4 ~ 5 c7 I6 G/ a9 W5 i& W/ O1 A
            /* 申请以res->start地址开始大小为size的I/O内存 */8 o1 W+ d1 `$ L+ u
            info->mem = request_mem_region(res->start, size, pdev->name);* I, w: i6 N1 v$ G: X! ]
            if (info->mem == NULL) {
    ' P; N; w( n( W  r                dev_err(&pdev->dev, "failed to get memory region\n");
      [0 q+ _: M  C% ]) y. g& {- U# r                ret = -ENOENT;
    / g- }1 r: ?  Q/ ]                goto dealloc_fb;
    4 u: [: ?6 |/ ]6 l+ j# X& b! L4 z        }
    5 {1 y' ]# F% T . k! |' d9 ?1 p5 G- I  s
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    6 H# p7 m, ]; \- b( r) ]' r        info->io = ioremap(res->start, size);! f# N5 I. ]& `" U2 P
            if (info->io == NULL) {2 v3 w# G$ S( S2 ]/ A% I8 r
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");
    , m% C" l2 T6 b7 r" e4 A                ret = -ENXIO;6 J2 Y9 ]# F. g' T" H+ x; |. l3 F6 T
                    goto release_mem;6 W) q- I; K; i; W+ C
            }
    * t* Z! k1 x6 ~0 z; {) A* j& P * a& ]+ @1 ?( @) c$ c- U
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */: ]- j. `. U1 v
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  J' T6 }5 p$ O* _8 E" c, W

    2 R; x: L( v/ A8 @8 [        dprintk("devinit\n");( u$ F0 L: g+ U9 g: ?% E
    # i$ m7 J3 A' e. @- ], `
            /* 驱动名,fbinfo->fix.id = s3c2410fb */
    9 C+ _  L3 L2 f$ P7 l$ z0 n; ?        strcpy(fbinfo->fix.id, driver_name);        
    - o: K' l6 I3 U5 v7 Z/ N# z
    7 g  O2 x; ~, D0 v9 B        /* Stop the video */
    4 q" W7 q0 B; x/ m4 p: K        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    ; h2 W# r: s, e        /* 禁止Video output */
    . A, O1 q6 n% w; F* ~2 \4 B        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    % W9 k* R3 u: O9 I) N8 @ 4 E( Z/ n' R2 x1 a" e6 a* z
            /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    - b6 g, ]$ x- ?1 O. m- Z# L        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    . x& d, Q. ~6 J4 w- |% I, j3 c        fbinfo->fix.type_aux            = 0;, o) {5 F! K1 g5 ~
            fbinfo->fix.xpanstep            = 0;
    6 y" X  Q5 h  f        fbinfo->fix.ypanstep            = 0;( w: d, `6 m8 U4 h) d9 d8 u
            fbinfo->fix.ywrapstep            = 0;
    1 i3 A' ^" h4 G& T/ f        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */) M! M+ A/ O2 M! {5 U* `
    + m+ a% `0 R; D- z9 E
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */5 Q9 S6 i' i6 Q& K0 y+ q" x( Q+ [) r
            fbinfo->var.nonstd                    = 0;
    / e' ^3 k" ^. P        fbinfo->var.activate            = FB_ACTIVATE_NOW;
    + ~1 C0 Z( A, t% T        fbinfo->var.accel_flags     = 0;
    * r6 L2 u( u5 n& X/ v. ~' H1 l        fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    , ~9 U* j% H- A 0 p% a. q- `: v" W
            /* 设置fb_ops结构体 */
    4 Z! {4 |0 N3 u! d7 |6 B6 T        fbinfo->fbops                            = &s3c2410fb_ops;
    & t" X% c: W' ^2 C) c        
    ; L. ]8 M" Q% t1 ?1 G* R4 \        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    - x! s) W! C& U' e 5 O& U: [8 j9 _" A4 W9 C
            /* 设置假调色板 *// r' j9 p1 b' S' g7 G' c: b
            fbinfo->pseudo_palette      = &info->pseudo_pal;3 Y3 @! r- ^) z, y8 J+ [
    * i; h  g& b" v  G" }( f" x
            /* palette_buffer = 0x80000000,清空调色板 */
    4 y, K/ X; p' ^0 z7 s; X        for (i = 0; i < 256; i++)+ O" N. [' f! z0 u! X3 z
                    info->palette_buffer = PALETTE_BUFF_CLEAR;# ~2 x: o, [  j5 x2 T1 h: w

    ' i/ _0 P0 C& P( l        /* 申请中断,s3c2410fb_irq是中断处理函数 */- \, t+ ]6 J! n& j
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    8 h  j9 h1 S# [  [' r$ H4 x& T        if (ret) {7 N: y7 @: E' {& a6 \2 g. i
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
    ' {- v. i, h3 Q8 L0 [" G( h" d                ret = -EBUSY;
      _5 T! ?. V$ w# j% Q                goto release_regs;
    3 ~/ C: _1 h1 u, L" z8 ~, J        }- s5 ~4 ?& P( y- s$ C2 \& g% N+ K
    3 g8 s4 ?. ~% d" O
            /* 获取lcd时钟 */
    : T  H) \) A  b: f        info->clk = clk_get(NULL, "lcd");
    2 D0 y* ^+ x; w: {        if (!info->clk || IS_ERR(info->clk)) {
    5 b6 L0 l; E2 M2 S                printk(KERN_ERR "failed to get lcd clock source\n");
    % h  M  y. f1 f) z/ S# v                ret = -ENOENT;
    $ T9 P  r( L/ }! R" ]6 U                goto release_irq;
    ' a- s% f% c$ V        }  Z5 r1 W; U  g

    ' ~6 P& S" b; j0 M& ]  s        /* 使能lcd时钟 *// M' m# I1 i  l$ U9 [2 w* k
            clk_enable(info->clk);               
    8 ]; L3 ~5 Z# z, ~        dprintk("got and enabled clock\n");
    . m/ B+ J. i8 L/ F/ g; n' p2 j$ t
    3 ~" G& G* I" [/ o* B5 o        msleep(1);
    ) q3 b, Y- e/ t' S / \- [' ~& d- @$ G1 Q
            /* find maximum required memory size for display */
    0 `" A" D# U8 {2 i1 s$ u 4 T6 v1 V6 W( m) u: `
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,5 w( o4 |3 \  f% ^
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。1 C& L) @7 P# s4 l5 v
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。7 c' S7 I3 t& g" j5 v
             */9 q* l# a2 ]- U, I6 \4 P
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    / h# m/ x- d3 {0 s' ~9 T                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    0 l4 v: D8 w5 r5 j9 [, B
    + O9 I3 V$ L8 ~  h9 L, ^# R9 Y                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    * e/ I1 O' M8 z! x) O! [                smem_len *= mach_info->displays.bpp;                                        /* bpp */& {2 l6 r& f( w& z7 m
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */
    & M) n& V# v" {                if (fbinfo->fix.smem_len < smem_len); T7 e8 `$ j0 Q* n) a$ Q
                            fbinfo->fix.smem_len = smem_len;
    0 i  l6 p; `( s7 x: x# [: J. a        }1 U( W4 @# i+ K" [; Z

    + ?  e4 ^2 _& u        /* Initialize video memory */6 N7 c( K* A6 P4 N$ ]
            ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */2 h* W0 K- J; n1 j
            if (ret) {/ l; z) O1 L& E) Y( M4 U' Q) t
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);, m% i8 W  B+ @$ s" p1 ~
                    ret = -ENOMEM;
    $ Z, W( j6 w8 e& [4 M  f" a                goto release_clock;
    & G( a, D( w- y+ {$ d1 Q. M        }
    ; R, \& y  q. @/ @8 ~' f) c  U - o% L! D/ T9 q' O
            dprintk("got video memory\n");
    " i* V0 V5 n  q; P0 Z* F# o' {, B $ V8 _, G$ X" d) j* m1 N7 M
            /* display指向tq2440_lcd_cfg */, A. Q' `  a9 I" ?
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */0 p5 _6 Y! I. H$ X/ u
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */# V+ D- l* T0 {/ @. L# e
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */! ~- v! m& x. C& h

    0 O7 j9 o3 ]' ~6 u# R' a! ?2 V' L        /* 初始化LCD相关的寄存器 */1 d5 |+ l% c( M5 ^7 u& H' j
            s3c2410fb_init_registers(fbinfo);
    2 n  |3 J5 g0 K- X. s
    5 g* w) _- I; V1 s6 ~2 S        /* 检查可变参数 */) K8 z) F& A% ^
            s3c2410fb_check_var(&fbinfo->var, fbinfo);+ b4 Q& l: q+ D4 q" t
    5 o3 O: l/ t  ~
            /* 注册fb_info结构体 */
    6 |4 u3 o4 S, D/ c  l! ^        ret = register_framebuffer(fbinfo);7 |0 r" @: D) |2 h; |6 Y$ v) ]
            if (ret < 0) {1 H4 A: F- I' K/ v9 [" L) x7 y
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    + F* t6 N1 E9 Y- ?, C. Q% Z6 B# K  j                        ret);
    % o3 l7 N" o: P1 W5 U+ y                goto free_video_memory;
    2 G" p4 }8 ]. b        }
    1 n* g0 k, J# }  t% d7 Q+ n
    0 Z& w- G0 E* n3 i; l: u( f        /* create device files */5 Y  L- c+ Q; m6 t2 k8 e% x
            ret = device_create_file(&pdev->dev, &dev_attr_debug);) {# A9 h8 E  L* J& G$ b0 d' v
            if (ret) {* v. q# r9 B9 Y* j9 O6 o
                    printk(KERN_ERR "failed to add debug attribute\n");% @0 o7 k, {2 i" x/ d5 D
            }
    ( p1 u- R8 Z6 b; U* S  @% b3 u 0 @: R$ N" k3 ^9 j1 |2 i
    8 n6 y/ Y" z$ w! Y$ Z
            /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */0 _( d5 G, e% D  F1 m# P
            printk(KERN_INFO "fb%d: %s frame buffer device\n",
    " ?8 l  x6 u1 t) l; y                fbinfo->node, fbinfo->fix.id);7 L0 Z2 N" u3 ]8 h; t
    0 Q$ w9 P* N* B
            return 0;
    $ z. f. ^8 ?1 o/ ^% t+ U3 R % u2 h* _; `9 g5 z0 L1 o' P" e
    free_video_memory:
    - ?; m. E) S! q% e        s3c2410fb_unmap_video_memory(fbinfo);
    7 a* t: r3 P5 }/ g4 |- Zrelease_clock:
    " T: v: F$ L5 W: p( D0 ?4 e7 J# e        clk_disable(info->clk);                                /* 禁止lcd时钟 */3 H, }. a! `) F4 F% i! S/ |# ?
            clk_put(info->clk);                                        /* 删除lcd时钟 */1 e# v$ m) S# ^. f
    release_irq:" L1 ^' S* Y/ r" L8 Z
            free_irq(irq, info);                                /* 释放IRQ */
    * Z1 `8 [6 \; [) S9 M  drelease_regs:
    / Z( D: i/ S# a  h0 ]6 N7 \* K0 e7 Z0 H        iounmap(info->io);                                        /* 解除映射 */
    0 k$ Y" ^$ H& D- J; [7 ?/ arelease_mem:3 Q! `2 q% d! x. g- |" J% ?$ e( I" b& a
            release_resource(info->mem);                /* 释放资源 */. Q' k3 L4 ]4 H9 h7 `
            kfree(info->mem);                                        /* 释放刚申请的内存 */
    8 g( p3 T3 r5 l3 L2 F$ T! Idealloc_fb:
    1 Q" X# e, Q9 O# |0 |- l+ }5 Q        platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */
    3 [! m* E- p* H' K& A# R4 M0 e        framebuffer_release(fbinfo);                /* 释放fb_info结构体 */
    3 T% a  m# O3 ^$ ^2 k8 ^# w" g        return ret;8 s2 y& g& ]0 ]" |. ^
    }: `+ g6 M/ {) X( e/ L$ |
    拆分详解:
    5 x1 [( ^' u& I1 Z0 u一、获得平台数据( A( P: B! N$ L2 V

    0 ^2 j, J, T) P8 V1 C" x6 T2 S8 A) E4 t& O
            mach_info = pdev->dev.platform_data;3 j: B$ d( K0 d% h" }
    * Q0 a: Y+ {5 U' y
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */& y+ j" g: Y# s# B* r
            if (mach_info == NULL) {* _( J" ^2 ^2 b- r0 e
                    dev_err(&pdev->dev,# `, q, X$ O' ^$ D1 Y
                            "no platform data for lcd, cannot attach\n");
    # C; G/ E7 i7 a5 u( z9 X2 f                return -EINVAL;                /* 表示无效的参数 */
    3 a2 F9 D, Z& ~        }
    6 ^: ^3 K0 |. W! X. Vs3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    4 |6 G# L4 p* q6 y9 P所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
    7 k7 [# p& [. `$ Y; T
    ! x1 V9 [* a/ Q$ Fstatic void __init tq2440_machine_init(void)! l" Q' h. m) l3 m' W
    {( V# r- b$ y: g; h7 _
            /* 初始化tq2440_fb_info实体结构体 */: G' B& u# U; e. U+ B% T8 r. X
            s3c24xx_fb_set_platdata(&tq2440_fb_info);
    , D$ u! k3 A1 k0 }! `7 n4 n        s3c_i2c0_set_platdata(NULL);$ K  ?$ c7 n! s- a9 U

    + T* o! A7 N. t4 [9 K0 `  n) M        /* 添加tq2440_devices到内核,它是platform_device类的设备 */8 W: B% K- P) ^" f8 C" ?# w' u9 B
            platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));: I6 C8 I) h3 H" |: G1 m4 S9 i
            EmbedSky_machine_init();- G* |- {5 H: I, t/ r
            s3c2410_gpio_setpin(S3C2410_GPG12, 0);" ^. h( y3 n0 L$ q
            s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);# V  m6 @6 I2 W' ]+ H3 b
            s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);) U7 o  u/ `( E0 J8 c. s
    }
    & B- V; G7 @7 c/ F- C2 Ps3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
    ; B# z0 w/ D& `- L  h& Y: H0 Y  w4 g, x. Z* I
    void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
    7 W: j- C, X1 h) K{6 O0 p3 V: X( i( X. c5 _9 l9 m
            struct s3c2410fb_mach_info *npd;
    $ O7 ^* o. U/ \, c 2 j: A: M" }% ~5 U! S
            /* 申请分配s3c2410fb_mach_info大小的内存 */1 e- s: F4 p- }6 b9 y" U  M3 e6 R
            npd = kmalloc(sizeof(*npd), GFP_KERNEL);
    7 l% V) ]. F+ O% t+ y        if (npd) {
    ' f, [; Y! g$ o" t& E* ~                /* 拷贝s3c2410fb_mach_info型实体给npd */
    : i2 X( y! E" M9 b. c( ?$ @                mEMCpy(npd, pd, sizeof(*npd));
    ( d. n0 \: G* m. P9 q/ F ) _9 k+ H1 u9 L( c  L
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */0 ~3 N* @; ^2 l0 O% E7 f2 p
                    s3c_device_lcd.dev.platform_data = npd;
    . a. X  q0 W2 `( K4 U; ~! g' X+ h% O        } else {8 C. a8 k3 @1 D0 e: B8 }
                    printk(KERN_ERR "no memory for LCD platform data\n");
    0 s! A2 b& _" _5 p        }
    0 e9 B+ O- H# k}
    ( V6 [' m# {% R4 e: B0 D二、设置s3c2410fb_display指向tq2440_lcd_cfg  e0 E& V& V! h$ m$ l: W

    2 g4 r, t5 `  H9 h0 a8 {& I6 e        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */" k+ n5 l8 I1 T2 _( |: }) H* ~9 J
            if (mach_info->default_display >= mach_info->num_displays) {
    2 h( l8 {, Y1 K% Z# M  G                dev_err(&pdev->dev, "default is %d but only %d displays\n",4 ^4 h; Q2 T% P/ [, g
                            mach_info->default_display, mach_info->num_displays);( h9 w& _) A4 \
                    return -EINVAL;: U2 O- d8 T) E) n, E0 m
            }; x' i; Q5 Z% T

    ! W+ R7 {2 w# [3 g        /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */2 o& B( i) c% ?/ s# N) m, d' D
            display = mach_info->displays + mach_info->default_display;
    3 V  k6 H- R% f- U+ w( G3 T) O三、获得IRQ资源
    ( w5 a0 ]6 `( [3 X: m% [6 f; |8 R4 y2 z, B/ }
            irq = platform_get_irq(pdev, 0);4 Q& k2 c) b: g9 y
            if (irq < 0) {
    5 ^8 {. n1 H& [) G% F                dev_err(&pdev->dev, "no irq for device\n");
    : F* m1 O( t) g                return -ENOENT;
    7 F% L3 m9 }6 |        }) o" o# @/ y, k. j5 X* E. \
    四、分配fb_info内存
    : h$ n- Y( A  {; ?+ Y( Gfbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);( D1 \% |9 ?) p/ E
            if (!fbinfo)/ F+ r% L- l/ q, `) [% s- i1 l
                    return -ENOMEM;                /* 返回NULL表示失败 */
    - c, k; c7 H: w# T# Yframebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。0 E; p8 [( ~( Y$ M+ V
    五、设置s3c2410fb_info结构体
    1 N8 {: B5 o0 e: k$ Y  `4 y' z# p& W+ {3 W1 P. |; q
    /* 相当于pdev->dev->driver_data = fbinfo */( a3 a& H7 ]3 }! K9 ~
            platform_set_drvdata(pdev, fbinfo);
    ! h+ N( c9 e3 H7 D 9 y( |6 C0 |" M0 R1 _' o7 X2 j
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */5 l: r# U0 E3 d7 _" B, L) k0 W- ~" M
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    $ _6 b' K  p" ?6 C: ^        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */( C0 s( C9 P$ j: G' ]
            info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */( n  e0 ]) Q0 \) X& q' p
    六、获取IO资源,映射IO
    5 m. a; z5 p. }; J- k
    : ^* f  `; b. |7 _- h( E/*  通过平台设备platform_device获得资源(IO) */
    " \7 r5 a& s3 N( V: D" P) d. N5 n        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);6 l2 E/ j5 C0 V0 ?* U' ^8 C: C
            if (res == NULL) {, ]" p# |+ @$ ~+ Z4 b
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    % d; w& I: N% ]' A  W7 a" h                ret = -ENXIO;( q+ _9 K) U# c, y" ^
                    goto dealloc_fb;
    0 @1 j) ]  }' D        }
    9 A! {  i" Z% D: Q, F
    + E' ^  D7 a3 [6 r: H$ a        size = (res->end - res->start) + 1;                /* 资源的大小 */
    ! l  |$ R. I& S  Z
    8 k- R2 Z$ _! Q        /* 申请以res->start地址开始大小为size的I/O内存 */5 V5 a1 Y- Z5 n0 t
            info->mem = request_mem_region(res->start, size, pdev->name);7 \* P$ F0 _, y6 f
            if (info->mem == NULL) {
    4 a' B  \% b- F/ o  D* H' C                dev_err(&pdev->dev, "failed to get memory region\n");+ u8 ]' K4 p. d# c/ P+ g+ h2 c
                    ret = -ENOENT;
    $ Q- w! p# ~' }                goto dealloc_fb;
    4 S) b9 Y& B% E' t, G5 Y! P        }8 P% e2 w0 j% b6 h) ?1 {( p

    + I; t3 B' Y. t' g) O8 J4 D+ C        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */- R+ t: s  O  Q! f& g
            info->io = ioremap(res->start, size);# V0 L" b8 i1 E; |# \% k. R2 [
            if (info->io == NULL) {  M$ }& v" T. @4 u5 ~# i- p7 n
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");
    + H; e. @5 [1 J3 _5 t                ret = -ENXIO;
    ) A5 P& y9 H- q7 j- D                goto release_mem;
    2 o% z$ U" H* _: u        }
    ; b7 t3 ^/ s, o: R7 }; r5 a0 _
    & ^1 t0 X/ g  p. u0 G% b/ G3 ^# x        /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    ) Z  g- P# ~5 m1 Q" a& M3 M/ P        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  ]- m. {  Q' l0 H& y+ N
    七、读写LCDCON1,禁止视频数据输出  Y0 S5 F. u6 i( k* ~# }( g2 R$ T; X
    , p% A) z9 D5 W7 N
            /* Stop the video */
    . }1 x/ s, R! }: v( B        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    & C& ^& X6 L( G* B% r8 ^1 O        /* 禁止Video output */
    ; s- E! B1 ]) \+ Y! R        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);- N% u8 S/ L' f0 j) o- p
    八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    , g+ |, H" N' d- K$ w4 @9 l- [; N) Q' f! v/ ^! ?
    /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */4 b. ]" J% J8 b& M' A
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    * i5 n# m: j0 u/ o        fbinfo->fix.type_aux            = 0;5 ?" v  z7 a# v6 j
            fbinfo->fix.xpanstep            = 0;0 _1 B" W. `0 O' }/ T# q9 s
            fbinfo->fix.ypanstep            = 0;
    1 C7 |/ X0 m8 x( D0 U% ^( j  j        fbinfo->fix.ywrapstep            = 0;
    ! W6 \* W- Y- A* B        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    & i$ b2 }/ E, z6 J+ `9 A6 b 6 p. u7 p. H* Q# R. t
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    . k8 k/ [* `& x6 T& W! u# B5 }        fbinfo->var.nonstd                    = 0;
    : R( X6 u6 G6 y, C        fbinfo->var.activate            = FB_ACTIVATE_NOW;
    1 ~8 a4 V  \7 @" @; o( L        fbinfo->var.accel_flags     = 0;. @& n' @3 B, b: Q
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    0 ]3 U& ~3 `5 c3 y% r5 o: X& y
    5 F' Y& z8 m1 Y        /* 设置fb_ops结构体 */5 v% p7 W6 G. H# f, [' M  d$ s- d
            fbinfo->fbops                            = &s3c2410fb_ops;4 {/ I4 v9 }+ T8 e- ]& V5 H6 L
            
    3 e5 X6 ]2 u  b( a        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    0 q% ]1 ]* f5 S0 ^3 c 8 i: n8 ]" ?: D
            /* 设置假调色板 */* X1 Y" f7 P9 A; G" X. R+ \- l
            fbinfo->pseudo_palette      = &info->pseudo_pal;
    1 U8 Z) c' N  [( C- @0 v ' j! p! Q- G% H' A  _5 ]8 \& L& q
            /* palette_buffer = 0x80000000,清空调色板 */
    " P- [1 C/ S+ l  D4 Y/ N        for (i = 0; i < 256; i++)
    3 B" P" h- D  W0 x2 c* L                info->palette_buffer = PALETTE_BUFF_CLEAR;
    5 ^* c) ~1 [/ t九、申请中断、获取LCD时钟,使能LCD时钟
    1 s' o' }' h( P' L8 A7 O        /* 申请中断,s3c2410fb_irq是中断处理函数 */4 d1 m0 B. ?0 F5 e
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    3 e% R: L: e/ ]; }4 J1 F        if (ret) {
    ( F' ^" N$ U; D) q+ t' I                dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
    3 x" a; M8 f4 w/ c. i$ {# R  l6 U                ret = -EBUSY;. P1 O, a' J5 U7 Y
                    goto release_regs;+ J6 a' |2 }- x" }" B
            }
    ! t: @& z8 i  @* \; f # P" f0 H: R7 @( [* X
            /* 获取lcd时钟 */
    # g9 F; ?7 N% z) r- e4 L5 r! Y        info->clk = clk_get(NULL, "lcd");: ?2 H* I: ?1 t, z8 {& V/ J
            if (!info->clk || IS_ERR(info->clk)) {8 L6 G; j/ }' a) a
                    printk(KERN_ERR "failed to get lcd clock source\n");) i5 N  O6 m7 D0 {
                    ret = -ENOENT;8 A  q& n/ w; H3 f% E4 s5 U' `8 W
                    goto release_irq;
    , O( A) v/ l) W4 G8 t1 d) I, x        }! t: e6 x' s0 y+ S5 o" K/ R/ U
    ( S) v6 ^# _% w; S1 y& u8 W
            /* 使能lcd时钟 */
    + e6 T7 K" w1 ?/ g+ p6 d0 _/ e" E2 g        clk_enable(info->clk);               
    ' ~  J+ l5 A$ I0 U3 H        dprintk("got and enabled clock\n");2 p& \; U. x5 r* ^. {9 m' B3 ~
    十、计算显存大小、分配显存内存
    . G' ~! Y9 z6 Y! ^- T6 D
    2 b% v/ L7 g) b8 c6 x7 D        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    % ^* w* R9 ^/ |     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。) W0 \) X: W( v3 W" Q
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。2 o% L# b, e3 [6 z: ~) m5 u9 O
             */
    : Q+ z7 U. R8 ^) w* m        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */& E8 \" ~% S8 f2 ~' c
                    unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    ( P" ?! ]" O. A3 t0 j
    * Y8 I/ W5 w) j2 v) i1 X                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    ! k! [& s3 }* J* N8 ?$ e                smem_len *= mach_info->displays.bpp;                                        /* bpp */
    & @! U- ~2 _1 l! ?1 v" s                smem_len >>= 3;                                                                                        /* smem_len除以8 */1 w! l6 A# ]/ X+ p
                    if (fbinfo->fix.smem_len < smem_len)
    ( u) ]2 Z2 A4 M3 p! K9 y                        fbinfo->fix.smem_len = smem_len;
    . S2 V/ t- l  W8 G  O8 O3 {        }
    $ k2 }3 b$ [- U ; u6 n3 a& v& Y, y+ V, H
            /* Initialize video memory */3 k. J. u, q# [$ w4 t& d* }
            ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */; W/ g' u% a+ V! {$ c* y
            if (ret) {
    8 G/ V- V( x  P% O& T2 M* R7 @                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    8 m& k' G0 B7 F4 N: i% c# p                ret = -ENOMEM;
    9 v0 n9 m2 `# g2 v; C+ }                goto release_clock;
    - {0 M) {" y6 p0 O6 @/ N# Z        }
    3 ?0 K) k" _5 r; b* S# U5 A* g十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    * o  I9 K6 w! W1 h* O1 e* E0 A
    & b3 f1 v1 H! t4 `& x        /* display指向tq2440_lcd_cfg */& T1 b/ ^' v* ]: D5 l& h" z
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */1 _3 d$ z. x4 P7 f9 a0 K0 Y
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */) i) h* T1 K! j
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    , _* l' o6 }7 _9 R% @十二、LCD相关寄存器的设置和fb_info的可变参数的检测
    * f9 r0 q* q# D' T* B4 n) T! U
    / X0 P) h' |- l: [. A* X/* 初始化LCD相关的寄存器 */
    & k- m+ w- [: a" p% J        s3c2410fb_init_registers(fbinfo);6 C* o$ w& \* `

    5 C2 }0 F3 f* u4 x+ ^* v1 }        /* 检查可变参数 */
    5 S- }( z8 j8 i6 a- v" f        s3c2410fb_check_var(&fbinfo->var, fbinfo);) k# j5 Q5 f' k3 }# q( \+ {6 a: f: `
    十三、注册fb_info结构体
    - X) ]; E! e- V1 e5 j9 @9 n. m- i2 l; J5 y8 z. C3 c
            /* 注册fb_info结构体 */
    ! a. d; \9 I/ _' S1 @  [: s6 b        ret = register_framebuffer(fbinfo);
    # o5 v: s2 \! m! g( e        if (ret < 0) {% ^$ C: X- W: t* h1 n! ^
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",$ H" N0 ~5 M/ Y3 I! C% e9 y
                            ret);0 O% G& k/ w1 [+ L5 T5 t
                    goto free_video_memory;) _2 {7 G% G3 H' L1 e
            }  U5 ?! m1 I' x" ]( g

    ; b0 q2 o8 K0 |* n

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-25 21:37 , Processed in 0.234375 second(s), 23 queries , Gzip On.

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

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

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