Maemo patchset 20101501+0m5
[h-e-n] / drivers / media / video / isp / isp_af.c
diff --git a/drivers/media/video/isp/isp_af.c b/drivers/media/video/isp/isp_af.c
new file mode 100644 (file)
index 0000000..5936249
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * isp_af.c
+ *
+ * AF module for TI's OMAP3 Camera ISP
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contributors:
+ *     Sergio Aguirre <saaguirre@ti.com>
+ *     Troy Laramy
+ *     David Cohen <david.cohen@nokia.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.
+ */
+
+/* Linux specific include files */
+#include <linux/device.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isph3a.h"
+#include "isp_af.h"
+
+#define IS_OUT_OF_BOUNDS(value, min, max)              \
+       (((value) < (min)) || ((value) > (max)))
+
+/* Function to check paxel parameters */
+static int isp_af_check_params(struct isp_af_device *isp_af,
+                              struct af_configuration *afconfig)
+{
+       struct af_paxel *paxel_cfg = &afconfig->paxel_config;
+       struct af_iir *iir_cfg = &afconfig->iir_config;
+       int index;
+
+       /* Check horizontal Count */
+       if (IS_OUT_OF_BOUNDS(paxel_cfg->hz_cnt, AF_PAXEL_HORIZONTAL_COUNT_MIN,
+                            AF_PAXEL_HORIZONTAL_COUNT_MAX))
+               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))
+               return -AF_ERR_VT_COUNT;
+
+       /* Check Height */
+       if (IS_OUT_OF_BOUNDS(paxel_cfg->height, AF_PAXEL_HEIGHT_MIN,
+                            AF_PAXEL_HEIGHT_MAX))
+               return -AF_ERR_HEIGHT;
+
+       /* Check width */
+       if (IS_OUT_OF_BOUNDS(paxel_cfg->width, AF_PAXEL_WIDTH_MIN,
+                            AF_PAXEL_WIDTH_MAX))
+               return -AF_ERR_WIDTH;
+
+       /* Check Line Increment */
+       if (IS_OUT_OF_BOUNDS(paxel_cfg->line_incr, AF_PAXEL_INCREMENT_MIN,
+                            AF_PAXEL_INCREMENT_MAX))
+               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))
+               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))
+               return -AF_ERR_VT_START;
+
+       /* Check IIR */
+       for (index = 0; index < AF_NUMBER_OF_COEF; index++) {
+               if ((iir_cfg->coeff_set0[index]) > AF_COEF_MAX)
+                       return -AF_ERR_IIR_COEF;
+
+               if ((iir_cfg->coeff_set1[index]) > AF_COEF_MAX)
+                       return -AF_ERR_IIR_COEF;
+       }
+
+       if (IS_OUT_OF_BOUNDS(iir_cfg->hz_start_pos, AF_IIRSH_MIN,
+                            AF_IIRSH_MAX))
+               return -AF_ERR_IIRSH;
+
+       /* Check HMF Threshold Values */
+       if (afconfig->hmf_config.threshold > AF_THRESHOLD_MAX)
+               return -AF_ERR_THRESHOLD;
+
+       return 0;
+}
+
+void isp_af_config_registers(struct isp_af_device *isp_af)
+{
+       struct device *dev = to_device(isp_af);
+       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;
+       unsigned long irqflags;
+
+       if (!isp_af->config.af_config)
+               return;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+
+       isp_reg_writel(dev, isp_af->buf_next->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+                      ISPH3A_AFBUFST);
+
+       if (!isp_af->update) {
+               spin_unlock_irqrestore(isp_af->lock, irqflags);
+               return;
+       }
+
+       /* Configure Hardware Registers */
+       pax1 |= isp_af->config.paxel_config.width << AF_PAXW_SHIFT;
+       /* Set height in AFPAX1 */
+       pax1 |= isp_af->config.paxel_config.height;
+       isp_reg_writel(dev, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
+
+       /* Configure AFPAX2 Register */
+       /* Set Line Increment in AFPAX2 Register */
+       pax2 |= isp_af->config.paxel_config.line_incr << AF_LINE_INCR_SHIFT;
+       /* Set Vertical Count */
+       pax2 |= isp_af->config.paxel_config.vt_cnt << AF_VT_COUNT_SHIFT;
+       /* Set Horizontal Count */
+       pax2 |= isp_af->config.paxel_config.hz_cnt;
+       isp_reg_writel(dev, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
+
+       /* Configure PAXSTART Register */
+       /*Configure Horizontal Start */
+       paxstart |= isp_af->config.paxel_config.hz_start << AF_HZ_START_SHIFT;
+       /* Configure Vertical Start */
+       paxstart |= isp_af->config.paxel_config.vt_start;
+       isp_reg_writel(dev, paxstart, OMAP3_ISP_IOMEM_H3A,
+                      ISPH3A_AFPAXSTART);
+
+       /*SetIIRSH Register */
+       isp_reg_writel(dev, isp_af->config.iir_config.hz_start_pos,
+                      OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
+
+       base_coef_set0 = ISPH3A_AFCOEF010;
+       base_coef_set1 = ISPH3A_AFCOEF110;
+       for (index = 0; index <= 8; index += 2) {
+               /*Set IIR Filter0 Coefficients */
+               coef = 0;
+               coef |= isp_af->config.iir_config.coeff_set0[index];
+               coef |= isp_af->config.iir_config.coeff_set0[index + 1] <<
+                       AF_COEF_SHIFT;
+               isp_reg_writel(dev, coef, OMAP3_ISP_IOMEM_H3A,
+                              base_coef_set0);
+               base_coef_set0 += AFCOEF_OFFSET;
+
+               /*Set IIR Filter1 Coefficients */
+               coef = 0;
+               coef |= isp_af->config.iir_config.coeff_set1[index];
+               coef |= isp_af->config.iir_config.coeff_set1[index + 1] <<
+                       AF_COEF_SHIFT;
+               isp_reg_writel(dev, coef, OMAP3_ISP_IOMEM_H3A,
+                              base_coef_set1);
+               base_coef_set1 += AFCOEF_OFFSET;
+       }
+       /* set AFCOEF0010 Register */
+       isp_reg_writel(dev, isp_af->config.iir_config.coeff_set0[10],
+                      OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
+       /* set AFCOEF1010 Register */
+       isp_reg_writel(dev, isp_af->config.iir_config.coeff_set1[10],
+                      OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
+
+       /* PCR Register */
+       /* Set Accumulator Mode */
+       if (isp_af->config.mode == ACCUMULATOR_PEAK)
+               pcr |= FVMODE;
+       /* Set A-law */
+       if (isp_af->config.alaw_enable == H3A_AF_ALAW_ENABLE)
+               pcr |= AF_ALAW_EN;
+       /* Set RGB Position */
+       pcr |= isp_af->config.rgb_pos << AF_RGBPOS_SHIFT;
+       /* HMF Configurations */
+       if (isp_af->config.hmf_config.enable == H3A_AF_HMF_ENABLE) {
+               /* Enable HMF */
+               pcr |= AF_MED_EN;
+               /* Set Median Threshold */
+               pcr |= isp_af->config.hmf_config.threshold << AF_MED_TH_SHIFT;
+       }
+       /* Set PCR Register */
+       isp_reg_and_or(dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+                      ~AF_PCR_MASK, pcr);
+
+       isp_af->update = 0;
+       isp_af->stat.config_counter++;
+       ispstat_bufs_set_size(&isp_af->stat, isp_af->buf_size);
+
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+}
+
+/* Update local parameters */
+static void isp_af_update_params(struct isp_af_device *isp_af,
+                                struct af_configuration *afconfig)
+{
+       int update = 0;
+       int index;
+
+       /* alaw */
+       if (isp_af->config.alaw_enable != afconfig->alaw_enable) {
+               update = 1;
+               goto out;
+       }
+
+       /* hmf */
+       if (isp_af->config.hmf_config.enable != afconfig->hmf_config.enable) {
+               update = 1;
+               goto out;
+       }
+       if (isp_af->config.hmf_config.threshold !=
+                                       afconfig->hmf_config.threshold) {
+               update = 1;
+               goto out;
+       }
+
+       /* rgbpos */
+       if (isp_af->config.rgb_pos != afconfig->rgb_pos) {
+               update = 1;
+               goto out;
+       }
+
+       /* iir */
+       if (isp_af->config.iir_config.hz_start_pos !=
+                                       afconfig->iir_config.hz_start_pos) {
+               update = 1;
+               goto out;
+       }
+       for (index = 0; index < AF_NUMBER_OF_COEF; index++) {
+               if (isp_af->config.iir_config.coeff_set0[index] !=
+                               afconfig->iir_config.coeff_set0[index]) {
+                       update = 1;
+                       goto out;
+               }
+               if (isp_af->config.iir_config.coeff_set1[index] !=
+                               afconfig->iir_config.coeff_set1[index]) {
+                       update = 1;
+                       goto out;
+               }
+       }
+
+       /* paxel */
+       if ((isp_af->config.paxel_config.width !=
+                               afconfig->paxel_config.width) ||
+           (isp_af->config.paxel_config.height !=
+                               afconfig->paxel_config.height) ||
+           (isp_af->config.paxel_config.hz_start !=
+                               afconfig->paxel_config.hz_start) ||
+           (isp_af->config.paxel_config.vt_start !=
+                               afconfig->paxel_config.vt_start) ||
+           (isp_af->config.paxel_config.hz_cnt !=
+                               afconfig->paxel_config.hz_cnt) ||
+           (isp_af->config.paxel_config.line_incr !=
+                               afconfig->paxel_config.line_incr)) {
+               update = 1;
+               goto out;
+       }
+
+       /* af_mode */
+       if (isp_af->config.mode != afconfig->mode) {
+               update = 1;
+               goto out;
+       }
+
+       isp_af->config.af_config = afconfig->af_config;
+
+out:
+       if (update) {
+               memcpy(&isp_af->config, afconfig, sizeof(*afconfig));
+               isp_af->update = 1;
+       }
+}
+
+void isp_af_try_enable(struct isp_af_device *isp_af)
+{
+       unsigned long irqflags;
+
+       if (!isp_af->config.af_config)
+               return;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+       if (unlikely(!isp_af->enabled && isp_af->config.af_config)) {
+               isp_af->update = 1;
+               isp_af->buf_next = ispstat_buf_next(&isp_af->stat);
+               spin_unlock_irqrestore(isp_af->lock, irqflags);
+               isp_af_config_registers(isp_af);
+               isp_af_enable(isp_af, 1);
+       } else
+               spin_unlock_irqrestore(isp_af->lock, irqflags);
+}
+
+/* Function to perform hardware set up */
+int isp_af_config(struct isp_af_device *isp_af,
+                 struct af_configuration *afconfig)
+{
+       struct device *dev = to_device(isp_af);
+       int result;
+       int buf_size;
+       unsigned long irqflags;
+
+       if (!afconfig) {
+               dev_dbg(dev, "af: Null argument in configuration.\n");
+               return -EINVAL;
+       }
+
+       /* Check Parameters */
+       spin_lock_irqsave(isp_af->lock, irqflags);
+       result = isp_af_check_params(isp_af, afconfig);
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+       if (result) {
+               dev_dbg(dev, "af: wrong configure params received.\n");
+               return result;
+       }
+
+       /* Compute buffer size */
+       buf_size = (afconfig->paxel_config.hz_cnt + 1) *
+                  (afconfig->paxel_config.vt_cnt + 1) * AF_PAXEL_SIZE;
+
+       result = ispstat_bufs_alloc(&isp_af->stat, buf_size, 0);
+       if (result)
+               return result;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+       isp_af->buf_size = buf_size;
+       isp_af_update_params(isp_af, afconfig);
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+
+       /* Success */
+       return 0;
+}
+EXPORT_SYMBOL(isp_af_config);
+
+/*
+ * 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_device *isp_af,
+                             struct isp_af_data *afdata)
+{
+       struct device *dev = to_device(isp_af);
+       struct ispstat_buffer *buf;
+
+       if (!isp_af->config.af_config) {
+               dev_dbg(dev, "af: statistics requested while af engine"
+                            " is not configured\n");
+               return -EINVAL;
+       }
+
+       if (afdata->update & REQUEST_STATISTICS) {
+               buf = ispstat_buf_get(&isp_af->stat,
+                             (void *)afdata->af_statistics_buf,
+                             afdata->frame_number);
+               if (IS_ERR(buf))
+                       return PTR_ERR(buf);
+
+               afdata->xtrastats.ts = buf->ts;
+               afdata->config_counter = buf->config_counter;
+               afdata->frame_number = buf->frame_number;
+
+               ispstat_buf_release(&isp_af->stat);
+       }
+
+       afdata->curr_frame = isp_af->stat.frame_number;
+
+       return 0;
+}
+EXPORT_SYMBOL(isp_af_request_statistics);
+
+/* This function will handle the AF buffer. */
+int isp_af_buf_process(struct isp_af_device *isp_af)
+{
+       if (likely(!isp_af->buf_err && isp_af->config.af_config)) {
+               int ret;
+
+               ret = ispstat_buf_queue(&isp_af->stat);
+               isp_af->buf_next = ispstat_buf_next(&isp_af->stat);
+               return ret;
+       } else {
+               isp_af->buf_err = 0;
+               return -1;
+       }
+}
+
+static void __isp_af_enable(struct isp_af_device *isp_af, int enable)
+{
+       struct device *dev = to_device(isp_af);
+       unsigned int pcr;
+
+       pcr = isp_reg_readl(dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+
+       /* Set AF_EN bit in PCR Register */
+       if (enable)
+               pcr |= AF_EN;
+       else
+               pcr &= ~AF_EN;
+
+       isp_reg_writel(dev, pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR);
+}
+
+/* Function to Enable/Disable AF Engine */
+void isp_af_enable(struct isp_af_device *isp_af, int enable)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+
+       if (!isp_af->config.af_config && enable) {
+               spin_unlock_irqrestore(isp_af->lock, irqflags);
+               return;
+       }
+
+       __isp_af_enable(isp_af, enable);
+       isp_af->enabled = enable;
+
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+}
+
+/* Function to Suspend AF Engine */
+void isp_af_suspend(struct isp_af_device *isp_af)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+       if (isp_af->enabled)
+               __isp_af_enable(isp_af, 0);
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+}
+
+/* Function to Resume AF Engine */
+void isp_af_resume(struct isp_af_device *isp_af)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(isp_af->lock, irqflags);
+       if (isp_af->enabled)
+               __isp_af_enable(isp_af, 1);
+       spin_unlock_irqrestore(isp_af->lock, irqflags);
+}
+
+int isp_af_busy(struct isp_af_device *isp_af)
+{
+       struct device *dev = to_device(isp_af);
+
+       return isp_reg_readl(dev, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
+               & ISPH3A_PCR_BUSYAF;
+}
+
+/* Function to register the AF character device driver. */
+int __init isp_af_init(struct device *dev)
+{
+       struct isp_device *isp = dev_get_drvdata(dev);
+       struct isp_af_device *isp_af = &isp->isp_af;
+
+       isp_af->lock = &isp->h3a_lock;
+       ispstat_init(dev, "AF", &isp_af->stat, H3A_MAX_BUFF, MAX_FRAME_COUNT);
+
+       return 0;
+}
+
+void isp_af_exit(struct device *dev)
+{
+       struct isp_device *isp = dev_get_drvdata(dev);
+
+       /* Free buffers */
+       ispstat_free(&isp->isp_af.stat);
+}