当前位置:   article > 正文

7.1 ipu_device.c分析(一)---流程分析_ipu device

ipu device

ipu_common.c文件的ipu_probe函数中,最后调用到register_ipu_device函数,这个函数在ipu_device.c中,所以从这个文件开始分析。

这个文件中主要是两个内核线程的执行过程,根据FTF-CON-F0119.pdf中写的:

1)每个IPU有两个内核线程为了ICtaskPP&VF

2)每一个内核线程通过将task添加到它的taskqueue list中来执行这个task

3)每一个task执行流程是:ipu_init_channel--->ipu_init_channel_buffer--->request_ipu_irq--->ipu_enable_channel--->wait_irq(taskfinish)--->ipu_disable_channel--->ipu_uninit_channel

根据我的初步分析,这几个函数中有的函数名称已经发生变化,暂时先这样写,后面再具体分析。

4tasks基于单缓冲区模式

5)应用中只需要准备一个task,然后将这个task加入队列中即可。

6task操作包括:

设置taskinput/output/overlay/rotation/deinterlacing/buffer

首先调用ioctlIPU_CHECK_TASK,然后调用IPU_QUEUE_TASK这个ioctltask加入队列中。


ipu_common.c中的ipu_gen_init函数中,可以看到通过platform_driver_register(&mxcipu_driver);来将这个platform_driver类型的mxcipu_driver结构体注册到platform平台总线上面了。这个结构体作为驱动的部分注册到平台总线上面,这个驱动匹配的设备是:ipu*。而当设备也注册到这个总线上面的时候,平台就会执行对应的match函数,最终调用到mxcipu_driver结构体里面的probe函数。

同时,在mxc_v4l2_capture.c中,同样是通过platform_driver_register(&mxc_v4l2_driver);函数来将mxc_v4l2_driver结构体作为驱动部分注册到平台总线上面,这个驱动匹配的设备是:v4l2_cap_*


所以,暂时不理解这两者有什么关系。都是作为一个驱动来注册到platform平台上面的。而在一般的应用程序中,都是通过操作"/dev/video0"设备,而这个设备就是mxc_v4l2_capture.c中注册的v4l2_cap_*。这个设备包含v4l2里面很多的ioctl操作。

而想要操作ipu_common.c中注册的设备,是需要操作"/dev/mxc_ipu",这个设备只有简单的几个Ioctl操作,比如:IPU_CHECK_TASKIPU_QUEUE_TASK等。


(一)register_ipu_device函数

  1. int register_ipu_device(struct ipu_soc *ipu, int id)
  2. {
  3. int ret = 0;
  4. static int idx;
  5. static struct ipu_thread_data thread_data[5];
  6. if (!major) {
  7. major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
  8. if (major < 0) {
  9. printk(KERN_ERR "Unable to register mxc_ipu as a char device\n");
  10. ret = major;
  11. goto register_cdev_fail;
  12. }
  13. /* 注册字符设备,主要是把这个mxc_ipu_fops结构体注册到内核中了。 */
  14. ipu_class = class_create(THIS_MODULE, "mxc_ipu");
  15. if (IS_ERR(ipu_class)) {
  16. ret = PTR_ERR(ipu_class);
  17. goto ipu_class_fail;
  18. }
  19. /* 创建类 */
  20. ipu_dev = device_create(ipu_class, NULL, MKDEV(major, 0),
  21. NULL, "mxc_ipu");
  22. if (IS_ERR(ipu_dev)) {
  23. ret = PTR_ERR(ipu_dev);
  24. goto dev_create_fail;
  25. }
  26. /* 创建设备节点 */
  27. ipu_dev->dma_mask = kmalloc(sizeof(*ipu_dev->dma_mask), GFP_KERNEL);
  28. *ipu_dev->dma_mask = DMA_BIT_MASK(32);
  29. ipu_dev->coherent_dma_mask = DMA_BIT_MASK(32);
  30. /* 为struct device *ipu_dev中的 dma_mask项分配内存空间,然后设置它。 */
  31. mutex_init(&ipu_ch_tbl.lock);
  32. }
  33. max_ipu_no = ++id;
  34. ipu->rot_dma[0].size = 0;
  35. ipu->rot_dma[1].size = 0;
  36. /* 设置 max_ipu_no的值,它是根据ipu_common.c中的ipu_probe函数传过来的id参数设置的,暂时没分析。大致意思是获取的ipu数目。 */
  37. thread_data[idx].ipu = ipu;
  38. thread_data[idx].id = 0;
  39. thread_data[idx].is_vdoa = 0;
  40. ipu->thread[0] = kthread_run(ipu_task_thread, &thread_data[idx++],
  41. "ipu%d_task", id);
  42. if (IS_ERR(ipu->thread[0])) {
  43. ret = PTR_ERR(ipu->thread[0]);
  44. goto kthread0_fail;
  45. }

/*这个idx是个函数局部变量,在这用的时候,上面只有一句声明:staticint idx;并没有对它进行初始化操作,这是一个bug么???暂且认为它是0,然后设置thread_data[0]ipuidis_vdoa参数。*/

/*查看ipu_soc结构体可以看到里面有这一项:structtask_struct *thread[2];这个结构体准备了两个内核线程供我们使用。现在先调用kthread_run来创建并启动第一个内核线程。关于内核线程的知识,参看《Linux内核kthread_run函数理解学习》和《运行不息的内核线程kthread》这两个文件。*/

  1. thread_data[idx].ipu = ipu;
  2. thread_data[idx].id = 1;
  3. thread_data[idx].is_vdoa = 0;
  4. ipu->thread[1] = kthread_run(ipu_task_thread, &thread_data[idx++],
  5. "ipu%d_task", id);
  6. if (IS_ERR(ipu->thread[1])) {
  7. ret = PTR_ERR(ipu->thread[1]);
  8. goto kthread1_fail;
  9. }

/*在这创建并启动第二个内核线程。这两个线程的运行函数都是ipu_task_thread*/

  1. return ret;
  2. kthread1_fail:
  3. kthread_stop(ipu->thread[0]);
  4. kthread0_fail:
  5. if (id == 0)
  6. device_destroy(ipu_class, MKDEV(major, 0));
  7. dev_create_fail:
  8. if (id == 0) {
  9. class_destroy(ipu_class);
  10. }
  11. ipu_class_fail:
  12. if (id == 0)
  13. unregister_chrdev(major, "mxc_ipu");
  14. register_cdev_fail:
  15. return ret;
  16. }

