4.0 ipu_soc,ipu_channel_t ,ipu_channel_params_t结构体详解

1.ipu_soc结构体:

struct ipu_soc {

  1. unsigned int id; //ipu的ID号
  2. unsigned int devtype; //ipu的一些信息,包含cm,ic等模块的地址偏移值
  3. bool online; //表示这个ipu是否正在使用中
  4. /*clk*/
  5. struct clk *ipu_clk;
  6. struct clk *di_clk[2];
  7. struct clk *di_clk_sel[2];
  8. struct clk *pixel_clk[2];
  9. bool pixel_clk_en[2];
  10. struct clk *pixel_clk_sel[2];
  11. struct clk *csi_clk[2];
  12. struct clk *prg_clk;
  13. /*irq*/
  14. int irq_sync;
  15. int irq_err;
  16. struct ipu_irq_node irq_list[IPU_IRQ_COUNT]; //在request_irq函数中会根据传入的irq号在这个数组中选择对应的下标,来存取有关这个irq的
  17. //信息如irq服务函数,名字,flags等参数。
  18. /*reg*/ /* ipu内部模块经过ioremap后的地址 */
  19. void __iomem *cm_reg;
  20. void __iomem *idmac_reg;
  21. void __iomem *dp_reg;
  22. void __iomem *ic_reg;
  23. void __iomem *dc_reg;
  24. void __iomem *dc_tmpl_reg;
  25. void __iomem *dmfc_reg;
  26. void __iomem *di_reg[2];
  27. void __iomem *smfc_reg;
  28. void __iomem *csi_reg[2];
  29. void __iomem *cpmem_base;
  30. void __iomem *tpmem_base;
  31. void __iomem *vdi_reg;
  32. struct device *dev;
  33. ipu_channel_t csi_channel[2]; //每个ipu有两个csi设备,将此时csi设备对应的channel根据csi号保存在这个csi_channel[]数组中
  34. ipu_channel_t using_ic_dirct_ch; //表示那个channel直接使用IC设备
  35. unsigned char dc_di_assignment[10];
  36. bool sec_chan_en[24]; //对应的channel是否使能了second channel
  37. bool thrd_chan_en[24]; //对应的channel是否使能了third channel
  38. bool chan_is_interlaced[52]; //对应的channel中的数据是否是隔行的,每个channel对应其中的一位,每一位是一个bool类型的值
  39. uint32_t channel_init_mask; //这是一个32位的数,其中每一位代表一个channel号,如果初始化一个channel的话,就将这个channel对应的位置1
  40. uint32_t channel_enable_mask; //每一位对应一个channel号,如果使能了一个channel 的话,就将这个channel对应的位置1,与上面那个channel_init_mask类似。
  41. /*use count*/ /* 下面几个是IPU内部模块的引用计数 */
  42. int dc_use_count; //dc引用计数
  43. int dp_use_count; //dp引用计数
  44. int dmfc_use_count; //dmfc引用计数
  45. int smfc_use_count; //smfc引用计数
  46. int ic_use_count; //ic引用计数
  47. int rot_use_count; //rot引用计数
  48. int vdi_use_count; //vdi引用计数
  49. int di_use_count[2]; //di引用计数,每个ipu只有两个di
  50. int csi_use_count[2]; //csi引用计数,每个ipu只有两个csi
  51. struct mutex mutex_lock;
  52. spinlock_t int_reg_spin_lock;
  53. spinlock_t rdy_reg_spin_lock;
  54. int dmfc_size_28;
  55. int dmfc_size_29;
  56. int dmfc_size_24;
  57. int dmfc_size_27;
  58. int dmfc_size_23;
  59. enum csc_type_t fg_csc_type;
  60. enum csc_type_t bg_csc_type;
  61. bool color_key_4rgb;
  62. bool dc_swap;
  63. struct completion dc_comp;
  64. struct completion csi_comp;
  65. struct rot_mem {
  66. void *vaddr;
  67. dma_addr_t paddr;
  68. int size;
  69. } rot_dma[2];
  70. int vdoa_en; //是否使能VDOA
  71. struct task_struct *thread[2]; //两个内核线程
  72. /*
  73. * Bypass reset to avoid display channel being
  74. * stopped by probe since it may starts to work
  75. * in bootloader.
  76. */
  77. bool bypass_reset; /* 这个值是从dts文件中获得的bypass_reset,因为在开发板启动过程中,需要使能屏幕来显示,所以显示通道可能已经在bootloader中开启了,
  78. * 在这里设置这个值,使得显示通道在probe函数中不会关闭,也就是这个参数的含义(旁路)。*/
  79. /* AXI protocol id */
  80. unsigned int ch0123_axi;
  81. unsigned int ch23_axi;
  82. unsigned int ch27_axi;
  83. unsigned int ch28_axi;
  84. unsigned int normal_axi;
  85. /* 不同的channel可能会对应不同的 AXI protocol,在ipu_probe函数中对这些值进行了赋值,在ipu_platform_type结构体中指定的。 */
  86. bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */
  87. };

2.ipu_channel_t枚举:

typedef enum {

  1. CHAN_NONE = -1,
  2. MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
  3. MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
  4. MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
  5. MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
  6. MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
  7. MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
  8. MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
  9. MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
  10. MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
  11. MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
  12. MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
  13. MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
  14. MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
  15. MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
  16. DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  17. DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  18. CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
  19. CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
  20. CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
  21. CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
  22. CSI_MEM = CSI_MEM0,
  23. CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
  24. CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
  25. /* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
  26. MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
  27. MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
  28. MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
  29. /* for vdi mem->vdi->mem */
  30. MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5),
  31. MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5),
  32. MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5),
  33. /* fake channel for vdoa to link with IPU */
  34. MEM_VDOA_MEM = _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  35. MEM_PP_ADC = CHAN_NONE,
  36. ADC_SYS2 = CHAN_NONE,
  37. } ipu_channel_t;

再来看看这个_MAKE_CHAN宏:

#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \

  1. ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
  2. #define NO_DMA 0x3F

从这里就可以看出来,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)

  1. {
  2. return ((uint32_t) ch >> (6 * type)) & 0x3F;
  3. };

这个函数能够根据不同的type类型从channel里面提取出所使用的dmachannel。

typedef enum {

  1. IPU_OUTPUT_BUFFER = 0, /*!< Buffer for output from IPU */
  2. IPU_ALPHA_IN_BUFFER = 1, /*!< Buffer for input to IPU */
  3. IPU_GRAPH_IN_BUFFER = 2, /*!< Buffer for input to IPU */
  4. IPU_VIDEO_IN_BUFFER = 3, /*!< Buffer for input to IPU */
  5. IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
  6. IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
  7. } 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 {

  1. struct {
  2. uint32_t csi; //csi设备号,0或1
  3. uint32_t mipi_id; //mipi ID号
  4. uint32_t mipi_vc; //mipi虚拟通道号
  5. bool mipi_en; //是否使能mipi接口
  6. bool interlaced; //数据是否是隔行的
  7. } csi_mem;
  8. struct {
  9. uint32_t in_width; //输入数据的宽度
  10. uint32_t in_height; //输入数据的高度
  11. uint32_t in_pixel_fmt; //输入数据的像素格式
  12. uint32_t out_width; //输出数据的宽度
  13. uint32_t out_height; //输出数据的高度
  14. uint32_t out_pixel_fmt; //输出数据的像素格式
  15. uint32_t outh_resize_ratio; //输出水平方向上重定义大小系数
  16. uint32_t outv_resize_ratio; //输出垂直方向上重定义大小系数
  17. uint32_t csi; //csi 设备号
  18. uint32_t mipi_id; //mipi ID号
  19. uint32_t mipi_vc; //mipi 虚拟通道号
  20. bool mipi_en; //是否是能mipi接口
  21. } csi_prp_enc_mem;
  22. struct {
  23. uint32_t in_width;
  24. uint32_t in_height;
  25. uint32_t in_pixel_fmt;
  26. uint32_t out_width;
  27. uint32_t out_height;
  28. uint32_t out_pixel_fmt;
  29. uint32_t outh_resize_ratio;
  30. uint32_t outv_resize_ratio;
  31. } mem_prp_enc_mem;
  32. struct {
  33. uint32_t in_width;
  34. uint32_t in_height;
  35. uint32_t in_pixel_fmt;
  36. uint32_t out_width;
  37. uint32_t out_height;
  38. uint32_t out_pixel_fmt;
  39. } mem_rot_enc_mem;
  40. struct {
  41. uint32_t in_width;
  42. uint32_t in_height;
  43. uint32_t in_pixel_fmt;
  44. uint32_t out_width;
  45. uint32_t out_height;
  46. uint32_t out_pixel_fmt;
  47. uint32_t outh_resize_ratio;
  48. uint32_t outv_resize_ratio;
  49. bool graphics_combine_en;
  50. bool global_alpha_en;
  51. bool key_color_en;
  52. uint32_t in_g_pixel_fmt;
  53. uint8_t alpha;
  54. uint32_t key_color;
  55. bool alpha_chan_en;
  56. ipu_motion_sel motion_sel;
  57. enum v4l2_field field_fmt;
  58. uint32_t csi;
  59. uint32_t mipi_id;
  60. uint32_t mipi_vc;
  61. bool mipi_en;
  62. } csi_prp_vf_mem;
  63. struct {
  64. uint32_t in_width;
  65. uint32_t in_height;
  66. uint32_t in_pixel_fmt;
  67. uint32_t out_width;
  68. uint32_t out_height;
  69. uint32_t out_pixel_fmt;
  70. bool graphics_combine_en;
  71. bool global_alpha_en;
  72. bool key_color_en;
  73. display_port_t disp;
  74. uint32_t out_left;
  75. uint32_t out_top;
  76. } csi_prp_vf_adc;
  77. struct {
  78. uint32_t in_width; //输入宽度
  79. uint32_t in_height; //输入高度
  80. uint32_t in_pixel_fmt; //输入像素格式
  81. uint32_t out_width; //输出宽度
  82. uint32_t out_height; //输出高度
  83. uint32_t out_pixel_fmt; //输出像素格式
  84. uint32_t outh_resize_ratio; //输出水平方向上重定义大小的比例
  85. uint32_t outv_resize_ratio; //输出垂直方向上重定义大小的比例
  86. bool graphics_combine_en; //是否使能second channel的标志位
  87. bool global_alpha_en; //是否使能third channel的标志位
  88. bool key_color_en;
  89. uint32_t in_g_pixel_fmt;
  90. uint8_t alpha;
  91. uint32_t key_color;
  92. bool alpha_chan_en;
  93. ipu_motion_sel motion_sel;
  94. enum v4l2_field field_fmt;
  95. } mem_prp_vf_mem;
  96. struct {
  97. uint32_t temp;
  98. } mem_prp_vf_adc;
  99. struct {
  100. uint32_t temp;
  101. } mem_rot_vf_mem;
  102. struct {
  103. uint32_t in_width;
  104. uint32_t in_height;
  105. uint32_t in_pixel_fmt;
  106. uint32_t out_width;
  107. uint32_t out_height;
  108. uint32_t out_pixel_fmt;
  109. uint32_t outh_resize_ratio;
  110. uint32_t outv_resize_ratio;
  111. bool graphics_combine_en;
  112. bool global_alpha_en;
  113. bool key_color_en;
  114. uint32_t in_g_pixel_fmt;
  115. uint8_t alpha;
  116. uint32_t key_color;
  117. bool alpha_chan_en;
  118. } mem_pp_mem;
  119. struct {
  120. uint32_t temp;
  121. } mem_rot_mem;
  122. struct {
  123. uint32_t in_width;
  124. uint32_t in_height;
  125. uint32_t in_pixel_fmt;
  126. uint32_t out_width;
  127. uint32_t out_height;
  128. uint32_t out_pixel_fmt;
  129. bool graphics_combine_en;
  130. bool global_alpha_en;
  131. bool key_color_en;
  132. display_port_t disp;
  133. uint32_t out_left;
  134. uint32_t out_top;
  135. } mem_pp_adc;
  136. struct {
  137. uint32_t di;
  138. bool interlaced; //数据是否是隔行的
  139. uint32_t in_pixel_fmt;
  140. uint32_t out_pixel_fmt;
  141. } mem_dc_sync;
  142. struct {
  143. uint32_t temp;
  144. } mem_sdc_fg;
  145. struct {
  146. uint32_t di;
  147. bool interlaced; //数据是否是隔行的
  148. uint32_t in_pixel_fmt;
  149. uint32_t out_pixel_fmt;
  150. bool alpha_chan_en;
  151. } mem_dp_bg_sync;
  152. struct {
  153. uint32_t temp;
  154. } mem_sdc_bg;
  155. struct {
  156. uint32_t di;
  157. bool interlaced; //数据是否是隔行的
  158. uint32_t in_pixel_fmt;
  159. uint32_t out_pixel_fmt;
  160. bool alpha_chan_en;
  161. } mem_dp_fg_sync;
  162. struct {
  163. uint32_t di;
  164. } direct_async;
  165. struct {
  166. display_port_t disp;
  167. mcu_mode_t ch_mode;
  168. uint32_t out_left;
  169. uint32_t out_top;
  170. } adc_sys1;
  171. struct {
  172. display_port_t disp;
  173. mcu_mode_t ch_mode;
  174. uint32_t out_left;
  175. uint32_t out_top;
  176. } adc_sys2;
  177. } 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是作为一个子系统注册到内核中的:

  1. int32_t __init ipu_gen_init(void)
  2. {
  3. int32_t ret;
  4. ret = platform_driver_register(&mxcipu_driver);
  5. return 0;
  6. }
  7. subsys_initcall(ipu_gen_init);

(二)这个mxcipu_driver结构体注册到平台以后,如果有匹配的设备的话,就会调用其中的probe函

static int ipu_probe(struct platform_device *pdev)

  1. {
  2. struct ipu_soc *ipu;
  3. struct resource *res;
  4. unsigned long ipu_base;
  5. const struct of_device_id *of_id =
  6. of_match_device(imx_ipuv3_dt_ids, &pdev->dev);
  7. const struct ipu_platform_type *iputype = of_id->data;
  8. const struct ipu_devtype *devtype = &iputype->devtype;
  9. int ret = 0, id;
  10. 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[] = {

  1. { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
  2. { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
  3. { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
  4. { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp, },
  5. { /* sentinel */ }
  6. };
  7. 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 = {

  1. <span style="color:#FF0000;">.devtype = {
  2. .name = "IPUv3H",
  3. .cm_ofs = 0x00200000,
  4. .idmac_ofs = 0x00208000,
  5. .ic_ofs = 0x00220000,
  6. .csi0_ofs = 0x00230000,
  7. .csi1_ofs = 0x00238000,
  8. .di0_ofs = 0x00240000,
  9. .di1_ofs = 0x00248000,
  10. .smfc_ofs = 0x00250000,
  11. .dc_ofs = 0x00258000,
  12. .dmfc_ofs = 0x00260000,
  13. .vdi_ofs = 0x00268000,
  14. .cpmem_ofs = 0x00300000,
  15. .srm_ofs = 0x00340000,
  16. .tpm_ofs = 0x00360000,
  17. .dc_tmpl_ofs = 0x00380000,
  18. .type = IPUv3H,
  19. .idmac_used_bufs_present = true,
  20. }, </span>
  21. .ch0123_axi = 0,
  22. .ch23_axi = 0,
  23. .ch27_axi = 2,
  24. .ch28_axi = 3,
  25. .normal_axi = 1,
  26. .idmac_used_bufs_en_r = true,
  27. .idmac_used_bufs_en_w = true,
  28. .idmac_used_bufs_max_r = 0x3,
  29. .idmac_used_bufs_max_w = 0x3,
  30. .smfc_idmac_12bit_3planar_bs_fixup = true,
  31. };

*/

dev_dbg(&pdev->dev, "<%s>\n", __func__);

  1. ret = of_property_read_u32(pdev->dev.of_node,
  2. "bypass_reset", &bypass_reset);
  3. if (ret < 0) {
  4. dev_dbg(&pdev->dev, "can not get bypass_reset\n");
  5. return ret;
  6. }

/*从pdev->dev.of_node这个devicenode结构体里面读取“bypass_reset”这一项,将读出的结果存在&bypass_reset中。*/

id = of_alias_get_id(pdev->dev.of_node, "ipu");

  1. if (id < 0) {
  2. dev_dbg(&pdev->dev, "can not get alias id\n");
  3. return id;
  4. }

/*这个of_alias_get_id函数的大致意思是根据名字“ipu”获取到它的id,将这个id返回。但是有两个ipu呢,这个怎么选择??如果根据名字的话,怎么区分这两个ipu?*/

ipu = &ipu_array[id];

  1. memset(ipu, 0, sizeof(struct ipu_soc));
  2. ipu->bypass_reset = (bool)bypass_reset;
  3. ipu->dev = &pdev->dev;
  4. ipu->id = id;
  5. ipu->devtype = devtype->type;
  6. ipu->ch0123_axi = iputype->ch0123_axi;
  7. ipu->ch23_axi = iputype->ch23_axi;
  8. ipu->ch27_axi = iputype->ch27_axi;
  9. ipu->ch28_axi = iputype->ch28_axi;
  10. ipu->normal_axi = iputype->normal_axi;
  11. ipu->smfc_idmac_12bit_3planar_bs_fixup =
  12. iputype->smfc_idmac_12bit_3planar_bs_fixup;
  13. spin_lock_init(&ipu->int_reg_spin_lock);
  14. spin_lock_init(&ipu->rdy_reg_spin_lock);
  15. mutex_init(&ipu->mutex_lock);

/*这一些就是根据上面获取到的信息来填充这个structipu_soc *ipu结构体。*/

dev_dbg(&pdev->dev, "revision is %s\n", devtype->name);

  1. ipu->irq_sync = platform_get_irq(pdev, 0);
  2. ipu->irq_err = platform_get_irq(pdev, 1);
  3. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  4. if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) {
  5. dev_err(&pdev->dev, "can't get device resources\n");
  6. return -ENODEV;
  7. }

/*获取irq资源和内存资源。*/

if (!devm_request_mem_region(&pdev->dev, res->start,

  1. resource_size(res), pdev->name))
  2. return -EBUSY;

/*申请I/O内存资源,申请后还需要通过ioremap等函数映射后才能够使用。*/

ret = devm_request_irq(&pdev->dev, ipu->irq_sync,

  1. ipu_sync_irq_handler, 0, pdev->name, ipu);
  2. if (ret) {
  3. dev_err(ipu->dev, "request SYNC interrupt failed\n");
  4. return ret;
  5. }
  6. ret = devm_request_irq(&pdev->dev, ipu->irq_err,
  7. ipu_err_irq_handler, 0, pdev->name, ipu);
  8. if (ret) {
  9. dev_err(ipu->dev, "request ERR interrupt failed\n");
  10. return ret;
  11. }

/*为上面获取到的irq资源注册中断服务函数。*/

ipu_base = res->start; //ipu地址的初始值。

  1. ipu->cm_reg = devm_ioremap(&pdev->dev,
  2. ipu_base + devtype->cm_ofs, PAGE_SIZE);
  3. ipu->ic_reg = devm_ioremap(&pdev->dev,
  4. ipu_base + devtype->ic_ofs, PAGE_SIZE);
  5. ipu->idmac_reg = devm_ioremap(&pdev->dev,
  6. ipu_base + devtype->idmac_ofs, PAGE_SIZE);
  7. /* DP Registers are accessed thru the SRM */
  8. ipu->dp_reg = devm_ioremap(&pdev->dev,
  9. ipu_base + devtype->srm_ofs, PAGE_SIZE);
  10. ipu->dc_reg = devm_ioremap(&pdev->dev,
  11. ipu_base + devtype->dc_ofs, PAGE_SIZE);
  12. ipu->dmfc_reg = devm_ioremap(&pdev->dev,
  13. ipu_base + devtype->dmfc_ofs, PAGE_SIZE);
  14. ipu->di_reg[0] = devm_ioremap(&pdev->dev,
  15. ipu_base + devtype->di0_ofs, PAGE_SIZE);
  16. ipu->di_reg[1] = devm_ioremap(&pdev->dev,
  17. ipu_base + devtype->di1_ofs, PAGE_SIZE);
  18. ipu->smfc_reg = devm_ioremap(&pdev->dev,
  19. ipu_base + devtype->smfc_ofs, PAGE_SIZE);
  20. ipu->csi_reg[0] = devm_ioremap(&pdev->dev,
  21. ipu_base + devtype->csi0_ofs, PAGE_SIZE);
  22. ipu->csi_reg[1] = devm_ioremap(&pdev->dev,
  23. ipu_base + devtype->csi1_ofs, PAGE_SIZE);
  24. ipu->cpmem_base = devm_ioremap(&pdev->dev,
  25. ipu_base + devtype->cpmem_ofs, SZ_128K);
  26. ipu->tpmem_base = devm_ioremap(&pdev->dev,
  27. ipu_base + devtype->tpm_ofs, SZ_64K);
  28. ipu->dc_tmpl_reg = devm_ioremap(&pdev->dev,
  29. ipu_base + devtype->dc_tmpl_ofs, SZ_128K);
  30. ipu->vdi_reg = devm_ioremap(&pdev->dev,
  31. ipu_base + devtype->vdi_ofs, PAGE_SIZE);
  32. if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg ||
  33. !ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg ||
  34. !ipu->di_reg[0] || !ipu->di_reg[1] || !ipu->smfc_reg ||
  35. !ipu->csi_reg[0] || !ipu->csi_reg[1] || !ipu->cpmem_base ||
  36. !ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->vdi_reg)
  37. return -ENOMEM;
  38. dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg);
  39. dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg);
  40. dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg);
  41. dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg);
  42. dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg);
  43. dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg);
  44. dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[0]);
  45. dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[1]);
  46. dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg);
  47. dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[0]);
  48. dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[1]);
  49. dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base);
  50. dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base);
  51. dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg);
  52. dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg);

/*根据获取到的资源来为每个寄存器映射内存空间。*/

ipu->ipu_clk = devm_clk_get(ipu->dev, "bus");

  1. if (IS_ERR(ipu->ipu_clk)) {
  2. dev_err(ipu->dev, "clk_get ipu failed");
  3. return PTR_ERR(ipu->ipu_clk);
  4. }

/*获取"bus"的时钟。*/

/* ipu_clk is always prepared */

  1. ret = clk_prepare_enable(ipu->ipu_clk);
  2. if (ret < 0) {
  3. dev_err(ipu->dev, "ipu clk enable failed\n");
  4. return ret;
  5. }

/*这个函数是clk_prepare和clk_enable两个函数的集合,其中clk_prepare函数是个预定义的函数,需要定义CONFIG_HAVE_CLK_PREPARE这个宏,然后就是调用clk_enable函数来使能时钟。*/

ipu->prg_clk = devm_clk_get(ipu->dev, "prg");

  1. if (IS_ERR(ipu->prg_clk))
  2. 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) {

  1. ret = device_reset(&pdev->dev);
  2. if (ret) {
  3. dev_err(&pdev->dev, "failed to reset: %d\n", ret);
  4. return ret;
  5. }
  6. ipu_mem_reset(ipu);
  7. ipu_disp_init(ipu);
  8. /* Set MCU_T to divide MCU access window into 2 */
  9. ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
  10. IPU_DISP_GEN);
  11. }

/*这个ipu_mem_reset函数同样在这个文件中,如下所示:

static int ipu_mem_reset(struct ipu_soc *ipu)

  1. {
  2. int timeout = 1000;
  3. ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
  4. while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
  5. if (!timeout--)
  6. return -ETIME;
  7. msleep(1);
  8. }
  9. return 0;
  10. }

这个ipu_disp_init函数同样在ipu_common.c文件中,如下所示:

void ipu_disp_init(struct ipu_soc *ipu)

  1. {
  2. ipu->fg_csc_type = ipu->bg_csc_type = CSC_NONE;
  3. ipu->color_key_4rgb = true;
  4. _ipu_init_dc_mappings(ipu);
  5. _ipu_dmfc_init(ipu, DMFC_NORMAL, 1);
  6. }

最后通过ipu_cm_write函数来设置有关显示的一些寄存器。

*/

/* setup ipu clk tree after ipu reset */

  1. ret = ipu_clk_setup_enable(ipu);
  2. if (ret < 0) {
  3. dev_err(ipu->dev, "ipu clk setup failed\n");
  4. ipu->online = false;
  5. return ret;
  6. }

/*然后调用ipu_clk_setup_enable函数设置时钟树,这个函数也在这个ipu_common.c文件中,在后面分析。*/

if (devtype->idmac_used_bufs_present) {

  1. /* devtype->idmac_used_bufs_present = true。 */
  2. reg = ipu_idmac_read(ipu, IDMAC_CONF);
  3. if (iputype->idmac_used_bufs_en_r) //idmac_used_bufs_en_r = true
  4. reg |= IDMAC_CONF_USED_BUFS_EN_R;
  5. else
  6. reg &= ~IDMAC_CONF_USED_BUFS_EN_R;
  7. if (iputype->idmac_used_bufs_en_w) //idmac_used_bufs_en_w = true
  8. reg |= IDMAC_CONF_USED_BUFS_EN_W;
  9. else
  10. reg &= ~IDMAC_CONF_USED_BUFS_EN_W;
  11. reg &= ~IDMAC_CONF_USED_BUFS_MAX_R_MASK;
  12. reg |= (iputype->idmac_used_bufs_max_r <<
  13. IDMAC_CONF_USED_BUFS_MAX_R_OFFSET);
  14. reg &= ~IDMAC_CONF_USED_BUFS_MAX_W_MASK;
  15. reg |= (iputype->idmac_used_bufs_max_w <<
  16. IDMAC_CONF_USED_BUFS_MAX_W_OFFSET);
  17. /* idmac_used_bufs_max_r = 0x3,
  18. idmac_used_bufs_max_w = 0x3。 */
  19. ipu_idmac_write(ipu, reg, IDMAC_CONF);
  20. }

/*上面这段代码先通过ipu_idmac_read函数来读取IDMAC_CONF寄存器的值,然后根据iputype的一些信息设置它们在寄存器中对应的值,最后将新值通过ipu_idmac_write函数重新写入寄存器中。这个函数用来配置IDMAC。*/

/* Set sync refresh channels and CSI->mem channel as high priority */

  1. ipu_idmac_write(ipu, 0x18800003L, IDMAC_CHA_PRI(0));

/*通过设置IDMAC_CHA_PRI(0)寄存器,将syncrefresh channels 和CSI->memchannel的设为高优先级*/

/* Enable error interrupts by default */

  1. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5));
  2. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6));
  3. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9));
  4. 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)

  1. clk_disable(ipu->ipu_clk);

/*同样是这个bypass_reset参数,暂时不知道它是多少。*/

 register_ipu_device(ipu, id); 

/*注册ipu_device设备,这个函数在ipu_device.c文件中*/

 pm_runtime_enable(&pdev->dev); 

/*使能设备的电源管理*/

return ret;

  1. }<span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"></span></span>

4.2 ipu_init_channel函数的详细分析

在看这个函数之前,先对ipu内部的channel有所了解,先来看看ipu内部的flow的定义:

对于每个flow,代表数据从摄像头采集到显示(或者保存在内存中)的过程中的一个分步骤,每个flow都对应一个或几个channel,而每个channel的数据流通过程中,是通过DMA传输的,所以每个channel里面又包含一个或几个dma_channel。在这不太好理解,看看手册中的介绍(captureflow):

其中上面和程序中所说的channel对应图中的Taskschain,对于这个channel的理解需要根据ipu内部架构图来理解,如下所示:

以上面CSI0-->SMFC-->MEM这个channel为例,它代表数据从CSI经过SMFC到达内存MEM中,因为数据是从CSI中获得的,所以physicalDMA channel中的videoinput为空,而数据从这个channel输出的话,就需要通过DMAchannel了,从图上可以看出来,physicalDMA channel中的videooutput可以为IDMAC_CH_0~ IDMAC_CH_3。

同样对于captureflow,还可以通过CSI0-->SMFC--->MEM和CSI0-->VDIC-->MEM这两种方式将摄像头采集到的数据存放到内存中。

再来看手册中的Processingflows的图:

以MEM-->IC-->MEM这个channel为例,根据IPU内部框架图可以看到,从内存中取出数据,经过IC处理以后再放入内存中,那么取出数据的时候,就使用到DMAchannel了,从这个图中可以看出来,使用的是IDMAC_CH12,再次放入内存中的时候就使用到IDMAC_CH20。

对于其他channel就暂时不一一分析了。再来看看程序中是怎样定义channel的。

内核中使用ipu_channel_t枚举来表示一个channel:

  1. typedef enum {
  2. CHAN_NONE = -1,
  3. MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
  4. MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
  5. MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
  6. MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
  7. MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
  8. MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
  9. MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
  10. MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
  11. MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
  12. MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
  13. MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
  14. MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
  15. MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
  16. MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
  17. DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  18. DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  19. CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
  20. CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
  21. CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
  22. CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
  23. CSI_MEM = CSI_MEM0,
  24. CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
  25. CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
  26. /* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
  27. MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
  28. MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
  29. MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
  30. /* for vdi mem->vdi->mem */
  31. MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5),
  32. MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5),
  33. MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5),
  34. /* fake channel for vdoa to link with IPU */
  35. MEM_VDOA_MEM = _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
  36. MEM_PP_ADC = CHAN_NONE,
  37. ADC_SYS2 = CHAN_NONE,
  38. } ipu_channel_t;
  1. #define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
  2. ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)

这个枚举就包含了所有的channel,如果对于上面讲解的过程理解的话,很容易根据channel的过程在这个枚举中找到对应的channel名字。同时可以看到在channel通过_MAKE_CHAN宏的构造过程中,每个channel里面都包含了输入输出dmachannel号。

分析完对channel的理解过程以后,再来看看具体的函数实现:

ipu_init_channel函数

  1. int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel,
  2. ipu_channel_params_t *params)

/*这个函数有3个参数,第一个参数ipu代表正在使用的ipu。第二个参数是想要初始化的channel,它其实就是一个数字,可以理解为ID,第三个参数params是想要将这个channel初始化成什么样子,它里面包含channel的一些信息,会根据params参数来初始化这个channel。关于这个结构体的详细讲解可以看《ipu_channel_params_t结构体详解》*/

  1. {
  2. int ret = 0;
  3. bool bad_pixfmt;
  4. uint32_t ipu_conf, reg, in_g_pixel_fmt, sec_dma;
  5. dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel));
  6. ret = pm_runtime_get_sync(ipu->dev);
  7. if (ret < 0) {
  8. dev_err(ipu->dev, "ch = %d, pm_runtime_get failed:%d!\n",
  9. IPU_CHAN_ID(channel), ret);
  10. dump_stack();
  11. return ret;
  12. }
  13. /*
  14. * Here, ret could be 1 if the device's runtime PM status was
  15. * already 'active', so clear it to be 0.
  16. */

/*看注释,可以看出来,上面一个函数如果执行成功的话,它的返回值是1,所以在下面需要将这个ret清零。跟踪源码,确实是这样的。*/

  1. ret = 0;
  2. _ipu_get(ipu);
  3. mutex_lock(&ipu->mutex_lock);
  4. /* Re-enable error interrupts every time a channel is initialized */
  5. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5));
  6. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6));
  7. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9));
  8. ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10));

/*在初始化每一个channel的时候都需要重新使能错误中断。*/

  1. if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
  2. dev_warn(ipu->dev, "Warning: channel already initialized %d\n",
  3. IPU_CHAN_ID(channel));
  4. }

/*这个ipu_soc结构体里面的channel_init_mask项是uint32_t类型的,每个channel对应这个数字里面的某一位,如果这个channel进行过初始化操作的话,就将它的那一位置1.通过这个来判断其中的某一个channel是否进行过初始化。所以在这个初始化函数中肯定有将它置位的操作,我们搜索源码可以发现,在这个函数的后面确实有这样的操作。先在这粘贴一下:

ipu->channel_init_mask|= 1L << IPU_CHAN_ID(channel);

*/

    ipu_conf = ipu_cm_read(ipu, IPU_CONF); 

/*下面的switch判断语句就是根据channel的值来初始化不同的channel,所以这么多case就包括了所有的channel。*/

  1. switch (channel) {
  2. case CSI_MEM0:
  3. case CSI_MEM1:
  4. case CSI_MEM2:
  5. case CSI_MEM3:
  6. if (params->csi_mem.csi > 1) { //csi有2个,取值为0和1.
  7. ret = -EINVAL;
  8. goto err;
  9. }
  10. if (params->csi_mem.interlaced)
  11. ipu->chan_is_interlaced[channel_2_dma(channel,
  12. IPU_OUTPUT_BUFFER)] = true;
  13. else
  14. ipu->chan_is_interlaced[channel_2_dma(channel,
  15. IPU_OUTPUT_BUFFER)] = false;

/*ipu->chan_is_interlaced是一个bool型的数组,根据params里面的csi_mem.interlaced参数,来设置ipu->chan_is_interlaced数组的某一项。怎么确定是数组的第几项?根据channel的值通过channel_2_dma函数为它在数组中挑选一个位置,然后将这个位置设置为true或false。*/

  1. ipu->smfc_use_count++; //增加smfc使用计数,这几个channel都会使用到smfc。
  2. ipu->csi_channel[params->csi_mem.csi] = channel;

/*根据params->csi_mem.csi来确定是ipu->csi_channel数组的第几项,然后将channel赋给它。这个ipu->csi_channel数组包含两项,意思就是每个ipu有两个csi接口,每个接口选择哪个channel,都在这里进行保存。*/

  1. /*SMFC setting*/
  2. if (params->csi_mem.mipi_en) {
  3. ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  4. params->csi_mem.csi));
  5. _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc,
  6. params->csi_mem.csi);
  7. _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc,
  8. params->csi_mem.mipi_id, params->csi_mem.csi);
  9. } else {
  10. ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  11. params->csi_mem.csi));
  12. _ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi);
  13. }

/*params中csi_mem.mipi_en是个bool类型的值,如果它为1的话,表示使用的是mipi引脚。

下面来看看ipu_conf寄存器:

这里设置的是ipu_conf寄存器的28或者29位,看看这两位的介绍:

这两位的含义是选择CSI的数据来源,我们知道,CSI接口可以接并行接口或者MIPI接口,驱动会根据mipi_en的值来选择将这两位的数据来源设置为并口或者MIPI接口。如果是MIPI接口的话,需要通过_ipu_csi_set_mipi_di函数来设置MIPI相关的寄存器。

同时,不论是哪一种接口,都需要使用SMFC,就需要通过_ipu_smfc_init函数来初始化smfc的SMFC_MAP寄存器,在这个_ipu_smfc_init函数中根据不同的case选择不同的SMFC_MAP_CH;然后再调用_ipu_csi_set_mipi_di函数来初始化CSI_MIPI_DI寄存器。这个寄存器是mipidata identifier的意思。

*/

  1. /*CSI data (include compander) dest*/
  2. _ipu_csi_init(ipu, channel, params->csi_mem.csi);
  3. break;

/*最后通过_ipu_csi_init函数初始化csi。*/

/*上面的channel流程就是下图这种模式:数据从CSI中通过SMFC直接到达IDMAC,然后通过IDMAC来进行数据的存放等操作。

*/

  1. case CSI_PRP_ENC_MEM:
  2. if (params->csi_prp_enc_mem.csi > 1) {
  3. ret = -EINVAL;
  4. goto err;
  5. }
  6. if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
  7. (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) {
  8. ret = -EINVAL;
  9. goto err;
  10. }
  11. ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM;

/*这个ipu->using_ic_dirct_ch是个ipu_channel_t类型的变量,usingic direct channel的意思,只能使用ICchannel而不能使用VDIchannel,所以是MEM_VDI_PRP_VF_MEM和MEM_VDI_MEM的话就会报错。最后再把这一位置位成CSI_PRP_ENC_MEM。*/

  1. ipu->ic_use_count++; //增加ic的引用计数。
  2. ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel;

/*这个ipu_soc结构体中有一项ipu_channel_tcsi_channel[2];因为每个ipu有两个csi,所以这个数组里面保存的是每个csi选用的channel通道。在每一个需要用到csi接口的channel中都需要设置这一项,可以看到在这个switch中前几个用到csi的case,里面都设置了这一项。*/

/*同时在params这个参数中,它是ipu_channel_params_t类型的联合,其中包含的两个关于csi的结构体csi_mem和csi_prp_enc_mem,这两个结构体里面都含有一个uint32_tcsi,就是通过它来确定想要使用的是哪一个csi设备。 */

  1. if (params->csi_prp_enc_mem.mipi_en) {
  2. ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  3. params->csi_prp_enc_mem.csi));
  4. _ipu_csi_set_mipi_di(ipu,
  5. params->csi_prp_enc_mem.mipi_vc,
  6. params->csi_prp_enc_mem.mipi_id,
  7. params->csi_prp_enc_mem.csi);
  8. } else
  9. ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  10. params->csi_prp_enc_mem.csi));

/*这个channel没有用到smfc,所以不用初始化它,其他跟上面那个channel一样。设置IPU_CONF寄存器中的28,29位。如果是MIPI接口的话,还需要通过_ipu_csi_set_mipi_di函数来初始化MIPI相关的寄存器。*/

  1. /*CSI0/1 feed into IC*/
  2. ipu_conf &= ~IPU_CONF_IC_INPUT;
  3. if (params->csi_prp_enc_mem.csi)
  4. ipu_conf |= IPU_CONF_CSI_SEL;
  5. else
  6. ipu_conf &= ~IPU_CONF_CSI_SEL;

/*设置ipu_conf中的IPU_CONF_IC_INPUT位和CSI_SEL位。这两位的含义如下:

在初始化这个channel的时候,我们应该首先想到这个channel使用到了IPU内部的哪些模块,在上面一个channel中,使用到了CSI和SMFC这两个模块,所以需要设置ipu_conf寄存器中的28,29数据来源位,选择数据是从并口来的还是从MIPI接口来的,然后分别初始化CSI和SMFC即可。

对于本次这个channel,CSI-->IC(prpenc)-->MEM,同样的思想,首先想想这个channel使用了哪几个模块,使用了CSI和IC模块,然后同样设置ipu_conf寄存器中的28,29数据来源位,然后就是设置30和31位了,30位设置IC的输入数据是从哪里来的,从IPU内部框架中可以看出来,输入IC的数据只能是VDI和CSI,所以这里设置30位为0。但是CSI的话还有两个呢,这就需要设置31位来确定是哪一个CSI设备了。

*/

  1. /*PRP skip buffer in memory, only valid when RWS_EN is true*/
  2. reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
  3. ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);

/*将IPU_FS_PROC_FLOW1寄存器里面的FS_ENC_IN_VALID位清零。


这里设置的就是IPU_FS_PROC_FLOW1寄存器的30位,这一位的含义如上所示,它表示是否应该跳过内存中的buffer。这一位是根据RWS_EN来选择的。那么RWS_EN又是什么?在IC_CONF寄存器中找到了它,如下所示:

其中RWS是rawsensor的意思,一般sensor的输出数据可以为YUV,JPEG,RGB,RAW几种格式,其中这个RAW格式是未经处理、也未经压缩的格式,记录了数码相机传感器的原始信息,可以把RAW概念化为“原始图像编码数据”或更形象的称为“数字底片”。所以RWS_EN表示摄像头输出的数据是否是原始的RAW数据。

当RWS_EN为0时,数据直接从CSI输入到IC中,所以IPU_FS_PROC_FLOW1这个寄存器就需要设置跳过从内存中到达IC的数据,将IPU_FS_PROC_FLOW1寄存器的ENC_IN_VALID位清零。

*/

  1. /*CSI data (include compander) dest*/
  2. _ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi);
  3. _ipu_ic_init_prpenc(ipu, params, true);
  4. break;

/*最后通过_ipu_csi_init函数初始化csi,同时通过_ipu_ic_init_prpenc函数预处理编码,这个函数在ipu_ic.c中。*/

/*至此,这个CSI_PRP_ENC_MEMchannel简单分析完毕,它的流程如下图所示:

*/

  1. case CSI_PRP_VF_MEM:
  2. if (params->csi_prp_vf_mem.csi > 1) {
  3. ret = -EINVAL;
  4. goto err;
  5. }
  6. if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
  7. (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) {
  8. ret = -EINVAL;
  9. goto err;
  10. }
  11. ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM;

/*与上面那个CSI_PRP_ENC_MEMchannel一样,这个ipu->using_ic_dirct_ch只能置位成当前这个CSI_PRP_VF_MEMchannel。*/

  1. ipu->ic_use_count++; //增加ic的引用计数。
  2. ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel;
  3. if (params->csi_prp_vf_mem.mipi_en) {
  4. ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  5. params->csi_prp_vf_mem.csi));
  6. _ipu_csi_set_mipi_di(ipu,
  7. params->csi_prp_vf_mem.mipi_vc,
  8. params->csi_prp_vf_mem.mipi_id,
  9. params->csi_prp_vf_mem.csi);
  10. } else
  11. ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
  12. params->csi_prp_vf_mem.csi));
  13. /*CSI0/1 feed into IC*/
  14. ipu_conf &= ~IPU_CONF_IC_INPUT;
  15. if (params->csi_prp_vf_mem.csi)
  16. ipu_conf |= IPU_CONF_CSI_SEL;
  17. else
  18. ipu_conf &= ~IPU_CONF_CSI_SEL;
  19. /*PRP skip buffer in memory, only valid when RWS_EN is true*/
  20. reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
  21. ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);

/*与上一个函数不同的是,设置的IPU_FS_PROC_FLOW1寄存器的VF_IN_VALID位,原理是相同的。*/

  1. /*CSI data (include compander) dest*/
  2. _ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi);
  3. _ipu_ic_init_prpvf(ipu, params, true);
  4. break;

/*除了最后一个函数,其他都与上一个case一样,最后一个函数_ipu_ic_init_prpvf同样在ipu_ic.c中。*/

/*这个CSI_PRP_VF_MEMchannel的流程如下所示:

*/

  1. case MEM_PRP_VF_MEM:
  2. if (params->mem_prp_vf_mem.graphics_combine_en) {
  3. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  4. in_g_pixel_fmt = params->mem_prp_vf_mem.in_g_pixel_fmt;
  5. bad_pixfmt =
  6. _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt);
  7. if (params->mem_prp_vf_mem.alpha_chan_en) {
  8. if (bad_pixfmt) {
  9. dev_err(ipu->dev, "bad pixel format "
  10. "for graphics plane from "
  11. "ch%d\n", sec_dma);
  12. ret = -EINVAL;
  13. goto err;
  14. }
  15. ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
  16. }
  17. ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
  18. }

/*bad_pixfmt的意思可以在查看_ipu_ch_param_bad_alpha_pos函数的时候大致了解,在IPUv3IDMAC中对于32bpp的格式有个bug,所以对于32bpp的都返回true,对于其他返回false。*/

/*下面这个涉及到alpha通道的一些知识,

http://baike.baidu.com/link?url=8p8Z8B2o_qOdahK5nfH-9ZiGfcubcPFWVxVCxCWn3XEItoG2KDmTLUpObjQMIp59VvTLBpJNzHYs4-9N4O_KOq

《RGBA》里面有简单介绍。*/

/*上面的代码,会根据ipu_channel_params_t里面的mem_prp_vf_mem这一项的graphics_combine_en来决定是否使能secondchannel,根据mem_prp_vf_mem这一项的alpha_chan_en来决定是否使能thirdchannel。如果使能的话,就会设置ipu_soc中的sec_chan_en[]和thrd_chan_en[]这两项,根据channelID号来置位。*/

  1. reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
  2. ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
  3. _ipu_ic_init_prpvf(ipu, params, false);
  4. ipu->ic_use_count++;
  5. break;

/*其他函数大致都相似,就是细节不太相同,比如将IPU_FS_PROC_FLOW1寄存器的FS_VF_IN_VALID位设置为1;_ipu_ic_init_prpvf函数的第三个参数是false等等,_ipu_ic_init_prpvf函数的第三个参数是bool类型的变量,true代表IC的数据来源是CSI,false代表IC的数据来源不是CSI。*/

/*注意这个函数中设置了ipu_soc结构体中的sec_chan_en和thrd_chan_en,是两个bool型的数组,从名字上来看就是secondchannel enable和thirdchannelenable的意思,我认为MEM_PRP_VF_MEM这个channel是可以和其他某些channel一起启用的(在手册的CM模块对于IPUmainflows进行了详细的讲解,在后面具体分析)。根据params参数中mem_prp_vf_mem.graphics_combine_en置位的话,就会启用第二个channel,如果params参数中mem_prp_vf_mem.alpha_chan_en继续置位的话,就继续启用第三个channel。将启用了几个channel这些信息都保存在ipu_soc结构体中。*/

  1. case MEM_VDI_PRP_VF_MEM:
  2. if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
  3. (ipu->using_ic_dirct_ch == MEM_VDI_MEM) ||
  4. (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
  5. ret = -EINVAL;
  6. goto err;
  7. }
  8. ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM;
  9. ipu->ic_use_count++;
  10. ipu->vdi_use_count++;
  11. reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
  12. reg &= ~FS_VDI_SRC_SEL_MASK;
  13. ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1);
  14. if (params->mem_prp_vf_mem.graphics_combine_en)
  15. ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
  16. _ipu_ic_init_prpvf(ipu, params, false);
  17. _ipu_vdi_init(ipu, channel, params);
  18. break;

/*大部分设置都是相似的,在这个channel中使用到了VDIC,所以需要设置IPU_FS_PROC_FLOW1寄存器中有关VDIC的位:VDI_SRC_SEL,手册中的解释如下:

从程序中可以看到,只是将这些位清空了,所以默认的VDIC的数据来源是ARM平台。

同样,设置IC初始化为prpvf模式,然后通过_ipu_vdi_init初始化VDIC,_ipu_vdi_init在ipu_ic.c中定义。*/

/*这个模式的流程是下面这张图:

*/

  1. case MEM_VDI_PRP_VF_MEM_P:
  2. case MEM_VDI_PRP_VF_MEM_N:
  3. case MEM_VDI_MEM_P:
  4. case MEM_VDI_MEM_N:
  5. _ipu_vdi_init(ipu, channel, params);
  6. break;

/*这几个channel不再分析了。*/

  1. case MEM_VDI_MEM:
  2. if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
  3. (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ||
  4. (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
  5. ret = -EINVAL;
  6. goto err;
  7. }
  8. ipu->using_ic_dirct_ch = MEM_VDI_MEM;
  9. ipu->ic_use_count++;
  10. ipu->vdi_use_count++;
  11. _ipu_vdi_init(ipu, channel, params);
  12. break;

/*设置ipu_soc中的using_ic_dirct_ch,增加ic_use_count和vdi_use_count的引用计数,最终也是调用这个_ipu_vdi_init函数。这个函数中增加了vdi_use_count的引用计数,上面几个与这个channel相似的channel都没有增加这个引用计数。这个channel只使用了VDIC。*/

  1. case MEM_ROT_VF_MEM:
  2. ipu->ic_use_count++;
  3. ipu->rot_use_count++;
  4. _ipu_ic_init_rotate_vf(ipu, params);
  5. break;
  6. case MEM_PRP_ENC_MEM:
  7. ipu->ic_use_count++;
  8. reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
  9. ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
  10. _ipu_ic_init_prpenc(ipu, params, false);
  11. break;
  12. case MEM_ROT_ENC_MEM:
  13. ipu->ic_use_count++;
  14. ipu->rot_use_count++;
  15. _ipu_ic_init_rotate_enc(ipu, params);
  16. break;

/*上面几个channel程序比较简单,就暂时不分析。*/

  1. case MEM_PP_MEM:
  2. if (params->mem_pp_mem.graphics_combine_en) {
  3. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  4. in_g_pixel_fmt = params->mem_pp_mem.in_g_pixel_fmt;
  5. bad_pixfmt =
  6. _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt);
  7. if (params->mem_pp_mem.alpha_chan_en) {
  8. if (bad_pixfmt) {
  9. dev_err(ipu->dev, "bad pixel format "
  10. "for graphics plane from "
  11. "ch%d\n", sec_dma);
  12. ret = -EINVAL;
  13. goto err;
  14. }
  15. ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
  16. }
  17. ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true;
  18. }
  19. _ipu_ic_init_pp(ipu, params);
  20. ipu->ic_use_count++;
  21. break;

/*这个channel与MEM_PRP_VF_MEMchannel类似,它也是根据params参数启用了sec_chan_en和thrd_chan_en。*/

/*

*/

  1. case MEM_ROT_PP_MEM:
  2. _ipu_ic_init_rotate_pp(ipu, params);
  3. ipu->ic_use_count++;
  4. ipu->rot_use_count++;
  5. break;
  6. case MEM_DC_SYNC:
  7. if (params->mem_dc_sync.di > 1) {
  8. ret = -EINVAL;
  9. goto err;
  10. }
  11. ipu->dc_di_assignment[1] = params->mem_dc_sync.di;
  12. _ipu_dc_init(ipu, 1, params->mem_dc_sync.di,
  13. params->mem_dc_sync.interlaced,
  14. params->mem_dc_sync.out_pixel_fmt);
  15. ipu->di_use_count[params->mem_dc_sync.di]++;
  16. ipu->dc_use_count++;
  17. ipu->dmfc_use_count++;
  18. break;
  19. case MEM_BG_SYNC:
  20. if (params->mem_dp_bg_sync.di > 1) {
  21. ret = -EINVAL;
  22. goto err;
  23. }
  24. if (params->mem_dp_bg_sync.alpha_chan_en)
  25. ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
  26. ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di;
  27. _ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt,
  28. params->mem_dp_bg_sync.out_pixel_fmt);
  29. _ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di,
  30. params->mem_dp_bg_sync.interlaced,
  31. params->mem_dp_bg_sync.out_pixel_fmt);
  32. ipu->di_use_count[params->mem_dp_bg_sync.di]++;
  33. ipu->dc_use_count++;
  34. ipu->dp_use_count++;
  35. ipu->dmfc_use_count++;
  36. break;
  37. case MEM_FG_SYNC:
  38. _ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt,
  39. params->mem_dp_fg_sync.out_pixel_fmt);
  40. if (params->mem_dp_fg_sync.alpha_chan_en)
  41. ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true;
  42. ipu->dc_use_count++;
  43. ipu->dp_use_count++;
  44. ipu->dmfc_use_count++;
  45. break;
  46. case DIRECT_ASYNC0:
  47. if (params->direct_async.di > 1) {
  48. ret = -EINVAL;
  49. goto err;
  50. }
  51. ipu->dc_di_assignment[8] = params->direct_async.di;
  52. _ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
  53. ipu->di_use_count[params->direct_async.di]++;
  54. ipu->dc_use_count++;
  55. break;
  56. case DIRECT_ASYNC1:
  57. if (params->direct_async.di > 1) {
  58. ret = -EINVAL;
  59. goto err;
  60. }
  61. ipu->dc_di_assignment[9] = params->direct_async.di;
  62. _ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
  63. ipu->di_use_count[params->direct_async.di]++;
  64. ipu->dc_use_count++;
  65. break;
  66. default:
  67. dev_err(ipu->dev, "Missing channel initialization\n");
  68. break;
  69. }
  70. ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel);
  71. ipu_cm_write(ipu, ipu_conf, IPU_CONF);
  72. err:
  73. mutex_unlock(&ipu->mutex_lock);
  74. return ret;
  75. }
  76. EXPORT_SYMBOL(ipu_init_channel);

/*后面几个channel是关于显示的channel,他们与前面几个case的步骤是相似的,暂时不分析,用到的时候再分析。*/

小总结:

可以看出来,在ipu_init_channel函数中,会根据对应的channel来决定使用哪个模块。

比如对于CSI_MEM0~CSI_MEM3,就会使用SMFC(_ipu_smfc_init函数),然后通过_ipu_csi_init函数来指定数据的目的地址。

对于CSI_PRP_ENC_MEMchannel,就会使用IC,使用的是IC的prpenc功能,就需要使用_ipu_ic_init_prpenc函数,同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。

对于CSI_PRP_VF_MEMchannel,需要使用到IC,但是使用的是IC的prpvf功能,就需要使用_ipu_ic_init_prpvf函数。同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。

上面几个函数都使用到了CSI设备,所以都使用了_ipu_csi_init函数来指定数据的目的地址。对于有些channel没有使用CSI设备,肯定就不用使用_ipu_csi_init这个函数了。

比如对于MEM_VDI_MEMchannel,它使用到VDI模块,只需要通过_ipu_vdi_init函数来设置即可。

比如MEM_ROT_VF_MEMchannel,它需要使用到IC的rotate和viewfinder功能,就需要通过_ipu_ic_init_rotate_vf函数来设置了。

4.3 ipu_init_channel_buffer函数的详细分析

ipu_init_channel_buffer函数

  1. int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel,
  2. ipu_buffer_t type,
  3. uint32_t pixel_fmt,
  4. uint16_t width, uint16_t height,
  5. uint32_t stride,
  6. ipu_rotate_mode_t rot_mode,
  7. dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
  8. dma_addr_t phyaddr_2,
  9. uint32_t u, uint32_t v)
  10. {
  11. uint32_t reg;
  12. uint32_t dma_chan;
  13. uint32_t burst_size;
  14. dma_chan = channel_2_dma(channel, type);
  15. if (!idma_is_valid(dma_chan))
  16. return -EINVAL;

/*通过channel_2_dma函数将channel根据type提取出使用了哪个dmachannel。对于这个channel_2_dma函数,因为每个channel需要使用到dmachannel,在输入的时候可能使用,在输出的时候也可能使用,所以需要传入一个type类型来从channel中提取出所需要的dmachannel号。*/

  1. if (stride < width * bytes_per_pixel(pixel_fmt))
  2. stride = width * bytes_per_pixel(pixel_fmt);
  3. if (stride % 4) {
  4. dev_err(ipu->dev,
  5. "Stride not 32-bit aligned, stride = %d\n", stride);
  6. return -EINVAL;
  7. }

/*对传入的stride参数进行判断。*/

  1. /* IC & IRT channels' width must be multiple of 8 pixels */
  2. if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan))
  3. && (width % 8)) {
  4. dev_err(ipu->dev, "Width must be 8 pixel multiple\n");
  5. return -EINVAL;
  6. }

