| /* |
| * drivers/media/video/isp/omap_previewer.c |
| * |
| * Wrapper for Preview module in TI's OMAP3430 ISP |
| * |
| * Copyright (C) 2008 Texas Instruments, Inc. |
| * |
| * Contributors: |
| * Leonides Martinez <leonides.martinez@ti.com> |
| * Sergio Aguirre <saaguirre@ti.com> |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include <linux/mutex.h> |
| #include <linux/cdev.h> |
| #include <linux/device.h> |
| #include <linux/delay.h> |
| #include <linux/fs.h> |
| #include <linux/mm.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/uaccess.h> |
| #include <linux/io.h> |
| #include <media/v4l2-dev.h> |
| #include <asm/cacheflush.h> |
| |
| #include "isp.h" |
| #include "ispmmu.h" |
| #include "ispreg.h" |
| #include "omap_previewer.h" |
| |
| #define OMAP_PREV_NAME "omap-previewer" |
| |
| #define BIT_SET(var, shift, mask, val) \ |
| do { \ |
| var = (var & ~(mask << shift)) \ |
| | (val << shift); \ |
| } while (0) |
| |
| /* |
| #define OMAP_ISP_PREVIEWER_DEBUG |
| */ |
| #undef OMAP_ISP_PREVIEWER_DEBUG |
| |
| #ifdef OMAP_ISP_PREVIEWER_DEBUG |
| #define DPRINTK_PREVIEWER(format, ...) \ |
| printk(KERN_DEBUG "PREV: " format, ## __VA_ARGS__) |
| #else |
| #define DPRINTK_PREVIEWER(format, ...) |
| #endif |
| |
| #define ISP_CTRL_SBL_SHARED_RPORTB (1 << 28) |
| #define ISP_CTRL_SBL_SHARED_RPORTA (1 << 27) |
| #define SBL_RD_RAM_EN 18 |
| |
| static u32 isp_ctrl; |
| static u32 prv_wsdr_addr; |
| static int prev_major = -1; |
| static struct device *prev_dev; |
| static struct class *prev_class; |
| static struct prev_device *prevdevice; |
| static struct platform_driver omap_previewer_driver; |
| |
| static u32 prev_bufsize; |
| static u32 lsc_bufsize; |
| static u32 ytable[ISPPRV_YENH_TBL_SIZE]; |
| |
| /** |
| * prev_calculate_crop - Calculate crop size according to device parameters |
| * @device: Structure containing ISP preview wrapper global information |
| * @crop: Structure containing crop size |
| * |
| * This function is used to calculate frame size reduction depending on |
| * the features enabled by the application. |
| **/ |
| static int prev_calculate_crop(struct prev_device *device, |
| struct prev_cropsize *crop) |
| { |
| int ret; |
| |
| dev_dbg(prev_dev, "prev_calculate_crop E\n"); |
| |
| if (!device || !crop) { |
| dev_err(prev_dev, "\nErron in argument"); |
| return -EINVAL; |
| } |
| |
| ret = isppreview_try_size(device->params->size_params.hsize, |
| device->params->size_params.vsize, |
| &crop->hcrop, &crop->vcrop); |
| |
| crop->hcrop &= PREV_16PIX_ALIGN_MASK; |
| |
| dev_dbg(prev_dev, "prev_calculate_crop L\n"); |
| |
| return ret; |
| } |
| |
| /** |
| * prev_get_status - Get status of ISP preview module |
| * @status: Structure containing the busy state. |
| * |
| * Checks if the ISP preview module is busy. |
| * |
| * Returns 0 if successful, or -EINVAL if the status parameter is invalid. |
| **/ |
| static int prev_get_status(struct prev_status *status) |
| { |
| if (!status) { |
| dev_err(prev_dev, "get_status: invalid parameter\n"); |
| return -EINVAL; |
| } |
| status->hw_busy = (char)isppreview_busy(); |
| return 0; |
| } |
| |
| /** |
| * prev_hw_setup - Stores the desired configuration in the proper HW registers |
| * @config: Structure containing the desired configuration for ISP preview |
| * module. |
| * |
| * Reads the structure sent, and modifies the desired registers. |
| * |
| * Always returns 0. |
| **/ |
| static int prev_hw_setup(struct prev_params *config) |
| { |
| dev_dbg(prev_dev, "prev_hw_setup E\n"); |
| |
| if (config->features & PREV_AVERAGER) |
| isppreview_config_averager(config->average); |
| else |
| isppreview_config_averager(0); |
| |
| if (config->features & PREV_INVERSE_ALAW) |
| isppreview_enable_invalaw(1); |
| else |
| isppreview_enable_invalaw(0); |
| |
| if (config->features & PREV_HORZ_MEDIAN_FILTER) { |
| isppreview_config_hmed(config->hmf_params); |
| isppreview_enable_hmed(1); |
| } else |
| isppreview_enable_hmed(0); |
| |
| if (config->features & PREV_DARK_FRAME_SUBTRACT) { |
| DPRINTK_PREVIEWER("[%s] darkaddr %08x, darklineoffset %d\n", |
| __func__, |
| config->drkf_params.addr, |
| config->drkf_params.offset); |
| isppreview_set_darkaddr(config->drkf_params.addr); |
| isppreview_config_darklineoffset(config->drkf_params.offset); |
| isppreview_enable_drkframe(1); |
| } else |
| isppreview_enable_drkframe(0); |
| |
| if (config->features & PREV_LENS_SHADING) { |
| isppreview_config_drkf_shadcomp(config->lens_shading_shift); |
| isppreview_enable_shadcomp(1); |
| } else |
| isppreview_enable_shadcomp(0); |
| |
| if (config->ytable) |
| isppreview_set_luma_enhancement(config->ytable); |
| |
| dev_dbg(prev_dev, "prev_hw_setup L\n"); |
| return 0; |
| } |
| |
| /** |
| * prev_validate_params - Validate configuration parameters for Preview Wrapper |
| * @params: Structure containing configuration parameters |
| * |
| * Validate configuration parameters for Preview Wrapper |
| * |
| * Returns 0 if successful, or -EINVAL if a parameter value is invalid. |
| **/ |
| static int prev_validate_params(struct prev_params *params) |
| { |
| if (!params) { |
| dev_err(prev_dev, "validate_params: error in argument"); |
| goto err_einval; |
| } |
| |
| if ((params->features & PREV_AVERAGER) == PREV_AVERAGER) { |
| if ((params->average != NO_AVE) |
| && (params->average != AVE_2_PIX) |
| && (params->average != AVE_4_PIX) |
| && (params->average != AVE_8_PIX)) { |
| dev_err(prev_dev, "validate_params: wrong pix " |
| "average\n"); |
| goto err_einval; |
| } else if (((params->average == AVE_2_PIX) |
| && (params->size_params.hsize % 2)) |
| || ((params->average == AVE_4_PIX) |
| && (params->size_params.hsize % 4)) |
| || ((params->average == AVE_8_PIX) |
| && (params->size_params.hsize % 8))) { |
| dev_err(prev_dev, "validate_params: " |
| "wrong pix average for input size\n"); |
| goto err_einval; |
| } |
| } |
| |
| if ((params->size_params.pixsize != PREV_INWIDTH_8BIT) |
| && (params->size_params.pixsize |
| != PREV_INWIDTH_10BIT)) { |
| dev_err(prev_dev, "validate_params: wrong pixsize\n"); |
| goto err_einval; |
| } |
| |
| if (params->size_params.hsize > MAX_IMAGE_WIDTH |
| || params->size_params.hsize < 0) { |
| dev_err(prev_dev, "validate_params: wrong hsize\n"); |
| goto err_einval; |
| } |
| |
| if (params->size_params.hsize % 32) { |
| dev_err(prev_dev, "validate_params: width must be multiple of" |
| " 64 bytes\n"); |
| goto err_einval; |
| } |
| |
| if ((params->pix_fmt != YCPOS_YCrYCb) |
| && (YCPOS_YCbYCr != params->pix_fmt) |
| && (YCPOS_CbYCrY != params->pix_fmt) |
| && (YCPOS_CrYCbY != params->pix_fmt)) { |
| dev_err(prev_dev, "validate_params: wrong pix_fmt"); |
| goto err_einval; |
| } |
| |
| if ((params->features & PREV_DARK_FRAME_SUBTRACT) |
| && (params->features |
| & PREV_DARK_FRAME_CAPTURE)) { |
| dev_err(prev_dev, "validate_params: DARK FRAME CAPTURE and " |
| "SUBSTRACT cannot be enabled " |
| "at same time\n"); |
| goto err_einval; |
| } |
| |
| if ((params->size_params.in_pitch <= 0) |
| || (params->size_params.in_pitch % 32)) { |
| params->size_params.in_pitch = |
| (params->size_params.hsize * 2) & 0xFFE0; |
| dev_err(prev_dev, "Error in in_pitch; new value = %d\n", |
| params->size_params.in_pitch); |
| } |
| |
| return 0; |
| err_einval: |
| return -EINVAL; |
| } |
| |
| /** |
| * preview_isr - Callback from ISP driver for ISP Preview Interrupt |
| * @status: ISP IRQ0STATUS register value |
| * @arg1: Structure containing ISP preview wrapper global information |
| * @arg2: Currently not used |
| **/ |
| static void prev_isr(unsigned long status, isp_vbq_callback_ptr arg1, |
| void *arg2) |
| { |
| struct prev_device *device = (struct prev_device *)arg1; |
| |
| if ((status & PREV_DONE) != PREV_DONE) |
| return; |
| |
| if (device) |
| complete(&device->wfc); |
| } |
| |
| /* |
| * Set shared ports for using dark frame (lens shading) |
| */ |
| static void prev_set_isp_ctrl(u16 mode) |
| { |
| u32 val; |
| |
| val = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); |
| |
| isp_ctrl = val; |
| |
| /* Read port used by preview module data read */ |
| val &= ~ISP_CTRL_SBL_SHARED_RPORTA; |
| |
| /* Read port used by preview module dark frame read */ |
| if (mode & (PREV_DARK_FRAME_SUBTRACT | PREV_LENS_SHADING)) |
| val &= ~ISP_CTRL_SBL_SHARED_RPORTB; |
| |
| BIT_SET(val, SBL_RD_RAM_EN, 0x1, 0x1); |
| |
| /* write ISP CTRL register */ |
| isp_reg_writel(val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); |
| |
| prv_wsdr_addr = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); |
| } |
| |
| /* |
| * Set old isp shared port configuration |
| */ |
| static void prev_unset_isp_ctrl(void) |
| { |
| u32 val; |
| |
| val = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); |
| |
| if (isp_ctrl & ISP_CTRL_SBL_SHARED_RPORTB) |
| val |= ISP_CTRL_SBL_SHARED_RPORTB; |
| |
| if (isp_ctrl & ISP_CTRL_SBL_SHARED_RPORTA) |
| val |= ISP_CTRL_SBL_SHARED_RPORTA; |
| |
| if (isp_ctrl & (1 << SBL_RD_RAM_EN)) |
| val &= ~(1 << SBL_RD_RAM_EN); |
| |
| /* write ISP CTRL register */ |
| isp_reg_writel(val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); |
| |
| /* disable dark frame and shading compensation */ |
| isppreview_enable_drkframe(0); |
| isppreview_enable_shadcomp(0); |
| |
| /* Set output and input adresses to 0 */ |
| isppreview_set_outaddr(prv_wsdr_addr); |
| } |
| |
| /** |
| * prev_do_preview - Performs the Preview process |
| * @device: Structure containing ISP preview wrapper global information |
| * |
| * Returns 0 if successful, or -EINVAL if the sent parameters are invalid. |
| **/ |
| static int prev_do_preview(struct prev_device *device) |
| { |
| int bpp, size; |
| int ret = 0; |
| u32 out_hsize, out_vsize, out_line_offset; |
| |
| dev_dbg(prev_dev, "prev_do_preview E\n"); |
| |
| if (!device) { |
| dev_err(prev_dev, "preview: invalid parameters\n"); |
| return -EINVAL; |
| } |
| |
| prev_set_isp_ctrl(device->params->features); |
| isppreview_set_skip(2, 0); |
| |
| if (device->params->size_params.pixsize == PREV_INWIDTH_8BIT) |
| bpp = 1; |
| else |
| bpp = 2; |
| |
| size = device->params->size_params.hsize * |
| device->params->size_params.vsize * bpp; |
| |
| isppreview_config_datapath(PRV_RAW_MEM, PREVIEW_MEM); |
| |
| ret = isppreview_try_size(device->params->size_params.hsize, |
| device->params->size_params.vsize, |
| &out_hsize, &out_vsize); |
| |
| if (ret) { |
| dev_err(prev_dev, "ERROR while try size!\n"); |
| goto out; |
| } |
| |
| ret = isppreview_config_inlineoffset(device->params->size_params.hsize |
| * bpp); |
| if (ret) |
| goto out; |
| |
| out_line_offset = (out_hsize * bpp) & PREV_32BYTES_ALIGN_MASK; |
| |
| ret = isppreview_config_outlineoffset(out_line_offset); |
| if (ret) |
| goto out; |
| |
| ret = isppreview_config_size(device->params->size_params.hsize, |
| device->params->size_params.vsize, |
| out_hsize, out_vsize); |
| if (ret) |
| goto out; |
| |
| device->params->drkf_params.addr = device->isp_addr_lsc; |
| |
| prev_hw_setup(device->params); |
| |
| ret = isppreview_set_inaddr(device->isp_addr_read); |
| if (ret) |
| goto out; |
| |
| ret = isppreview_set_outaddr(device->isp_addr_read); |
| if (ret) |
| goto out; |
| |
| isppreview_config_datapath(PRV_RAW_MEM, PREVIEW_MEM); |
| |
| isppreview_try_size(device->params->size_params.hsize, |
| device->params->size_params.vsize, |
| &out_hsize, |
| &out_vsize); |
| |
| ret = isppreview_config_inlineoffset(device->params->size_params.hsize |
| * bpp); |
| if (ret) |
| goto out; |
| |
| out_line_offset = (out_hsize * bpp) & PREV_32BYTES_ALIGN_MASK; |
| |
| ret = isppreview_config_outlineoffset(out_line_offset); |
| if (ret) |
| goto out; |
| |
| ret = isppreview_config_size(device->params->size_params.hsize, |
| device->params->size_params.vsize, |
| out_hsize, out_vsize); |
| |
| ret = isp_set_callback(CBK_PREV_DONE, prev_isr, (void *) device, |
| (void *) NULL); |
| if (ret) { |
| dev_err(prev_dev, "ERROR while setting Previewer callback!\n"); |
| goto out; |
| } |
| |
| /* Make sure we don't wait for any HS_VS interrupts */ |
| isp_set_hs_vs(0); |
| |
| isppreview_enable(1); |
| |
| wait_for_completion_interruptible(&device->wfc); |
| |
| isppreview_enable(0); |
| |
| ret = isp_unset_callback(CBK_PREV_DONE); |
| |
| prev_unset_isp_ctrl(); |
| |
| dev_dbg(prev_dev, "prev_do_preview L\n"); |
| out: |
| return ret; |
| } |
| |
| /** |
| * previewer_vbq_release - Videobuffer queue release |
| * @q: Structure containing the videobuffer queue. |
| * @vb: Structure containing the videobuffer used for previewer processing. |
| **/ |
| static void previewer_vbq_release(struct videobuf_queue *q, |
| struct videobuf_buffer *vb) |
| { |
| struct prev_fh *fh = q->priv_data; |
| struct prev_device *device = fh->device; |
| |
| if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
| ispmmu_vunmap(device->isp_addr_read); |
| device->isp_addr_read = 0; |
| spin_lock(&device->inout_vbq_lock); |
| vb->state = VIDEOBUF_NEEDS_INIT; |
| spin_unlock(&device->inout_vbq_lock); |
| } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { |
| ispmmu_vunmap(device->isp_addr_lsc); |
| device->isp_addr_lsc = 0; |
| spin_lock(&device->lsc_vbq_lock); |
| vb->state = VIDEOBUF_NEEDS_INIT; |
| spin_unlock(&device->lsc_vbq_lock); |
| } |
| |
| if (vb->memory != V4L2_MEMORY_MMAP) { |
| videobuf_dma_unmap(q, videobuf_to_dma(vb)); |
| videobuf_dma_free(videobuf_to_dma(vb)); |
| } |
| |
| dev_dbg(prev_dev, "previewer_vbq_release\n"); |
| } |
| |
| /** |
| * previewer_vbq_setup - Sets up the videobuffer size and validates count. |
| * @q: Structure containing the videobuffer queue. |
| * @cnt: Number of buffers requested |
| * @size: Size in bytes of the buffer used for previewing |
| * |
| * Always returns 0. |
| **/ |
| static int previewer_vbq_setup(struct videobuf_queue *q, |
| unsigned int *cnt, |
| unsigned int *size) |
| { |
| struct prev_fh *fh = q->priv_data; |
| struct prev_device *device = fh->device; |
| u32 bpp = 1; |
| |
| if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
| spin_lock(&device->inout_vbq_lock); |
| |
| if (*cnt <= 0) |
| *cnt = 1; |
| |
| if (*cnt > VIDEO_MAX_FRAME) |
| *cnt = VIDEO_MAX_FRAME; |
| |
| if (!device->params->size_params.hsize || |
| !device->params->size_params.vsize) { |
| dev_err(prev_dev, "Can't setup inout buffer size\n"); |
| spin_unlock(&device->inout_vbq_lock); |
| return -EINVAL; |
| } |
| |
| if (device->params->size_params.pixsize == PREV_INWIDTH_10BIT) |
| bpp = 2; |
| *size = prev_bufsize = bpp * device->params->size_params.hsize * |
| device->params->size_params.vsize; |
| spin_unlock(&device->inout_vbq_lock); |
| } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { |
| spin_lock(&device->lsc_vbq_lock); |
| if (*cnt <= 0) |
| *cnt = 1; |
| |
| if (*cnt > 1) |
| *cnt = 1; |
| |
| if (!device->params->size_params.hsize || |
| !device->params->size_params.vsize) { |
| dev_err(prev_dev, "Can't setup lsc buffer size\n"); |
| spin_unlock(&device->lsc_vbq_lock); |
| return -EINVAL; |
| } |
| |
| /* upsampled lsc table size - for now bpp = 2 */ |
| bpp = 2; |
| *size = lsc_bufsize = bpp * device->params->size_params.hsize * |
| device->params->size_params.vsize; |
| |
| spin_unlock(&device->lsc_vbq_lock); |
| } else { |
| return -EINVAL; |
| } |
| |
| dev_dbg(prev_dev, "previewer_vbq_setup\n"); |
| return 0; |
| } |
| |
| /** |
| * previewer_vbq_prepare - Videobuffer is prepared and mmapped. |
| * @q: Structure containing the videobuffer queue. |
| * @vb: Structure containing the videobuffer used for previewer processing. |
| * @field: Type of field to set in videobuffer device. |
| * |
| * Returns 0 if successful, or -EINVAL if buffer couldn't get allocated, or |
| * -EIO if the ISP MMU mapping fails |
| **/ |
| static int previewer_vbq_prepare(struct videobuf_queue *q, |
| struct videobuf_buffer *vb, |
| enum v4l2_field field) |
| { |
| struct prev_fh *fh = q->priv_data; |
| struct prev_device *device = fh->device; |
| int err = -EINVAL; |
| unsigned int isp_addr; |
| struct videobuf_dmabuf *dma = videobuf_to_dma(vb); |
| |
| dev_dbg(prev_dev, "previewer_vbq_prepare E\n"); |
| |
| if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
| spin_lock(&device->inout_vbq_lock); |
| |
| if (vb->baddr) { |
| vb->size = prev_bufsize; |
| vb->bsize = prev_bufsize; |
| DPRINTK_PREVIEWER("[%s] bsize = %d\n", __func__, |
| vb->bsize); |
| } else { |
| spin_unlock(&device->inout_vbq_lock); |
| dev_err(prev_dev, "No user buffer allocated\n"); |
| goto out; |
| } |
| |
| vb->width = device->params->size_params.hsize; |
| vb->height = device->params->size_params.vsize; |
| vb->field = field; |
| spin_unlock(&device->inout_vbq_lock); |
| |
| if (vb->state == VIDEOBUF_NEEDS_INIT) { |
| DPRINTK_PREVIEWER("[%s] baddr = %08x\n", __func__, |
| (int)vb->baddr); |
| err = videobuf_iolock(q, vb, NULL); |
| if (!err) { |
| isp_addr = ispmmu_vmap(dma->sglist, |
| dma->sglen); |
| |
| if (!isp_addr) { |
| err = -EIO; |
| } else { |
| device->isp_addr_read = isp_addr; |
| DPRINTK_PREVIEWER("[%s] isp_addr_read =" |
| " %08x\n", __func__, isp_addr); |
| } |
| } |
| } |
| |
| if (!err) { |
| vb->state = VIDEOBUF_PREPARED; |
| flush_cache_user_range(NULL, vb->baddr, (vb->baddr + |
| vb->bsize)); |
| } else { |
| previewer_vbq_release(q, vb); |
| } |
| |
| } else if (q->type == V4L2_BUF_TYPE_PRIVATE) { |
| |
| spin_lock(&device->lsc_vbq_lock); |
| |
| if (vb->baddr) { |
| vb->size = lsc_bufsize; |
| vb->bsize = lsc_bufsize; |
| DPRINTK_PREVIEWER("[%s] bsize = %d\n", __func__, |
| vb->bsize); |
| } else { |
| spin_unlock(&device->lsc_vbq_lock); |
| dev_err(prev_dev, "No user buffer allocated\n"); |
| goto out; |
| } |
| |
| vb->width = device->params->size_params.hsize; |
| vb->height = device->params->size_params.vsize; |
| vb->field = field; |
| spin_unlock(&device->lsc_vbq_lock); |
| |
| if (vb->state == VIDEOBUF_NEEDS_INIT) { |
| DPRINTK_PREVIEWER("[%s] baddr = %08x\n", __func__, |
| (int)vb->baddr); |
| err = videobuf_iolock(q, vb, NULL); |
| if (!err) { |
| isp_addr = ispmmu_vmap(dma->sglist, |
| dma->sglen); |
| if (!isp_addr) { |
| err = -EIO; |
| } else { |
| device->isp_addr_lsc = isp_addr; |
| DPRINTK_PREVIEWER("[%s] isp_addr_lsc =" |
| " %08x\n", __func__, isp_addr); |
| } |
| } |
| } |
| |
| if (!err) { |
| vb->state = VIDEOBUF_PREPARED; |
| flush_cache_user_range(NULL, vb->baddr, (vb->baddr + |
| vb->bsize)); |
| } else { |
| previewer_vbq_release(q, vb); |
| } |
| |
| } else { |
| return -EINVAL; |
| } |
| |
| dev_dbg(prev_dev, "previewer_vbq_prepare L\n"); |
| out: |
| return err; |
| } |
| |
| static void previewer_vbq_queue(struct videobuf_queue *q, |
| struct videobuf_buffer *vb) |
| { |
| return; |
| } |
| |
| /** |
| * previewer_open - Initializes and opens the Preview Wrapper |
| * @inode: Inode structure associated with the Preview Wrapper |
| * @filp: File structure associated with the Preview Wrapper |
| * |
| * Returns 0 if successful, -EACCES if its unable to initialize default config, |
| * -EBUSY if its already opened or the ISP module is not available, or -ENOMEM |
| * if its unable to allocate the device in kernel space memory. |
| **/ |
| static int previewer_open(struct inode *inode, struct file *filp) |
| { |
| int ret = 0; |
| struct prev_device *device = prevdevice; |
| struct prev_params *config = isppreview_get_config(); |
| struct prev_fh *fh; |
| |
| if (config == NULL) { |
| dev_err(prev_dev, "Unable to initialize default config " |
| "from isppreviewer\n\n"); |
| return -EACCES; |
| } |
| |
| if (device->opened || (filp->f_flags & O_NONBLOCK)) { |
| dev_err(prev_dev, "previewer_open: device is already " |
| "opened\n"); |
| return -EBUSY; |
| } |
| |
| fh = kzalloc(sizeof(struct prev_fh), GFP_KERNEL); |
| if (NULL == fh) |
| return -ENOMEM; |
| |
| ret = isp_get(); |
| |
| if (ret < 0) { |
| kfree(fh); |
| printk(KERN_ERR "Can't enable ISP clocks (ret %d)\n", ret); |
| return -EACCES; |
| } |
| |
| ret = isppreview_request(); |
| |
| if (ret) { |
| kfree(fh); |
| isp_put(); |
| dev_err(prev_dev, "Can't acquire isppreview\n"); |
| return ret; |
| } |
| |
| device->params = config; |
| device->opened = 1; |
| |
| filp->private_data = fh; |
| fh->inout_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| fh->lsc_type = V4L2_BUF_TYPE_PRIVATE; |
| fh->device = device; |
| |
| videobuf_queue_sg_init(&fh->inout_vbq, &device->vbq_ops, NULL, |
| &device->inout_vbq_lock, fh->inout_type, |
| V4L2_FIELD_NONE, |
| sizeof(struct videobuf_buffer), fh); |
| |
| videobuf_queue_sg_init(&fh->lsc_vbq, &device->vbq_ops, NULL, |
| &device->lsc_vbq_lock, fh->lsc_type, |
| V4L2_FIELD_NONE, |
| sizeof(struct videobuf_buffer), fh); |
| |
| init_completion(&device->wfc); |
| device->wfc.done = 0; |
| mutex_init(&device->prevwrap_mutex); |
| |
| return 0; |
| } |
| |
| /** |
| * previewer_release - Releases Preview Wrapper and frees up allocated memory |
| * @inode: Inode structure associated with the Preview Wrapper |
| * @filp: File structure associated with the Preview Wrapper |
| * |
| * Always returns 0. |
| **/ |
| static int previewer_release(struct inode *inode, struct file *filp) |
| { |
| struct prev_fh *fh = filp->private_data; |
| struct prev_device *device = fh->device; |
| struct videobuf_queue *q1 = &fh->inout_vbq; |
| struct videobuf_queue *q2 = &fh->lsc_vbq; |
| |
| device->opened = 0; |
| device->params = NULL; |
| videobuf_mmap_free(q1); |
| videobuf_mmap_free(q2); |
| videobuf_queue_cancel(q1); |
| videobuf_queue_cancel(q2); |
| isppreview_free(); |
| isp_put(); |
| prev_bufsize = 0; |
| lsc_bufsize = 0; |
| filp->private_data = NULL; |
| kfree(fh); |
| |
| dev_dbg(prev_dev, "previewer_release\n"); |
| return 0; |
| } |
| |
| /** |
| * previewer_mmap - Memory maps the Preview Wrapper module. |
| * @file: File structure associated with the Preview Wrapper |
| * @vma: Virtual memory area structure. |
| * |
| * Returns 0 if successful, or returned value by the videobuf_mmap_mapper() |
| * function. |
| **/ |
| static int previewer_mmap(struct file *file, struct vm_area_struct *vma) |
| { |
| return -EINVAL; |
| } |
| |
| /** |
| * previewer_ioctl - I/O control function for Preview Wrapper |
| * @inode: Inode structure associated with the Preview Wrapper. |
| * @file: File structure associated with the Preview Wrapper. |
| * @cmd: Type of command to execute. |
| * @arg: Argument to send to requested command. |
| * |
| * Returns 0 if successful, -1 if bad command passed or access is denied, |
| * -EFAULT if copy_from_user() or copy_to_user() fails, -EINVAL if parameter |
| * validation fails or parameter structure is not present |
| **/ |
| static int previewer_ioctl(struct inode *inode, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| int ret = 0; |
| struct prev_params params; |
| struct prev_fh *fh = file->private_data; |
| struct prev_device *device = fh->device; |
| struct v4l2_buffer b; |
| struct v4l2_requestbuffers req; |
| |
| dev_dbg(prev_dev, "Entering previewer_ioctl()\n"); |
| |
| if ((_IOC_TYPE(cmd) != PREV_IOC_BASE) |
| || (_IOC_NR(cmd) > PREV_IOC_MAXNR)) { |
| dev_err(prev_dev, "Bad command Value \n"); |
| goto err_minusone; |
| } |
| |
| if (_IOC_DIR(cmd) & _IOC_READ) |
| ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); |
| else if (_IOC_DIR(cmd) & _IOC_WRITE) |
| ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); |
| if (ret) { |
| dev_err(prev_dev, "access denied\n"); |
| goto err_minusone; |
| } |
| |
| switch (cmd) { |
| case PREV_REQBUF: |
| if (copy_from_user(&req, (struct v4l2_requestbuffers *)arg, |
| sizeof(struct v4l2_requestbuffers))) |
| return -EFAULT; |
| |
| if (mutex_lock_interruptible(&device->prevwrap_mutex)) |
| goto err_eintr; |
| |
| if (req.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
| ret = videobuf_reqbufs(&fh->inout_vbq, &req); |
| else if (req.type == V4L2_BUF_TYPE_PRIVATE) |
| ret = videobuf_reqbufs(&fh->lsc_vbq, &req); |
| else |
| ret = -EINVAL; |
| |
| if (!ret && copy_to_user((struct v4l2_requestbuffers *)arg, |
| &req, sizeof(struct v4l2_requestbuffers))) |
| ret = -EFAULT; |
| |
| mutex_unlock(&device->prevwrap_mutex); |
| break; |
| |
| case PREV_QUERYBUF: |
| if (copy_from_user(&b, (struct v4l2_buffer *)arg, |
| sizeof(struct v4l2_buffer))) |
| return -EFAULT; |
| |
| if (mutex_lock_interruptible(&device->prevwrap_mutex)) |
| goto err_eintr; |
| |
| if (b.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
| ret = videobuf_querybuf(&fh->inout_vbq, &b); |
| else if (b.type == V4L2_BUF_TYPE_PRIVATE) |
| ret = videobuf_querybuf(&fh->lsc_vbq, &b); |
| else |
| ret = -EINVAL; |
| |
| if (!ret && copy_to_user((struct v4l2_buffer *)arg, &b, |
| sizeof(struct v4l2_buffer))) |
| ret = -EFAULT; |
| |
| mutex_unlock(&device->prevwrap_mutex); |
| break; |
| |
| case PREV_QUEUEBUF: |
| if (copy_from_user(&b, (struct v4l2_buffer *)arg, |
| sizeof(struct v4l2_buffer))) |
| return -EFAULT; |
| |
| if (mutex_lock_interruptible(&device->prevwrap_mutex)) |
| goto err_eintr; |
| |
| if (b.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) |
| ret = videobuf_qbuf(&fh->inout_vbq, &b); |
| else if (b.type == V4L2_BUF_TYPE_PRIVATE) |
| ret = videobuf_qbuf(&fh->lsc_vbq, &b); |
| else |
| ret = -EINVAL; |
| |
| mutex_unlock(&device->prevwrap_mutex); |
| break; |
| |
| case PREV_SET_PARAM: |
| if (mutex_lock_interruptible(&device->prevwrap_mutex)) |
| goto err_eintr; |
| |
| if (copy_from_user(¶ms, (struct prev_params *)arg, |
| sizeof(struct prev_params))) { |
| mutex_unlock(&device->prevwrap_mutex); |
| return -EFAULT; |
| } |
| |
| if (params.ytable && copy_from_user(ytable, params.ytable, |
| ISPPRV_YENH_TBL_SIZE*sizeof(u32))) { |
| mutex_unlock(&device->prevwrap_mutex); |
| return -EFAULT; |
| } |
| |
| ret = prev_validate_params(¶ms); |
| if (ret < 0) { |
| dev_err(prev_dev, "Error validating parameters!\n"); |
| mutex_unlock(&device->prevwrap_mutex); |
| goto out; |
| } |
| |
| if (device->params) { |
| memcpy(device->params, ¶ms, |
| sizeof(struct prev_params)); |
| |
| if (params.ytable) |
| device->params->ytable = ytable; |
| } else { |
| mutex_unlock(&device->prevwrap_mutex); |
| return -EINVAL; |
| } |
| |
| /* Update the values in Preview module now b/c otherwise when |
| * isppreview_try_size() is next called it won't update the |
| * output size correctly. |
| */ |
| ret = prev_hw_setup(device->params); |
| isppreview_config_datapath(PRV_RAW_MEM, PREVIEW_MEM); |
| |
| mutex_unlock(&device->prevwrap_mutex); |
| break; |
| |
| case PREV_GET_PARAM: |
| if (copy_to_user((struct prev_params *)arg, device->params, |
| sizeof(struct prev_params))) |
| ret = -EFAULT; |
| break; |
| |
| case PREV_GET_STATUS: |
| { |
| struct prev_status status; |
| |
| ret = prev_get_status(&status); |
| if (ret) |
| break; |
| |
| if (copy_to_user((struct prev_status *)arg, &status, |
| sizeof(struct prev_status))) |
| ret = -EFAULT; |
| break; |
| } |
| |
| case PREV_PREVIEW: |
| if (mutex_lock_interruptible(&device->prevwrap_mutex)) |
| goto err_eintr; |
| ret = prev_do_preview(device); |
| mutex_unlock(&device->prevwrap_mutex); |
| break; |
| |
| case PREV_GET_CROPSIZE: |
| { |
| struct prev_cropsize outputsize; |
| |
| ret = prev_calculate_crop(device, &outputsize); |
| if (ret) |
| break; |
| |
| if (copy_to_user((struct prev_cropsize *)arg, &outputsize, |
| sizeof(struct prev_cropsize))) |
| ret = -EFAULT; |
| break; |
| } |
| |
| default: |
| dev_err(prev_dev, "previewer_ioctl: Invalid Command Value\n"); |
| ret = -EINVAL; |
| } |
| out: |
| return ret; |
| err_minusone: |
| return -1; |
| err_eintr: |
| return -EINTR; |
| } |
| |
| /** |
| * previewer_platform_release - Acts when Reference count is zero |
| * @device: Structure containing ISP preview wrapper global information |
| * |
| * This is called when the reference count goes to zero |
| **/ |
| static void previewer_platform_release(struct device *device) |
| { |
| dev_dbg(prev_dev, "previewer_platform_release()\n"); |
| } |
| |
| static const struct file_operations prev_fops = { |
| .owner = THIS_MODULE, |
| .open = previewer_open, |
| .release = previewer_release, |
| .mmap = previewer_mmap, |
| .ioctl = previewer_ioctl, |
| }; |
| |
| static struct platform_device omap_previewer_device = { |
| .name = OMAP_PREV_NAME, |
| .id = -1, |
| .dev = { |
| .release = previewer_platform_release, |
| } |
| }; |
| |
| /** |
| * previewer_probe - Checks for device presence |
| * @pdev: Structure containing details of the current device. |
| * |
| * Always returns 0 |
| **/ |
| static int previewer_probe(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| /** |
| * previewer_remove - Handles the removal of the driver |
| * @pdev: Structure containing details of the current device. |
| * |
| * Always returns 0. |
| **/ |
| static int previewer_remove(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| static struct platform_driver omap_previewer_driver = { |
| .probe = previewer_probe, |
| .remove = previewer_remove, |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = OMAP_PREV_NAME, |
| }, |
| }; |
| |
| /** |
| * omap_previewer_init - Initialization of Preview Wrapper |
| * |
| * Returns 0 if successful, -ENOMEM if could not allocate memory, -ENODEV if |
| * could not register the wrapper as a character device, or other errors if the |
| * device or driver can't register. |
| **/ |
| static int __init omap_previewer_init(void) |
| { |
| int ret; |
| struct prev_device *device; |
| |
| device = kzalloc(sizeof(struct prev_device), GFP_KERNEL); |
| if (!device) { |
| dev_err(prev_dev, OMAP_PREV_NAME ": could not allocate" |
| " memory\n"); |
| return -ENOMEM; |
| } |
| prev_major = register_chrdev(0, OMAP_PREV_NAME, &prev_fops); |
| |
| if (prev_major < 0) { |
| dev_err(prev_dev, OMAP_PREV_NAME ": initialization " |
| "failed. could not register character " |
| "device\n"); |
| return -ENODEV; |
| } |
| |
| ret = platform_driver_register(&omap_previewer_driver); |
| if (ret) { |
| dev_err(prev_dev, OMAP_PREV_NAME |
| ": failed to register platform driver!\n"); |
| goto fail2; |
| } |
| ret = platform_device_register(&omap_previewer_device); |
| if (ret) { |
| dev_err(prev_dev, OMAP_PREV_NAME |
| ": failed to register platform device!\n"); |
| goto fail3; |
| } |
| |
| prev_class = class_create(THIS_MODULE, OMAP_PREV_NAME); |
| if (!prev_class) |
| goto fail4; |
| |
| prev_dev = device_create(prev_class, prev_dev, |
| MKDEV(prev_major, 0), NULL, |
| OMAP_PREV_NAME); |
| dev_dbg(prev_dev, OMAP_PREV_NAME ": Registered Previewer Wrapper\n"); |
| device->opened = 0; |
| |
| device->vbq_ops.buf_setup = previewer_vbq_setup; |
| device->vbq_ops.buf_prepare = previewer_vbq_prepare; |
| device->vbq_ops.buf_release = previewer_vbq_release; |
| device->vbq_ops.buf_queue = previewer_vbq_queue; |
| spin_lock_init(&device->inout_vbq_lock); |
| spin_lock_init(&device->lsc_vbq_lock); |
| prevdevice = device; |
| return 0; |
| |
| fail4: |
| platform_device_unregister(&omap_previewer_device); |
| fail3: |
| platform_driver_unregister(&omap_previewer_driver); |
| fail2: |
| unregister_chrdev(prev_major, OMAP_PREV_NAME); |
| |
| return ret; |
| } |
| |
| /** |
| * omap_previewer_exit - Close of Preview Wrapper |
| **/ |
| static void __exit omap_previewer_exit(void) |
| { |
| device_destroy(prev_class, MKDEV(prev_major, 0)); |
| class_destroy(prev_class); |
| platform_device_unregister(&omap_previewer_device); |
| platform_driver_unregister(&omap_previewer_driver); |
| unregister_chrdev(prev_major, OMAP_PREV_NAME); |
| |
| kfree(prevdevice); |
| prev_major = -1; |
| } |
| |
| module_init(omap_previewer_init); |
| module_exit(omap_previewer_exit); |
| |
| MODULE_AUTHOR("Texas Instruments"); |
| MODULE_DESCRIPTION("OMAP ISP Previewer"); |
| MODULE_LICENSE("GPL"); |