?? mx27_prpsw.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 mx27_prpsw.c * * @brief MX27 Video For Linux 2 capture driver * * @ingroup MXC_V4L2_CAPTURE */#include <linux/kernel.h>#include <linux/module.h>#include <linux/string.h>#include <linux/fb.h>#include <linux/pci.h>#include <asm/io.h>#include <asm/irq.h>#include "mxc_v4l2_capture.h"#include "mx27_prp.h"#include "mx27_csi.h"#include "../drivers/video/mxc/mx2fb.h"#include "../opl/opl.h"#define MEAN_COEF (SZ_COEF >> 1)static char prp_dev[] = "emma_prp";static int g_still_on = 0;static emma_prp_cfg g_prp_cfg;static int g_vfbuf, g_rotbuf;static struct tasklet_struct prp_vf_tasklet;/* * The following variables represents the virtual address for the cacheable * buffers accessed by SW rotation/mirroring. The rotation/mirroring in * cacheable buffers has significant performance improvement than it in * non-cacheable buffers. */static char *g_vaddr_vfbuf[2] = { 0, 0 };static char *g_vaddr_rotbuf[2] = { 0, 0 };static char *g_vaddr_fb = 0;static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam);static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam);static int prp_vf_mem_alloc(cam_data * cam);static void prp_vf_mem_free(cam_data * cam);static int prp_rot_mem_alloc(cam_data * cam);static void prp_rot_mem_free(cam_data * cam);static int prp_enc_update_eba(u32 eba, int *buffer_num);static int prp_enc_enable(void *private);static int prp_enc_disable(void *private);static int prp_vf_start(void *private);static int prp_vf_stop(void *private);static int prp_still_start(void *private);static int prp_still_stop(void *private);static irqreturn_t prp_isr(int irq, void *dev_id);static void rotation(unsigned long private);static int prp_resize_check_ch1(emma_prp_cfg * cfg);static int prp_resize_check_ch2(emma_prp_cfg * cfg);#define PRP_DUMP(val) pr_debug("%s\t = 0x%08X\t%d\n", #val, val, val)/*! * @brief Dump PrP configuration parameters. * @param cfg The pointer to PrP configuration parameter */static void prp_cfg_dump(emma_prp_cfg * cfg){ PRP_DUMP(cfg->in_pix); PRP_DUMP(cfg->in_width); PRP_DUMP(cfg->in_height); PRP_DUMP(cfg->in_csi); PRP_DUMP(cfg->in_line_stride); PRP_DUMP(cfg->in_line_skip); PRP_DUMP(cfg->in_ptr); PRP_DUMP(cfg->ch1_pix); PRP_DUMP(cfg->ch1_width); PRP_DUMP(cfg->ch1_height); PRP_DUMP(cfg->ch1_scale.algo); PRP_DUMP(cfg->ch1_scale.width.num); PRP_DUMP(cfg->ch1_scale.width.den); PRP_DUMP(cfg->ch1_scale.height.num); PRP_DUMP(cfg->ch1_scale.height.den); PRP_DUMP(cfg->ch1_stride); PRP_DUMP(cfg->ch1_ptr); PRP_DUMP(cfg->ch1_ptr2); PRP_DUMP(cfg->ch1_csi); PRP_DUMP(cfg->ch2_pix); PRP_DUMP(cfg->ch2_width); PRP_DUMP(cfg->ch2_height); PRP_DUMP(cfg->ch2_scale.algo); PRP_DUMP(cfg->ch2_scale.width.num); PRP_DUMP(cfg->ch2_scale.width.den); PRP_DUMP(cfg->ch2_scale.height.num); PRP_DUMP(cfg->ch2_scale.height.den); PRP_DUMP(cfg->ch2_ptr); PRP_DUMP(cfg->ch2_ptr2); PRP_DUMP(cfg->ch2_csi);}/*! * @brief Set PrP channel 1 output address. * @param cfg Pointer to emma_prp_cfg structure * @param cam Pointer to cam_data structure * @return Zero on success, others on failure */static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam){ if (cam->rotation != V4L2_MXC_ROTATE_NONE) { cfg->ch1_ptr = (unsigned int)cam->rot_vf_bufs[0]; cfg->ch1_ptr2 = (unsigned int)cam->rot_vf_bufs[1]; if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) cfg->ch1_stride = cam->win.w.height; else cfg->ch1_stride = cam->win.w.width; if (cam->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY) { struct fb_info *fb = cam->overlay_fb; if (!fb) return -1; if (g_vaddr_fb) iounmap(g_vaddr_fb); g_vaddr_fb = ioremap_cached(fb->fix.smem_start, fb->fix.smem_len); if (!g_vaddr_fb) return -1; } } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { cfg->ch1_ptr = (unsigned int)cam->vf_bufs[0]; cfg->ch1_ptr2 = (unsigned int)cam->vf_bufs[1]; cfg->ch1_stride = cam->win.w.width; } else { struct fb_info *fb = cam->overlay_fb; if (!fb) return -1; cfg->ch1_ptr = fb->fix.smem_start; cfg->ch1_ptr += cam->win.w.top * fb->var.xres_virtual * (fb->var.bits_per_pixel >> 3) + cam->win.w.left * (fb->var.bits_per_pixel >> 3); cfg->ch1_ptr2 = cfg->ch1_ptr; cfg->ch1_stride = fb->var.xres_virtual; } return 0;}/*! * @brief Setup PrP configuration parameters. * @param cfg Pointer to emma_prp_cfg structure * @param cam Pointer to cam_data structure * @return Zero on success, others on failure */static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam){ cfg->in_pix = PRP_PIXIN_YUYV; cfg->in_width = cam->crop_current.width; cfg->in_height = cam->crop_current.height; cfg->in_line_stride = cam->crop_current.left; cfg->in_line_skip = cam->crop_current.top; cfg->in_ptr = 0; cfg->in_csi = PRP_CSI_LOOP; memset(cfg->in_csc, 0, sizeof(cfg->in_csc)); if (cam->overlay_on) { /* Convert V4L2 pixel format to PrP pixel format */ switch (cam->v4l2_fb.fmt.pixelformat) { case V4L2_PIX_FMT_RGB332: cfg->ch1_pix = PRP_PIX1_RGB332; break; case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_BGR32: cfg->ch1_pix = PRP_PIX1_RGB888; break; case V4L2_PIX_FMT_YUYV: cfg->ch1_pix = PRP_PIX1_YUYV; break; case V4L2_PIX_FMT_UYVY: cfg->ch1_pix = PRP_PIX1_UYVY; break; case V4L2_PIX_FMT_RGB565: default: cfg->ch1_pix = PRP_PIX1_RGB565; break; } if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT) || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP) || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP) || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) { cfg->ch1_width = cam->win.w.height; cfg->ch1_height = cam->win.w.width; } else { cfg->ch1_width = cam->win.w.width; cfg->ch1_height = cam->win.w.height; } if (set_ch1_addr(cfg, cam)) return -1; } else { cfg->ch1_pix = PRP_PIX1_UNUSED; cfg->ch1_width = cfg->in_width; cfg->ch1_height = cfg->in_height; } cfg->ch1_scale.algo = 0; cfg->ch1_scale.width.num = cfg->in_width; cfg->ch1_scale.width.den = cfg->ch1_width; cfg->ch1_scale.height.num = cfg->in_height; cfg->ch1_scale.height.den = cfg->ch1_height; cfg->ch1_csi = PRP_CSI_EN; if (cam->capture_on || g_still_on) { switch (cam->v2f.fmt.pix.pixelformat) { case V4L2_PIX_FMT_YUYV: cfg->ch2_pix = PRP_PIX2_YUV422; break; case V4L2_PIX_FMT_YUV420: cfg->ch2_pix = PRP_PIX2_YUV420; break; /* * YUV444 is not defined by V4L2. * We support it in default case. */ default: cfg->ch2_pix = PRP_PIX2_YUV444; break; } cfg->ch2_width = cam->v2f.fmt.pix.width; cfg->ch2_height = cam->v2f.fmt.pix.height; } else { cfg->ch2_pix = PRP_PIX2_UNUSED; cfg->ch2_width = cfg->in_width; cfg->ch2_height = cfg->in_height; } cfg->ch2_scale.algo = 0; cfg->ch2_scale.width.num = cfg->in_width; cfg->ch2_scale.width.den = cfg->ch2_width; cfg->ch2_scale.height.num = cfg->in_height; cfg->ch2_scale.height.den = cfg->ch2_height; cfg->ch2_csi = PRP_CSI_EN; memset(cfg->scale, 0, sizeof(cfg->scale)); cfg->scale[0].algo = cfg->ch1_scale.algo & 3; cfg->scale[1].algo = (cfg->ch1_scale.algo >> 2) & 3; cfg->scale[2].algo = cfg->ch2_scale.algo & 3; cfg->scale[3].algo = (cfg->ch2_scale.algo >> 2) & 3; prp_cfg_dump(cfg); if (prp_resize_check_ch2(cfg)) return -1; if (prp_resize_check_ch1(cfg)) return -1; return 0;}/*! * @brief PrP interrupt handler */static irqreturn_t prp_isr(int irq, void *dev_id){ int status; cam_data *cam = (cam_data *) dev_id; status = prphw_isr(); if (g_still_on && (status & PRP_INTRSTAT_CH2BUF1)) { prp_still_stop(cam); cam->still_counter++; wake_up_interruptible(&cam->still_queue); /* * Still & video capture use the same PrP channel 2. * They are execlusive. */ } else if (cam->capture_on) {// if (status & PRP_INTRSTAT_CH2OVF) { if (0) { prphw_disable(PRP_CHANNEL_2); prphw_enable(PRP_CHANNEL_2); cam->enc_callback(1, cam); } else if (status & (PRP_INTRSTAT_CH2BUF1 | PRP_INTRSTAT_CH2BUF2)) { cam->enc_callback(0, cam); } } if (cam->overlay_on && (status & (PRP_INTRSTAT_CH1BUF1 | PRP_INTRSTAT_CH1BUF2))) { if (cam->rotation != V4L2_MXC_ROTATE_NONE) { g_rotbuf = (status & PRP_INTRSTAT_CH1BUF1) ? 0 : 1; tasklet_schedule(&prp_vf_tasklet); } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) { struct fb_gwinfo gwinfo; gwinfo.enabled = 1; gwinfo.alpha_value = 255; gwinfo.ck_enabled = 0; gwinfo.xpos = cam->win.w.left; gwinfo.ypos = cam->win.w.top; gwinfo.xres = cam->win.w.width; gwinfo.yres = cam->win.w.height; gwinfo.xres_virtual = cam->win.w.width; gwinfo.vs_reversed = 0; if (status & PRP_INTRSTAT_CH1BUF1) gwinfo.base = (unsigned long)cam->vf_bufs[0]; else gwinfo.base = (unsigned long)cam->vf_bufs[1]; mx2_gw_set(&gwinfo); } } return IRQ_HANDLED;}/*! * @brief PrP initialization. * @param dev_id Pointer to cam_data structure * @return Zero on success, others on failure */int prp_init(void *dev_id){ enable_irq(INT_EMMAPRP); if (request_irq(INT_EMMAPRP, prp_isr, 0, prp_dev, dev_id)) return -1; prphw_init(); return 0;}/*! * @brief PrP initialization. * @param dev_id Pointer to cam_data structure */void prp_exit(void *dev_id){ prphw_exit(); disable_irq(INT_EMMAPRP); free_irq(INT_EMMAPRP, dev_id);}/*! * @brief Update PrP channel 2 output buffer address. * @param eba Physical address for PrP output buffer * @param buffer_num The PrP channel 2 buffer number to be updated * @return Zero on success, others on failure */static int prp_enc_update_eba(u32 eba, int *buffer_num){ if (*buffer_num) { g_prp_cfg.ch2_ptr2 = eba; prphw_ch2ptr2(&g_prp_cfg); *buffer_num = 0; } else { g_prp_cfg.ch2_ptr = eba; prphw_ch2ptr(&g_prp_cfg); *buffer_num = 1; } return 0;}/*! * @brief Enable PrP for encoding. * @param private Pointer to cam_data structure * @return Zero on success, others on failure */static int prp_enc_enable(void *private){ cam_data *cam = (cam_data *) private; if (prp_v4l2_cfg(&g_prp_cfg, cam)) return -1; csi_enable_mclk(CSI_MCLK_ENC, true, true); prphw_reset(); if (prphw_cfg(&g_prp_cfg)) return -1; prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2) : PRP_CHANNEL_2); return 0;}/*! * @brief Disable PrP for encoding. * @param private Pointer to cam_data structure * @return Zero on success, others on failure */static int prp_enc_disable(void *private){ prphw_disable(PRP_CHANNEL_2); csi_enable_mclk(CSI_MCLK_ENC, false, false); return 0;}/*! * @brief Setup encoding functions. * @param private Pointer to cam_data structure * @return Zero on success, others on failure */int prp_enc_select(void *private){ int ret = 0; cam_data *cam = (cam_data *) private; if (cam) { cam->enc_update_eba = prp_enc_update_eba; cam->enc_enable = prp_enc_enable; cam->enc_disable = prp_enc_disable; } else ret = -EIO; return ret;}/*! * @brief Uninstall encoding functions. * @param private Pointer to cam_data structure * @return Zero on success, others on failure */int prp_enc_deselect(void *private){ int ret = 0; cam_data *cam = (cam_data *) private; ret = prp_enc_disable(private); if (cam) { cam->enc_update_eba = NULL; cam->enc_enable = NULL; cam->enc_disable = NULL; } return ret;}/*! * @brief Allocate memory for overlay. * @param cam Pointer to cam_data structure * @return Zero on success, others on failure */static int prp_vf_mem_alloc(cam_data * cam){ int i; for (i = 0; i < 2; i++) { cam->vf_bufs_size[i] = cam->win.w.width * cam->win.w.height * 2; cam->vf_bufs_vaddr[i] = dma_alloc_coherent(0, cam->vf_bufs_size[i], &cam->vf_bufs[i], GFP_DMA | GFP_KERNEL); if (!cam->vf_bufs_vaddr[i]) { pr_debug("Failed to alloc memory for vf.\n"); prp_vf_mem_free(cam); return -1; } g_vaddr_vfbuf[i] = ioremap_cached(cam->vf_bufs[i], cam->vf_bufs_size[i]); if (!g_vaddr_vfbuf[i]) { pr_debug("Failed to ioremap_cached() for vf.\n"); prp_vf_mem_free(cam); return -1; } } return 0;}/*! * @brief Free memory for overlay. * @param cam Pointer to cam_data structure * @return Zero on success, others on failure */static void prp_vf_mem_free(cam_data * cam){ int i; for (i = 0; i < 2; i++) { if (cam->vf_bufs_vaddr[i]) { dma_free_coherent(0, cam->vf_bufs_size[i], cam->vf_bufs_vaddr[i], cam->vf_bufs[i]); } cam->vf_bufs[i] = 0; cam->vf_bufs_vaddr[i] = 0; cam->vf_bufs_size[i] = 0; if (g_vaddr_vfbuf[i]) { iounmap(g_vaddr_vfbuf[i]); g_vaddr_vfbuf[i] = 0; } }}/*! * @brief Allocate intermediate memory for overlay rotation/mirroring. * @param cam Pointer to cam_data structure * @return Zero on success, others on failure */static int prp_rot_mem_alloc(cam_data * cam){ int i; for (i = 0; i < 2; i++) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -