4.0 ipu_soc,ipu_channel_t ,ipu_channel_params_t结构体详解
1.ipu_soc结构体:
struct ipu_soc {
-
unsigned int id; //ipu的ID号
-
unsigned int devtype; //ipu的一些信息,包含cm,ic等模块的地址偏移值
-
bool online; //表示这个ipu是否正在使用中
-
-
/*clk*/
-
struct clk *ipu_clk;
-
struct clk *di_clk[2];
-
struct clk *di_clk_sel[2];
-
struct clk *pixel_clk[2];
-
bool pixel_clk_en[ 2];
-
struct clk *pixel_clk_sel[2];
-
struct clk *csi_clk[2];
-
struct clk *prg_clk;
-
-
/*irq*/
-
int irq_sync;
-
int irq_err;
-
struct ipu_irq_node irq_list[IPU_IRQ_COUNT]; //在request_irq函数中会根据传入的irq号在这个数组中选择对应的下标,来存取有关这个irq的
-
//信息如irq服务函数,名字,flags等参数。
-
-
/*reg*/ /* ipu内部模块经过ioremap后的地址 */
-
void __iomem *cm_reg;
-
void __iomem *idmac_reg;
-
void __iomem *dp_reg;
-
void __iomem *ic_reg;
-
void __iomem *dc_reg;
-
void __iomem *dc_tmpl_reg;
-
void __iomem *dmfc_reg;
-
void __iomem *di_reg[ 2];
-
void __iomem *smfc_reg;
-
void __iomem *csi_reg[ 2];
-
void __iomem *cpmem_base;
-
void __iomem *tpmem_base;
-
void __iomem *vdi_reg;
-
-
struct device *dev;
-
-
ipu_channel_t csi_channel[ 2]; //每个ipu有两个csi设备,将此时csi设备对应的channel根据csi号保存在这个csi_channel[]数组中
-
ipu_channel_t using_ic_dirct_ch; //表示那个channel直接使用IC设备
-
unsigned char dc_di_assignment[ 10];
-
bool sec_chan_en[ 24]; //对应的channel是否使能了second channel
-
bool thrd_chan_en[ 24]; //对应的channel是否使能了third channel
-
bool chan_is_interlaced[ 52]; //对应的channel中的数据是否是隔行的,每个channel对应其中的一位,每一位是一个bool类型的值
-
uint32_t channel_init_mask; //这是一个32位的数,其中每一位代表一个channel号,如果初始化一个channel的话,就将这个channel对应的位置1
-
uint32_t channel_enable_mask; //每一位对应一个channel号,如果使能了一个channel 的话,就将这个channel对应的位置1,与上面那个channel_init_mask类似。
-
-
/*use count*/ /* 下面几个是IPU内部模块的引用计数 */
-
int dc_use_count; //dc引用计数
-
int dp_use_count; //dp引用计数
-
int dmfc_use_count; //dmfc引用计数
-
int smfc_use_count; //smfc引用计数
-
int ic_use_count; //ic引用计数
-
int rot_use_count; //rot引用计数
-
int vdi_use_count; //vdi引用计数
-
int di_use_count[ 2]; //di引用计数,每个ipu只有两个di
-
int csi_use_count[ 2]; //csi引用计数,每个ipu只有两个csi
-
-
struct mutex mutex_lock;
-
spinlock_t int_reg_spin_lock;
-
spinlock_t rdy_reg_spin_lock;
-
-
int dmfc_size_28;
-
int dmfc_size_29;
-
int dmfc_size_24;
-
int dmfc_size_27;
-
int dmfc_size_23;
-
-
enum csc_type_t fg_csc_type;
-
enum csc_type_t bg_csc_type;
-
bool color_key_4rgb;
-
bool dc_swap;
-
struct completion dc_comp;
-
struct completion csi_comp;
-
-
struct rot_mem {
-
void *vaddr;
-
dma_addr_t paddr;
-
int size;
-
} rot_dma[ 2];
-
-
int vdoa_en; //是否使能VDOA
-
struct task_struct *thread[2]; //两个内核线程
-
-
/*
-
* Bypass reset to avoid display channel being
-
* stopped by probe since it may starts to work
-
* in bootloader.
-
*/
-
bool bypass_reset; /* 这个值是从dts文件中获得的bypass_reset,因为在开发板启动过程中,需要使能屏幕来显示,所以显示通道可能已经在bootloader中开启了,
-
* 在这里设置这个值,使得显示通道在probe函数中不会关闭,也就是这个参数的含义(旁路)。*/
-
-
/* AXI protocol id */
-
unsigned int ch0123_axi;
-
unsigned int ch23_axi;
-
unsigned int ch27_axi;
-
unsigned int ch28_axi;
-
unsigned int normal_axi;
-
/* 不同的channel可能会对应不同的 AXI protocol,在ipu_probe函数中对这些值进行了赋值,在ipu_platform_type结构体中指定的。 */
-
-
bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */
-
};
2.ipu_channel_t枚举:
typedef enum {
-
CHAN_NONE = -1,
-
MEM_ROT_ENC_MEM = _MAKE_CHAN( 1, 45, NO_DMA, NO_DMA, 48),
-
MEM_ROT_VF_MEM = _MAKE_CHAN( 2, 46, NO_DMA, NO_DMA, 49),
-
MEM_ROT_PP_MEM = _MAKE_CHAN( 3, 47, NO_DMA, NO_DMA, 50),
-
-
MEM_PRP_ENC_MEM = _MAKE_CHAN( 4, 12, 14, 17, 20),
-
MEM_PRP_VF_MEM = _MAKE_CHAN( 5, 12, 14, 17, 21),
-
MEM_PP_MEM = _MAKE_CHAN( 6, 11, 15, 18, 22),
-
-
MEM_DC_SYNC = _MAKE_CHAN( 7, 28, NO_DMA, NO_DMA, NO_DMA),
-
MEM_DC_ASYNC = _MAKE_CHAN( 8, 41, NO_DMA, NO_DMA, NO_DMA),
-
MEM_BG_SYNC = _MAKE_CHAN( 9, 23, NO_DMA, 51, NO_DMA),
-
MEM_FG_SYNC = _MAKE_CHAN( 10, 27, NO_DMA, 31, NO_DMA),
-
-
MEM_BG_ASYNC0 = _MAKE_CHAN( 11, 24, NO_DMA, 52, NO_DMA),
-
MEM_FG_ASYNC0 = _MAKE_CHAN( 12, 29, NO_DMA, 33, NO_DMA),
-
MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
-
MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
-
-
DIRECT_ASYNC0 = _MAKE_CHAN( 13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
-
DIRECT_ASYNC1 = _MAKE_CHAN( 14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
-
-
CSI_MEM0 = _MAKE_CHAN( 15, NO_DMA, NO_DMA, NO_DMA, 0),
-
CSI_MEM1 = _MAKE_CHAN( 16, NO_DMA, NO_DMA, NO_DMA, 1),
-
CSI_MEM2 = _MAKE_CHAN( 17, NO_DMA, NO_DMA, NO_DMA, 2),
-
CSI_MEM3 = _MAKE_CHAN( 18, NO_DMA, NO_DMA, NO_DMA, 3),
-
-
CSI_MEM = CSI_MEM0,
-
-
CSI_PRP_ENC_MEM = _MAKE_CHAN( 19, NO_DMA, NO_DMA, NO_DMA, 20),
-
CSI_PRP_VF_MEM = _MAKE_CHAN( 20, NO_DMA, NO_DMA, NO_DMA, 21),
-
-
/* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
-
MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN( 21, 8, 14, 17, 21),
-
MEM_VDI_PRP_VF_MEM = _MAKE_CHAN( 22, 9, 14, 17, 21),
-
MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN( 23, 10, 14, 17, 21),
-
-
/* for vdi mem->vdi->mem */
-
MEM_VDI_MEM_P = _MAKE_CHAN( 24, 8, NO_DMA, NO_DMA, 5),
-
MEM_VDI_MEM = _MAKE_CHAN( 25, 9, NO_DMA, NO_DMA, 5),
-
MEM_VDI_MEM_N = _MAKE_CHAN( 26, 10, NO_DMA, NO_DMA, 5),
-
-
/* fake channel for vdoa to link with IPU */
-
MEM_VDOA_MEM = _MAKE_CHAN( 27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
-
-
MEM_PP_ADC = CHAN_NONE,
-
ADC_SYS2 = CHAN_NONE,
-
-
} ipu_channel_t;
再来看看这个_MAKE_CHAN宏:
# _MAKE_CHAN(num, v_in, g_in, a_in, out) \
-
((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
-
从这里就可以看出来,ipu_channel_t只是根据5个值左移形成的一个数字。
那么想要从channel中获取它的ID号怎么办?只需要将它右移24位即可,就能得到,就是下面一个宏:
#define IPU_CHAN_ID(ch) (ch >> 24)
再来看看channel_2_dma函数:
static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
-
{
-
return (( uint32_t) ch >> ( 6 * type)) & 0x3F;
-
};
这个函数能够根据不同的type类型从channel里面提取出所使用的dmachannel。
typedef enum {
-
IPU_OUTPUT_BUFFER = 0, /*!< Buffer for output from IPU */
-
IPU_ALPHA_IN_BUFFER = 1, /*!< Buffer for input to IPU */
-
IPU_GRAPH_IN_BUFFER = 2, /*!< Buffer for input to IPU */
-
IPU_VIDEO_IN_BUFFER = 3, /*!< Buffer for input to IPU */
-
IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
-
IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
-
} ipu_buffer_t;
同时根据_MAKE_CHAN宏中几个变量的名字,就能够理解ipu_channel_t中各个位的含义:
0~5位:输出dmachannel号
6~11位:alpha通道号
12~17位:graph通道号
18~23位:video输入dmachannel号
24~ :channel的序列号
3. ipu_channel_params_t联合:
typedef union {
-
struct {
-
uint32_t csi; //csi设备号,0或1
-
uint32_t mipi_id; //mipi ID号
-
uint32_t mipi_vc; //mipi虚拟通道号
-
bool mipi_en; //是否使能mipi接口
-
bool interlaced; //数据是否是隔行的
-
} csi_mem;
-
struct {
-
uint32_t in_width; //输入数据的宽度
-
uint32_t in_height; //输入数据的高度
-
uint32_t in_pixel_fmt; //输入数据的像素格式
-
uint32_t out_width; //输出数据的宽度
-
uint32_t out_height; //输出数据的高度
-
uint32_t out_pixel_fmt; //输出数据的像素格式
-
uint32_t outh_resize_ratio; //输出水平方向上重定义大小系数
-
uint32_t outv_resize_ratio; //输出垂直方向上重定义大小系数
-
uint32_t csi; //csi 设备号
-
uint32_t mipi_id; //mipi ID号
-
uint32_t mipi_vc; //mipi 虚拟通道号
-
bool mipi_en; //是否是能mipi接口
-
} csi_prp_enc_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
uint32_t outh_resize_ratio;
-
uint32_t outv_resize_ratio;
-
} mem_prp_enc_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
} mem_rot_enc_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
uint32_t outh_resize_ratio;
-
uint32_t outv_resize_ratio;
-
bool graphics_combine_en;
-
bool global_alpha_en;
-
bool key_color_en;
-
uint32_t in_g_pixel_fmt;
-
uint8_t alpha;
-
uint32_t key_color;
-
bool alpha_chan_en;
-
ipu_motion_sel motion_sel;
-
enum v4l2_field field_fmt;
-
uint32_t csi;
-
uint32_t mipi_id;
-
uint32_t mipi_vc;
-
bool mipi_en;
-
} csi_prp_vf_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
bool graphics_combine_en;
-
bool global_alpha_en;
-
bool key_color_en;
-
display_port_t disp;
-
uint32_t out_left;
-
uint32_t out_top;
-
} csi_prp_vf_adc;
-
struct {
-
uint32_t in_width; //输入宽度
-
uint32_t in_height; //输入高度
-
uint32_t in_pixel_fmt; //输入像素格式
-
uint32_t out_width; //输出宽度
-
uint32_t out_height; //输出高度
-
uint32_t out_pixel_fmt; //输出像素格式
-
uint32_t outh_resize_ratio; //输出水平方向上重定义大小的比例
-
uint32_t outv_resize_ratio; //输出垂直方向上重定义大小的比例
-
bool graphics_combine_en; //是否使能second channel的标志位
-
bool global_alpha_en; //是否使能third channel的标志位
-
bool key_color_en;
-
uint32_t in_g_pixel_fmt;
-
uint8_t alpha;
-
uint32_t key_color;
-
bool alpha_chan_en;
-
ipu_motion_sel motion_sel;
-
enum v4l2_field field_fmt;
-
} mem_prp_vf_mem;
-
struct {
-
uint32_t temp;
-
} mem_prp_vf_adc;
-
struct {
-
uint32_t temp;
-
} mem_rot_vf_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
uint32_t outh_resize_ratio;
-
uint32_t outv_resize_ratio;
-
bool graphics_combine_en;
-
bool global_alpha_en;
-
bool key_color_en;
-
uint32_t in_g_pixel_fmt;
-
uint8_t alpha;
-
uint32_t key_color;
-
bool alpha_chan_en;
-
} mem_pp_mem;
-
struct {
-
uint32_t temp;
-
} mem_rot_mem;
-
struct {
-
uint32_t in_width;
-
uint32_t in_height;
-
uint32_t in_pixel_fmt;
-
uint32_t out_width;
-
uint32_t out_height;
-
uint32_t out_pixel_fmt;
-
bool graphics_combine_en;
-
bool global_alpha_en;
-
bool key_color_en;
-
display_port_t disp;
-
uint32_t out_left;
-
uint32_t out_top;
-
} mem_pp_adc;
-
struct {
-
uint32_t di;
-
bool interlaced; //数据是否是隔行的
-
uint32_t in_pixel_fmt;
-
uint32_t out_pixel_fmt;
-
} mem_dc_sync;
-
struct {
-
uint32_t temp;
-
} mem_sdc_fg;
-
struct {
-
uint32_t di;
-
bool interlaced; //数据是否是隔行的
-
uint32_t in_pixel_fmt;
-
uint32_t out_pixel_fmt;
-
bool alpha_chan_en;
-
} mem_dp_bg_sync;
-
struct {
-
uint32_t temp;
-
} mem_sdc_bg;
-
struct {
-
uint32_t di;
-
bool interlaced; //数据是否是隔行的
-
uint32_t in_pixel_fmt;
-
uint32_t out_pixel_fmt;
-
bool alpha_chan_en;
-
} mem_dp_fg_sync;
-
struct {
-
uint32_t di;
-
} direct_async;
-
struct {
-
display_port_t disp;
-
mcu_mode_t ch_mode;
-
uint32_t out_left;
-
uint32_t out_top;
-
} adc_sys1;
-
struct {
-
display_port_t disp;
-
mcu_mode_t ch_mode;
-
uint32_t out_left;
-
uint32_t out_top;
-
} adc_sys2;
-
} ipu_channel_params_t;
这个ipu_channel_params_t这个联合,在这个联合中对于每一种channel都有一个对应的结构体类型来保存channel的参数。如果想要获取哪一个channel的信息,就从这个联合中的对应channel里面找即可。同时,在initchannel的时候,也是通过对这个联合里面对应的channel参数赋值。
4.1 ipu_common.c分析---入口函数及probe函数分析
这个ipu_common.c函数提供ipu底层函数调用的一些关系和函数。
(一)分析这个文件从init函数入口,发现有这个subsys_initcall,说明ipu是作为一个子系统注册到内核中的:
-
int32_t __ init ipu_gen_init(void)
-
{
-
int32_t ret;
-
-
ret = platform_driver_register(&mxcipu_driver);
-
return 0;
-
}
-
-
subsys_initcall(ipu_gen_init);
(二)这个mxcipu_driver结构体注册到平台以后,如果有匹配的设备的话,就会调用其中的probe函
static int ipu_probe(struct platform_device *pdev)
-
{
-
struct ipu_soc *ipu;
-
struct resource *res;
-
unsigned long ipu_base;
-
const struct of_device_id *of_id =
-
of_match_device( imx_ipuv3_dt_ids, & pdev-> dev);
-
const struct ipu_platform_type *iputype = of_id->data;
-
const struct ipu_devtype *devtype = &iputype->devtype;
-
int ret = 0, id;
-
u32 bypass_reset, reg;
/*以imx6qp为例(后面本文件中如果有涉及到板子的一些资源,都以imx6qp为例),
*iputype= of_id->data,这个of_id返回的是imx_ipuv3_dt_ids[]数组中的某一项,
static const struct of_device_id imx_ipuv3_dt_ids[] = {
-
{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
-
{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
-
{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
-
{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp, },
-
{ /* sentinel */ }
-
};
-
MODULE_DEVICE_TABLE(of, imx_ipuv3_dt_ids);
在本文件中就是{.compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp,}这一项。所以:
*iputype= &ipu_type_imx6qp,
*devtype= 下面标红的部分:
static struct ipu_platform_type ipu_type_imx6qp = {
-
<span style="color:#FF0000;">.devtype = {
-
.name = "IPUv3H",
-
.cm_ofs = 0x00200000,
-
.idmac_ofs = 0x00208000,
-
.ic_ofs = 0x00220000,
-
.csi0_ofs = 0x00230000,
-
.csi1_ofs = 0x00238000,
-
.di0_ofs = 0x00240000,
-
.di1_ofs = 0x00248000,
-
.smfc_ofs = 0x00250000,
-
.dc_ofs = 0x00258000,
-
.dmfc_ofs = 0x00260000,
-
.vdi_ofs = 0x00268000,
-
.cpmem_ofs = 0x00300000,
-
.srm_ofs = 0x00340000,
-
.tpm_ofs = 0x00360000,
-
.dc_tmpl_ofs = 0x00380000,
-
.type = IPUv3H,
-
.idmac_used_bufs_present = true,
-
}, </span>
-
.ch0123_axi = 0,
-
.ch23_axi = 0,
-
.ch27_axi = 2,
-
.ch28_axi = 3,
-
.normal_axi = 1,
-
.idmac_used_bufs_en_r = true,
-
.idmac_used_bufs_en_w = true,
-
.idmac_used_bufs_max_r = 0x3,
-
.idmac_used_bufs_max_w = 0x3,
-
.smfc_idmac_12bit_3planar_bs_fixup = true,
-
};
*/
dev_dbg(&pdev->dev, "<%s>\n", __func__);
-
-
ret = of_property_read_u32(pdev->dev.of_node,
-
"bypass_reset", &bypass_reset);
-
if (ret < 0) {
-
dev_dbg(&pdev->dev, "can not get bypass_reset\n");
-
return ret;
-
}
/*从pdev->dev.of_node这个devicenode结构体里面读取“bypass_reset”这一项,将读出的结果存在&bypass_reset中。*/
id = of_alias_get_id(pdev->dev.of_node, "ipu");
-
if (id < 0) {
-
dev_dbg(&pdev->dev, "can not get alias id\n");
-
return id;
-
}
/*这个of_alias_get_id函数的大致意思是根据名字“ipu”获取到它的id,将这个id返回。但是有两个ipu呢,这个怎么选择??如果根据名字的话,怎么区分这两个ipu?*/
ipu = &ipu_array[id];
-
memset(ipu, 0, sizeof(struct ipu_soc));
-
ipu->bypass_reset = ( bool)bypass_reset;
-
ipu->dev = &pdev->dev;
-
ipu->id = id;
-
ipu->devtype = devtype->type;
-
ipu->ch0123_axi = iputype->ch0123_axi;
-
ipu->ch23_axi = iputype->ch23_axi;
-
ipu->ch27_axi = iputype->ch27_axi;
-
ipu->ch28_axi = iputype->ch28_axi;
-
ipu->normal_axi = iputype->normal_axi;
-
ipu->smfc_idmac_12bit_3planar_bs_fixup =
-
iputype->smfc_idmac_12bit_3planar_bs_fixup;
-
spin_lock_init(&ipu->int_reg_spin_lock);
-
spin_lock_init(&ipu->rdy_reg_spin_lock);
-
mutex_init(&ipu->mutex_lock);
/*这一些就是根据上面获取到的信息来填充这个structipu_soc *ipu结构体。*/
dev_dbg(&pdev->dev, "revision is %s\n", devtype->name);
-
-
ipu->irq_sync = platform_get_irq(pdev, 0);
-
ipu->irq_err = platform_get_irq(pdev, 1);
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-
if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) {
-
dev_err(&pdev->dev, "can't get device resources\n");
-
return -ENODEV;
-
}
/*获取irq资源和内存资源。*/
if (!devm_request_mem_region(&pdev->dev, res->start,
-
resource_size(res), pdev->name))
-
return -EBUSY;
/*申请I/O内存资源,申请后还需要通过ioremap等函数映射后才能够使用。*/
ret = devm_request_irq(&pdev->dev, ipu->irq_sync,
-
ipu_sync_irq_handler, 0, pdev->name, ipu);
-
if (ret) {
-
dev_err(ipu->dev, "request SYNC interrupt failed\n");
-
return ret;
-
}
-
ret = devm_request_irq(&pdev->dev, ipu->irq_err,
-
ipu_err_irq_handler, 0, pdev->name, ipu);
-
if (ret) {
-
dev_err(ipu->dev, "request ERR interrupt failed\n");
-
return ret;
-
}
/*为上面获取到的irq资源注册中断服务函数。*/
ipu_base = res->start; //ipu地址的初始值。
-
-
ipu->cm_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->cm_ofs, PAGE_SIZE);
-
ipu->ic_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->ic_ofs, PAGE_SIZE);
-
ipu->idmac_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->idmac_ofs, PAGE_SIZE);
-
/* DP Registers are accessed thru the SRM */
-
ipu->dp_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->srm_ofs, PAGE_SIZE);
-
ipu->dc_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->dc_ofs, PAGE_SIZE);
-
ipu->dmfc_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->dmfc_ofs, PAGE_SIZE);
-
ipu->di_reg[ 0] = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->di0_ofs, PAGE_SIZE);
-
ipu->di_reg[ 1] = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->di1_ofs, PAGE_SIZE);
-
ipu->smfc_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->smfc_ofs, PAGE_SIZE);
-
ipu->csi_reg[ 0] = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->csi0_ofs, PAGE_SIZE);
-
ipu->csi_reg[ 1] = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->csi1_ofs, PAGE_SIZE);
-
ipu->cpmem_base = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->cpmem_ofs, SZ_128K);
-
ipu->tpmem_base = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->tpm_ofs, SZ_64K);
-
ipu->dc_tmpl_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->dc_tmpl_ofs, SZ_128K);
-
ipu->vdi_reg = devm_ioremap(&pdev->dev,
-
ipu_base + devtype->vdi_ofs, PAGE_SIZE);
-
if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg ||
-
!ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg ||
-
!ipu->di_reg[ 0] || !ipu->di_reg[ 1] || !ipu->smfc_reg ||
-
!ipu->csi_reg[ 0] || !ipu->csi_reg[ 1] || !ipu->cpmem_base ||
-
!ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->vdi_reg)
-
return -ENOMEM;
-
-
dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg);
-
dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg);
-
dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg);
-
dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg);
-
dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg);
-
dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg);
-
dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[ 0]);
-
dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[ 1]);
-
dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg);
-
dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[ 0]);
-
dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[ 1]);
-
dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base);
-
dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base);
-
dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg);
-
dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg);
/*根据获取到的资源来为每个寄存器映射内存空间。*/
ipu->ipu_clk = devm_clk_get(ipu->dev, "bus");
-
if (IS_ERR(ipu->ipu_clk)) {
-
dev_err(ipu->dev, "clk_get ipu failed");
-
return PTR_ERR(ipu->ipu_clk);
-
}
/*获取"bus"的时钟。*/
/* ipu_clk is always prepared */
-
ret = clk_prepare_enable(ipu->ipu_clk);
-
if (ret < 0) {
-
dev_err(ipu->dev, "ipu clk enable failed\n");
-
return ret;
-
}
/*这个函数是clk_prepare和clk_enable两个函数的集合,其中clk_prepare函数是个预定义的函数,需要定义CONFIG_HAVE_CLK_PREPARE这个宏,然后就是调用clk_enable函数来使能时钟。*/
ipu->prg_clk = devm_clk_get(ipu->dev, "prg");
-
if (IS_ERR(ipu->prg_clk))
-
ipu->prg_clk = NULL;
/*获取"prg"的时钟。*/
ipu->online = true;
/*这个online是一个bool类型的变量,表示当前这个ipu是否正在使用中。*/
platform_set_drvdata(pdev, ipu);
/*设置私有数据。*/
/*下面这个bypass_reset参数在前面通过of_property_read_u32函数获得了,从dts文件中可以看到它等于0.同时在structipu_soc中关于这个bypass_reset有这样的注释:Bypassreset to avoid display channel being stopped by probe since it maystarts to work inbootloader.这个值是从dts文件中获得的bypass_reset,因为在开发板启动过程中,需要使能屏幕来显示,所以显示通道可能已将在bootloader中开启了,在这里设置这个值,使得显示通道在probe函数中不会关闭,也就是这个参数的含义(旁路)。*/
if (!bypass_reset) {
-
ret = device_reset(&pdev->dev);
-
if (ret) {
-
dev_err(&pdev->dev, "failed to reset: %d\n", ret);
-
return ret;
-
}
-
-
ipu_mem_reset(ipu);
-
-
ipu_disp_init(ipu);
-
-
/* Set MCU_T to divide MCU access window into 2 */
-
ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
-
IPU_DISP_GEN);
-
}
/*这个ipu_mem_reset函数同样在这个文件中,如下所示:
static int ipu_mem_reset(struct ipu_soc *ipu)
-
{
-
int timeout = 1000;
-
-
ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
-
-
while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
-
if (!timeout--)
-
return -ETIME;
-
msleep( 1);
-
}
-
-
return 0;
-
}
这个ipu_disp_init函数同样在ipu_common.c文件中,如下所示:
void ipu_disp_init(struct ipu_soc *ipu)
-
{
-
ipu->fg_csc_type = ipu->bg_csc_type = CSC_NONE;
-
ipu->color_key_4rgb = true;
-
_ipu_init_dc_mappings(ipu);
-
_ipu_dmfc_init(ipu, DMFC_NORMAL, 1);
-
}
最后通过ipu_cm_write函数来设置有关显示的一些寄存器。
*/
/* setup ipu clk tree after ipu reset */
-
ret = ipu_clk_setup_enable(ipu);
-
if (ret < 0) {
-
dev_err(ipu->dev, "ipu clk setup failed\n");
-
ipu->online = false;
-
return ret;
-
}
/*然后调用ipu_clk_setup_enable函数设置时钟树,这个函数也在这个ipu_common.c文件中,在后面分析。*/
if (devtype->idmac_used_bufs_present) {
-
/* devtype->idmac_used_bufs_present = true。 */
-
reg = ipu_idmac_read(ipu, IDMAC_CONF);
-
if (iputype->idmac_used_bufs_en_r) //idmac_used_bufs_en_r = true
-
reg |= IDMAC_CONF_USED_BUFS_EN_R;
-
else
-
reg &= ~IDMAC_CONF_USED_BUFS_EN_R;
-
if (iputype->idmac_used_bufs_en_w) //idmac_used_bufs_en_w = true
-
reg |= IDMAC_CONF_USED_BUFS_EN_W;
-
else
-
reg &= ~IDMAC_CONF_USED_BUFS_EN_W;
-
-
reg &= ~IDMAC_CONF_USED_BUFS_MAX_R_MASK;
-
reg |= (iputype->idmac_used_bufs_max_r <<
-
IDMAC_CONF_USED_BUFS_MAX_R_OFFSET);
-
reg &= ~IDMAC_CONF_USED_BUFS_MAX_W_MASK;
-
reg |= (iputype->idmac_used_bufs_max_w <<
-
IDMAC_CONF_USED_BUFS_MAX_W_OFFSET);
-
/* idmac_used_bufs_max_r = 0x3,
-
idmac_used_bufs_max_w = 0x3。 */
-
ipu_idmac_write(ipu, reg, IDMAC_CONF);
-
}
/*上面这段代码先通过ipu_idmac_read函数来读取IDMAC_CONF寄存器的值,然后根据iputype的一些信息设置它们在寄存器中对应的值,最后将新值通过ipu_idmac_write函数重新写入寄存器中。这个函数用来配置IDMAC。*/
/* Set sync refresh channels and CSI->mem channel as high priority */
-
ipu_idmac_write(ipu, 0x18800003L, IDMAC_CHA_PRI( 0));
/*通过设置IDMAC_CHA_PRI(0)寄存器,将syncrefresh channels 和CSI->memchannel的设为高优先级*/
/* Enable error interrupts by default */
-
ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL( 5));
-
ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL( 6));
-
ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL( 9));
-
ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL( 10));
/*将IPU_INT_CTRL(5),IPU_INT_CTRL(6),IPU_INT_CTRL(9),IPU_INT_CTRL(10)这几个寄存器设置为默认值0xFFFFFFFF,这几个寄存器应该是错误中断使能的寄存器。*/
if (!bypass_reset)
-
clk_disable(ipu->ipu_clk);
/*同样是这个bypass_reset参数,暂时不知道它是多少。*/
register_ipu_device(ipu, id);
/*注册ipu_device设备,这个函数在ipu_device.c文件中*/
pm_runtime_enable(&pdev->dev);
/*使能设备的电源管理*/
return ret;
-
}<span style= "font-family:Courier 10 Pitch;"><span style= "font-size:12px;"></span></span>
-
int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel)
-
{
-
uint32_t reg;
-
uint32_t ipu_conf;
-
uint32_t in_dma;
-
uint32_t out_dma;
-
uint32_t sec_dma;
-
uint32_t thrd_dma;
-
-
mutex_lock(&ipu->mutex_lock);
-
-
if (ipu->channel_enable_mask & ( 1L << IPU_CHAN_ID(channel))) {
-
dev_err(ipu->dev, "Warning: channel already enabled %d\n",
-
IPU_CHAN_ID(channel));
-
mutex_unlock(&ipu->mutex_lock);
-
return -EACCES;
-
}
/*ipu->channel_enable_mask中每一位对应一个channel是否使能了。首先检查要使能的这个channel是否已经使能了,如果已经使能了的话就会报错。*/
-
/* Get input and output dma channels */
-
out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
-
in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
/*通过这两步将channel转化成dma_ch,在之前分析过这个函数,会根据type类型从channel中选取出输入或者输出的dmachannel. */
-
ipu_conf = ipu_cm_read(ipu, IPU_CONF);
-
if (ipu->di_use_count[ 0] > 0) {
-
ipu_conf |= IPU_CONF_DI0_EN;
-
}
-
if (ipu->di_use_count[ 1] > 0) {
-
ipu_conf |= IPU_CONF_DI1_EN;
-
}
-
if (ipu->dp_use_count > 0)
-
ipu_conf |= IPU_CONF_DP_EN;
-
if (ipu->dc_use_count > 0)
-
ipu_conf |= IPU_CONF_DC_EN;
-
if (ipu->dmfc_use_count > 0)
-
ipu_conf |= IPU_CONF_DMFC_EN;
-
if (ipu->ic_use_count > 0)
-
ipu_conf |= IPU_CONF_IC_EN;
-
if (ipu->vdi_use_count > 0) {
-
ipu_conf |= IPU_CONF_ISP_EN;
-
ipu_conf |= IPU_CONF_VDI_EN;
-
ipu_conf |= IPU_CONF_IC_INPUT;
-
}
-
if (ipu->rot_use_count > 0)
-
ipu_conf |= IPU_CONF_ROT_EN;
-
if (ipu->smfc_use_count > 0)
-
ipu_conf |= IPU_CONF_SMFC_EN;
-
ipu_cm_write(ipu, ipu_conf, IPU_CONF);
/*根据ipu参数里面的引用计数来决定将ipu_conf寄存器中的对应位置位。最终将这些值都写到这个ipu_conf寄存器中。*/
-
if (idma_is_valid(in_dma)) {
-
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
-
}
-
if (idma_is_valid(out_dma)) {
-
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
-
}
/*通过这几个函数来将in_dma和out_dma的值写到ipu里面的idmac寄存器中。*/
-
if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) &&
-
((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
-
(channel == MEM_VDI_PRP_VF_MEM))) {
-
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
-
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
-
}
/*ipu_soc结构体中sec_chan_en是一个bool类型的数组,它会根据channel通过IPU_CHAN_ID转化成的数字来从这个数组中找到对应的一项,如果使能了secondchannel的话,就是true,否则就是false。同时channel的需要是PP,PRP_VF,VDI_PRP_VF的情况,如果条件都成立的话,同样会设置idmac寄存器里面的某些位。*/
-
if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
-
((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
-
thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
-
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
-
-
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
-
reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
-
ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
-
}
/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是PP和PRP_VF。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/
-
else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
-
((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
-
thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
-
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
-
reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
-
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
-
}
/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是BG和FG。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/
-
if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
-
(channel == MEM_FG_SYNC)) {
-
reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
-
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
-
-
_ipu_dp_dc_enable(ipu, channel);
-
}
/*这几个通道是关于输出显示的通道,个人理解是displaycontroll sync, background sync和foregroundsyncchannel,这时肯定不能仅仅设置idmac寄存器,同时需要调用_ipu_dp_dc_enable函数来使能显示设备,这个函数在ipu_disp.c中定义。*/
-
if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
-
_ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
-
_ipu_is_vdi_out_chan(out_dma))
-
_ipu_ic_enable_task(ipu, channel);
/*如果是in_dma和out_dma使用到了ic,irt,vdi_out的话,都需要通过_ipu_ic_enable_task函数来使能ic,这个函数在ipu_ic.c中定义。*/
ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
/*这个channel_enable_mask是一个uint32_t类型的掩码,如果使能了哪个channel的话,就将这个channel对应的位置1。同时也会在这个函数开始的地方通过它来判断一个channel是否已经使能过了。*/
-
if (ipu->prg_clk)
-
clk_prepare_enable(ipu->prg_clk);
-
-
mutex_unlock(&ipu->mutex_lock);
-
-
return 0;
-
}
-
EXPORT_SYMBOL(ipu_enable_channel);
总结一下,这个函数都做了哪些事情:
(1)既然是使能channel函数,设置的首要寄存器就是ipu_conf,会根据ipu_soc结构体里面的各个子模块的引用计数来将ipu_conf中对应的位使能。
(2)每一个channel都会使用到一个或者多个dmachannel,那么同样的,需要将这几个dmachannel所对应的寄存器的位使能,对应的寄存器是IPUx_IDMAC_CH_EN_1或者IPUx_IDMAC_CH_EN_2.
至此,ipu_common.c文件中的重要函数都分析完毕。
4.7 ipu_capture.c分析
在ipu_common.c文件中,会调用到这个文件很多底层的函数,来设置视频捕获设备中底层的一些操作寄存器。这个文件就直接从头至尾开始分析:
1._ipu_csi_mclk_set函数
-
int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi)
-
{
-
uint32_t temp;
-
uint32_t div_ratio;
-
-
div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1;
-
-
if (div_ratio > 0xFF || div_ratio < 0) {
-
dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n");
-
return -EINVAL;
-
}
-
-
temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
-
temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
-
ipu_csi_write(ipu, csi, temp |
-
(div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
-
CSI_SENS_CONF);
-
-
return 0;
-
}
这个函数是设置csi设备的mclk时钟参数,首先根据想要设置的pixel_clk时钟参数计算出来需要设置的div_ratio值,然后通过ipu_csi_read函数读出原来的CSI_SENS_CONF寄存器的值,然后将div_ratio设置进去,再通过ipu_csi_write将值重新写到CSI_SENS_CONF寄存器中。
2.ipu_csi_init_interface函数,这个函数被mxc_v4l2_capture.c的mxc_v4l_open函数和mxc_v4l2_s_param函数调用。
-
int32_t
-
ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
-
uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
-
{
-
uint32_t data = 0;
-
uint32_t csi = cfg_param.csi;
-
-
/* Set SENS_DATA_FORMAT bits (8, 9 and 10)
-
RGB or YUV444 is 0 which is current value in data so not set
-
explicitly
-
This is also the default value if attempts are made to set it to
-
something invalid. */
-
switch (pixel_fmt) {
-
case IPU_PIX_FMT_YUYV:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
-
break;
-
case IPU_PIX_FMT_UYVY:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-
break;
-
case IPU_PIX_FMT_RGB24:
-
case IPU_PIX_FMT_BGR24:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
-
break;
-
case IPU_PIX_FMT_GENERIC:
-
case IPU_PIX_FMT_GENERIC_16:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
-
break;
-
case IPU_PIX_FMT_RGB565:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
-
break;
-
case IPU_PIX_FMT_RGB555:
-
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
-
break;
-
default:
-
return -EINVAL;
-
}
/*首先根据传进来的不同pixel_fmt参数的值,设置cfg_param.data_fmt的值。*/
-
/* Set the CSI_SENS_CONF register remaining fields */
-
data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
-
cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
-
cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
-
cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
-
cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
-
cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
-
cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
-
cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
-
cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
-
cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
-
cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
-
-
_ipu_get(ipu);
-
-
mutex_lock(&ipu->mutex_lock);
-
-
ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);
/*设置CSI_SENS_CONF寄存器的值。*/
-
/* Setup sensor frame size */
-
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);
/*根据传进来的width和height参数,设置CSI_SENS_FRM_SIZE寄存器的值。*/
-
/* Set CCIR registers */
-
if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
-
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
-
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
-
} else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
-
if (width == 720 && height == 625) {
-
/* PAL case */
-
/*
-
* Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
-
* Field0ActiveEnd = 0x4, Field0ActiveStart = 0
-
*/
-
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
-
/*
-
* Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
-
* Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
-
*/
-
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);
-
-
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
-
-
} else if (width == 720 && height == 525) {
-
/* NTSC case */
-
/*
-
* Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
-
* Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
-
*/
-
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
-
/*
-
* Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
-
* Field1ActiveEnd = 0x4, Field1ActiveStart = 0
-
*/
-
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
-
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
-
} else {
-
dev_err(ipu->dev, "Unsupported CCIR656 interlaced "
-
"video mode\n");
-
mutex_unlock(&ipu->mutex_lock);
-
_ipu_put(ipu);
-
return -EINVAL;
-
}
-
_ipu_csi_ccir_err_detection_enable(ipu, csi);
-
} else if ((cfg_param.clk_mode ==
-
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
-
(cfg_param.clk_mode ==
-
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
-
(cfg_param.clk_mode ==
-
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
-
(cfg_param.clk_mode ==
-
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
-
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
-
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
-
_ipu_csi_ccir_err_detection_enable(ipu, csi);
-
} else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
-
(cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
-
_ipu_csi_ccir_err_detection_disable(ipu, csi);
-
}
/*根据cfg_param.clk_mode参数的不同值来设置CSI_CCIR_CODE_1,CSI_CCIR_CODE_2和CSI_CCIR_CODE_3寄存器的值,同时在IPU_CSI_CLK_MODE_CCIR656_INTERLACED的情况下也会参考width和height的值进行设置。其中会调用到_ipu_csi_ccir_err_detection_enable函数,这个函数在后面分析。在下面13中分析。*/
-
dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
-
ipu_csi_read(ipu, csi, CSI_SENS_CONF));
-
dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-
ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE));
-
-
mutex_unlock(&ipu->mutex_lock);
-
-
_ipu_put(ipu);
-
-
return 0;
-
}
-
EXPORT_SYMBOL(ipu_csi_init_interface);
3.ipu_csi_get_sensor_protocol函数,这个函数会在ipu_fg_overlay_sdc.c文件中的csi_enc_setup函数中调用:
-
int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi)
-
{
-
int ret;
-
_ipu_get(ipu);
-
ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) &
-
CSI_SENS_CONF_SENS_PRTCL_MASK) >>
-
CSI_SENS_CONF_SENS_PRTCL_SHIFT;
-
_ipu_put(ipu);
-
return ret;
-
}
-
EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
这个函数的大致意思就是通过指定的ipu的某一个csi,返回这个csi设备的CSI_SENS_CONF寄存器里面已经配置好的值。
4.ipu_csi_enable_mclk函数,在mxc_v4l2_capture.c文件中调用了ipu_csi_enable_mclk_if函数,这个函数其实就是ipu_csi_enable_mclk函数。
-
int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait)
-
{
-
/* Return immediately if there is no csi_clk to manage */
-
if (ipu->csi_clk[csi] == NULL)
-
return 0;
-
-
if (flag) {
-
clk_enable(ipu->csi_clk[csi]);
-
if (wait == true)
-
msleep( 10);
-
} else {
-
clk_disable(ipu->csi_clk[csi]);
-
}
-
-
return 0;
-
}
-
EXPORT_SYMBOL(ipu_csi_enable_mclk);
这个函数根据传入的flag参数来决定使能还是关闭时钟,如果flag为true的话,就调用clk_enable函数使能时钟,如果flag参数为false的话,就调用clk_disable函数来关闭时钟。
5.ipu_csi_get_window_size函数,这个函数在ipu_prp_vf_sdc_bg.c,ipu_prp_vf_sdc.c和ipu_prp_enc.c中都有调用。
-
void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi)
-
{
-
uint32_t reg;
-
-
_ipu_get(ipu);
-
-
mutex_lock(&ipu->mutex_lock);
-
-
reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE);
-
*width = (reg & 0xFFFF) + 1;
-
*height = (reg >> 16 & 0xFFFF) + 1;
-
-
mutex_unlock(&ipu->mutex_lock);
-
-
_ipu_put(ipu);
-
}
-
EXPORT_SYMBOL(ipu_csi_get_window_size);
这个函数就是从指定ipu的某个csi设备中,读取CSI_ACT_FRM_SIZE寄存器的值保存在*width和*height中返回。
6.ipu_csi_set_window_size函数,这个函数在mxc_v4l2_capture.c中的mxc_v4l2_s_param,mxc_v4l_open,init_camera_struct,和mxc_v4l_do_ioctl的VIDIOC_S_CROP函数中都有调用。
-
void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi)
-
{
-
_ipu_get(ipu);
-
-
mutex_lock(&ipu->mutex_lock);
-
-
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
-
-
mutex_unlock(&ipu->mutex_lock);
-
-
_ipu_put(ipu);
-
}
-
EXPORT_SYMBOL(ipu_csi_set_window_size);
这个函数就是将传进来的width和height参数设置到CSI_ACT_FRM_SIZE寄存器中。
7.ipu_csi_set_window_pos函数,这个函数和上一个ipu_csi_set_window_size函数是一起使用的。想要确定一个窗口的大小,只需要知道它的左上角坐标和窗口的长度/宽度就可以设置了。
-
void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi)
-
{
-
uint32_t temp;
-
-
_ipu_get(ipu);
-
-
mutex_lock(&ipu->mutex_lock);
-
-
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
-
temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
-
temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
-
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
-
-
mutex_unlock(&ipu->mutex_lock);
-
-
_ipu_put(ipu);
-
}
-
EXPORT_SYMBOL(ipu_csi_set_window_pos);
将传进来的left和top参数对应设置到CSI_OUT_FRM_CTRL寄存器中即可。
8._ipu_csi_horizontal_downsize_enable函数,这个函数暂时没人调用。。。
-
void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
-
temp |= CSI_HORI_DOWNSIZE_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
-
}
函数的大致意思是设置csi设备允许水平方向上的小型化(缩放),这个概念不好理解,但是底层的操作很简单,就是将CSI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位置位即可。
9._ipu_csi_horizontal_downsize_disable函数,是上一个函数的反函数。
-
void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
-
temp &= ~CSI_HORI_DOWNSIZE_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
-
}
同样的,想要关闭这个功能,只需要将SI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位清零即可。
10._ipu_csi_vertical_downsize_enable函数
-
void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
-
temp |= CSI_VERT_DOWNSIZE_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
-
}
这个函数就是允许csi设备垂直方向上的小型化(缩放),同样,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位置位即可。
11._ipu_csi_vertical_downsize_disable函数
-
void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
-
temp &= ~CSI_VERT_DOWNSIZE_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
-
}
这个函数是第10个函数的反函数,想要关闭垂直方向上的小型化这个功能,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位清零即可。
12._ipu_csi_set_test_generator函数
-
void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
-
uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL);
-
-
if (active == false) {
-
temp &= ~CSI_TEST_GEN_MODE_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
-
} else {
-
/* Set sensb_mclk div_ratio*/
-
_ipu_csi_mclk_set(ipu, pix_clk, csi);
-
-
temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
-
CSI_TEST_GEN_B_MASK);
-
temp |= CSI_TEST_GEN_MODE_EN;
-
temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
-
(g_value << CSI_TEST_GEN_G_SHIFT) |
-
(b_value << CSI_TEST_GEN_B_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
-
}
-
}
这个函数根据active参数的值决定是否启动这个测试发生器的功能。如果active为false的话,就将CSI_TST_CTRL寄存器中的CSI_TEST_GEN_MODE_EN位清零,如果active为true的话,就根据传入的pix_clk参数设置时钟,然后将传入的R,G,B色彩写到CSI_TST_CTRL寄存器中。
这个函数不知道具体想要实现什么功能,也没有其他的函数调用它,所以先分析这些。
13._ipu_csi_ccir_err_detection_enable函数
-
void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
-
temp |= CSI_CCIR_ERR_DET_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
-
-
}
这个函数在ipu_csi_init_interface函数中使用了它,我们在这分析:
它只是将CSI_CCIR_CODE_1寄存器的CSI_CCIR_ERR_DET_EN位置位了。启用CCIR交错模式的错误校验和修正。
14._ipu_csi_ccir_err_detection_disable函数
-
void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
-
temp &= ~CSI_CCIR_ERR_DET_EN;
-
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
-
-
}
这个函数是_ipu_csi_ccir_err_detection_enable函数的反函数。
15._ipu_csi_set_mipi_di函数,这个函数在ipu_common.c中的ipu_init_channel函数里调用.
-
int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi)
-
{
-
uint32_t temp;
-
int retval = 0;
-
-
if (di_val > 0xFFL) {
-
retval = -EINVAL;
-
goto err;
-
}
-
-
temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI);
-
-
switch (num) {
-
case IPU_CSI_MIPI_DI0:
-
temp &= ~CSI_MIPI_DI0_MASK;
-
temp |= (di_val << CSI_MIPI_DI0_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
-
break;
-
case IPU_CSI_MIPI_DI1:
-
temp &= ~CSI_MIPI_DI1_MASK;
-
temp |= (di_val << CSI_MIPI_DI1_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
-
break;
-
case IPU_CSI_MIPI_DI2:
-
temp &= ~CSI_MIPI_DI2_MASK;
-
temp |= (di_val << CSI_MIPI_DI2_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
-
break;
-
case IPU_CSI_MIPI_DI3:
-
temp &= ~CSI_MIPI_DI3_MASK;
-
temp |= (di_val << CSI_MIPI_DI3_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
-
break;
-
default:
-
retval = -EINVAL;
-
}
-
-
err:
-
return retval;
-
}
它就是根据这个传入的num值来修改CSI_MIPI_DI寄存器中的值,我在用户手册中搜索到的寄存器的名字叫做CSI1Data Identifier Register (IPUx_CSI1_DI),它应该就是为了区分数据来源是哪个流。
16._ipu_csi_set_skip_isp函数
-
int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi)
-
{
-
uint32_t temp;
-
int retval = 0;
-
-
if (max_ratio > 5) {
-
retval = -EINVAL;
-
goto err;
-
}
-
-
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
-
temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
-
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
-
(skip << CSI_SKIP_ISP_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
-
-
err:
-
return retval;
-
}
它主要是用来设置CSI_SKIP寄存器的,根据传入的skip和max_ratio两个值来修改。查看用户数据手册可以发现,这个寄存器可以设置的地方有3个,这个函数可以修改skip和max_ratio,下面那个函数可以修改另外一个smfc。
17._ipu_csi_set_skip_smfc函数
-
int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip,
-
uint32_t max_ratio, uint32_t id, uint32_t csi)
-
{
-
uint32_t temp;
-
int retval = 0;
-
-
if (max_ratio > 5 || id > 3) {
-
retval = -EINVAL;
-
goto err;
-
}
-
-
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
-
temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
-
CSI_SKIP_SMFC_MASK);
-
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
-
(id << CSI_ID_2_SKIP_SHIFT) |
-
(skip << CSI_SKIP_SMFC_SHIFT);
-
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
-
-
err:
-
return retval;
-
}
这个函数与上面那个类似,只是需要通过3个参数来确定修改CSI_SKIP中的哪些位。以后再具体查看这些位的含义。
18._ipu_smfc_init函数,它在ipu_common.c文件中的ipu_init_channel函数中调用了。
-
void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
-
{
-
uint32_t temp;
-
-
temp = ipu_smfc_read(ipu, SMFC_MAP);
-
-
switch (channel) {
-
case CSI_MEM0:
-
temp &= ~SMFC_MAP_CH0_MASK;
-
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
-
break;
-
case CSI_MEM1:
-
temp &= ~SMFC_MAP_CH1_MASK;
-
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
-
break;
-
case CSI_MEM2:
-
temp &= ~SMFC_MAP_CH2_MASK;
-
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
-
break;
-
case CSI_MEM3:
-
temp &= ~SMFC_MAP_CH3_MASK;
-
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
-
break;
-
default:
-
return;
-
}
-
-
ipu_smfc_write(ipu, temp, SMFC_MAP);
-
}
看函数的注释,MapCSI frames to IDMACchannels.这个SMFC位于csi设备与IDMAC之间的位置,意思应该就是映射csi的frame到IDMAC中。操作很简单,根据channel参数的值来设置SMFC_MAP寄存器中的值。
19._ipu_smfc_set_wmc函数
-
void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level)
-
{
-
uint32_t temp;
-
-
temp = ipu_smfc_read(ipu, SMFC_WMC);
-
-
switch (channel) {
-
case CSI_MEM0:
-
if ( set == true) {
-
temp &= ~SMFC_WM0_SET_MASK;
-
temp |= level << SMFC_WM0_SET_SHIFT;
-
} else {
-
temp &= ~SMFC_WM0_CLR_MASK;
-
temp |= level << SMFC_WM0_CLR_SHIFT;
-
}
-
break;
-
case CSI_MEM1:
-
if ( set == true) {
-
temp &= ~SMFC_WM1_SET_MASK;
-
temp |= level << SMFC_WM1_SET_SHIFT;
-
} else {
-
temp &= ~SMFC_WM1_CLR_MASK;
-
temp |= level << SMFC_WM1_CLR_SHIFT;
-
}
-
break;
-
case CSI_MEM2:
-
if ( set == true) {
-
temp &= ~SMFC_WM2_SET_MASK;
-
temp |= level << SMFC_WM2_SET_SHIFT;
-
} else {
-
temp &= ~SMFC_WM2_CLR_MASK;
-
temp |= level << SMFC_WM2_CLR_SHIFT;
-
}
-
break;
-
case CSI_MEM3:
-
if ( set == true) {
-
temp &= ~SMFC_WM3_SET_MASK;
-
temp |= level << SMFC_WM3_SET_SHIFT;
-
} else {
-
temp &= ~SMFC_WM3_CLR_MASK;
-
temp |= level << SMFC_WM3_CLR_SHIFT;
-
}
-
break;
-
default:
-
return;
-
}
-
-
ipu_smfc_write(ipu, temp, SMFC_WMC);
-
}
根据channel的值来设置SMFC_WMC寄存器中的不同位。函数意思不懂。
20._ipu_smfc_set_burst_size函数,在ipu_common.c中的ipu_init_channel_buffer中调用了。
-
void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs)
-
{
-
uint32_t temp;
-
-
temp = ipu_smfc_read(ipu, SMFC_BS);
-
-
switch (channel) {
-
case CSI_MEM0:
-
temp &= ~SMFC_BS0_MASK;
-
temp |= bs << SMFC_BS0_SHIFT;
-
break;
-
case CSI_MEM1:
-
temp &= ~SMFC_BS1_MASK;
-
temp |= bs << SMFC_BS1_SHIFT;
-
break;
-
case CSI_MEM2:
-
temp &= ~SMFC_BS2_MASK;
-
temp |= bs << SMFC_BS2_SHIFT;
-
break;
-
case CSI_MEM3:
-
temp &= ~SMFC_BS3_MASK;
-
temp |= bs << SMFC_BS3_SHIFT;
-
break;
-
default:
-
return;
-
}
-
-
ipu_smfc_write(ipu, temp, SMFC_BS);
-
}
函数作用是设置IDMACchannel的burstsize(脉冲串?),根据传入的bs参数设置SMFC_BS寄存器即可。
21._ipu_csi_init函数,它在ipu_common.c中的ipu_init_channel函数中调用了。
-
int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi)
-
{
-
uint32_t csi_sens_conf, csi_dest;
-
int retval = 0;
-
-
switch (channel) {
-
case CSI_MEM0:
-
case CSI_MEM1:
-
case CSI_MEM2:
-
case CSI_MEM3:
-
csi_dest = CSI_DATA_DEST_IDMAC;
-
break;
-
case CSI_PRP_ENC_MEM:
-
case CSI_PRP_VF_MEM:
-
csi_dest = CSI_DATA_DEST_IC;
-
break;
-
default:
-
retval = -EINVAL;
-
goto err;
-
}
-
-
csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
-
csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
-
ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest <<
-
CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF);
-
err:
-
return retval;
-
}
这个函数初始化csi设备,它根据传入的channel的不同,设置不同的csi数据目的地,然后将这个目的地设置到CSI_SENS_CONF寄存器中。感觉这个channel在ipu内部的意思就是ipu内数据流通的过程,每个channle里面都含有目的地等信息。
22._ipu_csi_wait4eof函数,在ipu_common.c中的ipu_disable_csi函数中调用了。
-
void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel)
-
{
-
int ret;
-
int irq = 0;
-
-
if (channel == CSI_MEM0)
-
irq = IPU_IRQ_CSI0_OUT_EOF;
-
else if (channel == CSI_MEM1)
-
irq = IPU_IRQ_CSI1_OUT_EOF;
-
else if (channel == CSI_MEM2)
-
irq = IPU_IRQ_CSI2_OUT_EOF;
-
else if (channel == CSI_MEM3)
-
irq = IPU_IRQ_CSI3_OUT_EOF;
-
else if (channel == CSI_PRP_ENC_MEM)
-
irq = IPU_IRQ_PRP_ENC_OUT_EOF;
-
else if (channel == CSI_PRP_VF_MEM)
-
irq = IPU_IRQ_PRP_VF_OUT_EOF;
-
else{
-
dev_err(ipu->dev, "Not a CSI channel\n");
-
return;
-
}
-
-
init_completion(&ipu->csi_comp);
-
ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu);
-
if (ret < 0) {
-
dev_err(ipu->dev, "CSI irq %d in use\n", irq);
-
return;
-
}
-
ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies( 500));
-
ipu_free_irq(ipu, irq, ipu);
-
dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret);
-
}
它的中断处理函数如下:
-
static irqreturn_t csi_irq_handler(int irq, void *dev_id)
-
{
-
struct ipu_soc *ipu = dev_id;
-
struct completion *comp = &ipu->csi_comp;
-
-
complete(comp);
-
return IRQ_HANDLED;
-
}
这个函数中,首先根据channel的不同值设置不同的irq触发方式,然后初始化完成量,然后调用ipu_request_irq函数申请中断,这个函数在ipu_common.c中定义。如果发生中断后,就会调用到这个中断处理函数,在中断处理函数中通过complete()函数来唤醒阻塞在ipu->csi_comp上的首个线程。wait_for_completion_timeout函数表示,如果超过一定时间没有发生这个中断,就直接结束等待。
关于这个完成量的解释和操作函数,可以看《Linux的completion机制.pdf》和《线程的约会completion.pdf》两个文件。
至此,这个文件简单分析完毕。