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

linux lcd设备驱动剖析二

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    1 X9 ^1 V% r4 a/ G+ Z上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
    4 o: n, {$ V+ t& P1 n
    4 n' ]2 s, N/ g4 P, y  g; J: d上一节文章:linux lcd设备驱动剖析一0 s' _4 ^! o7 f- H# ]( S. X

    7 O/ Q% f! ^! i9 i  ^上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
    8 z* O. x1 ^: @, K
    2 U% u7 a4 V/ E5 e0 o: u/ z+ Z  l! [* B# m. U
    static int __init s3c24xxfb_probe(struct platform_device *pdev,+ N7 Y, N2 k4 D; f+ j1 z; U* z
                                      enum s3c_drv_type drv_type)
    . H6 g6 d9 Y8 I( K$ X! S  g{  e8 W9 q1 T( c2 _! K
            struct s3c2410fb_info *info;6 J/ Y+ ^/ |% M
            struct s3c2410fb_display *display;6 p' h, E) i* p
            struct fb_info *fbinfo;
      E1 K, D+ q+ a: S6 v9 L" P        struct s3c2410fb_mach_info *mach_info;  /* 包含s3c2410fb_display */
    " N4 ^0 o) X4 p& e9 V/ `        struct resource *res;1 L7 y) ^3 V! Z$ a$ G( m
            int ret;7 W9 ~3 J7 S) y- d# D' @
            int irq;
    9 U, `1 G- C+ G' ]6 t        int i;1 j4 V7 ]5 f' o* R
            int size;
    " {7 q9 m# x; \& x        u32 lcdcon1;
    : @0 f2 G0 F8 ]7 @
    ; W; W! S/ x+ c* k% \        /*  s3c24xx_fb_set_platdata()里会设置platform_data
    2 G+ m& s; K2 E: \. Y         *  tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    0 W+ ]8 u7 l+ [* J* B% N- F         *  所以这里传入来的platform_data就是tq2440_fb_info结构体实例6 Q7 j; |& K8 E( e. o1 v4 y
             */- H) k' D+ a1 V  X: g
            mach_info = pdev->dev.platform_data;
    0 u- q' b0 P9 A  a* G. `4 a
    4 x7 [& |( I4 y8 h) G/ v# Z        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    . }: _; w" O; T0 {8 @        if (mach_info == NULL) {
    7 t! x& u9 d4 g- ]                dev_err(&pdev->dev,0 F+ M+ L) p/ j
                            "no platform data for lcd, cannot attach\n");- M8 D; B  c  X3 y& d; j/ a+ g5 e
                    return -EINVAL;                /* 表示无效的参数 */
      W8 p7 N' p  f/ R        }- h% L2 {! g: S
    7 W) f: U  j# \) w4 B- _
            /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */& R, Q, K9 i0 M0 J1 V5 X
            if (mach_info->default_display >= mach_info->num_displays) {$ C5 b5 Z6 U2 ?- R6 l
                    dev_err(&pdev->dev, "default is %d but only %d displays\n",
    ( A" L6 c5 d4 d# @7 u7 W( S( b                        mach_info->default_display, mach_info->num_displays);
    . C5 [5 P! Y4 j, {. _" ?                return -EINVAL;4 m6 V! T; @0 W% P2 v4 v
            }8 ~$ u. t  P+ r3 G7 O
    : E4 ]  w4 P  ^) j6 o8 X/ J
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    * j& D( u5 F% C" w- ]( g# v8 C        display = mach_info->displays + mach_info->default_display;) S% d. X& n. e# V% |

    ; V4 ^5 N9 b* _        /* 通过平台设备platform_device获得IRQ+ m8 P0 r! t% F6 x5 b
             * platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)
    8 d9 d. u# y" W9 L4 f         */
    $ h& p, l$ V' t! a% L        irq = platform_get_irq(pdev, 0);! N, o% |. x! ?) N
            if (irq < 0) {% |. d% ^3 u: ~
                    dev_err(&pdev->dev, "no irq for device\n");6 ^) \; Y% {1 e# [3 j9 d; z. L! Y
                    return -ENOENT;
    # L' F2 T+ e5 K! U" O0 ~        }' ~* ?9 G: E* U# b# X$ K$ F, [; G

      Y* v3 X( x2 a" S7 y4 f7 _% R; k        /*         分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间' R) I. }4 Z, d, C2 j% L+ L
             *        用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
    - c/ a7 a# w: l1 l% ^9 S$ ?) V         *  比如:clk,resource,io,irq_base,drv_type等额外信息& ]9 n4 b( |+ x9 N8 \& T
             */  v& ]% Y0 f' V# P8 t4 c  f
            fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);4 G- i6 b, ~* e/ ]' D  i* U
            if (!fbinfo)7 ^0 I" ]) n6 o- ~* M0 R% ~) ], l
                    return -ENOMEM;                /* 返回NULL表示失败 */; x' f( ~( P2 H
    & _5 w+ Y( F& e( ~0 H5 `" {  }5 m- y) B
            /* 相当于pdev->dev->driver_data = fbinfo */
    1 w+ Z. D8 B+ A& }4 y( P9 s$ I# F  l" J        platform_set_drvdata(pdev, fbinfo);
      V$ c5 \2 P9 z% b# N; N: | ; x9 U5 n) ~; u: v9 l3 E& M
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    / h, X$ v' M# v% J- O3 B        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    ( \# ]2 a: l6 L; h        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
    , B& C  b# p. v3 z        info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
    5 g9 g) R# n1 T6 V+ P
    6 R! A% D+ u1 A6 [" c& K* H        /*  通过平台设备platform_device获得资源(IO) */
      x# |, ~8 x. _  u6 n        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ( u: z3 ~3 j" f4 v3 J        if (res == NULL) {& [* ]" t: A6 v7 {( J% e2 D6 s
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    7 h) p: _, b* H0 Z                ret = -ENXIO;
      K* @; j: s' I  f. z( ^                goto dealloc_fb;
    # d' t) A$ D+ ~; F8 f1 r, C        }+ H+ z+ p) N! K, c

    7 x8 r' u6 X) ?        size = (res->end - res->start) + 1;                /* 资源的大小 */
      U! P+ N- h+ \! B+ |% ~" q
    1 g* T1 [1 ^$ ~- i. {        /* 申请以res->start地址开始大小为size的I/O内存 */6 e( W: d( y& v0 `) U( S; D3 P' X
            info->mem = request_mem_region(res->start, size, pdev->name);" Q% x& }9 D. i5 v
            if (info->mem == NULL) {
    ! g& W* @3 x# T, q+ F                dev_err(&pdev->dev, "failed to get memory region\n");
    6 `* S, u: {  n. r                ret = -ENOENT;% {: G7 F" }- h6 m
                    goto dealloc_fb;
    , h+ K& `8 ~3 A3 ^        }( M* W* X( V/ A

    0 {2 G) j% q# \# }        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    : |9 p* o5 {4 d/ p1 e$ |        info->io = ioremap(res->start, size);
    & g+ T9 G( j. @        if (info->io == NULL) {
    : G( N( V) Z+ L7 |                dev_err(&pdev->dev, "ioremap() of registers failed\n");
    " n+ f, ]" s6 M                ret = -ENXIO;+ U4 ^1 ^8 D* C6 z' u+ m! }) I- @
                    goto release_mem;& z5 S: g" t' `6 R. @2 Y/ |
            }. J* ~! g) g+ `8 c$ L: |. J( a' f

      K; t# m! l5 y4 n        /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */7 \" v; g1 U2 N) A' w: f5 W( a) _8 Z
            info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    ' _  B6 j3 L5 s- A5 q+ \
    + o+ I0 N0 ]' }( Q0 f. z- V% V        dprintk("devinit\n");; x4 F4 W2 r1 T8 }9 w; ]

    8 ?) c; P$ |- E* {' [* e5 c        /* 驱动名,fbinfo->fix.id = s3c2410fb */
    # b! u/ y/ \' Z) h        strcpy(fbinfo->fix.id, driver_name);        % G0 l9 X! L. U! `
    * p  V, Z4 t7 _4 d, a0 [4 o( D: F
            /* Stop the video */
    ' w5 R9 j  _$ k' U$ c& [2 x; l        lcdcon1 = readl(info->io + S3C2410_LCDCON1);* M' O/ p3 Y! w# O4 x; {8 d/ U. c
            /* 禁止Video output */; ]  b( O7 B$ y6 P# `! o$ Z! K
            writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
    * D7 k  W# @# @, E; v! u  F$ R
    0 \/ R# e/ O2 i' a9 o' E        /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
    3 Y3 y, K3 p, K. c" L7 \! V. ?! {. n        fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    ; D  _1 J" g2 c( S, l        fbinfo->fix.type_aux            = 0;5 X9 L+ h. M- V9 @
            fbinfo->fix.xpanstep            = 0;4 f& {1 g- L. G: o! e: s) {/ }8 O1 ^
            fbinfo->fix.ypanstep            = 0;+ r: a8 Y0 i+ T, e1 {* l/ j
            fbinfo->fix.ywrapstep            = 0;
    6 V7 X0 l( @! K9 K" y: t: F7 C        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    : n* D' O3 R) C* V3 z& u
    " p3 K- ~, b9 w        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
    . G$ R3 W; }- a! a* P! e2 B        fbinfo->var.nonstd                    = 0;  A9 i8 }2 r% S5 h, b
            fbinfo->var.activate            = FB_ACTIVATE_NOW;
    0 t% v( m+ B/ G  ^        fbinfo->var.accel_flags     = 0;; v6 ^3 @6 u8 h+ j4 q" S' l. P
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    % F" Z( ]- `8 L$ X. m
    , j( z; w* l) p1 I/ `        /* 设置fb_ops结构体 */! g4 V' W, Q, [
            fbinfo->fbops                            = &s3c2410fb_ops;2 F" H. d+ |$ k* `. d1 H+ `
            8 U6 b+ B2 a, H4 Q
            fbinfo->flags                            = FBINFO_FLAG_DEFAULT;( C( C2 i+ M( @! C
    ' T! P# {4 d% h
            /* 设置假调色板 */5 \$ b' e% |) y; a! S  Z" J
            fbinfo->pseudo_palette      = &info->pseudo_pal;# z5 o# a. S9 ~8 \
    ; v0 ?8 s) Q  P7 v6 @
            /* palette_buffer = 0x80000000,清空调色板 *// x9 g2 G9 U5 `2 L% H* A/ w3 t0 m
            for (i = 0; i < 256; i++)
    2 y( ^. J8 Z/ ^# _; I                info->palette_buffer = PALETTE_BUFF_CLEAR;+ E( A& i0 p! b, e6 Y+ Z3 f: s
    1 R" v; O  t. T9 ]* h
            /* 申请中断,s3c2410fb_irq是中断处理函数 */0 z$ i9 y" D3 v) l3 e
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    2 h* E4 E' y' R& n- ^5 S% F: D        if (ret) {
    # I7 M7 o1 u% B% p                dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);& M) c1 J8 ~+ `$ D. c) }' _
                    ret = -EBUSY;
    ! Z6 d! v0 p" y7 I                goto release_regs;2 H( p' _- `  C9 G5 c
            }  g# K: F, q# _. x8 U  w0 ~
      P+ \4 ]9 G! O# n" G
            /* 获取lcd时钟 */% G1 L( I  G9 P
            info->clk = clk_get(NULL, "lcd");
    8 w5 Z! s; v3 w# r: m' c5 Y: G        if (!info->clk || IS_ERR(info->clk)) {  Y$ p" O. C, j9 s3 w" U+ z
                    printk(KERN_ERR "failed to get lcd clock source\n");
    # \8 }- g1 A. |! B! ^/ w                ret = -ENOENT;
    " ^* a  G5 q7 V2 J9 }                goto release_irq;9 Y& Y4 p9 J9 M. |, l" R; `1 O& C
            }9 [4 m6 n7 c( @3 T. W

    4 I% ]% v2 g% d        /* 使能lcd时钟 */
    $ w5 J% h3 o! \        clk_enable(info->clk);                $ g; |* I' K  Q' L7 W# ?' I( w
            dprintk("got and enabled clock\n");
    * p2 o; k* m( L+ `/ D
    % S7 j3 R4 j7 B8 K        msleep(1);$ k" _/ q, l9 C" h6 z, P

    ; ~! B6 l7 L/ X9 \3 {# U: q; d        /* find maximum required memory size for display */
    $ O* N. i$ v6 L2 S - C' t) k5 h  O( T4 e  b4 y& g
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,  d0 Z9 j& B: L3 D9 ~' T, p0 H2 Q
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。8 U9 @# b1 }+ o: o% @" k
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
    # u1 P) G4 W. m: J         */! k6 g" q( u0 h0 m7 w" W
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    ; K' a$ Z  F4 p3 _                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */
    ( o- y  G6 S* K6 D2 y5 Z+ b
    ) O; q3 n* Z5 d7 ~. S* S                smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */4 }( h! i# W6 d1 A0 @) X7 o, a% p1 Y0 D" r
                    smem_len *= mach_info->displays.bpp;                                        /* bpp */
    0 D3 P, k5 p) z, U" q. z6 B                smem_len >>= 3;                                                                                        /* smem_len除以8 */4 q* ?4 g$ y3 C5 D' J
                    if (fbinfo->fix.smem_len < smem_len)1 u2 x: }5 `/ e" |
                            fbinfo->fix.smem_len = smem_len;, Y7 T  z6 n5 ]2 w, @
            }% ?5 V/ ~+ P2 f% G  a
    9 o- k: v( _8 N: c* z1 t
            /* Initialize video memory */
    & {& v3 ?+ ^3 U4 O2 {        ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    ) o0 L8 v2 R7 A        if (ret) {7 r: I0 Z9 J% R- _( S; M6 b7 Z
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);/ `% ~! r0 S* w$ H6 W. y
                    ret = -ENOMEM;' j+ K2 Z1 c2 p1 U, Z3 S4 M
                    goto release_clock;
      l: V1 o7 E4 t( C- M3 V        }: T) K( W5 T; Z% K" }
    * Y1 V9 M  `0 b$ L7 ~9 W! E
            dprintk("got video memory\n");6 C0 |* o) ], j" j0 R+ y
    - Q( O) H* p5 K! Z9 s7 _
            /* display指向tq2440_lcd_cfg */+ Y( s8 O/ d( N) v) \
            fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */" T& D7 p- k0 h8 ?
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    8 F! u' T0 R  u% n$ S8 C1 Z        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    - Q8 m3 A6 a, a% V1 h; L1 E; K
    0 k( E+ r/ R, R        /* 初始化LCD相关的寄存器 */
    1 d2 A# P1 A' q7 `1 e/ L        s3c2410fb_init_registers(fbinfo);& I" Y5 I$ c" F

    # G$ f( T; R. V1 I8 c        /* 检查可变参数 */% f3 I; \0 i3 b" h9 M
            s3c2410fb_check_var(&fbinfo->var, fbinfo);7 S4 T. h4 v2 S

    ( h6 J# S7 G6 ~" L) K0 i        /* 注册fb_info结构体 */  f9 U0 N# o9 K5 i: Y8 E
            ret = register_framebuffer(fbinfo);
    " Y! I2 S+ U/ p4 b        if (ret < 0) {
    / q6 q! f. R& m; _2 Y% l' O                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    , K7 s9 s$ |# f1 B- \$ r! I                        ret);
    4 G* P0 |5 m4 U* i7 z& U                goto free_video_memory;
    * J. |, }% |2 |( L" `6 v: M$ o" }* w! }        }
    + F' g" S9 ?2 o
    & i  h6 z4 t# P/ Q, h        /* create device files */
    2 n; \  B! h& f" ^$ S9 B        ret = device_create_file(&pdev->dev, &dev_attr_debug);4 L; j" s3 G) J
            if (ret) {
    ! ?- [/ E. u* o8 t  W8 }                printk(KERN_ERR "failed to add debug attribute\n");% R1 W3 d# p, u. a
            }/ X- _9 e5 ~  b* B$ @

    0 c$ ]1 ~6 _+ {+ K  {
    & z0 }) A! W* r6 T+ @        /* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device  */3 h, S) ?% d+ ~0 p) o
            printk(KERN_INFO "fb%d: %s frame buffer device\n",& w& s: t- Q% w& G1 z& f
                    fbinfo->node, fbinfo->fix.id);$ s! m7 C9 Z# q3 b+ C) _

    - B1 r+ n# @6 }  r- l  ~8 Z7 W        return 0;
    ; @$ X6 ?) V; ^7 f* C  W* { 5 A! y, f9 d9 w
    free_video_memory:4 k+ I' ]& t/ ]% G2 ^
            s3c2410fb_unmap_video_memory(fbinfo);
    , @! }1 m) W+ I) p- Trelease_clock:
    / \7 h3 N: c6 w+ o        clk_disable(info->clk);                                /* 禁止lcd时钟 */! \  r; N, \6 i: `! {/ L, l: P; @
            clk_put(info->clk);                                        /* 删除lcd时钟 */% h8 @6 G7 F8 i* A
    release_irq:% u7 h3 W8 D$ T* u
            free_irq(irq, info);                                /* 释放IRQ */
    / M# A1 {3 I, a, zrelease_regs:5 a* L8 u  n! ?% n+ n3 M
            iounmap(info->io);                                        /* 解除映射 */, k% E: M! d/ P
    release_mem:
    0 E5 L7 B  y  ]6 ^' i) u        release_resource(info->mem);                /* 释放资源 */
    5 n8 g  p$ J' H# W# e- P) d        kfree(info->mem);                                        /* 释放刚申请的内存 */* U* X1 |  x1 f, o$ U
    dealloc_fb:: h& f/ ?6 ]$ D8 d' T
            platform_set_drvdata(pdev, NULL);        /* 相当于pdev->dev->driver_data = NULL */8 F* C1 ^, C4 Q8 G' K
            framebuffer_release(fbinfo);                /* 释放fb_info结构体 */3 Y4 n8 G: k" J
            return ret;3 v9 p. g4 f/ H
    }
    9 [, @0 a. ~6 s5 t. W0 S$ r% @  v拆分详解:
    : A$ [# B9 _( r一、获得平台数据* ~( G1 C9 ]$ B7 c# F
    + [4 Y; }3 [/ r5 L. m" r7 ]. v
    ; s6 O/ ?1 _0 e3 E: A
            mach_info = pdev->dev.platform_data;9 D' E6 S9 k' [4 K$ P

    % Q' T- b) R' ^& _8 D        /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL  */
    ' k. J% b3 H' y1 _% b% a7 G' F6 @: N        if (mach_info == NULL) {" a) S' v9 ^2 ?0 K4 ^0 y
                    dev_err(&pdev->dev,& Q, o# X5 T) S$ ^
                            "no platform data for lcd, cannot attach\n");# i7 @9 O2 d' w9 p, G
                    return -EINVAL;                /* 表示无效的参数 */
    9 u0 u" P" Z7 X( C) G# G- s        }" y' ^. |0 Z( S1 v/ I
    s3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
    5 o  B5 h8 J9 Z* C: |1 z; K所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
    6 j3 X3 e: B% F# z3 M
    5 Z& `, `7 G- |: D- jstatic void __init tq2440_machine_init(void)
    7 N- {  V( q: C0 F{
    " J% a  \8 }, P3 f        /* 初始化tq2440_fb_info实体结构体 */" n. {  X$ E. l6 u# V+ F2 {
            s3c24xx_fb_set_platdata(&tq2440_fb_info);
    % j# |9 v4 i- x* r        s3c_i2c0_set_platdata(NULL);
    / W3 L) r) Z$ Y! Z7 d" K 3 A5 r) a. m' z. X
            /* 添加tq2440_devices到内核,它是platform_device类的设备 */1 ~$ d3 ]$ ~* ~- E$ v9 R
            platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
    9 T3 T0 A% M8 P; H$ n        EmbedSky_machine_init();" D, _8 ?% E4 W( _6 l: M
            s3c2410_gpio_setpin(S3C2410_GPG12, 0);0 c, a+ w/ t2 ~* R3 q8 H+ S- \
            s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);, r) g% w* E# M* J  w
            s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);0 E" L1 N- `  g" g
    }/ u4 m& E% U0 U! o3 z5 x9 H
    s3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
    & S$ ^4 H" i7 }, ^% \1 y! r' A% ^# ?! b  s3 M
    void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
    . X3 e1 M) B# R: ^, S{; M* [2 Z" r, I
            struct s3c2410fb_mach_info *npd;; E- h/ \6 U. L5 L4 D' B$ }

    . `$ U8 C' y9 b        /* 申请分配s3c2410fb_mach_info大小的内存 */5 k" t2 o8 a, o0 H  G. I
            npd = kmalloc(sizeof(*npd), GFP_KERNEL);6 C4 `  A; \9 q* R
            if (npd) {& b2 F  e( Z/ X( a5 x6 g
                    /* 拷贝s3c2410fb_mach_info型实体给npd */
    & X# G8 C: Y+ X% w! m- [                mEMCpy(npd, pd, sizeof(*npd));
    ) V# e+ R" i+ U* k4 l$ |- e ' ]) _# B+ F7 X0 G, P+ Z
                    /* 最后将s3c2410fb_mach_info型实体赋给platform_data */* H9 g, @7 {8 }6 E6 N
                    s3c_device_lcd.dev.platform_data = npd;
    * R' L1 ^0 J. ?& s( E* |0 h+ ^        } else {
    - }( Q( a/ T* Q" L; E                printk(KERN_ERR "no memory for LCD platform data\n");$ ?+ S( f' E" N1 V
            }
    ' J- O- z$ E' n}
    9 Y4 N3 c* n8 e# F- v! v; c6 p6 ~二、设置s3c2410fb_display指向tq2440_lcd_cfg7 ?! v% k& K2 |7 p

    7 p5 ~7 ^" [! q% X        /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */% }* t; j( a5 r. K
            if (mach_info->default_display >= mach_info->num_displays) {
    4 k2 b& ^; x7 P* L2 t                dev_err(&pdev->dev, "default is %d but only %d displays\n",
    * ]1 @& `7 R" D+ O  [5 I                        mach_info->default_display, mach_info->num_displays);+ o% u; o( |0 \# C
                    return -EINVAL;
    / @; E1 u- q1 p( b) b        }! ]- b2 c( Q9 Y+ k4 i" o3 ^
    6 ^3 B5 J. r* A- f! w; ~/ p
            /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
    . ]5 s4 [$ I* C1 p4 j5 n        display = mach_info->displays + mach_info->default_display;
    2 O" s) I* Q" K) _6 c三、获得IRQ资源" l6 i: I4 k, U; l; `+ G" Q+ W! f
    . C8 e9 @2 Y+ G- N$ }
            irq = platform_get_irq(pdev, 0);
    5 E, M* ^1 s$ c6 K        if (irq < 0) {
    3 s: y% D$ Y1 m2 J                dev_err(&pdev->dev, "no irq for device\n");
    4 C( E$ {4 P$ t2 n7 `' I1 }( J                return -ENOENT;
    % ^) |1 ^$ E* y, x  R) b9 O        }6 e+ \7 y  K1 ?: V/ T, w4 b
    四、分配fb_info内存
    7 q: v; ^. I6 I/ {" u! z+ K- t# P5 hfbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);) F' D+ K' ]9 S  M# h
            if (!fbinfo)  T  l4 ?" y0 y$ S. R7 ?
                    return -ENOMEM;                /* 返回NULL表示失败 */0 M5 _1 Z1 B4 W1 d' d
    framebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。# e* A8 x+ E/ A+ U7 x
    五、设置s3c2410fb_info结构体
    ' N& A+ k, a' Q, v) ^  x
    8 H) n2 a; f" q, T/* 相当于pdev->dev->driver_data = fbinfo */
    9 f: Y. y/ x6 d* Z2 G        platform_set_drvdata(pdev, fbinfo);# G( s2 j% v) ?
    7 Q. ]( U; c( P5 `2 N% k1 {) R1 `
            /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
    1 F/ @( }/ C9 u/ Z        info = fbinfo->par;                        /* 将私有数据赋给info指针变量 */
    8 q& m. |- r# F0 |4 o  T* _$ ?) N        info->dev = &pdev->dev;                /* 指定struct s3c2410fb_info中dev为平台设备中的dev */" N  Y; c9 w5 l- s
            info->drv_type = drv_type;        /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
      X0 ^  {5 Q2 o' [9 ^) W六、获取IO资源,映射IO, j* r4 W6 y1 e3 f+ a: s

      k8 ?' [& U0 G1 S5 L, }/*  通过平台设备platform_device获得资源(IO) */
    4 b+ a3 @$ X' T        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  c/ `  x! t7 E1 b/ y  {
            if (res == NULL) {. ]/ \  j3 P- f. }+ t; B6 e
                    dev_err(&pdev->dev, "failed to get memory registers\n");
    - H& ]. Z& C  S! ]/ p2 z* Y* f5 N                ret = -ENXIO;
    , V: ?: q: f! @  V3 x1 h' b                goto dealloc_fb;
    4 x, F! m( {8 ~$ R; C* R& B. g, S1 m. J! K        }
      @$ D1 n. N# a4 V" [" Y1 Y" X9 H. j / W+ Z" l' Y6 i5 @' V' U; O. Y
            size = (res->end - res->start) + 1;                /* 资源的大小 */  O  r  R3 S  F: p- ?( }

    0 R* ?. U6 I6 y. q3 j2 _" v* w. {3 _        /* 申请以res->start地址开始大小为size的I/O内存 */
    & ^. v1 B1 b# G  d8 i        info->mem = request_mem_region(res->start, size, pdev->name);8 c, X7 O2 m1 ?! g
            if (info->mem == NULL) {. p5 b/ Y. @3 O
                    dev_err(&pdev->dev, "failed to get memory region\n");& `3 R1 W3 l% f( [0 }) {
                    ret = -ENOENT;& h+ D' g! U/ N$ B
                    goto dealloc_fb;9 P: k0 `! r4 k* k6 l, u; d8 r
            }
    / p, _' k/ h0 A" v" X: h 0 {  L* `! }0 D. k! o  I& ~
            /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
    8 Z, x/ V# u# o$ z& }2 K6 W/ J        info->io = ioremap(res->start, size);
    0 \3 P$ i3 q# Z7 R: b* p        if (info->io == NULL) {0 z; Z2 J7 ?9 e0 o+ |3 O3 S" e
                    dev_err(&pdev->dev, "ioremap() of registers failed\n");6 V, R9 ]+ g" z( @% ~
                    ret = -ENXIO;
    / f/ k7 q% ^1 N6 m2 H6 ]* F7 k                goto release_mem;
    " p6 o2 o: \- E! c3 @        }
    # O; D$ t$ I1 S6 K4 T+ t6 f
    , G0 p+ V  F* S        /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
    * Z( ~% z' e9 {8 l# b% U        info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
    5 J* d- h& ]5 x1 L3 ~7 F七、读写LCDCON1,禁止视频数据输出
    * Z7 {% I6 P/ i( E1 v# j
    7 X. L/ h% M$ a- e        /* Stop the video */
    & p% S: q0 L8 T5 D. g        lcdcon1 = readl(info->io + S3C2410_LCDCON1);, T3 ~% v; C$ _2 _. u" h
            /* 禁止Video output */
    2 [0 B  G- w: E% W8 i' B        writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);" L6 h, H, d2 l* N
    八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
    2 c5 f- m, y  x6 C- V8 k
    ' @" ]& b3 C$ T% v) I/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */( R( I* m+ B3 v; b
            fbinfo->fix.type                           = FB_TYPE_PACKED_PIXELS;
    ' @$ p6 \+ K7 F/ b$ `3 Z        fbinfo->fix.type_aux            = 0;
    # t' R, n0 F) }& B. E        fbinfo->fix.xpanstep            = 0;: Z- m0 S7 h) s9 u
            fbinfo->fix.ypanstep            = 0;. V) H) P6 t- Z# e; H" A
            fbinfo->fix.ywrapstep            = 0;
    8 a1 l( j# I' v+ T  o; E6 G- x8 v        fbinfo->fix.accel                    = FB_ACCEL_NONE;        /* 无硬件加速 */
    $ i- d4 B4 Q: U0 E6 r7 K
    . [$ A3 p7 J* C9 o3 ]6 ]3 x        /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */; d" o& e0 N6 ^0 t
            fbinfo->var.nonstd                    = 0;
    $ \4 x5 T3 d' F5 E        fbinfo->var.activate            = FB_ACTIVATE_NOW;8 I6 G0 Q# H3 \9 N" T
            fbinfo->var.accel_flags     = 0;1 l2 w' t1 o8 V7 f% K
            fbinfo->var.vmode                    = FB_VMODE_NONINTERLACED;
    ' q3 z6 c, t' N4 g/ t  ~, y
    ; |  U  X7 Y$ R4 R3 Z        /* 设置fb_ops结构体 */
    ( q) F5 N& o5 X, M/ Z3 U        fbinfo->fbops                            = &s3c2410fb_ops;
    ; g6 z5 n. {: {' S5 r        
      o& B7 v1 H8 f3 i, g( H. ?' o        fbinfo->flags                            = FBINFO_FLAG_DEFAULT;; a; n3 C+ |% ~9 ?. ]$ r

    # Q! h- z" s% i; J6 O, R) i        /* 设置假调色板 *// w" j$ Y6 x3 r9 Q% }% W( B8 g
            fbinfo->pseudo_palette      = &info->pseudo_pal;
    9 F2 Y" i, H+ I7 }; z! n
    2 ?; ]! a* \& W1 \0 W        /* palette_buffer = 0x80000000,清空调色板 */2 y) m4 o1 u+ H% I
            for (i = 0; i < 256; i++)- L4 t+ W) t) S' H) Q# {
                    info->palette_buffer = PALETTE_BUFF_CLEAR;/ f$ \5 R9 ]  l6 F1 a* D
    九、申请中断、获取LCD时钟,使能LCD时钟' i6 p6 S! ]+ {2 Z2 _. \
            /* 申请中断,s3c2410fb_irq是中断处理函数 */7 U4 f3 f# I$ b1 r  ~
            ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
    3 f8 L; ^. G3 z9 P        if (ret) {0 R* h; a& V  s$ P0 ~' w
                    dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);8 b: E1 C2 M" x4 d
                    ret = -EBUSY;
    & }" z. w! D6 q  ?3 p                goto release_regs;
      C% X0 m. R3 C/ Q( p        }
    3 O/ p- ~/ z( z" V+ ^
    ; [. F) g5 V2 L, @% J        /* 获取lcd时钟 */
    7 O1 Z, i8 `0 ~3 f( @8 z        info->clk = clk_get(NULL, "lcd");
    . @5 H, H0 w7 f& R        if (!info->clk || IS_ERR(info->clk)) {
    ) ^( y: O. L6 E1 l+ g1 @+ m                printk(KERN_ERR "failed to get lcd clock source\n");; Z$ l3 }1 ^% M0 l+ o7 \8 ?0 W
                    ret = -ENOENT;7 x# @* r* I  Z, ^  \6 [
                    goto release_irq;
    + M, |+ T% Z1 ~( F" T        }
    7 o3 h; M" W' c7 i7 [0 W# \+ {
    0 _+ F3 J# J# N; M& q; C        /* 使能lcd时钟 */0 B- m2 M) r. g' P
            clk_enable(info->clk);               
    6 t# G, v* ^: G) C, s        dprintk("got and enabled clock\n");
    3 T- ]& z6 C* B. \十、计算显存大小、分配显存内存5 z1 D; |0 C1 h" I0 ~/ a- V* Y
    ! N/ F& H( e: {
            /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,2 m: L: v2 v. s1 [1 l
         * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。& a) H- ?, H' e: p* O' f
         * 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。/ A, s, e. R* G, K( V( [
             */5 N+ i7 e7 ~! K: _, _! e' K
            for (i = 0; i < mach_info->num_displays; i++) {                          /* 这里mach_info->num_displays = 1 */
    % H$ u$ W& ]  D  Q1 r                unsigned long smem_len = mach_info->displays.xres;        /* x方向分辨率 */3 O9 E2 K& p& h% e4 ~( N
    ! C9 r% O7 p3 b7 H7 j2 B
                    smem_len *= mach_info->displays.yres;                                /* y方向分辨率 */
    " T) u! k/ J8 O7 i                smem_len *= mach_info->displays.bpp;                                        /* bpp */( k5 F' M3 W7 R2 Q  T
                    smem_len >>= 3;                                                                                        /* smem_len除以8 */$ w: m0 d* L) Z. r: B0 b8 _
                    if (fbinfo->fix.smem_len < smem_len)9 b: ]8 D& c# y; u5 E
                            fbinfo->fix.smem_len = smem_len;
    8 }0 Q- ?" _; V; J% R6 ~4 `; d        }
    ' l) X* S1 f6 f   L: X' ?8 |! V( t" g
            /* Initialize video memory */* Q2 u- K% O/ X. n; Z. S
            ret = s3c2410fb_map_video_memory(fbinfo);        /* 分配显存 */
    9 R$ R% b' A9 I$ m2 _+ W2 o" l- v        if (ret) {; P( s; }8 p1 S' {# ~+ {) h  w
                    printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
    3 l' j9 O6 b7 G7 h: g9 D                ret = -ENOMEM;" \8 ]' A5 v( U/ g3 Y
                    goto release_clock;# B% p& P0 q3 h+ l' C% D
            }/ e: h- u: `8 b0 e! s) D+ V9 [" l
    十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
    4 f$ n' C+ G+ d. T& k; g+ w
    ( N. C& }7 m( L, Q- h; m        /* display指向tq2440_lcd_cfg */
    2 Z7 e: Y! o4 R/ Q# y        fbinfo->var.xres = display->xres;                        /* 设置x方向的分辨率 */" ]1 z' E" Y, ?' z3 j
            fbinfo->var.yres = display->yres;                        /* 设置y方向的分辨率 */
    , }$ t7 |$ H/ v; G9 E( @1 i( v        fbinfo->var.bits_per_pixel = display->bpp;        /* 设置bpp位数 */
    / }/ |; Z: v" @% J% c" O" s十二、LCD相关寄存器的设置和fb_info的可变参数的检测1 [+ \* @$ `/ q' @+ _4 P
    " m! \& i( m4 w: f- m$ M
    /* 初始化LCD相关的寄存器 */
      K! s" L  Z) q4 U4 |5 _        s3c2410fb_init_registers(fbinfo);
    1 P+ e3 P$ f" t/ m* r+ [
    3 Q7 b0 P& Q/ z! `        /* 检查可变参数 */  `5 e, l+ q% e  q% k! B
            s3c2410fb_check_var(&fbinfo->var, fbinfo);; m% _, V3 Y' N
    十三、注册fb_info结构体+ P. |5 b7 g; K* q4 l- b' h

    6 ?5 n& l. @6 y3 F8 B        /* 注册fb_info结构体 */  m( e9 ^7 f  _% y6 {+ i
            ret = register_framebuffer(fbinfo);4 o) W: F6 V' P+ Z7 g/ K
            if (ret < 0) {5 S$ ]% E8 R! \5 @
                    printk(KERN_ERR "Failed to register framebuffer device: %d\n",
    + T  ]; D. c* R0 f7 i' ?3 X# N                        ret);$ n$ s9 S0 O+ v! @4 o$ }$ a
                    goto free_video_memory;% o5 {3 B! }, q# _0 R( V# b! t2 Q- N
            }
    8 G7 `0 E* G4 c, N
      Z, `/ e: S" |& h

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-10-26 10:14 , Processed in 0.171875 second(s), 24 queries , Gzip On.

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

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

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