(二)下面就到ipu_task_thread函数中去:

  1. static int ipu_task_thread(void *argv)
  2. {
  3. struct ipu_task_entry *tsk;
  4. struct ipu_task_entry *sp_tsk0;
  5. struct ipu_split_task sp_task[4];
  6. /* priority lower than irq_thread */
  7. const struct sched_param param = {
  8. .sched_priority = MAX_USER_RT_PRIO/2 - 1,
  9. };
  10. int ret;
  11. int curr_thread_id;
  12. uint32_t size;
  13. unsigned long flags;
  14. unsigned int cpu;
  15. struct cpumask cpu_mask;
  16. struct ipu_thread_data *data = (struct ipu_thread_data *)argv;
  17. thread_id++;
  18. curr_thread_id = thread_id;
  19. sched_setscheduler(current, SCHED_FIFO, ¶m);

/*这个函数参考http://blog.csdn.net/allwtg/article/details/5254306sched_setscheduler用法*/

  1. if (!data->is_vdoa) {
  2. cpu = cpumask_first(cpu_online_mask);
  3. cpumask_set_cpu(cpu, &cpu_mask);

/*这个函数参考http://blog.csdn.net/nirenxiaoxiao/article/details/21462053Linuxcpumask分析*/

  1. ret = sched_setaffinity(data->ipu->thread[data->id]->pid,
  2. &cpu_mask);

/*这个函数参考http://www.cnblogs.com/visayafan/archive/2011/12/10/2283375.htmlLinuxCPU亲和性(affinity)及与亲和性有关的两个函数sched_setaffinity()sched_getaffinity()*/

  1. if (ret < 0) {
  2. pr_err("%s: sched_setaffinity fail:%d.\n", __func__, ret);
  3. }
  4. pr_debug("%s: sched_setaffinity cpu:%d.\n", __func__, cpu);
  5. }
  6. while (!kthread_should_stop()) {
  7. int split_fail = 0;
  8. int split_parent;
  9. int split_child;

/*这个函数参考http://blog.csdn.net/angle_birds/article/details/8206091运行不息的内核线程kthread*/

  1. wait_event_interruptible(thread_waitq, find_task(&tsk, curr_thread_id));
  2. if (!tsk) {
  3. pr_err("thread:%d can not find task.\n",
  4. curr_thread_id);
  5. continue;
  6. }
  7. /* note: other threads run split child task */
  8. split_parent = need_split(tsk) && !tsk->parent;
  9. split_child = need_split(tsk) && tsk->parent;
  10. if (split_parent) {
  11. if ((tsk->set.split_mode == RL_SPLIT) ||
  12. (tsk->set.split_mode == UD_SPLIT))
  13. size = 2;
  14. else
  15. size = 4;
  16. ret = queue_split_task(tsk, sp_task, size);
  17. if (ret < 0) {
  18. split_fail = 1;
  19. } else {
  20. struct list_head *pos;
  21. spin_lock_irqsave(&ipu_task_list_lock, flags);
  22. sp_tsk0 = list_first_entry(&tsk->split_list,
  23. struct ipu_task_entry, node);
  24. list_del(&sp_tsk0->node);
  25. list_for_each(pos, &tsk->split_list) {
  26. struct ipu_task_entry *tmp;
  27. tmp = list_entry(pos,
  28. struct ipu_task_entry, node);
  29. tmp->task_in_list = 1;
  30. dev_dbg(tmp->dev,
  31. "[0x%p] no-0x%x,id:%d sp_tsk "
  32. "add_to_list.\n", tmp,
  33. tmp->task_no, tmp->task_id);
  34. }
  35. /* add to global list */
  36. list_splice(&tsk->split_list, &ipu_task_list);
  37. spin_unlock_irqrestore(&ipu_task_list_lock,
  38. flags);
  39. /* let the parent thread do the first sp_task */
  40. /* FIXME: ensure the correct sequence for split
  41. 4size: 5/6->9/a*/
  42. if (!sp_tsk0)
  43. dev_err(tsk->dev,
  44. "ERR: no-0x%x,can not get split_tsk0\n",
  45. tsk->task_no);
  46. wake_up_interruptible(&thread_waitq);
  47. get_res_do_task(sp_tsk0);
  48. dev_dbg(sp_tsk0->dev,
  49. "thread:%d complete tsk no:0x%x.\n",
  50. curr_thread_id, sp_tsk0->task_no);
  51. ret = atomic_read(&req_cnt);
  52. if (ret > 0) {
  53. wake_up(&res_waitq);
  54. dev_dbg(sp_tsk0->dev,
  55. "sp_tsk0 sche thread:%d no:0x%x,"
  56. "req_cnt:%d\n", curr_thread_id,
  57. sp_tsk0->task_no, ret);
  58. /* For other threads to get_res */
  59. schedule();
  60. }
  61. }
  62. } else
  63. get_res_do_task(tsk);
  64. /* wait for all 4 sp_task finished here or timeout
  65. and then release all resources */
  66. if (split_parent && !split_fail)
  67. wait_split_task_complete(tsk, sp_task, size);
  68. if (!split_child) {
  69. atomic_inc(&tsk->done);
  70. wake_up(&tsk->task_waitq);
  71. }
  72. dev_dbg(tsk->dev, "thread:%d complete tsk no:0x%x-[0x%p].\n",
  73. curr_thread_id, tsk->task_no, tsk);
  74. ret = atomic_read(&req_cnt);
  75. if (ret > 0) {
  76. wake_up(&res_waitq);
  77. dev_dbg(tsk->dev, "sche thread:%d no:0x%x,req_cnt:%d\n",
  78. curr_thread_id, tsk->task_no, ret);
  79. /* note: give cpu to other threads to get_res */
  80. schedule();
  81. }
  82. kref_put(&tsk->refcount, task_mem_free);
  83. }
  84. pr_info("ERR %s exit.\n", __func__);
  85. return 0;
  86. }

