?? mx27_v4l2_output.c
字號:
/* * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. *//* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html *//*! * @file mx27_v4l2_output.c * * @brief MX27 V4L2 Video Output Driver * * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality. * * @ingroup MXC_V4L2_OUTPUT */#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/platform_device.h>#include <media/v4l2-dev.h>#include <asm/poll.h>#include <asm/io.h>#include <asm/semaphore.h>#include "mxc_v4l2_output.h"#include "mx27_pp.h"#include "../drivers/video/mxc/mx2fb.h"#define SDC_FG_FB_FORMAT V4L2_PIX_FMT_RGB565struct v4l2_output mxc_outputs[1] = { { .index = 0, .name = "DISP0 Video Out", .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct, but no other choice */ .audioset = 0, .modulator = 0, .std = V4L2_STD_UNKNOWN},};static int video_nr = 16;static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;vout_data *g_vout;/* debug counters */uint32_t g_irq_cnt;uint32_t g_buf_output_cnt;uint32_t g_buf_q_cnt;uint32_t g_buf_dq_cnt;static int dq_intr_cnt = 0;#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNCstatic uint32_t g_output_fb = -1;static uint32_t g_fb_enabled = 0;static uint32_t g_pp_ready = 0;static int fb_event_notify(struct notifier_block *self, unsigned long action, void *data){ struct fb_event *event = data; struct fb_info *info = event->info; unsigned long lock_flags; int blank, i; for (i = 0; i < num_registered_fb; i++) if (registered_fb[i] == info) break; /* * Check if the event is sent by the framebuffer in which * the video is displayed. */ if (i != g_output_fb) return 0; switch (action) { case FB_EVENT_BLANK: blank = *(int *)event->data; spin_lock_irqsave(&g_lock, lock_flags); g_fb_enabled = !blank; if (blank && g_pp_ready) { if (pp_enable(1)) pr_debug("unable to enable PP\n"); g_pp_ready = 0; } spin_unlock_irqrestore(&g_lock, lock_flags); break; case FB_EVENT_MXC_EOF: spin_lock_irqsave(&g_lock, lock_flags); g_fb_enabled = 1; if (g_pp_ready) { if (pp_enable(1)) pr_debug("unable to enable PP\n"); g_pp_ready = 0; } spin_unlock_irqrestore(&g_lock, lock_flags); break; } return 0;}static struct notifier_block fb_event_notifier = { .notifier_call = fb_event_notify,};static struct notifier_block mx2fb_event_notifier = { .notifier_call = fb_event_notify,};#endif#define QUEUE_SIZE (MAX_FRAME_NUM + 1)static __inline int queue_size(v4l_queue * q){ if (q->tail >= q->head) return (q->tail - q->head); else return ((q->tail + QUEUE_SIZE) - q->head);}static __inline int queue_buf(v4l_queue * q, int idx){ if (((q->tail + 1) % QUEUE_SIZE) == q->head) return -1; /* queue full */ q->list[q->tail] = idx; q->tail = (q->tail + 1) % QUEUE_SIZE; return 0;}static __inline int dequeue_buf(v4l_queue * q){ int ret; if (q->tail == q->head) return -1; /* queue empty */ ret = q->list[q->head]; q->head = (q->head + 1) % QUEUE_SIZE; return ret;}static __inline int peek_next_buf(v4l_queue * q){ if (q->tail == q->head) return -1; /* queue empty */ return q->list[q->head];}static __inline unsigned long get_jiffies(struct timeval *t){ struct timeval cur; if (t->tv_usec >= 1000000) { t->tv_sec += t->tv_usec / 1000000; t->tv_usec = t->tv_usec % 1000000; } do_gettimeofday(&cur); if ((t->tv_sec < cur.tv_sec) || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) return jiffies; if (t->tv_usec < cur.tv_usec) { cur.tv_sec = t->tv_sec - cur.tv_sec - 1; cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; } else { cur.tv_sec = t->tv_sec - cur.tv_sec; cur.tv_usec = t->tv_usec - cur.tv_usec; } return jiffies + timeval_to_jiffies(&cur);}/*! * Private function to free buffers * * @param bufs_paddr Array of physical address of buffers to be freed * * @param bufs_vaddr Array of virtual address of buffers to be freed * * @param num_buf Number of buffers to be freed * * @param size Size for each buffer to be free * * @return status 0 success. */static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], int num_buf, int size){ int i; for (i = 0; i < num_buf; i++) { if (bufs_vaddr[i] != 0) { dma_free_coherent(0, size, bufs_vaddr[i], bufs_paddr[i]); pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]); bufs_paddr[i] = 0; bufs_vaddr[i] = NULL; } } return 0;}/*! * Private function to allocate buffers * * @param bufs_paddr Output array of physical address of buffers allocated * * @param bufs_vaddr Output array of virtual address of buffers allocated * * @param num_buf Input number of buffers to allocate * * @param size Input size for each buffer to allocate * * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. */static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], int num_buf, int size){ int i; for (i = 0; i < num_buf; i++) { bufs_vaddr[i] = dma_alloc_coherent(0, size, &bufs_paddr[i], GFP_DMA | GFP_KERNEL); if (bufs_vaddr[i] == 0) { mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size); pr_debug("dma_alloc_coherent failed.\n"); return -ENOBUFS; } pr_debug("allocated @ paddr=0x%08X, size=%d.\n", (u32) bufs_paddr[i], size); } return 0;}static void mxc_v4l2out_timer_handler(unsigned long arg){ int index; unsigned long timeout; unsigned long lock_flags; vout_data *vout = (vout_data *) arg; pr_debug("timer handler: %lu\n", jiffies); spin_lock_irqsave(&g_lock, lock_flags); if ((vout->state == STATE_STREAM_OFF) || (vout->state == STATE_STREAM_STOPPING)) { pr_debug("stream has stopped\n"); goto exit0; } /* * If timer occurs before PP h/w is ready, then set the state to * paused and the timer will be set again when next buffer is queued * or PP completes. */ if (vout->ipu_buf[0] != -1) { pr_debug("buffer is busy\n"); vout->state = STATE_STREAM_PAUSED; goto exit0; } /* Dequeue buffer and pass to PP */ index = dequeue_buf(&vout->ready_q); if (index == -1) { /* no buffers ready, should never occur */ pr_debug("mxc_v4l2out: timer - no queued buffers ready\n"); goto exit0; } g_buf_dq_cnt++; vout->frame_count++; vout->ipu_buf[0] = index; if (pp_ptr((unsigned int)vout->queue_buf_paddr[index])) { pr_debug("unable to update buffer\n"); goto exit0; }#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC if (vout->tear_protection == TEARING_PROTECTION_ACTIVE) { if (g_fb_enabled && (vout->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY)) g_pp_ready = 1; else if (pp_enable(1)) { pr_debug("unable to enable PP\n"); goto exit0; } } else if (pp_enable(1)) { pr_debug("unable to enable PP\n"); goto exit0; }#else if (pp_enable(1)) { pr_debug("unable to enable PP\n"); goto exit0; }#endif pr_debug("enabled index %d\n", index); /* Setup timer for next buffer */ index = peek_next_buf(&vout->ready_q); pr_debug("next index %d\n", index); if (index != -1) { /* if timestamp is 0, then default to 30fps */ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)) timeout = vout->start_jiffies + vout->frame_count * HZ / 30; else timeout = get_jiffies(&vout->v4l2_bufs[index].timestamp); if (jiffies >= timeout) { pr_debug("warning: timer timeout already expired.\n"); } if (mod_timer(&vout->output_timer, timeout)) pr_debug("warning: timer was already set\n"); pr_debug("timer handler next schedule: %lu\n", timeout); } else { vout->state = STATE_STREAM_PAUSED; pr_debug("timer handler paused\n"); } exit0: spin_unlock_irqrestore(&g_lock, lock_flags);}irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id){ int last_buf; int index; unsigned long timeout; unsigned long lock_flags; vout_data *vout = dev_id; spin_lock_irqsave(&g_lock, lock_flags); g_irq_cnt++; if ((vout->state == STATE_STREAM_OFF) || (vout->state == STATE_STREAM_STOPPING)) { spin_unlock_irqrestore(&g_lock, lock_flags); return IRQ_HANDLED; } if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { struct fb_gwinfo gwinfo; gwinfo.enabled = 1; gwinfo.alpha_value = 255; gwinfo.ck_enabled = 0; gwinfo.xpos = vout->crop_current.left; gwinfo.ypos = vout->crop_current.top; gwinfo.base = (unsigned long)vout->display_bufs[pp_num_last()]; gwinfo.xres = vout->crop_current.width; gwinfo.yres = vout->crop_current.height; gwinfo.xres_virtual = vout->crop_current.width; gwinfo.vs_reversed = 0; mx2_gw_set(&gwinfo); } /* Process previous buffer */ last_buf = vout->ipu_buf[0]; pr_debug("last_buf %d g_irq_cnt %d\n", last_buf, g_irq_cnt); if (last_buf != -1) { g_buf_output_cnt++; vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; queue_buf(&vout->done_q, last_buf); vout->ipu_buf[0] = -1; wake_up_interruptible(&vout->v4l_bufq); } /* Setup timer for next buffer, when stream has been paused */ if ((vout->state == STATE_STREAM_PAUSED) && ((index = peek_next_buf(&vout->ready_q)) != -1)) { pr_debug("next index %d\n", index); /* if timestamp is 0, then default to 30fps */ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)) timeout = vout->start_jiffies + vout->frame_count * HZ / 30; else timeout = get_jiffies(&vout->v4l2_bufs[index].timestamp); if (jiffies >= timeout) { pr_debug("warning: timer timeout already expired.\n"); } vout->state = STATE_STREAM_ON; if (mod_timer(&vout->output_timer, timeout)) pr_debug("warning: timer was already set\n"); pr_debug("timer handler next schedule: %lu\n", timeout); } spin_unlock_irqrestore(&g_lock, lock_flags); return IRQ_HANDLED;}/*! * Start the output stream * * @param vout structure vout_data * * * @return status 0 Success */static int mxc_v4l2out_streamon(vout_data * vout){ unsigned long timeout; int index; if (!vout) return -EINVAL; if (vout->state != STATE_STREAM_OFF) return -EBUSY; if (queue_size(&vout->ready_q) < 1) { pr_debug("no buffers queued yet!\n"); return -EINVAL; } vout->ipu_buf[0] = -1; if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { /* Free previously allocated buffer */ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr, 2, vout->sdc_fg_buf_size); /* Allocate buffers for foreground */ if (mxc_allocate_buffers(vout->display_bufs, vout->display_bufs_vaddr, 2, vout->sdc_fg_buf_size) < 0) { pr_debug("unable to allocate SDC FG buffers\n"); return -ENOMEM; } } /* Configure PP */ if (pp_cfg(vout)) { /* Free previously allocated buffer */ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr, 2, vout->sdc_fg_buf_size); pr_debug("failed to config PP.\n"); return -EINVAL; }#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC if (vout->tear_protection == TEARING_PROTECTION_ACTIVE) { g_output_fb = vout->output_fb_num[vout->cur_disp_output]; g_fb_enabled = 0; g_pp_ready = 0; fb_register_client(&fb_event_notifier); mx2fb_register_client(&mx2fb_event_notifier); }#endif vout->frame_count = 0; vout->state = STATE_STREAM_ON; index = peek_next_buf(&vout->ready_q); /* if timestamp is 0, then default to 30fps */ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)) timeout = jiffies; else timeout = get_jiffies(&vout->v4l2_bufs[index].timestamp); if (jiffies >= timeout) { pr_debug("warning: timer timeout already expired.\n"); } vout->start_jiffies = vout->output_timer.expires = timeout; pr_debug("STREAMON:Add timer %d timeout @ %lu jiffies, current = %lu\n", index, timeout, jiffies); add_timer(&vout->output_timer); return 0;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -