From 394b4755f7951c7c598b3162a98772677ee9dace Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Feb 2012 15:01:45 +0100 Subject: stats: Add support for the AEWB statistics engine Signed-off-by: Laurent Pinchart --- isp/stats.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 isp/stats.c (limited to 'isp/stats.c') diff --git a/isp/stats.c b/isp/stats.c new file mode 100644 index 0000000..e4543af --- /dev/null +++ b/isp/stats.c @@ -0,0 +1,375 @@ +/* + * OMAP3 ISP library - OMAP3 ISP statistics + * + * Copyright (C) 2010-2012 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This library 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 Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "omap3isp.h" +#include "omap3isp-priv.h" +#include "stats.h" +#include "subdev.h" +#include "tools.h" + +/* ----------------------------------------------------------------------------- + * Automatic Exposure and White Balance + */ + +static void omap3_isp_aewb_process(struct omap3_isp_device *isp, void *buffer, + size_t size __attribute__((__unused__))) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct omap3_isp_aewb_stats stats; + uint16_t *data = buffer; + unsigned int windows; + unsigned int i, j; + + memset(&stats, 0, sizeof stats); + + /* Number of windows, exclusing black row. */ + windows = aewb->win_n_x * aewb->win_n_y; + + for (i = 0; i < windows; ++i) { + for (j = 0; j < 8; ++j) + stats.accum[j] += *data++; + + if (i % 8 == 7) { + for (j = 0; j < 8; ++j) + stats.unsat += *data++; + } + } + + if (windows % 8) { + /* Skip black row windows up to the next boundary of 8 + * windows. + */ + data += min(8 - windows % 8, aewb->win_n_x) * 8; + for (j = 0; j < windows % 8; ++j) + stats.unsat += *data++; + } + + stats.npix = div_round_up(aewb->win_w, aewb->win_inc_x) * aewb->win_n_x + * div_round_up(aewb->win_h, aewb->win_inc_y) * aewb->win_n_y; + + isp->ops->aewb_ready(isp, &stats); +} + +static void omap3_isp_aewb_event(void *priv) +{ + struct omap3_isp_device *isp = priv; + struct omap3_isp_aewb *aewb = &isp->aewb; + struct omap3isp_stat_data data; + struct v4l2_event event; + struct omap3isp_stat_event_status *status = + (struct omap3isp_stat_event_status *)event.u.data; + int ret; + + memset(&event, 0, sizeof event); + ret = ioctl(aewb->entity->fd, VIDIOC_DQEVENT, &event); + if (ret < 0) { + printf("unable to retrieve AEWB event: %s (%d).\n", + strerror(errno), errno); + return; + } + + if (status->buf_err) { + printf("AEWB: stats error, skipping buffer.\n"); + return; + } + + memset(&data, 0, sizeof data); + data.buf = aewb->buffer; + data.buf_size = aewb->size; + + ret = ioctl(aewb->entity->fd, VIDIOC_OMAP3ISP_STAT_REQ, &data); + if (ret < 0) { + printf("unable to retrieve AEWB data: %s (%d).\n", + strerror(errno), errno); + return; + } + + omap3_isp_aewb_process(isp, data.buf, data.buf_size); +} + +static const struct format_info { + __u32 code; + unsigned int bpp; +} formats[] = { + { V4L2_MBUS_FMT_SBGGR8_1X8, 8, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, 8, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, 8, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, }, +}; + +static int omap3_isp_aewb_setup(struct omap3_isp_device *isp) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct omap3isp_h3a_aewb_config config; + unsigned int buf_size; + int ret; + + memset(&config, 0, sizeof config); + config.saturation_limit = aewb->saturation; + config.win_width = aewb->win_w; + config.win_height = aewb->win_h; + config.hor_win_count = aewb->win_n_x; + config.ver_win_count = aewb->win_n_y; + config.hor_win_start = aewb->win_x; + config.ver_win_start = aewb->win_y; + config.blk_win_height = 2; + config.blk_ver_win_start = aewb->win_y + aewb->win_h * (aewb->win_n_y - 1) + 2; + config.subsample_hor_inc = aewb->win_inc_x; + config.subsample_ver_inc = aewb->win_inc_y; + config.alaw_enable = 0; + + buf_size = (aewb->win_n_x * aewb->win_n_y + + (aewb->win_n_x * aewb->win_n_y + 7) / 8 + + aewb->win_n_x + (aewb->win_n_x + 7) / 8) + * 16; + config.buf_size = buf_size; + + ret = ioctl(aewb->entity->fd, VIDIOC_OMAP3ISP_AEWB_CFG, &config); + if (ret < 0) + return -errno; + + if (config.buf_size != buf_size) + printf("AEWB: buf size was %u, is %u\n", buf_size, + config.buf_size); + + aewb->size = config.buf_size; + aewb->buffer = malloc(config.buf_size); + if (aewb->buffer == NULL) + return -ENOMEM; + + return 0; +} + +int omap3_isp_aewb_configure(struct omap3_isp_device *isp, struct v4l2_rect *rect, + unsigned int saturation) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct v4l2_mbus_framefmt format; + struct media_entity_pad *source; + unsigned int win_n_x = 10; + unsigned int win_n_y = 7; + unsigned int win_inc_x; + unsigned int win_inc_y; + unsigned int win_h; + unsigned int win_w; + unsigned int win_x; + unsigned int win_y; + unsigned int bpp = 0; + unsigned int i; + int ret; + + source = media_entity_remote_source(&aewb->entity->pads[0]); + if (source == NULL) + return -ENOENT; + + ret = v4l2_subdev_get_format(source->entity, &format, source->index, + V4L2_SUBDEV_FORMAT_TRY); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) { + if (formats[i].code == format.code) { + bpp = formats[i].bpp; + break; + } + } + + if (bpp == 0) + return -EINVAL; + + /* Validate the requested rectangle. */ + if (rect->left < 0 || rect->left + rect->width > (int)format.width || + rect->top < 0 || rect->top + rect->height > (int)format.height) + return -ERANGE; + + /* Window width and height are computed by dividing the frame width and + * height by the number of windows horizontally and vertically. Make + * sure they fall in the admissible ranges by modifying the number of + * windows is necessary. Finally, clamp the number of windows to the + * admissible range. + */ + win_w = (rect->width / win_n_x) & ~1; + win_w = clamp_t(unsigned int, win_w, OMAP3ISP_AEWB_MIN_WIN_W, + OMAP3ISP_AEWB_MAX_WIN_W); + win_n_x = rect->width / win_w; + win_n_x = clamp_t(unsigned int, win_n_x, OMAP3ISP_AEWB_MIN_WINHC, + OMAP3ISP_AEWB_MAX_WINHC); + + win_h = (rect->height / win_n_y) & ~1; + win_h = clamp_t(unsigned int, win_h, OMAP3ISP_AEWB_MIN_WIN_H, + OMAP3ISP_AEWB_MAX_WIN_H); + win_n_y = rect->height / win_h; + win_n_y = clamp_t(unsigned int, win_n_y, OMAP3ISP_AEWB_MIN_WINVC, + OMAP3ISP_AEWB_MAX_WINVC); + + /* Accumulators are 16-bit registers. To avoid overflows limit the + * number of pixels to 64 (for 10-bit formats) or 256 (for 8-bit + * formats) by increasing the horizontal and vertical increments. + */ + win_inc_x = 2; + win_inc_y = 2; + + while ((win_w / win_inc_x) * (win_h / win_inc_y) > 1U << (16 - bpp)) { + if (win_inc_x <= win_inc_y) + win_inc_x += 2; + else + win_inc_y += 2; + } + + /* Center the windows in the image area. The black row will be + * positionned at the end of the frame. + */ + win_x = (format.width - win_w * win_n_x) / 2; + win_y = (format.height - win_h * win_n_y) / 2; + + printf("AEWB: #win %ux%u start %ux%u size %ux%u inc %ux%u\n", + win_n_x, win_n_y, win_x, win_y, win_w, win_h, + win_inc_x, win_inc_y); + + aewb->win_x = win_x; + aewb->win_y = win_y; + aewb->win_n_x = win_n_x; + aewb->win_n_y = win_n_y; + aewb->win_w = win_w; + aewb->win_h = win_h; + aewb->win_inc_x = win_inc_x; + aewb->win_inc_y = win_inc_y; + + aewb->saturation = saturation; + + rect->width = win_n_x * win_w; + rect->height = win_n_y * win_h; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Start/stop, init/cleanup + */ + +int omap3_isp_stats_get_format(struct omap3_isp_device *isp, + struct v4l2_mbus_framefmt *format) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct media_entity_pad *source; + + source = media_entity_remote_source(&aewb->entity->pads[0]); + if (source == NULL) + return -ENOENT; + + return v4l2_subdev_get_format(source->entity, format, source->index, + V4L2_SUBDEV_FORMAT_TRY); +} + +void omap3_isp_stats_enable(struct omap3_isp_device *isp, bool enable) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + + aewb->enabled = enable; +} + +int omap3_isp_stats_start(struct omap3_isp_device *isp) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct v4l2_event_subscription esub; + unsigned long enable = 1; + int ret; + + if (!aewb->enabled) + return 0; + + ret = omap3_isp_aewb_setup(isp); + if (ret < 0) { + printf("unable to configure AEWB engine: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + memset(&esub, 0, sizeof esub); + esub.type = V4L2_EVENT_OMAP3ISP_AEWB; + ret = ioctl(aewb->entity->fd, VIDIOC_SUBSCRIBE_EVENT, &esub); + if (ret < 0) { + printf("unable to subscribe to AEWB event: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + ret = ioctl(aewb->entity->fd, VIDIOC_OMAP3ISP_STAT_EN, &enable); + if (ret < 0) { + printf("unable to start AEWB engine: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + isp->ops->watch_fd(aewb->entity->fd, OMAP3_ISP_EVENT_EXCEPTION, + omap3_isp_aewb_event, isp); + + return 0; +} + +void omap3_isp_stats_stop(struct omap3_isp_device *isp) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + struct v4l2_event_subscription esub; + unsigned long enable = 0; + + if (!aewb->enabled) + return; + + isp->ops->unwatch_fd(aewb->entity->fd); + ioctl(aewb->entity->fd, VIDIOC_OMAP3ISP_STAT_EN, &enable); + + memset(&esub, 0, sizeof esub); + esub.type = V4L2_EVENT_OMAP3ISP_AEWB; + ioctl(aewb->entity->fd, VIDIOC_UNSUBSCRIBE_EVENT, &esub); +} + +int omap3_isp_stats_init(struct omap3_isp_device *isp) +{ + struct omap3_isp_aewb *aewb = &isp->aewb; + + aewb->entity = media_get_entity_by_name(isp->mdev, "OMAP3 ISP AEWB"); + if (aewb->entity == NULL) + return -ENOENT; + + return v4l2_subdev_open(aewb->entity); +} + +void omap3_isp_stats_cleanup(struct omap3_isp_device *isp) +{ + v4l2_subdev_close(isp->aewb.entity); +} -- cgit v1.2.3