关于内核线程这一块的知识没有详细了解过,所以只能简单查看了一下几个函数的意思,这个函数最终调用的函数就是get_res_do_task函数,所以下面来分析这个函数。


(三)get_res_do_task函数

  1. static void get_res_do_task(struct ipu_task_entry *t)
  2. {
  3. uint32_t found;
  4. uint32_t split_child;
  5. struct mutex *lock;
  6. found = get_vdoa_ipu_res(t);
  7. if (!found) {
  8. dev_err(t->dev, "ERR:[0x%p] no-0x%x can not get res\n",
  9. t, t->task_no);
  10. return;
  11. } else {
  12. if (t->set.task & VDOA_ONLY)
  13. do_task_vdoa_only(t);
  14. else if ((IPU_PIX_FMT_TILED_NV12F == t->input.format) &&
  15. (t->set.mode & VDOA_BAND_MODE) &&
  16. (t->input.crop.w >
  17. soc_max_vdi_in_width(t->ipu)))
  18. do_task_vdoa_vdi(t);
  19. else
  20. do_task(t);
  21. put_vdoa_ipu_res(t, 0);
  22. }
  23. if (t->state != STATE_OK) {
  24. dev_err(t->dev, "ERR:[0x%p] no-0x%x state: %s\n",
  25. t, t->task_no, state_msg[t->state].msg);
  26. }
  27. split_child = need_split(t) && t->parent;
  28. if (split_child) {
  29. lock = &t->parent->split_lock;
  30. mutex_lock(lock);
  31. t->split_done = 1;
  32. mutex_unlock(lock);
  33. wake_up(&t->parent->split_waitq);
  34. }
  35. return;
  36. }

