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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    ) e' g/ M9 m+ L) k4 T* Q! V0 D6 U' ^上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。  i& Y/ |3 v0 k1 U" r

    : ~$ z# [- F) k4 _7 v( W上一节文章:linux lcd设备驱动剖析一
    : a) Z7 m1 f4 {& i2 K8 Y# E1 r& I; s; N
    上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:8 j7 K- q6 k: S  g" B, x/ Q$ n
    7 e- a) }, \$ y
    & _6 k$ A6 Z( x# _
    static int __init s3c24xxfb_probe(struct platform_device *pdev,6 }5 O9 q! T) v* q  p
                                      enum s3c_drv_type drv_type)$ s' K4 i! r( h- _
    {4 H0 S# F& H9 v, I) U! r5 L
            struct s3c2410fb_info *info;
    2 P. a% ~3 S3 d* R+ j+ j! q4 z! o        struct s3c2410fb_display *display;: z' `7 E" O  C$ D8 h
            struct fb_info *fbinfo;
    ; I; K, g8 |( q, Y, M, g        struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */
    1 ?) z! ^1 I/ D$ s. M        struct resource *res;7 f' a. k1 g* w& \
            int ret;
    7 z$ k* l$ M8 M/ |' C4 ]/ S6 ]        int irq;& U2 @, U% _2 f' `* R' s
            int i;
    5 q! q( t+ V4 s' N+ I" B: r) H        int size;. W! K8 J1 ?3 i7 r: M' f0 ]8 T% ]0 M" O
            u32 lcdcon1;2 }/ j' H) `$ j; @

    , D; L4 }6 w/ t8 d: v# T        /*  s3c24xx_fb_set_platdata()里会设置platform_data7 c+ F! q1 z- n! K
             *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);* z' e. `/ U; l$ A
             *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    * x+ q5 s. }8 O9 |  }3 d. `/ {         */" t! g! w/ W/ G
            mach_info = pdev->dev.platform_data;8 d. F! Z9 t; Y9 l" K& }' c  X
      X8 K" _/ F( Q6 j6 b3 M
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    * z+ F. _* |& S- k5 j        if (mach_info == NULL) {2 n/ f* x1 Y$ V+ \( N
                    dev_err(&pdev->dev,3 ?% O/ T$ E" u; {& _# [
                            "no platform data for lcd, cannot attach\n");, G# w) w$ U" W/ W& V, ~
                    return -EINVAL;                /* 表示无效的参数 */
    5 K3 N, n" K! r  V' l# v4 A        }& O% ^9 |$ m# J' M. C4 U6 W
    " x) ~- `  R) r
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    , E" L2 A* ], e* T7 J7 e        if (mach_info->default_display >= mach_info->num_displays) {
    3 a7 {0 r- A& o( T! ^                dev_err(&pdev->dev, "default is %d but only %d displays\n",% k* y2 b8 g0 B& l( v; h. b
                            mach_info->default_display, mach_info->num_displays);
    , W5 o+ N( `+ s  p8 S) M                return -EINVAL;/ x$ b! p  s# Y6 m8 |8 L3 `
            }2 K2 y6 l& c) Z  ~
    8 T! M5 h5 c2 Z3 j+ }
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    4 F" W: I. U7 A& L- w        display = mach_info->displays + mach_info->default_display;
    ' o5 r/ Z: d6 ~5 Q6 { + F8 \7 M+ l4 }* ]. _* O. P
            /* 通过平台设备platform_device获得IRQ$ @8 b/ |3 b/ Y, U/ C
             * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)8 {+ D! \  C! Q: n7 J  ?6 q& L& r
             */
    ; ]0 @$ c) q; k  z, f. C3 r        irq = platform_get_irq(pdev, 0);
    , _& Y$ I$ \" O- f: u; O        if (irq < 0) {
    / x) N$ e6 X: W                dev_err(&pdev->dev, "no irq for device\n");
    ) R  r! `. B4 ~; u5 S" [                return -ENOENT;
    : |! C( n+ P5 o% S6 T4 T        }
      B* p: k2 G  d( ~ 2 k) T. o1 |5 I
            /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间
    % [& U2 v4 {  m1 Y- j         *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
    0 B5 C5 F+ E% w5 _% H) M" F# |         *  比如:clk,resource,io,irq_base,drv_type等额外信息! X) X% W# I, V) R2 f/ K
             */' Z4 X3 Y! K1 d, r
            fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    . U1 e+ s" P5 G; I1 w) s        if (!fbinfo)
    7 d" X6 Q0 N+ S$ o$ e9 ~+ K6 @                return -ENOMEM;                /* 返回NULL表示失败 *// X0 E  n+ W  B+ f. s

    6 s" S- z# }+ j        /* 相当于pdev->dev->driver_data = fbinfo */
    - s  ]- A' [' f! `        platform_set_drvdata(pdev, fbinfo);
    , Q3 d( R$ g" k) R. F$ B6 ?! l% G* C
    % t% O. M+ ^, I        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */: F! W1 o8 {8 X
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */5 n0 ~, m: b; {9 R3 B) I8 r
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    * I& D! |/ N, R$ O% x        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    ) d5 }5 S. Y) V" h4 x& g, r
    & J" p6 G# s( y        /*  通过平台设备platform_device获得资源(IO) */
    - y4 A& }; O9 L& v* n( M# N        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ! ~0 X, g  s0 n% t# i, @        if (res == NULL) {
    $ Y& U1 q  B# y8 n( l                dev_err(&pdev->dev, "failed to get memory registers\n");" \4 L; V$ A0 k2 k% j! \: A
                    ret = -ENXIO;" ?! P& \6 w% V
                    goto dealloc_fb;7 A: j! i# }7 A) J( |, L( ~- D9 x1 M
            }
    0 l9 O% A2 F5 P' s# L( o % h, K$ M- {3 \
            size = (res->end - res->start) + 1;                /* 资源的大小 *// r, t% b6 Z: ?) M7 Z$ b- G
    ! j5 c, ], [- P) [" q/ e- k+ @
            /* 申请以res->start地址开始大小为size的I/O内存 */
    . S9 |9 q, {( \# u        info->mem = request_mem_region(res->start, size, pdev->name);5 {6 g- B+ b1 m/ x( F# o/ Q' q6 y
            if (info->mem == NULL) {" F" d1 z1 w/ G7 }0 o4 ^7 S( n" i( b/ ~
                    dev_err(&pdev->dev, "failed to get memory region\n");
    ) y2 v0 j( h: r, V' S                ret = -ENOENT;
    6 ]  R( n" W. w# K7 ~* {8 G                goto dealloc_fb;/ f# O, p. L, d+ j* X
            }8 C1 i7 [1 g0 T/ q/ M, S! a
    ! E& u& N0 m7 h& h3 _4 j8 Y" f
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    # A# u. }8 r- j        info->io = ioremap(res->start, size);
    / w; n! ]1 ^1 B4 X3 J6 K, D8 }6 I        if (info->io == NULL) {; Q8 F5 L' d0 j# ?
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");3 w/ d- c5 S, I9 y
                    ret = -ENXIO;* ]9 D. z2 h& b$ j8 s
                    goto release_mem;4 H1 r1 n0 U5 }4 Q+ q0 j
            }
    & R# U9 n/ C, l4 L% p 6 x8 v1 }: x; B# O' K. X5 ]' p
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */* Y/ D. S! F" c- ^
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    - W6 I& ?% x1 ~ - ?3 N- x: R# _# _0 J4 n3 l1 G. n+ k
            dprintk("devinit\n");
    8 x3 E6 f! j: a  S$ X4 e ( e. V4 {3 N8 o- s; E* s
            /* 驱动名,fbinfo->fix.id = s3c2410fb */5 e4 g: L  M. u/ [& R2 f& n) I) g8 I' N
            strcpy(fbinfo->fix.id, driver_name);        
    5 e; q% L/ l4 J& P. q9 t 0 U- m( M" G5 ~
            /* Stop the video */) ]9 [) l3 m9 u
            lcdcon1 = readl(info->io + S3C2410_LCDCON1);0 [! ]# g$ T$ U) {
            /* 禁止Video output */# m8 C' z8 N; R, l6 j; L
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);! B( y  R: s1 M. n9 c# P9 H
    1 f$ J# j1 T$ [9 \
            /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    ( Z0 G( A% i7 K1 g# f        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;# @" D+ Z2 r, L$ f6 K) s
            fbinfo->fix.type_aux            = 0;
    * A4 @" w9 S' A% a1 B7 `        fbinfo->fix.xpanstep            = 0;
    ( J0 Z0 Q: o* s( w( p        fbinfo->fix.ypanstep            = 0;
    ) e" o9 L9 m5 n5 n        fbinfo->fix.ywrapstep            = 0;
    2 g' t0 [5 X  B# E+ K6 z        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    ) i" n, ~+ X4 `' q6 i0 B' q : g9 y9 g2 N1 U# O& x
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */) G, Z; |, h, ]9 P1 c
            fbinfo->var.nonstd                    = 0;) G# ~: l4 V) n& S0 Y9 D( p
            fbinfo->var.activate            = FB_ACTIVATE_NOW;
    3 N# I6 f9 r8 I* @        fbinfo->var.accel_flags     = 0;, y/ B# E. X& ~9 o7 P' n6 s
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    6 [- Y0 l3 |( F: f& U3 Z
    - f# f3 L; V- _0 V6 v+ K$ N+ V2 _        /* 设置fb_ops结构体 */
    1 @3 |4 X% b- A0 `4 y+ h        fbinfo->fbops                            = &s3c2410fb_ops;# D( w4 Q2 k* E$ T% V
            
    ( ~1 q5 G' y% g/ K' V& Y+ @" F        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;3 z- B+ u+ N0 b( W$ _. f

    ! E2 ~5 d  i* |0 j        /* 设置假调色板 */
    ( d  W% }$ N, L" |* J4 R        fbinfo->pseudo_palette      = &info->pseudo_pal;
    5 s# I  Z6 F7 ?  C; R% U" v/ x " m0 B# n% c! C. x% @% L
            /* palette_buffer = 0x80000000,清空调色板 */
    * u; f6 C  N; w$ ?2 s  @3 o        for (i = 0; i < 256; i++)
    5 {+ E" P' ^. Z( R' l  y                info->palette_buffer = PALETTE_BUFF_CLEAR;: z+ U+ j# I4 ?- ^  Y1 }

    # I; [( n8 y  }" G1 I        /* 申请中断,s3c2410fb_irq是中断处理函数 */5 }3 p6 J- y% G1 G7 H# a) B
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);6 p# o& c) X  q! x6 |" y
            if (ret) {( J% a' j* R$ y$ v, w1 o
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);  M( I2 \" J7 ^6 m9 {5 [
                    ret = -EBUSY;% z8 r7 X, ]& g: r( o* e: t
                    goto release_regs;7 E5 K" ^8 Y6 l1 k& R
            }$ `% H7 f0 C( G% j0 u9 [0 B

    9 p) b+ I- h. ?8 y9 y        /* 获取lcd时钟 */. q1 Q3 h1 }- Y+ y) Q
            info->clk = clk_get(NULL, "lcd");; a, @1 T1 n) G  ?
            if (!info->clk || IS_ERR(info->clk)) {5 y" M. k- C$ G. s3 _
                    printk(KERN_ERR "failed to get lcd clock source\n");
    ( ~5 m, ^- _5 O( S( d                ret = -ENOENT;8 b: e; t  V! V9 i
                    goto release_irq;
    0 x+ n7 y% E8 ^- L! s        }+ i! \, k$ R- F0 [, ?% I
    % L; t5 [8 X# n* x( f
            /* 使能lcd时钟 */( j9 I- Q# ?/ D# z. J# D
            clk_enable(info->clk);               
    ! z! r- d# b2 L0 V        dprintk("got and enabled clock\n");
    $ @8 V' B' i$ V- }& A
    6 l& j* v, Q$ ^# b# p( p) C; X        msleep(1);3 h( s- W- x3 J

    # d$ @5 B6 v. i0 T% ^4 ?9 Q) P+ t        /* find maximum required memory size for display */
    / ~. ]+ ^  h7 R2 | $ I- ~" a0 x" E
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    . {- }5 O1 C/ M9 H7 R- b     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。- c# E$ X6 D! |  P3 O7 Y5 }1 w
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    " N7 [/ n4 }% x3 d         */
    & |+ }6 O" A  ?5 C8 @        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    ) t% R5 u4 f* o: [                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    " F' D- P7 G3 a ' X$ i& }" @0 o( Z
                    smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    : m9 V4 f4 h: W                smem_len *= mach_info->displays.bpp;                                        /* bpp */( F& m+ X- i: g. S
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */
    - z/ |( k8 t" Z                if (fbinfo->fix.smem_len < smem_len)6 E9 e; I3 o1 q$ i6 N: _* W
                            fbinfo->fix.smem_len = smem_len;8 m2 a2 g9 }& V# f2 t! \5 A. W
            }
    " m0 d' |6 R* b7 K1 o8 f5 B4 N : W  F- x! ^) k+ z9 w9 q
            /* Initialize video memory */
    ' b/ N6 V- H' |" l1 l- j9 y9 q        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    0 w% [' Z8 ^; y! M! c+ m        if (ret) {
    & m& W6 q. V- T2 O8 j1 ~* x. a                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);. u: e+ A; V4 C- ^
                    ret = -ENOMEM;5 V  m, @  X5 L  h+ Z4 h9 o
                    goto release_clock;
    : L& r6 `; B: i8 p1 z/ p        }, ]! u& c9 _- e2 a; z
    6 S1 G, ?: c+ u: A
            dprintk("got video memory\n");- m1 B( t9 Y* l& Q# i4 z1 t
    ! q& t! E1 l) D0 E, a2 {' [- b6 F9 O
            /* display指向tq2440_lcd_cfg */
      e' X2 }# B" |6 q! \4 s+ ^7 P- \        fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */; X+ I9 m" P! r' {1 H; [
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */. o4 P2 ?  @1 s3 E( C0 l0 a" B
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */3 q' P5 e' j  d4 G# Y: u; P6 r
    8 B& F) T+ R* k. m
            /* 初始化LCD相关的寄存器 */' M: y( @; w/ V2 c  S% j# Y
            s3c2410fb_init_registers(fbinfo);( N) u% M! `6 b# U8 ~+ |7 x, |
    3 I: N3 u; C( c7 k8 S* V
            /* 检查可变参数 */! j% O0 D6 @$ \# X/ g# K# X
            s3c2410fb_check_var(&fbinfo->var, fbinfo);
    7 l8 _: t. ]4 _7 x
    ! V5 N1 _5 ?" X! O# x5 I! j        /* 注册fb_info结构体 */# K: H" u  _: q! |8 L
            ret = register_framebuffer(fbinfo);
    & `0 x# a9 Y: R! l0 X! g        if (ret < 0) {. _/ i( L6 @8 F  V9 e. {* N5 o2 V" @
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
      u% }+ b( M$ F2 [$ F                        ret);
    8 q3 C6 H) w, h0 X1 s                goto free_video_memory;# N# H. L+ n3 n- r& r
            }" s" y) h( z' D  V% u% B
    9 P% H( T& y9 j7 _  Y
            /* create device files */$ @# Q& l& {' d* `- D! a& v" I
            ret = device_create_file(&pdev->dev, &dev_attr_debug);
    ! B5 D: W1 H% m1 F; I. m        if (ret) {
    7 A, x. f0 _# F# g( b6 u                printk(KERN_ERR "failed to add debug attribute\n");( d) N2 }6 _: e& c) F" D, H
            }3 W! g6 e6 k) `$ q+ u- u/ i; R
    9 d% L- q, M* l" \/ ]4 O

    ; ]+ }5 n+ B7 e3 T/ O0 W: w        /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */
    . n7 ]5 ?! e* G# K        printk(KERN_INFO "fb%d: %s frame buffer device\n",
    ; s8 e2 T/ \6 ?7 |" K/ D/ I9 h                fbinfo->node, fbinfo->fix.id);
    * `0 b/ P$ J; x/ K - F& w5 I3 @  K
            return 0;
    5 G5 T, n# V, u1 S 8 ^8 O; k% _2 h( D; u
    free_video_memory:
    7 ^/ ~  u$ d. B% v        s3c2410fb_unmap_video_memory(fbinfo);
    # w6 a7 Y$ C3 Z4 Nrelease_clock:
    # r+ D9 f/ u( d2 W1 D2 @4 r        clk_disable(info->clk);                                /* 禁止lcd时钟 */8 W3 ?8 }& ^0 W- {
            clk_put(info->clk);                                        /* 删除lcd时钟 */) w' b# W* d0 a: O% z+ D: w0 g
    release_irq:
    8 C, D' B# J+ |) l        free_irq(irq, info);                                /* 释放IRQ */3 e- w% C0 d) d& x
    release_regs:5 Y6 R* `3 ^0 L, Q/ {
            iounmap(info->io);                                        /* 解除映射 */
    ; K2 f3 X6 C. m% X6 s* T$ U# n6 g5 T5 urelease_mem:
    5 ^2 B& n5 i! a$ z6 ]% H# N2 b        release_resource(info->mem);                /* 释放资源 */
    9 J: B/ z( M2 L/ B6 z8 `( J1 i        kfree(info->mem);                                        /* 释放刚申请的内存 */
    " B% L0 Q7 K( h/ J, r9 q$ `dealloc_fb:
    : A' f$ g: i, ]3 q1 p2 w9 t" C        platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */
    3 _. N; F. K" y) J5 M9 M        framebuffer_release(fbinfo);                /* 释放fb_info结构体 */
    $ c* b0 e5 D9 i6 x: O5 g9 F        return ret;
    ! C+ ]5 B4 [0 b4 ^( r}
    * F$ j  V9 P. K2 m7 d$ {' n拆分详解:
    7 s# i" o) e5 @7 \7 S' a- V+ i  Q一、获得平台数据
    , U4 Q: y* m$ q; a7 u9 z/ {# K* T7 z  L+ N
    - g8 Y1 k/ J' A, e: G0 `
            mach_info = pdev->dev.platform_data;
    7 U$ P; ?, w* E; {$ c. G$ l! {1 v : B6 i8 J$ o- f+ y2 `
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */0 Y  y' [6 E5 `1 a5 G8 a2 E0 g1 r
            if (mach_info == NULL) {" S9 i- }% V0 p& B/ v
                    dev_err(&pdev->dev,9 F* V  V! w2 z$ M
                            "no platform data for lcd, cannot attach\n");
    * O- D0 K: _; c0 h0 C( U& b                return -EINVAL;                /* 表示无效的参数 */- F1 U3 h( l& d6 t. s+ ]
            }" o# @8 ~2 ?/ G# T$ E
    s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);9 O6 |1 ~- G8 L1 A' {2 q* j
    所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
    " o  w( u9 e  S% m2 o6 P7 m' c
    8 a9 M( ?  [+ e- b) q' R4 Gstatic void __init tq2440_machine_init(void)
    ) d, j/ X6 Q7 _" w" {{9 L9 m$ J5 L8 I0 B
            /* 初始化tq2440_fb_info实体结构体 */
    " ]  h& {5 S4 |. `4 E& h0 f        s3c24xx_fb_set_platdata(&tq2440_fb_info);2 A+ C8 Q5 q1 M! S! |" S4 y* D
            s3c_i2c0_set_platdata(NULL);
    " n9 X  E* n/ b
    - E, m( I+ @. o  A8 i8 @        /* 添加tq2440_devices到内核,它是platform_device类的设备 */
    ! N! f9 A, l( T        platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
    2 W# l7 V0 ^; |" f3 ?9 Z        EmbedSky_machine_init();  r# v, c) o5 W; {% y2 R
            s3c2410_gpio_setpin(S3C2410_GPG12, 0);
    : s" H" y% l$ {: n* v+ P6 l( w        s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);
    6 @$ K7 s$ l$ g. w9 T. E: w4 D        s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);
    / W' m' P7 z: i1 g}/ X( ^% s) w8 P5 w# E4 D9 I' \& x1 E& X) d
    s3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info) l4 y  B* W1 ~  a2 i
      o9 t5 [! I, f: o5 m- a8 A* E
    void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd), i& t7 Q* j0 i. F8 U
    {
    , z* _3 T/ `7 c  Z8 [        struct s3c2410fb_mach_info *npd;* w' T. J+ \. d) y: g. i  |

    7 {, H7 x+ X. @# r; T; Y        /* 申请分配s3c2410fb_mach_info大小的内存 */7 L, g: R! w/ \( [  F8 T
            npd = kmalloc(sizeof(*npd), GFP_KERNEL);
    8 C% ]. n" K: q" s8 |: B4 p        if (npd) {8 F0 C1 t. b' {+ O! D5 l
                    /* 拷贝s3c2410fb_mach_info型实体给npd */
    ' a) _8 [" y* f* P                mEMCpy(npd, pd, sizeof(*npd));
    & a  i' N9 k7 F- ^* c * f, L1 d! h3 v. H
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */& I5 M# L+ f9 v! |* y
                    s3c_device_lcd.dev.platform_data = npd;
    - w1 j& u+ @. M! t$ L        } else {9 ^" D: A  f* U2 {% h8 W
                    printk(KERN_ERR "no memory for LCD platform data\n");
    3 x8 u$ H% x8 W/ K8 ^' A        }
    * i) `; h) d3 H, }}
    6 ]" }8 a. Q8 U, |/ N二、设置s3c2410fb_display指向tq2440_lcd_cfg
    7 }! i9 z+ W( W' x  D% d8 S/ u+ x) _( P' D: q& I
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    " @+ a( e( L- q9 B        if (mach_info->default_display >= mach_info->num_displays) {
      B: ~5 E" c5 w) g' C% W                dev_err(&pdev->dev, "default is %d but only %d displays\n",* |1 f' G4 n! @* m! X9 Z+ p
                            mach_info->default_display, mach_info->num_displays);3 G8 V, ~7 ?, Y
                    return -EINVAL;- x2 }$ d9 Y: o/ z# s: s
            }
    ) l: G& w" F  ^, C1 V* M! q , {1 u& ]; z, w3 b1 U' S! l9 P9 B
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    8 M. |5 m0 v' M' T8 o7 n        display = mach_info->displays + mach_info->default_display;
    4 v' j/ N3 H5 D# ?三、获得IRQ资源
    ; `/ s. T/ A- P7 }$ K5 R
    ( Q- y4 [/ b# ]        irq = platform_get_irq(pdev, 0);
    ' J$ |* |: i, t/ Q# H  N9 q        if (irq < 0) {2 g. t' J9 e3 W, @
                    dev_err(&pdev->dev, "no irq for device\n");
      N# j/ R) l0 J                return -ENOENT;# e8 B* o. }, f4 d- W" X4 L7 v/ \$ v7 ?
            }7 b, L2 U( M2 i% g. \8 V
    四、分配fb_info内存5 q5 d- n# C' w
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);: B5 Q- r- n- B' G0 O
            if (!fbinfo)5 y! }0 T. Z- E5 `- o  E
                    return -ENOMEM;                /* 返回NULL表示失败 */2 N8 e0 }+ ?' G, Q) O
    framebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。
    * }' ^0 r/ b& D4 D. U五、设置s3c2410fb_info结构体0 E( o% ?  s8 P1 X% E

    $ Y" E2 t" H4 y+ e+ Q/ B/* 相当于pdev->dev->driver_data = fbinfo */
    2 o  f4 T& Q" A+ Y0 x/ I        platform_set_drvdata(pdev, fbinfo);
    ! j: V0 @, ?' d* a8 b 4 o5 o! r8 s4 g: z/ t* g
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */# ]: y' ^; C9 b! z: A
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    3 }6 T' ?, C, R$ p! p        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */( Y3 f5 l% [& p
            info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    ! g' h3 t* i6 b0 T: n* v0 j六、获取IO资源,映射IO* B- O, s! ^$ d
    . F. E) ^' m9 |7 x
    /*  通过平台设备platform_device获得资源(IO) */
    ( `4 V, a: D& s) d, g- n/ |        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    9 }" j5 T3 }3 I: |/ z& Z( K        if (res == NULL) {
    0 u. S" g% t1 {6 |- l- [" z                dev_err(&pdev->dev, "failed to get memory registers\n");2 D( L8 y5 @0 F& d
                    ret = -ENXIO;" P: I" T1 d( J: W$ I  [( _
                    goto dealloc_fb;
    ! q8 p2 E0 T* ~5 C% n        }5 X- Z; a5 B; o& {" i
    / l, A$ {" F7 P" `/ i, ~# E! v
            size = (res->end - res->start) + 1;                /* 资源的大小 */) l) t5 r  _% {6 C6 B

    1 ~8 Y: ?6 Y6 |% S        /* 申请以res->start地址开始大小为size的I/O内存 */8 o+ e4 _- M8 m2 ]
            info->mem = request_mem_region(res->start, size, pdev->name);
    7 B8 T, x/ b' E, R6 i# i        if (info->mem == NULL) {3 p  S, D& B6 s) b
                    dev_err(&pdev->dev, "failed to get memory region\n");: I+ U, @% L9 a: l5 D
                    ret = -ENOENT;
      V- P6 F0 P8 M                goto dealloc_fb;
    : [5 ]. V- O" E) E: F+ \( p        }
    $ A( w- }; A* P- B+ D- ~/ A
    - C: G, i$ M/ N4 ]) I8 h# d9 {        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */! @9 e1 }7 r7 @" S5 `
            info->io = ioremap(res->start, size);
    * h& X3 g" W# A3 F        if (info->io == NULL) {
      b  K9 E* ?. A, I; Q* `! R* B6 V                dev_err(&pdev->dev, "ioremap() of registers failed\n");
    , W0 s- U  Z! U: u7 p, a, z" {                ret = -ENXIO;
    1 T4 ?) F7 m4 w5 K: `# N, X                goto release_mem;
    + ^, P& O  C; i# r3 D* H! ^# i" A        }& V. j* T# v# n6 E8 f: r
    ! I/ ]2 y. v+ C" o
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */) j# X6 L0 O/ i5 C' u- n) X
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    5 V, a3 }; @; m- I七、读写LCDCON1,禁止视频数据输出, w; [- Z0 L* o% O, m
    6 A. |$ t' ]1 Y; x! m: _
            /* Stop the video */
    " N, j4 b" ?0 w6 I1 \0 [        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    ! [, [. V6 s1 b( k        /* 禁止Video output */0 d3 u- a, n+ |8 P/ a  }. G% c3 q
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);4 J  z5 Y! v9 r& l
    八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    - A: z' E% ^, A) P; S
    $ M8 g/ [  ?4 s6 T- V9 t/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    0 f' q. P8 k! n/ @( u& v        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;1 b* j( b1 a( E) `
            fbinfo->fix.type_aux            = 0;
    % d0 `0 @# Y- k  G! ]! t2 y4 c+ F        fbinfo->fix.xpanstep            = 0;
    4 s& d% Q# d; x  t+ D        fbinfo->fix.ypanstep            = 0;1 [% Q- a7 B, z
            fbinfo->fix.ywrapstep            = 0;
    ( L- s1 e4 y) m- B9 a        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */. A4 v) L6 r" D

    4 w! {5 N; }- e5 T  z- ~        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    . b- b7 E$ K/ K/ |        fbinfo->var.nonstd                    = 0;8 Y' p& z0 j& R; ~1 {; n5 i
            fbinfo->var.activate            = FB_ACTIVATE_NOW;
    ) W+ s8 j0 x( |, h" f! c: \5 `        fbinfo->var.accel_flags     = 0;! R+ I. J0 Z) b$ V( D' v
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;; C- j3 v* F: C4 a+ o! }+ z4 c4 S
    / f% n$ d  \, l7 B2 K0 M. `8 L
            /* 设置fb_ops结构体 */, F# n4 c0 M( Q
            fbinfo->fbops                            = &s3c2410fb_ops;
    ; q3 B4 o" m9 O* P" G! A        
    " d  O+ R7 k) s8 O1 n8 S; R- I        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;& m& N2 S5 h2 f" O3 {0 {
    6 f1 ^/ B' c% D( M
            /* 设置假调色板 */8 a6 M; R' z; z; z; a% `# \
            fbinfo->pseudo_palette      = &info->pseudo_pal;: l. Q- v$ ^9 J" F- ^. L
    ( @7 i3 g3 m9 |- T1 M$ c3 _
            /* palette_buffer = 0x80000000,清空调色板 */" o+ O2 D; \; y9 D3 _- W
            for (i = 0; i < 256; i++)
      W$ H2 n) j- K                info->palette_buffer = PALETTE_BUFF_CLEAR;. e2 j  U+ h" ~# M
    九、申请中断、获取LCD时钟,使能LCD时钟- y9 q) b  B4 I; A: H- @+ }
            /* 申请中断,s3c2410fb_irq是中断处理函数 */
    9 ]; c2 Z: b: c( V        ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);4 ^2 X2 N. v! y% r) q/ i
            if (ret) {1 i# v0 @4 M# S; y
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
    , m9 A& ^5 o' f' d, z8 O* @( ^                ret = -EBUSY;
    6 _6 E( [3 b$ C. Q2 r1 Q& u                goto release_regs;
    & A! `2 v5 Y- O4 Z$ C        }
    : ]1 M5 W4 ]' m4 S: H ' O7 \/ }0 T# N/ ~/ U
            /* 获取lcd时钟 */, q4 n% f6 P  j2 I1 |, M4 `. O
            info->clk = clk_get(NULL, "lcd");
    & Q3 _* n5 p' I        if (!info->clk || IS_ERR(info->clk)) {$ s+ }8 ^& K; E. G+ P1 a2 b
                    printk(KERN_ERR "failed to get lcd clock source\n");
    8 `) v; Z% x) K- o                ret = -ENOENT;
    ( j& \9 P+ t6 @9 m" D' M                goto release_irq;
    & r- u& z, w  R        }: {. v/ C! u2 y$ u
    ; e& `0 m1 ~" Q
            /* 使能lcd时钟 */
    / m+ S/ q- Y! R9 Q. K1 a        clk_enable(info->clk);                # E' d, t" E9 u* W& M1 G; O9 }! F* K2 f
            dprintk("got and enabled clock\n");
      Q* @* s. y4 Y  b, O0 ^( x十、计算显存大小、分配显存内存
    0 Z2 S% B1 v. M3 S1 x) C' v8 g9 r1 a
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,, O+ i1 L' |7 d  t; V" t
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    : ^) d" g) e% @; u% |5 Q+ X     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。" B* K' w! N, Y7 o' q+ [
             */
    8 F2 x2 `' G9 f, D4 R4 ~        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    : C7 h1 N& i+ Z8 O3 k! L                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    / T  Q* O6 D2 ~& |( Y" L
    + D9 W- I6 V: c# Q: O0 @& w1 d                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    3 }! a3 G8 X  F) t" _# R; e) c) e/ m2 w                smem_len *= mach_info->displays.bpp;                                        /* bpp */4 `& V* n) X) T9 z6 Y
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */; q: S3 F' o1 G- q  ~$ e9 ?8 r
                    if (fbinfo->fix.smem_len < smem_len); r8 d$ l; b# z4 M+ O  z
                            fbinfo->fix.smem_len = smem_len;
    " M5 r' ~9 f) J' R1 x7 c" X        }
    8 Q" x1 _: y$ e* D6 m6 C, v. C ! F$ l$ |! M6 L/ D9 g1 W) L! `( B
            /* Initialize video memory */
    # R2 E5 g! T4 D7 B* q9 D        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    6 X' W9 R7 {9 g        if (ret) {
    , H  z0 W6 {2 E" f                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    ( O) Q2 j6 R8 q                ret = -ENOMEM;, {2 v4 Y2 y+ j/ y) j$ ~' n
                    goto release_clock;' U; e; N0 X  u7 b
            }
    7 A  x/ C/ c# Y4 I2 {8 D) B十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    2 F; V' r6 F! h
      @& R, k- E: v5 _        /* display指向tq2440_lcd_cfg */' ^: l& g1 ]8 i& b7 i
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */" t8 z# A# o" v0 J
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */! ^9 ?/ b- d2 k0 ~" N
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    ) \, y! D& o3 B7 z1 O4 V3 ]* [十二、LCD相关寄存器的设置和fb_info的可变参数的检测
    ! X' i' H% n1 A/ W8 n1 H* J/ }; ]" D2 r5 U
    /* 初始化LCD相关的寄存器 */
    2 G/ G& n5 h0 ~        s3c2410fb_init_registers(fbinfo);
    0 c1 v& V; W# n3 q 6 y/ W( ^0 T( k5 V( J7 j( H0 u
            /* 检查可变参数 */* u0 o- M2 v5 X
            s3c2410fb_check_var(&fbinfo->var, fbinfo);
    / i; ~' z, W$ S& Z0 M, R十三、注册fb_info结构体
    6 O1 m+ P8 z, b- Q  h
    4 a, J. y! B1 R7 t2 Z        /* 注册fb_info结构体 */
    0 k  J6 Y/ l0 Z# c1 B6 i* h        ret = register_framebuffer(fbinfo);3 X- S* q. d: w# ]9 S6 B/ M4 B! X: t
            if (ret < 0) {
    . ~" S+ X+ s5 k, O/ I                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    " m3 J% d: }7 i# B) |# @                        ret);
    * ?. o: w& w2 j# l5 u$ d                goto free_video_memory;
    * d- o% T8 |! [) x, k- i        }
    # ]! _5 ~$ ]) a  r& i1 P( |* b' J7 x5 u

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-26 04:42 , Processed in 0.187500 second(s), 23 queries , Gzip On.

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

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

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