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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    % F. ?$ ~! ?  ?- [6 a% m/ `上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    ( C! n; g0 b4 l
    7 E6 O8 r& i7 l$ \% t上一节文章:linux lcd设备驱动剖析一
    3 w( b5 u2 g4 }8 f
    ; H9 l, X& p/ Z上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:% b; I# S( u5 b1 _! ?
    ) W8 a) i: \+ N! t* s
    ( Q7 R+ n' Q+ s. ?, u
    static int __init s3c24xxfb_probe(struct platform_device *pdev,
    ' G) P( U7 d6 f) g! `/ ^0 Q                                  enum s3c_drv_type drv_type)6 [, m3 _  A" f" @+ p
    {5 s: \7 E) y3 h1 j7 r; w) f
            struct s3c2410fb_info *info;
    + o5 X7 E. _: n        struct s3c2410fb_display *display;' R' C; a2 h$ R4 t* `0 ]2 B4 d
            struct fb_info *fbinfo;
    3 u/ x# _* d* d, y8 `; q& B        struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */
    & _+ q2 M0 [& I. j  \2 ?! h+ x        struct resource *res;+ Y' ~& G& B" e0 V" E% n0 i' F
            int ret;
    , K9 H& D9 f% t  k; N' H( d        int irq;1 ~1 l3 D' i, M3 z5 B
            int i;
    # o2 p: M7 q8 u        int size;+ ^$ E+ ^1 U! C
            u32 lcdcon1;
    5 K/ r; X6 d6 z4 u5 I( ~" a& S
    " X7 P# V/ P8 L2 E. f        /*  s3c24xx_fb_set_platdata()里会设置platform_data# a4 y) `" V0 w' k& y8 G) u
             *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);  R5 `  m2 G, r! B
             *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例
    + }: z; E: z$ q5 y) O+ d5 n         */
    ! J" }  r# W( s- @) u        mach_info = pdev->dev.platform_data;0 h) C+ W' Y, s1 t9 P- R
    : o# V# m. K3 O- v
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */" c/ a( `. o% p! ^5 ^3 _
            if (mach_info == NULL) {
      z, a# ^) F4 e                dev_err(&pdev->dev,
    4 g$ S4 k( }2 X' `3 J0 R" |                        "no platform data for lcd, cannot attach\n");/ u/ v5 p3 L" {( L2 }- q
                    return -EINVAL;                /* 表示无效的参数 */* m+ _1 B$ k1 x/ e
            }
    $ X9 h. K  O5 a ( h2 {$ L! c' p, L- }
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 *// n2 H' e9 T) J1 O7 n
            if (mach_info->default_display >= mach_info->num_displays) {
    3 n6 e( P6 [7 p7 F                dev_err(&pdev->dev, "default is %d but only %d displays\n",& @+ p2 ^4 B' ~0 [8 c, W* j
                            mach_info->default_display, mach_info->num_displays);$ G0 Y; ^, ?$ Q6 F* V
                    return -EINVAL;
    6 n# _. |' y) c. @, Q        }
    ! i" K7 r1 b" q! b5 _+ ~ " S! y6 L* a8 g2 z
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    / B! [+ \0 A% X" P+ u  ?; B        display = mach_info->displays + mach_info->default_display;# l8 j( h, U: @, f
    3 c( I, H' }* T0 v
            /* 通过平台设备platform_device获得IRQ$ U% O& W$ f$ \8 q1 u
             * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)( O6 Q5 G& n0 _2 T8 d, i  s% y/ J
             */! z$ m, h6 Q0 j* X* c
            irq = platform_get_irq(pdev, 0);: E& ]7 e# b2 H! v+ [
            if (irq < 0) {7 E2 H% I+ z, Z' V6 e, Q
                    dev_err(&pdev->dev, "no irq for device\n");! i+ f/ |5 P$ L3 L
                    return -ENOENT;
    " s6 E9 r2 S; i% \/ P  S        }
    : {& p0 b5 S3 |0 l1 l
    & E' s/ ~" D9 p/ j) Q        /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间7 G6 h/ y8 T4 d, H' o/ ?( S
             *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
    . `0 I5 k6 e- F  R! _7 ^0 k* {         *  比如:clk,resource,io,irq_base,drv_type等额外信息* }2 S; @# S2 B
             */( s  e9 w& ~  d7 h) I# J
            fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  {/ w* q' n% m. {/ M  f
            if (!fbinfo)* i4 _4 H8 ^5 I
                    return -ENOMEM;                /* 返回NULL表示失败 */
    & r$ }/ g+ l+ K8 F) G1 \+ u
    9 F% ?' x& i3 ~! N        /* 相当于pdev->dev->driver_data = fbinfo */
    / j7 `1 W1 Q/ i. }* T0 o) u        platform_set_drvdata(pdev, fbinfo);9 s: P7 e7 e- M  W& |- V0 i' p% R
    # l3 M0 E' D" b$ Z
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    6 x- m0 ]6 v* _2 P" Q% f3 [. H        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */; }/ V4 b3 s7 y8 v
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    , m& M7 o; c. y# m        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */' z- ]1 m. P5 ^5 a
    4 R. C$ ]- }, S' e. T
            /*  通过平台设备platform_device获得资源(IO) */% U. Y( L& T1 z
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  t5 p5 P: Z5 ^6 C$ `3 r# B
            if (res == NULL) {4 i# a* ^( [( t. I: ^" D4 @
                    dev_err(&pdev->dev, "failed to get memory registers\n");1 ~' r& s! Q& `0 T; J4 J$ U8 |$ V
                    ret = -ENXIO;% d' x- k4 n  s  h$ n  J
                    goto dealloc_fb;- w. B) m; n# h) H* s! R
            }
    ( |. U+ y: S- F4 u
    ( R4 D. N" }. I: {- ^( b2 m4 `4 [        size = (res->end - res->start) + 1;                /* 资源的大小 */
    / }0 R: H  k7 t4 c7 l6 k4 ?2 v2 o4 L# i
    + Q% S$ O" I0 e- m7 s. ^        /* 申请以res->start地址开始大小为size的I/O内存 */
    3 `  w; o$ D1 u! K) U1 e        info->mem = request_mem_region(res->start, size, pdev->name);
    - b3 O) S' t% x% I        if (info->mem == NULL) {( Z, Q7 Z9 _% o
                    dev_err(&pdev->dev, "failed to get memory region\n");
    ' y% ]8 ]/ E7 Z' q                ret = -ENOENT;3 i& z) H% r% O
                    goto dealloc_fb;% ~; }- z( o0 e% F* j5 G" A7 H! S
            }, k! a3 b. U9 A! k  U' R
    1 S: K* i# ^  {
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    2 b+ U1 N* I: K& d3 Y8 i( I/ K        info->io = ioremap(res->start, size);
    6 F4 c& I4 w1 s8 ^. |$ q        if (info->io == NULL) {
    0 S4 [/ C: J0 {* x                dev_err(&pdev->dev, "ioremap() of registers failed\n");, P& D9 X, p9 N' s
                    ret = -ENXIO;
    + b" p6 B, h, [% M0 O2 F! v                goto release_mem;4 H& E$ Z' j7 N' F# n3 W
            }
    5 u! V8 h2 {; }$ o( t  a- G
    + ?" l2 W6 o1 i2 s( \8 k        /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    # e7 C* {# A: K* A! q7 J3 w        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    . T8 Z& r- n3 s, w0 k; @; W$ \
    . `3 k  Y7 a; Q. M4 r        dprintk("devinit\n");
    6 U/ e6 x+ l1 D+ q0 U0 d. j9 R0 O 9 m$ b& [6 U% R
            /* 驱动名,fbinfo->fix.id = s3c2410fb */3 `1 W2 U" u. S0 h9 V' _
            strcpy(fbinfo->fix.id, driver_name);        ( s, Q- }! z3 }9 h9 o, u) {

    2 ]7 G0 o/ e9 Q8 N$ H4 J        /* Stop the video */- R9 D; \. E+ _5 m' `
            lcdcon1 = readl(info->io + S3C2410_LCDCON1);
    + l2 l1 z/ p3 s4 O' `8 V8 ?        /* 禁止Video output */
    7 l3 S" X7 s( F9 J% ^- q1 }2 ^        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);" k1 g# \. S9 ?6 b

    / S- @7 z' J% `, V5 S% f        /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */: K! O1 g! ?. j9 W- \+ t6 Y6 k
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    0 X8 r3 E3 r- x; w2 ^* J9 q" u/ U        fbinfo->fix.type_aux            = 0;
    2 X# ]: Y' y) `8 V% U1 t) `# m( c        fbinfo->fix.xpanstep            = 0;
    / w/ F; f* E8 a        fbinfo->fix.ypanstep            = 0;
    3 w4 p: e6 v; z" z+ a$ k        fbinfo->fix.ywrapstep            = 0;/ E8 P( j. N. d+ w$ l
            fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */( M, T" a2 S2 C+ N6 W
    3 p9 f# T1 m7 l
            /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    ; n' ^! n5 l$ N        fbinfo->var.nonstd                    = 0;& R% l* W; S7 O$ n8 r
            fbinfo->var.activate            = FB_ACTIVATE_NOW;# W% }+ L0 T0 n. B5 ]( S
            fbinfo->var.accel_flags     = 0;, {5 l# l4 L! a( f
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;0 Q! j- N' u' J% P: c2 }$ r3 b% @

    * t  v* O/ ^5 p9 r1 a        /* 设置fb_ops结构体 */
    : z/ B! S4 Y. F( i) ~% T; @        fbinfo->fbops                            = &s3c2410fb_ops;( x9 n4 G/ \$ E
            4 ^! Z3 A- s, `1 M9 ]7 u8 `  q
            fbinfo->flags                            = FBINFO_FLAG_DEFAULT;( U4 {6 H) j, I* k3 h) ^
    ! f; E7 r. y& F& J
            /* 设置假调色板 */& o# V% G9 j6 p
            fbinfo->pseudo_palette      = &info->pseudo_pal;" l$ ~$ T& W7 r  V3 C7 P
    ( L* [0 n; V0 ?" \
            /* palette_buffer = 0x80000000,清空调色板 */" ?) A3 @! Y4 T8 S% P- O4 ?7 o
            for (i = 0; i < 256; i++)
    ! c5 I; |+ b/ f                info->palette_buffer = PALETTE_BUFF_CLEAR;& z: |0 t8 w# i, c  ]6 P
    - |, w9 @7 |! I+ [) _5 o
            /* 申请中断,s3c2410fb_irq是中断处理函数 */
    $ W, o% T0 |/ I" b        ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    * u- T+ b' C0 W* R' c        if (ret) {5 |* A' C; \% R& D& r3 n
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);0 h9 Y: P+ ~& ]# Y; {8 L5 M
                    ret = -EBUSY;
    2 e# z; b* l- e, _  E                goto release_regs;7 j; {5 }$ w; v, [* B  [
            }
    9 |, T& z; u* e# c7 [ ( |6 {! J# Q# r
            /* 获取lcd时钟 */
    6 x" V9 M' w/ [% k  f0 M        info->clk = clk_get(NULL, "lcd");
    # t  M) k: u3 J" T. _/ ~2 V        if (!info->clk || IS_ERR(info->clk)) {& w- s7 G6 s2 }7 ]  r2 R, R. o
                    printk(KERN_ERR "failed to get lcd clock source\n");
    ' _) K- j5 t; l( }9 D  x0 t  O4 W5 t                ret = -ENOENT;
    * o- ?$ d! D+ b1 }+ y                goto release_irq;
    ) ~! }$ m# q* V; |# b        }
    ! E" \; E9 o/ ~
    1 Y  k6 s8 V& @) a0 n        /* 使能lcd时钟 */
    / s5 j* D# W' [; {        clk_enable(info->clk);               
    " l/ R; F+ r9 [/ R: d        dprintk("got and enabled clock\n");
    5 G7 Z  a" b4 p/ i/ I- j$ r4 {/ t
    6 c/ F# y) F+ X) A' W        msleep(1);6 Y4 x4 Y" l' j% t. Y
    $ @1 @- J8 T! ~1 t" J
            /* find maximum required memory size for display */+ b; E7 R. F8 i' d' N

    0 |& f6 V6 ^# J        /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,! M$ ^8 w; T4 E2 w! x3 T$ t5 a; ~
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    2 M" E, R. {% {     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    - @! c0 l8 z! D  w( g& f0 }! R         */
      L+ X3 Z7 G( E* K8 R% ~        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    $ F) x9 p( k1 c& m" n5 |- j9 q" h                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    / i" }! @$ m4 t6 U5 a4 _( G# W6 c
    ! S5 h: {+ P, C) d0 t                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    % B8 t/ @+ ^, r( Q8 F9 r* j                smem_len *= mach_info->displays.bpp;                                        /* bpp */0 ^. x; l6 {. B! O+ c
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */$ Y" r0 Z8 |  P$ c. M- T7 b0 [6 {
                    if (fbinfo->fix.smem_len < smem_len), K3 ?5 q1 ~: a! v% D( F$ u0 M, r
                            fbinfo->fix.smem_len = smem_len;
    ' M& [/ S) u" B; b7 O5 n$ k        }( [5 y: G2 F2 }; M1 [5 e+ P6 O

    & k# E) m7 X7 @9 D8 a  y* X        /* Initialize video memory */
    # i5 a! C, ^. V7 Z" @  u: e- t        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    6 ]! f& L% J0 \- D; v        if (ret) {
    0 n) J0 {8 y8 N6 X. A                printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    ' @9 A- U, d! M: A# o- K                ret = -ENOMEM;
    7 _7 l! [0 }% k  P                goto release_clock;
    * `/ ^+ h( k5 e' W        }
      @& x/ Q+ E( ~; i. R   I% X7 d4 t  |: Z) Y
            dprintk("got video memory\n");2 Z9 y& S" ]5 N$ b# p
    1 d- C. k5 A) [* n/ \1 |$ y4 ^9 l6 t
            /* display指向tq2440_lcd_cfg */
    . x# h% q  e3 y        fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */3 {0 W# r" w# d! d- H" c4 E* j$ q
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    % R& U- J* s: G7 q3 x( W        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */: G1 T4 R* }( U4 i. s% w
    : l: J% E) ?* n
            /* 初始化LCD相关的寄存器 */: S1 K( w( v8 K; I4 _- }& y
            s3c2410fb_init_registers(fbinfo);
    ' ~/ }4 _, G# F! r
    7 `. i+ V; c1 ^  V) g: @5 R        /* 检查可变参数 */( [) [5 ^0 s4 k0 v' p
            s3c2410fb_check_var(&fbinfo->var, fbinfo);
    " L+ W$ C0 l" N- `9 t! ?6 Z# l! } ( p& m5 C+ l; h+ X1 j
            /* 注册fb_info结构体 */2 a# V! Q& _7 |
            ret = register_framebuffer(fbinfo);" I4 R% L- W  V1 F1 F+ H5 J* w' g
            if (ret < 0) {
    8 E8 m' B* u' I4 Y0 \5 n: J' M                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    3 `  W" C# W) _                        ret);/ e5 o' C  m- q* O2 J5 Q, w
                    goto free_video_memory;! @& [3 z& J$ U) b1 J! C
            }
    6 a- c5 A* y! ]& B
    5 K, v" Q: w$ B' D# `& o& @( P7 `        /* create device files */: i/ ^' `' y! j6 `5 [  k% y
            ret = device_create_file(&pdev->dev, &dev_attr_debug);7 B/ q) e& Q2 I  Y
            if (ret) {& d( m5 |! Z$ Y
                    printk(KERN_ERR "failed to add debug attribute\n");8 t, O) L& z: L# `* u
            }
      ]) l) c; e8 ~ - _! i/ K3 S! E

    # e2 V0 C' G& h5 C6 H        /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */1 s$ A/ S; z& d: H2 d
            printk(KERN_INFO "fb%d: %s frame buffer device\n",+ q  a6 a2 {0 K+ k. ?
                    fbinfo->node, fbinfo->fix.id);
    8 A8 Z0 ~' m7 ?7 M9 x) r! D 3 v: V% N  G1 U8 ]  V9 O
            return 0;
    & Y  m( k/ q9 f7 Y' k0 x& T0 } ) `/ Q. R. s. @4 ]
    free_video_memory:4 ~* j4 y  I/ x( |. R: I: W" E
            s3c2410fb_unmap_video_memory(fbinfo);* }2 v/ H' N2 w+ U3 d! L. J. f
    release_clock:
    * a1 D- [. t3 F! R3 o2 X. t- Z5 ~        clk_disable(info->clk);                                /* 禁止lcd时钟 */8 j- Y# U- b- K( E& R
            clk_put(info->clk);                                        /* 删除lcd时钟 */! C. {; Z- {& m5 X2 [
    release_irq:
    , s1 G7 N0 }" b5 Y" r* P/ W        free_irq(irq, info);                                /* 释放IRQ */" ?5 `" u1 M2 ~
    release_regs:
    / N$ c1 B7 C# T6 l. B* ]        iounmap(info->io);                                        /* 解除映射 */  N% a" @4 Q1 {! x$ u4 F5 l, a7 s( j
    release_mem:
    2 |+ j# b! y) i2 n+ x1 @6 x+ v        release_resource(info->mem);                /* 释放资源 */
    " ~# a$ y, R8 z$ e! f* W; K        kfree(info->mem);                                        /* 释放刚申请的内存 */; D: r7 I% ^6 y$ I  B2 {" }6 Q" C
    dealloc_fb:
    * `& Y7 ^3 U* G" _& i# O8 T! z        platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */! k& X% ?, v2 J7 i9 l; C
            framebuffer_release(fbinfo);                /* 释放fb_info结构体 */" j3 E$ `' b+ Q! W* h
            return ret;
    ( Q: D# \8 ]: t5 z$ R}
    ' N8 E8 u0 x2 O* Y& Q8 @7 J3 W; i0 b拆分详解:
    % u5 B5 V' h' I' O一、获得平台数据# Z- W1 L1 U7 l) i& ^7 D, V' p
    0 O+ _+ D1 j% B) F; ^
    & E1 i- H: `! G  s0 |" Y2 s
            mach_info = pdev->dev.platform_data;
    5 h' W& X. @) S4 p) D- j" P ; H* {& P- \4 p3 q- _
            /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    5 A4 \9 {2 M3 s5 J        if (mach_info == NULL) {
    ( }7 G: M) _, ~& R! X" l( g4 W1 X                dev_err(&pdev->dev,1 I& L' m# I2 m4 l. @- _) |
                            "no platform data for lcd, cannot attach\n");0 u: u- q+ B# G
                    return -EINVAL;                /* 表示无效的参数 */
    ) Z5 L% l# s) H! q* s* `- O) z        }. y" B2 @- A& S. S. Z
    s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    9 Y2 W# {$ H, F& E所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
    , N2 j% ?5 o9 A% L& Z& H, h2 S, S! G3 y3 o; C& x# e
    static void __init tq2440_machine_init(void)
    * E5 q& \( ^3 P1 G3 B7 r# N0 f% c{
    8 Z1 x# x# d, ^1 z* d  }        /* 初始化tq2440_fb_info实体结构体 */
    : L. R4 M$ I- A% L* h% a        s3c24xx_fb_set_platdata(&tq2440_fb_info);
    0 B0 z$ ~* c+ L/ A- B" E1 a5 M        s3c_i2c0_set_platdata(NULL);
    4 C- L6 i- w, l, ?
    # ]" L; B# z4 A. ~8 M- ]2 Z8 S! k        /* 添加tq2440_devices到内核,它是platform_device类的设备 */9 P& n/ N* a3 b& u  Y
            platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
    . g  D* W1 y  J# W3 C2 I1 @        EmbedSky_machine_init();
    # _" F% b$ K8 a, ~5 m; ?% u        s3c2410_gpio_setpin(S3C2410_GPG12, 0);
    & ]& B# P! L* y/ I' c        s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);0 E1 u& l4 d7 i5 {: y% Z- E) \
            s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);+ z8 r9 e& c0 c6 i3 \- L* F6 Q9 k+ m
    }0 s. u, j# a5 K: c4 a; \0 z
    s3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
    8 W# Z* `4 l; j9 y5 X, o' @0 k& x# I+ j* I
    void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
    3 J, M' w+ w5 J, s' W& n{
    8 X) A. x$ O0 A3 |$ A) _5 ?. h2 d        struct s3c2410fb_mach_info *npd;
    & G/ O6 C0 o7 h4 z; P1 [" B
    3 F+ o; i' s' g# A* v" {3 |        /* 申请分配s3c2410fb_mach_info大小的内存 */( Z4 t% J, c4 @# `# t$ |
            npd = kmalloc(sizeof(*npd), GFP_KERNEL);3 Z, _9 d8 d1 L' P0 h
            if (npd) {3 m% J" p; i1 D
                    /* 拷贝s3c2410fb_mach_info型实体给npd */
    0 w: Q# _/ }* H  |+ t3 G6 _6 x  l                mEMCpy(npd, pd, sizeof(*npd));
    " H% L8 K( S8 v0 P( o
    1 l- M/ ~! r) z2 X, w                /* 最后将s3c2410fb_mach_info型实体赋给platform_data */
    9 S5 |2 b5 |0 i  p7 x, \6 p$ P  V                s3c_device_lcd.dev.platform_data = npd;8 [/ o& T# d% _' G# m! E; d, _5 ]$ \
            } else {. P0 @  o" ~9 r- W' V; _% P$ w
                    printk(KERN_ERR "no memory for LCD platform data\n");
    ' S# _4 ?' {7 @0 t6 }7 V        }( e/ s9 `8 q3 e3 }' G. r) a) V
    }
    ' k1 ]4 n% k# i% I" u1 v, {二、设置s3c2410fb_display指向tq2440_lcd_cfg# b5 [& c3 e: K9 _

    ( _9 |2 Y' D/ ?        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
    6 g" X. b$ w0 v- V% b# L        if (mach_info->default_display >= mach_info->num_displays) {
    9 a7 N- S! R! V9 H" ~                dev_err(&pdev->dev, "default is %d but only %d displays\n",8 x% w/ E  P4 ~' U
                            mach_info->default_display, mach_info->num_displays);
    & D: t. R* u6 k; t2 t# p, k                return -EINVAL;
    8 ]9 J% W5 g! f% b7 w2 p        }
    6 n, y& f+ C: H( t& |0 Y. Y 6 D" M! X/ N# C/ L! c
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */3 z+ {- V- _+ s0 J( ?0 t5 N" @
            display = mach_info->displays + mach_info->default_display;' w2 L6 G1 N6 o/ y9 h
    三、获得IRQ资源
    & x1 ~4 v0 ]  j" [% ]# ?* U" e9 u0 f; Z/ D( q8 Q) T" u( Q2 @! `! g
            irq = platform_get_irq(pdev, 0);
    " s2 W! A$ C% X9 t3 j2 \        if (irq < 0) {( T& Z1 ?8 g/ k! J; ^
                    dev_err(&pdev->dev, "no irq for device\n");
    , v; {; [/ a& ?% u                return -ENOENT;
    9 W. X( [7 @& h2 |+ Z        }% M2 X' ?" I* t8 a" t2 R
    四、分配fb_info内存( I) ^' D* }6 ^0 _! a
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
    $ A  Z- _* q! j  ~1 ?4 A        if (!fbinfo)
    * }) B: g! o! D1 u# @# a9 Y                return -ENOMEM;                /* 返回NULL表示失败 */
    ( ~) B8 G  s1 a. Aframebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。
    % H& D  Q9 r, G- A2 c' U  s4 d五、设置s3c2410fb_info结构体8 n3 w& D, s3 E2 C

    , U; ?; j9 @( L) f- l/* 相当于pdev->dev->driver_data = fbinfo */( Z# V4 _# F0 B( H
            platform_set_drvdata(pdev, fbinfo);
    & E% d! W- `4 E* n" _
    : _8 e' s/ f1 k- W3 g        /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    2 C8 J# r, F; D        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */1 I+ l/ S8 \' A) |# U; \$ U
            info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */1 v* d. Y. ^% ]7 L% r
            info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    4 \$ ^4 M9 Z- e六、获取IO资源,映射IO! K- ]6 S/ F' m1 X$ F8 \1 Q) K

    : h9 P+ D3 X( t+ u/*  通过平台设备platform_device获得资源(IO) */
    3 o# k* l8 s+ Y$ ~$ P* J+ ^/ w9 f# e        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);; j0 l) e9 x6 y! v0 t* [6 |
            if (res == NULL) {0 v, U4 x# S" @$ R6 C1 |
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    ) x0 I3 \& G3 t  w, a6 y                ret = -ENXIO;) n/ Q, U4 a: n: X2 k/ K5 p3 B
                    goto dealloc_fb;
    . v/ M" g) c# p  k1 S        }8 E  W3 d6 T! L0 T

    2 ^$ H2 q: Z3 S. j( V$ F1 b' A5 w, y        size = (res->end - res->start) + 1;                /* 资源的大小 */* g0 A, F' v4 ?: Z0 Z- e7 |- S
    " S! A* p% H) ]5 A* H5 ?
            /* 申请以res->start地址开始大小为size的I/O内存 */
    $ \3 ~( k, `; K        info->mem = request_mem_region(res->start, size, pdev->name);
    & ?" T* {6 x, F) n        if (info->mem == NULL) {
    ( @+ I! x$ X6 J4 e% Q                dev_err(&pdev->dev, "failed to get memory region\n");
    + ~  y+ ?: U" ^( C& ?$ B0 v# c# B* k                ret = -ENOENT;
    ' B6 R7 K; ~$ J5 X' p8 W; ?# H. q                goto dealloc_fb;
    ( S7 e( F* W7 B        }
    : U0 ~, t& [, Z+ ~* K4 p
      u* C0 w* t* g  X2 N+ W# a+ A        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */  ~5 e4 `: U9 A' i' M
            info->io = ioremap(res->start, size);
    ( W: w! }7 e" @$ b! Y        if (info->io == NULL) {
    * U/ G5 E: O* Q6 _* d                dev_err(&pdev->dev, "ioremap() of registers failed\n");7 x( F/ s) h& e5 N4 Q
                    ret = -ENXIO;
    $ ?$ B; I# e1 k% S8 H; b                goto release_mem;( Z; U  {2 Z# ^+ P% T# @
            }' G- E1 I+ {( |- _6 D& c2 ?
    * ~1 W& M) L" e: c( ^" V" ]$ B* E
            /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    ! ^+ A) Z  Z- `+ t# G2 o4 P1 `        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    : ?2 {9 w. D: m& n3 R% A; n3 d4 b七、读写LCDCON1,禁止视频数据输出$ j- x5 M& }# S" Z/ M: R0 u

    , h& c& S# w( e8 ^: L" ~7 N        /* Stop the video */- I* E9 ~! ~% v- T( q4 u1 g% ^+ r
            lcdcon1 = readl(info->io + S3C2410_LCDCON1);2 c* P! s: s. W/ o
            /* 禁止Video output */- J4 P" ?2 ^" T" h
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    % P. T1 M: Y8 R# X9 G6 m八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    ) y8 }. _& A. ]5 ]( O3 M
    9 y" h+ ]: k4 J/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */& T* K5 x" x' o
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    2 p7 n8 C6 d0 k; R        fbinfo->fix.type_aux            = 0;0 U0 g9 I9 J- v% I# k7 H
            fbinfo->fix.xpanstep            = 0;) S& K% Y+ p- e8 _
            fbinfo->fix.ypanstep            = 0;
    4 `8 W$ t1 b0 V# Z  b3 J# j5 h, K        fbinfo->fix.ywrapstep            = 0;
    : S& M1 J6 s* `/ b        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */' t0 _7 G7 u% t8 Z; E

      J& a2 d) H" f- K0 \        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    ' T3 h0 p# h- w5 M4 s6 F' B( {        fbinfo->var.nonstd                    = 0;6 ~$ k; w8 T+ |0 Q& ?! ?
            fbinfo->var.activate            = FB_ACTIVATE_NOW;2 G/ b; k( U2 U: R0 N# Y, k. a
            fbinfo->var.accel_flags     = 0;" \9 Y( Y  H8 r! o) g9 P3 Q9 S
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    ; ~# z$ n' U) {4 P" a/ J' F4 O) K# A + C& F1 x) ^2 X5 f; L5 ~
            /* 设置fb_ops结构体 */
    & z, w9 N6 V! s% r6 k& H( N8 ?        fbinfo->fbops                            = &s3c2410fb_ops;- o* G6 v% i+ H* N
            6 Z4 i* }, \$ g
            fbinfo->flags                            = FBINFO_FLAG_DEFAULT;8 m1 {. ^6 {7 U5 S9 ^
    4 L$ L+ r  {0 p9 p/ E9 P, Z
            /* 设置假调色板 */1 G6 \; }9 \& T; B  Q
            fbinfo->pseudo_palette      = &info->pseudo_pal;
      g6 C+ H8 `# S  a6 M* ` ) X6 [' d& m* J* q
            /* palette_buffer = 0x80000000,清空调色板 */
    9 f- w: P1 w: i) c# k9 a) ^        for (i = 0; i < 256; i++)
    . @' i0 p. [& o" c- N3 S& _; Y                info->palette_buffer = PALETTE_BUFF_CLEAR;
    5 }0 {3 {1 P. }7 U九、申请中断、获取LCD时钟,使能LCD时钟
    $ |6 S& t$ B. x3 ?! h0 M        /* 申请中断,s3c2410fb_irq是中断处理函数 */
    ! ?" h$ X5 h) u' ]        ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);% U8 w. B9 I6 Z' z2 }6 c8 r
            if (ret) {1 m% t; w. I8 Y; k+ B
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);) I, _( I- u+ W' Q8 o$ Y8 E
                    ret = -EBUSY;/ ^  J$ E. V7 `% y( u: ?' U
                    goto release_regs;
    + g; F9 u) \) R% ^$ G; |+ ^        }, C: W; W, L) N4 T9 q4 ]2 D

    4 N$ Z$ _- e. O; B4 r8 x        /* 获取lcd时钟 */. w9 _! o2 X! z; E
            info->clk = clk_get(NULL, "lcd");
    . e! F" |3 ?+ p8 x        if (!info->clk || IS_ERR(info->clk)) {6 ]8 E0 I1 I% ?6 j) M& M4 p
                    printk(KERN_ERR "failed to get lcd clock source\n");
    2 H4 F# N' ?) @* A- S7 d1 r                ret = -ENOENT;
    7 B, a' U6 a# @  x                goto release_irq;: i# v3 o4 \( r4 F0 ~+ }+ @
            }; G+ p" U. A0 u

    ( b* o1 ]3 v! y4 i3 c) L) v4 n        /* 使能lcd时钟 */
    ) R$ }; K- {3 w        clk_enable(info->clk);                * h8 F% t, \1 o5 P4 i+ J& k
            dprintk("got and enabled clock\n");
    ; R1 g% r& L& I* }$ `) q" L十、计算显存大小、分配显存内存- l' ^" R/ ~' g4 k. _8 {) s2 E0 C
    - j+ a* w% k  O# u1 z, A
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,# m* K& }' F4 r$ m2 f6 x
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。
    5 r/ b1 t" j( K# \+ @% r     * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。& C- l) \+ v1 v; p; O4 L
             */
    $ c  Z3 r- y' P; W        for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    6 z) x* n2 x1 [                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    % x- @0 e- o9 X5 @! s6 V% D% Q
    7 y. M4 g, c9 q! s2 |% L                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */  U# V$ w, h% X0 [8 T5 V
                    smem_len *= mach_info->displays.bpp;                                        /* bpp */
    & V, X  q* L4 x$ W+ H8 J                smem_len >>= 3;                                                                                        /* smem_len除以8 */
    0 o; @- k; e+ b4 t3 q                if (fbinfo->fix.smem_len < smem_len), ~0 ]1 s& `( D" z0 e
                            fbinfo->fix.smem_len = smem_len;
    4 t. _( u1 ]1 o3 ^" c1 O: }        }7 m0 E- d* `) }6 o7 Z" {$ P' {8 k7 z

    $ b" S5 E3 u  |7 a# q& r        /* Initialize video memory */
      o0 V& _" N2 a; B! t; s" S8 T        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
      E9 \% }# j1 i, ~        if (ret) {2 k& u4 `- `* h
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);) U1 ]& \, v. k2 v; n
                    ret = -ENOMEM;  `8 _' w" _5 b9 x
                    goto release_clock;
    ( I7 _' {" M" e4 g; `, A. O        }
    + F% z/ t& p) k0 a4 P十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP7 U' M, V2 d- _* l  F
    1 r& ~# h8 |3 R, u, ?
            /* display指向tq2440_lcd_cfg */
    , }0 O% o( p% z( i1 H: `$ i. K        fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */
    ) S1 X+ p$ R* [2 ^        fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */) B3 R* l: P9 A( p6 x9 [* G
            fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    / q" u2 O4 u- ?十二、LCD相关寄存器的设置和fb_info的可变参数的检测$ a- l) o( w' p5 E, ]

    # H0 G# f" k+ H6 c- _/* 初始化LCD相关的寄存器 */
    3 Y3 O; S2 O$ e& f7 N9 Q6 H4 a        s3c2410fb_init_registers(fbinfo);
    1 Z8 x& Z3 {8 Z0 l# X* _2 z  a! S
    ( g& o$ w- F4 [- \+ s, o  H4 Y; w        /* 检查可变参数 */
    $ K6 F2 H* V7 m% z, x: a        s3c2410fb_check_var(&fbinfo->var, fbinfo);
    / Q4 h# R4 D  C4 k6 ]$ D2 N8 P% n十三、注册fb_info结构体, T) R  @) G0 M2 ~* f
    , x5 I/ `  Y& C, r
            /* 注册fb_info结构体 */6 @  D# p9 o/ J, U# l' A6 B5 s
            ret = register_framebuffer(fbinfo);8 S% ]# h3 F% j
            if (ret < 0) {7 I1 X( \# V/ v3 H
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",+ y5 T" [% ]4 P9 I
                            ret);+ @1 r& x) z* q1 n4 y0 B- d( f4 }# z1 x
                    goto free_video_memory;# T* R) W1 l( ^2 C# S
            }  f( Q% \* G9 B& l  z

    ) S8 W$ n9 b3 O% v0 p$ c$ p

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

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

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

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

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