这个函数首先通过调用get_vdoa_ipu_res函数,这个函数看了半天都没理解想干嘛。。。然后根据t->set.task中的值来决定调用哪个执行函数。当为VDOA_ONLY时,就调用到do_task_vdoa_only函数,为VDOA_BAND_MODE时,就会调用do_task_vdoa_vdi函数,其他的情况下就直接调用do_task函数。


(四)do_task_vdoa_only函数

  1. static void do_task_vdoa_only(struct ipu_task_entry *t)
  2. {
  3. int ret;
  4. ret = init_tiled_ch_bufs(NULL, t);
  5. CHECK_RETCODE(ret < 0, "do_vdoa_only", STATE_ERR, out, ret);
  6. ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
  7. vdoa_stop(t->vdoa_handle);
  8. CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_only",
  9. STATE_VDOA_IRQ_TIMEOUT, out, ret);
  10. t->state = STATE_OK;
  11. out:
  12. return;
  13. }

4.1这个函数通过调用init_tiled_ch_bufs函数来初始化channeltiledbuffers(平铺的缓冲区,不知道它与普通的缓冲区有什么不同)。在这个函数中会根据t->input.format里面保存的格式调用到init_tiled_buf函数,在这个init_tiled_buf函数里面会调用到vdoa_setup函数来完成vdoa里面寄存器的一些设置,通过vdoa_get_output_buf来读取vdoa中某些寄存器的值填充在buf中,然后再调用ipu_init_channel_buffer函数来初始化buffer

4.2然后就会调用vdoa_start函数来启动vdoa,在这个函数中会初始化完成量(init_completion(&vdoa->comp);),启动irq,然后等待完成量(wait_for_completion_timeout(&vdoa->comp,msecs_to_jiffies(timeout_ms)); )。

4.3之后调用vdoa_stop函数来结束vdoa


(五)do_task_vdoa_vdi函数

  1. static void do_task_vdoa_vdi(struct ipu_task_entry *t)
  2. {
  3. int i;
  4. int ret;
  5. u32 stripe_width;
  6. /* FIXME: crop mode not support now */
  7. stripe_width = t->input.width >> 1;
  8. t->input.crop.pos.x = 0;
  9. t->input.crop.pos.y = 0;
  10. t->input.crop.w = stripe_width;
  11. t->input.crop.h = t->input.height;
  12. t->output.crop.w = stripe_width;
  13. t->output.crop.h = t->input.height;
  14. for (i = 0; i < 2; i++) {
  15. t->input.crop.pos.x = t->input.crop.pos.x + i * stripe_width;
  16. t->output.crop.pos.x = t->output.crop.pos.x + i * stripe_width;
  17. /* check input */
  18. ret = set_crop(&t->input.crop, t->input.width, t->input.height,
  19. t->input.format);
  20. if (ret < 0) {
  21. ret = STATE_ERR;
  22. goto done;
  23. } else
  24. update_offset(t->input.format,
  25. t->input.width, t->input.height,
  26. t->input.crop.pos.x,
  27. t->input.crop.pos.y,
  28. &t->set.i_off, &t->set.i_uoff,
  29. &t->set.i_voff, &t->set.istride);
  30. dev_dbg(t->dev, "i_off:0x%x, i_uoff:0x%x, istride:%d.\n",
  31. t->set.i_off, t->set.i_uoff, t->set.istride);
  32. /* check output */
  33. ret = set_crop(&t->output.crop, t->input.width,
  34. t->output.height, t->output.format);
  35. if (ret < 0) {
  36. ret = STATE_ERR;
  37. goto done;
  38. } else
  39. update_offset(t->output.format,
  40. t->output.width, t->output.height,
  41. t->output.crop.pos.x,
  42. t->output.crop.pos.y,
  43. &t->set.o_off, &t->set.o_uoff,
  44. &t->set.o_voff, &t->set.ostride);
  45. dev_dbg(t->dev, "o_off:0x%x, o_uoff:0x%x, ostride:%d.\n",
  46. t->set.o_off, t->set.o_uoff, t->set.ostride);
  47. do_task(t);
  48. }
  49. return;
  50. done:
  51. dev_err(t->dev, "ERR %s set_crop.\n", __func__);
  52. t->state = ret;
  53. return;
  54. }

在这个函数里面,有一条注释:cropmode not supportnow。所以这个函数就是设置了crop的一些相关信息,最后就调用到了do_task函数。所以下面重点来看看这个do_task函数。