/*如果是IC和IRTchannel的话,函数传入的width参数必须是8pixels的整数倍,否则就返回错误。*/

  1. if (_ipu_is_vdi_out_chan(dma_chan) &&
  2. ((width < 16) || (height < 16) || (width % 2) || (height % 4))) {
  3. dev_err(ipu->dev, "vdi width/height limited err\n");
  4. return -EINVAL;
  5. }
  6. /* IPUv3EX and IPUv3M support triple buffer */
  7. if ((!_ipu_is_trb_chan(ipu, dma_chan)) && phyaddr_2) {
  8. dev_err(ipu->dev, "Chan%d doesn't support triple buffer "
  9. "mode\n", dma_chan);
  10. return -EINVAL;
  11. }

/*这个函数比较绕,首先来看函数传入的参数里面有三个dma_addr_t类型的变量phyaddr_0,phyaddr_1和phyaddr_2。这三个变量每一个都对应一个buffer的基地址。_ipu_is_trb_chan函数是判断dma_chan是否支持3buffers,如果支持的话,就肯定不会执行下面的dev_err语句;如果不支持3buffers的话,那传入的参数里面phyaddr_2就必须为0,否则就会执行dev_err语句(都不支持3buffers了,那么第三个buffer的地址肯定为0)。*/

  1. if (!phyaddr_1 && phyaddr_2) {
  2. dev_err(ipu->dev, "Chan%d's buf1 physical addr is NULL for "
  3. "triple buffer mode\n", dma_chan);
  4. return -EINVAL;
  5. }

/*这个判断语句是phyaddr_1存在的话就肯定不会执行dev_err语句,如果phyaddr_1和phyaddr_2都不存在的话,就会报错。*/

  1. mutex_lock(&ipu->mutex_lock);
  2. /* Build parameter memory data for DMA channel */
  3. _ipu_ch_param_init(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0, phyaddr_0, phyaddr_1, phyaddr_2);

/*在_ipu_ch_param_init函数内部,首先根据传入的pixel_fmt,width, height, stride, u, v, 0, phyaddr_0, phyaddr_1,phyaddr_2的值来设置一个structipu_ch_param类型的params变量,然后将这个变量通过fill_cpmem函数写到通过ipu,dma_chan这两个参数确定的CPMEM中。如果phyaddr_2参数不为空的话,还需要通过fill_cpmem函数将params的值写入ipu和sub_ch确定的寄存器的基址中。这个_ipu_ch_param_init函数很重要。*/

  1. /* Set correlative channel parameter of local alpha channel */
  2. if ((_ipu_is_ic_graphic_chan(dma_chan) ||
  3. _ipu_is_dp_graphic_chan(dma_chan)) &&
  4. (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] == true)) {
  5. _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, true);
  6. _ipu_ch_param_set_alpha_buffer_memory(ipu, dma_chan);
  7. _ipu_ch_param_set_alpha_condition_read(ipu, dma_chan);
  8. /* fix alpha width as 8 and burst size as 16*/
  9. _ipu_ch_params_set_alpha_width(ipu, dma_chan, 8);
  10. _ipu_ch_param_set_burst_size(ipu, dma_chan, 16);
  11. } else if (_ipu_is_ic_graphic_chan(dma_chan) &&
  12. ipu_pixel_format_has_alpha(pixel_fmt))
  13. _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, false);

/*设置alpha通道的一些信息,上面这些子函数都分析了,他们就是设置dma_chan对应的channel的某些位。*/

  1. if (rot_mode)
  2. _ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode);

/*设置rot_mode参数在dma_chan中对应的某些位。*/

  1. /* IC and ROT channels have restriction of 8 or 16 pix burst length */
  2. if (_ipu_is_ic_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) {
  3. if ((width % 16) == 0)
  4. _ipu_ch_param_set_burst_size(ipu, dma_chan, 16);
  5. else
  6. _ipu_ch_param_set_burst_size(ipu, dma_chan, 8);
  7. } else if (_ipu_is_irt_chan(dma_chan)) {
  8. _ipu_ch_param_set_burst_size(ipu, dma_chan, 8);
  9. _ipu_ch_param_set_block_mode(ipu, dma_chan);
  10. } else if (_ipu_is_dmfc_chan(dma_chan)) {
  11. burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
  12. _ipu_dmfc_set_wait4eot(ipu, dma_chan, width);
  13. _ipu_dmfc_set_burst_size(ipu, dma_chan, burst_size);
  14. }

/*根据不同的channel设置burstlength的不同值。*/

  1. if (_ipu_disp_chan_is_interlaced(ipu, channel) ||
  2. ipu->chan_is_interlaced[dma_chan])
  3. _ipu_ch_param_set_interlaced_scan(ipu, dma_chan);

/*设置interlaced参数。*/

  1. if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan) ||
  2. _ipu_is_vdi_out_chan(dma_chan)) {
  3. burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
  4. _ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size,
  5. rot_mode);
  6. }
/* 如果是 ic, irt或者 vdi_out channel的话,就需要调用_ipu_ic_idma_init函数来初始化ic和idma,这个函数在ipu_ic.c中定义。 */
  1. else if (_ipu_is_smfc_chan(dma_chan)) {
  2. burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan);
  3. /*
  4. * This is different from IPUv3 spec, but it is confirmed
  5. * in IPUforum that SMFC burst size should be NPB[6:3]
  6. * when IDMAC works in 16-bit generic data mode.
  7. */
  8. if (pixel_fmt == IPU_PIX_FMT_GENERIC)
  9. /* 8 bits per pixel */
  10. burst_size = burst_size >> 4;
  11. else if (pixel_fmt == IPU_PIX_FMT_GENERIC_16)
  12. /* 16 bits per pixel */
  13. burst_size = burst_size >> 3;
  14. else
  15. burst_size = burst_size >> 2;
  16. _ipu_smfc_set_burst_size(ipu, channel, burst_size-1);
  17. }

/*如果是smfcchannel的话,就直接根据channel参数的值设置(burst_size)即可,_ipu_smfc_set_burst_size这个函数在ipu_capture.c中定义。*/

  1. switch (dma_chan) {
  2. case 0:
  3. case 1:
  4. case 2:
  5. case 3:
  6. _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch0123_axi);
  7. break;
  8. case 23:
  9. _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch23_axi);
  10. break;
  11. case 27:
  12. _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch27_axi);
  13. break;
  14. case 28:
  15. _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch28_axi);
  16. break;
  17. default:
  18. _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->normal_axi);
  19. break;
  20. }

/*设置AXIprotocol id,这些AXIid是在ipu_probe函数中,从ipu_platform_type中获取到的。*/

  1. if (idma_is_set(ipu, IDMAC_CHA_PRI(dma_chan), dma_chan) &&
  2. ipu->devtype == IPUv3H) {
  3. uint32_t reg = IDMAC_CH_LOCK_EN_1(ipu->devtype);
  4. uint32_t value = 0;
  5. switch (dma_chan) {
  6. case 5:
  7. value = 0x3;
  8. break;
  9. case 11:
  10. value = 0x3 << 2;
  11. break;
  12. case 12:
  13. value = 0x3 << 4;
  14. break;
  15. case 14:
  16. value = 0x3 << 6;
  17. break;
  18. case 15:
  19. value = 0x3 << 8;
  20. break;
  21. case 20:
  22. value = 0x3 << 10;
  23. break;
  24. case 21:
  25. value = 0x3 << 12;
  26. break;
  27. case 22:
  28. value = 0x3 << 14;
  29. break;
  30. case 23:
  31. value = 0x3 << 16;
  32. break;
  33. case 27:
  34. value = 0x3 << 18;
  35. break;
  36. case 28:
  37. value = 0x3 << 20;
  38. break;
  39. case 45:
  40. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  41. value = 0x3 << 0;
  42. break;
  43. case 46:
  44. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  45. value = 0x3 << 2;
  46. break;
  47. case 47:
  48. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  49. value = 0x3 << 4;
  50. break;
  51. case 48:
  52. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  53. value = 0x3 << 6;
  54. break;
  55. case 49:
  56. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  57. value = 0x3 << 8;
  58. break;
  59. case 50:
  60. reg = IDMAC_CH_LOCK_EN_2(ipu->devtype);
  61. value = 0x3 << 10;
  62. break;
  63. default:
  64. break;
  65. }
  66. value |= ipu_idmac_read(ipu, reg);
  67. ipu_idmac_write(ipu, value, reg);
  68. }

/*设置idmac寄存器的一些参数。*/

  1. _ipu_ch_param_dump(ipu, dma_chan);
  2. if (phyaddr_2 && ipu->devtype >= IPUv3EX) {
  3. reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan));
  4. reg &= ~idma_mask(dma_chan);
  5. ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan));
  6. reg = ipu_cm_read(ipu,
  7. IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
  8. reg |= idma_mask(dma_chan);
  9. ipu_cm_write(ipu, reg,
  10. IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));

/*上面这些步骤的意思是:如果(phyaddr_2&& ipu->devtype >=IPUv3EX)这个条件满足的话,就意味着这个channel支持triplebuffer MODE,所以先将寄存器中doublebuffer MODE的位清零,然后将triplebuffer MODE的位置位。至于是哪个寄存器。。。。通过IPU_CHA_DB_MODE_SEL这个宏可以看出来应该是CM寄存器。*/

  1. /* Set IDMAC third buffer's cpmem number */
  2. /* See __ipu_ch_get_third_buf_cpmem_num() for mapping */
  3. ipu_idmac_write(ipu, 0x00444047L,
  4. IDMAC_SUB_ADDR_4(ipu->devtype));
  5. ipu_idmac_write(ipu, 0x46004241L,
  6. IDMAC_SUB_ADDR_3(ipu->devtype));
  7. ipu_idmac_write(ipu, 0x00000045L,
  8. IDMAC_SUB_ADDR_1(ipu->devtype));

/*设置idmac寄存器中关于triplebuffer的某些位*/

  1. /* Reset to buffer 0 */
  2. ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan),
  3. IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan));
  4. } else {
  5. reg = ipu_cm_read(ipu,
  6. IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
  7. reg &= ~idma_mask(dma_chan);
  8. ipu_cm_write(ipu, reg,
  9. IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan));
  10. reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan));
  11. if (phyaddr_1)
  12. reg |= idma_mask(dma_chan);
  13. else
  14. reg &= ~idma_mask(dma_chan);
  15. ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan));
  16. /* Reset to buffer 0 */
  17. ipu_cm_write(ipu, idma_mask(dma_chan),
  18. IPU_CHA_CUR_BUF(ipu->devtype, dma_chan));
  19. }
  20. mutex_unlock(&ipu->mutex_lock);
  21. return 0;
  22. }
  23. EXPORT_SYMBOL(ipu_init_channel_buffer);

在这个函数中,有很多有特殊含义的行参,比如说stride,u_offset,v_offset等等,在这里分析的话,也不太懂是什么含义,不如在分析应用程序的时候,每个函数都有传入的实参值,那时候再具体分析。同时这个函数中用到了很多ipu_param_mem.h头文件中的函数,来填充CPMEM中的某一位或者某几位,下面就来分析ipu_param_mem.h这个头文件。

4.4 ipu_param_mem.h头文件分析

1.下面这两个结构体是本文件的核心结构体。

  1. struct ipu_ch_param_word {
  2. uint32_t data[5];
  3. uint32_t res[3];
  4. }; 
  5. struct ipu_ch_param {
  1. struct ipu_ch_param_word word[2];
  2. };

因为CPMEM是两个160位的word,所以每个word使用5个uint32_t类型来表示,同时有两个word。

2.这个宏暂时不分析,在后面用到的时候再分析。

#define ipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base) + (ch))

3._param_word宏

  1. #define _param_word(base, w) \
  2. (((struct ipu_ch_param *)(base))->word[(w)].data)

这个宏有两个参数,第一个参数是一个起始地址值,它一般是一个指针,在宏中会进行强制类型转化;第二个参数是第几个word,看ipu_ch_param结构体,它的取值为0和1。

这个宏的意思是根据base这个起始地址值取到里面的第w个word的data数据段。

4.ipu_ch_param_set_field宏

#define ipu_ch_param_set_field(base, w, bit, size, v) { \

  1. int i = (bit) / 32; \
  2. int off = (bit) % 32; \
  3. _param_word(base, w)[i] |= (v) << off; \
  4. if (((bit)+(size)-1)/32 > i) { \
  5. _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
  6. } \
  7. }

这个宏有5个参数,前两个参数与_param_word宏中的参数相同,它们也确实是给_param_word宏使用的。第三个参数bit表示某一个数据在通过_param_word宏所找到的data数据段中的准确起始bit位,因为data数据段是uint32_tdata[5]的,所以这个值的范围为0~160。size表示数据所占的位数。v表示传入的数据字面值。

这个宏的意思是首先通过(bit)/32来计算出这个数据的起始bit在data数组成员中的哪一个(因为data数组有5个成员)。然后off表示这个数据在data数组成员中的偏移值。然后通过_param_word(base,w)[i]来找到对应的data数组成员。同时这个宏中也给出了这个数据所占的位数:size,如果bit所在的data数组成员放不下这么多size数的话,就需要在data数组中的下一个数组成员中存储剩下的bit。

注意在数据的存储过程中涉及到大端小端的问题,对大端小端的解释:http://www.cnblogs.com/wuyuegb2312/archive/2013/06/08/3126510.html#《轻松记住大端小端的含义(附对大端和小端的解释)》

以下面这个函数中的各个数据为例来解释一下:

  1. static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
  2. int red_width, int red_offset,
  3. int green_width, int green_offset,
  4. int blue_width, int blue_offset,
  5. int alpha_width, int alpha_offset)
  6. {
  7. /* Setup red width and offset */
  8. ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
  9. ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
  10. /* Setup green width and offset */
  11. ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
  12. ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
  13. /* Setup blue width and offset */
  14. ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
  15. ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
  16. /* Setup alpha width and offset */
  17. ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
  18. ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
  19. }

首先以ipu_ch_param_set_field( p, 1, 116, 3, red_width – 1)为例:

  1. #define ipu_ch_param_set_field( base, w, bit, size, v) { \
  2. int i = (bit) / 32; \
  3. int off = (bit) % 32; \
  4. _param_word(base, w)[i] |= (v) << off; \
  5. if (((bit)+(size)-1)/32 > i) { \
  6. _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
  7. } \
  8. }

解释:

i= 116/32= 3;

off= 116%32 = 20;

  1. #define _param_word(base, w) \
  2. (((struct ipu_ch_param *)(base))->word[(w)].data)

_param_word(base,w)[i] |= (v) << off

==>p->word[w].data[i]|= (v) << off

==>p->word[1].data[3] |= (red_width – 1)<<20;

如下图所示:

然后继续往下执行:

  1. ipu_ch_param_set_field(p, 1, 119, 3, green_width – 1);
  2. ipu_ch_param_set_field(p, 1, 122, 3, blue_width – 1);
  3. ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);

同样,它最终就会在119的位置存储(green_width– 1),在122的位置存储(blue_width– 1),在125的位置存储(alpha_width– 1)。

它们详细表示如下:

同样对于

  1. ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
  2. ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
  3. ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
  4. ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);

它们详细表示如下:

再分析一个ipu_ch_param_set_field函数中w=1, bit = 125, size = 13 的情况,对大端小端理解的更清楚。

i= 125/32 = 3;

off= 125%32 = 29;

_param_word(base,w)[i] |= (v) << off

==>p->word[w].data[i] |= (v) << off

==>p->word[1].data[3] |= (v)<<29;

if(((bit)+(size)-1)/32 > i) (125+12)/32= 4 > 3成立,所以会执行if下面的语句:

_param_word(base,w)[i + 1] |= (v) >> (off ? (32 - off) : 0);

==>_param_word(base,w)[4] |= (v) >> 3

==>p->word[1].data[4]|=(v) >> 3

重要的部分我用红色标出了,

从这里可以看出来,它的存储方式是将v的前10位存在了data[4]中,而v的后3位存在了data[3]中,从这里可以看出来,数据的存储方式是小端模式。

5.ipu_ch_param_set_field_io宏

  1. #define ipu_ch_param_set_field_io(base, w, bit, size, v) { \
  2. int i = (bit) / 32; \
  3. int off = (bit) % 32; \
  4. unsigned reg_offset; \
  5. u32 temp; \
  6. reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
  7. reg_offset += i; \
  8. temp = readl((u32 *)base + reg_offset); \
  9. temp |= (v) << off; \
  10. writel(temp, (u32 *)base + reg_offset); \
  11. if (((bit)+(size)-1)/32 > i) { \
  12. reg_offset++; \
  13. temp = readl((u32 *)base + reg_offset); \
  14. temp |= (v) >> (off ? (32 - off) : 0); \
  15. writel(temp, (u32 *)base + reg_offset); \
  16. } \
  17. }

这个宏根据base,w,bit的值计算出寄存器的位置,然后将v的值写进去。

但是这个ipu_ch_param_set_field_io宏与上面那个ipu_ch_param_set_field宏有什么不同呢?

往下搜索源码可以发现,虽然这两个宏的第一个参数都是base,但是他们两个不相同:

  1. ipu_ch_param_set_field(¶ms, 0, 125, 13, width – 1);
  2. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1);

而这个ipu_ch_param_addr宏就是本文件开始的那个宏

#defineipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base)+ (ch))

