From patchwork Mon Aug 3 17:42:56 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: s-paulraj@ti.com X-Patchwork-Id: 38940 Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n73Hj3Cd001560 for ; Mon, 3 Aug 2009 17:45:04 GMT Received: from dlep36.itg.ti.com ([157.170.170.91]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id n73Hh0FR017752; Mon, 3 Aug 2009 12:43:05 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id n73HgxBg012551; Mon, 3 Aug 2009 12:42:59 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id C2DCF80627; Mon, 3 Aug 2009 12:42:59 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dlep34.itg.ti.com (dlep34.itg.ti.com [157.170.170.115]) by linux.omap.com (Postfix) with ESMTP id 3C97E80626 for ; Mon, 3 Aug 2009 12:42:58 -0500 (CDT) Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep34.itg.ti.com (8.13.7/8.13.7) with ESMTP id n73HgvoF017889; Mon, 3 Aug 2009 12:42:57 -0500 (CDT) Received: from gt5d9d821.telogy.design.ti.com (gt5d9d821.telogy.design.ti.com [158.218.100.23]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id n73HgvZ12281; Mon, 3 Aug 2009 12:42:57 -0500 (CDT) Received: from gt5d9d821.telogy.design.ti.com (localhost.localdomain [127.0.0.1]) by gt5d9d821.telogy.design.ti.com (8.13.1/8.13.1) with ESMTP id n73HgvUi009117; Mon, 3 Aug 2009 13:42:57 -0400 Received: (from a0866907@localhost) by gt5d9d821.telogy.design.ti.com (8.13.1/8.13.1/Submit) id n73HguSn009114; Mon, 3 Aug 2009 13:42:56 -0400 From: s-paulraj@ti.com To: davinci-linux-open-source@linux.davincidsp.com Date: Mon, 3 Aug 2009 13:42:56 -0400 Message-Id: <1249321376-9091-1-git-send-email-s-paulraj@ti.com> X-Mailer: git-send-email 1.6.0.4 Cc: Subject: [PATCH] DaVinci: DM355 AEW driver X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com From: Sandeep Paulraj The patch adds support for the DM355 AEW driver Signed-off-by: Sandeep Paulraj --- This patch is dependent on the VPFE patches aubmitted by Murali Karicheri. Those patches are not yet in the Davinci GIT so this patch will not compile These patches can be found at http://arago-project.org/git/people/?p=sneha/linux-davinci-staging.git;a=summary Test applications for AEW driver can be found at http://arago-project.org/git/people/?p=murali/linux-davinci-examples.git;a=summary This AEW driver patch is not meant for upstream submission drivers/media/video/Kconfig | 8 + drivers/media/video/davinci/Makefile | 3 +- drivers/media/video/davinci/dm355_aew.c | 847 ++++++++++++++++++++++++++++ drivers/media/video/davinci/dm355_aew_hw.c | 139 +++++ include/media/davinci/dm355_aew.h | 153 +++++ include/media/davinci/dm355_aew_hw.h | 120 ++++ 6 files changed, 1269 insertions(+), 1 deletions(-) create mode 100644 drivers/media/video/davinci/dm355_aew.c create mode 100644 drivers/media/video/davinci/dm355_aew_hw.c create mode 100644 include/media/davinci/dm355_aew.h create mode 100644 include/media/davinci/dm355_aew_hw.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e772b52..44d61b2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -514,6 +514,14 @@ config VIDEO_DM355_AF control loop for Auto Focus. It collects metrics about the image or video data +config VIDEO_DM355_AEW + tristate "DM355 Auto exposure /White Balance Driver" + depends on ARCH_DAVINCI_DM355 + help + DM355 Auto Exposure and Auto White Balance driver is used to support + the control loops for Auto Exposure and Auto White Balance. It collects + metrics about the image or video data + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index 17b3ab9..f859345 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -1,3 +1,4 @@ dm355_af_driver-objs += dm355_af.o dm355_af_hw.o obj-$(CONFIG_VIDEO_DM355_AF) += dm355_af_driver.o - +dm355_aew_driver-objs += dm355_aew.o dm355_aew_hw.o +obj-$(CONFIG_VIDEO_DM355_AEW) += dm355_aew_driver.o diff --git a/drivers/media/video/davinci/dm355_aew.c b/drivers/media/video/davinci/dm355_aew.c new file mode 100644 index 0000000..c9d3890 --- /dev/null +++ b/drivers/media/video/davinci/dm355_aew.c @@ -0,0 +1,847 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct class *aew_class; +struct aew_device *aew_dev_configptr; +struct device *aewdev; +static dev_t dev; +static struct cdev c_dev; + +int aew_validate_parameters(void) +{ + int result = 0; + + /* Check horizontal Count */ + if ((aew_dev_configptr->config->window_config.hz_cnt < + AEW_WINDOW_HORIZONTAL_COUNT_MIN) || + (aew_dev_configptr->config->window_config.hz_cnt > + AEW_WINDOW_HORIZONTAL_COUNT_MAX)) { + dev_err(aewdev, "\n Horizontal Count" + " is incorrect"); + result = -EINVAL; + } + + /* Check Vertical Count */ + if ((aew_dev_configptr->config->window_config.vt_cnt < + AEW_WINDOW_VERTICAL_COUNT_MIN) || + (aew_dev_configptr->config->window_config.vt_cnt > + AEW_WINDOW_VERTICAL_COUNT_MAX)) { + dev_err(aewdev, "\n Vertical Count" + " is incorrect"); + result = -EINVAL; + } + + /* Check line increment */ + if ((AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->window_config. + hz_line_incr)) || + (aew_dev_configptr->config->window_config.hz_line_incr < + AEW_HZ_LINEINCR_MIN) || + (aew_dev_configptr->config->window_config.hz_line_incr > + AEW_HZ_LINEINCR_MAX)) { + dev_err(aewdev, "\n Invalid Parameters"); + dev_err(aewdev, "\n Horizontal Line" + " Increment is incorrect"); + result = -EINVAL; + } + + /* Check line increment */ + if ((AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->window_config. + vt_line_incr)) || + (aew_dev_configptr->config->window_config.vt_line_incr < + AEW_VT_LINEINCR_MIN) || + (aew_dev_configptr->config->window_config.vt_line_incr > + AEW_VT_LINEINCR_MAX)) { + dev_err(aewdev, "\n Invalid Parameters"); + dev_err(aewdev, "\n Vertical Line" + " Increment is incorrect"); + result = -EINVAL; + } + + /* Check width */ + if ((AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->window_config. + width)) || + (aew_dev_configptr->config->window_config.width < + AEW_WIDTH_MIN) || + (aew_dev_configptr->config->window_config.width > + AEW_WIDTH_MAX)) { + dev_err(aewdev, "\n Width is incorrect"); + + result = -EINVAL; + } + + /* Check Height */ + if ((AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->window_config. + height)) || (aew_dev_configptr->config->window_config.height < + AEW_HEIGHT_MIN) || + (aew_dev_configptr->config->window_config.height > + AEW_HEIGHT_MAX)) { + dev_err(aewdev, "\n height incorrect"); + result = -EINVAL; + } + + /* Check Horizontal Start */ + if ((aew_dev_configptr->config->window_config.hz_start < + AEW_HZSTART_MIN) || + (aew_dev_configptr->config->window_config.hz_start > + AEW_HZSTART_MAX)) { + dev_err(aewdev, "\n horizontal start" + " is incorrect"); + result = -EINVAL; + } + + if ((aew_dev_configptr->config->window_config.vt_start > + AEW_VTSTART_MAX)) { + dev_err(aewdev, "\n Vertical start is incorrect"); + result = -EINVAL; + } + + if ((aew_dev_configptr->config->alaw_enable > H3A_AEW_ENABLE) || + (aew_dev_configptr->config->alaw_enable < H3A_AEW_DISABLE)) { + dev_err(aewdev, "\n A Law setting is incorrect"); + result = -EINVAL; + } + + if (aew_dev_configptr->config->saturation_limit > AEW_AVELMT_MAX) { + dev_err(aewdev, "\n Saturation Limit is incorrect"); + result = -EINVAL; + } + + /* Check Black Window Height */ + if (AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->blackwindow_config. + height) || + (aew_dev_configptr->config->blackwindow_config.height < + AEW_BLKWINHEIGHT_MIN) || + (aew_dev_configptr->config->blackwindow_config.height > + AEW_BLKWINHEIGHT_MAX)) { + dev_err(aewdev, "\n Black Window height incorrect"); + result = -EINVAL; + } + + /* Check Black Window Height */ + if ((AEW_NOT_EVEN == + AEW_CHECK_EVEN(aew_dev_configptr->config->blackwindow_config. + height)) || + (aew_dev_configptr->config->blackwindow_config.vt_start < + AEW_BLKWINVTSTART_MIN) || + (aew_dev_configptr->config->blackwindow_config.vt_start > + AEW_BLKWINVTSTART_MAX)) { + dev_err(aewdev, "\n Black Window vertical" + " Start is incorrect"); + result = -EINVAL; + } + + if (((aew_dev_configptr->config->window_config.vt_cnt) * + (aew_dev_configptr->config->window_config.height) + + (aew_dev_configptr->config->window_config.vt_start)) > 156) { + dev_err(aewdev, "\n Only 156 Lines are" + " supported for CCDC mode"); + dev_err(aewdev, "\n Vertical count * Height +" + " vertical Start should not exceed 156"); + result = -EINVAL; + } + + return result; +} + +/* inline function to free reserver pages */ +inline void aew_free_pages(unsigned long addr, unsigned long bufsize) +{ + unsigned long tempaddr; + unsigned long size; + + tempaddr = addr; + if (!addr) + return; + + size = PAGE_SIZE << (get_order(bufsize)); + while (size > 0) { + ClearPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + free_pages(tempaddr, get_order(bufsize)); +} + +/* Function to perform hardware Configuration */ +int aew_hardware_setup(void) +{ + int result; + int buff_size = 0; + unsigned long adr; + unsigned long size; + unsigned int busyaew; + + /* Get the value of PCR register */ + busyaew = AEW_GET_PCR; + + /* Mask with BUSYAF bit */ + busyaew = busyaew & AEW_BUSYAEWB; + + /* Shift it 18 times to get value of 1 or 0 */ + busyaew = busyaew >> AEW_BUSYAEWB_SHIFT; + + /* If H3A Engine is busy then return */ + if (busyaew == 1) { + dev_err(aewdev, "\n Error : AEW Engine is busy"); + return -EBUSY; + } + + result = aew_validate_parameters(); + + dev_dbg(aewdev, "Result = %d\n", result); + if (result < 0) { + dev_err(aewdev, "Error : Parameters are incorrect \n"); + return result; + } + + /* Deallocate the previously allocated buffers */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr->buff_old, + aew_dev_configptr->size_window); + + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, aew_dev_configptr->size_window); + + if (aew_dev_configptr->buff_app) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_app, aew_dev_configptr->size_window); + + /* + * Allocat the buffers as per the new buffer size + * Allocate memory for old buffer + */ + buff_size = (aew_dev_configptr->config->window_config.hz_cnt) * + (aew_dev_configptr->config->window_config.vt_cnt) * + AEW_WINDOW_SIZE; + + aew_dev_configptr->buff_old = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_old == NULL) + return -ENOMEM; + + /* Make pages reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_old; + size = PAGE_SIZE << (get_order(buff_size)); + + while (size > 0) { + /* + * Make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /*Allocate memory for current buffer */ + aew_dev_configptr->buff_curr = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_curr == NULL) { + /* Free all buffer that are allocated */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_old, buff_size); + return -ENOMEM; + } + + /* Make pages reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_curr; + size = PAGE_SIZE << (get_order(buff_size)); + while (size > 0) { + /* + * Make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* Allocate memory for application buffer */ + aew_dev_configptr->buff_app = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_app == NULL) { + /* Free all buffer that were allocated previously */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_old, buff_size); + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, buff_size); + return -ENOMEM; + } + + /* Make pages reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_app; + size = PAGE_SIZE << (get_order(buff_size)); + while (size > 0) { + /* + * Make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* Set the registers */ + aew_register_setup(aew_dev_configptr); + aew_dev_configptr->size_window = buff_size; + aew_dev_configptr->aew_config = H3A_AEW_CONFIG; + + return 0; +} + +static int aew_open(struct inode *inode, struct file *filp) +{ + /* Return if Device is in use (Single Channel Support is provided) */ + if (aew_dev_configptr->in_use == AEW_IN_USE) + return -EBUSY; + + /* Set the aew_dev_configptr structure */ + aew_dev_configptr->config = NULL; + + /* Allocate memory for configuration structure of this channel */ + aew_dev_configptr->config = (struct aew_configuration *) + kmalloc(sizeof(struct aew_configuration), GFP_KERNEL); + + if (aew_dev_configptr->config == NULL) { + dev_err(aewdev, "Error : Kmalloc fail\n"); + return -ENOMEM; + } + + /* Initiaze the wait queue */ + init_waitqueue_head(&(aew_dev_configptr->aew_wait_queue)); + + /* Device is in use */ + aew_dev_configptr->in_use = AEW_IN_USE; + + /* No Hardware Set up done */ + aew_dev_configptr->aew_config = H3A_AEW_CONFIG_NOT_DONE; + + /* No statistics are available */ + aew_dev_configptr->buffer_filled = 0; + + /* Set Window Size to 0 */ + aew_dev_configptr->size_window = 0; + + /* Initialize the mutex */ + mutex_init(&(aew_dev_configptr->read_blocked)); + + return 0; +} + +static void aew_platform_release(struct device *device) +{ + /* This is called when the reference count goes to zero */ +} + +static int aew_probe(struct device *device) +{ + aewdev = device; + return 0; +} + +static int aew_remove(struct device *device) +{ + return 0; +} + +/* This Function is called when driver is closed */ +static int aew_release(struct inode *inode, struct file *filp) +{ + aew_engine_setup(0); + + /* The Application has closed device so device is not in use */ + aew_dev_configptr->in_use = AEW_NOT_IN_USE; + + /* Release memory for configuration structure of this channel */ + kfree(aew_dev_configptr->config); + + /* Free Old Buffer */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr->buff_old, + aew_dev_configptr->size_window); + + /* Free Current Buffer */ + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, aew_dev_configptr->size_window); + + /* Free Application Buffer */ + if (aew_dev_configptr->buff_app) + aew_free_pages((unsigned long)aew_dev_configptr->buff_app, + aew_dev_configptr->size_window); + + aew_dev_configptr->buff_old = NULL; + aew_dev_configptr->buff_curr = NULL; + aew_dev_configptr->config = NULL; + aew_dev_configptr->buff_app = NULL; + + return 0; +} + +/* + * This function will process IOCTL commands sent by the application and + * control the devices IO operations. + */ +static int aew_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + /* Stores Previous Configurations */ + struct aew_configuration aewconfig = *(aew_dev_configptr->config); + int result = 0; + + /* Decrement the mutex */ + result = mutex_lock_interruptible(&aew_dev_configptr->read_blocked); + if (result) + return result; + + /* + * Extract the type and number bitfields and + * don't decode wrong cmds + * verify the magic number + */ + if (_IOC_TYPE(cmd) != AEW_MAGIC_NO) { + mutex_unlock(&aew_dev_configptr->read_blocked); + return -ENOTTY; + } + + /* verify the command number */ + if (_IOC_NR(cmd) > AEW_IOC_MAXNR) { + /* Release mutex in case of fault */ + mutex_unlock(&aew_dev_configptr->read_blocked); + return -ENOTTY; + } + + /* check for the permission of the operation */ + if (_IOC_DIR(cmd) & _IOC_READ) + result = !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + result = !access_ok(VERIFY_READ, (void __user *)arg, + _IOC_SIZE(cmd)); + + if (result) { + /* Release mutex in case of fault */ + mutex_unlock(&aew_dev_configptr->read_blocked); + return -EFAULT; + } + + /* Switch according to IOCTL command */ + switch (cmd) { + /* + * This ioctl is used to perform hardware set up + * and will set all the regiseters for AF engine + */ + case AEW_S_PARAM: + + /* Copy config structure passed by user */ + if (copy_from_user(aew_dev_configptr->config, + (struct aew_configuration *)arg, + sizeof(struct aew_configuration))) { + *(aew_dev_configptr->config) = aewconfig; + mutex_unlock(&aew_dev_configptr->read_blocked); + return -EFAULT; + } + + /* Call aew_hardware_setup to perform register configuration */ + result = aew_hardware_setup(); + if (!result) { + /* + * Hardware Set up is successful + * Return the no of bytes required for buffer + */ + result = aew_dev_configptr->size_window; + } else { + /* Change Configuration Structure to original */ + *(aew_dev_configptr->config) = aewconfig; + dev_err(aewdev, "Error : AEW_S_PARAM failed\n"); + } + + break; + + /* This ioctl is used to return parameters in user space */ + case AEW_G_PARAM: + if (aew_dev_configptr->aew_config == H3A_AEW_CONFIG) { + if (copy_to_user + ((struct aew_configuration *)arg, + aew_dev_configptr->config, + sizeof(struct aew_configuration))) { + mutex_unlock(&aew_dev_configptr->read_blocked); + return -EFAULT; + } else + result = aew_dev_configptr->size_window; + } else { + dev_err(aewdev, + "Error : AEW Hardware is not configured.\n"); + result = -EINVAL; + } + break; + + /* This ioctl is used to enable AEW Engine */ + case AEW_ENABLE: + /*Enable AEW Engine if Hardware set up is done */ + if (aew_dev_configptr->aew_config == H3A_AEW_CONFIG_NOT_DONE) { + dev_err(aewdev, + "Error : AEW Hardware is not configured.\n"); + result = -EINVAL; + } else + /* Enable AF Engine */ + aew_engine_setup(1); + break; + + /* This ioctl is used to disable AEW Engine */ + case AEW_DISABLE: + /* Disable AEW Engine */ + aew_engine_setup(0); + break; + + /* Invalid Command */ + default: + dev_err(aewdev, "Error: It should not come here!!\n"); + result = -ENOTTY; + break; + } + + /*Release the mutex */ + mutex_unlock(&aew_dev_configptr->read_blocked); + + return result; +} + +/* This function will return statistics to user */ +static ssize_t aew_read(struct file *filep, char *kbuff, + size_t size, loff_t *offset) +{ + void *buffer_temp; + int result = 0; + int ret; + + /* Mutex will return immediately if read call is busy */ + ret = mutex_lock_interruptible(&(aew_dev_configptr->read_blocked)); + if (ret != 0) { + dev_dbg(aewdev, "Read Call : busy : %d\n", ret); + return -EBUSY; + } + + /* First Check the size given by user */ + if (size < aew_dev_configptr->size_window) { + /* + * Return Failure to applicaiton + * if size is less than required size + */ + dev_dbg(aewdev, "Error : Invalid size of buffer\n"); + mutex_unlock(&(aew_dev_configptr->read_blocked)); + return -1; + } + + /* + * The value of buffer_filled flag determines + * the status of statistics + */ + if (aew_dev_configptr->buffer_filled == 0) { + /* Decrement the mutex */ + dev_dbg(aewdev, "READ CALL IS BLOCKED............\n"); + /* Block the read call */ + wait_event_interruptible_timeout(aew_dev_configptr-> + aew_wait_queue, + aew_dev_configptr-> + buffer_filled, AEW_TIMEOUT); + dev_dbg(aewdev, "Read Call is unbloked and waking up.....\n"); + dev_dbg(aewdev, "Buffer Filled.... %d\n", + aew_dev_configptr->buffer_filled); + } + + if (aew_dev_configptr->buffer_filled == 1) { + /* Disable the interrupts and then swap the buffers */ + dev_dbg(aewdev, "READING............\n"); + disable_irq(4); + + /* New Statistics are availaible */ + aew_dev_configptr->buffer_filled = 0; + + /* Swap application buffer and old buffer */ + buffer_temp = aew_dev_configptr->buff_old; + aew_dev_configptr->buff_old = aew_dev_configptr->buff_app; + aew_dev_configptr->buff_app = buffer_temp; + + /* Interrupts are enabled */ + enable_irq(4); + + /* + * Copy the entire statistics located in application + * buffer to user space + */ + if (copy_to_user(kbuff, aew_dev_configptr->buff_app, + aew_dev_configptr->size_window)) { + dev_err(aewdev, "Error : Read Fault\n"); + mutex_unlock(&(aew_dev_configptr->read_blocked)); + return -EFAULT; + } else + result = aew_dev_configptr->size_window; + + dev_dbg(aewdev, "Reading Done........................\n"); + } + + dev_dbg(aewdev, "APP BUFF VALUE %x\n", + (*((unsigned int *)(aew_dev_configptr->buff_app)))); + + /* Increment the mutex */ + mutex_unlock(&(aew_dev_configptr->read_blocked)); + + return result; +} + +/* This function will handle interrupt generated by H3A Engine */ +static irqreturn_t aew_isr(int irq, void *dev_id) +{ + /* Busy AF Bit */ + unsigned int busyaew; + + /* Temporary Buffer for Swapping */ + void *buffer_temp; + + /* Get the value of PCR register */ + busyaew = AEW_GET_PCR; + + /* If AEW engine is not enabled, interrupt is not for AEW */ + if (((busyaew & 0x10000) >> 16) == 0) + return IRQ_RETVAL(IRQ_NONE); + + /* + * Interrupt is generated by AEW, so Service the Interrupt + * Swap current buffer and old buffer + */ + if (aew_dev_configptr) { + buffer_temp = aew_dev_configptr->buff_curr; + aew_dev_configptr->buff_curr = aew_dev_configptr->buff_old; + aew_dev_configptr->buff_old = buffer_temp; + + /* Set the AEWBUFSTAT Register to current buffer Address */ + aew_set_address((unsigned + long)(virt_to_phys(aew_dev_configptr->buff_curr))); + + /* + * Set buffer filled flag to indicate + * statistics are available + */ + aew_dev_configptr->buffer_filled = 1; + + /* + * New statistics are available + * Wake up the read call + */ + wake_up(&(aew_dev_configptr->aew_wait_queue)); + + return IRQ_RETVAL(IRQ_HANDLED); + } + return IRQ_RETVAL(IRQ_NONE); +} + +/* File Operation Structure */ +static const struct file_operations aew_fops = { + .owner = THIS_MODULE, + .open = aew_open, + .read = aew_read, + .ioctl = aew_ioctl, + .release = aew_release, +}; + +static struct platform_device aewdevice = { + .name = "dm355_aew", + .id = 2, + .dev = { + .release = aew_platform_release, + } +}; + +static struct device_driver aew_driver = { + .name = "dm355_aew", + .bus = &platform_bus_type, + .probe = aew_probe, + .remove = aew_remove, +}; + +#define DRIVERNAME "DM355AEW" +/* Function to register the AF character device driver */ +int __init aew_init(void) +{ + int err; + int result = 0; + unsigned int vpssclk; + + /* + * Register the driver in the kernel + * dynmically get the major number for + * the driver using alloc_chrdev_region function + */ + result = alloc_chrdev_region(&dev, 0, 1, DRIVERNAME); + + if (result < 0) { + printk(KERN_ERR "Error : Could not register character device"); + return -ENODEV; + } + + printk(KERN_INFO "aew major#: %d, minor# %d\n", MAJOR(dev), MINOR(dev)); + + /* Allocate memory for device structure and initialize it with 0 */ + aew_dev_configptr = kmalloc(sizeof(struct aew_device), GFP_KERNEL); + if (!aew_dev_configptr) { + printk(KERN_ERR "Error : kmalloc fail"); + unregister_chrdev_region(dev, AEW_NR_DEVS); + return -ENOMEM; + } + + /* Initialize character device */ + cdev_init(&c_dev, &aew_fops); + c_dev.owner = THIS_MODULE; + c_dev.ops = &aew_fops; + + err = cdev_add(&c_dev, dev, 1); + if (err) { + printk(KERN_ERR "Error : Error in Adding Davinci AEW"); + unregister_chrdev_region(dev, AEW_NR_DEVS); + kfree(aew_dev_configptr); + return -err; + } + + /* Register driver as a platform driver */ + if (driver_register(&aew_driver) != 0) { + unregister_chrdev_region(dev, 1); + cdev_del(&c_dev); + return -EINVAL; + } + + /* Register the drive as a platform device */ + if (platform_device_register(&aewdevice) != 0) { + driver_unregister(&aew_driver); + unregister_chrdev_region(dev, 1); + cdev_del(&c_dev); + return -EINVAL; + } + + aew_class = class_create(THIS_MODULE, "dm355_aew"); + if (!aew_class) { + printk("aew_init: error in creating device class\n"); + driver_unregister(&aew_driver); + platform_device_unregister(&aewdevice); + unregister_chrdev_region(dev, 1); + unregister_chrdev(MAJOR(dev), DRIVERNAME); + cdev_del(&c_dev); + return -EINVAL; + } + + device_create(aew_class, NULL, dev, NULL, "dm355_aew"); + + /* AEW_SELINT(interrupt_no) */ + AEW_SETGAMMAWD; + vpssclk = AEW_GETCLKCTRL; + vpssclk |= (1 << 4); + AEW_SETCLKCTRL(vpssclk); + /* Set up the Interrupt handler for H3AINT interrupt */ + result = request_irq(4, aew_isr, IRQF_SHARED, "dm355h3a_aew", + (void *)aew_dev_configptr); + + if (result != 0) { + printk(KERN_ERR "Error : Request IRQ Failed"); + unregister_chrdev_region(dev, AEW_NR_DEVS); + device_destroy(aew_class, dev); + class_destroy(aew_class); + kfree(aew_dev_configptr); + driver_unregister(&aew_driver); + platform_device_unregister(&aewdevice); + cdev_del(&c_dev); + return result; + } + + /* Initialize device structure */ + memset(aew_dev_configptr, 0, sizeof(struct aew_device)); + + aew_dev_configptr->in_use = AEW_NOT_IN_USE; + aew_dev_configptr->buffer_filled = 0; + + return 0; +} + +/* + * This Function is called by the kernel while unloading the driver + * This will unregister the Character Device Driver + */ +void __exit aew_cleanup(void) +{ + /* Device is in use */ + if (aew_dev_configptr->in_use == AEW_IN_USE) { + printk("Error : Driver in use"); + return; + } + + free_irq(4, aew_dev_configptr); + + /* Free device structure */ + kfree(aew_dev_configptr); + + aew_dev_configptr = NULL; + unregister_chrdev_region(dev, AEW_NR_DEVS); + + driver_unregister(&aew_driver); + + device_destroy(aew_class, dev); + + class_destroy(aew_class); + + platform_device_unregister(&aewdevice); + + cdev_del(&c_dev); + + /* Unregistering the driver from the kernel */ + unregister_chrdev(MAJOR(dev), DEVICE_NAME); +} + +module_init(aew_init) +module_exit(aew_cleanup) +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/davinci/dm355_aew_hw.c b/drivers/media/video/davinci/dm355_aew_hw.c new file mode 100644 index 0000000..7208cf1 --- /dev/null +++ b/drivers/media/video/davinci/dm355_aew_hw.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +int aew_register_setup(struct aew_device *aew_dev) +{ + unsigned int pcr = 0, win1 = 0, winstart = 0, blkwin = 0, subwin = 0; + + /* Set up the registers */ + pcr = regr(AEWPCR); + + /* Enable A Law */ + if (aew_dev->config->alaw_enable == H3A_AEW_ENABLE) + pcr |= AEW_ALAW_EN; + else + pcr &= ~AEW_ALAW_EN; + + /*Configure Saturation limit */ + pcr &= ~AVE2LMT; + pcr |= aew_dev->config->saturation_limit << AEW_AVE2LMT_SHIFT; + + /* Set Input Source */ + pcr &= ~AEW_INP_SRC; + pcr |= (AEW_CCDC) << AEW_INP_SRC_SHIFT; + + regw(pcr, AEWPCR); + + /* Configure Window Width in AEWWIN1 register */ + win1 = 0; + win1 |= ((AEW_SET_VAL(aew_dev->config->window_config.height)) << + AEW_WINH_SHIFT); + + /* Configure Window height in AEWWIN1 register */ + win1 |= ((AEW_SET_VAL(aew_dev->config->window_config.width)) << + AEW_WINW_SHIFT); + + /* Configure Window vertical count in AEWWIN2 register */ + win1 |= ((aew_dev->config->window_config).vt_cnt - 1) << + AEW_VT_COUNT_SHIFT; + + /* Configure Window horizontal count in AEWWIN1 register */ + win1 |= ((aew_dev->config->window_config).hz_cnt - 1); + + /* Configure Window vertical start in AEWWIN1 register */ + regw(win1, AEWWIN1); + + /* Window Start parameter configuration */ + winstart &= ~WINSV; + winstart |= (aew_dev->config->window_config).vt_start << + AEW_VT_START_SHIFT; + + /* Configure Window horizontal start in AEWWIN2 register */ + winstart &= ~WINSH; + winstart |= (aew_dev->config->window_config).hz_start; + regw(winstart, AEWINSTART); + + /* + * Window Line Increment configuration + * Configure vertical line increment in AEWSUBWIN + */ + subwin &= ~AEWINCV; + subwin |= (AEW_SET_VAL(aew_dev->config->window_config. + vt_line_incr) << AEW_LINE_INCR_SHIFT); + + /* Configuring Horizontal Line increment in AEWSUBWIN */ + subwin &= ~AEWINCH; + subwin |= (AEW_SET_VAL(aew_dev->config->window_config.hz_line_incr)); + + regw(subwin, AEWSUBWIN); + + /* + * Black Window Configuration + * Configure vertical start and height in AEWWINBLK + */ + blkwin &= ~BLKWINSV; + blkwin |= (aew_dev->config->blackwindow_config). + vt_start << AEW_BLKWIN_VT_START_SHIFT; + + /* Configure height in Black window */ + blkwin &= ~BLKWINH; + blkwin |= (AEW_SET_VAL(aew_dev->config->blackwindow_config.height)); + regw(blkwin, AEWINBLK); + + /* Set AFBUFST to Current buffer Physical Address */ + regw((unsigned int)(virt_to_phys(aew_dev->buff_curr)), AEWBUFST); + dev_dbg(aewdev, "\n PCR is %x", regr(AEWPCR)); + dev_dbg(aewdev, "\n SUBWIN is %x", regr(AEWSUBWIN)); + dev_dbg(aewdev, "\n WINSTART is %x", regr(AEWINSTART)); + dev_dbg(aewdev, "\n WINBLK is %x", regr(AEWINBLK)); + dev_dbg(aewdev, "\n WIN1 is %x", regr(AEWWIN1)); + dev_dbg(aewdev, "\n AEWBUST %x", regr(AEWBUFST)); + + AEW_SETGAMMAWD; + + return 0; +} + +/* Function to enable/ disable AEW Engine */ +inline void aew_engine_setup(int value) +{ + unsigned int pcr; + + dev_dbg(aewdev, "\nAEW_REG(PCR) Before Setting %x", regr(AEWPCR)); + + /* Read Pcr Register */ + pcr = regr(AEWPCR); + pcr &= ~AEW_EN; + pcr |= (value << AEW_EN_SHIFT); + + /*Set AF_EN bit in PCR Register */ + regw(pcr, AEWPCR); + + dev_dbg(aewdev, "\nAfter Setting %d : PCR VALUE %x", value, + regr(AEWPCR)); +} + +/* Function used to set adddress */ +inline void aew_set_address(unsigned long address) +{ + regw(address, AEWBUFST); +} diff --git a/include/media/davinci/dm355_aew.h b/include/media/davinci/dm355_aew.h new file mode 100644 index 0000000..1c65285 --- /dev/null +++ b/include/media/davinci/dm355_aew.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DM355_AEW_DRIVER_H +#define DM355_AEW_DRIVER_H + +#include + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#endif + +/* Driver Range Constants*/ +#define AEW_WINDOW_VERTICAL_COUNT_MIN 1 +#define AEW_WINDOW_VERTICAL_COUNT_MAX 128 +#define AEW_WINDOW_HORIZONTAL_COUNT_MIN 2 +#define AEW_WINDOW_HORIZONTAL_COUNT_MAX 36 +#define AEW_WINDOW_SIZE 18 +#define AEW_WIDTH_MIN 8 +#define AEW_WIDTH_MAX 256 +#define AEW_AVELMT_MAX 1023 +#define AEW_HZ_LINEINCR_MIN 2 +#define AEW_HZ_LINEINCR_MAX 32 +#define AEW_VT_LINEINCR_MIN 2 +#define AEW_VT_LINEINCR_MAX 32 +#define AEW_HEIGHT_MIN 2 +#define AEW_HEIGHT_MAX 256 +#define AEW_HZSTART_MIN 0 +#define AEW_HZSTART_MAX 4095 +#define AEW_VTSTART_MIN 0 +#define AEW_VTSTART_MAX 4095 +#define AEW_BLKWINHEIGHT_MIN 2 +#define AEW_BLKWINHEIGHT_MAX 256 +#define AEW_BLKWINVTSTART_MIN 0 +#define AEW_BLKWINVTSTART_MAX 4095 + +#ifdef __KERNEL__ + +/* Device Constants*/ +#define AEW_NR_DEVS 1 +#define DEVICE_NAME "dm355_aew" +#define AEW_MAJOR_NUMBER 0 +#define AEW_IOC_MAXNR 4 +#define AEW_TIMEOUT (300 * HZ / 1000) +#endif + +/* List of ioctls */ +#pragma pack(1) +#define AEW_MAGIC_NO 'e' +#define AEW_S_PARAM _IOWR(AEW_MAGIC_NO , 1 , struct aew_configuration *) +#define AEW_G_PARAM _IOWR(AEW_MAGIC_NO , 2 , struct aew_configuration *) +#define AEW_ENABLE _IO(AEW_MAGIC_NO , 3) +#define AEW_DISABLE _IO(AEW_MAGIC_NO , 4) +#pragma pack() + +/*Enum for device usage*/ +enum aew_In_use { + AEW_NOT_IN_USE = 0, /* Device is not in use */ + AEW_IN_USE = 1 /* Device in use */ +}; + +/*Enum for Enable/Disable specific feature*/ +enum aew_alaw_enable { + H3A_AEW_ENABLE = 1, + H3A_AEW_DISABLE = 0 +}; + +enum aew_config_flag { + H3A_AEW_CONFIG_NOT_DONE, + H3A_AEW_CONFIG +}; + +/* Contains the information regarding Window Structure in AEW Engine */ +struct aew_window { + unsigned int width; /* Width of the window */ + unsigned int height; /* Height of the window */ + unsigned int hz_start; /* Horizontal Start of the window */ + unsigned int vt_start; /* Vertical Start of the window */ + unsigned int hz_cnt; /* Horizontal Count */ + unsigned int vt_cnt; /* Vertical Count */ + unsigned int hz_line_incr; /* Horizontal Line Increment */ + unsigned int vt_line_incr; /* Vertical Line Increment */ +}; + +/* Contains the information regarding the AEW Black Window Structure*/ +struct aew_black_window { + unsigned int height; /* Height of the Black Window */ + unsigned int vt_start; /* Vertical Start of the black Window */ +}; + +enum _aew_input_src { + AEW_CCDC = 0, + AEW_SDRAM = 1 +} aew_input_src_t; + +/* Contains configuration required for setup of AEW engine*/ +struct aew_configuration { + enum aew_alaw_enable alaw_enable; /* A-law status */ + int saturation_limit; /* Saturation Limit */ + struct aew_window window_config; /* Window for AEW Engine */ + struct aew_black_window blackwindow_config; /* Black Window */ +}; + +#ifdef __KERNEL__ +/* Contains information about device structure of AEW*/ +struct aew_device { + enum aew_In_use in_use; /* Driver usage flag */ + struct aew_configuration *config; /* Device configuration */ + void *buff_old; /* Contains latest statistics */ + void *buff_curr; /* Buffer in which HW will */ + + /* + * Fill the statistics + * or HW is already filling + * statistics + */ + + void *buff_app; /* Buffer which will be passed */ + + int buffer_filled; /* Flag indicates statistics */ + + unsigned int size_window; + wait_queue_head_t aew_wait_queue; + struct mutex read_blocked; + + /* Flag indicates Engine is configured */ + enum aew_config_flag aew_config; +}; + +int aew_hardware_setup(void); +int aew_validate_parameters(void); +#endif +#endif diff --git a/include/media/davinci/dm355_aew_hw.h b/include/media/davinci/dm355_aew_hw.h new file mode 100644 index 0000000..2ad2398 --- /dev/null +++ b/include/media/davinci/dm355_aew_hw.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DM355_AEW_DRIVER_HW_H +#define DM355_AEW_DRIVER_HW_H + +/* Include Files */ +#include + +#ifdef __KERNEL__ + +/* Register Offsets */ +#define AEWPID 0x00000000 /*Peripheral Revision */ +#define AEWPCR 0x00000004 /*Peripheral Control Register */ +#define AEWWIN1 0x0000004c /*Configuration for AE/AWB Windows */ +#define AEWINSTART 0x00000050 /*Start position for AE/AWB Windows */ + +/* Start position and height for black line of AE/AWB Windows */ +#define AEWINBLK 0x00000054 + +/* Configuration for subsampled data in AE/AWB windows */ +#define AEWSUBWIN 0x00000058 + +/* SDRAM/DDRAM Start address for AEW Engine */ +#define AEWBUFST 0x0000005c + +#define AEW_RSDR_ADDR 0x00000060 /*SDRAM/DDRAM Read Address */ +#define AEW_RSDR_OFFSET 0x00000064 /*SDRAM/DDRAM Line Offset */ +#define AEW_SDR_FRSIZE 0x00000068 /*Frame Size for SDRAM read data */ + +/* PID fields */ +#define AEW_TID (0xFF << 16) +#define AEW_CID (0xFF << 8) +#define AEW_PREV 0xFF + +/* PCR FIELDS */ +#define AVE2LMT (0x3ff << 22) /* Saturation Limit */ +#define AEW_SDR_FETCH_ENABLE (1 << 21) +#define AEW_INP_WIDTH (1 << 20) +#define AEW_INP_SRC (1 << 19) +#define AEW_ALAW_EN (1 << 17) /* Alaw Enable/Disable Bit */ +#define AEW_BUSYAF (1 << 15) /* Busy Bit for AF */ +#define AEW_BUSYAEWB (1 << 18) /* Busy bit for AEW */ +#define AEW_EN (1 << 16) /* AEW Engine Enable/Disable */ + +/* AEWWIN1 fields */ +#define WINH (0x7F << 24) /* Window Height */ +#define WINW (0x7f << 13) /* Window Width */ +#define WINVC (0x7f << 6) /* Window vertical Count */ +#define WINHC 0x3f /* Window Horizontal Count */ + +/* AEWWINSTART fields */ +#define WINSV (0xfff << 16) /* Window Vertical Start */ +#define WINSH 0xfff /* Window Horizontal start */ + +/* AEWWINBLK fields */ +#define BLKWINSV (0xfff << 16) /* Black Window Start */ +#define BLKWINH 0x7f /* Black Window height */ + +/* AEWSUBWIN fields */ +#define AEWINCV (0xf << 8) /* Vertical Lime Increment */ +#define AEWINCH 0xf /*Horizontal Line Increment */ + +/* BIT POSITIONS */ +#define AEW_AVE2LMT_SHIFT 22 +#define AEW_WINH_SHIFT 24 +#define AEW_WINW_SHIFT 13 +#define AEW_VT_COUNT_SHIFT 6 +#define AEW_VT_START_SHIFT 16 +#define AEW_LINE_INCR_SHIFT 8 +#define AEW_BLKWIN_VT_START_SHIFT 16 +#define AEW_EN_SHIFT 16 +#define AEW_BUSYAEWB_SHIFT 18 +#define AEW_INP_SRC_SHIFT 19 +#define AEW_SET_VAL(x) (((x) / 2) - 1) +#define AEW_NOT_EVEN 1 +#define AEW_CHECK_EVEN(x) ((x) % 2) + +#define AEW_CCDC 0 +#define AEW_INTSTATBASE (IO_ADDRESS(0x01C7080C)) +#define AEW_EVNTSELADDR (IO_ADDRESS(0x01C70814)) +#define AEW_GETINTSTAT __raw_readl(AEW_INTSTATBASE) +#define AEW_SETGAMMAWD __raw_writel(0x00000010 ,\ + IO_ADDRESS(0x01C70680)) +#define AEW_CLKCTRL_ADDR (IO_ADDRESS(0x01C70004)) +#define AEW_GETCLKCTRL __raw_readl(AEW_CLKCTRL_ADDR) +#define AEW_SETCLKCTRL(val) __raw_writel(val , AEW_CLKCTRL_ADDR) +#define AEW_IOBASE_VADDR (IO_ADDRESS(0x01c70080)) + +#define regw(val, reg) __raw_writel(val , (reg + AEW_IOBASE_VADDR)) +#define regr(reg) __raw_readl(reg + AEW_IOBASE_VADDR) +#define AEW_GETEVNT __raw_readl(AEW_EVNTSELADDR) +#define AEW_GET_PCR __raw_readl(AEW_IOBASE_VADDR + AEWPCR); + +#define isbusy() (regr(AEWPCR) & 0x40000) + +extern struct device *aewdev; + +/* Function Declaration */ +int aew_register_setup(struct aew_device *); +void aew_engine_setup(int); +void aew_set_address(unsigned long); + +#endif +#endif