(六)do_task函数

  1. static void do_task(struct ipu_task_entry *t)
  2. {
  3. int r_size;
  4. int irq;
  5. int ret;
  6. uint32_t busy;
  7. struct ipu_soc *ipu = t->ipu;
  8. CHECK_PERF(&t->ts_dotask);
  9. if (!ipu) {
  10. t->state = STATE_NO_IPU;
  11. return;
  12. }
  13. init_completion(&t->irq_comp); //初始化完成量。
  14. dev_dbg(ipu->dev, "[0x%p]Do task no:0x%x: id %d\n", (void *)t,
  15. t->task_no, t->task_id);
  16. dump_task_info(t); //打印task信息。
  17. if (t->set.task & IC_PP) {
  18. t->set.ic_chan = MEM_PP_MEM;
  19. dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PP_MEM\n", (void *)t);
  20. } else if (t->set.task & IC_VF) {
  21. t->set.ic_chan = MEM_PRP_VF_MEM;
  22. dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t);
  23. } else if (t->set.task & VDI_VF) {
  24. if (t->set.mode & VDOA_BAND_MODE) {
  25. t->set.ic_chan = MEM_VDI_MEM;
  26. if (deinterlace_3_field(t)) {
  27. t->set.vdi_ic_p_chan = MEM_VDI_MEM_P;
  28. t->set.vdi_ic_n_chan = MEM_VDI_MEM_N;
  29. }
  30. dev_dbg(ipu->dev, "[0x%p]ic ch MEM_VDI_MEM\n",
  31. (void *)t);
  32. } else {
  33. t->set.ic_chan = MEM_VDI_PRP_VF_MEM;
  34. if (deinterlace_3_field(t)) {
  35. t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P;
  36. t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N;
  37. }
  38. dev_dbg(ipu->dev,
  39. "[0x%p]ic ch MEM_VDI_PRP_VF_MEM\n", t);
  40. }
  41. }

/*以上就是根据t->set.task中的值来决定t->set.ic_chant->set.vdi_ic_p_chant->set.vdi_ic_n_chan的取值。*/

  1. if (t->set.task & ROT_PP) {
  2. t->set.rot_chan = MEM_ROT_PP_MEM;
  3. dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_PP_MEM\n", (void *)t);
  4. } else if (t->set.task & ROT_VF) {
  5. t->set.rot_chan = MEM_ROT_VF_MEM;
  6. dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t);
  7. }

/*设置t->set.rot_chan的值。*/

  1. if (t->task_id == IPU_TASK_ID_VF)
  2. busy = ic_vf_pp_is_busy(ipu, true);
  3. else if (t->task_id == IPU_TASK_ID_PP)
  4. busy = ic_vf_pp_is_busy(ipu, false);
  5. else {
  6. dev_err(ipu->dev, "ERR[no:0x%x]ipu task_id:%d invalid!\n",
  7. t->task_no, t->task_id);
  8. return;
  9. }
  10. if (busy) {
  11. dev_err(ipu->dev, "ERR[0x%p-no:0x%x]ipu task_id:%d busy!\n",
  12. (void *)t, t->task_no, t->task_id);
  13. t->state = STATE_IPU_BUSY;
  14. return;
  15. }
  16. irq = get_irq(t);
  17. if (irq < 0) {
  18. t->state = STATE_NO_IRQ;
  19. return;
  20. }
  21. t->irq = irq;

