1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * vsp1_histo.c -- R-Car VSP1 Histogram API
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2016 Renesas Electronics Corporation
6*4882a593Smuzhiyun * Copyright (C) 2016 Laurent Pinchart
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/gfp.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <media/v4l2-ioctl.h>
15*4882a593Smuzhiyun #include <media/v4l2-subdev.h>
16*4882a593Smuzhiyun #include <media/videobuf2-vmalloc.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include "vsp1.h"
19*4882a593Smuzhiyun #include "vsp1_histo.h"
20*4882a593Smuzhiyun #include "vsp1_pipe.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define HISTO_MIN_SIZE 4U
23*4882a593Smuzhiyun #define HISTO_MAX_SIZE 8192U
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
26*4882a593Smuzhiyun * Buffer Operations
27*4882a593Smuzhiyun */
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun static inline struct vsp1_histogram_buffer *
to_vsp1_histogram_buffer(struct vb2_v4l2_buffer * vbuf)30*4882a593Smuzhiyun to_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun return container_of(vbuf, struct vsp1_histogram_buffer, buf);
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun struct vsp1_histogram_buffer *
vsp1_histogram_buffer_get(struct vsp1_histogram * histo)36*4882a593Smuzhiyun vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun struct vsp1_histogram_buffer *buf = NULL;
39*4882a593Smuzhiyun unsigned long flags;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun spin_lock_irqsave(&histo->irqlock, flags);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun if (list_empty(&histo->irqqueue))
44*4882a593Smuzhiyun goto done;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer,
47*4882a593Smuzhiyun queue);
48*4882a593Smuzhiyun list_del(&buf->queue);
49*4882a593Smuzhiyun histo->readout = true;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun done:
52*4882a593Smuzhiyun spin_unlock_irqrestore(&histo->irqlock, flags);
53*4882a593Smuzhiyun return buf;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
vsp1_histogram_buffer_complete(struct vsp1_histogram * histo,struct vsp1_histogram_buffer * buf,size_t size)56*4882a593Smuzhiyun void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
57*4882a593Smuzhiyun struct vsp1_histogram_buffer *buf,
58*4882a593Smuzhiyun size_t size)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun struct vsp1_pipeline *pipe = histo->entity.pipe;
61*4882a593Smuzhiyun unsigned long flags;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun /*
64*4882a593Smuzhiyun * The pipeline pointer is guaranteed to be valid as this function is
65*4882a593Smuzhiyun * called from the frame completion interrupt handler, which can only
66*4882a593Smuzhiyun * occur when video streaming is active.
67*4882a593Smuzhiyun */
68*4882a593Smuzhiyun buf->buf.sequence = pipe->sequence;
69*4882a593Smuzhiyun buf->buf.vb2_buf.timestamp = ktime_get_ns();
70*4882a593Smuzhiyun vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size);
71*4882a593Smuzhiyun vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun spin_lock_irqsave(&histo->irqlock, flags);
74*4882a593Smuzhiyun histo->readout = false;
75*4882a593Smuzhiyun wake_up(&histo->wait_queue);
76*4882a593Smuzhiyun spin_unlock_irqrestore(&histo->irqlock, flags);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
80*4882a593Smuzhiyun * videobuf2 Queue Operations
81*4882a593Smuzhiyun */
82*4882a593Smuzhiyun
histo_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])83*4882a593Smuzhiyun static int histo_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
84*4882a593Smuzhiyun unsigned int *nplanes, unsigned int sizes[],
85*4882a593Smuzhiyun struct device *alloc_devs[])
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct vsp1_histogram *histo = vb2_get_drv_priv(vq);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (*nplanes) {
90*4882a593Smuzhiyun if (*nplanes != 1)
91*4882a593Smuzhiyun return -EINVAL;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun if (sizes[0] < histo->data_size)
94*4882a593Smuzhiyun return -EINVAL;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun *nplanes = 1;
100*4882a593Smuzhiyun sizes[0] = histo->data_size;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
histo_buffer_prepare(struct vb2_buffer * vb)105*4882a593Smuzhiyun static int histo_buffer_prepare(struct vb2_buffer *vb)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
108*4882a593Smuzhiyun struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue);
109*4882a593Smuzhiyun struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (vb->num_planes != 1)
112*4882a593Smuzhiyun return -EINVAL;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if (vb2_plane_size(vb, 0) < histo->data_size)
115*4882a593Smuzhiyun return -EINVAL;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun buf->addr = vb2_plane_vaddr(vb, 0);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
histo_buffer_queue(struct vb2_buffer * vb)122*4882a593Smuzhiyun static void histo_buffer_queue(struct vb2_buffer *vb)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
125*4882a593Smuzhiyun struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue);
126*4882a593Smuzhiyun struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf);
127*4882a593Smuzhiyun unsigned long flags;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun spin_lock_irqsave(&histo->irqlock, flags);
130*4882a593Smuzhiyun list_add_tail(&buf->queue, &histo->irqqueue);
131*4882a593Smuzhiyun spin_unlock_irqrestore(&histo->irqlock, flags);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
histo_start_streaming(struct vb2_queue * vq,unsigned int count)134*4882a593Smuzhiyun static int histo_start_streaming(struct vb2_queue *vq, unsigned int count)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun return 0;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
histo_stop_streaming(struct vb2_queue * vq)139*4882a593Smuzhiyun static void histo_stop_streaming(struct vb2_queue *vq)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun struct vsp1_histogram *histo = vb2_get_drv_priv(vq);
142*4882a593Smuzhiyun struct vsp1_histogram_buffer *buffer;
143*4882a593Smuzhiyun unsigned long flags;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun spin_lock_irqsave(&histo->irqlock, flags);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun /* Remove all buffers from the IRQ queue. */
148*4882a593Smuzhiyun list_for_each_entry(buffer, &histo->irqqueue, queue)
149*4882a593Smuzhiyun vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
150*4882a593Smuzhiyun INIT_LIST_HEAD(&histo->irqqueue);
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /* Wait for the buffer being read out (if any) to complete. */
153*4882a593Smuzhiyun wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun spin_unlock_irqrestore(&histo->irqlock, flags);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun static const struct vb2_ops histo_video_queue_qops = {
159*4882a593Smuzhiyun .queue_setup = histo_queue_setup,
160*4882a593Smuzhiyun .buf_prepare = histo_buffer_prepare,
161*4882a593Smuzhiyun .buf_queue = histo_buffer_queue,
162*4882a593Smuzhiyun .wait_prepare = vb2_ops_wait_prepare,
163*4882a593Smuzhiyun .wait_finish = vb2_ops_wait_finish,
164*4882a593Smuzhiyun .start_streaming = histo_start_streaming,
165*4882a593Smuzhiyun .stop_streaming = histo_stop_streaming,
166*4882a593Smuzhiyun };
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
169*4882a593Smuzhiyun * V4L2 Subdevice Operations
170*4882a593Smuzhiyun */
171*4882a593Smuzhiyun
histo_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_mbus_code_enum * code)172*4882a593Smuzhiyun static int histo_enum_mbus_code(struct v4l2_subdev *subdev,
173*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
174*4882a593Smuzhiyun struct v4l2_subdev_mbus_code_enum *code)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun if (code->pad == HISTO_PAD_SOURCE) {
179*4882a593Smuzhiyun code->code = MEDIA_BUS_FMT_FIXED;
180*4882a593Smuzhiyun return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun return vsp1_subdev_enum_mbus_code(subdev, cfg, code, histo->formats,
184*4882a593Smuzhiyun histo->num_formats);
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
histo_enum_frame_size(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_frame_size_enum * fse)187*4882a593Smuzhiyun static int histo_enum_frame_size(struct v4l2_subdev *subdev,
188*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
189*4882a593Smuzhiyun struct v4l2_subdev_frame_size_enum *fse)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun if (fse->pad != HISTO_PAD_SINK)
192*4882a593Smuzhiyun return -EINVAL;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun return vsp1_subdev_enum_frame_size(subdev, cfg, fse, HISTO_MIN_SIZE,
195*4882a593Smuzhiyun HISTO_MIN_SIZE, HISTO_MAX_SIZE,
196*4882a593Smuzhiyun HISTO_MAX_SIZE);
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
histo_get_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)199*4882a593Smuzhiyun static int histo_get_selection(struct v4l2_subdev *subdev,
200*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
201*4882a593Smuzhiyun struct v4l2_subdev_selection *sel)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
204*4882a593Smuzhiyun struct v4l2_subdev_pad_config *config;
205*4882a593Smuzhiyun struct v4l2_mbus_framefmt *format;
206*4882a593Smuzhiyun struct v4l2_rect *crop;
207*4882a593Smuzhiyun int ret = 0;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun if (sel->pad != HISTO_PAD_SINK)
210*4882a593Smuzhiyun return -EINVAL;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun mutex_lock(&histo->entity.lock);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which);
215*4882a593Smuzhiyun if (!config) {
216*4882a593Smuzhiyun ret = -EINVAL;
217*4882a593Smuzhiyun goto done;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun switch (sel->target) {
221*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_BOUNDS:
222*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE_DEFAULT:
223*4882a593Smuzhiyun crop = vsp1_entity_get_pad_selection(&histo->entity, config,
224*4882a593Smuzhiyun HISTO_PAD_SINK,
225*4882a593Smuzhiyun V4L2_SEL_TGT_CROP);
226*4882a593Smuzhiyun sel->r.left = 0;
227*4882a593Smuzhiyun sel->r.top = 0;
228*4882a593Smuzhiyun sel->r.width = crop->width;
229*4882a593Smuzhiyun sel->r.height = crop->height;
230*4882a593Smuzhiyun break;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_BOUNDS:
233*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP_DEFAULT:
234*4882a593Smuzhiyun format = vsp1_entity_get_pad_format(&histo->entity, config,
235*4882a593Smuzhiyun HISTO_PAD_SINK);
236*4882a593Smuzhiyun sel->r.left = 0;
237*4882a593Smuzhiyun sel->r.top = 0;
238*4882a593Smuzhiyun sel->r.width = format->width;
239*4882a593Smuzhiyun sel->r.height = format->height;
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun case V4L2_SEL_TGT_COMPOSE:
243*4882a593Smuzhiyun case V4L2_SEL_TGT_CROP:
244*4882a593Smuzhiyun sel->r = *vsp1_entity_get_pad_selection(&histo->entity, config,
245*4882a593Smuzhiyun sel->pad, sel->target);
246*4882a593Smuzhiyun break;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun default:
249*4882a593Smuzhiyun ret = -EINVAL;
250*4882a593Smuzhiyun break;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun done:
254*4882a593Smuzhiyun mutex_unlock(&histo->entity.lock);
255*4882a593Smuzhiyun return ret;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
histo_set_crop(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * config,struct v4l2_subdev_selection * sel)258*4882a593Smuzhiyun static int histo_set_crop(struct v4l2_subdev *subdev,
259*4882a593Smuzhiyun struct v4l2_subdev_pad_config *config,
260*4882a593Smuzhiyun struct v4l2_subdev_selection *sel)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
263*4882a593Smuzhiyun struct v4l2_mbus_framefmt *format;
264*4882a593Smuzhiyun struct v4l2_rect *selection;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* The crop rectangle must be inside the input frame. */
267*4882a593Smuzhiyun format = vsp1_entity_get_pad_format(&histo->entity, config,
268*4882a593Smuzhiyun HISTO_PAD_SINK);
269*4882a593Smuzhiyun sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
270*4882a593Smuzhiyun sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
271*4882a593Smuzhiyun sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE,
272*4882a593Smuzhiyun format->width - sel->r.left);
273*4882a593Smuzhiyun sel->r.height = clamp_t(unsigned int, sel->r.height, HISTO_MIN_SIZE,
274*4882a593Smuzhiyun format->height - sel->r.top);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun /* Set the crop rectangle and reset the compose rectangle. */
277*4882a593Smuzhiyun selection = vsp1_entity_get_pad_selection(&histo->entity, config,
278*4882a593Smuzhiyun sel->pad, V4L2_SEL_TGT_CROP);
279*4882a593Smuzhiyun *selection = sel->r;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun selection = vsp1_entity_get_pad_selection(&histo->entity, config,
282*4882a593Smuzhiyun sel->pad,
283*4882a593Smuzhiyun V4L2_SEL_TGT_COMPOSE);
284*4882a593Smuzhiyun *selection = sel->r;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun return 0;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
histo_set_compose(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * config,struct v4l2_subdev_selection * sel)289*4882a593Smuzhiyun static int histo_set_compose(struct v4l2_subdev *subdev,
290*4882a593Smuzhiyun struct v4l2_subdev_pad_config *config,
291*4882a593Smuzhiyun struct v4l2_subdev_selection *sel)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
294*4882a593Smuzhiyun struct v4l2_rect *compose;
295*4882a593Smuzhiyun struct v4l2_rect *crop;
296*4882a593Smuzhiyun unsigned int ratio;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun /*
299*4882a593Smuzhiyun * The compose rectangle is used to configure downscaling, the top left
300*4882a593Smuzhiyun * corner is fixed to (0,0) and the size to 1/2 or 1/4 of the crop
301*4882a593Smuzhiyun * rectangle.
302*4882a593Smuzhiyun */
303*4882a593Smuzhiyun sel->r.left = 0;
304*4882a593Smuzhiyun sel->r.top = 0;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun crop = vsp1_entity_get_pad_selection(&histo->entity, config, sel->pad,
307*4882a593Smuzhiyun V4L2_SEL_TGT_CROP);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun /*
310*4882a593Smuzhiyun * Clamp the width and height to acceptable values first and then
311*4882a593Smuzhiyun * compute the closest rounded dividing ratio.
312*4882a593Smuzhiyun *
313*4882a593Smuzhiyun * Ratio Rounded ratio
314*4882a593Smuzhiyun * --------------------------
315*4882a593Smuzhiyun * [1.0 1.5[ 1
316*4882a593Smuzhiyun * [1.5 3.0[ 2
317*4882a593Smuzhiyun * [3.0 4.0] 4
318*4882a593Smuzhiyun *
319*4882a593Smuzhiyun * The rounded ratio can be computed using
320*4882a593Smuzhiyun *
321*4882a593Smuzhiyun * 1 << (ceil(ratio * 2) / 3)
322*4882a593Smuzhiyun */
323*4882a593Smuzhiyun sel->r.width = clamp(sel->r.width, crop->width / 4, crop->width);
324*4882a593Smuzhiyun ratio = 1 << (crop->width * 2 / sel->r.width / 3);
325*4882a593Smuzhiyun sel->r.width = crop->width / ratio;
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun sel->r.height = clamp(sel->r.height, crop->height / 4, crop->height);
329*4882a593Smuzhiyun ratio = 1 << (crop->height * 2 / sel->r.height / 3);
330*4882a593Smuzhiyun sel->r.height = crop->height / ratio;
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun compose = vsp1_entity_get_pad_selection(&histo->entity, config,
333*4882a593Smuzhiyun sel->pad,
334*4882a593Smuzhiyun V4L2_SEL_TGT_COMPOSE);
335*4882a593Smuzhiyun *compose = sel->r;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun return 0;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun
histo_set_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)340*4882a593Smuzhiyun static int histo_set_selection(struct v4l2_subdev *subdev,
341*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
342*4882a593Smuzhiyun struct v4l2_subdev_selection *sel)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
345*4882a593Smuzhiyun struct v4l2_subdev_pad_config *config;
346*4882a593Smuzhiyun int ret;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun if (sel->pad != HISTO_PAD_SINK)
349*4882a593Smuzhiyun return -EINVAL;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun mutex_lock(&histo->entity.lock);
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun config = vsp1_entity_get_pad_config(&histo->entity, cfg, sel->which);
354*4882a593Smuzhiyun if (!config) {
355*4882a593Smuzhiyun ret = -EINVAL;
356*4882a593Smuzhiyun goto done;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun if (sel->target == V4L2_SEL_TGT_CROP)
360*4882a593Smuzhiyun ret = histo_set_crop(subdev, config, sel);
361*4882a593Smuzhiyun else if (sel->target == V4L2_SEL_TGT_COMPOSE)
362*4882a593Smuzhiyun ret = histo_set_compose(subdev, config, sel);
363*4882a593Smuzhiyun else
364*4882a593Smuzhiyun ret = -EINVAL;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun done:
367*4882a593Smuzhiyun mutex_unlock(&histo->entity.lock);
368*4882a593Smuzhiyun return ret;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
histo_get_format(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)371*4882a593Smuzhiyun static int histo_get_format(struct v4l2_subdev *subdev,
372*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
373*4882a593Smuzhiyun struct v4l2_subdev_format *fmt)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun if (fmt->pad == HISTO_PAD_SOURCE) {
376*4882a593Smuzhiyun fmt->format.code = MEDIA_BUS_FMT_FIXED;
377*4882a593Smuzhiyun fmt->format.width = 0;
378*4882a593Smuzhiyun fmt->format.height = 0;
379*4882a593Smuzhiyun fmt->format.field = V4L2_FIELD_NONE;
380*4882a593Smuzhiyun fmt->format.colorspace = V4L2_COLORSPACE_RAW;
381*4882a593Smuzhiyun return 0;
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun return vsp1_subdev_get_pad_format(subdev, cfg, fmt);
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun
histo_set_format(struct v4l2_subdev * subdev,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)387*4882a593Smuzhiyun static int histo_set_format(struct v4l2_subdev *subdev,
388*4882a593Smuzhiyun struct v4l2_subdev_pad_config *cfg,
389*4882a593Smuzhiyun struct v4l2_subdev_format *fmt)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(subdev);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun if (fmt->pad != HISTO_PAD_SINK)
394*4882a593Smuzhiyun return histo_get_format(subdev, cfg, fmt);
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun return vsp1_subdev_set_pad_format(subdev, cfg, fmt,
397*4882a593Smuzhiyun histo->formats, histo->num_formats,
398*4882a593Smuzhiyun HISTO_MIN_SIZE, HISTO_MIN_SIZE,
399*4882a593Smuzhiyun HISTO_MAX_SIZE, HISTO_MAX_SIZE);
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun static const struct v4l2_subdev_pad_ops histo_pad_ops = {
403*4882a593Smuzhiyun .enum_mbus_code = histo_enum_mbus_code,
404*4882a593Smuzhiyun .enum_frame_size = histo_enum_frame_size,
405*4882a593Smuzhiyun .get_fmt = histo_get_format,
406*4882a593Smuzhiyun .set_fmt = histo_set_format,
407*4882a593Smuzhiyun .get_selection = histo_get_selection,
408*4882a593Smuzhiyun .set_selection = histo_set_selection,
409*4882a593Smuzhiyun };
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun static const struct v4l2_subdev_ops histo_ops = {
412*4882a593Smuzhiyun .pad = &histo_pad_ops,
413*4882a593Smuzhiyun };
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
416*4882a593Smuzhiyun * V4L2 ioctls
417*4882a593Smuzhiyun */
418*4882a593Smuzhiyun
histo_v4l2_querycap(struct file * file,void * fh,struct v4l2_capability * cap)419*4882a593Smuzhiyun static int histo_v4l2_querycap(struct file *file, void *fh,
420*4882a593Smuzhiyun struct v4l2_capability *cap)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun struct v4l2_fh *vfh = file->private_data;
423*4882a593Smuzhiyun struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
426*4882a593Smuzhiyun | V4L2_CAP_VIDEO_CAPTURE_MPLANE
427*4882a593Smuzhiyun | V4L2_CAP_VIDEO_OUTPUT_MPLANE
428*4882a593Smuzhiyun | V4L2_CAP_META_CAPTURE;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun strscpy(cap->driver, "vsp1", sizeof(cap->driver));
431*4882a593Smuzhiyun strscpy(cap->card, histo->video.name, sizeof(cap->card));
432*4882a593Smuzhiyun snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
433*4882a593Smuzhiyun dev_name(histo->entity.vsp1->dev));
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun return 0;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
histo_v4l2_enum_format(struct file * file,void * fh,struct v4l2_fmtdesc * f)438*4882a593Smuzhiyun static int histo_v4l2_enum_format(struct file *file, void *fh,
439*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun struct v4l2_fh *vfh = file->private_data;
442*4882a593Smuzhiyun struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev);
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun if (f->index > 0 || f->type != histo->queue.type)
445*4882a593Smuzhiyun return -EINVAL;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun f->pixelformat = histo->meta_format;
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun return 0;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
histo_v4l2_get_format(struct file * file,void * fh,struct v4l2_format * format)452*4882a593Smuzhiyun static int histo_v4l2_get_format(struct file *file, void *fh,
453*4882a593Smuzhiyun struct v4l2_format *format)
454*4882a593Smuzhiyun {
455*4882a593Smuzhiyun struct v4l2_fh *vfh = file->private_data;
456*4882a593Smuzhiyun struct vsp1_histogram *histo = vdev_to_histo(vfh->vdev);
457*4882a593Smuzhiyun struct v4l2_meta_format *meta = &format->fmt.meta;
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun if (format->type != histo->queue.type)
460*4882a593Smuzhiyun return -EINVAL;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun memset(meta, 0, sizeof(*meta));
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun meta->dataformat = histo->meta_format;
465*4882a593Smuzhiyun meta->buffersize = histo->data_size;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun return 0;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun static const struct v4l2_ioctl_ops histo_v4l2_ioctl_ops = {
471*4882a593Smuzhiyun .vidioc_querycap = histo_v4l2_querycap,
472*4882a593Smuzhiyun .vidioc_enum_fmt_meta_cap = histo_v4l2_enum_format,
473*4882a593Smuzhiyun .vidioc_g_fmt_meta_cap = histo_v4l2_get_format,
474*4882a593Smuzhiyun .vidioc_s_fmt_meta_cap = histo_v4l2_get_format,
475*4882a593Smuzhiyun .vidioc_try_fmt_meta_cap = histo_v4l2_get_format,
476*4882a593Smuzhiyun .vidioc_reqbufs = vb2_ioctl_reqbufs,
477*4882a593Smuzhiyun .vidioc_querybuf = vb2_ioctl_querybuf,
478*4882a593Smuzhiyun .vidioc_qbuf = vb2_ioctl_qbuf,
479*4882a593Smuzhiyun .vidioc_dqbuf = vb2_ioctl_dqbuf,
480*4882a593Smuzhiyun .vidioc_create_bufs = vb2_ioctl_create_bufs,
481*4882a593Smuzhiyun .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
482*4882a593Smuzhiyun .vidioc_streamon = vb2_ioctl_streamon,
483*4882a593Smuzhiyun .vidioc_streamoff = vb2_ioctl_streamoff,
484*4882a593Smuzhiyun };
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
487*4882a593Smuzhiyun * V4L2 File Operations
488*4882a593Smuzhiyun */
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun static const struct v4l2_file_operations histo_v4l2_fops = {
491*4882a593Smuzhiyun .owner = THIS_MODULE,
492*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
493*4882a593Smuzhiyun .open = v4l2_fh_open,
494*4882a593Smuzhiyun .release = vb2_fop_release,
495*4882a593Smuzhiyun .poll = vb2_fop_poll,
496*4882a593Smuzhiyun .mmap = vb2_fop_mmap,
497*4882a593Smuzhiyun };
498*4882a593Smuzhiyun
vsp1_histogram_cleanup(struct vsp1_histogram * histo)499*4882a593Smuzhiyun static void vsp1_histogram_cleanup(struct vsp1_histogram *histo)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun if (video_is_registered(&histo->video))
502*4882a593Smuzhiyun video_unregister_device(&histo->video);
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun media_entity_cleanup(&histo->video.entity);
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
vsp1_histogram_destroy(struct vsp1_entity * entity)507*4882a593Smuzhiyun void vsp1_histogram_destroy(struct vsp1_entity *entity)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun struct vsp1_histogram *histo = subdev_to_histo(&entity->subdev);
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun vsp1_histogram_cleanup(histo);
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun
vsp1_histogram_init(struct vsp1_device * vsp1,struct vsp1_histogram * histo,enum vsp1_entity_type type,const char * name,const struct vsp1_entity_operations * ops,const unsigned int * formats,unsigned int num_formats,size_t data_size,u32 meta_format)514*4882a593Smuzhiyun int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo,
515*4882a593Smuzhiyun enum vsp1_entity_type type, const char *name,
516*4882a593Smuzhiyun const struct vsp1_entity_operations *ops,
517*4882a593Smuzhiyun const unsigned int *formats, unsigned int num_formats,
518*4882a593Smuzhiyun size_t data_size, u32 meta_format)
519*4882a593Smuzhiyun {
520*4882a593Smuzhiyun int ret;
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun histo->formats = formats;
523*4882a593Smuzhiyun histo->num_formats = num_formats;
524*4882a593Smuzhiyun histo->data_size = data_size;
525*4882a593Smuzhiyun histo->meta_format = meta_format;
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun histo->pad.flags = MEDIA_PAD_FL_SINK;
528*4882a593Smuzhiyun histo->video.vfl_dir = VFL_DIR_RX;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun mutex_init(&histo->lock);
531*4882a593Smuzhiyun spin_lock_init(&histo->irqlock);
532*4882a593Smuzhiyun INIT_LIST_HEAD(&histo->irqqueue);
533*4882a593Smuzhiyun init_waitqueue_head(&histo->wait_queue);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun /* Initialize the VSP entity... */
536*4882a593Smuzhiyun histo->entity.ops = ops;
537*4882a593Smuzhiyun histo->entity.type = type;
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun ret = vsp1_entity_init(vsp1, &histo->entity, name, 2, &histo_ops,
540*4882a593Smuzhiyun MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
541*4882a593Smuzhiyun if (ret < 0)
542*4882a593Smuzhiyun return ret;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun /* ... and the media entity... */
545*4882a593Smuzhiyun ret = media_entity_pads_init(&histo->video.entity, 1, &histo->pad);
546*4882a593Smuzhiyun if (ret < 0)
547*4882a593Smuzhiyun return ret;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun /* ... and the video node... */
550*4882a593Smuzhiyun histo->video.v4l2_dev = &vsp1->v4l2_dev;
551*4882a593Smuzhiyun histo->video.fops = &histo_v4l2_fops;
552*4882a593Smuzhiyun snprintf(histo->video.name, sizeof(histo->video.name),
553*4882a593Smuzhiyun "%s histo", histo->entity.subdev.name);
554*4882a593Smuzhiyun histo->video.vfl_type = VFL_TYPE_VIDEO;
555*4882a593Smuzhiyun histo->video.release = video_device_release_empty;
556*4882a593Smuzhiyun histo->video.ioctl_ops = &histo_v4l2_ioctl_ops;
557*4882a593Smuzhiyun histo->video.device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun video_set_drvdata(&histo->video, histo);
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun /* ... and the buffers queue... */
562*4882a593Smuzhiyun histo->queue.type = V4L2_BUF_TYPE_META_CAPTURE;
563*4882a593Smuzhiyun histo->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
564*4882a593Smuzhiyun histo->queue.lock = &histo->lock;
565*4882a593Smuzhiyun histo->queue.drv_priv = histo;
566*4882a593Smuzhiyun histo->queue.buf_struct_size = sizeof(struct vsp1_histogram_buffer);
567*4882a593Smuzhiyun histo->queue.ops = &histo_video_queue_qops;
568*4882a593Smuzhiyun histo->queue.mem_ops = &vb2_vmalloc_memops;
569*4882a593Smuzhiyun histo->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
570*4882a593Smuzhiyun histo->queue.dev = vsp1->dev;
571*4882a593Smuzhiyun ret = vb2_queue_init(&histo->queue);
572*4882a593Smuzhiyun if (ret < 0) {
573*4882a593Smuzhiyun dev_err(vsp1->dev, "failed to initialize vb2 queue\n");
574*4882a593Smuzhiyun goto error;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun /* ... and register the video device. */
578*4882a593Smuzhiyun histo->video.queue = &histo->queue;
579*4882a593Smuzhiyun ret = video_register_device(&histo->video, VFL_TYPE_VIDEO, -1);
580*4882a593Smuzhiyun if (ret < 0) {
581*4882a593Smuzhiyun dev_err(vsp1->dev, "failed to register video device\n");
582*4882a593Smuzhiyun goto error;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun return 0;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun error:
588*4882a593Smuzhiyun vsp1_histogram_cleanup(histo);
589*4882a593Smuzhiyun return ret;
590*4882a593Smuzhiyun }
591