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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    / X. ^8 s, J# I5 b$ f  j9 U; W上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    / X9 m+ e& q' u* O# `: F+ z. B, Y% m0 b  r/ e3 g$ h$ @$ o
    上一节文章:linux lcd设备驱动剖析一- f# U  ?, e3 n

    # c2 K, a& L; X( e2 D上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
    6 _4 _& Q& N/ E6 G0 e: H
    $ ^; B9 Z( U' S3 \9 k" y+ P& n) L5 o1 _( Y9 {' K7 Y
    static int __init s3c24xxfb_probe(struct platform_device *pdev,
    4 L" n# ~7 W) ]+ |                                  enum s3c_drv_type drv_type)
    1 K" b- _# v, l1 V{, t# v' i' F, ?
            struct s3c2410fb_info *info;. F+ q) W! ~. }2 v$ H# @  C) m
            struct s3c2410fb_display *display;
    ( q, F* j2 Q; N+ L- N        struct fb_info *fbinfo;8 i# F! f' M; V  @# h/ a
            struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */
    : q: [5 n2 _& _0 p: ]+ X        struct resource *res;9 o# b0 I9 h7 ^! l  J0 N& X+ m5 n" b7 \
            int ret;6 a4 x, O% h% D; M; D
            int irq;3 r5 {  J7 }0 m; h$ m/ b
            int i;; ~' ?8 z( |# b7 `
            int size;
    ( Q6 c# y$ T; B: ~% N1 W# S        u32 lcdcon1;
      C$ {0 @8 s' T: V 7 x6 W9 x3 M, H# P: e3 k. ^% K  L5 h
            /*  s3c24xx_fb_set_platdata()里会设置platform_data
    " {* s: m2 @; }2 L5 X# P# V0 Z$ {         *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    * I* `6 h1 @% c. c6 z( x         *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    " D1 a6 L( \5 @1 z- Z/ t/ H/ @         */
    & H7 K8 A5 {8 J& B/ Y: z* G/ n- |        mach_info = pdev->dev.platform_data;
    , y' [$ q7 J! W4 Y
    , n) B- x5 C+ Y# [- V/ q. b+ b; {        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */' c+ O9 A( s9 P
            if (mach_info == NULL) {
    ' l3 X9 R' i- V                dev_err(&pdev->dev,6 l' ?' }% O: S
                            "no platform data for lcd, cannot attach\n");7 [/ x& p# V4 J
                    return -EINVAL;                /* 表示无效的参数 */, Q6 g0 _5 O+ ]- {) Y
            }
    : H, Q8 G# Y+ l/ _$ l) k ) X1 [+ R& u& M& o
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */% \0 V9 u; o4 {9 H/ w
            if (mach_info->default_display >= mach_info->num_displays) {- t" C' H- f% T4 W1 C; G
                    dev_err(&pdev->dev, "default is %d but only %d displays\n"," G; F0 ^# U: N* ?: X
                            mach_info->default_display, mach_info->num_displays);- I* o( g+ b# x! i, O' J, f! P; e$ w
                    return -EINVAL;- I+ r9 d9 n) a/ ?) o$ d, q' W* I. T
            }
    & I# a& P; e( F 2 v: q6 t& {' T) F
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */7 P! e' D. |/ p- i4 e
            display = mach_info->displays + mach_info->default_display;2 `2 X& R! F" o9 W

    & Z: P1 ?, q9 n8 j        /* 通过平台设备platform_device获得IRQ
    ' B' _" A! V  w2 i; e+ g         * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)
    ( o- Z2 |1 h2 y         */
    5 e0 m7 P2 D; u* m8 j8 A        irq = platform_get_irq(pdev, 0);
    9 L9 q1 {2 A) [6 l- y/ C) N$ P        if (irq < 0) {
      l( k! b9 s4 t! E                dev_err(&pdev->dev, "no irq for device\n");
    6 m% I3 W2 \( w6 i                return -ENOENT;
    3 d; u3 @1 ^) B; t7 L. p/ `. R6 H) I% b        }  S7 d* G  c% l/ v; ]
    0 R) I( ]8 w8 T, [% a6 X% s
            /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间7 T+ y/ |5 u) H1 Q, D8 P
             *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据2 F8 N0 Y3 \2 T& p
             *  比如:clk,resource,io,irq_base,drv_type等额外信息0 p+ a; c$ H' X
             */
    3 p( h: |( `  G- Q' _        fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    ) W/ U! }5 |1 o        if (!fbinfo)
    # e& I8 Q! z  @4 W                return -ENOMEM;                /* 返回NULL表示失败 */
    & Y; ?7 u+ q) g' z5 k5 N, ? 3 o" J, b$ Y/ I' H4 ]
            /* 相当于pdev->dev->driver_data = fbinfo */
    / t& E2 w! C' F3 b8 `2 t- T& c        platform_set_drvdata(pdev, fbinfo);
    ) O. T( |" H- _  s$ L  ]
    9 s" K+ j; D9 `. F( c        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */' g* o3 t4 R" ~7 \) v  o
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    1 R- K6 n& W; B, P* F1 g% d: H        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */9 O) P8 k- B+ d2 @+ i
            info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    4 y# t  D# o; n- c! N3 U
    + x+ _# E/ x2 s2 E+ N- G        /*  通过平台设备platform_device获得资源(IO) */
    # H5 r4 O, V+ \        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    8 u( v  m% ?8 ~( F$ P        if (res == NULL) {/ |  V1 p) y7 R2 @5 ~' ^7 ]
                    dev_err(&pdev->dev, "failed to get memory registers\n");: I2 Q$ N3 z4 ^) ^) j8 ]
                    ret = -ENXIO;
    7 f% q' y; d% O1 i8 y$ ~; d1 |: o                goto dealloc_fb;+ ~6 u" C# B4 J) O! W* B; V: F
            }5 O+ K* W7 Z6 S" o

    5 \$ Q, v; x% L        size = (res->end - res->start) + 1;                /* 资源的大小 */) g3 {  |+ h) a) p. ?% ]. l
    * X# E" P3 o% a
            /* 申请以res->start地址开始大小为size的I/O内存 */
    % j' L+ @: {7 F4 X  t        info->mem = request_mem_region(res->start, size, pdev->name);1 V3 R; [: o; J4 T
            if (info->mem == NULL) {/ Y0 l; h  S+ ~& c, O$ V0 J
                    dev_err(&pdev->dev, "failed to get memory region\n");9 L7 c9 A: @3 `5 B! Y) e; _
                    ret = -ENOENT;
    % D2 r' I% G6 t+ @! U4 O                goto dealloc_fb;
    1 U. G) X7 b4 u! ^1 D7 }/ c        }3 F$ ^0 a; f2 v
    , Y1 v, ?* x0 `) M
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */- T9 H) j6 n: E9 U# ~3 o9 e+ h* a
            info->io = ioremap(res->start, size);
    : V0 E, B& q  V, v2 j4 G9 U7 c        if (info->io == NULL) {
    9 t7 i, ?0 ~. f. f  a6 |; ^                dev_err(&pdev->dev, "ioremap() of registers failed\n");
    " B( D5 {+ }) A: R$ Y2 |                ret = -ENXIO;* `, c' D* c0 _8 J( b
                    goto release_mem;& m0 O/ k7 g4 \, r5 P2 I6 s
            }
    1 `  O& o/ g: K) m6 y4 _( z- n : |$ ~! x2 c! z, ~5 V! w
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */& ^/ c$ ~& X# D$ z! U
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);* F- k4 u* a  r9 u/ c$ y  [7 R

    # V! b9 D5 M4 u, _0 s- W1 ~        dprintk("devinit\n");! H9 s1 O$ ~" }/ u) Y

    * F2 d% k6 v; I4 g( a5 P        /* 驱动名,fbinfo->fix.id = s3c2410fb */4 H) X" F0 c) ?5 o/ F4 V
            strcpy(fbinfo->fix.id, driver_name);        
    2 F" L5 @% `0 u8 X/ F  Q
    : a0 N0 F( |8 x7 |: {0 A; m- O        /* Stop the video */
    6 w' n% n' U* q: j# B0 o4 e* u, ^/ m        lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    : }9 N3 P1 s3 }7 n* ]        /* 禁止Video output */: f. U' |/ m% I' y, K) x
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
      ?/ C( s! r5 k# T% c
    ' b0 [" j9 g, j6 Z        /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */" T2 R, i- ^- S- i
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    * [8 V# n0 E* c- w        fbinfo->fix.type_aux            = 0;
    8 R4 c4 |- L0 ]0 S# E        fbinfo->fix.xpanstep            = 0;- i7 l* j2 _$ ^- \
            fbinfo->fix.ypanstep            = 0;. k9 [# F& N2 [! g7 u0 X$ D
            fbinfo->fix.ywrapstep            = 0;/ V/ a) o6 y9 Y# y) y
            fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */. x5 t- W- g# v5 s8 A4 F7 O
    / p5 w( l: i" F5 q: O" C0 P
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */" B- _/ p. J* G7 [7 N# c) c
            fbinfo->var.nonstd                    = 0;  c6 P" ?% y% s2 ?, W( n
            fbinfo->var.activate            = FB_ACTIVATE_NOW;% F' t5 Y6 `2 U; T$ l0 H& d
            fbinfo->var.accel_flags     = 0;$ r- Y3 s6 w0 p6 T( c* O
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    ! l& v, h; D' k: ?) @
    ; o! w5 `( Z! u0 n# R        /* 设置fb_ops结构体 */. B% `: D3 _/ b; L, D. A# |7 l
            fbinfo->fbops                            = &s3c2410fb_ops;5 V0 t" B3 Q0 v! v7 Z
            
    # C, p, Y/ g: V        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;4 W. H* V, ~5 a# O7 x, D

    ( m& B  l; ~, D/ t. I; t        /* 设置假调色板 */+ `7 s4 s+ s# p) V( K, W  T
            fbinfo->pseudo_palette      = &info->pseudo_pal;
    ' U6 m+ Z! I1 ?* f 5 N3 J+ R( i% b  D$ {% @4 S2 x0 O
            /* palette_buffer = 0x80000000,清空调色板 */3 B% o4 c' _5 f# }1 m* U% h
            for (i = 0; i < 256; i++)
    8 q( b& M& w. R% z7 o$ y' H                info->palette_buffer = PALETTE_BUFF_CLEAR;
    / E/ Y. s5 M8 L6 | * \, {: X0 Y7 S$ q9 o
            /* 申请中断,s3c2410fb_irq是中断处理函数 */! Y, D3 {, X  g" x- F. Q8 d, H
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    ; E' n6 @; D0 ]0 ^! g        if (ret) {0 i4 D; q8 p. T
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);6 C) b/ L. g& H3 F1 r
                    ret = -EBUSY;7 g% @) O; y" h: d
                    goto release_regs;
    - P/ x0 _& ~+ X/ l& Y- @        }
    ' w4 i! L+ ^, |% `' P6 l
    5 z6 p# `+ `" e+ d3 Z" r0 q2 n        /* 获取lcd时钟 */3 l+ c7 w- I: K4 l
            info->clk = clk_get(NULL, "lcd");0 s+ ^) ]. |; f
            if (!info->clk || IS_ERR(info->clk)) {4 W. B2 b4 Z8 r; Z/ `5 ]2 P& f
                    printk(KERN_ERR "failed to get lcd clock source\n");9 I7 A$ _, x4 t8 a6 U& F
                    ret = -ENOENT;
    1 e* ^2 L% q( \/ d                goto release_irq;# @5 U& S/ p+ r. N
            }- `4 z# J. t, o1 q( }
    ' D: y" @2 H  L7 V9 [" Q
            /* 使能lcd时钟 */2 e5 I/ B5 T2 m
            clk_enable(info->clk);               
    0 }7 t; }1 w/ ]! a; |6 d$ h8 y        dprintk("got and enabled clock\n");
    " D6 _; o+ E' { ; k9 r9 B0 K! P4 s) N* O1 x$ a
            msleep(1);
    $ i9 X* l9 n) T4 { 6 U  }; n1 ?* q, H
            /* find maximum required memory size for display */) Y) ?' j6 I( F) U4 G
    ' K' g& ~9 B  J
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
    0 |( X. Y+ r" D+ t9 L3 F     * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。/ z, i8 L! N0 K3 b
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    , k% f. e- p. U, g         */
    ! a' p0 v$ n6 ^. d& m        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */+ L8 _# U! z8 z
                    unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    * p# x: o2 w, \; t - a1 R4 R% ~0 b
                    smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    / Q/ T. p& S  d# ^( t/ V                smem_len *= mach_info->displays.bpp;                                        /* bpp */
    : n& z9 U3 T* `) R1 w- K* ^* H                smem_len >>= 3;                                                                                        /* smem_len除以8 */
    , Y( Z0 Y. {5 n9 {1 G5 k                if (fbinfo->fix.smem_len < smem_len)
    " T  K' N6 F/ b+ I                        fbinfo->fix.smem_len = smem_len;
    - a( T3 f* H# d4 B$ @0 z7 S        }
    4 [7 p' C& B, K: r 6 m$ u2 c& h8 U7 k4 |, q. D
            /* Initialize video memory */4 W9 i/ w$ N5 W6 b
            ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    2 O- g4 X  M% L" W9 g4 Q        if (ret) {
    : _0 U+ \; V+ G9 g% B                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    7 l- j) b- U: h                ret = -ENOMEM;$ o: H/ ^. q6 s* h2 D" b+ j
                    goto release_clock;( S5 t3 w/ q0 q0 L% o+ |: b9 |5 I
            }: v3 f3 f+ R) [

    - A/ F6 l  M3 J+ z6 T* X        dprintk("got video memory\n");
    9 f4 F6 s4 c& L, K& h3 v
    * @/ f/ V+ P  b  W) n7 Q: E        /* display指向tq2440_lcd_cfg */  O! X$ _) S9 f) }. l! j
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */7 R. r2 u: D7 y# u5 z
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    ' P+ F5 P( \% x        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */: [% U$ E4 Z, R$ Y
    5 y2 M, H6 u5 `
            /* 初始化LCD相关的寄存器 */
    2 y( L) O9 h& ^; ^7 ^        s3c2410fb_init_registers(fbinfo);
    - q% M4 Y: S* r/ ^- ^! G! m 7 L+ X. {6 h( q/ ?/ v0 d
            /* 检查可变参数 */
    0 y' l) @% ?; p, m( R) }        s3c2410fb_check_var(&fbinfo->var, fbinfo);  Z; @2 e7 s/ O. m7 b) j! P

    ' p& `4 H& ?! H; w& {1 s        /* 注册fb_info结构体 */
    ; h" v' Z2 L( O6 e' i        ret = register_framebuffer(fbinfo);; [; J. H! |0 @+ [
            if (ret < 0) {
    9 l- [- ]# n! _- d                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    % f( p7 l* K0 b! T, [5 J- {                        ret);
    ( D  ^, J" C/ t/ Q                goto free_video_memory;1 M* q. [( A! {6 K: @. ?' v. \' Z
            }+ E6 H1 y) P8 c) r3 L5 w  p; m4 M0 p

    $ ~; l7 ]/ m5 U/ S' U        /* create device files */
    # F. d  R! I# j9 W0 b& s  |7 r        ret = device_create_file(&pdev->dev, &dev_attr_debug);9 X, L2 F% @/ w* t" B6 O
            if (ret) {
    + b8 }3 D. K2 I& B) p1 D, w                printk(KERN_ERR "failed to add debug attribute\n");3 ~+ K$ C# Z- ?/ ]( W
            }/ h1 q. }" Z. K$ v: S2 f8 W, b8 ]
    , D' v! j( N  F  k

    0 x& s& a# S5 k; B6 J- Y; F        /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */& x" h0 B) M5 P: L1 S, M! ]
            printk(KERN_INFO "fb%d: %s frame buffer device\n",% l) K; b, H8 V+ h  N( J
                    fbinfo->node, fbinfo->fix.id);
    & `8 o0 R* z  u5 k* ^ 9 X/ ^& H( @$ K) H7 |
            return 0;
    # g$ x1 G( k, {
    7 N  d6 {( w" I7 j- Q0 Pfree_video_memory:
    / R' @8 x# U9 H6 p8 l/ H2 d  f7 q3 i: v        s3c2410fb_unmap_video_memory(fbinfo);
    % o/ ~0 q$ X4 G) O! Z/ nrelease_clock:8 T: v7 e6 H* @
            clk_disable(info->clk);                                /* 禁止lcd时钟 */
    ) u0 q1 b) m! `1 p* `        clk_put(info->clk);                                        /* 删除lcd时钟 */8 I& c' L2 \& [
    release_irq:
    & X$ Y7 l! o. ]& ]5 g3 A        free_irq(irq, info);                                /* 释放IRQ */" s" x: U0 f" v% ^
    release_regs:
    ; p' y: z5 k/ W1 V4 x- Y& A, a        iounmap(info->io);                                        /* 解除映射 */
    / _. d. O+ p& G, ?2 trelease_mem:% t" y3 q8 I, r) K7 T; N
            release_resource(info->mem);                /* 释放资源 */$ P2 R2 A# H$ b7 e" O- G
            kfree(info->mem);                                        /* 释放刚申请的内存 */
    + A" [4 L5 @/ X. P  rdealloc_fb:) V8 X! b! k  T+ ?$ l, d
            platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */
    9 b7 I/ s8 H2 S9 Q" [        framebuffer_release(fbinfo);                /* 释放fb_info结构体 */
    8 F8 f- _# s4 ?/ S" n, B: C: O* @        return ret;. T0 @% ?6 `3 b2 ~3 }2 P2 @
    }8 y/ s2 n' {2 [, t
    拆分详解:
    2 \. W2 J5 Z" X5 r6 r一、获得平台数据0 J. F: V' \" v  f
    9 p& I  y+ W! ~3 ?/ T4 U/ ?8 I

    " h, E/ E. `1 U( T* [/ @        mach_info = pdev->dev.platform_data;3 c% F! i9 O) ]9 p5 y* x% |# P, W

    , R0 y+ i4 G7 ?2 g0 \. U& w        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */: e9 @4 M' u1 i: K1 s
            if (mach_info == NULL) {
      e( S- u9 A' j                dev_err(&pdev->dev," G* B" `( j$ a& U
                            "no platform data for lcd, cannot attach\n");+ i7 t" D, n: i$ {% |2 R3 h. W
                    return -EINVAL;                /* 表示无效的参数 */
    & [" L( F* g6 b9 s2 `- e7 i        }! s9 J; i: F8 [: {, v! J
    s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    3 J- A" U/ m/ E所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
    2 k( h# L& R7 U# P, d) A& R
    % Z6 k3 e$ s2 v/ C; l" Y# fstatic void __init tq2440_machine_init(void)" i& c* c+ [2 ?* `
    {8 n0 c" v$ L4 M8 Q( A4 i
            /* 初始化tq2440_fb_info实体结构体 */
    ' g+ Y8 ]$ V4 Q  d: G1 Y        s3c24xx_fb_set_platdata(&tq2440_fb_info);
    + T$ u+ [, u! l! o; q        s3c_i2c0_set_platdata(NULL);2 C! S1 ]' S/ m8 o: l9 t, E

    $ U# I( V, {4 b/ V1 k        /* 添加tq2440_devices到内核,它是platform_device类的设备 */
    4 v* X% K3 _) ?+ x  E6 i        platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));# c  z& H# m, ]& H2 [* t; m) [
            EmbedSky_machine_init();  Y9 _8 @# F! c8 y
            s3c2410_gpio_setpin(S3C2410_GPG12, 0);4 {- |! Z+ P1 X! h! }' H
            s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);
    8 L% b" N2 i3 {& w        s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);
    / Y7 t2 r& t& O' c! p}
    7 y6 `% Q' c8 A, ss3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
    ; p9 p; M& C1 F" K0 W
    ; E+ p) x  i. u8 h- g* f$ wvoid __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
    4 q/ V) H* H; _4 T# I$ L. G( y{
    0 M; f; c2 B% h# y8 w        struct s3c2410fb_mach_info *npd;
    0 q) U) ~/ \+ z+ q8 Q2 I# }   x/ j; g: P9 n0 ^1 ]+ N2 ?
            /* 申请分配s3c2410fb_mach_info大小的内存 */
    8 K. o) H4 I( p" s        npd = kmalloc(sizeof(*npd), GFP_KERNEL);9 m' R# j' [  K& B7 q; M# F$ O
            if (npd) {4 j9 |: B4 ?: \# c. T9 G) i
                    /* 拷贝s3c2410fb_mach_info型实体给npd */! x$ }/ W: R; E, U
                    mEMCpy(npd, pd, sizeof(*npd));0 w/ j  ?1 m0 h4 k3 c
    7 N- h0 z+ E* F. ^  e0 e
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */. k7 D1 {& S. b% x3 x* R+ v% l5 {$ j
                    s3c_device_lcd.dev.platform_data = npd;! d# W- l, `$ |0 t+ I' |
            } else {2 S5 u" e/ F: q% {7 @& w/ L
                    printk(KERN_ERR "no memory for LCD platform data\n");
    9 o/ `& p7 D' C( Z) H        }
    + b) n* F" g5 t}
    & R. v4 }3 f$ M) Y  N. f二、设置s3c2410fb_display指向tq2440_lcd_cfg
    0 s7 Z  I; Y- D: v7 [3 k5 S
    3 j( y. f7 j" L2 j) j        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    . N6 u- H& S9 s% w* B! |6 f8 v        if (mach_info->default_display >= mach_info->num_displays) {
    ; U' y) V- y2 K' ?8 L. z                dev_err(&pdev->dev, "default is %d but only %d displays\n",
    3 n: j" f+ _* s                        mach_info->default_display, mach_info->num_displays);7 ]. Q7 E% L& Y) @( F# H1 Z) Z& ]& S
                    return -EINVAL;
    1 Q5 O4 `6 X6 C% ^; N$ a% j$ ~        }
    - d- E3 z$ h0 A9 `0 ], c3 z: u  j ; A0 l8 R$ F! z4 K6 U: \% ~4 x
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    . V) w# S5 T) ]        display = mach_info->displays + mach_info->default_display;% \% J" o6 z3 a3 a
    三、获得IRQ资源6 E  S& k* W4 y2 X' F
    ( G; m9 C4 t6 e% O& R
            irq = platform_get_irq(pdev, 0);% X5 n& E# t9 V/ _6 y0 R$ O6 k
            if (irq < 0) {, [+ O- o6 W. F+ n9 z0 d
                    dev_err(&pdev->dev, "no irq for device\n");5 d, i% R" Q5 t/ z5 S
                    return -ENOENT;5 b( C' z2 S+ Z! b' s8 _4 j
            }
    " d" Q5 j$ q% f3 f1 d8 T' v四、分配fb_info内存& `9 L  S* @* u' v' A( M
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);9 P( Q9 Y1 ~# U" O* h
            if (!fbinfo)% k8 z2 w, [- e! p1 q, j( s0 K* v/ F. Q# ~
                    return -ENOMEM;                /* 返回NULL表示失败 */
    2 ]2 E4 _5 e6 ], D% O- o$ bframebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。" {# B3 a, H, O4 t1 m& C' x1 _1 g
    五、设置s3c2410fb_info结构体: t+ n% C* h1 c7 b6 i0 Q# i) m0 I! o' e
    1 v- I& Z" ^2 V. M1 h) L, F, M4 C( z" E
    /* 相当于pdev->dev->driver_data = fbinfo */6 j( c: S" t7 x. j7 B
            platform_set_drvdata(pdev, fbinfo);
    , T! {- A9 t9 ?9 z 3 o( u9 E% H3 k9 P0 l) d; o# x
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */  u  _/ V2 \- W9 O# x! \
            info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    " j+ ~7 C8 T0 C4 e        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    3 R& ^8 P3 G; j& e        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */) a. E8 k- ~9 s9 f" |
    六、获取IO资源,映射IO
    " `9 E1 d0 f! F8 d& k( }) G# L3 k$ P& l
    /*  通过平台设备platform_device获得资源(IO) */) o; ?+ V, N3 V5 q5 c- a4 f3 L
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    , B9 X1 Z( L0 @. P- }8 g5 }5 {        if (res == NULL) {
    ! k- ^5 e! d1 |  W9 `* p                dev_err(&pdev->dev, "failed to get memory registers\n");, e6 I- K( S* F6 f3 d' J' [
                    ret = -ENXIO;6 T4 d( G" i) S; y% w' i6 u
                    goto dealloc_fb;
    3 M9 N; v1 m5 P1 ^" r- L        }! h2 E: R7 g# d/ I+ _

    , p0 m9 I# M  {5 @, S3 s! ?        size = (res->end - res->start) + 1;                /* 资源的大小 */  W9 B( ?3 a0 @* k+ G; |

    / Q; x1 y- `" \" f: o1 R; V' H        /* 申请以res->start地址开始大小为size的I/O内存 */& G; w# y: }# }0 f+ u7 g+ E
            info->mem = request_mem_region(res->start, size, pdev->name);( z# u3 |, v, B9 D; @8 v) \- Y) f* y& e
            if (info->mem == NULL) {
    9 z3 Z" E- T6 m! _3 M                dev_err(&pdev->dev, "failed to get memory region\n");
    $ G+ T9 p& _+ V9 Z/ L                ret = -ENOENT;
    8 z4 h* {9 p5 j  u2 a; V                goto dealloc_fb;
    4 u, d# a3 s1 t2 h. `        }
    , l, S; O5 i' F/ u9 ?
    / L# W# V3 q4 a4 N        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    5 }; ~4 E( ~  j# W% i9 U        info->io = ioremap(res->start, size);# G- q! T  _9 ~
            if (info->io == NULL) {
    & o, P1 A2 [8 U: Y                dev_err(&pdev->dev, "ioremap() of registers failed\n");
    8 X: x1 u5 T1 E& }! r* G( Z                ret = -ENXIO;
    1 S: a6 h0 ^) e4 E: e( c                goto release_mem;5 o( }1 L4 @2 j% G' v+ {0 v
            }- F3 x; k- @: b3 N' D, A& T

    + R. x% y! j2 `/ V        /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    : B! P9 s$ n# i        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);! W# Y& F- v% ~# J2 |6 s) R6 Q
    七、读写LCDCON1,禁止视频数据输出. m" M0 |3 R+ d% A6 w' ?1 u$ v

      O. ~/ Q8 o8 x) V5 ~: Q        /* Stop the video */0 S% `0 _1 J4 q, H
            lcdcon1 = readl(info->io + S3C2410_LCDCON1);  E, N) Y9 H' \7 v% Y  b: w, S
            /* 禁止Video output */
    " r$ U5 Q% |$ b1 ?7 @) _" j: J        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);( e) c, z# y" T( f  ~& _$ T
    八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等* v& j4 F7 H/ _

    ( ~4 s, F1 g% X# c( ]) O6 S: C/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 *// ^$ E) ]4 d% d+ C
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;( l" {! F; s5 R5 A8 Z
            fbinfo->fix.type_aux            = 0;& @! @! m1 ^3 S4 J" K3 ^( h
            fbinfo->fix.xpanstep            = 0;
    2 J  [+ t9 g9 O8 M1 ^& h2 H        fbinfo->fix.ypanstep            = 0;
    2 L6 w9 {& {6 [0 Q( J6 d" O        fbinfo->fix.ywrapstep            = 0;
    . G7 L+ n* b/ x2 R" K: V        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    ; k$ E1 A' d3 g# W8 {1 z9 t: A! H, f0 s
    0 G$ T3 g: }# ?  M/ Z0 L        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    9 q+ F$ z8 I3 k- X5 ]7 J+ ^        fbinfo->var.nonstd                    = 0;
    ( \! v/ F7 I4 s- N2 o        fbinfo->var.activate            = FB_ACTIVATE_NOW;) _" e- b& Q! [; }6 M9 @7 k! ]
            fbinfo->var.accel_flags     = 0;' V8 S: r. F, [' J! ?
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;2 o3 K; N9 [! P9 w- L- L2 [3 W' L; m
      i3 c7 o0 p' @: C" W& }
            /* 设置fb_ops结构体 */7 f- I0 d+ _. g. F5 t
            fbinfo->fbops                            = &s3c2410fb_ops;1 @. z5 O4 Z$ K$ W, |9 ]0 l
            
    + M( T2 [2 l+ @) q' H, a        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;
    ' ]- b6 {5 W$ F7 A" E( E. y6 I9 X' d# l # ^; i/ z( c4 |; K- x
            /* 设置假调色板 */
    1 \5 y, k7 L1 ^        fbinfo->pseudo_palette      = &info->pseudo_pal;$ [2 f4 o- x: m( T1 h! f! |

    - S* f2 a; E2 t! w        /* palette_buffer = 0x80000000,清空调色板 */+ a" _& d) [) J
            for (i = 0; i < 256; i++)
    1 P6 ~$ U! g5 k2 m2 e( B1 u3 T" U; }                info->palette_buffer = PALETTE_BUFF_CLEAR;' ^4 E6 h- Y. w# [3 K0 `
    九、申请中断、获取LCD时钟,使能LCD时钟
    4 g# F' V/ I) V5 Y        /* 申请中断,s3c2410fb_irq是中断处理函数 *// m: J% ?2 D% l. {4 p
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    7 d: ^1 q2 i; |: k        if (ret) {
    " n2 w  }3 P4 Q) L, ^, `" D                dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
      Y4 l5 Y# y. a0 r5 Q: i0 w                ret = -EBUSY;- n) l3 k) V( D  P6 J
                    goto release_regs;
    9 h' n1 c* a0 W        }
    " S4 X8 A6 V5 W- ]7 m9 M( g ) [% R- r, R/ u& |' h" z5 T" {
            /* 获取lcd时钟 */
    / H- P* x( _( }0 I. ~/ a  N- M  R7 z  F        info->clk = clk_get(NULL, "lcd");
    1 Y, o, S$ M. L1 u+ J- W& I        if (!info->clk || IS_ERR(info->clk)) {
    4 Z! B, I; G1 g' o7 l0 y4 \                printk(KERN_ERR "failed to get lcd clock source\n");
    , H4 G+ p* x7 B/ T' s2 l5 i                ret = -ENOENT;
    3 _0 ~8 e6 p+ _) J" y5 K1 i/ S                goto release_irq;
    4 C1 G( l  y  f9 F: X. q        }+ x3 c: v/ B* k, z' @. c! A% b& v

    ) }4 U4 A0 K! K/ e" Z        /* 使能lcd时钟 */
    $ c$ P) K1 O3 B! [: t/ j5 v        clk_enable(info->clk);                9 o+ V$ s1 Y7 f2 Z( z1 T4 _
            dprintk("got and enabled clock\n");' K! W. Y# Y9 T+ z- f' ?
    十、计算显存大小、分配显存内存9 F1 I9 y7 Q5 E0 U; V0 T

    " i( F0 p" O: k        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,. t+ w3 W& {! ]1 \7 g( f
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。; I( C% m" X8 E1 y
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    ! A3 B( P7 J1 V9 B- s# ^# t         */
    : t% C, T1 H: t8 w7 f  e) l        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */9 ]4 j0 s: M5 F" w1 a. |" }
                    unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */% @4 [; y0 b% s2 N. M1 U# O

    # F" W, W4 s9 Y. g2 {                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */0 @# O: x6 ^: l: ^* H: r
                    smem_len *= mach_info->displays.bpp;                                        /* bpp *// a( k+ L0 A2 O
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */
    ( i# x3 ]$ \6 [  R8 s                if (fbinfo->fix.smem_len < smem_len)3 j  [  t: B1 g+ m- d) `
                            fbinfo->fix.smem_len = smem_len;
    ' g9 f$ o/ C+ ~5 @9 [        }& b! [  W+ k7 c- S# ]
    8 d5 P7 g7 l: G. b! L; V
            /* Initialize video memory */
    - W8 h- o8 w' w' U- c3 o- w        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    " A$ Z6 F$ b; c' @        if (ret) {
    2 x7 [; s2 Z6 J9 P# S! x4 l                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    1 \8 p5 W$ [4 N                ret = -ENOMEM;& j( f, h, F. b. `1 a  X
                    goto release_clock;, U8 [! n" ?- T) B9 o6 o3 [
            }# E5 H* P( B4 A
    十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    2 u: L( {! x- d) `% i4 [8 n0 O9 C3 i. t
            /* display指向tq2440_lcd_cfg */# F4 k7 |6 V. M
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */
    " b1 [1 k, t5 O: Z9 j( _$ T# W. K$ b& Z        fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    7 Q0 `' T% O1 P- Y6 g, O! Q        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */* m7 S/ A4 _6 J- R! N' F. _- G. e: Q# K
    十二、LCD相关寄存器的设置和fb_info的可变参数的检测1 H; x5 V. p5 Q5 |0 x

    5 I( d- p# e: t) I( n. i& J. A8 J* P/* 初始化LCD相关的寄存器 *// E0 e- @1 i# S& T) {$ M9 B
            s3c2410fb_init_registers(fbinfo);
    * ~- Q1 ~$ E8 c- F  y7 y: I
    0 I7 j; g. x% O( a        /* 检查可变参数 */
    3 P  E+ [& I9 k. R2 d/ [        s3c2410fb_check_var(&fbinfo->var, fbinfo);- |: H* @$ P6 ]
    十三、注册fb_info结构体7 t; ?1 W1 z3 ~) R0 y2 g
    ' Y, `5 `4 P) ~1 e, g! l5 z
            /* 注册fb_info结构体 */3 n8 h! f4 e  E4 w' D
            ret = register_framebuffer(fbinfo);6 _, g( Y' O7 E  g
            if (ret < 0) {/ f- K6 W3 E& q; p6 t* F: C6 `
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    # H" H0 M4 P- d+ K6 ^4 p4 N                        ret);* A5 }) A& U6 _7 F) f  Z6 L. U0 ^
                    goto free_video_memory;5 T! o" ]" J# Q* g$ V7 Q" z
            }
    ! Y- m7 D) }6 n( V5 j6 {7 |
    $ h5 y; s) a7 R3 p& }

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-26 00:29 , Processed in 0.234375 second(s), 23 queries , Gzip On.

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

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

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