mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-30 08:04:13 +08:00
378e3f81cb
C libraries with 64-bit time_t use an incompatible format for struct omap3isp_stat_data. This changes the kernel code to support either version, by moving over the normal handling to the 64-bit variant, and adding compatiblity code to handle the old binary format with the existing ioctl command code. Fortunately, the command code includes the size of the structure, so the difference gets handled automatically. In the process of eliminating the references to 'struct timeval' from the kernel, I also change the way the timestamp is generated internally, basically by open-coding the v4l2_get_timestamp() call. [Sakari Ailus: Alphabetical order of headers, clean up compat code] Cc: Sakari Ailus <sakari.ailus@iki.fi> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
399 lines
11 KiB
C
399 lines
11 KiB
C
/*
|
|
* isph3a_af.c
|
|
*
|
|
* TI OMAP3 ISP - H3A AF module
|
|
*
|
|
* Copyright (C) 2010 Nokia Corporation
|
|
* Copyright (C) 2009 Texas Instruments, Inc.
|
|
*
|
|
* Contacts: David Cohen <dacohen@gmail.com>
|
|
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
|
* Sakari Ailus <sakari.ailus@iki.fi>
|
|
*
|
|
* This program 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.
|
|
*/
|
|
|
|
/* Linux specific include files */
|
|
#include <linux/device.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "isp.h"
|
|
#include "isph3a.h"
|
|
#include "ispstat.h"
|
|
|
|
#define IS_OUT_OF_BOUNDS(value, min, max) \
|
|
(((value) < (min)) || ((value) > (max)))
|
|
|
|
static void h3a_af_setup_regs(struct ispstat *af, void *priv)
|
|
{
|
|
struct omap3isp_h3a_af_config *conf = priv;
|
|
u32 pcr;
|
|
u32 pax1;
|
|
u32 pax2;
|
|
u32 paxstart;
|
|
u32 coef;
|
|
u32 base_coef_set0;
|
|
u32 base_coef_set1;
|
|
int index;
|
|
|
|
if (af->state == ISPSTAT_DISABLED)
|
|
return;
|
|
|
|
isp_reg_writel(af->isp, af->active_buf->dma_addr, OMAP3_ISP_IOMEM_H3A,
|
|
ISPH3A_AFBUFST);
|
|
|
|
if (!af->update)
|
|
return;
|
|
|
|
/* Configure Hardware Registers */
|
|
pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT;
|
|
/* Set height in AFPAX1 */
|
|
pax1 |= (conf->paxel.height >> 1) - 1;
|
|
isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
|
|
|
|
/* Configure AFPAX2 Register */
|
|
/* Set Line Increment in AFPAX2 Register */
|
|
pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT;
|
|
/* Set Vertical Count */
|
|
pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT;
|
|
/* Set Horizontal Count */
|
|
pax2 |= (conf->paxel.h_cnt - 1);
|
|
isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
|
|
|
|
/* Configure PAXSTART Register */
|
|
/*Configure Horizontal Start */
|
|
paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT;
|
|
/* Configure Vertical Start */
|
|
paxstart |= conf->paxel.v_start;
|
|
isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A,
|
|
ISPH3A_AFPAXSTART);
|
|
|
|
/*SetIIRSH Register */
|
|
isp_reg_writel(af->isp, conf->iir.h_start,
|
|
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 |= conf->iir.coeff_set0[index];
|
|
coef |= conf->iir.coeff_set0[index + 1] <<
|
|
AF_COEF_SHIFT;
|
|
isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
|
|
base_coef_set0);
|
|
base_coef_set0 += AFCOEF_OFFSET;
|
|
|
|
/*Set IIR Filter1 Coefficients */
|
|
coef = 0;
|
|
coef |= conf->iir.coeff_set1[index];
|
|
coef |= conf->iir.coeff_set1[index + 1] <<
|
|
AF_COEF_SHIFT;
|
|
isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
|
|
base_coef_set1);
|
|
base_coef_set1 += AFCOEF_OFFSET;
|
|
}
|
|
/* set AFCOEF0010 Register */
|
|
isp_reg_writel(af->isp, conf->iir.coeff_set0[10],
|
|
OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
|
|
/* set AFCOEF1010 Register */
|
|
isp_reg_writel(af->isp, conf->iir.coeff_set1[10],
|
|
OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
|
|
|
|
/* PCR Register */
|
|
/* Set RGB Position */
|
|
pcr = conf->rgb_pos << AF_RGBPOS_SHIFT;
|
|
/* Set Accumulator Mode */
|
|
if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK)
|
|
pcr |= AF_FVMODE;
|
|
/* Set A-law */
|
|
if (conf->alaw_enable)
|
|
pcr |= AF_ALAW_EN;
|
|
/* HMF Configurations */
|
|
if (conf->hmf.enable) {
|
|
/* Enable HMF */
|
|
pcr |= AF_MED_EN;
|
|
/* Set Median Threshold */
|
|
pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT;
|
|
}
|
|
/* Set PCR Register */
|
|
isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
|
|
AF_PCR_MASK, pcr);
|
|
|
|
af->update = 0;
|
|
af->config_counter += af->inc_config;
|
|
af->inc_config = 0;
|
|
af->buf_size = conf->buf_size;
|
|
}
|
|
|
|
static void h3a_af_enable(struct ispstat *af, int enable)
|
|
{
|
|
if (enable) {
|
|
isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
|
|
ISPH3A_PCR_AF_EN);
|
|
omap3isp_subclk_enable(af->isp, OMAP3_ISP_SUBCLK_AF);
|
|
} else {
|
|
isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
|
|
ISPH3A_PCR_AF_EN);
|
|
omap3isp_subclk_disable(af->isp, OMAP3_ISP_SUBCLK_AF);
|
|
}
|
|
}
|
|
|
|
static int h3a_af_busy(struct ispstat *af)
|
|
{
|
|
return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
|
|
& ISPH3A_PCR_BUSYAF;
|
|
}
|
|
|
|
static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf)
|
|
{
|
|
return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE;
|
|
}
|
|
|
|
/* Function to check paxel parameters */
|
|
static int h3a_af_validate_params(struct ispstat *af, void *new_conf)
|
|
{
|
|
struct omap3isp_h3a_af_config *user_cfg = new_conf;
|
|
struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel;
|
|
struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir;
|
|
int index;
|
|
u32 buf_size;
|
|
|
|
/* Check horizontal Count */
|
|
if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt,
|
|
OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN,
|
|
OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX))
|
|
return -EINVAL;
|
|
|
|
/* Check Vertical Count */
|
|
if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt,
|
|
OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN,
|
|
OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX))
|
|
return -EINVAL;
|
|
|
|
if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN,
|
|
OMAP3ISP_AF_PAXEL_HEIGHT_MAX) ||
|
|
paxel_cfg->height % 2)
|
|
return -EINVAL;
|
|
|
|
/* Check width */
|
|
if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN,
|
|
OMAP3ISP_AF_PAXEL_WIDTH_MAX) ||
|
|
paxel_cfg->width % 2)
|
|
return -EINVAL;
|
|
|
|
/* Check Line Increment */
|
|
if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc,
|
|
OMAP3ISP_AF_PAXEL_INCREMENT_MIN,
|
|
OMAP3ISP_AF_PAXEL_INCREMENT_MAX) ||
|
|
paxel_cfg->line_inc % 2)
|
|
return -EINVAL;
|
|
|
|
/* Check Horizontal Start */
|
|
if ((paxel_cfg->h_start < iir_cfg->h_start) ||
|
|
IS_OUT_OF_BOUNDS(paxel_cfg->h_start,
|
|
OMAP3ISP_AF_PAXEL_HZSTART_MIN,
|
|
OMAP3ISP_AF_PAXEL_HZSTART_MAX))
|
|
return -EINVAL;
|
|
|
|
/* Check IIR */
|
|
for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
|
|
if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX)
|
|
return -EINVAL;
|
|
|
|
if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX)
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN,
|
|
OMAP3ISP_AF_IIRSH_MAX))
|
|
return -EINVAL;
|
|
|
|
/* Hack: If paxel size is 12, the 10th AF window may be corrupted */
|
|
if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) &&
|
|
(paxel_cfg->width * paxel_cfg->height == 12))
|
|
return -EINVAL;
|
|
|
|
buf_size = h3a_af_get_buf_size(user_cfg);
|
|
if (buf_size > user_cfg->buf_size)
|
|
/* User buf_size request wasn't enough */
|
|
user_cfg->buf_size = buf_size;
|
|
else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE)
|
|
user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update local parameters */
|
|
static void h3a_af_set_params(struct ispstat *af, void *new_conf)
|
|
{
|
|
struct omap3isp_h3a_af_config *user_cfg = new_conf;
|
|
struct omap3isp_h3a_af_config *cur_cfg = af->priv;
|
|
int update = 0;
|
|
int index;
|
|
|
|
/* alaw */
|
|
if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* hmf */
|
|
if (cur_cfg->hmf.enable != user_cfg->hmf.enable) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* rgbpos */
|
|
if (cur_cfg->rgb_pos != user_cfg->rgb_pos) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* iir */
|
|
if (cur_cfg->iir.h_start != user_cfg->iir.h_start) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
|
|
if (cur_cfg->iir.coeff_set0[index] !=
|
|
user_cfg->iir.coeff_set0[index]) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
if (cur_cfg->iir.coeff_set1[index] !=
|
|
user_cfg->iir.coeff_set1[index]) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* paxel */
|
|
if ((cur_cfg->paxel.width != user_cfg->paxel.width) ||
|
|
(cur_cfg->paxel.height != user_cfg->paxel.height) ||
|
|
(cur_cfg->paxel.h_start != user_cfg->paxel.h_start) ||
|
|
(cur_cfg->paxel.v_start != user_cfg->paxel.v_start) ||
|
|
(cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) ||
|
|
(cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) ||
|
|
(cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) {
|
|
update = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* af_mode */
|
|
if (cur_cfg->fvmode != user_cfg->fvmode)
|
|
update = 1;
|
|
|
|
out:
|
|
if (update || !af->configured) {
|
|
memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg));
|
|
af->inc_config++;
|
|
af->update = 1;
|
|
/*
|
|
* User might be asked for a bigger buffer than necessary for
|
|
* this configuration. In order to return the right amount of
|
|
* data during buffer request, let's calculate the size here
|
|
* instead of stick with user_cfg->buf_size.
|
|
*/
|
|
cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg);
|
|
}
|
|
}
|
|
|
|
static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|
{
|
|
struct ispstat *stat = v4l2_get_subdevdata(sd);
|
|
|
|
switch (cmd) {
|
|
case VIDIOC_OMAP3ISP_AF_CFG:
|
|
return omap3isp_stat_config(stat, arg);
|
|
case VIDIOC_OMAP3ISP_STAT_REQ:
|
|
return omap3isp_stat_request_statistics(stat, arg);
|
|
case VIDIOC_OMAP3ISP_STAT_REQ_TIME32:
|
|
return omap3isp_stat_request_statistics_time32(stat, arg);
|
|
case VIDIOC_OMAP3ISP_STAT_EN: {
|
|
int *en = arg;
|
|
return omap3isp_stat_enable(stat, !!*en);
|
|
}
|
|
}
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
static const struct ispstat_ops h3a_af_ops = {
|
|
.validate_params = h3a_af_validate_params,
|
|
.set_params = h3a_af_set_params,
|
|
.setup_regs = h3a_af_setup_regs,
|
|
.enable = h3a_af_enable,
|
|
.busy = h3a_af_busy,
|
|
};
|
|
|
|
static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = {
|
|
.ioctl = h3a_af_ioctl,
|
|
.subscribe_event = omap3isp_stat_subscribe_event,
|
|
.unsubscribe_event = omap3isp_stat_unsubscribe_event,
|
|
};
|
|
|
|
static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = {
|
|
.s_stream = omap3isp_stat_s_stream,
|
|
};
|
|
|
|
static const struct v4l2_subdev_ops h3a_af_subdev_ops = {
|
|
.core = &h3a_af_subdev_core_ops,
|
|
.video = &h3a_af_subdev_video_ops,
|
|
};
|
|
|
|
/* Function to register the AF character device driver. */
|
|
int omap3isp_h3a_af_init(struct isp_device *isp)
|
|
{
|
|
struct ispstat *af = &isp->isp_af;
|
|
struct omap3isp_h3a_af_config *af_cfg;
|
|
struct omap3isp_h3a_af_config *af_recover_cfg;
|
|
|
|
af_cfg = devm_kzalloc(isp->dev, sizeof(*af_cfg), GFP_KERNEL);
|
|
if (af_cfg == NULL)
|
|
return -ENOMEM;
|
|
|
|
af->ops = &h3a_af_ops;
|
|
af->priv = af_cfg;
|
|
af->event_type = V4L2_EVENT_OMAP3ISP_AF;
|
|
af->isp = isp;
|
|
|
|
/* Set recover state configuration */
|
|
af_recover_cfg = devm_kzalloc(isp->dev, sizeof(*af_recover_cfg),
|
|
GFP_KERNEL);
|
|
if (!af_recover_cfg) {
|
|
dev_err(af->isp->dev,
|
|
"AF: cannot allocate memory for recover configuration.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
|
|
af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN;
|
|
af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN;
|
|
af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN;
|
|
af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN;
|
|
af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN;
|
|
if (h3a_af_validate_params(af, af_recover_cfg)) {
|
|
dev_err(af->isp->dev,
|
|
"AF: recover configuration is invalid.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
|
|
af->recover_priv = af_recover_cfg;
|
|
|
|
return omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
|
|
}
|
|
|
|
void omap3isp_h3a_af_cleanup(struct isp_device *isp)
|
|
{
|
|
omap3isp_stat_cleanup(&isp->isp_af);
|
|
}
|