/*获取中断。*/

  1. /* channel setup */
  2. if (only_ic(t->set.mode)) {
  3. dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t);
  4. ret = init_ic(ipu, t);
  5. CHECK_RETCODE(ret < 0, "init_ic only_ic",
  6. t->state, chan_setup, ret);
  7. } else if (only_rot(t->set.mode)) {
  8. dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t);
  9. ret = init_rot(ipu, t);
  10. CHECK_RETCODE(ret < 0, "init_rot only_rot",
  11. t->state, chan_setup, ret);
  12. } else if (ic_and_rot(t->set.mode)) {
  13. int rot_idx = (t->task_id == IPU_TASK_ID_VF) ? 0 : 1;
  14. dev_dbg(t->dev, "[0x%p]ic + rot mode\n", (void *)t);
  15. t->set.r_fmt = t->output.format;
  16. if (t->output.rotate >= IPU_ROTATE_90_RIGHT) {
  17. t->set.r_width = t->output.crop.h;
  18. t->set.r_height = t->output.crop.w;
  19. } else {
  20. t->set.r_width = t->output.crop.w;
  21. t->set.r_height = t->output.crop.h;
  22. }
  23. t->set.r_stride = t->set.r_width *
  24. bytes_per_pixel(t->set.r_fmt);
  25. r_size = PAGE_ALIGN(t->set.r_width * t->set.r_height
  26. * fmt_to_bpp(t->set.r_fmt)/8);
  27. if (r_size > ipu->rot_dma[rot_idx].size) {
  28. dev_dbg(t->dev, "[0x%p]realloc rot buffer\n", (void *)t);
  29. if (ipu->rot_dma[rot_idx].vaddr)
  30. dma_free_coherent(t->dev,
  31. ipu->rot_dma[rot_idx].size,
  32. ipu->rot_dma[rot_idx].vaddr,
  33. ipu->rot_dma[rot_idx].paddr);
  34. ipu->rot_dma[rot_idx].size = r_size;
  35. ipu->rot_dma[rot_idx].vaddr = dma_alloc_coherent(t->dev,
  36. r_size,
  37. &ipu->rot_dma[rot_idx].paddr,
  38. GFP_DMA | GFP_KERNEL);
  39. CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL,
  40. "ic_and_rot", STATE_SYS_NO_MEM,
  41. chan_setup, -ENOMEM);
  42. }
  43. t->set.r_paddr = ipu->rot_dma[rot_idx].paddr;
  44. dev_dbg(t->dev, "[0x%p]rotation:\n", (void *)t);
  45. dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->set.r_fmt);
  46. dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->set.r_width);
  47. dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->set.r_height);
  48. dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->set.r_paddr);
  49. dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride);
  50. ret = init_ic(ipu, t);
  51. CHECK_RETCODE(ret < 0, "init_ic ic_and_rot",
  52. t->state, chan_setup, ret);
  53. ret = init_rot(ipu, t);
  54. CHECK_RETCODE(ret < 0, "init_rot ic_and_rot",
  55. t->state, chan_setup, ret);
  56. ret = ipu_link_channels(ipu, t->set.ic_chan,
  57. t->set.rot_chan);
  58. CHECK_RETCODE(ret < 0, "ipu_link_ch ic_and_rot",
  59. STATE_LINK_CHAN_FAIL, chan_setup, ret);
  60. } else {
  61. dev_err(t->dev, "ERR [0x%p]do task: should not be here\n", t);
  62. t->state = STATE_ERR;
  63. return;
  64. }

/*以上的就是t->set.mode中的模式选择操作。这些MODEstructtask_set中声明的,有IC_MODEROT_MODEVDI_MODEIPU_PREPROCESS_MODE_MASK几种,如果只是IC_MODE的话(IC_MODEVDI_MODE都算,具体可以查看only_ic函数的定义),就调用init_ic函数来初始化ic;如果只是ROT_MODE的话,就调用init_rot函数来初始化rot;如果是ic_and_rot的话,就同时调用init_ic函数和init_rot函数,最后通过ipu_link_channels函数将两个channel链接起来。*/

/*在这个init_ic函数中,会设置一些参数,然后调用ipu_init_channel函数来初始化channel;然后还会调用init_tiled_ch_bufs函数来初始化channelbuffers(这个函数在上面do_task_vdoa_only函数中出现了),然后就会调用到ipu_init_channel_buffer函数。*/

/*init_rot函数中,同样会调用到ipu_init_channel函数来初始化channel,然后也会调用到ipu_init_channel_buffer函数。*/

  1. ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t);
  2. CHECK_RETCODE(ret < 0, "ipu_req_irq",
  3. STATE_IRQ_FAIL, chan_setup, ret);

/* 申请中断。注意这里使用的是ipu_common.c中定义的ipu_request_irq函数,而不是普通的request_irq函数。 */

  1. /* enable/start channel */
  2. if (only_ic(t->set.mode)) {
  3. ret = ipu_enable_channel(ipu, t->set.ic_chan);
  4. CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic",
  5. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  6. if (deinterlace_3_field(t)) {
  7. ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
  8. CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_p",
  9. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  10. ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
  11. CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_n",
  12. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  13. }

/*通过调用ipu_enable_channel函数来使能/开始channel*/

  1. ret = ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER,
  2. 0); //将buffer设置为准备好状态。
  3. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic",
  4. STATE_SEL_BUF_FAIL, chan_buf, ret);
  5. if (t->overlay_en) {
  6. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  7. IPU_GRAPH_IN_BUFFER, 0);
  8. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_g",
  9. STATE_SEL_BUF_FAIL, chan_buf, ret);
  10. if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
  11. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  12. IPU_ALPHA_IN_BUFFER, 0);
  13. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_a",
  14. STATE_SEL_BUF_FAIL, chan_buf,
  15. ret);
  16. }
  17. }
  18. if (!(t->set.mode & VDOA_BAND_MODE)) {
  19. if (deinterlace_3_field(t))
  20. ipu_select_multi_vdi_buffer(ipu, 0);
  21. else {
  22. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  23. IPU_INPUT_BUFFER, 0);
  24. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_i",
  25. STATE_SEL_BUF_FAIL, chan_buf, ret);
  26. }
  27. }
  28. }