这个宏根据ipu和ch两个参数来找到对应寄存器的基址,具体来说就是根据ipu_soc结构体里面存放的cpmem_base寄存器的地址,ch是一般是uint32_t类型的dmachannel,通过这两个参数来找到对应寄存器的基地址。所以这个cpmem_base寄存器是设置dmachannel的关键寄存器。

对比上面两条语句,可以发现ipu_ch_param_set_field宏用于设置structipu_ch_param结构体参数params中某些位的值。而ipu_ch_param_set_field_io宏根据传入的ipu和ch参数来找到某个寄存器的基址,然后修改这个寄存器中的某些位。

以下的几个宏都与这两种情况类似。

6.ipu_ch_param_mod_field宏

  1. #define ipu_ch_param_mod_field(base, w, bit, size, v) { \
  2. int i = (bit) / 32; \
  3. int off = (bit) % 32; \
  4. u32 mask = (1UL << size) - 1; \
  5. u32 temp = _param_word(base, w)[i]; \
  6. temp &= ~(mask << off); \
  7. _param_word(base, w)[i] = temp | (v) << off; \
  8. if (((bit)+(size)-1)/32 > i) { \
  9. temp = _param_word(base, w)[i + 1]; \
  10. temp &= ~(mask >> (32 - off)); \
  11. _param_word(base, w)[i + 1] = \
  12. temp | ((v) >> (off ? (32 - off) : 0)); \
  13. } \
  14. }

这个函数首先为size大小设置掩码,比如size=7,这个mask就等于111111(二进制),然后通过temp&= ~(mask << off)将这几位都清零,最后再通过temp| (v) << off将v的值写到这几位中。修改某些位值的时候,一定要先清零了再写。

7.ipu_ch_param_mod_field_io宏

  1. #define ipu_ch_param_mod_field_io(base, w, bit, size, v) { \
  2. int i = (bit) / 32; \
  3. int off = (bit) % 32; \
  4. u32 mask = (1UL << size) - 1; \
  5. unsigned reg_offset; \
  6. u32 temp; \
  7. reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
  8. reg_offset += i; \
  9. temp = readl((u32 *)base + reg_offset); \
  10. temp &= ~(mask << off); \
  11. temp |= (v) << off; \
  12. writel(temp, (u32 *)base + reg_offset); \
  13. if (((bit)+(size)-1)/32 > i) { \
  14. reg_offset++; \
  15. temp = readl((u32 *)base + reg_offset); \
  16. temp &= ~(mask >> (32 - off)); \
  17. temp |= ((v) >> (off ? (32 - off) : 0)); \
  18. writel(temp, (u32 *)base + reg_offset); \
  19. } \
  20. }

这个宏与上一个宏类似,修改寄存器中某些位的值。

8.ipu_ch_param_read_field宏

  1. #define ipu_ch_param_read_field(base, w, bit, size) ({ \
  2. u32 temp2; \
  3. int i = (bit) / 32; \
  4. int off = (bit) % 32; \
  5. u32 mask = (1UL << size) - 1; \
  6. u32 temp1 = _param_word(base, w)[i]; \
  7. temp1 = mask & (temp1 >> off); \
  8. if (((bit)+(size)-1)/32 > i) { \
  9. temp2 = _param_word(base, w)[i + 1]; \
  10. temp2 &= mask >> (off ? (32 - off) : 0); \
  11. temp1 |= temp2 << (off ? (32 - off) : 0); \
  12. } \
  13. temp1; \
  14. })

这个宏的意思是读取某些位的值,这个宏最后的结果是temp1的值。

9.ipu_ch_param_read_field_io宏

  1. #define ipu_ch_param_read_field_io(base, w, bit, size) ({ \
  2. u32 temp1, temp2; \
  3. int i = (bit) / 32; \
  4. int off = (bit) % 32; \
  5. u32 mask = (1UL << size) - 1; \
  6. unsigned reg_offset; \
  7. reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \
  8. reg_offset += i; \
  9. temp1 = readl((u32 *)base + reg_offset); \
  10. temp1 = mask & (temp1 >> off); \
  11. if (((bit)+(size)-1)/32 > i) { \
  12. reg_offset++; \
  13. temp2 = readl((u32 *)base + reg_offset); \
  14. temp2 &= mask >> (off ? (32 - off) : 0); \
  15. temp1 |= temp2 << (off ? (32 - off) : 0); \
  16. } \
  17. temp1; \
  18. })

这个宏的意思是读取某个寄存器中某些位的值,最后的结果是temp1。

10.__ipu_ch_get_third_buf_cpmem_num函数

  1. static inline int __ipu_ch_get_third_buf_cpmem_num(int ch)
  2. {
  3. switch (ch) {
  4. case 8:
  5. return 64;
  6. case 9:
  7. return 65;
  8. case 10:
  9. return 66;
  10. case 13:
  11. return 67;
  12. case 21:
  13. return 68;
  14. case 23:
  15. return 69;
  16. case 27:
  17. return 70;
  18. case 28:
  19. return 71;
  20. default:
  21. return -EINVAL;
  22. }
  23. return 0;
  24. }

这个函数的大致意思是从函数传入的参数ch中获取到第三个buffer的起始地址。

11._ipu_ch_params_set_packing函数

  1. static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
  2. int red_width, int red_offset,
  3. int green_width, int green_offset,
  4. int blue_width, int blue_offset,
  5. int alpha_width, int alpha_offset)
  6. {
  7. /* Setup red width and offset */
  8. ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
  9. ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
  10. /* Setup green width and offset */
  11. ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
  12. ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
  13. /* Setup blue width and offset */
  14. ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
  15. ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
  16. /* Setup alpha width and offset */
  17. ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
  18. ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
  19. }

这个函数在前面分析了,它主要目的是设置第一个参数p里面的red_width,red_offset,green_width,green_offset等信息。这个函数在_ipu_ch_param_init函数中调用,比如下面这样:

_ipu_ch_params_set_packing(&params,5, 0, 6, 5, 5, 11, 8, 16);

通过这样调用,就分别设置了params中的RGB的信息,从上面可以看出来是RGB565格式的。

_ipu_ch_params_set_packing(&params,4, 4, 4, 8, 4, 12, 4, 0);

这样调用设置的是RGBA4444的格式。

12._ipu_ch_param_dump函数

这个函数是输出ipu_ch_param中的一些信息,就不分析了。

13.fill_cpmem函数

  1. static inline void fill_cpmem(struct ipu_soc *ipu, int ch, struct ipu_ch_param *params)
  2. {
  3. int i, w;
  4. void *addr = ipu_ch_param_addr(ipu, ch);
  5. /* 2 words, 5 valid data */
  6. for (w = 0; w < 2; w++) {
  7. for (i = 0; i < 5; i++) {
  8. writel(params->word[w].data[i], addr);
  9. addr += 4;
  10. }
  11. addr += 12;
  12. }
  13. }

这个函数首先通过ipu_ch_param_addr函数根据ipu和ch参数取得dmachannel的基址,然后将params参数里面两个word里面的data数据填充到获得的这个基址中。这个函数被_ipu_ch_param_init函数中调用。

14._ipu_ch_param_init函数

  1. static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch,
  2. uint32_t pixel_fmt, uint32_t width,
  3. uint32_t height, uint32_t stride,
  4. uint32_t u, uint32_t v,
  5. uint32_t uv_stride, dma_addr_t addr0,
  6. dma_addr_t addr1, dma_addr_t addr2)
  7. {
  8. uint32_t u_offset = 0;
  9. uint32_t v_offset = 0;
  10. uint32_t bs = 0;
  11. int32_t sub_ch = 0;
  12. struct ipu_ch_param params;
  13. memset(&params<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }&params</style>, 0, sizeof(params));
  14. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>&params<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 125, 13, width - 1);

/*将params参数里面的word[0]里面的125位到138位设置为(width- 1) */

  1. if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) {
  2. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>&params<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 138, 12, (height / 2) - 1);
  3. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>&para<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms, 1, 102, 14, (stride * 2) – 1);

/*将params参数里面的word[0]里面的138位到150位设置为((height/ 2) - 1) */

/*将params参数里面的word[1]里面的102位到116位设置为((stride* 2) - 1) */

  1. } else {
  2. /* note: for vdoa+vdi- ch8/9/10, always use band mode */
  3. ipu_ch_param_set_field(<span style="background: transparent"></span>&para<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms, 0, 138, 12, height - 1);
  4. <span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span> ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>&para<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 102, 14, stride - 1);
  5. }

/*将params参数里面的word[0]里面的138位到150位设置为(height- 1) */

/*将params参数里面的word[1]里面的102位到116位设置为(stride- 1) */

如果channel是8/9/10的话,从注释上来看是vdoa+vdichannel,用的是bandmode,会将params那几位设置成height/2和stride*2。

  1. /* EBA is 8-byte aligned */
  2. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>&para<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 0, 29, addr0 >> 3);
  3. ipu_ch_param_set_field(<span style="font-family:Courier 10 Pitch;">&params</span>, 1, 29, 29, addr1 >> 3);
  4. if (addr0%8)
  5. dev_warn(ipu->dev,
  6. "IDMAC%d's EBA0 is not 8-byte aligned\n", ch);
  7. if (addr1%8)
  8. dev_warn(ipu->dev,
  9. "IDMAC%d's EBA1 is not 8-byte aligned\n", ch);

/*将params参数里面的word[1]里面的0位到29位设置为(addr0>> 3),29位到58位设置成addr1>> 3。原因是EBA是8字节对齐的,后面需要分析这里。*/

  1. switch (pixel_fmt) {
  2. case IPU_PIX_FMT_GENERIC:
  3. /*Represents 8-bit Generic data */
  4. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 107, 3, 5); /* bits/pixel */
  5. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 85, 4, 6); /* pix format */
  6. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 78, 7, 63); /* burst size */
  7. break;
  8. case IPU_PIX_FMT_GENERIC_16:
  9. /* Represents 16-bit generic data */
  10. ipu_ch_param_set_field(<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }&params</style><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 107, 3, 3); /* bits/pixel */
  11. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 85, 4, 6); /* pix format */
  12. ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">&params</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 78, 7, 31); /* burst size */
  13. break;
  14. case IPU_PIX_FMT_GENERIC_32:
  15. /*Represents 32-bit Generic data */
  16. break;
  17. case IPU_PIX_FMT_RGB565:
  18. ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
  19. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  20. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  21. _ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16);
  22. break;
  23. case IPU_PIX_FMT_BGRA4444:
  24. ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
  25. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  26. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  27. _ipu_ch_params_set_packing(¶ms, 4, 4, 4, 8, 4, 12, 4, 0);
  28. break;
  29. case IPU_PIX_FMT_BGRA5551:
  30. ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
  31. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  32. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  33. _ipu_ch_params_set_packing(¶ms, 5, 1, 5, 6, 5, 11, 1, 0);
  34. break;
  35. case IPU_PIX_FMT_BGR24:
  36. ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
  37. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  38. ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
  39. _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
  40. break;
  41. case IPU_PIX_FMT_RGB24:
  42. case IPU_PIX_FMT_YUV444:
  43. ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
  44. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  45. ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
  46. _ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24);
  47. break;
  48. case IPU_PIX_FMT_VYU444:
  49. ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
  50. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  51. ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
  52. _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 0, 8, 16, 8, 24);
  53. break;
  54. case IPU_PIX_FMT_AYUV:
  55. ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
  56. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  57. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  58. _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0);
  59. break;
  60. case IPU_PIX_FMT_BGRA32:
  61. case IPU_PIX_FMT_BGR32:
  62. ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
  63. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  64. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  65. _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0);
  66. break;
  67. case IPU_PIX_FMT_RGBA32:
  68. case IPU_PIX_FMT_RGB32:
  69. ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
  70. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  71. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  72. _ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0);
  73. break;
  74. case IPU_PIX_FMT_ABGR32:
  75. ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
  76. ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
  77. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  78. _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
  79. break;
  80. case IPU_PIX_FMT_UYVY:
  81. ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
  82. ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */
  83. if ((ch == 8) || (ch == 9) || (ch == 10)) {
  84. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  85. } else {
  86. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  87. }
  88. break;
  89. case IPU_PIX_FMT_YUYV:
  90. ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
  91. ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */
  92. if ((ch == 8) || (ch == 9) || (ch == 10)) {
  93. if (ipu->vdoa_en) {
  94. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31);
  95. } else {
  96. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15);
  97. }
  98. } else {
  99. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  100. }
  101. break;
  102. case IPU_PIX_FMT_YUV420P2:
  103. case IPU_PIX_FMT_YUV420P:
  104. ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */
  105. if (uv_stride < stride / 2)
  106. uv_stride = stride / 2;
  107. u_offset = stride * height;
  108. v_offset = u_offset + (uv_stride * height / 2);
  109. if ((ch == 8) || (ch == 9) || (ch == 10)) {
  110. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  111. uv_stride = uv_stride*2;
  112. } else {
  113. if (_ipu_is_smfc_chan(ch) &&
  114. ipu->smfc_idmac_12bit_3planar_bs_fixup)
  115. bs = 15;
  116. else
  117. bs = 31;
  118. ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */
  119. }
  120. break;
  121. case IPU_PIX_FMT_YVU420P:
  122. ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */
  123. if (uv_stride < stride / 2)
  124. uv_stride = stride / 2;
  125. v_offset = stride * height;
  126. u_offset = v_offset + (uv_stride * height / 2);
  127. if ((ch == 8) || (ch == 9) || (ch == 10)) {
  128. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
  129. uv_stride = uv_stride*2;
  130. } else {
  131. if (_ipu_is_smfc_chan(ch) &&
  132. ipu->smfc_idmac_12bit_3planar_bs_fixup)
  133. bs = 15;
  134. else
  135. bs = 31;
  136. ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */
  137. }
  138. break;
  139. case IPU_PIX_FMT_YVU422P:
  140. /* BPP & pixel format */
  141. ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
  142. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  143. if (uv_stride < stride / 2)
  144. uv_stride = stride / 2;
  145. v_offset = (v == 0) ? stride * height : v;
  146. u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
  147. break;
  148. case IPU_PIX_FMT_YUV422P:
  149. /* BPP & pixel format */
  150. ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
  151. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  152. if (uv_stride < stride / 2)
  153. uv_stride = stride / 2;
  154. u_offset = (u == 0) ? stride * height : u;
  155. v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
  156. break;
  157. case IPU_PIX_FMT_YUV444P:
  158. /* BPP & pixel format */
  159. ipu_ch_param_set_field(¶ms, 1, 85, 4, 0); /* pix format */
  160. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  161. uv_stride = stride;
  162. u_offset = (u == 0) ? stride * height : u;
  163. v_offset = (v == 0) ? u_offset * 2 : v;
  164. break;
  165. case IPU_PIX_FMT_NV16:
  166. ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
  167. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  168. uv_stride = stride;
  169. u_offset = (u == 0) ? stride * height : u;
  170. break;
  171. case IPU_PIX_FMT_NV12:
  172. /* BPP & pixel format */
  173. ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */
  174. uv_stride = stride;
  175. u_offset = (u == 0) ? stride * height : u;
  176. if ((ch == 8) || (ch == 9) || (ch == 10)) {
  177. if (ipu->vdoa_en) {
  178. /* one field buffer, memory width 64bits */
  179. ipu_ch_param_set_field(¶ms, 1, 78, 7, 63);
  180. } else {
  181. ipu_ch_param_set_field(¶ms, 1, 78, 7, 15);
  182. /* top/bottom field in one buffer*/
  183. uv_stride = uv_stride*2;
  184. }
  185. } else {
  186. ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
  187. }
  188. break;
  189. default:
  190. dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
  191. break;
  192. }
  193. /*set burst size to 16*/

/*根据函数传入的pixel_fmt参数的至来决定设置params里面的哪些位。从这个switch语句中可以看出来params参数里面word[0]里面从107位开始的几位决定bits/pixel,word[1]里面从85位开始的几位决定pixformat,word[1]里面从78位开始的几位决定burstsize。*/

  1. if (uv_stride)
  2. ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1);

/*如果uv_stride存在的话,就设置params参数里面的word[1]的128位到142位的值为(uv_stride- 1) */

  1. /* Get the uv offset from user when need cropping */
  2. if (u || v) {
  3. u_offset = u;
  4. v_offset = v;
  5. }
  6. /* UBO and VBO are 22-bit and 8-byte aligned */
  7. if (u_offset/8 > 0x3fffff)
  8. dev_warn(ipu->dev,
  9. "IDMAC%d's U offset exceeds IPU limitation\n", ch);
  10. if (v_offset/8 > 0x3fffff)
  11. dev_warn(ipu->dev,
  12. "IDMAC%d's V offset exceeds IPU limitation\n", ch);
  13. if (u_offset%8)
  14. dev_warn(ipu->dev,
  15. "IDMAC%d's U offset is not 8-byte aligned\n", ch);
  16. if (v_offset%8)
  17. dev_warn(ipu->dev,
  18. "IDMAC%d's V offset is not 8-byte aligned\n", ch);
  19. ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8);
  20. ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8);

/*上面这一段代码是设置u_offset和v_offset的值。他俩分别位于params->word[0]里面的46~68和68~90位。*/

  1. dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch));
  2. fill_cpmem(ipu, ch, ¶ms);

/*辛辛苦苦设置好了params的值,肯定需要将它使用起来,就是通过这个函数来将params填充到根据ipu和ch参数找到的基址中。*/

  1. if (addr2) { //在ipu_common.c中调用的_ipu_ch_param_init函数中有addr2这个值。
  2. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  3. if (sub_ch <= 0)
  4. return;

/*通过__ipu_ch_get_third_buf_cpmem_num函数来找到第三个buffer的基址。*/

  1. ipu_ch_param_set_field(¶ms, 1, 0, 29, addr2 >> 3);
  2. ipu_ch_param_set_field(¶ms, 1, 29, 29, 0);
  3. if (addr2%8)
  4. dev_warn(ipu->dev,
  5. "IDMAC%d's sub-CPMEM entry%d EBA0 is not "
  6. "8-byte aligned\n", ch, sub_ch);
  7. dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch,
  8. ipu_ch_param_addr(ipu, sub_ch));
  9. fill_cpmem(ipu, sub_ch, ¶ms);
  10. }
  11. };

