|
This adds the OMAP ISP SCM modules to the kernel. Includes:
* ISP Autofocus Driver
* ISP H3A Driver
* ISP Histogram Driver
Signed-off-by: Sergio Aguirre <saaguirre@xxxxxx
---
drivers/media/video/isp/isp_af.c | 749 +++++++++++++++++++++++++++++++
drivers/media/video/isp/isp_af.h | 118 +++++
drivers/media/video/isp/isph3a.c | 872 +++++++++++++++++++++++++++++++++++++
drivers/media/video/isp/isph3a.h | 119 +++++
drivers/media/video/isp/isphist.c | 577 ++++++++++++++++++++++++
drivers/media/video/isp/isphist.h | 97 ++++
6 files changed, 2532 insertions(+), 0 deletions(-)
create mode 100644 drivers/media/video/isp/isp_af.c
create mode 100644 drivers/media/video/isp/isp_af.h
create mode 100644 drivers/media/video/isp/isph3a.c
create mode 100644 drivers/media/video/isp/isph3a.h
create mode 100644 drivers/media/video/isp/isphist.c
create mode 100644 drivers/media/video/isp/isphist.h
diff --git a/drivers/media/video/isp/isp_af.c b/drivers/media/video/isp/isp_af.c
new file mode 100644
index 0000000..633eb27
--- /dev/null
+++ b/drivers/media/video/isp/isp_af.c
@@ -0,0 +1,749 @@
+/*
+ * drivers/media/video/isp/isp_af.c
+ *
+ * AF module for TIs OMAP3 Camera ISP
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * 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.
+ */
+
+/* Linux specific include files */
+#include <asm/cacheflush.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 <linux/mman.h
+#include <linux/syscalls.h
+#include <linux/errno.h
+#include <linux/types.h
+#include <linux/dma-mapping.h
+
+#include <media/v4l2-int-device.h
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isph3a.h"
+#include "isp_af.h"
+#include "ispmmu.h"
+
+/**
+ * struct isp_af_buffer - AF frame stats buffer.
+ * @virt_addr: Virtual address to mmap the buffer.
+ * @phy_addr: Physical address of the buffer.
+ * @addr_align: Virtual Address 32 bytes aligned.
+ * @ispmmu_addr: Address of the buffer mapped by the ISPMMU.
+ * @mmap_addr: Mapped memory area of buffer. For userspace access.
+ * @locked: 1 - Buffer locked from write. 0 - Buffer can be overwritten.
+ * @frame_num: Frame number from which the statistics are taken.
+ * @lens_position: Lens position currently set in the DW9710 Coil motor driver.
+ * @next: Pointer to link next buffer.
+ */
+struct isp_af_buffer {
+ unsigned long virt_addr;
+ unsigned long phy_addr;
+ unsigned long addr_align;
+ unsigned long ispmmu_addr;
+ unsigned long mmap_addr;
+
+ u8 locked;
+ u16 frame_num;
+ struct isp_af_xtrastats xtrastats;
+ struct isp_af_buffer *next;
+};
+
+/**
+ * struct isp_af_status - AF status.
+ * @initialized: 1 - Buffers initialized.
+ * @update: 1 - Update registers.
+ * @stats_req: 1 - Future stats requested.
+ * @stats_done: 1 - Stats ready for user.
+ * @frame_req: Number of frame requested for statistics.
+ * @af_buff: Array of statistics buffers to access.
+ * @stats_buf_size: Statistics buffer size.
+ * @curr_cfg_buf_size: Current user configured stats buff size.
+ * @min_buf_size: Minimum statisitics buffer size.
+ * @frame_count: Frame Count.
+ * @stats_wait: Wait primitive for locking/unlocking the stats request.
+ * @buffer_lock: Spinlock for statistics buffers access.
+ */
+static struct isp_af_status {
+ u8 initialized;
+ u8 update;
+ u8 stats_req;
+ u8 stats_done;
+ u16 frame_req;
+
+ struct isp_af_buffer af_buff[H3A_MAX_BUFF];
+ unsigned int stats_buf_size;
+ unsigned int min_buf_size;
+ unsigned int curr_cfg_buf_size;
+
+ u32 frame_count;
+ wait_queue_head_t stats_wait;
+ spinlock_t buffer_lock; /* For stats buffers read/write sync */
+} afstat;
+
+struct af_device *af_dev_configptr;
+static struct isp_af_buffer *active_buff;
+static int af_major = -1;
+static int camnotify;
+
+/**
+ * isp_af_setxtrastats - Receives extra statistics from prior frames.
+ * @xtrastats: Pointer to structure containing extra statistics fields like
+ * field count and timestamp of frame.
+ *
+ * Called from update_vbq in camera driver
+ **/
+void isp_af_setxtrastats(struct isp_af_xtrastats *xtrastats, u8 updateflag)
+{
+ int i, past_i;
+
+ if (active_buff == NULL)
+ return;
+
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if (afstat.af_buff[i].frame_num == active_buff- frame_num)
+ break;
+ }
+
+ if (i == H3A_MAX_BUFF)
+ return;
+
+ if (i == 0) {
+ if (afstat.af_buff[H3A_MAX_BUFF - 1].locked == 0)
+ past_i = H3A_MAX_BUFF - 1;
+ else
+ past_i = H3A_MAX_BUFF - 2;
+ } else if (i == 1) {
+ if (afstat.af_buff[0].locked == 0)
+ past_i = 0;
+ else
+ past_i = H3A_MAX_BUFF - 1;
+ } else {
+ if (afstat.af_buff[i - 1].locked == 0)
+ past_i = i - 1;
+ else
+ past_i = i - 2;
+ }
+
+ if (updateflag & AF_UPDATEXS_TS)
+ afstat.af_buff[past_i].xtrastats.ts = xtrastats- ts;
+
+ if (updateflag & AF_UPDATEXS_FIELDCOUNT)
+ afstat.af_buff[past_i].xtrastats.field_count =
+ xtrastats- field_count;
+}
+EXPORT_SYMBOL(isp_af_setxtrastats);
+
+/*
+ * Helper function to update buffer cache pages
+ */
+static void isp_af_update_req_buffer(struct isp_af_buffer *buffer)
+{
+ int size = afstat.stats_buf_size;
+
+ size = PAGE_ALIGN(size);
+ /* Update the kernel pages of the requested buffer */
+ dmac_inv_range((void *)buffer- addr_align, (void *)buffer- addr_align +
+ size);
+}
+
+#define IS_OUT_OF_BOUNDS(value, min, max)
+ (((value) < (min)) || ((value) (max)))
+
+/* Function to check paxel parameters */
+int isp_af_check_paxel(void)
+{
+ struct af_paxel *paxel_cfg = &af_dev_configptr- config- paxel_config;
+ struct af_iir *iir_cfg = &af_dev_configptr- config- iir_config;
+
+ /* Check horizontal Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- hz_cnt, AF_PAXEL_HORIZONTAL_COUNT_MIN,
+ AF_PAXEL_HORIZONTAL_COUNT_MAX)) {
+ DPRINTK_ISP_AF("Error : Horizontal Count is incorrect");
+ return -AF_ERR_HZ_COUNT;
+ }
+
+ /*Check Vertical Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- vt_cnt, AF_PAXEL_VERTICAL_COUNT_MIN,
+ AF_PAXEL_VERTICAL_COUNT_MAX)) {
+ DPRINTK_ISP_AF("Error : Vertical Count is incorrect");
+ return -AF_ERR_VT_COUNT;
+ }
+
+ /*Check Height */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- height, AF_PAXEL_HEIGHT_MIN,
+ AF_PAXEL_HEIGHT_MAX)) {
+ DPRINTK_ISP_AF("Error : Height is incorrect");
+ return -AF_ERR_HEIGHT;
+ }
+
+ /*Check width */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- width, AF_PAXEL_WIDTH_MIN,
+ AF_PAXEL_WIDTH_MAX)) {
+ DPRINTK_ISP_AF("Error : Width is incorrect");
+ return -AF_ERR_WIDTH;
+ }
+
+ /*Check Line Increment */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- line_incr, AF_PAXEL_INCREMENT_MIN,
+ AF_PAXEL_INCREMENT_MAX)) {
+ DPRINTK_ISP_AF("Error : Line Increment is incorrect");
+ return -AF_ERR_INCR;
+ }
+
+ /*Check Horizontal Start */
+ if ((paxel_cfg- hz_start % 2 != 0) ||
+ (paxel_cfg- hz_start < (iir_cfg- hz_start_pos + 2)) ||
+ IS_OUT_OF_BOUNDS(paxel_cfg- hz_start,
+ AF_PAXEL_HZSTART_MIN, AF_PAXEL_HZSTART_MAX)) {
+ DPRINTK_ISP_AF("Error : Horizontal Start is incorrect");
+ return -AF_ERR_HZ_START;
+ }
+
+ /*Check Vertical Start */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg- vt_start, AF_PAXEL_VTSTART_MIN,
+ AF_PAXEL_VTSTART_MAX)) {
+ DPRINTK_ISP_AF("Error : Vertical Start is incorrect");
+ return -AF_ERR_VT_START;
+ }
+ return 0;
+}
+
+/**
+ * isp_af_check_iir - Function to check IIR Coefficient.
+ **/
+int isp_af_check_iir(void)
+{
+ struct af_iir *iir_cfg = &af_dev_configptr- config- iir_config;
+ int index;
+
+ for (index = 0; index < AF_NUMBER_OF_COEF; index++) {
+ if ((iir_cfg- coeff_set0[index]) AF_COEF_MAX) {
+ DPRINTK_ISP_AF("Error : Coefficient for set 0 is "
+ "incorrect");
+ return -AF_ERR_IIR_COEF;
+ }
+
+ if ((iir_cfg- coeff_set1[index]) AF_COEF_MAX) {
+ DPRINTK_ISP_AF("Error : Coefficient for set 1 is "
+ "incorrect");
+ return -AF_ERR_IIR_COEF;
+ }
+ }
+
+ if (IS_OUT_OF_BOUNDS(iir_cfg- hz_start_pos, AF_IIRSH_MIN,
+ AF_IIRSH_MAX)) {
+ DPRINTK_ISP_AF("Error : IIRSH is incorrect");
+ return -AF_ERR_IIRSH;
+ }
+
+ return 0;
+}
+/**
+ * isp_af_unlock_buffers - Helper function to unlock all buffers.
+ **/
+static void isp_af_unlock_buffers(void)
+{
+ int i;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&afstat.buffer_lock, irqflags);
+ for (i = 0; i < H3A_MAX_BUFF; i++)
+ afstat.af_buff[i].locked = 0;
+
+ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags);
+}
+
+/*
+ * Helper function to link allocated buffers
+ */
+static void isp_af_link_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if ((i + 1) < H3A_MAX_BUFF)
+ afstat.af_buff[i].next = &afstat.af_buff[i + 1];
+ else
+ afstat.af_buff[i].next = &afstat.af_buff[0];
+ }
+}
+
+/* Function to perform hardware set up */
+int isp_af_configure(struct af_configuration *afconfig)
+{
+ int result;
+ int buff_size, i;
+ unsigned int busyaf;
+ struct af_configuration *af_curr_cfg = af_dev_configptr- config;
+
+ if (NULL == afconfig) {
+ printk(KERN_ERR "Null argument in configuration.
");
+ return -EINVAL;
+ }
+
+ memcpy(af_curr_cfg, afconfig, sizeof(struct af_configuration));
+ /* Get the value of PCR register */
+ busyaf = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+
+ if ((busyaf & AF_BUSYAF) == AF_BUSYAF) {
+ DPRINTK_ISP_AF("AF_register_setup_ERROR : Engine Busy");
+ DPRINTK_ISP_AF("
Configuration cannot be done ");
+ return -AF_ERR_ENGINE_BUSY;
+ }
+
+ /* Check IIR Coefficient and start Values */
+ result = isp_af_check_iir();
+ if (result < 0)
+ return result;
+
+ /* Check Paxel Values */
+ result = isp_af_check_paxel();
+ if (result < 0)
+ return result;
+
+ /* Check HMF Threshold Values */
+ if (af_curr_cfg- hmf_config.threshold AF_THRESHOLD_MAX) {
+ DPRINTK_ISP_AF("Error : HMF Threshold is incorrect");
+ return -AF_ERR_THRESHOLD;
+ }
+
+ /* Compute buffer size */
+ buff_size = (af_curr_cfg- paxel_config.hz_cnt + 1) *
+ (af_curr_cfg- paxel_config.vt_cnt + 1) * AF_PAXEL_SIZE;
+
+ afstat.curr_cfg_buf_size = buff_size;
+ /* Deallocate the previous buffers */
+ if (afstat.stats_buf_size && (buff_size afstat.stats_buf_size)) {
+ isp_af_enable(0);
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ ispmmu_unmap(afstat.af_buff[i].ispmmu_addr);
+ dma_free_coherent(NULL, afstat.min_buf_size,
+ (void *)afstat.af_buff[i].virt_addr,
+ (dma_addr_t)afstat.af_buff[i].phy_addr);
+ afstat.af_buff[i].virt_addr = 0;
+ }
+ afstat.stats_buf_size = 0;
+ }
+
+ if (!afstat.af_buff[0].virt_addr) {
+ afstat.stats_buf_size = buff_size;
+ afstat.min_buf_size = PAGE_ALIGN(afstat.stats_buf_size);
+
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ afstat.af_buff[i].virt_addr =
+ (unsigned long)dma_alloc_coherent(NULL,
+ afstat.min_buf_size,
+ (dma_addr_t *)
+ &afstat.af_buff[i].phy_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (afstat.af_buff[i].virt_addr == 0) {
+ printk(KERN_ERR "Cant acquire memory for "
+ "buffer[%d]
", i);
+ return -ENOMEM;
+ }
+ afstat.af_buff[i].addr_align =
+ afstat.af_buff[i].virt_addr;
+ while ((afstat.af_buff[i].addr_align & 0xFFFFFFC0) !=
+ afstat.af_buff[i].addr_align)
+ afstat.af_buff[i].addr_align++;
+ afstat.af_buff[i].ispmmu_addr =
+ ispmmu_map(afstat.af_buff[i].phy_addr,
+ afstat.min_buf_size);
+ }
+ isp_af_unlock_buffers();
+ isp_af_link_buffers();
+
+ /* First active buffer */
+ if (active_buff == NULL)
+ active_buff = &afstat.af_buff[0];
+ isp_af_set_address(active_buff- ispmmu_addr);
+ }
+
+ result = isp_af_register_setup(af_dev_configptr);
+ if (result < 0)
+ return result;
+ af_dev_configptr- size_paxel = buff_size;
+ afstat.initialized = 1;
+ /* Set configuration flag to indicate HW setup done */
+ if (af_curr_cfg- af_config)
+ isp_af_enable(1);
+ else
+ isp_af_enable(0);
+
+ /* Success */
+ return 0;
+}
+EXPORT_SYMBOL(isp_af_configure);
+
+int isp_af_register_setup(struct af_device *af_dev)
+{
+ unsigned int pcr = 0, pax1 = 0, pax2 = 0, paxstart = 0;
+ unsigned int coef = 0;
+ unsigned int base_coef_set0 = 0;
+ unsigned int base_coef_set1 = 0;
+ int index;
+
+ /* Configure Hardware Registers */
+ /* Set PCR Register */
+ pcr = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); /* Read PCR Register */
+
+ /* Set Accumulator Mode */
+ if (af_dev- config- mode == ACCUMULATOR_PEAK)
+ pcr |= FVMODE;
+ else
+ pcr &= ~FVMODE;
+
+ /* Set A-law */
+ if (af_dev- config- alaw_enable == H3A_AF_ALAW_ENABLE)
+ pcr |= AF_ALAW_EN;
+ else
+ pcr &= ~AF_ALAW_EN;
+
+ /* Set RGB Position */
+ pcr &= ~RGBPOS;
+ pcr |= (af_dev- config- rgb_pos) << AF_RGBPOS_SHIFT;
+
+ /* HMF Configurations */
+ if (af_dev- config- hmf_config.enable == H3A_AF_HMF_ENABLE) {
+ pcr &= ~AF_MED_EN;
+ /* Enable HMF */
+ pcr |= AF_MED_EN;
+
+ /* Set Median Threshold */
+ pcr &= ~MED_TH;
+ pcr |= (af_dev- config- hmf_config.threshold) <<
+ AF_MED_TH_SHIFT;
+ } else
+ pcr &= ~AF_MED_EN;
+
+ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+
+ pax1 &= ~PAXW;
+ pax1 |= (af_dev- config- paxel_config.width) << AF_PAXW_SHIFT;
+
+ /* Set height in AFPAX1 */
+ pax1 &= ~PAXH;
+ pax1 |= af_dev- config- paxel_config.height;
+
+ isp_reg_writel(pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
+
+ /* Configure AFPAX2 Register */
+ /* Set Line Increment in AFPAX2 Register */
+ pax2 &= ~AFINCV;
+ pax2 |= (af_dev- config- paxel_config.line_incr) << AF_LINE_INCR_SHIFT;
+ /* Set Vertical Count */
+ pax2 &= ~PAXVC;
+ pax2 |= (af_dev- config- paxel_config.vt_cnt) << AF_VT_COUNT_SHIFT;
+ /* Set Horizontal Count */
+ pax2 &= ~PAXHC;
+ pax2 |= af_dev- config- paxel_config.hz_cnt;
+ isp_reg_writel(pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
+
+ /* Configure PAXSTART Register */
+ /*Configure Horizontal Start */
+ paxstart &= ~PAXSH;
+ paxstart |= (af_dev- config- paxel_config.hz_start) <<
+ AF_HZ_START_SHIFT;
+ /* Configure Vertical Start */
+ paxstart &= ~PAXSV;
+ paxstart |= af_dev- config- paxel_config.vt_start;
+ isp_reg_writel(paxstart, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAXSTART);
+
+ /*SetIIRSH Register */
+ isp_reg_writel(af_dev- config- iir_config.hz_start_pos,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
+
+ /*Set IIR Filter0 Coefficients */
+ base_coef_set0 = ISPH3A_AFCOEF010;
+ for (index = 0; index <= 8; index += 2) {
+ coef &= ~COEF_MASK0;
+ coef |= af_dev- config- iir_config.coeff_set0[index];
+ coef &= ~COEF_MASK1;
+ coef |= (af_dev- config- iir_config.coeff_set0[index + 1]) <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(coef, OMAP3_ISP_IOMEM_H3A, base_coef_set0);
+ base_coef_set0 = base_coef_set0 + AFCOEF_OFFSET;
+ }
+
+ /* set AFCOEF0010 Register */
+ isp_reg_writel(af_dev- config- iir_config.coeff_set0[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF010);
+
+ /*Set IIR Filter1 Coefficients */
+
+ base_coef_set1 = ISPH3A_AFCOEF110;
+ for (index = 0; index <= 8; index += 2) {
+ coef &= ~COEF_MASK0;
+ coef |= af_dev- config- iir_config.coeff_set1[index];
+ coef &= ~COEF_MASK1;
+ coef |= (af_dev- config- iir_config.coeff_set1[index + 1]) <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(coef, OMAP3_ISP_IOMEM_H3A, base_coef_set1);
+
+ base_coef_set1 = base_coef_set1 + AFCOEF_OFFSET;
+ }
+ isp_reg_writel(af_dev- config- iir_config.coeff_set1[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
+
+ return 0;
+}
+
+/* Function to set address */
+void isp_af_set_address(unsigned long address)
+{
+ isp_reg_writel(address, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFBUFST);
+}
+
+static int isp_af_stats_available(struct isp_af_data *afdata)
+{
+ int i, ret;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&afstat.buffer_lock, irqflags);
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if ((afdata- frame_number == afstat.af_buff[i].frame_num) &&
+ (afstat.af_buff[i].frame_num !=
+ active_buff- frame_num)) {
+ afstat.af_buff[i].locked = 1;
+ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags);
+ isp_af_update_req_buffer(&afstat.af_buff[i]);
+ afstat.af_buff[i].frame_num = 0;
+ ret = copy_to_user((void *)afdata- af_statistics_buf,
+ (void *)afstat.af_buff[i].virt_addr,
+ afstat.curr_cfg_buf_size);
+ if (ret) {
+ printk(KERN_ERR "Failed copy_to_user for "
+ "H3A stats buff, %d
", ret);
+ }
+ afdata- xtrastats.ts = afstat.af_buff[i].xtrastats.ts;
+ afdata- xtrastats.field_count =
+ afstat.af_buff[i].xtrastats.field_count;
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags);
+ /* Stats unavailable */
+
+ return -1;
+}
+
+void isp_af_notify(int notify)
+{
+ camnotify = notify;
+ if (camnotify && afstat.initialized) {
+ printk(KERN_DEBUG "Warning Camera Off
");
+ afstat.stats_req = 0;
+ afstat.stats_done = 1;
+ wake_up_interruptible(&afstat.stats_wait);
+ }
+}
+EXPORT_SYMBOL(isp_af_notify);
+/*
+ * This API allows the user to update White Balance gains, as well as
+ * exposure time and analog gain. It is also used to request frame
+ * statistics.
+ */
+int isp_af_request_statistics(struct isp_af_data *afdata)
+{
+ int ret = 0;
+ u16 frame_diff = 0;
+ u16 frame_cnt = afstat.frame_count;
+ wait_queue_t wqt;
+
+ if (!af_dev_configptr- config- af_config) {
+ printk(KERN_ERR "AF engine not enabled
");
+ return -EINVAL;
+ }
+
+ if (!(afdata- update & REQUEST_STATISTICS)) {
+ afdata- af_statistics_buf = NULL;
+ goto out;
+ }
+
+ isp_af_unlock_buffers();
+ /* Stats available? */
+ DPRINTK_ISP_AF("Stats available?
");
+ ret = isp_af_stats_available(afdata);
+ if (!ret)
+ goto out;
+
+ /* Stats in near future? */
+ DPRINTK_ISP_AF("Stats in near future?
");
+ if (afdata- frame_number frame_cnt)
+ frame_diff = afdata- frame_number - frame_cnt;
+ else if (afdata- frame_number < frame_cnt) {
+ if ((frame_cnt (MAX_FRAME_COUNT - MAX_FUTURE_FRAMES)) &&
+ (afdata- frame_number < MAX_FRAME_COUNT)) {
+ frame_diff = afdata- frame_number + MAX_FRAME_COUNT -
+ frame_cnt;
+ } else {
+ /* Frame unavailable */
+ frame_diff = MAX_FUTURE_FRAMES + 1;
+ }
+ }
+
+ if (frame_diff MAX_FUTURE_FRAMES) {
+ printk(KERN_ERR "Invalid frame requested, returning current"
+ " frame stats
");
+ afdata- frame_number = frame_cnt;
+ }
+ if (!camnotify) {
+ /* Block until frame in near future completes */
+ afstat.frame_req = afdata- frame_number;
+ afstat.stats_req = 1;
+ afstat.stats_done = 0;
+ init_waitqueue_entry(&wqt, current);
+ ret = wait_event_interruptible(afstat.stats_wait,
+ afstat.stats_done == 1);
+ if (ret < 0) {
+ afdata- af_statistics_buf = NULL;
+ return ret;
+ }
+ DPRINTK_ISP_AF("ISP AF request status interrupt raised
");
+
+ /* Stats now available */
+ ret = isp_af_stats_available(afdata);
+ if (ret) {
+ printk(KERN_ERR "After waiting for stats, stats not"
+ " available!!
");
+ afdata- af_statistics_buf = NULL;
+ }
+ }
+
+out:
+ afdata- curr_frame = afstat.frame_count;
+
+ return 0;
+}
+EXPORT_SYMBOL(isp_af_request_statistics);
+
+/* This function will handle the H3A interrupt. */
+static void isp_af_isr(unsigned long status, isp_vbq_callback_ptr arg1,
+ void *arg2)
+{
+ u16 frame_align;
+
+ if ((H3A_AF_DONE & status) != H3A_AF_DONE)
+ return;
+
+ /* timestamp stats buffer */
+ do_gettimeofday(&active_buff- xtrastats.ts);
+
+ /* Exchange buffers */
+ active_buff = active_buff- next;
+ if (active_buff- locked == 1)
+ active_buff = active_buff- next;
+ isp_af_set_address(active_buff- ispmmu_addr);
+
+ /* Update frame counter */
+ afstat.frame_count++;
+ frame_align = afstat.frame_count;
+ if (afstat.frame_count MAX_FRAME_COUNT) {
+ afstat.frame_count = 1;
+ frame_align++;
+ }
+ active_buff- frame_num = afstat.frame_count;
+
+ /* Future Stats requested? */
+ if (afstat.stats_req) {
+ /* Is the frame we want already done? */
+ if (frame_align = (afstat.frame_req + 1)) {
+ afstat.stats_req = 0;
+ afstat.stats_done = 1;
+ wake_up_interruptible(&afstat.stats_wait);
+ }
+ }
+}
+
+/* Function to Enable/Disable AF Engine */
+int isp_af_enable(int enable)
+{
+ unsigned int pcr;
+
+ pcr = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+
+ /* Set AF_EN bit in PCR Register */
+ if (enable) {
+ if (isp_set_callback(CBK_H3A_AF_DONE, isp_af_isr,
+ (void *)NULL, (void *)NULL)) {
+ printk(KERN_ERR "No callback for AF
");
+ return -EINVAL;
+ }
+
+ pcr |= AF_EN;
+ } else {
+ isp_unset_callback(CBK_H3A_AF_DONE);
+ pcr &= ~AF_EN;
+ }
+ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+ return 0;
+}
+
+/* Function to register the AF character device driver. */
+int __init isp_af_init(void)
+{
+ /*allocate memory for device structure and initialize it with 0 */
+ af_dev_configptr = kzalloc(sizeof(struct af_device), GFP_KERNEL);
+ if (!af_dev_configptr)
+ goto err_nomem1;
+
+ active_buff = NULL;
+
+ af_dev_configptr- config = (struct af_configuration *)
+ kzalloc(sizeof(struct af_configuration), GFP_KERNEL);
+
+ if (af_dev_configptr- config == NULL)
+ goto err_nomem2;
+
+ memset(&afstat, 0, sizeof(afstat));
+
+ init_waitqueue_head(&afstat.stats_wait);
+ spin_lock_init(&afstat.buffer_lock);
+
+ return 0;
+
+err_nomem2:
+ kfree(af_dev_configptr);
+err_nomem1:
+ printk(KERN_ERR "Error: kmalloc fail");
+ return -ENOMEM;
+}
+
+void __exit isp_af_exit(void)
+{
+ int i;
+
+ if (afstat.af_buff) {
+ /* Free buffers */
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ ispmmu_unmap(afstat.af_buff[i].ispmmu_addr);
+ dma_free_coherent(NULL, afstat.min_buf_size,
+ (void *)afstat.af_buff[i].virt_addr,
+ (dma_addr_t)afstat.af_buff[i].phy_addr);
+ }
+ }
+ kfree(af_dev_configptr- config);
+ kfree(af_dev_configptr);
+
+ memset(&afstat, 0, sizeof(afstat));
+
+ af_major = -1;
+ isp_af_enable(0);
+}
diff --git a/drivers/media/video/isp/isp_af.h b/drivers/media/video/isp/isp_af.h
new file mode 100644
index 0000000..83f127d
--- /dev/null
+++ b/drivers/media/video/isp/isp_af.h
@@ -0,0 +1,118 @@
+/*
+ * drivers/media/video/isp/isp_af.h
+ *
+ * Include file for AF module in TIs OMAP3 Camera ISP
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * 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.
+ */
+
+/* Device Constants */
+#ifndef OMAP_ISP_AF_H
+#define OMAP_ISP_AF_H
+
+#include <mach/isp_user.h
+
+#define AF_MAJOR_NUMBER 0
+#define ISPAF_NAME "OMAPISP_AF"
+#define AF_NR_DEVS 1
+#define AF_TIMEOUT ((300 * HZ) / 1000)
+
+
+
+/* Print Macros */
+/*list of error code */
+#define AF_ERR_HZ_COUNT 800 /* Invalid Horizontal Count */
+#define AF_ERR_VT_COUNT 801 /* Invalid Vertical Count */
+#define AF_ERR_HEIGHT 802 /* Invalid Height */
+#define AF_ERR_WIDTH 803 /* Invalid width */
+#define AF_ERR_INCR 804 /* Invalid Increment */
+#define AF_ERR_HZ_START 805 /* Invalid horizontal Start */
+#define AF_ERR_VT_START 806 /* Invalud vertical Start */
+#define AF_ERR_IIRSH 807 /* Invalid IIRSH value */
+#define AF_ERR_IIR_COEF 808 /* Invalid Coefficient */
+#define AF_ERR_SETUP 809 /* Setup not done */
+#define AF_ERR_THRESHOLD 810 /* Invalid Threshold */
+#define AF_ERR_ENGINE_BUSY 811 /* Engine is busy */
+
+#define AFPID 0x0 /* Peripheral Revision
+ * and Class Information
+ */
+
+#define AFCOEF_OFFSET 0x00000004 /* COEFFICIENT BASE
+ * ADDRESS
+ */
+
+/*
+ * PCR fields
+ */
+#define AF_BUSYAF (1 << 15)
+#define FVMODE (1 << 14)
+#define RGBPOS (0x7 << 11)
+#define MED_TH (0xFF << 3)
+#define AF_MED_EN (1 << 2)
+#define AF_ALAW_EN (1 << 1)
+#define AF_EN (1 << 0)
+
+/*
+ * AFPAX1 fields
+ */
+#define PAXW (0x7F << 16)
+#define PAXH 0x7F
+
+/*
+ * AFPAX2 fields
+ */
+#define AFINCV (0xF << 13)
+#define PAXVC (0x7F << 6)
+#define PAXHC 0x3F
+
+/*
+ * AFPAXSTART fields
+ */
+#define PAXSH (0xFFF<<16)
+#define PAXSV 0xFFF
+
+/*
+ * COEFFICIENT MASK
+ */
+
+#define COEF_MASK0 0xFFF
+#define COEF_MASK1 (0xFFF<<16)
+
+/* BIT SHIFTS */
+#define AF_RGBPOS_SHIFT 11
+#define AF_MED_TH_SHIFT 3
+#define AF_PAXW_SHIFT 16
+#define AF_LINE_INCR_SHIFT 13
+#define AF_VT_COUNT_SHIFT 6
+#define AF_HZ_START_SHIFT 16
+#define AF_COEF_SHIFT 16
+
+#define AF_UPDATEXS_TS (1 << 0)
+#define AF_UPDATEXS_FIELDCOUNT (1 << 1)
+#define AF_UPDATEXS_LENSPOS (1 << 2)
+
+/* Structure for device of AF Engine */
+struct af_device {
+ struct af_configuration *config; /*Device configuration structure */
+ int size_paxel; /*Paxel size in bytes */
+};
+
+int isp_af_check_paxel(void);
+int isp_af_check_iir(void);
+int isp_af_register_setup(struct af_device *af_dev);
+int isp_af_enable(int);
+void isp_af_notify(int notify);
+int isp_af_request_statistics(struct isp_af_data *afdata);
+int isp_af_configure(struct af_configuration *afconfig);
+void isp_af_set_address(unsigned long);
+void isp_af_setxtrastats(struct isp_af_xtrastats *xtrastats, u8 updateflag);
+#endif /* OMAP_ISP_AF_H */
diff --git a/drivers/media/video/isp/isph3a.c b/drivers/media/video/isp/isph3a.c
new file mode 100644
index 0000000..5c5aad4
--- /dev/null
+++ b/drivers/media/video/isp/isph3a.c
@@ -0,0 +1,872 @@
+/*
+ * drivers/media/video/isp/isph3a.c
+ *
+ * H3A module for TIs OMAP3 Camera ISP
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * Contributors:
+ * Sergio Aguirre <saaguirre@xxxxxx
+ * Troy Laramy
+ *
+ * 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 <asm/cacheflush.h
+
+#include <linux/mm.h
+#include <linux/mman.h
+#include <linux/syscalls.h
+#include <linux/module.h
+#include <linux/errno.h
+#include <linux/types.h
+#include <linux/dma-mapping.h
+#include <linux/io.h
+#include <linux/uaccess.h
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isph3a.h"
+#include "ispmmu.h"
+#include "isppreview.h"
+
+/**
+ * struct isph3a_aewb_buffer - AE, AWB frame stats buffer.
+ * @virt_addr: Virtual address to mmap the buffer.
+ * @phy_addr: Physical address of the buffer.
+ * @addr_align: Virtual Address 32 bytes aligned.
+ * @ispmmu_addr: Address of the buffer mapped by the ISPMMU.
+ * @mmap_addr: Mapped memory area of buffer. For userspace access.
+ * @locked: 1 - Buffer locked from write. 0 - Buffer can be overwritten.
+ * @frame_num: Frame number from which the statistics are taken.
+ * @next: Pointer to link next buffer.
+ */
+struct isph3a_aewb_buffer {
+ unsigned long virt_addr;
+ unsigned long phy_addr;
+ unsigned long addr_align;
+ unsigned long ispmmu_addr;
+ unsigned long mmap_addr; /* For userspace */
+ struct timeval ts;
+
+ u8 locked;
+ u16 frame_num;
+ struct isph3a_aewb_buffer *next;
+};
+
+/**
+ * struct isph3a_aewb_status - AE, AWB status.
+ * @initialized: 1 - Buffers initialized.
+ * @update: 1 - Update registers.
+ * @stats_req: 1 - Future stats requested.
+ * @stats_done: 1 - Stats ready for user.
+ * @frame_req: Number of frame requested for statistics.
+ * @h3a_buff: Array of statistics buffers to access.
+ * @stats_buf_size: Statistics buffer size.
+ * @min_buf_size: Minimum statisitics buffer size.
+ * @win_count: Window Count.
+ * @frame_count: Frame Count.
+ * @stats_wait: Wait primitive for locking/unlocking the stats request.
+ * @buffer_lock: Spinlock for statistics buffers access.
+ */
+static struct isph3a_aewb_status {
+ u8 initialized;
+ u8 update;
+ u8 stats_req;
+ u8 stats_done;
+ u16 frame_req;
+
+ struct isph3a_aewb_buffer h3a_buff[H3A_MAX_BUFF];
+ unsigned int stats_buf_size;
+ unsigned int min_buf_size;
+ unsigned int curr_cfg_buf_size;
+
+ u16 win_count;
+ u32 frame_count;
+ wait_queue_head_t stats_wait;
+ spinlock_t buffer_lock; /* For stats buffers read/write sync */
+} aewbstat;
+
+/**
+ * struct isph3a_aewb_regs - Current value of AE, AWB configuration registers.
+ * reg_pcr: Peripheral control register.
+ * reg_win1: Control register.
+ * reg_start: Start position register.
+ * reg_blk: Black line register.
+ * reg_subwin: Configuration register.
+ */
+static struct isph3a_aewb_regs {
+ u32 reg_pcr;
+ u32 reg_win1;
+ u32 reg_start;
+ u32 reg_blk;
+ u32 reg_subwin;
+} aewb_regs;
+
+static struct isph3a_aewb_config aewb_config_local = {
+ .saturation_limit = 0x3FF,
+ .win_height = 0,
+ .win_width = 0,
+ .ver_win_count = 0,
+ .hor_win_count = 0,
+ .ver_win_start = 0,
+ .hor_win_start = 0,
+ .blk_ver_win_start = 0,
+ .blk_win_height = 0,
+ .subsample_ver_inc = 0,
+ .subsample_hor_inc = 0,
+ .alaw_enable = 0,
+ .aewb_enable = 0,
+};
+
+/* Structure for saving/restoring h3a module registers */
+static struct isp_reg isph3a_reg_list[] = {
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINSTART, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWSUBWIN, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAXSTART, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFBUFST, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF010, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF032, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF054, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF076, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF098, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF110, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF132, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF154, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF176, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF198, 0},
+ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010, 0},
+ {0, ISP_TOK_TERM, 0}
+};
+
+static struct ispprev_wbal h3awb_update;
+static struct isph3a_aewb_buffer *active_buff;
+static struct isph3a_aewb_xtrastats h3a_xtrastats[H3A_MAX_BUFF];
+static int camnotify;
+static int wb_update;
+static void isph3a_print_status(void);
+
+/**
+ * isph3a_aewb_setxtrastats - Receives extra statistics from prior frames.
+ * @xtrastats: Pointer to structure containing extra statistics fields like
+ * field count and timestamp of frame.
+ *
+ * Called from update_vbq in camera driver
+ **/
+void isph3a_aewb_setxtrastats(struct isph3a_aewb_xtrastats *xtrastats)
+{
+ int i;
+
+ if (active_buff == NULL)
+ return;
+
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if (aewbstat.h3a_buff[i].frame_num != active_buff- frame_num)
+ continue;
+
+ if (i == 0) {
+ if (aewbstat.h3a_buff[H3A_MAX_BUFF - 1].
+ locked == 0) {
+ h3a_xtrastats[H3A_MAX_BUFF - 1] =
+ *xtrastats;
+ } else {
+ h3a_xtrastats[H3A_MAX_BUFF - 2] =
+ *xtrastats;
+ }
+ } else if (i == 1) {
+ if (aewbstat.h3a_buff[0].locked == 0)
+ h3a_xtrastats[0] = *xtrastats;
+ else {
+ h3a_xtrastats[H3A_MAX_BUFF - 1] =
+ *xtrastats;
+ }
+ } else {
+ if (aewbstat.h3a_buff[i - 1].locked == 0)
+ h3a_xtrastats[i - 1] = *xtrastats;
+ else
+ h3a_xtrastats[i - 2] = *xtrastats;
+ }
+ return;
+ }
+}
+EXPORT_SYMBOL(isph3a_aewb_setxtrastats);
+
+/**
+ * isph3a_aewb_enable - Enables AE, AWB engine in the H3A module.
+ * @enable: 1 - Enables the AE & AWB engine.
+ *
+ * Client should configure all the AE & AWB registers in H3A before this.
+ **/
+static void isph3a_aewb_enable(u8 enable)
+{
+ isp_reg_writel(IRQ0STATUS_H3A_AWB_DONE_IRQ, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+
+ if (enable) {
+ aewb_regs.reg_pcr |= ISPH3A_PCR_AEW_EN;
+ isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) | ISPH3A_PCR_AEW_EN,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+ DPRINTK_ISPH3A(" H3A enabled
");
+ } else {
+ aewb_regs.reg_pcr &= ~ISPH3A_PCR_AEW_EN;
+ isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) & ~ISPH3A_PCR_AEW_EN,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+ DPRINTK_ISPH3A(" H3A disabled
");
+ }
+ aewb_config_local.aewb_enable = enable;
+}
+
+/**
+ * isph3a_update_wb - Updates WB parameters.
+ *
+ * Needs to be called when no ISP Preview processing is taking place.
+ **/
+void isph3a_update_wb(void)
+{
+ if (wb_update) {
+ isppreview_config_whitebalance(h3awb_update);
+ wb_update = 0;
+ }
+ return;
+}
+EXPORT_SYMBOL(isph3a_update_wb);
+
+/**
+ * isph3a_aewb_update_regs - Helper function to update h3a registers.
+ **/
+static void isph3a_aewb_update_regs(void)
+{
+ isp_reg_writel(aewb_regs.reg_pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+ isp_reg_writel(aewb_regs.reg_win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1);
+ isp_reg_writel(aewb_regs.reg_start, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINSTART);
+ isp_reg_writel(aewb_regs.reg_blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK);
+ isp_reg_writel(aewb_regs.reg_subwin, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWSUBWIN);
+
+ aewbstat.update = 0;
+ aewbstat.frame_count = 0;
+}
+
+/**
+ * isph3a_aewb_update_req_buffer - Helper function to update buffer cache pages
+ * @buffer: Pointer to structure
+ **/
+static void isph3a_aewb_update_req_buffer(struct isph3a_aewb_buffer *buffer)
+{
+ int size = aewbstat.stats_buf_size;
+
+ size = PAGE_ALIGN(size);
+ dmac_inv_range((void *)buffer- addr_align,
+ (void *)buffer- addr_align + size);
+}
+
+/**
+ * isph3a_aewb_stats_available - Check for stats available of specified frame.
+ * @aewbdata: Pointer to return AE AWB statistics data
+ *
+ * Returns 0 if successful, or -1 if statistics are unavailable.
+ **/
+static int isph3a_aewb_stats_available(struct isph3a_aewb_data *aewbdata)
+{
+ int i, ret;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&aewbstat.buffer_lock, irqflags);
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if ((aewbdata- frame_number != aewbstat.h3a_buff[i].frame_num) ||
+ (aewbstat.h3a_buff[i].frame_num == active_buff- frame_num))
+ continue;
+ aewbstat.h3a_buff[i].locked = 1;
+ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags);
+ isph3a_aewb_update_req_buffer(&aewbstat.h3a_buff[i]);
+ aewbstat.h3a_buff[i].frame_num = 0;
+ ret = copy_to_user((void *)aewbdata- h3a_aewb_statistics_buf,
+ (void *)aewbstat.h3a_buff[i].virt_addr,
+ aewbstat.curr_cfg_buf_size);
+ if (ret) {
+ printk(KERN_ERR "Failed copy_to_user for "
+ "H3A stats buff, %d
", ret);
+ }
+ aewbdata- ts = aewbstat.h3a_buff[i].ts;
+ aewbdata- field_count = h3a_xtrastats[i].field_count;
+ return 0;
+ }
+ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags);
+
+ return -1;
+}
+
+/**
+ * isph3a_aewb_link_buffers - Helper function to link allocated buffers.
+ **/
+static void isph3a_aewb_link_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ if ((i + 1) < H3A_MAX_BUFF) {
+ aewbstat.h3a_buff[i].next = &aewbstat.h3a_buff[i + 1];
+ h3a_xtrastats[i].next = &h3a_xtrastats[i + 1];
+ } else {
+ aewbstat.h3a_buff[i].next = &aewbstat.h3a_buff[0];
+ h3a_xtrastats[i].next = &h3a_xtrastats[0];
+ }
+ }
+}
+
+/**
+ * isph3a_aewb_unlock_buffers - Helper function to unlock all buffers.
+ **/
+static void isph3a_aewb_unlock_buffers(void)
+{
+ int i;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&aewbstat.buffer_lock, irqflags);
+ for (i = 0; i < H3A_MAX_BUFF; i++)
+ aewbstat.h3a_buff[i].locked = 0;
+
+ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags);
+}
+
+/**
+ * isph3a_aewb_isr - Callback from ISP driver for H3A AEWB interrupt.
+ * @status: IRQ0STATUS in case of MMU error, 0 for H3A interrupt.
+ * @arg1: Not used as of now.
+ * @arg2: Not used as of now.
+ */
+static void isph3a_aewb_isr(unsigned long status, isp_vbq_callback_ptr arg1,
+ void *arg2)
+{
+ u16 frame_align;
+
+ if ((H3A_AWB_DONE & status) != H3A_AWB_DONE)
+ return;
+
+ do_gettimeofday(&active_buff- ts);
+ active_buff = active_buff- next;
+ if (active_buff- locked == 1)
+ active_buff = active_buff- next;
+ isp_reg_writel(active_buff- ispmmu_addr, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
+
+ aewbstat.frame_count++;
+ frame_align = aewbstat.frame_count;
+ if (aewbstat.frame_count MAX_FRAME_COUNT) {
+ aewbstat.frame_count = 1;
+ frame_align++;
+ }
+ active_buff- frame_num = aewbstat.frame_count;
+
+ if (aewbstat.stats_req) {
+ DPRINTK_ISPH3A("waiting for frame %d
", aewbstat.frame_req);
+ if (frame_align = (aewbstat.frame_req + 1)) {
+ aewbstat.stats_req = 0;
+ aewbstat.stats_done = 1;
+ wake_up_interruptible(&aewbstat.stats_wait);
+ }
+ }
+
+ if (aewbstat.update)
+ isph3a_aewb_update_regs();
+}
+
+/**
+ * isph3a_aewb_set_params - Helper function to check & store user given params.
+ * @user_cfg: Pointer to AE and AWB parameters struct.
+ *
+ * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to
+ * program them during ISR.
+ *
+ * Returns 0 if successful, or -EINVAL if any of the parameters are invalid.
+ **/
+static int isph3a_aewb_set_params(struct isph3a_aewb_config *user_cfg)
+{
+ if (unlikely(user_cfg- saturation_limit MAX_SATURATION_LIM)) {
+ printk(KERN_ERR "Invalid Saturation_limit: %d
",
+ user_cfg- saturation_limit);
+ return -EINVAL;
+ }
+ if (aewb_config_local.saturation_limit != user_cfg- saturation_limit) {
+ WRITE_SAT_LIM(aewb_regs.reg_pcr, user_cfg- saturation_limit);
+ aewb_config_local.saturation_limit =
+ user_cfg- saturation_limit;
+ aewbstat.update = 1;
+ }
+
+ if (aewb_config_local.alaw_enable != user_cfg- alaw_enable) {
+ WRITE_ALAW(aewb_regs.reg_pcr, user_cfg- alaw_enable);
+ aewb_config_local.alaw_enable = user_cfg- alaw_enable;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- win_height < MIN_WIN_H) ||
+ (user_cfg- win_height MAX_WIN_H) ||
+ (user_cfg- win_height & 0x01))) {
+ printk(KERN_ERR "Invalid window height: %d
",
+ user_cfg- win_height);
+ return -EINVAL;
+ }
+ if (aewb_config_local.win_height != user_cfg- win_height) {
+ WRITE_WIN_H(aewb_regs.reg_win1, user_cfg- win_height);
+ aewb_config_local.win_height = user_cfg- win_height;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- win_width < MIN_WIN_W) ||
+ (user_cfg- win_width MAX_WIN_W) ||
+ (user_cfg- win_width & 0x01))) {
+ printk(KERN_ERR "Invalid window width: %d
",
+ user_cfg- win_width);
+ return -EINVAL;
+ }
+ if (aewb_config_local.win_width != user_cfg- win_width) {
+ WRITE_WIN_W(aewb_regs.reg_win1, user_cfg- win_width);
+ aewb_config_local.win_width = user_cfg- win_width;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- ver_win_count < 1) ||
+ (user_cfg- ver_win_count MAX_WINVC))) {
+ printk(KERN_ERR "Invalid vertical window count: %d
",
+ user_cfg- ver_win_count);
+ return -EINVAL;
+ }
+ if (aewb_config_local.ver_win_count != user_cfg- ver_win_count) {
+ WRITE_VER_C(aewb_regs.reg_win1, user_cfg- ver_win_count);
+ aewb_config_local.ver_win_count = user_cfg- ver_win_count;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- hor_win_count < 1) ||
+ (user_cfg- hor_win_count MAX_WINHC))) {
+ printk(KERN_ERR "Invalid horizontal window count: %d
",
+ user_cfg- hor_win_count);
+ return -EINVAL;
+ }
+ if (aewb_config_local.hor_win_count != user_cfg- hor_win_count) {
+ WRITE_HOR_C(aewb_regs.reg_win1, user_cfg- hor_win_count);
+ aewb_config_local.hor_win_count = user_cfg- hor_win_count;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely(user_cfg- ver_win_start MAX_WINSTART)) {
+ printk(KERN_ERR "Invalid vertical window start: %d
",
+ user_cfg- ver_win_start);
+ return -EINVAL;
+ }
+ if (aewb_config_local.ver_win_start != user_cfg- ver_win_start) {
+ WRITE_VER_WIN_ST(aewb_regs.reg_start, user_cfg- ver_win_start);
+ aewb_config_local.ver_win_start = user_cfg- ver_win_start;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely(user_cfg- hor_win_start MAX_WINSTART)) {
+ printk(KERN_ERR "Invalid horizontal window start: %d
",
+ user_cfg- hor_win_start);
+ return -EINVAL;
+ }
+ if (aewb_config_local.hor_win_start != user_cfg- hor_win_start) {
+ WRITE_HOR_WIN_ST(aewb_regs.reg_start, user_cfg- hor_win_start);
+ aewb_config_local.hor_win_start = user_cfg- hor_win_start;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely(user_cfg- blk_ver_win_start MAX_WINSTART)) {
+ printk(KERN_ERR "Invalid black vertical window start: %d
",
+ user_cfg- blk_ver_win_start);
+ return -EINVAL;
+ }
+ if (aewb_config_local.blk_ver_win_start != user_cfg- blk_ver_win_start) {
+ WRITE_BLK_VER_WIN_ST(aewb_regs.reg_blk,
+ user_cfg- blk_ver_win_start);
+ aewb_config_local.blk_ver_win_start =
+ user_cfg- blk_ver_win_start;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- blk_win_height < MIN_WIN_H) ||
+ (user_cfg- blk_win_height MAX_WIN_H) ||
+ (user_cfg- blk_win_height & 0x01))) {
+ printk(KERN_ERR "Invalid black window height: %d
",
+ user_cfg- blk_win_height);
+ return -EINVAL;
+ }
+ if (aewb_config_local.blk_win_height != user_cfg- blk_win_height) {
+ WRITE_BLK_WIN_H(aewb_regs.reg_blk, user_cfg- blk_win_height);
+ aewb_config_local.blk_win_height = user_cfg- blk_win_height;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- subsample_ver_inc < MIN_SUB_INC) ||
+ (user_cfg- subsample_ver_inc MAX_SUB_INC) ||
+ (user_cfg- subsample_ver_inc & 0x01))) {
+ printk(KERN_ERR "Invalid vertical subsample increment: %d
",
+ user_cfg- subsample_ver_inc);
+ return -EINVAL;
+ }
+ if (aewb_config_local.subsample_ver_inc != user_cfg- subsample_ver_inc) {
+ WRITE_SUB_VER_INC(aewb_regs.reg_subwin,
+ user_cfg- subsample_ver_inc);
+ aewb_config_local.subsample_ver_inc =
+ user_cfg- subsample_ver_inc;
+ aewbstat.update = 1;
+ }
+
+ if (unlikely((user_cfg- subsample_hor_inc < MIN_SUB_INC) ||
+ (user_cfg- subsample_hor_inc MAX_SUB_INC) ||
+ (user_cfg- subsample_hor_inc & 0x01))) {
+ printk(KERN_ERR "Invalid horizontal subsample increment: %d
",
+ user_cfg- subsample_hor_inc);
+ return -EINVAL;
+ }
+ if (aewb_config_local.subsample_hor_inc != user_cfg- subsample_hor_inc) {
+ WRITE_SUB_HOR_INC(aewb_regs.reg_subwin,
+ user_cfg- subsample_hor_inc);
+ aewb_config_local.subsample_hor_inc =
+ user_cfg- subsample_hor_inc;
+ aewbstat.update = 1;
+ }
+
+ if ((!aewbstat.initialized) || (0 == aewb_config_local.aewb_enable)) {
+ isph3a_aewb_update_regs();
+ aewbstat.initialized = 1;
+ }
+ return 0;
+}
+
+/**
+ * isph3a_aewb_configure - Configure AEWB regs, enable/disable H3A engine.
+ * @aewbcfg: Pointer to AEWB config structure.
+ *
+ * Returns 0 if successful, -EINVAL if aewbcfg pointer is NULL, -ENOMEM if
+ * was unable to allocate memory for the buffer, of other errors if H3A
+ * callback is not set or the parameters for AEWB are invalid.
+ **/
+int isph3a_aewb_configure(struct isph3a_aewb_config *aewbcfg)
+{
+ int ret = 0;
+ int i;
+ int win_count = 0;
+
+ if (NULL == aewbcfg) {
+ printk(KERN_ERR "Null argument in configuration.
");
+ return -EINVAL;
+ }
+
+ if (!aewbstat.initialized) {
+ DPRINTK_ISPH3A("Setting callback for H3A
");
+ ret = isp_set_callback(CBK_H3A_AWB_DONE, isph3a_aewb_isr,
+ (void *)NULL, (void *)NULL);
+ if (ret) {
+ printk(KERN_ERR "No callback for H3A
");
+ return ret;
+ }
+ }
+
+ ret = isph3a_aewb_set_params(aewbcfg);
+ if (ret) {
+ printk(KERN_ERR "Invalid parameters!
");
+ return ret;
+ }
+
+ win_count = (aewbcfg- ver_win_count * aewbcfg- hor_win_count);
+ win_count += aewbcfg- hor_win_count;
+ ret = (win_count / 8);
+ win_count += (win_count % 8) ? 1 : 0;
+ win_count += ret;
+
+ aewbstat.win_count = win_count;
+ aewbstat.curr_cfg_buf_size = win_count * AEWB_PACKET_SIZE;
+
+ if (aewbstat.stats_buf_size && ((win_count * AEWB_PACKET_SIZE)
+ aewbstat.stats_buf_size)) {
+ DPRINTK_ISPH3A("There was a previous buffer... "
+ "Freeing/unmapping current stat busffs
");
+ isph3a_aewb_enable(0);
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ ispmmu_unmap(aewbstat.h3a_buff[i].ispmmu_addr);
+ dma_free_coherent(NULL,
+ aewbstat.min_buf_size,
+ (void *)aewbstat.h3a_buff[i].virt_addr,
+ (dma_addr_t)aewbstat.h3a_buff[i].phy_addr);
+ aewbstat.h3a_buff[i].virt_addr = 0;
+ }
+ aewbstat.stats_buf_size = 0;
+ }
+
+ if (!aewbstat.h3a_buff[0].virt_addr) {
+ aewbstat.stats_buf_size = win_count * AEWB_PACKET_SIZE;
+ aewbstat.min_buf_size = PAGE_ALIGN(aewbstat.stats_buf_size);
+
+ DPRINTK_ISPH3A("Allocating/mapping new stat buffs
");
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ aewbstat.h3a_buff[i].virt_addr =
+ (unsigned long)dma_alloc_coherent(NULL,
+ aewbstat.min_buf_size,
+ (dma_addr_t *)&aewbstat.h3a_buff[i].phy_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (aewbstat.h3a_buff[i].virt_addr == 0) {
+ printk(KERN_ERR "Cant acquire memory for "
+ "buffer[%d]
", i);
+ return -ENOMEM;
+ }
+ aewbstat.h3a_buff[i].addr_align =
+ aewbstat.h3a_buff[i].virt_addr;
+ while ((aewbstat.h3a_buff[i].addr_align &
+ 0xFFFFFFC0) !=
+ aewbstat.h3a_buff[i].
+ addr_align)
+ aewbstat.h3a_buff[i].addr_align++;
+ aewbstat.h3a_buff[i].ispmmu_addr = ispmmu_map(aewbstat.
+ h3a_buff[i].phy_addr,
+ aewbstat.min_buf_size);
+ }
+ isph3a_aewb_unlock_buffers();
+ isph3a_aewb_link_buffers();
+
+ if (active_buff == NULL)
+ active_buff = &aewbstat.h3a_buff[0];
+ isp_reg_writel(active_buff- ispmmu_addr, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
+ }
+ for (i = 0; i < H3A_MAX_BUFF; i++) {
+ DPRINTK_ISPH3A("buff[%d] addr is:
virt 0x%lX
"
+ " aligned 0x%lX
"
+ " phys 0x%lX
"
+ " ispmmu 0x%08lX
"
+ " mmapped 0x%lX
", i,
+ aewbstat.h3a_buff[i].virt_addr,
+ aewbstat.h3a_buff[i].addr_align,
+ aewbstat.h3a_buff[i].phy_addr,
+ aewbstat.h3a_buff[i].ispmmu_addr,
+ aewbstat.h3a_buff[i].mmap_addr);
+ }
+ isph3a_aewb_enable(aewbcfg- aewb_enable);
+ isph3a_print_status();
+
+ return 0;
+}
+EXPORT_SYMBOL(isph3a_aewb_configure);
+
+/**
+ * isph3a_aewb_request_statistics - REquest statistics and update gains in AEWB
+ * @aewbdata: Pointer to return AE AWB statistics data.
+ *
+ * This API allows the user to update White Balance gains, as well as
+ * exposure time and analog gain. It is also used to request frame
+ * statistics.
+ *
+ * Returns 0 if successful, -EINVAL when H3A engine is not enabled, or other
+ * errors when setting gains.
+ **/
+int isph3a_aewb_request_statistics(struct isph3a_aewb_data *aewbdata)
+{
+ int ret = 0;
+ u16 frame_diff = 0;
+ u16 frame_cnt = aewbstat.frame_count;
+ wait_queue_t wqt;
+
+ if (!aewb_config_local.aewb_enable) {
+ printk(KERN_ERR "H3A engine not enabled
");
+ return -EINVAL;
+ }
+
+ DPRINTK_ISPH3A("isph3a_aewb_request_statistics: Enter "
+ "(frame req. = %d, current frame = %d, update = %d)
",
+ aewbdata- frame_number, frame_cnt, aewbdata- update);
+ DPRINTK_ISPH3A("User data received:
");
+ DPRINTK_ISPH3A("Digital gain = 0x%04x
", aewbdata- dgain);
+ DPRINTK_ISPH3A("WB gain b *= 0x%04x
", aewbdata- wb_gain_b);
+ DPRINTK_ISPH3A("WB gain r *= 0x%04x
", aewbdata- wb_gain_r);
+ DPRINTK_ISPH3A("WB gain gb = 0x%04x
", aewbdata- wb_gain_gb);
+ DPRINTK_ISPH3A("WB gain gr = 0x%04x
", aewbdata- wb_gain_gr);
+ DPRINTK_ISPH3A("ISP AEWB request status wait for interrupt
");
+
+ if (!aewbdata- update) {
+ aewbdata- h3a_aewb_statistics_buf = NULL;
+ goto out;
+ }
+ if (aewbdata- update & SET_DIGITAL_GAIN)
+ h3awb_update.dgain = (u16)aewbdata- dgain;
+ if (aewbdata- update & SET_COLOR_GAINS) {
+ h3awb_update.coef3 = (u8)aewbdata- wb_gain_b;
+ h3awb_update.coef2 = (u8)aewbdata- wb_gain_gr;
+ h3awb_update.coef1 = (u8)aewbdata- wb_gain_gb;
+ h3awb_update.coef0 = (u8)aewbdata- wb_gain_r;
+ }
+ if (aewbdata- update & (SET_COLOR_GAINS | SET_DIGITAL_GAIN))
+ wb_update = 1;
+
+ if (!(aewbdata- update & REQUEST_STATISTICS)) {
+ aewbdata- h3a_aewb_statistics_buf = NULL;
+ goto out;
+ }
+ isph3a_aewb_unlock_buffers();
+
+ DPRINTK_ISPH3A("Stats available?
");
+ ret = isph3a_aewb_stats_available(aewbdata);
+ if (!ret)
+ goto out;
+
+ DPRINTK_ISPH3A("Stats in near future?
");
+ if (aewbdata- frame_number frame_cnt)
+ frame_diff = aewbdata- frame_number - frame_cnt;
+ else if (aewbdata- frame_number < frame_cnt) {
+ if ((frame_cnt (MAX_FRAME_COUNT - MAX_FUTURE_FRAMES)) &&
+ (aewbdata- frame_number < MAX_FRAME_COUNT)) {
+ frame_diff = aewbdata- frame_number + MAX_FRAME_COUNT -
+ frame_cnt;
+ } else
+ frame_diff = MAX_FUTURE_FRAMES + 1;
+ }
+
+ if (frame_diff MAX_FUTURE_FRAMES) {
+ printk(KERN_ERR "Invalid frame requested, returning current"
+ " frame stats
");
+ aewbdata- frame_number = frame_cnt;
+ }
+ if (camnotify) {
+ DPRINTK_ISPH3A("NOT Waiting on stats IRQ for frame %d "
+ "because camnotify set
", aewbdata- frame_number);
+ aewbdata- h3a_aewb_statistics_buf = NULL;
+ goto out;
+ }
+ DPRINTK_ISPH3A("Waiting on stats IRQ for frame %d
",
+ aewbdata- frame_number);
+ aewbstat.frame_req = aewbdata- frame_number;
+ aewbstat.stats_req = 1;
+ aewbstat.stats_done = 0;
+ init_waitqueue_entry(&wqt, current);
+ ret = wait_event_interruptible(aewbstat.stats_wait,
+ aewbstat.stats_done == 1);
+ if (ret < 0) {
+ printk(KERN_ERR "isph3a_aewb_request_statistics"
+ " Error on wait event %d
", ret);
+ aewbdata- h3a_aewb_statistics_buf = NULL;
+ return ret;
+ }
+
+ DPRINTK_ISPH3A("ISP AEWB request status interrupt raised
");
+ ret = isph3a_aewb_stats_available(aewbdata);
+ |