/* 以上的代码是only_ic情况下的执行流程,可以发现就是调用ipu_enable_channel来使能channel,然后调用ipu_select_buffer函数将buffer设置为准备好状态。 */

  1. else if (only_rot(t->set.mode)) {
  2. ret = ipu_enable_channel(ipu, t->set.rot_chan);
  3. CHECK_RETCODE(ret < 0, "ipu_enable_ch only_rot",
  4. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  5. ret = ipu_select_buffer(ipu, t->set.rot_chan,
  6. IPU_OUTPUT_BUFFER, 0);
  7. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_o",
  8. STATE_SEL_BUF_FAIL, chan_buf, ret);
  9. ret = ipu_select_buffer(ipu, t->set.rot_chan,
  10. IPU_INPUT_BUFFER, 0);
  11. CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_i",
  12. STATE_SEL_BUF_FAIL, chan_buf, ret);
  13. }

/*以上的代码是only_rot的情况下的执行流程,他们的大致流程是相似的。*/

  1. else if (ic_and_rot(t->set.mode)) {
  2. ret = ipu_enable_channel(ipu, t->set.rot_chan);
  3. CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-rot",
  4. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  5. ret = ipu_enable_channel(ipu, t->set.ic_chan);
  6. CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-ic",
  7. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  8. if (deinterlace_3_field(t)) {
  9. ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan);
  10. CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-p",
  11. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  12. ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan);
  13. CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-n",
  14. STATE_ENABLE_CHAN_FAIL, chan_en, ret);
  15. }
  16. ret = ipu_select_buffer(ipu, t->set.rot_chan,
  17. IPU_OUTPUT_BUFFER, 0);
  18. CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-rot-o",
  19. STATE_SEL_BUF_FAIL, chan_buf, ret);
  20. if (t->overlay_en) {
  21. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  22. IPU_GRAPH_IN_BUFFER, 0);
  23. CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-g",
  24. STATE_SEL_BUF_FAIL, chan_buf, ret);
  25. if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
  26. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  27. IPU_ALPHA_IN_BUFFER, 0);
  28. CHECK_RETCODE(ret < 0, "ipu_sel_buf icrot-ic-a",
  29. STATE_SEL_BUF_FAIL,
  30. chan_buf, ret);
  31. }
  32. }
  33. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  34. IPU_OUTPUT_BUFFER, 0);
  35. CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-o",
  36. STATE_SEL_BUF_FAIL, chan_buf, ret);
  37. if (deinterlace_3_field(t))
  38. ipu_select_multi_vdi_buffer(ipu, 0);
  39. else {
  40. ret = ipu_select_buffer(ipu, t->set.ic_chan,
  41. IPU_INPUT_BUFFER, 0);
  42. CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-i",
  43. STATE_SEL_BUF_FAIL, chan_buf, ret);
  44. }
  45. }

/*以上代码是ic_and_rot情况下的执行流程,可以发现这三种情况下的执行流程都是相似的,只是对于某些的判断不同,会导致某一个函数会多次调用。*/

  1. if (need_split(t))
  2. t->state = STATE_IN_PROGRESS;
  3. if (t->set.mode & VDOA_BAND_MODE) {
  4. ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS);
  5. CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_band",
  6. STATE_VDOA_IRQ_TIMEOUT, chan_rel, ret);
  7. }

/*调用vdoa_start函数来开启vdoa*/

  1. CHECK_PERF(&t->ts_waitirq);
  2. ret = wait_for_completion_timeout(&t->irq_comp,
  3. msecs_to_jiffies(t->timeout - DEF_DELAY_MS));
  4. CHECK_PERF(&t->ts_wakeup);
  5. CHECK_RETCODE(ret == 0, "wait_for_comp_timeout",
  6. STATE_IRQ_TIMEOUT, chan_rel, ret);
  7. dev_dbg(t->dev, "[0x%p] no-0x%x ipu irq done!", t, t->task_no);

/*等待完成量。*/

  1. chan_rel:
  2. chan_buf:
  3. chan_en:
  4. chan_setup:
  5. if (t->set.mode & VDOA_BAND_MODE)
  6. vdoa_stop(t->vdoa_handle);
  7. do_task_release(t, t->state >= STATE_ERR);
  8. return;
  9. }

/*至此,可以发现这几个函数,do_task_vdoa_onlydo_task_vdoa_vdido_task函数他们的执行流程也是相似的。不用说do_task_vdoa_vdi函数中直接调用了do_task函数,do_task_vdoa_only函数中也是调用ipu_init_channel_buffer--->vdoa_start--->vdoa_stopdo_task函数也基本就是这个流程。*/