最后这些设置是在某些情况下,比如之前分析过,可能会将几个channel一起启用。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。

15._ipu_ch_param_set_burst_size函数

  1. static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu,
  2. uint32_t ch,
  3. uint16_t burst_pixels)
  4. {
  5. int32_t sub_ch = 0;
  6. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7,
  7. burst_pixels - 1);
  8. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  9. if (sub_ch <= 0)
  10. return;
  11. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7,
  12. burst_pixels - 1);
  13. };

这个函数用来设置或修改burst_size的值。因为在_ipu_ch_param_init初始化函数中,已经根据pixel_fmt的值设置了burst_size的初始值,在这里可以继续通过ipu和ch的值来修改他们的burst_size。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。

16._ipu_ch_param_get_burst_size函数

  1. static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1;
  4. };

通过ipu和ch参数找到对应的寄存器然后读取它的值。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。

17._ipu_ch_param_get_bpp函数

  1. static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3);
  4. };

通过ipu和ch参数找到对应的寄存器然后读取它的值。

18._ipu_ch_param_set_buffer函数

  1. static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch,
  2. int bufNum, dma_addr_t phyaddr)
  3. {
  4. if (bufNum == 2) {
  5. ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  6. if (ch <= 0)
  7. return;
  8. bufNum = 0;
  9. }
  10. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29,
  11. phyaddr / 8);
  12. };

首先根据ipu和ch来获取基址,然后根据传入的bufNum参数来决定修改基址中的哪些位。这个函数被ipu_common.c中的ipu_update_channel_buffer函数调用。根据手册上面可以看出来,关于buffer的设置是在word[1]中从0~28,59~57,而且根据后面的分析,对于双buffer模式,这个bufNum的取值是0或者1.

19._ipu_ch_param_set_rotation函数

  1. static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch,
  2. ipu_rotate_mode_t rot)
  3. {
  4. u32 temp_rot = bitrev8(rot) >> 5;
  5. int32_t sub_ch = 0;
  6. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot);
  7. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  8. if (sub_ch <= 0)
  9. return;
  10. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot);
  11. };

设置rotation参数,这个函数首先根据rot的值通过bitrev8函数从byte_rev_table数组中取出对应的值,然后将那个值设置到word[0]的119~121位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

20._ipu_ch_param_set_block_mode函数

  1. static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. int32_t sub_ch = 0;
  4. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1);
  5. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  6. if (sub_ch <= 0)
  7. return;
  8. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1);
  9. };

设置block_mode参数,这个block_mode参数应该是位于word[0]里面的117~119位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

21._ipu_ch_param_set_alpha_use_separate_channel函数

  1. static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu,
  2. uint32_t ch,
  3. bool option)
  4. {
  5. int32_t sub_ch = 0;
  6. if (option) {
  7. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1);
  8. } else {
  9. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0);
  10. }

/*根据option这个bool类型的值来设置ch对应的基址中word[1]的89位。*/

  1. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  2. if (sub_ch <= 0)
  3. return;
  4. if (option) {
  5. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1);
  6. } else {
  7. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0);
  8. }
  9. };

如果有第三个buffer的话,就设置sub_ch对应的基址中word[1]的89位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

22._ipu_ch_param_set_alpha_condition_read函数

  1. static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. int32_t sub_ch = 0;
  4. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1);
  5. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  6. if (sub_ch <= 0)
  7. return;
  8. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1);
  9. };

这个函数的名字里面有read,但是里面调用的是ipu_ch_param_mod_field_io函数,他用来将ch对应的基址里面的word[1]的149位修改为1。如果有sub_ch的话,就同时修改sub_ch所对应的基址。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

23._ipu_ch_param_set_alpha_buffer_memory函数

  1. static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. int alp_mem_idx;
  4. int32_t sub_ch = 0;
  5. switch (ch) {
  6. case 14: /* PRP graphic */
  7. alp_mem_idx = 0;
  8. break;
  9. case 15: /* PP graphic */
  10. alp_mem_idx = 1;
  11. break;
  12. case 23: /* DP BG SYNC graphic */
  13. alp_mem_idx = 4;
  14. break;
  15. case 27: /* DP FG SYNC graphic */
  16. alp_mem_idx = 2;
  17. break;
  18. default:
  19. dev_err(ipu->dev, "unsupported correlative channel of local "
  20. "alpha channel\n");
  21. return;
  22. }

/*根据ch的值设置alp_mem_idx的值。*/

  1. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx);
  2. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  3. if (sub_ch <= 0)
  4. return;
  5. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx);
  6. };

这个函数首先根据ch的值来确定alp_mem_idx。然后调用ipu_ch_param_mod_field_io将ch对应基址里面的word[1]里面的90~93位修改成alp_mem_idx的值。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

24._ipu_ch_param_set_interlaced_scan函数

  1. static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. u32 stride;
  4. int32_t sub_ch = 0;
  5. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  6. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1);
  7. if (sub_ch > 0)
  8. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1);
  9. stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1;
  10. /* ILO is 20-bit and 8-byte aligned */
  11. if (stride/8 > 0xfffff)
  12. dev_warn(ipu->dev,
  13. "IDMAC%d's ILO exceeds IPU limitation\n", ch);
  14. if (stride%8)
  15. dev_warn(ipu->dev,
  16. "IDMAC%d's ILO is not 8-byte aligned\n", ch);
  17. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8);
  18. if (sub_ch > 0)
  19. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20,
  20. stride / 8);
  21. stride *= 2;
  22. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1);
  23. if (sub_ch > 0)
  24. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14,
  25. stride - 1);
  26. };

首先通过ipu_ch_param_set_field_io函数将ch对应基址里面word[0]的113位设为1。如果存在sub_ch的话,将sub_ch对应的基址里面word[0]的113位设为1。之后通过ipu_ch_param_read_field_io函数读取ch对应基址里面word[1]的102~116位的值来求出stride的值。之后通过ipu_ch_param_mod_field_io函数将ch对应基址里面的word[1]的58~78位设置为(stride/ 8),将ch对应基址里面的word[1]的102~116位设置为(stride-1),如果存在sub_ch的话需要做同样的操作。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。

25._ipu_ch_param_set_axi_id函数

  1. static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id)
  2. {
  3. int32_t sub_ch = 0;
  4. id %= 4;
  5. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id);
  6. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  7. if (sub_ch <= 0)
  8. return;
  9. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id);
  10. };

根据传入的id参数来设置ch对应的基址里面word[1]的93~94位,这一位是关于axi_id的。

26._ipu_ch_param_get_axi_id函数

  1. static inline int _ipu_ch_param_get_axi_id(struct ipu_soc *ipu, uint32_t ch)
  2. {
  3. return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2);
  4. }

这个函数就是读取ch对应的基址里面word[1]的93~94位。这个函数被ipu_common.c中的ipu_ch_param_get_axi_id函数调用。

27.__ipu_ch_offset_calc函数

  1. static inline int __ipu_ch_offset_calc(uint32_t pixel_fmt,
  2. uint32_t width,
  3. uint32_t height,
  4. uint32_t stride,
  5. uint32_t u,
  6. uint32_t v,
  7. uint32_t uv_stride,
  8. uint32_t vertical_offset,
  9. uint32_t horizontal_offset,
  10. uint32_t *u_offset,
  11. uint32_t *v_offset)
  12. {
  13. uint32_t u_fix = 0;
  14. uint32_t v_fix = 0;
  15. switch (pixel_fmt) {
  16. case IPU_PIX_FMT_GENERIC:
  17. case IPU_PIX_FMT_GENERIC_16:
  18. case IPU_PIX_FMT_GENERIC_32:
  19. case IPU_PIX_FMT_RGB565:
  20. case IPU_PIX_FMT_BGR24:
  21. case IPU_PIX_FMT_RGB24:
  22. case IPU_PIX_FMT_YUV444:
  23. case IPU_PIX_FMT_BGRA32:
  24. case IPU_PIX_FMT_BGR32:
  25. case IPU_PIX_FMT_RGBA32:
  26. case IPU_PIX_FMT_RGB32:
  27. case IPU_PIX_FMT_ABGR32:
  28. case IPU_PIX_FMT_UYVY:
  29. case IPU_PIX_FMT_YUYV:
  30. case IPU_PIX_FMT_GPU32_SB_ST:
  31. case IPU_PIX_FMT_GPU32_SB_SRT:
  32. case IPU_PIX_FMT_GPU32_ST:
  33. case IPU_PIX_FMT_GPU32_SRT:
  34. case IPU_PIX_FMT_GPU16_SB_ST:
  35. case IPU_PIX_FMT_GPU16_SB_SRT:
  36. case IPU_PIX_FMT_GPU16_ST:
  37. case IPU_PIX_FMT_GPU16_SRT:
  38. *u_offset = 0;
  39. *v_offset = 0;
  40. break;
  41. case IPU_PIX_FMT_YUV420P2:
  42. case IPU_PIX_FMT_YUV420P:
  43. if (uv_stride < stride / 2)
  44. uv_stride = stride / 2;
  45. *u_offset = stride * (height - vertical_offset - 1) +
  46. (stride - horizontal_offset) +
  47. (uv_stride * vertical_offset / 2) +
  48. horizontal_offset / 2;
  49. *v_offset = *u_offset + (uv_stride * height / 2);
  50. u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
  51. (horizontal_offset / 2) -
  52. (stride * vertical_offset) - (horizontal_offset)) :
  53. *u_offset;
  54. v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
  55. (horizontal_offset / 2) -
  56. (stride * vertical_offset) - (horizontal_offset)) :
  57. *v_offset;
  58. break;
  59. case IPU_PIX_FMT_YVU420P:
  60. if (uv_stride < stride / 2)
  61. uv_stride = stride / 2;
  62. *v_offset = stride * (height - vertical_offset - 1) +
  63. (stride - horizontal_offset) +
  64. (uv_stride * vertical_offset / 2) +
  65. horizontal_offset / 2;
  66. *u_offset = *v_offset + (uv_stride * height / 2);
  67. u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
  68. (horizontal_offset / 2) -
  69. (stride * vertical_offset) - (horizontal_offset)) :
  70. *u_offset;
  71. v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
  72. (horizontal_offset / 2) -
  73. (stride * vertical_offset) - (horizontal_offset)) :
  74. *v_offset;
  75. break;
  76. case IPU_PIX_FMT_YVU422P:
  77. if (uv_stride < stride / 2)
  78. uv_stride = stride / 2;
  79. *v_offset = stride * (height - vertical_offset - 1) +
  80. (stride - horizontal_offset) +
  81. (uv_stride * vertical_offset) +
  82. horizontal_offset / 2;
  83. *u_offset = *v_offset + uv_stride * height;
  84. u_fix = u ? (u + (uv_stride * vertical_offset) +
  85. horizontal_offset / 2 -
  86. (stride * vertical_offset) - (horizontal_offset)) :
  87. *u_offset;
  88. v_fix = v ? (v + (uv_stride * vertical_offset) +
  89. horizontal_offset / 2 -
  90. (stride * vertical_offset) - (horizontal_offset)) :
  91. *v_offset;
  92. break;
  93. case IPU_PIX_FMT_YUV422P:
  94. if (uv_stride < stride / 2)
  95. uv_stride = stride / 2;
  96. *u_offset = stride * (height - vertical_offset - 1) +
  97. (stride - horizontal_offset) +
  98. (uv_stride * vertical_offset) +
  99. horizontal_offset / 2;
  100. *v_offset = *u_offset + uv_stride * height;
  101. u_fix = u ? (u + (uv_stride * vertical_offset) +
  102. horizontal_offset / 2 -
  103. (stride * vertical_offset) - (horizontal_offset)) :
  104. *u_offset;
  105. v_fix = v ? (v + (uv_stride * vertical_offset) +
  106. horizontal_offset / 2 -
  107. (stride * vertical_offset) - (horizontal_offset)) :
  108. *v_offset;
  109. break;
  110. case IPU_PIX_FMT_YUV444P:
  111. uv_stride = stride;
  112. *u_offset = stride * (height - vertical_offset - 1) +
  113. (stride - horizontal_offset) +
  114. (uv_stride * vertical_offset) +
  115. horizontal_offset;
  116. *v_offset = *u_offset + uv_stride * height;
  117. u_fix = u ? (u + (uv_stride * vertical_offset) +
  118. horizontal_offset -
  119. (stride * vertical_offset) -
  120. (horizontal_offset)) :
  121. *u_offset;
  122. v_fix = v ? (v + (uv_stride * vertical_offset) +
  123. horizontal_offset -
  124. (stride * vertical_offset) -
  125. (horizontal_offset)) :
  126. *v_offset;
  127. break;
  128. case IPU_PIX_FMT_NV12:
  129. case IPU_PIX_FMT_NV16:
  130. case PRE_PIX_FMT_NV21:
  131. case PRE_PIX_FMT_NV61:
  132. uv_stride = stride;
  133. *u_offset = stride * (height - vertical_offset - 1) +
  134. (stride - horizontal_offset) +
  135. (uv_stride * vertical_offset / 2) +
  136. horizontal_offset;
  137. *v_offset = 0;
  138. u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
  139. horizontal_offset -
  140. (stride * vertical_offset) - (horizontal_offset)) :
  141. *u_offset;
  142. break;
  143. default:
  144. return -EINVAL;
  145. }
  146. if (u_fix > *u_offset)
  147. *u_offset = u_fix;
  148. if (v_fix > *v_offset)
  149. *v_offset = v_fix;
  150. return 0;
  151. }

这个函数一共有11个参数,它的最终目的是根据前9个参数设置最后2个参数的值。这个函数被本文件中_ipu_ch_offset_update函数和ipu_common.c文件中ipu_get_channel_offset函数调用。

这个函数就是根据数据格式来算出u_offset和v_offset的值,以后根据各种格式来仔细算算。

28._ipu_ch_offset_update函数

  1. /* IDMAC U/V offset changing support */
  2. /* U and V input is not affected, */
  3. /* the update is done by new calculation according to */
  4. /* vertical_offset and horizontal_offset */
  5. static inline void _ipu_ch_offset_update(struct ipu_soc *ipu,
  6. int ch,
  7. uint32_t pixel_fmt,
  8. uint32_t width,
  9. uint32_t height,
  10. uint32_t stride,
  11. uint32_t u,
  12. uint32_t v,
  13. uint32_t uv_stride,
  14. uint32_t vertical_offset,
  15. uint32_t horizontal_offset)
  16. {
  17. uint32_t u_offset = 0;
  18. uint32_t v_offset = 0;
  19. uint32_t old_offset = 0;
  20. int32_t sub_ch = 0;
  21. int ret;
  22. ret = __ipu_ch_offset_calc(pixel_fmt, width, height, stride,
  23. u, v, uv_stride,
  24. vertical_offset, horizontal_offset,
  25. &u_offset, &v_offset);
  26. if (ret) {
  27. dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
  28. return;
  29. }
  30. /* UBO and VBO are 22-bit and 8-byte aligned */
  31. if (u_offset/8 > 0x3fffff)
  32. dev_warn(ipu->dev,
  33. "IDMAC%d's U offset exceeds IPU limitation\n", ch);
  34. if (v_offset/8 > 0x3fffff)
  35. dev_warn(ipu->dev,
  36. "IDMAC%d's V offset exceeds IPU limitation\n", ch);
  37. if (u_offset%8)
  38. dev_warn(ipu->dev,
  39. "IDMAC%d's U offset is not 8-byte aligned\n", ch);
  40. if (v_offset%8)
  41. dev_warn(ipu->dev,
  42. "IDMAC%d's V offset is not 8-byte aligned\n", ch);
  43. old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22);
  44. if (old_offset != u_offset / 8)
  45. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8);
  46. old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22);
  47. if (old_offset != v_offset / 8)
  48. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8);
  49. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  50. if (sub_ch <= 0)
  51. return;
  52. old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22);
  53. if (old_offset != u_offset / 8)
  54. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8);
  55. old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22);
  56. if (old_offset != v_offset / 8)
  57. ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8);
  58. };

从这个函数的名字可以看出来,ipuchannel offset update:ipuchannel偏移更新,这个函数首先通过上一个__ipu_ch_offset_calc函数来计算出&u_offset和&v_offset,然后根据传入的ipu和ch参数来读取ch对应基址word[0]的46~68位中的老&u_offset偏移值和ch对应基址word[0]的68~90位中的老&v_offset偏移值,然后修改它们。重点还是u_offset和v_offset这两个值。

29._ipu_ch_params_set_alpha_width函数

  1. static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width)
  2. {
  3. int32_t sub_ch = 0;
  4. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1);
  5. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  6. if (sub_ch <= 0)
  7. return;
  8. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1);
  9. };

这个函数同样就是设置ch对应基址里面word[1]的125~128位,将它设置为(alpha_width– 1),这一位应该就是关于alpha_width的。

30._ipu_ch_param_set_bandmode函数

  1. static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu,
  2. uint32_t ch, uint32_t band_height)
  3. {
  4. int32_t sub_ch = 0;
  5. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch),
  6. 0, 114, 3, band_height - 1);
  7. sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
  8. if (sub_ch <= 0)
  9. return;
  10. ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch),
  11. 0, 114, 3, band_height - 1);
  12. dev_dbg(ipu->dev, "BNDM 0x%x, ",
  13. ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3));
  14. }

这个函数同样就是设置ch对应基址里面word[0]的114~117位,将它设置为(band_height– 1),这一位应该就是关于band_height的。

