18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_histo.c -- R-Car VSP1 Histogram API 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Laurent Pinchart 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/gfp.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 168c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "vsp1.h" 198c2ecf20Sopenharmony_ci#include "vsp1_histo.h" 208c2ecf20Sopenharmony_ci#include "vsp1_pipe.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define HISTO_MIN_SIZE 4U 238c2ecf20Sopenharmony_ci#define HISTO_MAX_SIZE 8192U 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 268c2ecf20Sopenharmony_ci * Buffer Operations 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline struct vsp1_histogram_buffer * 308c2ecf20Sopenharmony_cito_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return container_of(vbuf, struct vsp1_histogram_buffer, buf); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct vsp1_histogram_buffer * 368c2ecf20Sopenharmony_civsp1_histogram_buffer_get(struct vsp1_histogram *histo) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf = NULL; 398c2ecf20Sopenharmony_ci unsigned long flags; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci spin_lock_irqsave(&histo->irqlock, flags); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (list_empty(&histo->irqqueue)) 448c2ecf20Sopenharmony_ci goto done; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer, 478c2ecf20Sopenharmony_ci queue); 488c2ecf20Sopenharmony_ci list_del(&buf->queue); 498c2ecf20Sopenharmony_ci histo->readout = true; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cidone: 528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&histo->irqlock, flags); 538c2ecf20Sopenharmony_ci return buf; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_civoid vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, 578c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf, 588c2ecf20Sopenharmony_ci size_t size) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe = histo->entity.pipe; 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * The pipeline pointer is guaranteed to be valid as this function is 658c2ecf20Sopenharmony_ci * called from the frame completion interrupt handler, which can only 668c2ecf20Sopenharmony_ci * occur when video streaming is active. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci buf->buf.sequence = pipe->sequence; 698c2ecf20Sopenharmony_ci buf->buf.vb2_buf.timestamp = ktime_get_ns(); 708c2ecf20Sopenharmony_ci vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); 718c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci spin_lock_irqsave(&histo->irqlock, flags); 748c2ecf20Sopenharmony_ci histo->readout = false; 758c2ecf20Sopenharmony_ci wake_up(&histo->wait_queue); 768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&histo->irqlock, flags); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 808c2ecf20Sopenharmony_ci * videobuf2 Queue Operations 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int histo_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 848c2ecf20Sopenharmony_ci unsigned int *nplanes, unsigned int sizes[], 858c2ecf20Sopenharmony_ci struct device *alloc_devs[]) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vb2_get_drv_priv(vq); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (*nplanes) { 908c2ecf20Sopenharmony_ci if (*nplanes != 1) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (sizes[0] < histo->data_size) 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci *nplanes = 1; 1008c2ecf20Sopenharmony_ci sizes[0] = histo->data_size; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int histo_buffer_prepare(struct vb2_buffer *vb) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1088c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); 1098c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (vb->num_planes != 1) 1128c2ecf20Sopenharmony_ci return -EINVAL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < histo->data_size) 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci buf->addr = vb2_plane_vaddr(vb, 0); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void histo_buffer_queue(struct vb2_buffer *vb) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1258c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); 1268c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); 1278c2ecf20Sopenharmony_ci unsigned long flags; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci spin_lock_irqsave(&histo->irqlock, flags); 1308c2ecf20Sopenharmony_ci list_add_tail(&buf->queue, &histo->irqqueue); 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&histo->irqlock, flags); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int histo_start_streaming(struct vb2_queue *vq, unsigned int count) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void histo_stop_streaming(struct vb2_queue *vq) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vb2_get_drv_priv(vq); 1428c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buffer; 1438c2ecf20Sopenharmony_ci unsigned long flags; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&histo->irqlock, flags); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Remove all buffers from the IRQ queue. */ 1488c2ecf20Sopenharmony_ci list_for_each_entry(buffer, &histo->irqqueue, queue) 1498c2ecf20Sopenharmony_ci vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); 1508c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&histo->irqqueue); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Wait for the buffer being read out (if any) to complete. */ 1538c2ecf20Sopenharmony_ci wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&histo->irqlock, flags); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct vb2_ops histo_video_queue_qops = { 1598c2ecf20Sopenharmony_ci .queue_setup = histo_queue_setup, 1608c2ecf20Sopenharmony_ci .buf_prepare = histo_buffer_prepare, 1618c2ecf20Sopenharmony_ci .buf_queue = histo_buffer_queue, 1628c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 1638c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 1648c2ecf20Sopenharmony_ci .start_streaming = histo_start_streaming, 1658c2ecf20Sopenharmony_ci .stop_streaming = histo_stop_streaming, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1698c2ecf20Sopenharmony_ci * V4L2 Subdevice Operations 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int histo_enum_mbus_code(struct v4l2_subdev *subdev, 1738c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1748c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (code->pad == HISTO_PAD_SOURCE) { 1798c2ecf20Sopenharmony_ci code->code = MEDIA_BUS_FMT_FIXED; 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return vsp1_subdev_enum_mbus_code(subdev, cfg, code, histo->formats, 1848c2ecf20Sopenharmony_ci histo->num_formats); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int histo_enum_frame_size(struct v4l2_subdev *subdev, 1888c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1898c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci if (fse->pad != HISTO_PAD_SINK) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HISTO_MIN_SIZE, 1958c2ecf20Sopenharmony_ci HISTO_MIN_SIZE, HISTO_MAX_SIZE, 1968c2ecf20Sopenharmony_ci HISTO_MAX_SIZE); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int histo_get_selection(struct v4l2_subdev *subdev, 2008c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2018c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 2048c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config; 2058c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 2068c2ecf20Sopenharmony_ci struct v4l2_rect *crop; 2078c2ecf20Sopenharmony_ci int ret = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (sel->pad != HISTO_PAD_SINK) 2108c2ecf20Sopenharmony_ci return -EINVAL; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci mutex_lock(&histo->entity.lock); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which); 2158c2ecf20Sopenharmony_ci if (!config) { 2168c2ecf20Sopenharmony_ci ret = -EINVAL; 2178c2ecf20Sopenharmony_ci goto done; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci switch (sel->target) { 2218c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 2228c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 2238c2ecf20Sopenharmony_ci crop = vsp1_entity_get_pad_selection(&histo->entity, config, 2248c2ecf20Sopenharmony_ci HISTO_PAD_SINK, 2258c2ecf20Sopenharmony_ci V4L2_SEL_TGT_CROP); 2268c2ecf20Sopenharmony_ci sel->r.left = 0; 2278c2ecf20Sopenharmony_ci sel->r.top = 0; 2288c2ecf20Sopenharmony_ci sel->r.width = crop->width; 2298c2ecf20Sopenharmony_ci sel->r.height = crop->height; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 2338c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 2348c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&histo->entity, config, 2358c2ecf20Sopenharmony_ci HISTO_PAD_SINK); 2368c2ecf20Sopenharmony_ci sel->r.left = 0; 2378c2ecf20Sopenharmony_ci sel->r.top = 0; 2388c2ecf20Sopenharmony_ci sel->r.width = format->width; 2398c2ecf20Sopenharmony_ci sel->r.height = format->height; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 2438c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 2448c2ecf20Sopenharmony_ci sel->r = *vsp1_entity_get_pad_selection(&histo->entity, config, 2458c2ecf20Sopenharmony_ci sel->pad, sel->target); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci default: 2498c2ecf20Sopenharmony_ci ret = -EINVAL; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cidone: 2548c2ecf20Sopenharmony_ci mutex_unlock(&histo->entity.lock); 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int histo_set_crop(struct v4l2_subdev *subdev, 2598c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config, 2608c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 2638c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 2648c2ecf20Sopenharmony_ci struct v4l2_rect *selection; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* The crop rectangle must be inside the input frame. */ 2678c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&histo->entity, config, 2688c2ecf20Sopenharmony_ci HISTO_PAD_SINK); 2698c2ecf20Sopenharmony_ci sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); 2708c2ecf20Sopenharmony_ci sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); 2718c2ecf20Sopenharmony_ci sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, 2728c2ecf20Sopenharmony_ci format->width - sel->r.left); 2738c2ecf20Sopenharmony_ci sel->r.height = clamp_t(unsigned int, sel->r.height, HISTO_MIN_SIZE, 2748c2ecf20Sopenharmony_ci format->height - sel->r.top); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Set the crop rectangle and reset the compose rectangle. */ 2778c2ecf20Sopenharmony_ci selection = vsp1_entity_get_pad_selection(&histo->entity, config, 2788c2ecf20Sopenharmony_ci sel->pad, V4L2_SEL_TGT_CROP); 2798c2ecf20Sopenharmony_ci *selection = sel->r; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci selection = vsp1_entity_get_pad_selection(&histo->entity, config, 2828c2ecf20Sopenharmony_ci sel->pad, 2838c2ecf20Sopenharmony_ci V4L2_SEL_TGT_COMPOSE); 2848c2ecf20Sopenharmony_ci *selection = sel->r; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int histo_set_compose(struct v4l2_subdev *subdev, 2908c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config, 2918c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 2948c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 2958c2ecf20Sopenharmony_ci struct v4l2_rect *crop; 2968c2ecf20Sopenharmony_ci unsigned int ratio; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* 2998c2ecf20Sopenharmony_ci * The compose rectangle is used to configure downscaling, the top left 3008c2ecf20Sopenharmony_ci * corner is fixed to (0,0) and the size to 1/2 or 1/4 of the crop 3018c2ecf20Sopenharmony_ci * rectangle. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci sel->r.left = 0; 3048c2ecf20Sopenharmony_ci sel->r.top = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci crop = vsp1_entity_get_pad_selection(&histo->entity, config, sel->pad, 3078c2ecf20Sopenharmony_ci V4L2_SEL_TGT_CROP); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * Clamp the width and height to acceptable values first and then 3118c2ecf20Sopenharmony_ci * compute the closest rounded dividing ratio. 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * Ratio Rounded ratio 3148c2ecf20Sopenharmony_ci * -------------------------- 3158c2ecf20Sopenharmony_ci * [1.0 1.5[ 1 3168c2ecf20Sopenharmony_ci * [1.5 3.0[ 2 3178c2ecf20Sopenharmony_ci * [3.0 4.0] 4 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * The rounded ratio can be computed using 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * 1 << (ceil(ratio * 2) / 3) 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci sel->r.width = clamp(sel->r.width, crop->width / 4, crop->width); 3248c2ecf20Sopenharmony_ci ratio = 1 << (crop->width * 2 / sel->r.width / 3); 3258c2ecf20Sopenharmony_ci sel->r.width = crop->width / ratio; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci sel->r.height = clamp(sel->r.height, crop->height / 4, crop->height); 3298c2ecf20Sopenharmony_ci ratio = 1 << (crop->height * 2 / sel->r.height / 3); 3308c2ecf20Sopenharmony_ci sel->r.height = crop->height / ratio; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci compose = vsp1_entity_get_pad_selection(&histo->entity, config, 3338c2ecf20Sopenharmony_ci sel->pad, 3348c2ecf20Sopenharmony_ci V4L2_SEL_TGT_COMPOSE); 3358c2ecf20Sopenharmony_ci *compose = sel->r; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int histo_set_selection(struct v4l2_subdev *subdev, 3418c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3428c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 3458c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config; 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (sel->pad != HISTO_PAD_SINK) 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci mutex_lock(&histo->entity.lock); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which); 3548c2ecf20Sopenharmony_ci if (!config) { 3558c2ecf20Sopenharmony_ci ret = -EINVAL; 3568c2ecf20Sopenharmony_ci goto done; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_CROP) 3608c2ecf20Sopenharmony_ci ret = histo_set_crop(subdev, config, sel); 3618c2ecf20Sopenharmony_ci else if (sel->target == V4L2_SEL_TGT_COMPOSE) 3628c2ecf20Sopenharmony_ci ret = histo_set_compose(subdev, config, sel); 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci ret = -EINVAL; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cidone: 3678c2ecf20Sopenharmony_ci mutex_unlock(&histo->entity.lock); 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int histo_get_format(struct v4l2_subdev *subdev, 3728c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3738c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci if (fmt->pad == HISTO_PAD_SOURCE) { 3768c2ecf20Sopenharmony_ci fmt->format.code = MEDIA_BUS_FMT_FIXED; 3778c2ecf20Sopenharmony_ci fmt->format.width = 0; 3788c2ecf20Sopenharmony_ci fmt->format.height = 0; 3798c2ecf20Sopenharmony_ci fmt->format.field = V4L2_FIELD_NONE; 3808c2ecf20Sopenharmony_ci fmt->format.colorspace = V4L2_COLORSPACE_RAW; 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return vsp1_subdev_get_pad_format(subdev, cfg, fmt); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int histo_set_format(struct v4l2_subdev *subdev, 3888c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3898c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(subdev); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (fmt->pad != HISTO_PAD_SINK) 3948c2ecf20Sopenharmony_ci return histo_get_format(subdev, cfg, fmt); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return vsp1_subdev_set_pad_format(subdev, cfg, fmt, 3978c2ecf20Sopenharmony_ci histo->formats, histo->num_formats, 3988c2ecf20Sopenharmony_ci HISTO_MIN_SIZE, HISTO_MIN_SIZE, 3998c2ecf20Sopenharmony_ci HISTO_MAX_SIZE, HISTO_MAX_SIZE); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops histo_pad_ops = { 4038c2ecf20Sopenharmony_ci .enum_mbus_code = histo_enum_mbus_code, 4048c2ecf20Sopenharmony_ci .enum_frame_size = histo_enum_frame_size, 4058c2ecf20Sopenharmony_ci .get_fmt = histo_get_format, 4068c2ecf20Sopenharmony_ci .set_fmt = histo_set_format, 4078c2ecf20Sopenharmony_ci .get_selection = histo_get_selection, 4088c2ecf20Sopenharmony_ci .set_selection = histo_set_selection, 4098c2ecf20Sopenharmony_ci}; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops histo_ops = { 4128c2ecf20Sopenharmony_ci .pad = &histo_pad_ops, 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4168c2ecf20Sopenharmony_ci * V4L2 ioctls 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int histo_v4l2_querycap(struct file *file, void *fh, 4208c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 4238c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING 4268c2ecf20Sopenharmony_ci | V4L2_CAP_VIDEO_CAPTURE_MPLANE 4278c2ecf20Sopenharmony_ci | V4L2_CAP_VIDEO_OUTPUT_MPLANE 4288c2ecf20Sopenharmony_ci | V4L2_CAP_META_CAPTURE; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci strscpy(cap->driver, "vsp1", sizeof(cap->driver)); 4318c2ecf20Sopenharmony_ci strscpy(cap->card, histo->video.name, sizeof(cap->card)); 4328c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 4338c2ecf20Sopenharmony_ci dev_name(histo->entity.vsp1->dev)); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int histo_v4l2_enum_format(struct file *file, void *fh, 4398c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 4428c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (f->index > 0 || f->type != histo->queue.type) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci f->pixelformat = histo->meta_format; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int histo_v4l2_get_format(struct file *file, void *fh, 4538c2ecf20Sopenharmony_ci struct v4l2_format *format) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 4568c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev); 4578c2ecf20Sopenharmony_ci struct v4l2_meta_format *meta = &format->fmt.meta; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (format->type != histo->queue.type) 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci memset(meta, 0, sizeof(*meta)); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci meta->dataformat = histo->meta_format; 4658c2ecf20Sopenharmony_ci meta->buffersize = histo->data_size; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops histo_v4l2_ioctl_ops = { 4718c2ecf20Sopenharmony_ci .vidioc_querycap = histo_v4l2_querycap, 4728c2ecf20Sopenharmony_ci .vidioc_enum_fmt_meta_cap = histo_v4l2_enum_format, 4738c2ecf20Sopenharmony_ci .vidioc_g_fmt_meta_cap = histo_v4l2_get_format, 4748c2ecf20Sopenharmony_ci .vidioc_s_fmt_meta_cap = histo_v4l2_get_format, 4758c2ecf20Sopenharmony_ci .vidioc_try_fmt_meta_cap = histo_v4l2_get_format, 4768c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 4778c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 4788c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 4798c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 4808c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 4818c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 4828c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 4838c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4878c2ecf20Sopenharmony_ci * V4L2 File Operations 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations histo_v4l2_fops = { 4918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4928c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 4938c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 4948c2ecf20Sopenharmony_ci .release = vb2_fop_release, 4958c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 4968c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void vsp1_histogram_cleanup(struct vsp1_histogram *histo) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci if (video_is_registered(&histo->video)) 5028c2ecf20Sopenharmony_ci video_unregister_device(&histo->video); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci media_entity_cleanup(&histo->video.entity); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_civoid vsp1_histogram_destroy(struct vsp1_entity *entity) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct vsp1_histogram *histo = subdev_to_histo(&entity->subdev); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci vsp1_histogram_cleanup(histo); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciint vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, 5158c2ecf20Sopenharmony_ci enum vsp1_entity_type type, const char *name, 5168c2ecf20Sopenharmony_ci const struct vsp1_entity_operations *ops, 5178c2ecf20Sopenharmony_ci const unsigned int *formats, unsigned int num_formats, 5188c2ecf20Sopenharmony_ci size_t data_size, u32 meta_format) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int ret; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci histo->formats = formats; 5238c2ecf20Sopenharmony_ci histo->num_formats = num_formats; 5248c2ecf20Sopenharmony_ci histo->data_size = data_size; 5258c2ecf20Sopenharmony_ci histo->meta_format = meta_format; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci histo->pad.flags = MEDIA_PAD_FL_SINK; 5288c2ecf20Sopenharmony_ci histo->video.vfl_dir = VFL_DIR_RX; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci mutex_init(&histo->lock); 5318c2ecf20Sopenharmony_ci spin_lock_init(&histo->irqlock); 5328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&histo->irqqueue); 5338c2ecf20Sopenharmony_ci init_waitqueue_head(&histo->wait_queue); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Initialize the VSP entity... */ 5368c2ecf20Sopenharmony_ci histo->entity.ops = ops; 5378c2ecf20Sopenharmony_ci histo->entity.type = type; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ret = vsp1_entity_init(vsp1, &histo->entity, name, 2, &histo_ops, 5408c2ecf20Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_STATISTICS); 5418c2ecf20Sopenharmony_ci if (ret < 0) 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* ... and the media entity... */ 5458c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&histo->video.entity, 1, &histo->pad); 5468c2ecf20Sopenharmony_ci if (ret < 0) 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* ... and the video node... */ 5508c2ecf20Sopenharmony_ci histo->video.v4l2_dev = &vsp1->v4l2_dev; 5518c2ecf20Sopenharmony_ci histo->video.fops = &histo_v4l2_fops; 5528c2ecf20Sopenharmony_ci snprintf(histo->video.name, sizeof(histo->video.name), 5538c2ecf20Sopenharmony_ci "%s histo", histo->entity.subdev.name); 5548c2ecf20Sopenharmony_ci histo->video.vfl_type = VFL_TYPE_VIDEO; 5558c2ecf20Sopenharmony_ci histo->video.release = video_device_release_empty; 5568c2ecf20Sopenharmony_ci histo->video.ioctl_ops = &histo_v4l2_ioctl_ops; 5578c2ecf20Sopenharmony_ci histo->video.device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci video_set_drvdata(&histo->video, histo); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* ... and the buffers queue... */ 5628c2ecf20Sopenharmony_ci histo->queue.type = V4L2_BUF_TYPE_META_CAPTURE; 5638c2ecf20Sopenharmony_ci histo->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 5648c2ecf20Sopenharmony_ci histo->queue.lock = &histo->lock; 5658c2ecf20Sopenharmony_ci histo->queue.drv_priv = histo; 5668c2ecf20Sopenharmony_ci histo->queue.buf_struct_size = sizeof(struct vsp1_histogram_buffer); 5678c2ecf20Sopenharmony_ci histo->queue.ops = &histo_video_queue_qops; 5688c2ecf20Sopenharmony_ci histo->queue.mem_ops = &vb2_vmalloc_memops; 5698c2ecf20Sopenharmony_ci histo->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 5708c2ecf20Sopenharmony_ci histo->queue.dev = vsp1->dev; 5718c2ecf20Sopenharmony_ci ret = vb2_queue_init(&histo->queue); 5728c2ecf20Sopenharmony_ci if (ret < 0) { 5738c2ecf20Sopenharmony_ci dev_err(vsp1->dev, "failed to initialize vb2 queue\n"); 5748c2ecf20Sopenharmony_ci goto error; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* ... and register the video device. */ 5788c2ecf20Sopenharmony_ci histo->video.queue = &histo->queue; 5798c2ecf20Sopenharmony_ci ret = video_register_device(&histo->video, VFL_TYPE_VIDEO, -1); 5808c2ecf20Sopenharmony_ci if (ret < 0) { 5818c2ecf20Sopenharmony_ci dev_err(vsp1->dev, "failed to register video device\n"); 5828c2ecf20Sopenharmony_ci goto error; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cierror: 5888c2ecf20Sopenharmony_ci vsp1_histogram_cleanup(histo); 5898c2ecf20Sopenharmony_ci return ret; 5908c2ecf20Sopenharmony_ci} 591