分析到这里,再来看文章开头写的每一个task执行流程是:ipu_init_channel--->ipu_init_channel_buffer--->request_ipu_irq--->ipu_enable_channel--->wait_irq(taskfinish)--->ipu_disable_channel--->ipu_uninit_channel

它的大致流程就基本能缕清了。


再来看这个register_ipu_device函数中的

major= register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);mxc_ipu_fops文件操作结构体:

  1. static struct file_operations mxc_ipu_fops = {
  2. .owner = THIS_MODULE,
  3. .open = mxc_ipu_open,
  4. .mmap = mxc_ipu_mmap,
  5. .release = mxc_ipu_release,
  6. .unlocked_ioctl = mxc_ipu_ioctl,
  7. };

它注册了一个名称为“mxc_ipu”的字符设备,重要的是这个 unlocked_ioctl函数:

  1. static long mxc_ipu_ioctl(struct file *file,
  2. unsigned int cmd, unsigned long arg)
  3. {
  4. int __user *argp = (void __user *)arg;
  5. int ret = 0;
  6. switch (cmd) {
  7. case <span style="color:#FF0000;">IPU_CHECK_TASK</span>:
  8. {
  9. struct ipu_task task;
  10. if (copy_from_user
  11. (&task, (struct ipu_task *) arg,
  12. sizeof(struct ipu_task)))
  13. return -EFAULT;
  14. ret = <span style="color:#FF0000;">ipu_check_task</span>(&task);
  15. if (copy_to_user((struct ipu_task *) arg,
  16. &task, sizeof(struct ipu_task)))
  17. return -EFAULT;
  18. break;
  19. }
  20. case <span style="color:#FF0000;">IPU_QUEUE_TASK</span>:
  21. {
  22. struct ipu_task task;
  23. if (copy_from_user
  24. (&task, (struct ipu_task *) arg,
  25. sizeof(struct ipu_task)))
  26. return -EFAULT;
  27. ret =<span style="color:#FF0000;"> ipu_queue_task</span>(&task);
  28. break;
  29. }
  30. case IPU_ALLOC:
  31. {
  32. int size;
  33. struct ipu_alloc_list *mem;
  34. mem = kzalloc(sizeof(*mem), GFP_KERNEL);
  35. if (mem == NULL)
  36. return -ENOMEM;
  37. if (get_user(size, argp))
  38. return -EFAULT;
  39. mem->size = PAGE_ALIGN(size);
  40. mem->cpu_addr = dma_alloc_coherent(ipu_dev, size,
  41. &mem->phy_addr,
  42. GFP_DMA | GFP_KERNEL);
  43. if (mem->cpu_addr == NULL) {
  44. kfree(mem);
  45. return -ENOMEM;
  46. }
  47. mem->file_index = file->private_data;
  48. mutex_lock(&ipu_alloc_lock);
  49. list_add(&mem->list, &ipu_alloc_list);
  50. mutex_unlock(&ipu_alloc_lock);
  51. dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n",
  52. mem->size, mem->phy_addr);
  53. if (put_user(mem->phy_addr, argp))
  54. return -EFAULT;
  55. break;
  56. }
  57. case IPU_FREE:
  58. {
  59. unsigned long offset;
  60. struct ipu_alloc_list *mem;
  61. if (get_user(offset, argp))
  62. return -EFAULT;
  63. ret = -EINVAL;
  64. mutex_lock(&ipu_alloc_lock);
  65. list_for_each_entry(mem, &ipu_alloc_list, list) {
  66. if (mem->phy_addr == offset) {
  67. list_del(&mem->list);
  68. dma_free_coherent(ipu_dev,
  69. mem->size,
  70. mem->cpu_addr,
  71. mem->phy_addr);
  72. kfree(mem);
  73. ret = 0;
  74. break;
  75. }
  76. }
  77. mutex_unlock(&ipu_alloc_lock);
  78. if (0 == ret)
  79. dev_dbg(ipu_dev, "free %d bytes @ 0x%08X\n",
  80. mem->size, mem->phy_addr);
  81. break;
  82. }
  83. default:
  84. break;
  85. }
  86. return ret;
  87. }

在应用程序中,用到了IPU_CHECK_TASKIPU_QUEUE_TASK宏,可以通过代码看出来,在IPU_CHECK_TASK宏中,它调用ipu_check_task函数,里面继续调用check_task函数来检查这个task是否合乎规范,在IPU_QUEUE_TASK宏中,调用ipu_queue_task函数来将task加入队列。


对于框架暂时先分析到这,下面就具体分析文件里面每一个函数的实现。







声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/946308
推荐阅读
  

闽ICP备14008679号