31._ipu_ch_param_bad_alpha_pos函数

  1. /*
  2. * The IPUv3 IDMAC has a bug to read 32bpp pixels from a graphics plane
  3. * whose alpha component is at the most significant 8 bits. The bug only
  4. * impacts on cases in which the relevant separate alpha channel is enabled.
  5. *
  6. * Return true on bad alpha component position, otherwise, return false.
  7. */
  8. static inline bool _ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt)
  9. {
  10. switch (pixel_fmt) {
  11. case IPU_PIX_FMT_BGRA32:
  12. case IPU_PIX_FMT_BGR32:
  13. case IPU_PIX_FMT_RGBA32:
  14. case IPU_PIX_FMT_RGB32:
  15. return true;
  16. }
  17. return false;
  18. }

这个注释里面写的很清楚了,当IPUv3IDMAC 在从一个alpha组件位于最重要8位的图像位面读取32bpp像素的时候有一个bug,所以当pixel_fmt为32位的时候,就返回true,否则返回false。

4.5 ipu_request_irq函数详细分析

ipu_request_irq函数

  1. int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
  2. irqreturn_t(*handler) (int, void *),
  3. uint32_t irq_flags, const char *devname, void *dev_id)
  4. {
  5. uint32_t reg;
  6. unsigned long lock_flags;
  7. int ret = 0;
  8. BUG_ON(irq >= IPU_IRQ_COUNT);
  9. /* 如果要申请的中断号大于 IPU_IRQ_COUNT的话,就会报错。 */
  10. _ipu_get(ipu);
  11. spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags);
  12. if (ipu->irq_list[irq].handler != NULL) {
  13. dev_err(ipu->dev,
  14. "handler already installed on irq %d\n", irq);
  15. ret = -EINVAL;
  16. goto out;
  17. }
  18. /* 如果这个irq中断号对应的数组中已经绑定了ISP的话,也会报错。 */
  19. /*
  20. * Check sync interrupt handler only, since we do nothing for
  21. * error interrupts but than print out register values in the
  22. * error interrupt source handler.
  23. */
  24. if (_ipu_is_sync_irq(irq) && (handler == NULL)) {
  25. dev_err(ipu->dev, "handler is NULL for sync irq %d\n", irq);
  26. ret = -EINVAL;
  27. goto out;
  28. }
  29. ipu->irq_list[irq].handler = handler;
  30. ipu->irq_list[irq].flags = irq_flags;
  31. ipu->irq_list[irq].dev_id = dev_id;
  32. ipu->irq_list[irq].name = devname;
  33. /* 将函数传入的参数保存咋数组中irq这个下标所对应的元素中。 */
  34. /* clear irq stat for previous use */
  35. ipu_cm_write(ipu, IPUIRQ_2_MASK(irq),
  36. IPUIRQ_2_STATREG(ipu->devtype, irq));
  37. /* 清空之前的状态。 */
  38. /* enable the interrupt */
  39. reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
  40. reg |= IPUIRQ_2_MASK(irq);
  41. ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
  42. /* 使能中断。 */
  43. out:
  44. spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags);
  45. _ipu_put(ipu);
  46. return ret;
  47. }
  48. EXPORT_SYMBOL(ipu_request_irq);

这个函数被很多函数所调用,以ipu_device.c中do_task函数为例,这个do_task函数首先通过:

irq= get_irq(t);来获取到中断号,然后通过:

ret= ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t);来将task_irq_handler这个中断服务程序(InterruptService Routines,ISR)与irq中断号绑定。那么再看查看这个ipu_request_irq中都做了哪些工作:

在structipu_soc中,有一项structipu_irq_node irq_list[IPU_IRQ_COUNT];

  1. struct ipu_irq_node {
  2. irqreturn_t(*handler) (int, void *); /*!< the ISR */
  3. const char *name; /*!< device associated with the interrupt */
  4. void *dev_id; /*!< some unique information for the ISR */
  5. __u32 flags; /*!< not used */
  6. };

每一个中断号都在这个数组中对应一项,如果这个中断号没有被申请的话,这一项就会为空。通过这个ipu_request_irq函数来申请的时候,就会将数组中这一项根据函数传入的参数都填充进去。

4.6 ipu_enable_channel函数详细分析

  1. int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel)
  2. {
  3. uint32_t reg;
  4. uint32_t ipu_conf;
  5. uint32_t in_dma;
  6. uint32_t out_dma;
  7. uint32_t sec_dma;
  8. uint32_t thrd_dma;
  9. mutex_lock(&ipu->mutex_lock);
  10. if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
  11. dev_err(ipu->dev, "Warning: channel already enabled %d\n",
  12. IPU_CHAN_ID(channel));
  13. mutex_unlock(&ipu->mutex_lock);
  14. return -EACCES;
  15. }

/*ipu->channel_enable_mask中每一位对应一个channel是否使能了。首先检查要使能的这个channel是否已经使能了,如果已经使能了的话就会报错。*/

  1. /* Get input and output dma channels */
  2. out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
  3. in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);

/*通过这两步将channel转化成dma_ch,在之前分析过这个函数,会根据type类型从channel中选取出输入或者输出的dmachannel. */

  1. ipu_conf = ipu_cm_read(ipu, IPU_CONF);
  2. if (ipu->di_use_count[0] > 0) {
  3. ipu_conf |= IPU_CONF_DI0_EN;
  4. }
  5. if (ipu->di_use_count[1] > 0) {
  6. ipu_conf |= IPU_CONF_DI1_EN;
  7. }
  8. if (ipu->dp_use_count > 0)
  9. ipu_conf |= IPU_CONF_DP_EN;
  10. if (ipu->dc_use_count > 0)
  11. ipu_conf |= IPU_CONF_DC_EN;
  12. if (ipu->dmfc_use_count > 0)
  13. ipu_conf |= IPU_CONF_DMFC_EN;
  14. if (ipu->ic_use_count > 0)
  15. ipu_conf |= IPU_CONF_IC_EN;
  16. if (ipu->vdi_use_count > 0) {
  17. ipu_conf |= IPU_CONF_ISP_EN;
  18. ipu_conf |= IPU_CONF_VDI_EN;
  19. ipu_conf |= IPU_CONF_IC_INPUT;
  20. }
  21. if (ipu->rot_use_count > 0)
  22. ipu_conf |= IPU_CONF_ROT_EN;
  23. if (ipu->smfc_use_count > 0)
  24. ipu_conf |= IPU_CONF_SMFC_EN;
  25. ipu_cm_write(ipu, ipu_conf, IPU_CONF);

/*根据ipu参数里面的引用计数来决定将ipu_conf寄存器中的对应位置位。最终将这些值都写到这个ipu_conf寄存器中。*/

  1. if (idma_is_valid(in_dma)) {
  2. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
  3. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
  4. }
  5. if (idma_is_valid(out_dma)) {
  6. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
  7. ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
  8. }

/*通过这几个函数来将in_dma和out_dma的值写到ipu里面的idmac寄存器中。*/

  1. if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
  3. (channel == MEM_VDI_PRP_VF_MEM))) {
  4. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  5. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
  6. ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
  7. }

/*ipu_soc结构体中sec_chan_en是一个bool类型的数组,它会根据channel通过IPU_CHAN_ID转化成的数字来从这个数组中找到对应的一项,如果使能了secondchannel的话,就是true,否则就是false。同时channel的需要是PP,PRP_VF,VDI_PRP_VF的情况,如果条件都成立的话,同样会设置idmac寄存器里面的某些位。*/

  1. if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
  3. thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
  4. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
  5. ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
  6. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  7. reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
  8. ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
  9. }

/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是PP和PRP_VF。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/

  1. else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
  3. thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
  4. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
  5. ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
  6. reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
  7. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
  8. }

/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是BG和FG。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/

  1. if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
  2. (channel == MEM_FG_SYNC)) {
  3. reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
  4. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
  5. _ipu_dp_dc_enable(ipu, channel);
  6. }

/*这几个通道是关于输出显示的通道,个人理解是displaycontroll sync, background sync和foregroundsyncchannel,这时肯定不能仅仅设置idmac寄存器,同时需要调用_ipu_dp_dc_enable函数来使能显示设备,这个函数在ipu_disp.c中定义。*/

  1. if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
  2. _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
  3. _ipu_is_vdi_out_chan(out_dma))
  4. _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是否已经使能过了。*/

  1. if (ipu->prg_clk)
  2. clk_prepare_enable(ipu->prg_clk);
  3. mutex_unlock(&ipu->mutex_lock);
  4. return 0;
  5. }
  6. 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文件中的重要函数都分析完毕。

  1. int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel)
  2. {
  3. uint32_t reg;
  4. uint32_t ipu_conf;
  5. uint32_t in_dma;
  6. uint32_t out_dma;
  7. uint32_t sec_dma;
  8. uint32_t thrd_dma;
  9. mutex_lock(&ipu->mutex_lock);
  10. if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
  11. dev_err(ipu->dev, "Warning: channel already enabled %d\n",
  12. IPU_CHAN_ID(channel));
  13. mutex_unlock(&ipu->mutex_lock);
  14. return -EACCES;
  15. }

/*ipu->channel_enable_mask中每一位对应一个channel是否使能了。首先检查要使能的这个channel是否已经使能了,如果已经使能了的话就会报错。*/

  1. /* Get input and output dma channels */
  2. out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
  3. in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);

/*通过这两步将channel转化成dma_ch,在之前分析过这个函数,会根据type类型从channel中选取出输入或者输出的dmachannel. */

  1. ipu_conf = ipu_cm_read(ipu, IPU_CONF);
  2. if (ipu->di_use_count[0] > 0) {
  3. ipu_conf |= IPU_CONF_DI0_EN;
  4. }
  5. if (ipu->di_use_count[1] > 0) {
  6. ipu_conf |= IPU_CONF_DI1_EN;
  7. }
  8. if (ipu->dp_use_count > 0)
  9. ipu_conf |= IPU_CONF_DP_EN;
  10. if (ipu->dc_use_count > 0)
  11. ipu_conf |= IPU_CONF_DC_EN;
  12. if (ipu->dmfc_use_count > 0)
  13. ipu_conf |= IPU_CONF_DMFC_EN;
  14. if (ipu->ic_use_count > 0)
  15. ipu_conf |= IPU_CONF_IC_EN;
  16. if (ipu->vdi_use_count > 0) {
  17. ipu_conf |= IPU_CONF_ISP_EN;
  18. ipu_conf |= IPU_CONF_VDI_EN;
  19. ipu_conf |= IPU_CONF_IC_INPUT;
  20. }
  21. if (ipu->rot_use_count > 0)
  22. ipu_conf |= IPU_CONF_ROT_EN;
  23. if (ipu->smfc_use_count > 0)
  24. ipu_conf |= IPU_CONF_SMFC_EN;
  25. ipu_cm_write(ipu, ipu_conf, IPU_CONF);

/*根据ipu参数里面的引用计数来决定将ipu_conf寄存器中的对应位置位。最终将这些值都写到这个ipu_conf寄存器中。*/

  1. if (idma_is_valid(in_dma)) {
  2. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
  3. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
  4. }
  5. if (idma_is_valid(out_dma)) {
  6. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
  7. ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
  8. }

/*通过这几个函数来将in_dma和out_dma的值写到ipu里面的idmac寄存器中。*/

  1. if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
  3. (channel == MEM_VDI_PRP_VF_MEM))) {
  4. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  5. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
  6. ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
  7. }

/*ipu_soc结构体中sec_chan_en是一个bool类型的数组,它会根据channel通过IPU_CHAN_ID转化成的数字来从这个数组中找到对应的一项,如果使能了secondchannel的话,就是true,否则就是false。同时channel的需要是PP,PRP_VF,VDI_PRP_VF的情况,如果条件都成立的话,同样会设置idmac寄存器里面的某些位。*/

  1. if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
  3. thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
  4. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
  5. ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
  6. sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
  7. reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
  8. ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
  9. }

/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是PP和PRP_VF。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/

  1. else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
  2. ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
  3. thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
  4. reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
  5. ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
  6. reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
  7. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
  8. }

/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是BG和FG。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/

  1. if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
  2. (channel == MEM_FG_SYNC)) {
  3. reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
  4. ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
  5. _ipu_dp_dc_enable(ipu, channel);
  6. }

/*这几个通道是关于输出显示的通道,个人理解是displaycontroll sync, background sync和foregroundsyncchannel,这时肯定不能仅仅设置idmac寄存器,同时需要调用_ipu_dp_dc_enable函数来使能显示设备,这个函数在ipu_disp.c中定义。*/

  1. if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
  2. _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
  3. _ipu_is_vdi_out_chan(out_dma))
  4. _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是否已经使能过了。*/

  1. if (ipu->prg_clk)
  2. clk_prepare_enable(ipu->prg_clk);
  3. mutex_unlock(&ipu->mutex_lock);
  4. return 0;
  5. }
  6. 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函数

  1. int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. uint32_t div_ratio;
  5. div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1;
  6. if (div_ratio > 0xFF || div_ratio < 0) {
  7. dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n");
  8. return -EINVAL;
  9. }
  10. temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
  11. temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
  12. ipu_csi_write(ipu, csi, temp |
  13. (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
  14. CSI_SENS_CONF);
  15. return 0;
  16. }

这个函数是设置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函数调用。

  1. int32_t
  2. ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
  3. uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
  4. {
  5. uint32_t data = 0;
  6. uint32_t csi = cfg_param.csi;
  7. /* Set SENS_DATA_FORMAT bits (8, 9 and 10)
  8. RGB or YUV444 is 0 which is current value in data so not set
  9. explicitly
  10. This is also the default value if attempts are made to set it to
  11. something invalid. */
  12. switch (pixel_fmt) {
  13. case IPU_PIX_FMT_YUYV:
  14. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
  15. break;
  16. case IPU_PIX_FMT_UYVY:
  17. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
  18. break;
  19. case IPU_PIX_FMT_RGB24:
  20. case IPU_PIX_FMT_BGR24:
  21. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
  22. break;
  23. case IPU_PIX_FMT_GENERIC:
  24. case IPU_PIX_FMT_GENERIC_16:
  25. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
  26. break;
  27. case IPU_PIX_FMT_RGB565:
  28. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
  29. break;
  30. case IPU_PIX_FMT_RGB555:
  31. cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
  32. break;
  33. default:
  34. return -EINVAL;
  35. }

/*首先根据传进来的不同pixel_fmt参数的值,设置cfg_param.data_fmt的值。*/

  1. /* Set the CSI_SENS_CONF register remaining fields */
  2. data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
  3. cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
  4. cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
  5. cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
  6. cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
  7. cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
  8. cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
  9. cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
  10. cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
  11. cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
  12. cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
  13. _ipu_get(ipu);
  14. mutex_lock(&ipu->mutex_lock);
  15. ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);

/*设置CSI_SENS_CONF寄存器的值。*/

  1. /* Setup sensor frame size */
  2. ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);

/*根据传进来的width和height参数,设置CSI_SENS_FRM_SIZE寄存器的值。*/

  1. /* Set CCIR registers */
  2. if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
  3. ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
  4. ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
  5. } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
  6. if (width == 720 && height == 625) {
  7. /* PAL case */
  8. /*
  9. * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
  10. * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
  11. */
  12. ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
  13. /*
  14. * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
  15. * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
  16. */
  17. ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);
  18. ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
  19. } else if (width == 720 && height == 525) {
  20. /* NTSC case */
  21. /*
  22. * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
  23. * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
  24. */
  25. ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
  26. /*
  27. * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
  28. * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
  29. */
  30. ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
  31. ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
  32. } else {
  33. dev_err(ipu->dev, "Unsupported CCIR656 interlaced "
  34. "video mode\n");
  35. mutex_unlock(&ipu->mutex_lock);
  36. _ipu_put(ipu);
  37. return -EINVAL;
  38. }
  39. _ipu_csi_ccir_err_detection_enable(ipu, csi);
  40. } else if ((cfg_param.clk_mode ==
  41. IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
  42. (cfg_param.clk_mode ==
  43. IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
  44. (cfg_param.clk_mode ==
  45. IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
  46. (cfg_param.clk_mode ==
  47. IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
  48. ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
  49. ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
  50. _ipu_csi_ccir_err_detection_enable(ipu, csi);
  51. } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
  52. (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
  53. _ipu_csi_ccir_err_detection_disable(ipu, csi);
  54. }

/*根据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中分析。*/

  1. dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
  2. ipu_csi_read(ipu, csi, CSI_SENS_CONF));
  3. dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
  4. ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE));
  5. mutex_unlock(&ipu->mutex_lock);
  6. _ipu_put(ipu);
  7. return 0;
  8. }
  9. EXPORT_SYMBOL(ipu_csi_init_interface);

3.ipu_csi_get_sensor_protocol函数,这个函数会在ipu_fg_overlay_sdc.c文件中的csi_enc_setup函数中调用:

  1. int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. int ret;
  4. _ipu_get(ipu);
  5. ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) &
  6. CSI_SENS_CONF_SENS_PRTCL_MASK) >>
  7. CSI_SENS_CONF_SENS_PRTCL_SHIFT;
  8. _ipu_put(ipu);
  9. return ret;
  10. }
  11. 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函数。

  1. int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait)
  2. {
  3. /* Return immediately if there is no csi_clk to manage */
  4. if (ipu->csi_clk[csi] == NULL)
  5. return 0;
  6. if (flag) {
  7. clk_enable(ipu->csi_clk[csi]);
  8. if (wait == true)
  9. msleep(10);
  10. } else {
  11. clk_disable(ipu->csi_clk[csi]);
  12. }
  13. return 0;
  14. }
  15. 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中都有调用。

  1. void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi)
  2. {
  3. uint32_t reg;
  4. _ipu_get(ipu);
  5. mutex_lock(&ipu->mutex_lock);
  6. reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE);
  7. *width = (reg & 0xFFFF) + 1;
  8. *height = (reg >> 16 & 0xFFFF) + 1;
  9. mutex_unlock(&ipu->mutex_lock);
  10. _ipu_put(ipu);
  11. }
  12. 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函数中都有调用。

  1. void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi)
  2. {
  3. _ipu_get(ipu);
  4. mutex_lock(&ipu->mutex_lock);
  5. ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
  6. mutex_unlock(&ipu->mutex_lock);
  7. _ipu_put(ipu);
  8. }
  9. EXPORT_SYMBOL(ipu_csi_set_window_size);

