📄 mxc_v4l2_capture.c
字号:
/* * Copyright 2004-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 drivers/media/video/mxc/capture/mxc_v4l2_capture.c * * @brief Mxc Video For Linux 2 driver * * @ingroup MXC_V4L2_CAPTURE */#include <linux/version.h>#include <linux/module.h>#include <linux/init.h>#include <linux/platform_device.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/ctype.h>#include <asm/io.h>#include <asm/semaphore.h>#include <linux/pagemap.h>#include <linux/vmalloc.h>#include <linux/types.h>#include <linux/fb.h>#include <linux/dma-mapping.h>#include <asm/arch/mxcfb.h>#include "mxc_v4l2_capture.h"#include "ipu_prp_sw.h"static int csi_mclk_flag_backup;static int video_nr = -1;static cam_data *g_cam;#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 2static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = { { .index = 0, .name = "DISP3", .type = V4L2_OUTPUT_TYPE_ANALOG, .audioset = 0, .modulator = 0, .std = V4L2_STD_UNKNOWN, }, { .index = 1, .name = "DISP0", .type = V4L2_OUTPUT_TYPE_ANALOG, .audioset = 0, .modulator = 0, .std = V4L2_STD_UNKNOWN, }};/*! * Free frame buffers * * @param cam Structure cam_data * * * @return status 0 success. */static int mxc_free_frame_buf(cam_data * cam){ int i; for (i = 0; i < FRAME_NUM; i++) { if (cam->frame[i].vaddress != 0) { dma_free_coherent(0, cam->frame[i].buffer.length, cam->frame[i].vaddress, cam->frame[i].paddress); cam->frame[i].vaddress = 0; } } return 0;}/*! * Allocate frame buffers * * @param cam Structure cam_data * * * @param count int number of buffer need to allocated * * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. */static int mxc_allocate_frame_buf(cam_data * cam, int count){ int i; for (i = 0; i < count; i++) { cam->frame[i].vaddress = dma_alloc_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), &cam->frame[i].paddress, GFP_DMA | GFP_KERNEL); if (cam->frame[i].vaddress == 0) { printk(KERN_ERR "mxc_allocate_frame_buf failed.\n"); mxc_free_frame_buf(cam); return -ENOBUFS; } cam->frame[i].buffer.index = i; cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->frame[i].buffer.length = PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; cam->frame[i].buffer.m.offset = cam->frame[i].paddress; cam->frame[i].index = i; } return 0;}/*! * Free frame buffers status * * @param cam Structure cam_data * * * @return none */static void mxc_free_frames(cam_data * cam){ int i; for (i = 0; i < FRAME_NUM; i++) { cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; } cam->enc_counter = 0; cam->skip_frame = 0; INIT_LIST_HEAD(&cam->ready_q); INIT_LIST_HEAD(&cam->working_q); INIT_LIST_HEAD(&cam->done_q);}/*! * Return the buffer status * * @param cam Structure cam_data * * @param buf Structure v4l2_buffer * * * @return status 0 success, EINVAL failed. */static int mxc_v4l2_buffer_status(cam_data * cam, struct v4l2_buffer *buf){ if (buf->index < 0 || buf->index >= FRAME_NUM) { printk(KERN_ERR "mxc_v4l2_buffer_status buffers not allocated\n"); return -EINVAL; } memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); return 0;}/*! * start the encoder job * * @param cam structure cam_data * * * @return status 0 Success */static int mxc_streamon(cam_data * cam){ struct mxc_v4l_frame *frame; int err = 0; if (list_empty(&cam->ready_q)) { printk(KERN_ERR "mxc_streamon buffer not been queued yet\n"); return -EINVAL; } cam->capture_pid = current->pid; if (cam->enc_enable) { err = cam->enc_enable(cam); if (err != 0) { return err; } } cam->ping_pong_csi = 0; if (cam->enc_update_eba) { frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); list_del(cam->ready_q.next); list_add_tail(&frame->queue, &cam->working_q); err = cam->enc_update_eba(frame->buffer.m.offset, &cam->ping_pong_csi); frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); list_del(cam->ready_q.next); list_add_tail(&frame->queue, &cam->working_q); err |= cam->enc_update_eba(frame->buffer.m.offset, &cam->ping_pong_csi); } else { return -EINVAL; } cam->capture_on = true; return err;}/*! * Shut down the encoder job * * @param cam structure cam_data * * * @return status 0 Success */static int mxc_streamoff(cam_data * cam){ int err = 0; if (cam->capture_on == false) return 0; if (cam->enc_disable) { err = cam->enc_disable(cam); } mxc_free_frames(cam); cam->capture_on = false; return err;}/*! * Valid whether the palette is supported * * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 * * @return 0 if failed */static inline int valid_mode(u32 palette){ return ((palette == V4L2_PIX_FMT_RGB565) || (palette == V4L2_PIX_FMT_BGR24) || (palette == V4L2_PIX_FMT_RGB24) || (palette == V4L2_PIX_FMT_BGR32) || (palette == V4L2_PIX_FMT_RGB32) || (palette == V4L2_PIX_FMT_YUV422P) || (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420));}/*! * Valid and adjust the overlay window size, position * * @param cam structure cam_data * * @param win struct v4l2_window * * * @return 0 */static int verify_preview(cam_data * cam, struct v4l2_window *win){ int i = 0; int *width, *height; do { cam->overlay_fb = (struct fb_info *)registered_fb[i]; if (cam->overlay_fb == NULL) { printk(KERN_ERR "verify_preview No matched.\n"); return -1; } if (strncmp(cam->overlay_fb->fix.id, mxc_capture_outputs[cam->output].name, 5) == 0) { break; } } while (++i < FB_MAX); /* 4 bytes alignment for both FG and BG */ if (cam->overlay_fb->var.bits_per_pixel == 24) { win->w.left -= win->w.left % 4; } else if (cam->overlay_fb->var.bits_per_pixel == 16) { win->w.left -= win->w.left % 2; } if (win->w.width + win->w.left > cam->overlay_fb->var.xres) win->w.width = cam->overlay_fb->var.xres - win->w.left; if (win->w.height + win->w.top > cam->overlay_fb->var.yres) win->w.height = cam->overlay_fb->var.yres - win->w.top; /* stride line limitation */ win->w.height -= win->w.height % 8; win->w.width -= win->w.width % 8; if (cam->rotation >= IPU_ROTATE_90_RIGHT) { height = &win->w.width; width = &win->w.height; } else { width = &win->w.width; height = &win->w.height; } if ((cam->crop_bounds.width / *width > 8) || ((cam->crop_bounds.width / *width == 8) && (cam->crop_bounds.width % *width))) { *width = cam->crop_bounds.width / 8; if (*width % 8) *width += 8 - *width % 8; if (*width + win->w.left > cam->overlay_fb->var.xres) { printk(KERN_ERR "width exceed resize limit.\n"); return -1; } printk(KERN_ERR "width exceed limit resize to %d.\n", *width); } if ((cam->crop_bounds.height / *height > 8) || ((cam->crop_bounds.height / *height == 8) && (cam->crop_bounds.height % *height))) { *height = cam->crop_bounds.height / 8; if (*height % 8) *height += 8 - *height % 8; if (*height + win->w.top > cam->overlay_fb->var.yres) { printk(KERN_ERR "height exceed resize limit.\n"); return -1; } printk(KERN_ERR "height exceed limit resize to %d.\n", *height); } return 0;}/*! * start the viewfinder job * * @param cam structure cam_data * * * @return status 0 Success */static int start_preview(cam_data * cam){ int err = 0;#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE) if (cam->output == 0) { if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)#if defined(CONFIG_FB_MXC_OVERLAY) return -1;#else err = prp_vf_sdc_select(cam);#endif else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) err = prp_vf_sdc_select_bg(cam); if (err != 0) return err; err = cam->vf_start_sdc(cam); }#endif#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE) if (cam->output == 1) { err = prp_vf_adc_select(cam); if (err != 0) return err; err = cam->vf_start_adc(cam); }#endif return err;}/*! * shut down the viewfinder job * * @param cam structure cam_data * * * @return status 0 Success */static int stop_preview(cam_data * cam){ int err = 0;#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE) if (cam->output == 1) { err = prp_vf_adc_deselect(cam); }#endif#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE) if (cam->output == 0) { if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)#if defined(CONFIG_FB_MXC_OVERLAY) return -1;#else err = prp_vf_sdc_deselect(cam);#endif else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) err = prp_vf_sdc_deselect_bg(cam); }#endif return err;}/*! * V4L2 - mxc_v4l2_g_fmt function * * @param cam structure cam_data * * * @param f structure v4l2_format * * * @return status 0 success, EINVAL failed */static int mxc_v4l2_g_fmt(cam_data * cam, struct v4l2_format *f){ int retval = 0; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: f->fmt.pix = cam->v2f.fmt.pix; retval = 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: f->fmt.win = cam->win; break; default: retval = -EINVAL; } return retval;}/*! * V4L2 - mxc_v4l2_s_fmt function * * @param cam structure cam_data * * * @param f structure v4l2_format * * * @return status 0 success, EINVAL failed */static int mxc_v4l2_s_fmt(cam_data * cam, struct v4l2_format *f){ int retval = 0; int size = 0; int bytesperline = 0; int *width, *height; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (!valid_mode(f->fmt.pix.pixelformat)) { printk(KERN_ERR "mxc_v4l2_s_fmt: format not supported\n"); return -EINVAL; } if (cam->rotation >= IPU_ROTATE_90_RIGHT) { height = &f->fmt.pix.width; width = &f->fmt.pix.height; } else { width = &f->fmt.pix.width; height = &f->fmt.pix.height; } /* stride line limitation */ *width -= *width % 8; *height -= *height % 8; if ((cam->crop_bounds.width / *width > 8) || ((cam->crop_bounds.width / *width == 8) && (cam->crop_bounds.width % *width))) { *width = cam->crop_bounds.width / 8; if (*width % 8) *width += 8 - *width % 8; printk(KERN_ERR "width exceed limit resize to %d.\n", *width); } if ((cam->crop_bounds.height / *height > 8) || ((cam->crop_bounds.height / *height == 8) && (cam->crop_bounds.height % *height))) { *height = cam->crop_bounds.height / 8; if (*height % 8) *height += 8 - *height % 8; printk(KERN_ERR "height exceed limit resize to %d.\n", *height); } switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB565: size = f->fmt.pix.width * f->fmt.pix.height * 2; bytesperline = f->fmt.pix.width * 2; break; case V4L2_PIX_FMT_BGR24: size = f->fmt.pix.width * f->fmt.pix.height * 3; bytesperline = f->fmt.pix.width * 3; break; case V4L2_PIX_FMT_RGB24: size = f->fmt.pix.width * f->fmt.pix.height * 3; bytesperline = f->fmt.pix.width * 3; break; case V4L2_PIX_FMT_BGR32: size = f->fmt.pix.width * f->fmt.pix.height * 4; bytesperline = f->fmt.pix.width * 4; break; case V4L2_PIX_FMT_RGB32: size = f->fmt.pix.width * f->fmt.pix.height * 4; bytesperline = f->fmt.pix.width * 4; break; case V4L2_PIX_FMT_YUV422P: size = f->fmt.pix.width * f->fmt.pix.height * 2; bytesperline = f->fmt.pix.width; break; case V4L2_PIX_FMT_UYVY: size = f->fmt.pix.width * f->fmt.pix.height * 2; bytesperline = f->fmt.pix.width * 2; break; case V4L2_PIX_FMT_YUV420: size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; bytesperline = f->fmt.pix.width; break; default: break; } if (f->fmt.pix.bytesperline < bytesperline) { f->fmt.pix.bytesperline = bytesperline; } else { bytesperline = f->fmt.pix.bytesperline; } if (f->fmt.pix.sizeimage < size) { f->fmt.pix.sizeimage = size; } else { size = f->fmt.pix.sizeimage; } cam->v2f.fmt.pix = f->fmt.pix; copy_from_user(&cam->offset, (void *)cam->v2f.fmt.pix.priv, sizeof(cam->offset)); retval = 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: retval = verify_preview(cam, &f->fmt.win); cam->win = f->fmt.win; break; default: retval = -EINVAL; } return retval;}/*! * get control param * * @param cam structure cam_data * * * @param c structure v4l2_control * * * @return status 0 success, EINVAL failed */static int mxc_get_v42l_control(cam_data * cam, struct v4l2_control *c){ int status = 0; switch (c->id) { case V4L2_CID_HFLIP: if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) c->value = 1; break; case V4L2_CID_VFLIP: if (cam->rotation == IPU_ROTATE_VERT_FLIP) c->value = 1; break; case V4L2_CID_MXC_ROT: c->value = cam->rotation; break; case V4L2_CID_BRIGHTNESS: c->value = cam->bright; break; case V4L2_CID_HUE: c->value = cam->hue; break; case V4L2_CID_CONTRAST: c->value = cam->contrast; break; case V4L2_CID_SATURATION: c->value = cam->saturation; break; case V4L2_CID_RED_BALANCE: c->value = cam->red; break; case V4L2_CID_BLUE_BALANCE: c->value = cam->blue; break; case V4L2_CID_BLACK_LEVEL: c->value = cam->ae_mode; break; default: status = -EINVAL; } return status;}/*! * V4L2 - set_control function * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing. * 0 for normal operation * 1 for vertical flip * 2 for horizontal flip * 3 for horizontal and vertical flip * 4 for 90 degree rotation * @param cam structure cam_data * * * @param c structure v4l2_control *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -