From 857d32bce873cc0a467e60f7f462c84594330730 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 8 Mar 2015 14:35:31 +0200 Subject: isp: Add histogram support Support enabling the histogram statistics engine and retrieving histogram data. Histogram data is currently ignored in the image quality tuning implementation. Signed-off-by: Laurent Pinchart --- iq.c | 9 +- iq.h | 2 + isp/omap3isp-priv.h | 1 + isp/omap3isp.h | 2 + isp/stats-priv.h | 18 ++++ isp/stats.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++------ isp/stats.h | 37 ++++++- live.c | 7 ++ snapshot.c | 7 ++ 9 files changed, 323 insertions(+), 34 deletions(-) diff --git a/iq.c b/iq.c index dd85cfc..41fa90d 100644 --- a/iq.c +++ b/iq.c @@ -94,6 +94,12 @@ void iq_aewb_process(struct iq_tuning *iq, omap3_isp_sensor_set_gain(iq->isp, iqp->gain); } +void iq_histogram_process(struct iq_tuning *iq __attribute__((__unused__)), + const struct omap3_isp_histogram_stats *stats __attribute__((__unused__))) +{ + printf("hist: data received\n"); +} + /** * iq_params_init - Initialize IQ parameters * @params: IQ parameters @@ -223,7 +229,8 @@ struct iq_tuning *iq_init(struct omap3_isp_device *isp, window.height = params->window.height * format.height; omap3_isp_aewb_configure(isp, &window, iq->pix_max - iq->params.black_level - 1); - omap3_isp_stats_enable(isp, true); + omap3_isp_histogram_configure(isp, &window); + omap3_isp_stats_enable(isp, OMAP3_ISP_STAT_AEWB | OMAP3_ISP_STAT_HIST); return iq; } diff --git a/iq.h b/iq.h index be13a9f..5b5217a 100644 --- a/iq.h +++ b/iq.h @@ -69,6 +69,8 @@ struct iq_params { void iq_aewb_process(struct iq_tuning *iq, const struct omap3_isp_aewb_stats *stats); +void iq_histogram_process(struct iq_tuning *iq, + const struct omap3_isp_histogram_stats *stats); void iq_params_init(struct iq_params *params); int iq_params_parse(struct iq_params *params, const char *arg); diff --git a/isp/omap3isp-priv.h b/isp/omap3isp-priv.h index a31f943..3a3eee7 100644 --- a/isp/omap3isp-priv.h +++ b/isp/omap3isp-priv.h @@ -177,6 +177,7 @@ struct omap3_isp_device { struct media_entity *entity; } resizer; struct omap3_isp_aewb aewb; + struct omap3_isp_histogram hist; struct omap3_isp_pipeline viewfinder; struct omap3_isp_pipeline snapshot; diff --git a/isp/omap3isp.h b/isp/omap3isp.h index 491dcdb..f31de3e 100644 --- a/isp/omap3isp.h +++ b/isp/omap3isp.h @@ -54,6 +54,8 @@ struct omap3_isp_operations { struct v4l2_video_buffer *buffer); void (*aewb_ready)(struct omap3_isp_device *isp, const struct omap3_isp_aewb_stats *stats); + void (*histogram_ready)(struct omap3_isp_device *isp, + const struct omap3_isp_histogram_stats *hist); 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 index c3f084c..43b920a 100644 --- a/isp/stats-priv.h +++ b/isp/stats-priv.h @@ -66,6 +66,24 @@ struct omap3_isp_aewb { bool enabled; }; +/* + * struct omap3_isp_histogram - OMAP3 ISP histogram statistics engine + * @entity: Histogram entity + * @size: buffer size + * @buffer: buffer memory + * @rect: histogram region of interest + * @enabled: statistics engine enabled + */ +struct omap3_isp_histogram { + struct media_entity *entity; + unsigned int size; + void *buffer; + + struct v4l2_rect rect; + + 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); diff --git a/isp/stats.c b/isp/stats.c index a96da5e..4ff11a9 100644 --- a/isp/stats.c +++ b/isp/stats.c @@ -278,32 +278,7 @@ int omap3_isp_aewb_configure(struct omap3_isp_device *isp, struct v4l2_rect *rec 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) +static int omap3_isp_aewb_start(struct omap3_isp_device *isp) { struct omap3_isp_aewb *aewb = &isp->aewb; struct v4l2_event_subscription esub; @@ -342,7 +317,7 @@ int omap3_isp_stats_start(struct omap3_isp_device *isp) return 0; } -void omap3_isp_stats_stop(struct omap3_isp_device *isp) +static void omap3_isp_aewb_stop(struct omap3_isp_device *isp) { struct omap3_isp_aewb *aewb = &isp->aewb; struct v4l2_event_subscription esub; @@ -359,7 +334,7 @@ void omap3_isp_stats_stop(struct omap3_isp_device *isp) ioctl(aewb->entity->fd, VIDIOC_UNSUBSCRIBE_EVENT, &esub); } -int omap3_isp_stats_init(struct omap3_isp_device *isp) +static int omap3_isp_aewb_init(struct omap3_isp_device *isp) { struct omap3_isp_aewb *aewb = &isp->aewb; @@ -370,7 +345,250 @@ int omap3_isp_stats_init(struct omap3_isp_device *isp) return v4l2_subdev_open(aewb->entity); } +/* ----------------------------------------------------------------------------- + * Histogram + */ + +static void omap3_isp_histogram_process(struct omap3_isp_device *isp, + void *buffer, + size_t size __attribute__((__unused__))) +{ + struct omap3_isp_histogram_stats stats; + + memset(&stats, 0, sizeof stats); + stats.nbins = 256; + stats.bins = (uint32_t *)buffer; + + isp->ops->histogram_ready(isp, &stats); +} + +static void omap3_isp_histogram_event(void *priv) +{ + struct omap3_isp_device *isp = priv; + struct omap3_isp_histogram *hist = &isp->hist; + 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(hist->entity->fd, VIDIOC_DQEVENT, &event); + if (ret < 0) { + printf("unable to retrieve histogram event: %s (%d).\n", + strerror(errno), errno); + return; + } + + if (status->buf_err) { + printf("histogram: stats error, skipping buffer.\n"); + return; + } + + memset(&data, 0, sizeof data); + data.buf = hist->buffer; + data.buf_size = hist->size; + + ret = ioctl(hist->entity->fd, VIDIOC_OMAP3ISP_STAT_REQ, &data); + if (ret < 0) { + printf("unable to retrieve histogram data: %s (%d).\n", + strerror(errno), errno); + return; + } + + omap3_isp_histogram_process(isp, data.buf, data.buf_size); +} + +static int omap3_isp_histogram_setup(struct omap3_isp_device *isp) +{ + struct omap3_isp_histogram *hist = &isp->hist; + struct omap3isp_hist_config config; + unsigned int buf_size; + int ret; + + memset(&config, 0, sizeof config); + config.num_acc_frames = 1; + config.hist_bins = OMAP3ISP_HIST_BINS_256; + config.cfa = OMAP3ISP_HIST_CFA_BAYER; + config.wg[0] = 1 << 5; + config.wg[1] = 1 << 5; + config.wg[2] = 1 << 5; + config.wg[3] = 1 << 5; + config.num_regions = 1; + config.region[0].h_start = hist->rect.left; + config.region[0].h_end = hist->rect.left + hist->rect.width - 1; + config.region[0].v_start = hist->rect.top; + config.region[0].v_end = hist->rect.top + hist->rect.height - 1; + + buf_size = OMAP3ISP_HIST_MEM_SIZE_BINS(config.hist_bins) + * config.num_regions; + config.buf_size = buf_size; + + ret = ioctl(hist->entity->fd, VIDIOC_OMAP3ISP_HIST_CFG, &config); + if (ret < 0) + return -errno; + + if (config.buf_size != buf_size) + printf("histogram: buf size was %u, is %u\n", buf_size, + config.buf_size); + + hist->size = config.buf_size; + hist->buffer = malloc(config.buf_size); + if (hist->buffer == NULL) + return -ENOMEM; + + return 0; +} + +int omap3_isp_histogram_configure(struct omap3_isp_device *isp, + struct v4l2_rect *rect) +{ + struct omap3_isp_histogram *hist = &isp->hist; + struct v4l2_mbus_framefmt format; + struct media_entity_pad *source; + int ret; + + source = media_entity_remote_source(&hist->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; + + /* 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; + + hist->rect = *rect; + + return 0; +} + +static int omap3_isp_histogram_start(struct omap3_isp_device *isp) +{ + struct omap3_isp_histogram *hist = &isp->hist; + struct v4l2_event_subscription esub; + unsigned long enable = 1; + int ret; + + if (!hist->enabled) + return 0; + + ret = omap3_isp_histogram_setup(isp); + if (ret < 0) { + printf("unable to configure histogram engine: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + memset(&esub, 0, sizeof esub); + esub.type = V4L2_EVENT_OMAP3ISP_HIST; + ret = ioctl(hist->entity->fd, VIDIOC_SUBSCRIBE_EVENT, &esub); + if (ret < 0) { + printf("unable to subscribe to histogram event: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + ret = ioctl(hist->entity->fd, VIDIOC_OMAP3ISP_STAT_EN, &enable); + if (ret < 0) { + printf("unable to start histogram engine: %s (%d).\n", + strerror(errno), errno); + return ret; + } + + isp->ops->watch_fd(hist->entity->fd, OMAP3_ISP_EVENT_EXCEPTION, + omap3_isp_histogram_event, isp); + + return 0; +} + +static void omap3_isp_histogram_stop(struct omap3_isp_device *isp) +{ + struct omap3_isp_histogram *hist = &isp->hist; + struct v4l2_event_subscription esub; + unsigned long enable = 0; + + if (!hist->enabled) + return; + + isp->ops->unwatch_fd(hist->entity->fd); + ioctl(hist->entity->fd, VIDIOC_OMAP3ISP_STAT_EN, &enable); + + memset(&esub, 0, sizeof esub); + esub.type = V4L2_EVENT_OMAP3ISP_HIST; + ioctl(hist->entity->fd, VIDIOC_UNSUBSCRIBE_EVENT, &esub); +} + +static int omap3_isp_histogram_init(struct omap3_isp_device *isp) +{ + struct omap3_isp_histogram *hist = &isp->hist; + + hist->entity = media_get_entity_by_name(isp->mdev, "OMAP3 ISP histogram"); + if (hist->entity == NULL) + return -ENOENT; + + return v4l2_subdev_open(hist->entity); +} + +/* ----------------------------------------------------------------------------- + * 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, + enum omap3_isp_stat_engine which) +{ + isp->aewb.enabled = !!(which & OMAP3_ISP_STAT_AEWB); + isp->hist.enabled = !!(which & OMAP3_ISP_STAT_HIST); +} + +int omap3_isp_stats_start(struct omap3_isp_device *isp) +{ + int ret; + + ret = omap3_isp_aewb_start(isp); + if (ret < 0) + return ret; + + return omap3_isp_histogram_start(isp); +} + +void omap3_isp_stats_stop(struct omap3_isp_device *isp) +{ + omap3_isp_aewb_stop(isp); + omap3_isp_histogram_stop(isp); +} + +int omap3_isp_stats_init(struct omap3_isp_device *isp) +{ + int ret; + + ret = omap3_isp_aewb_init(isp); + if (ret < 0) + return ret; + + return omap3_isp_histogram_init(isp); +} + void omap3_isp_stats_cleanup(struct omap3_isp_device *isp) { v4l2_subdev_close(isp->aewb.entity); + v4l2_subdev_close(isp->hist.entity); } diff --git a/isp/stats.h b/isp/stats.h index fb6c26a..f68d4fa 100644 --- a/isp/stats.h +++ b/isp/stats.h @@ -27,6 +27,11 @@ struct omap3_isp_device; +enum omap3_isp_stat_engine { + OMAP3_ISP_STAT_AEWB = 1 << 0, + OMAP3_ISP_STAT_HIST = 1 << 1, +}; + /* * struct omap3_isp_aewb_stats - OMAP3 ISP AEWB statistics * @npix: Total number of accumulated pixels @@ -51,16 +56,29 @@ struct omap3_isp_aewb_stats { uint32_t accum[8]; }; +/* + * struct omap3_isp_histogram_stats - OMAP3 ISP histogram statistics + * @nbins: Number of bins in the histogram + * @bins: Array of histogram frequency data + * + * Each bin entry is stored on 32 bits but limited to 20 bits. + */ +struct omap3_isp_histogram_stats { + unsigned int nbins; + uint32_t *bins; +}; + /* * omap3_isp_stats_enable - Enable or disable the statistics engine * @isp: The ISP device - * @enable: Whether to enable to disable the statistics engine + * @which: The statistics engine(s) to enable * - * 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. + * The statistics engines must be enabled prior to starting the video stream. + * When enabled, statistics will be computed for every frame and delivered + * through the ISP aewb_ready() and histogram_ready() callbacks. */ -void omap3_isp_stats_enable(struct omap3_isp_device *isp, bool enable); +void omap3_isp_stats_enable(struct omap3_isp_device *isp, + enum omap3_isp_stat_engine which); /* * omap3_isp_stats_get_format - Get frame format at the statistics engine input @@ -72,6 +90,7 @@ void omap3_isp_stats_enable(struct omap3_isp_device *isp, bool enable); */ 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 @@ -81,4 +100,12 @@ int omap3_isp_stats_get_format(struct omap3_isp_device *isp, int omap3_isp_aewb_configure(struct omap3_isp_device *isp, struct v4l2_rect *rect, unsigned int saturation); +/* + * omap3_isp_histogram_configure - Configure the histogram statistics engine + * @isp: The ISP device + * @rect: The region of interest, relative to the sensor output frame size + */ +int omap3_isp_histogram_configure(struct omap3_isp_device *isp, + struct v4l2_rect *rect); + #endif diff --git a/live.c b/live.c index 47e04d2..d4d14dc 100644 --- a/live.c +++ b/live.c @@ -103,6 +103,12 @@ static void __iq_aewb_process(struct omap3_isp_device *isp __attribute__((__unus iq_aewb_process(iq, stats); } +static void __iq_histogram_process(struct omap3_isp_device *isp __attribute__((__unused__)), + const struct omap3_isp_histogram_stats *stats) +{ + iq_histogram_process(iq, stats); +} + /* ----------------------------------------------------------------------------- * Events */ @@ -286,6 +292,7 @@ static struct omap3_isp_operations isp_ops = { .watch_fd = __events_watch_fd, .unwatch_fd = __events_unwatch_fd, .aewb_ready = __iq_aewb_process, + .histogram_ready = __iq_histogram_process, }; static int viewfinder_init(struct omap3_isp_device *isp, diff --git a/snapshot.c b/snapshot.c index 878d4bf..61e297f 100644 --- a/snapshot.c +++ b/snapshot.c @@ -119,6 +119,12 @@ static void __iq_aewb_process(struct omap3_isp_device *isp __attribute__((__unus iq_aewb_process(iq, stats); } +static void __iq_histogram_process(struct omap3_isp_device *isp __attribute__((__unused__)), + const struct omap3_isp_histogram_stats *stats) +{ + iq_histogram_process(iq, stats); +} + /* ----------------------------------------------------------------------------- * Events */ @@ -617,6 +623,7 @@ int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unu ops.watch_fd = __events_watch_fd; ops.unwatch_fd = __events_unwatch_fd; ops.aewb_ready = __iq_aewb_process; + ops.histogram_ready = __iq_histogram_process, isp = omap3_isp_open(MEDIA_DEVICE, &ops); if (isp == NULL) { -- cgit v1.2.3