这个函数就是将传进来的width和height参数设置到CSI_ACT_FRM_SIZE寄存器中。

7.ipu_csi_set_window_pos函数,这个函数和上一个ipu_csi_set_window_size函数是一起使用的。想要确定一个窗口的大小,只需要知道它的左上角坐标和窗口的长度/宽度就可以设置了。

  1. void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. _ipu_get(ipu);
  5. mutex_lock(&ipu->mutex_lock);
  6. temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
  7. temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
  8. temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
  9. ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
  10. mutex_unlock(&ipu->mutex_lock);
  11. _ipu_put(ipu);
  12. }
  13. EXPORT_SYMBOL(ipu_csi_set_window_pos);

将传进来的left和top参数对应设置到CSI_OUT_FRM_CTRL寄存器中即可。

8._ipu_csi_horizontal_downsize_enable函数,这个函数暂时没人调用。。。

  1. void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
  5. temp |= CSI_HORI_DOWNSIZE_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
  7. }

函数的大致意思是设置csi设备允许水平方向上的小型化(缩放),这个概念不好理解,但是底层的操作很简单,就是将CSI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位置位即可。

9._ipu_csi_horizontal_downsize_disable函数,是上一个函数的反函数。

  1. void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
  5. temp &= ~CSI_HORI_DOWNSIZE_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
  7. }

同样的,想要关闭这个功能,只需要将SI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位清零即可。

10._ipu_csi_vertical_downsize_enable函数

  1. void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
  5. temp |= CSI_VERT_DOWNSIZE_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
  7. }

这个函数就是允许csi设备垂直方向上的小型化(缩放),同样,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位置位即可。

11._ipu_csi_vertical_downsize_disable函数

  1. void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
  5. temp &= ~CSI_VERT_DOWNSIZE_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
  7. }

这个函数是第10个函数的反函数,想要关闭垂直方向上的小型化这个功能,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位清零即可。

12._ipu_csi_set_test_generator函数

  1. void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
  2. uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
  3. {
  4. uint32_t temp;
  5. temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL);
  6. if (active == false) {
  7. temp &= ~CSI_TEST_GEN_MODE_EN;
  8. ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
  9. } else {
  10. /* Set sensb_mclk div_ratio*/
  11. _ipu_csi_mclk_set(ipu, pix_clk, csi);
  12. temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
  13. CSI_TEST_GEN_B_MASK);
  14. temp |= CSI_TEST_GEN_MODE_EN;
  15. temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
  16. (g_value << CSI_TEST_GEN_G_SHIFT) |
  17. (b_value << CSI_TEST_GEN_B_SHIFT);
  18. ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
  19. }
  20. }

这个函数根据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函数

  1. void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
  5. temp |= CSI_CCIR_ERR_DET_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
  7. }

这个函数在ipu_csi_init_interface函数中使用了它,我们在这分析:

它只是将CSI_CCIR_CODE_1寄存器的CSI_CCIR_ERR_DET_EN位置位了。启用CCIR交错模式的错误校验和修正。

14._ipu_csi_ccir_err_detection_disable函数

  1. void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
  5. temp &= ~CSI_CCIR_ERR_DET_EN;
  6. ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
  7. }

这个函数是_ipu_csi_ccir_err_detection_enable函数的反函数。

15._ipu_csi_set_mipi_di函数,这个函数在ipu_common.c中的ipu_init_channel函数里调用.

  1. int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. int retval = 0;
  5. if (di_val > 0xFFL) {
  6. retval = -EINVAL;
  7. goto err;
  8. }
  9. temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI);
  10. switch (num) {
  11. case IPU_CSI_MIPI_DI0:
  12. temp &= ~CSI_MIPI_DI0_MASK;
  13. temp |= (di_val << CSI_MIPI_DI0_SHIFT);
  14. ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
  15. break;
  16. case IPU_CSI_MIPI_DI1:
  17. temp &= ~CSI_MIPI_DI1_MASK;
  18. temp |= (di_val << CSI_MIPI_DI1_SHIFT);
  19. ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
  20. break;
  21. case IPU_CSI_MIPI_DI2:
  22. temp &= ~CSI_MIPI_DI2_MASK;
  23. temp |= (di_val << CSI_MIPI_DI2_SHIFT);
  24. ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
  25. break;
  26. case IPU_CSI_MIPI_DI3:
  27. temp &= ~CSI_MIPI_DI3_MASK;
  28. temp |= (di_val << CSI_MIPI_DI3_SHIFT);
  29. ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
  30. break;
  31. default:
  32. retval = -EINVAL;
  33. }
  34. err:
  35. return retval;
  36. }

它就是根据这个传入的num值来修改CSI_MIPI_DI寄存器中的值,我在用户手册中搜索到的寄存器的名字叫做CSI1Data Identifier Register (IPUx_CSI1_DI),它应该就是为了区分数据来源是哪个流。

16._ipu_csi_set_skip_isp函数

  1. int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. int retval = 0;
  5. if (max_ratio > 5) {
  6. retval = -EINVAL;
  7. goto err;
  8. }
  9. temp = ipu_csi_read(ipu, csi, CSI_SKIP);
  10. temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
  11. temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
  12. (skip << CSI_SKIP_ISP_SHIFT);
  13. ipu_csi_write(ipu, csi, temp, CSI_SKIP);
  14. err:
  15. return retval;
  16. }

它主要是用来设置CSI_SKIP寄存器的,根据传入的skip和max_ratio两个值来修改。查看用户数据手册可以发现,这个寄存器可以设置的地方有3个,这个函数可以修改skip和max_ratio,下面那个函数可以修改另外一个smfc。

17._ipu_csi_set_skip_smfc函数

  1. int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip,
  2. uint32_t max_ratio, uint32_t id, uint32_t csi)
  3. {
  4. uint32_t temp;
  5. int retval = 0;
  6. if (max_ratio > 5 || id > 3) {
  7. retval = -EINVAL;
  8. goto err;
  9. }
  10. temp = ipu_csi_read(ipu, csi, CSI_SKIP);
  11. temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
  12. CSI_SKIP_SMFC_MASK);
  13. temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
  14. (id << CSI_ID_2_SKIP_SHIFT) |
  15. (skip << CSI_SKIP_SMFC_SHIFT);
  16. ipu_csi_write(ipu, csi, temp, CSI_SKIP);
  17. err:
  18. return retval;
  19. }

这个函数与上面那个类似,只是需要通过3个参数来确定修改CSI_SKIP中的哪些位。以后再具体查看这些位的含义。

18._ipu_smfc_init函数,它在ipu_common.c文件中的ipu_init_channel函数中调用了。

  1. void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
  2. {
  3. uint32_t temp;
  4. temp = ipu_smfc_read(ipu, SMFC_MAP);
  5. switch (channel) {
  6. case CSI_MEM0:
  7. temp &= ~SMFC_MAP_CH0_MASK;
  8. temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
  9. break;
  10. case CSI_MEM1:
  11. temp &= ~SMFC_MAP_CH1_MASK;
  12. temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
  13. break;
  14. case CSI_MEM2:
  15. temp &= ~SMFC_MAP_CH2_MASK;
  16. temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
  17. break;
  18. case CSI_MEM3:
  19. temp &= ~SMFC_MAP_CH3_MASK;
  20. temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
  21. break;
  22. default:
  23. return;
  24. }
  25. ipu_smfc_write(ipu, temp, SMFC_MAP);
  26. }

看函数的注释,MapCSI frames to IDMACchannels.这个SMFC位于csi设备与IDMAC之间的位置,意思应该就是映射csi的frame到IDMAC中。操作很简单,根据channel参数的值来设置SMFC_MAP寄存器中的值。

19._ipu_smfc_set_wmc函数

  1. void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level)
  2. {
  3. uint32_t temp;
  4. temp = ipu_smfc_read(ipu, SMFC_WMC);
  5. switch (channel) {
  6. case CSI_MEM0:
  7. if (set == true) {
  8. temp &= ~SMFC_WM0_SET_MASK;
  9. temp |= level << SMFC_WM0_SET_SHIFT;
  10. } else {
  11. temp &= ~SMFC_WM0_CLR_MASK;
  12. temp |= level << SMFC_WM0_CLR_SHIFT;
  13. }
  14. break;
  15. case CSI_MEM1:
  16. if (set == true) {
  17. temp &= ~SMFC_WM1_SET_MASK;
  18. temp |= level << SMFC_WM1_SET_SHIFT;
  19. } else {
  20. temp &= ~SMFC_WM1_CLR_MASK;
  21. temp |= level << SMFC_WM1_CLR_SHIFT;
  22. }
  23. break;
  24. case CSI_MEM2:
  25. if (set == true) {
  26. temp &= ~SMFC_WM2_SET_MASK;
  27. temp |= level << SMFC_WM2_SET_SHIFT;
  28. } else {
  29. temp &= ~SMFC_WM2_CLR_MASK;
  30. temp |= level << SMFC_WM2_CLR_SHIFT;
  31. }
  32. break;
  33. case CSI_MEM3:
  34. if (set == true) {
  35. temp &= ~SMFC_WM3_SET_MASK;
  36. temp |= level << SMFC_WM3_SET_SHIFT;
  37. } else {
  38. temp &= ~SMFC_WM3_CLR_MASK;
  39. temp |= level << SMFC_WM3_CLR_SHIFT;
  40. }
  41. break;
  42. default:
  43. return;
  44. }
  45. ipu_smfc_write(ipu, temp, SMFC_WMC);
  46. }

根据channel的值来设置SMFC_WMC寄存器中的不同位。函数意思不懂。

20._ipu_smfc_set_burst_size函数,在ipu_common.c中的ipu_init_channel_buffer中调用了。

  1. void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs)
  2. {
  3. uint32_t temp;
  4. temp = ipu_smfc_read(ipu, SMFC_BS);
  5. switch (channel) {
  6. case CSI_MEM0:
  7. temp &= ~SMFC_BS0_MASK;
  8. temp |= bs << SMFC_BS0_SHIFT;
  9. break;
  10. case CSI_MEM1:
  11. temp &= ~SMFC_BS1_MASK;
  12. temp |= bs << SMFC_BS1_SHIFT;
  13. break;
  14. case CSI_MEM2:
  15. temp &= ~SMFC_BS2_MASK;
  16. temp |= bs << SMFC_BS2_SHIFT;
  17. break;
  18. case CSI_MEM3:
  19. temp &= ~SMFC_BS3_MASK;
  20. temp |= bs << SMFC_BS3_SHIFT;
  21. break;
  22. default:
  23. return;
  24. }
  25. ipu_smfc_write(ipu, temp, SMFC_BS);
  26. }

函数作用是设置IDMACchannel的burstsize(脉冲串?),根据传入的bs参数设置SMFC_BS寄存器即可。

21._ipu_csi_init函数,它在ipu_common.c中的ipu_init_channel函数中调用了。

  1. int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi)
  2. {
  3. uint32_t csi_sens_conf, csi_dest;
  4. int retval = 0;
  5. switch (channel) {
  6. case CSI_MEM0:
  7. case CSI_MEM1:
  8. case CSI_MEM2:
  9. case CSI_MEM3:
  10. csi_dest = CSI_DATA_DEST_IDMAC;
  11. break;
  12. case CSI_PRP_ENC_MEM:
  13. case CSI_PRP_VF_MEM:
  14. csi_dest = CSI_DATA_DEST_IC;
  15. break;
  16. default:
  17. retval = -EINVAL;
  18. goto err;
  19. }
  20. csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
  21. csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
  22. ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest <<
  23. CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF);
  24. err:
  25. return retval;
  26. }

这个函数初始化csi设备,它根据传入的channel的不同,设置不同的csi数据目的地,然后将这个目的地设置到CSI_SENS_CONF寄存器中。感觉这个channel在ipu内部的意思就是ipu内数据流通的过程,每个channle里面都含有目的地等信息。

22._ipu_csi_wait4eof函数,在ipu_common.c中的ipu_disable_csi函数中调用了。

  1. void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel)
  2. {
  3. int ret;
  4. int irq = 0;
  5. if (channel == CSI_MEM0)
  6. irq = IPU_IRQ_CSI0_OUT_EOF;
  7. else if (channel == CSI_MEM1)
  8. irq = IPU_IRQ_CSI1_OUT_EOF;
  9. else if (channel == CSI_MEM2)
  10. irq = IPU_IRQ_CSI2_OUT_EOF;
  11. else if (channel == CSI_MEM3)
  12. irq = IPU_IRQ_CSI3_OUT_EOF;
  13. else if (channel == CSI_PRP_ENC_MEM)
  14. irq = IPU_IRQ_PRP_ENC_OUT_EOF;
  15. else if (channel == CSI_PRP_VF_MEM)
  16. irq = IPU_IRQ_PRP_VF_OUT_EOF;
  17. else{
  18. dev_err(ipu->dev, "Not a CSI channel\n");
  19. return;
  20. }
  21. init_completion(&ipu->csi_comp);
  22. ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu);
  23. if (ret < 0) {
  24. dev_err(ipu->dev, "CSI irq %d in use\n", irq);
  25. return;
  26. }
  27. ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies(500));
  28. ipu_free_irq(ipu, irq, ipu);
  29. dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret);
  30. }

它的中断处理函数如下:

  1. static irqreturn_t csi_irq_handler(int irq, void *dev_id)
  2. {
  3. struct ipu_soc *ipu = dev_id;
  4. struct completion *comp = &ipu->csi_comp;
  5. complete(comp);
  6. return IRQ_HANDLED;
  7. }

这个函数中,首先根据channel的不同值设置不同的irq触发方式,然后初始化完成量,然后调用ipu_request_irq函数申请中断,这个函数在ipu_common.c中定义。如果发生中断后,就会调用到这个中断处理函数,在中断处理函数中通过complete()函数来唤醒阻塞在ipu->csi_comp上的首个线程。wait_for_completion_timeout函数表示,如果超过一定时间没有发生这个中断,就直接结束等待。

关于这个完成量的解释和操作函数,可以看《Linux的completion机制.pdf》和《线程的约会completion.pdf》两个文件。

至此,这个文件简单分析完毕。

4.imx6 IPU代码详细分析相关推荐

  1. Blueprint代码详细分析-Android10.0编译系统(七)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  2. seq2seq翻译任务代码详细分析

    文章目录 题目 代码 总结 题目 ''' Description: seq2seq代码详细分析 Autor: 365JHWZGo Date: 2021-12-16 19:59:38 LastEdito ...

  3. linux加密模块,Linux加解密支持模块代码详细分析之演示验证方案1实验代码及结果...

    原标题:Linux加解密支持模块代码详细分析之演示验证方案1实验代码及结果 3.1.5.实验代码 #include #include #include #include #include #inclu ...

  4. [细读经典]Megatron论文和代码详细分析(1)

    [细读经典]Megatron论文和代码详细分析(1) 导航: 迷途小书僮:[细读经典]Megatron论文和代码详细分析(2)102 赞同 · 41 评论文章正在上传-重新上传取消 前言 作为一款支持 ...

  5. uboot代码详细分析.pdf

    目录 u-boot-1.1.6 之cpu/arm920t/start.s 分析 ............................................................ ...

  6. 区块链实现代码详细分析(Python)

    代码 import hashlib import json import requests from textwrap import dedent from time import time from ...

  7. Android RIL代码详细分析

    RIL代码分析 代码位于:android/hardware/ril 1 rild.c中的main()函数提供了rild的入口 首先,通过main函数的传参,cmdline,内核选项等方式获取rild. ...

  8. R语言曲面拟合代码详细分析(1)

    #代码参考<R语言数据可视化之美-专业图表绘制指南>例4.2.1,主要代码链接参见 #https://github.com/EasyChart/Beautiful-Visualizatio ...

  9. android ril 模拟,Android RIL代码详细分析

    RIL代码分析 代码位于:android/hardware/ril 1 rild.c中的main()函数提供了rild的入口 首先,通过main函数的传参,cmdline,内核选项等方式获取rild. ...

最新文章

  1. 「情报局21」2019 AI 进入新算力时代
  2. CentOS创建快捷按钮并设置文件图标
  3. java执行sql列名无效_嵌套异常是java.sql.SQLException:无效的列名ORACLE
  4. ai怎么约束每个字的大小_人工智能的约束满意问题
  5. 消息队列的其他实现方式
  6. 计算机考试时间2021安徽,安徽省2021年高考录取结果查询正式开通!查询方式权威公布...
  7. oracle 提取连续数字,oracle 得到连续不重复的数字序列
  8. 文本框焦点清空以及回车键提交表单的实现
  9. python安装成功之后教程_python安装教程 Pycharm安装详细教程
  10. 内幕:一个由罪犯秘密建立的“加密”手机公司
  11. c# list转为json_C#中List集合转换JSON
  12. 网络知识汇总(基于W5500以太网)
  13. 《SOA中国路线图》可圈可点之处
  14. 如何做好互联网广告销售
  15. 怎么下载优酷视频呢,你可以这样下
  16. Cisco思科IPS签名策略配置引擎告警和日志动作
  17. 南方的X-Men看过来〜Cocos2d-x开发者沙龙(广州站)即将举办!
  18. 从购买服务器到搭建WordPress博客详细教程
  19. PPA在芯片定义时的考虑
  20. Mina 粘包、断包、半包解决

热门文章

  1. 20170628总结
  2. [生存志] 第67节 夫差信谗杀伍员
  3. 【历史上的今天】2 月 16 日:世界上第一个 BBS 诞生;中国计算机教育开端;IBM 机器人赢得智能竞赛
  4. Java 工程师如何得到一个好 Offer
  5. 软件测试面试题:什么是数据的对立性,有几个层次?
  6. “印度管理”会成为超越中国的秘密武器吗?[高度关注]
  7. 政务使用区块链技术,网络安全风险不容小觑
  8. 用好Eclipse Task功能
  9. H5页面添加音乐播放
  10. 手机便签里的文字不小心点了个粘贴就消失了应该怎样复原呢?