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/Makefile | 1 + isp/omap3isp-priv.h | 2 + isp/omap3isp.c | 12 ++ isp/omap3isp.h | 3 + isp/stats-priv.h | 74 +++++++++++ isp/stats.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++ isp/stats.h | 84 ++++++++++++ 7 files changed, 551 insertions(+) create mode 100644 isp/stats-priv.h create mode 100644 isp/stats.c create mode 100644 isp/stats.h diff --git a/isp/Makefile b/isp/Makefile index 4b47ee6..6521a95 100644 --- a/isp/Makefile +++ b/isp/Makefile @@ -6,6 +6,7 @@ LDFLAGS := OBJECTS := controls.o \ media.o \ omap3isp.o \ + stats.o \ subdev.o \ v4l2.o \ v4l2-pool.o diff --git a/isp/omap3isp-priv.h b/isp/omap3isp-priv.h index 18fbf76..2f3dbb9 100644 --- a/isp/omap3isp-priv.h +++ b/isp/omap3isp-priv.h @@ -27,6 +27,7 @@ #include #include "omap3isp.h" +#include "stats-priv.h" #include "tools.h" struct omap3_isp_device; @@ -146,6 +147,7 @@ struct omap3_isp_device { struct media_entity *entity; struct omap3isp_prev_wbal wbal; } preview; + struct omap3_isp_aewb aewb; struct v4l2_mbus_framefmt sensor_format; diff --git a/isp/omap3isp.c b/isp/omap3isp.c index d1c74c1..f100254 100644 --- a/isp/omap3isp.c +++ b/isp/omap3isp.c @@ -947,6 +947,12 @@ struct omap3_isp_device *omap3_isp_open(const char *devname, goto error; } + ret = omap3_isp_stats_init(isp); + if (ret < 0) { + printf("error: unable to initialize statistics engine.\n"); + goto error; + } + ret = omap3_isp_preview_setup(isp); if (ret < 0) { printf("error: unable to setup preview engine.\n"); @@ -965,6 +971,7 @@ void omap3_isp_close(struct omap3_isp_device *isp) if (isp == NULL) return; + omap3_isp_stats_cleanup(isp); omap3_isp_pipeline_destroy(&isp->viewfinder); omap3_isp_pipeline_destroy(&isp->snapshot); media_close(isp->mdev); @@ -1030,6 +1037,9 @@ static int __omap3_isp_viewfinder_start(struct omap3_isp_device *isp) OMAP3_ISP_EVENT_READ, omap3_isp_viewfinder_event, isp); + /* Start the statistics engine. */ + omap3_isp_stats_start(isp); + ret = omap3_isp_video_start(isp->viewfinder.output); if (ret < 0) { printf("error: streamon failed for viewfinder (%d)\n", ret); @@ -1046,6 +1056,8 @@ static int __omap3_isp_viewfinder_stop(struct omap3_isp_device *isp) isp->ops->unwatch_fd(isp->viewfinder.output->video->fd); + omap3_isp_stats_stop(isp); + ret = omap3_isp_video_stop(isp->viewfinder.output); if (ret < 0) { printf("error: streamoff failed for viewfinder\n"); diff --git a/isp/omap3isp.h b/isp/omap3isp.h index 4e0588f..1e9489f 100644 --- a/isp/omap3isp.h +++ b/isp/omap3isp.h @@ -28,6 +28,7 @@ #include #include "media.h" +#include "stats.h" #include "v4l2.h" struct omap3_isp_device; @@ -49,6 +50,8 @@ struct omap3_isp_operations { struct v4l2_video_buffer *buffer); bool (*snapshot_ready)(struct omap3_isp_device *isp, struct v4l2_video_buffer *buffer); + void (*aewb_ready)(struct omap3_isp_device *isp, + const struct omap3_isp_aewb_stats *stats); void (*watch_fd)(int fd, enum omap3_isp_event_type type, void(*callback)(void *priv), void *priv); void (*unwatch_fd)(int fd); diff --git a/isp/stats-priv.h b/isp/stats-priv.h new file mode 100644 index 0000000..c3f084c --- /dev/null +++ b/isp/stats-priv.h @@ -0,0 +1,74 @@ +/* + * OMAP3 ISP library - OMAP3 ISP statistics private header + * + * 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 + */ + +#ifndef __OMAP3ISP_STATS_PRIV_H +#define __OMAP3ISP_STATS_PRIV_H + +#include + +#include +#include +#include + +#include "stats.h" + +/* + * struct omap3_isp_aewb - OMAP3 ISP AEWB statistics engine + * @entity: Automatic Exposure & White Balance entity + * @size: buffer size + * @buffer: buffer memory + * @rect: AEWB region of interest + * @win_x: horizontal coordinate of the left-most window + * @win_y: vertical coordinate of the top-most window + * @win_n_x: horizontal number of windows + * @win_n_y: vertical number of windows + * @win_w: window width + * @win_h: window height + * @win_inc_x: window horizontal increment + * @win_inc_y: window vertical increment + * @enabled: statistics engine enabled + */ +struct omap3_isp_aewb { + struct media_entity *entity; + unsigned int size; + void *buffer; + + unsigned int win_x; + unsigned int win_y; + unsigned int win_n_x; + unsigned int win_n_y; + unsigned int win_w; + unsigned int win_h; + unsigned int win_inc_x; + unsigned int win_inc_y; + + unsigned int saturation; + + bool enabled; +}; + +int omap3_isp_stats_start(struct omap3_isp_device *isp); +void omap3_isp_stats_stop(struct omap3_isp_device *isp); +int omap3_isp_stats_init(struct omap3_isp_device *isp); +void omap3_isp_stats_cleanup(struct omap3_isp_device *isp); + +#endif 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); +} diff --git a/isp/stats.h b/isp/stats.h new file mode 100644 index 0000000..fb6c26a --- /dev/null +++ b/isp/stats.h @@ -0,0 +1,84 @@ +/* + * 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 + */ + +#ifndef __OMAP3ISP_STATS_H +#define __OMAP3ISP_STATS_H + +#include + +struct omap3_isp_device; + +/* + * struct omap3_isp_aewb_stats - OMAP3 ISP AEWB statistics + * @npix: Total number of accumulated pixels + * @unsat: Number of accumulated unsaturated pixels + * @accum: Accumulators + * + * The first 4 accumulators store the accumulated pixel values for the 4 color + * components, organized as follows. + * + * +---+---+ + * | 0 | 1 | + * +---+---+ + * | 2 | 3 | + * +---+---+ + * + * The next 4 accumulators are similar, but store the accumulated saturated + * (clipped) pixel values. + */ +struct omap3_isp_aewb_stats { + uint32_t npix; + uint32_t unsat; + uint32_t accum[8]; +}; + +/* + * omap3_isp_stats_enable - Enable or disable the statistics engine + * @isp: The ISP device + * @enable: Whether to enable to disable the statistics engine + * + * The statistics engine must be enabled prior to starting the video stream. + * When enabled, it statistics will be computed for every frame and delivered + * through the ISP aewb_ready() callback. + */ +void omap3_isp_stats_enable(struct omap3_isp_device *isp, bool enable); + +/* + * omap3_isp_stats_get_format - Get frame format at the statistics engine input + * @isp: The ISP device + * @format: Frame format + * + * Fill the format argument with the frame format at the statistics engine + * input. + */ +int omap3_isp_stats_get_format(struct omap3_isp_device *isp, + struct v4l2_mbus_framefmt *format); +/* + * omap3_isp_aewb_configure - Configure the AEWB statistics engine + * @isp: The ISP device + * @rect: The region of interest, relative to the sensor output frame size + * @saturation: Pixel saturation threshold value + */ +int omap3_isp_aewb_configure(struct omap3_isp_device *isp, struct v4l2_rect *rect, + unsigned int saturation); + +#endif -- cgit v1.2.3