Tiny4412中配置Camera接口时钟
来源:互联网 发布:金融网络销售靠谱吗 编辑:程序博客网 时间:2024/04/30 11:23
由于Tiny4412是一块新出的开发板,资料比较少,本人又是小白,因此对于Camera的时钟配置没什么概念。没办法,只能从源码看起。
时钟驱动简述
Tiny4412的时钟配置源码在 /arch/arm/plat-samsung/clock.c、/arch/arm/plat-samsung/clock-clksrc.c 和 /arch/arm/mach-exynos/clock-exynos4.c下。
clock.c文件主要实现了Linux时钟接口的一些函数指针,这些指针的声明与统一调用在 include/linux/clk.h 和 drivers/clk/clkdev.c中,实际上系统通过这两个文件中实现的通用方法,最终会调用注册时钟的方法。比如调用clk_set_rate方法最终实际是调用了clk->ops->set_rate方法,这是Linux系统的驱动分层设计思想,在很多驱动中都很常见,这里就不说了。
clock-clksrc.c文件比较小,主要是实现了时钟注册的方法
clock-exynos4.c文件中声明了所有在CPU中能够使用的时钟源和配置寄存器,与芯片datasheet对应(但是没有用ioremap,貌似是给出偏移量来进行IO空间映射的,不知道这个偏移量是怎么算出来的)
三星平台关于时钟的主要结构体定义与顶层函数声明在/arch/arm/plat-samsung/include/plat/clock.h下
主要看的就是这几个源文件了。
首先来分析一下时钟是如何注册的,三星的时钟声明和注册代码在clock-exynos4.c下,主要关注最后一个函数:exynos4_register_clocks,这个函数中完成了注册操作。三星声明的clk结构体有两种,一种是单纯的clk结构体,一种是clksrc结构体。单纯的clk结构体从源码中可以看到,主要用于控制时钟的使能(对应CLK_GATE_xxx等寄存器),只是实现了enable方法,并没有实现set_rate, get_rate等方法,他们只是单纯的开关作用,调用方式为clk_enable(xxxx);
另一类为clksrc_clk结构体,这个结构体中不仅有clk结构体声明了时钟名、enable方法(对应CLK_MASK_XXX寄存器的控制),并且给出了时钟源寄存器(对应CLK_SRC_XXX)和分频寄存器(对应CLK_DIV_XXX),还给出了每个时钟的上层与他相关的时钟。因为要使能一个时钟必须要使能它上层与他相关的所有时钟,因此它采用了一个数组的形式,将所有可能的上层时钟罗列,在注册之前通过读取寄存器配置,可以选择它的上层时钟。这也是为什么不能直接配置时钟寄存器的原因,若只配置了芯片时钟寄存器,然而clk结构体内部的上层时钟的依赖关系并没有发生变化,因此最终得到的时钟配置仍然是错误的!
了解了这些之后,可以看看时钟的注册了,注册无非是将所有clk结构体信息添加至一个链表的尾部,使得系统保存所有clk配置的备份指针,从而在需要的时候可以通过查询链表获得这些clk指针。
注册也分为两种,一种是简单的clk结构体注册,注册函数为
int s3c24xx_register_clock(struct clk *clk){ if (clk->enable == NULL) clk->enable = clk_null_enable; /* fill up the clk_lookup structure and register it*/ clk->lookup.dev_id = clk->devname; clk->lookup.con_id = clk->name; clk->lookup.clk = clk; clkdev_add(&clk->lookup); return 0;}直接将名字添加至clk->lookup,并将这个指针加入链表后面。注意,Tiny4412下的所有clk结构体时钟是没有devname的,只有对应的name,在调用的时候传入devname=NULL
另一种是clksrc_clk的注册,这个注册方式比较麻烦,
void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size){ int ret; for (; size > 0; size--, clksrc++) { if (!clksrc->reg_div.reg && !clksrc->reg_src.reg) printk(KERN_ERR "%s: clock %s has no registers set\n", __func__, clksrc->clk.name); /* fill in the default functions */ if (!clksrc->clk.ops) { if (!clksrc->reg_div.reg) clksrc->clk.ops = &clksrc_ops_nodiv; else if (!clksrc->reg_src.reg) clksrc->clk.ops = &clksrc_ops_nosrc; else clksrc->clk.ops = &clksrc_ops; } /* setup the clocksource, but do not announce it * as it may be re-set by the setup routines * called after the rest of the clocks have been * registered */ s3c_set_clksrc(clksrc, false); ret = s3c24xx_register_clock(&clksrc->clk); if (ret < 0) { printk(KERN_ERR "%s: failed to register %s (%d)\n", __func__, clksrc->clk.name, ret); } } }传入的参数是一个clksrc_clk数组,依次进行注册,在每次注册时需要指定时钟的操作函数指针即clk.ops结构体,Tiny4412中所有的clksrc_clk初始都没有指定clk.ops参数,指针都需要在注册的过程中传入,因此所有的clksrc_clk结构体最终的操作函数指针都是相同的clksrc_ops。下面看看clksrc_ops中做了什么:
static struct clk_ops clksrc_ops = { .set_parent = s3c_setparent_clksrc, .get_rate = s3c_getrate_clksrc, .set_rate = s3c_setrate_clksrc, .round_rate = s3c_roundrate_clksrc,};给出了时钟的四种操作,对应上层函数中的,以set_rate函数指针为例:
static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate){ struct clksrc_clk *sclk = to_clksrc(clk); void __iomem *reg = sclk->reg_div.reg; unsigned int div; u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); u32 val; rate = clk_round_rate(clk, rate); div = clk_get_rate(clk->parent) / rate; if (div > (1 << sclk->reg_div.size)) return -EINVAL; val = __raw_readl(reg); val &= ~mask; val |= (div - 1) << sclk->reg_div.shift; __raw_writel(val, reg); return 0;}代码中计算了时钟的分频系数,并重新将它写入分频寄存器。这是一个通用的方法,也是Linux顶层调用的最终实现。
注意,将clksrc_ops赋值的操作只在clksrc_clk结构体注册的时候才会有,因此只Linux顶层调用的这些函数只能获取并操作clksrc_clk中的clk,这充分说明了普通的clk结构体声明的时钟只是一个开关作用,只能用clk_enable()方法调用。
s3c_set_clksrc()根据时钟源寄存器(CLK_SRC_XXX)中的值指定了每个clk结构体的parent参数。具体过程参见源码。
最后就是普通的注册时钟了。
至此时钟注册完毕。
Linux上层的调用:
clk.h文件中给出了Linux上层调用的规范函数名:
struct clk *clk_get(struct device *dev, const char *id);int clk_enable(struct clk *clk);void clk_disable(struct clk *clk);unsigned long clk_get_rate(struct clk *clk);void clk_put(struct clk *clk);long clk_round_rate(struct clk *clk, unsigned long rate);int clk_set_rate(struct clk *clk, unsigned long rate);int clk_set_parent(struct clk *clk, struct clk *parent);struct clk *clk_get_parent(struct clk *clk);struct clk *clk_get_sys(const char *dev_id, const char *con_id);int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,struct device *dev);
而具体的实现则在不同的平台下有所不同,Tiny4412平台下这些函数的实现在/arch/arm/plat-samsung/clock.c下,同样以clk_set_rate进行说明
int clk_set_rate(struct clk *clk, unsigned long rate){ int ret; if (IS_ERR(clk)) return -EINVAL; /* We do not default just do a clk->rate = rate as * the clock may have been made this way by choice. */ WARN_ON(clk->ops == NULL); WARN_ON(clk->ops && clk->ops->set_rate == NULL); if (clk->ops == NULL || clk->ops->set_rate == NULL) return -EINVAL; spin_lock(&clocks_lock); ret = (clk->ops->set_rate)(clk, rate); spin_unlock(&clocks_lock); return ret;}
只是简单地调用了传入clk对象中ops->set_rate方法,也就是上文的s3c_setrate_clksrc函数。
时钟寄存器的操作整个的流程就是这样了。要点为:
1. 注册时钟后ops指针绑定了一系列方法
2. 调用Linux顶层函数时同时实际就是调用这些方法
那么应该如何获得作为参数传入的clk指针呢?
clk_get提供了一个方法来通过时钟名和设备名获得一个clk指针,根据clock-exynos4.c文件中时钟注册时使用的name和devname作为参数传入,就能够返回对应的clk×指针。这个函数实际上是便利整个时钟链表,通过比较name和devname,返回第一个完整匹配的时钟指针,这部分比较简单,可以参看clk_get的源代码。
下面给出Tiny4412配置Camera时钟的源代码:首先是配置GPIO
GPJ0CON_MAP = ioremap(GPJ0CON,4); GPJ1CON_MAP = ioremap(GPJ1CON,4); GPJ0PUD_MAP = ioremap(GPJ0PUD,4); GPJ1PUD_MAP = ioremap(GPJ1PUD,4); /*Set GPJ as Camera interface*/// printk("%s():GPJ0CON_MAP = 0x%x\n",__func__,readl(GPJ0CON_MAP));// printk("%s():GPJ1CON_MAP = 0x%x\n",__func__,readl(GPJ1CON_MAP)); writel(0x22222222,GPJ0CON_MAP); writel(0x00022222,GPJ1CON_MAP);
struct device my_device = { .init_name = "exynos4-fimc.0", }; clk_set_rate(clk_get(NULL,"sclk_cam0"),24000000); cam_clk = clk_get(&my_device,"fimc"); clk_enable(clk_get(NULL,"sclk_cam0")); if(IS_ERR(cam_clk)){ printk("%s():clk_get failed!\n",__func__); } else{ printk("%s():clk_get succeed!\n",__func__); if(clk_enable(cam_clk)) printk("clk enable failed\n"); else printk("clk enable succeed\n"); } printk("cam clk rate is %lu\n",clk_get_rate(clk_get(NULL,"sclk_cam0")));这样就成功配置了Camera接口时钟,并在mclk管脚产生了24MHz时钟
- Tiny4412中配置Camera接口时钟
- tiny4412 时钟测试
- tiny4412 时钟测试
- 第四章、TIny4412 U-BOOT移植四 配置时钟频率源码分析
- tiny4412配置kernel
- tiny4412 配置dm9621网卡
- Camera接口
- tiny4412中JNI层问题
- 第三章、Tiny4412 U-BOOT移植三 时钟设置
- Android Camera中无法回调PictureCallback接口onPictureTaken()函数
- camera中简易图像转换,图像压缩接口记录
- 时钟配置
- 2440 camera接口
- camera接口介绍
- camera接口介紹
- 2440 camera接口
- android camera接口介绍
- android camera接口介绍
- Linux软件的安装、卸载
- Struts2<s:date>标签使用详解
- 封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度
- 清除WebSphere中缓存
- 机房收费系统之关系图
- Tiny4412中配置Camera接口时钟
- 简单的题目4
- 基类指针转换为子类指针,子类指针转换为基类指针
- 封装,capsulation,&&继承,Inheritance,&&多态,polymorphism
- hdu 2159 FATE
- 无限的路_hdu_2073(AC).java
- MFC,进入主对话框时,首先打开登录对话框
- android使用隐藏api的方法(使用被@hide的api)
- OC中的变量总结