Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only
2 : /*
3 : * Copyright (C) 2013--2024 Intel Corporation
4 : */
5 :
6 : #include <linux/align.h>
7 : #include <linux/bits.h>
8 : #include <linux/bug.h>
9 : #include <linux/completion.h>
10 : #include <linux/container_of.h>
11 : #include <linux/delay.h>
12 : #include <linux/device.h>
13 : #include <linux/list.h>
14 : #include <linux/math64.h>
15 : #include <linux/minmax.h>
16 : #include <linux/module.h>
17 : #include <linux/mutex.h>
18 : #include <linux/pm_runtime.h>
19 : #include <linux/spinlock.h>
20 : #include <linux/string.h>
21 :
22 : #include <media/media-entity.h>
23 : #include <media/v4l2-ctrls.h>
24 : #include <media/v4l2-dev.h>
25 : #include <media/v4l2-fh.h>
26 : #include <media/v4l2-ioctl.h>
27 : #include <media/v4l2-subdev.h>
28 : #include <media/videobuf2-v4l2.h>
29 :
30 : #include "ipu6.h"
31 : #include "ipu6-bus.h"
32 : #include "ipu6-cpd.h"
33 : #include "ipu6-fw-isys.h"
34 : #include "ipu6-isys.h"
35 : #include "ipu6-isys-csi2.h"
36 : #include "ipu6-isys-queue.h"
37 : #include "ipu6-isys-video.h"
38 : #include "ipu4-compat.h"
39 :
40 : bool force_need_reset;
41 : module_param(force_need_reset, bool, 0644);
42 : MODULE_PARM_DESC(force_need_reset, "emulate 'isys power cycle required' on next video_open");
43 :
44 : bool silent_reset_enable = true;
45 : module_param(silent_reset_enable, bool, 0644);
46 : MODULE_PARM_DESC(silent_reset_enable, "perform a silent reset of existing streams when 'isys power cycle required' would otherwise be logged");
47 :
48 : const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
49 : { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
50 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
51 : { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
52 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
53 : { V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
54 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
55 : { V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
56 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
57 : { V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
58 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
59 : { V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
60 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
61 : { V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
62 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
63 : { V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
64 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
65 : { V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
66 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
67 : { V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
68 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
69 : { V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
70 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
71 : { V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
72 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
73 : { V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
74 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
75 : { V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
76 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
77 : { V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
78 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
79 : { V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
80 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
81 : { V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
82 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
83 : { V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
84 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
85 : { V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
86 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
87 : { V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
88 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
89 :
90 : { V4L2_PIX_FMT_GREY, 8, 8, MEDIA_BUS_FMT_Y8_1X8,
91 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
92 : { V4L2_PIX_FMT_Y10, 16, 10, MEDIA_BUS_FMT_Y10_1X10,
93 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
94 : { V4L2_PIX_FMT_Y12, 16, 12, MEDIA_BUS_FMT_Y12_1X12,
95 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
96 : { V4L2_PIX_FMT_Y16, 16, 16, MEDIA_BUS_FMT_Y16_1X16,
97 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
98 : { V4L2_PIX_FMT_Y10P, 10, 10, MEDIA_BUS_FMT_Y10_1X10,
99 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
100 : { V4L2_PIX_FMT_Y12P, 12, 12, MEDIA_BUS_FMT_Y12_1X12,
101 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
102 :
103 : { V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
104 : IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
105 : { V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
106 : IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
107 : { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
108 : IPU6_FW_ISYS_FRAME_FORMAT_RGB565 },
109 : { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
110 : IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 },
111 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
112 : { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8,
113 : IPU6_FW_ISYS_FRAME_FORMAT_RAW8, true },
114 : { V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10,
115 : IPU6_FW_ISYS_FRAME_FORMAT_RAW10, true },
116 : { V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12,
117 : IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true },
118 : { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16,
119 : IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true },
120 : #endif
121 : { V4L2_PIX_FMT_XBGR32, 32, 24, MEDIA_BUS_FMT_RGB888_1X24,
122 : IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
123 : };
124 :
125 3 : static void wait_for_not_resetting(struct ipu6_isys *isys, const char *context)
126 : {
127 3 : struct device *dev = &isys->adev->auxdev.dev;
128 :
129 3 : while (isys->resetting) {
130 0 : mutex_unlock(&isys->mutex);
131 0 : dev_info(dev, "%s: Waiting for isys resetting\n", context);
132 0 : msleep_interruptible(100);
133 0 : mutex_lock(&isys->mutex);
134 : }
135 3 : }
136 :
137 1 : static int video_open(struct file *file)
138 : {
139 : struct ipu6_isys_video *av = video_drvdata(file);
140 1 : struct ipu6_isys *isys = av->isys;
141 1 : struct ipu6_bus_device *adev = isys->adev;
142 1 : struct device *dev = &adev->auxdev.dev;
143 : int ret = 0;
144 :
145 1 : mutex_lock(&isys->mutex);
146 1 : wait_for_not_resetting(isys, __func__);
147 :
148 1 : if (isys->need_reset || force_need_reset) {
149 0 : if (silent_reset_enable) {
150 0 : isys->resetting = true;
151 0 : mutex_unlock(&isys->mutex);
152 :
153 0 : dev_warn(dev, "restarting other streams (need_reset=%d, force_need_reset=%d)\n",
154 : isys->need_reset,
155 : force_need_reset);
156 0 : force_need_reset = false;
157 0 : ret = ipu6_isys_queue_restart_streams(av);
158 :
159 0 : mutex_lock(&isys->mutex);
160 0 : isys->resetting = false;
161 0 : if (ret)
162 0 : dev_err(dev, "ipu6_isys_queue_restart_streams failed: %d\n",
163 : ret);
164 : }
165 :
166 0 : if (ret || !silent_reset_enable) {
167 : // Keeping original log message below,
168 : // since it is widely known
169 0 : dev_warn(dev, "isys power cycle required\n");
170 0 : mutex_unlock(&isys->mutex);
171 0 : return -EIO;
172 : }
173 0 : dev_info(dev, "restarting other streams done: %d\n", ret);
174 : }
175 1 : mutex_unlock(&isys->mutex);
176 :
177 1 : ret = ipu6_buttress_authenticate(adev->isp);
178 1 : if (ret) {
179 0 : dev_err(dev, "%s: FW authentication failed: %d\n",
180 : __func__,
181 : ret);
182 0 : return ret;
183 : }
184 :
185 1 : ret = v4l2_fh_open(file);
186 1 : if (ret)
187 0 : dev_err(dev, "%s: v4l2_fh_open failed: %d\n", __func__, ret);
188 :
189 : return ret;
190 : }
191 :
192 : const struct ipu6_isys_pixelformat *
193 65 : ipu6_isys_get_isys_format(u32 pixelformat, u32 type)
194 : {
195 : const struct ipu6_isys_pixelformat *default_pfmt = NULL;
196 : unsigned int i;
197 :
198 2412 : for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
199 2350 : const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
200 :
201 2260 : if (type && ((!pfmt->is_meta &&
202 1239 : type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
203 248 : (pfmt->is_meta &&
204 : type != V4L2_BUF_TYPE_META_CAPTURE)))
205 1085 : continue;
206 :
207 1265 : if (!default_pfmt)
208 : default_pfmt = pfmt;
209 :
210 1265 : if (pfmt->pixelformat != pixelformat)
211 1172 : continue;
212 :
213 : return pfmt;
214 : }
215 :
216 : return default_pfmt;
217 : }
218 : EXPORT_SYMBOL_NS_GPL(ipu6_isys_get_isys_format, INTEL_IPU6);
219 :
220 1 : static int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
221 : struct v4l2_capability *cap)
222 : {
223 : struct ipu6_isys_video *av = video_drvdata(file);
224 :
225 1 : strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));
226 1 : strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
227 :
228 1 : return 0;
229 : }
230 :
231 0 : static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
232 : struct v4l2_fmtdesc *f)
233 : {
234 : unsigned int i, num_found;
235 :
236 0 : for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
237 0 : if ((ipu6_isys_pfmts[i].is_meta &&
238 0 : f->type != V4L2_BUF_TYPE_META_CAPTURE) ||
239 0 : (!ipu6_isys_pfmts[i].is_meta &&
240 0 : f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
241 0 : continue;
242 :
243 0 : if (f->mbus_code && f->mbus_code != ipu6_isys_pfmts[i].code)
244 0 : continue;
245 :
246 0 : if (num_found < f->index) {
247 0 : num_found++;
248 0 : continue;
249 : }
250 :
251 0 : f->flags = 0;
252 0 : f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
253 :
254 0 : return 0;
255 : }
256 :
257 : return -EINVAL;
258 : }
259 :
260 0 : static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
261 : struct v4l2_frmsizeenum *fsize)
262 : {
263 : unsigned int i;
264 :
265 0 : if (fsize->index > 0)
266 : return -EINVAL;
267 :
268 0 : for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
269 0 : if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat)
270 : continue;
271 :
272 0 : fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
273 0 : fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;
274 0 : fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;
275 0 : fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;
276 0 : fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;
277 0 : fsize->stepwise.step_width = 2;
278 0 : fsize->stepwise.step_height = 2;
279 :
280 0 : return 0;
281 : }
282 :
283 : return -EINVAL;
284 : }
285 :
286 0 : static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,
287 : struct v4l2_format *f)
288 : {
289 : struct ipu6_isys_video *av = video_drvdata(file);
290 :
291 0 : f->fmt.pix = av->pix_fmt;
292 :
293 0 : return 0;
294 : }
295 :
296 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
297 0 : static int ipu6_isys_vidioc_g_fmt_meta_cap(struct file *file, void *fh,
298 : struct v4l2_format *f)
299 : {
300 : struct ipu6_isys_video *av = video_drvdata(file);
301 :
302 0 : f->fmt.meta = av->meta_fmt;
303 :
304 0 : return 0;
305 : }
306 : #endif
307 :
308 63 : static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type,
309 : u32 *format, u32 *width, u32 *height,
310 : u32 *bytesperline, u32 *sizeimage)
311 : {
312 : const struct ipu6_isys_pixelformat *pfmt =
313 63 : ipu6_isys_get_isys_format(*format, type);
314 :
315 63 : *format = pfmt->pixelformat;
316 63 : *width = clamp(*width, IPU6_ISYS_MIN_WIDTH, IPU6_ISYS_MAX_WIDTH);
317 63 : *height = clamp(*height, IPU6_ISYS_MIN_HEIGHT, IPU6_ISYS_MAX_HEIGHT);
318 :
319 63 : if (pfmt->bpp != pfmt->bpp_packed)
320 31 : *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
321 : else
322 32 : *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);
323 :
324 63 : *bytesperline = ALIGN(*bytesperline, 64);
325 :
326 : /*
327 : * (height + 1) * bytesperline due to a hardware issue: the DMA unit
328 : * is a power of two, and a line should be transferred as few units
329 : * as possible. The result is that up to line length more data than
330 : * the image size may be transferred to memory after the image.
331 : * Another limitation is the GDA allocation unit size. For low
332 : * resolution it gives a bigger number. Use larger one to avoid
333 : * memory corruption.
334 : */
335 63 : *sizeimage = *bytesperline * *height +
336 63 : max(*bytesperline,
337 : av->isys->pdata->ipdata->isys_dma_overshoot);
338 63 : }
339 :
340 32 : static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av,
341 : struct v4l2_format *f)
342 : {
343 32 : ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,
344 32 : &f->fmt.pix.width, &f->fmt.pix.height,
345 32 : &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);
346 :
347 32 : f->fmt.pix.field = V4L2_FIELD_NONE;
348 32 : f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
349 32 : f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
350 32 : f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
351 32 : f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
352 32 : }
353 :
354 1 : static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,
355 : struct v4l2_format *f)
356 : {
357 : struct ipu6_isys_video *av = video_drvdata(file);
358 :
359 1 : if (vb2_is_busy(&av->aq.vbq))
360 : return -EBUSY;
361 :
362 1 : __ipu6_isys_vidioc_try_fmt_vid_cap(av, f);
363 :
364 1 : return 0;
365 : }
366 :
367 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
368 31 : static int __ipu6_isys_vidioc_try_fmt_meta_cap(struct ipu6_isys_video *av,
369 : struct v4l2_format *f)
370 : {
371 31 : ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.meta.dataformat,
372 31 : &f->fmt.meta.width, &f->fmt.meta.height,
373 31 : &f->fmt.meta.bytesperline,
374 31 : &f->fmt.meta.buffersize);
375 :
376 31 : return 0;
377 : }
378 :
379 0 : static int ipu6_isys_vidioc_try_fmt_meta_cap(struct file *file, void *fh,
380 : struct v4l2_format *f)
381 : {
382 : struct ipu6_isys_video *av = video_drvdata(file);
383 :
384 0 : __ipu6_isys_vidioc_try_fmt_meta_cap(av, f);
385 :
386 0 : return 0;
387 : }
388 : #endif
389 :
390 1 : static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,
391 : struct v4l2_format *f)
392 : {
393 : struct ipu6_isys_video *av = video_drvdata(file);
394 :
395 1 : ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f);
396 1 : av->pix_fmt = f->fmt.pix;
397 :
398 1 : return 0;
399 : }
400 :
401 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
402 0 : static int ipu6_isys_vidioc_s_fmt_meta_cap(struct file *file, void *fh,
403 : struct v4l2_format *f)
404 : {
405 : struct ipu6_isys_video *av = video_drvdata(file);
406 :
407 0 : if (vb2_is_busy(&av->aq.vbq))
408 : return -EBUSY;
409 :
410 0 : ipu6_isys_vidioc_try_fmt_meta_cap(file, fh, f);
411 0 : av->meta_fmt = f->fmt.meta;
412 :
413 0 : return 0;
414 : }
415 : #endif
416 :
417 1 : static int ipu6_isys_vidioc_reqbufs(struct file *file, void *priv,
418 : struct v4l2_requestbuffers *p)
419 : {
420 : struct ipu6_isys_video *av = video_drvdata(file);
421 : int ret;
422 :
423 1 : av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
424 1 : av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
425 :
426 1 : ret = vb2_queue_change_type(&av->aq.vbq, p->type);
427 1 : if (ret)
428 : return ret;
429 :
430 1 : return vb2_ioctl_reqbufs(file, priv, p);
431 : }
432 :
433 0 : static int ipu6_isys_vidioc_create_bufs(struct file *file, void *priv,
434 : struct v4l2_create_buffers *p)
435 : {
436 : struct ipu6_isys_video *av = video_drvdata(file);
437 : int ret;
438 :
439 0 : av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
440 0 : av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
441 :
442 0 : ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
443 0 : if (ret)
444 : return ret;
445 :
446 0 : return vb2_ioctl_create_bufs(file, priv, p);
447 : }
448 :
449 1 : static int link_validate(struct media_link *link)
450 : {
451 : struct ipu6_isys_video *av =
452 1 : container_of(link->sink, struct ipu6_isys_video, pad);
453 1 : struct device *dev = &av->isys->adev->auxdev.dev;
454 : struct v4l2_subdev_state *s_state;
455 : struct v4l2_subdev *s_sd;
456 : struct v4l2_mbus_framefmt *s_fmt;
457 : struct media_pad *s_pad;
458 : u32 s_stream, code;
459 : int ret = -EPIPE;
460 :
461 1 : if (!link->source->entity)
462 : return ret;
463 :
464 : s_sd = media_entity_to_v4l2_subdev(link->source->entity);
465 : s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
466 1 : if (!s_state)
467 : return ret;
468 :
469 1 : dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
470 : link->source->entity->name, link->source->index,
471 : link->sink->entity->name);
472 :
473 1 : s_pad = media_pad_remote_pad_first(&av->pad);
474 1 : s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
475 :
476 : v4l2_subdev_lock_state(s_state);
477 :
478 1 : s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
479 1 : if (!s_fmt) {
480 0 : dev_err(dev, "failed to get source pad format\n");
481 0 : goto unlock;
482 : }
483 :
484 1 : code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;
485 :
486 2 : if (s_fmt->width != ipu6_isys_get_frame_width(av) ||
487 2 : s_fmt->height != ipu6_isys_get_frame_height(av) ||
488 1 : s_fmt->code != code) {
489 0 : dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
490 : s_fmt->width, s_fmt->height, s_fmt->code,
491 : ipu6_isys_get_frame_width(av),
492 : ipu6_isys_get_frame_height(av), code);
493 0 : goto unlock;
494 : }
495 :
496 : v4l2_subdev_unlock_state(s_state);
497 :
498 1 : return 0;
499 0 : unlock:
500 : v4l2_subdev_unlock_state(s_state);
501 :
502 0 : return ret;
503 : }
504 :
505 1 : static void get_stream_opened(struct ipu6_isys_video *av)
506 : {
507 : unsigned long flags;
508 :
509 1 : spin_lock_irqsave(&av->isys->streams_lock, flags);
510 1 : av->isys->stream_opened++;
511 1 : spin_unlock_irqrestore(&av->isys->streams_lock, flags);
512 1 : }
513 :
514 1 : static void put_stream_opened(struct ipu6_isys_video *av)
515 : {
516 : unsigned long flags;
517 :
518 1 : spin_lock_irqsave(&av->isys->streams_lock, flags);
519 1 : av->isys->stream_opened--;
520 1 : spin_unlock_irqrestore(&av->isys->streams_lock, flags);
521 1 : }
522 :
523 1 : static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
524 : struct ipu4_fw_isys_stream_cfg_data_abi *cfg)
525 : {
526 1 : struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
527 1 : struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
528 : struct ipu4_fw_isys_input_pin_info_abi *input_pin;
529 : struct ipu4_fw_isys_output_pin_info_abi *output_pin;
530 1 : struct ipu6_isys_stream *stream = av->stream;
531 1 : struct ipu6_isys_queue *aq = &av->aq;
532 1 : struct v4l2_mbus_framefmt fmt;
533 : const struct ipu6_isys_pixelformat *pfmt =
534 : ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
535 1 : struct v4l2_rect v4l2_crop;
536 1 : struct ipu6_isys *isys = av->isys;
537 1 : struct device *dev = &isys->adev->auxdev.dev;
538 1 : int input_pins = cfg->nof_input_pins++;
539 : int output_pins;
540 : u32 src_stream;
541 : int ret;
542 :
543 1 : src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
544 1 : ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
545 : &fmt);
546 1 : if (ret < 0) {
547 0 : dev_err(dev, "can't get stream format (%d)\n", ret);
548 0 : return ret;
549 : }
550 :
551 1 : ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
552 : &v4l2_crop);
553 1 : if (ret < 0) {
554 0 : dev_err(dev, "can't get stream crop (%d)\n", ret);
555 0 : return ret;
556 : }
557 :
558 : input_pin = &cfg->input_pins[input_pins];
559 1 : input_pin->input_res.width = fmt.width;
560 1 : input_pin->input_res.height = fmt.height;
561 1 : input_pin->dt = av->dt;
562 1 : input_pin->bits_per_pix = pfmt->bpp_packed;
563 1 : input_pin->mapped_dt = 0x40; /* invalid mipi data type */
564 : #ifdef IPU6 // Disabled for IPU4
565 : input_pin->mipi_decompression = 0;
566 : input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
567 : #endif
568 1 : input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ?
569 1 : IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
570 : IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
571 : #ifdef IPU6 // Disabled for IPU4
572 : input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
573 : #endif
574 1 : output_pins = cfg->nof_output_pins++;
575 1 : aq->fw_output = output_pins;
576 1 : stream->output_pins_queue[output_pins] = aq;
577 :
578 : output_pin = &cfg->output_pins[output_pins];
579 1 : output_pin->input_pin_id = input_pins;
580 1 : output_pin->output_res.width = ipu6_isys_get_frame_width(av);
581 1 : output_pin->output_res.height = ipu6_isys_get_frame_height(av);
582 :
583 1 : output_pin->stride = ipu6_isys_get_bytes_per_line(av);
584 1 : if (pfmt->bpp != pfmt->bpp_packed)
585 0 : output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
586 : else
587 1 : output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
588 1 : output_pin->ft = pfmt->css_pixelformat;
589 1 : output_pin->send_irq = 1;
590 : #ifdef IPU6 // Disabled for IPU4
591 : memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
592 : output_pin->s2m_pixel_soc_pixel_remapping =
593 : S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
594 : output_pin->csi_be_soc_pixel_remapping =
595 : CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
596 :
597 : output_pin->snoopable = true;
598 : output_pin->error_handling_enable = false;
599 : output_pin->sensor_type = isys->sensor_type++;
600 : if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
601 : isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
602 : #endif
603 : /* Though payload_buf_size was added for compression, set sane value for
604 : * payload_buf_size, just in case...
605 : */
606 1 : output_pin->payload_buf_size = output_pin->stride *
607 : output_pin->output_res.height;
608 :
609 1 : return 0;
610 : }
611 :
612 1 : static int start_stream_firmware(struct ipu6_isys_video *av,
613 : struct ipu6_isys_buffer_list *bl)
614 : {
615 : struct ipu4_fw_isys_stream_cfg_data_abi *stream_cfg;
616 : struct ipu4_fw_isys_frame_buff_set_abi *buf = NULL;
617 1 : struct ipu6_isys_stream *stream = av->stream;
618 1 : struct device *dev = &av->isys->adev->auxdev.dev;
619 : struct isys_fw_msgs *msg = NULL;
620 : struct ipu6_isys_queue *aq;
621 : int ret, retout, tout;
622 : u16 send_type;
623 :
624 1 : msg = ipu6_get_fw_msg_buf(stream);
625 1 : if (!msg)
626 : return -ENOMEM;
627 :
628 1 : stream_cfg = &msg->fw_msg.stream;
629 1 : stream_cfg->src = stream->stream_source;
630 1 : stream_cfg->vc = stream->vc;
631 1 : stream_cfg->isl_use = 0;
632 : #ifdef IPU6 // Disabled for IPU4
633 : stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
634 : #endif
635 :
636 2 : list_for_each_entry(aq, &stream->queues, node) {
637 : struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
638 :
639 1 : ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);
640 1 : if (ret < 0) {
641 0 : ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
642 0 : return ret;
643 : }
644 : }
645 :
646 1 : ipu4_fw_isys_dump_stream_cfg(dev, stream_cfg);
647 :
648 1 : stream->nr_output_pins = stream_cfg->nof_output_pins;
649 :
650 : reinit_completion(&stream->stream_open_completion);
651 :
652 1 : ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
653 : stream_cfg, msg->dma_addr,
654 : sizeof(*stream_cfg),
655 : IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
656 1 : if (ret < 0) {
657 0 : dev_err(dev, "can't open stream (%d)\n", ret);
658 0 : ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
659 0 : return ret;
660 : }
661 :
662 1 : get_stream_opened(av);
663 :
664 1 : tout = wait_for_completion_timeout(&stream->stream_open_completion,
665 : IPU6_FW_CALL_TIMEOUT_JIFFIES);
666 :
667 1 : ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
668 :
669 1 : if (!tout) {
670 0 : dev_err(dev, "stream open time out\n");
671 : ret = -ETIMEDOUT;
672 0 : goto out_put_stream_opened;
673 : }
674 1 : if (stream->error) {
675 0 : dev_err(dev, "stream open error: %d\n", stream->error);
676 : ret = -EIO;
677 0 : goto out_put_stream_opened;
678 : }
679 1 : dev_dbg(dev, "start stream: open complete\n");
680 :
681 1 : if (bl) {
682 1 : msg = ipu6_get_fw_msg_buf(stream);
683 1 : if (!msg) {
684 : ret = -ENOMEM;
685 0 : goto out_put_stream_opened;
686 : }
687 1 : buf = &msg->fw_msg.frame;
688 1 : ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
689 1 : ipu6_isys_buffer_list_queue(bl,
690 : IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
691 : }
692 :
693 : reinit_completion(&stream->stream_start_completion);
694 :
695 1 : if (bl) {
696 : send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
697 1 : ipu4_fw_isys_dump_frame_buff_set(dev, buf,
698 1 : stream_cfg->nof_output_pins);
699 1 : ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
700 : buf, msg->dma_addr,
701 : sizeof(*buf), send_type);
702 : } else {
703 : send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
704 0 : ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
705 : send_type);
706 : }
707 :
708 1 : if (ret < 0) {
709 0 : dev_err(dev, "can't start streaming (%d)\n", ret);
710 0 : goto out_stream_close;
711 : }
712 :
713 1 : tout = wait_for_completion_timeout(&stream->stream_start_completion,
714 : IPU6_FW_CALL_TIMEOUT_JIFFIES);
715 1 : if (!tout) {
716 0 : dev_err(dev, "stream start time out\n");
717 : ret = -ETIMEDOUT;
718 0 : goto out_stream_close;
719 : }
720 1 : if (stream->error) {
721 0 : dev_err(dev, "stream start error: %d\n", stream->error);
722 : ret = -EIO;
723 0 : goto out_stream_close;
724 : }
725 1 : dev_dbg(dev, "start stream: complete\n");
726 :
727 : return 0;
728 :
729 0 : out_stream_close:
730 : reinit_completion(&stream->stream_close_completion);
731 :
732 0 : retout = ipu6_fw_isys_simple_cmd(av->isys,
733 0 : stream->stream_handle,
734 : IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
735 0 : if (retout < 0) {
736 0 : dev_dbg(dev, "can't close stream (%d)\n", retout);
737 0 : goto out_put_stream_opened;
738 : }
739 :
740 0 : tout = wait_for_completion_timeout(&stream->stream_close_completion,
741 : IPU6_FW_CALL_TIMEOUT_JIFFIES);
742 0 : if (!tout)
743 0 : dev_err(dev, "stream close time out\n");
744 0 : else if (stream->error)
745 0 : dev_err(dev, "stream close error: %d\n", stream->error);
746 : else
747 0 : dev_dbg(dev, "stream close complete\n");
748 :
749 0 : out_put_stream_opened:
750 0 : put_stream_opened(av);
751 :
752 0 : return ret;
753 : }
754 :
755 1 : static void stop_streaming_firmware(struct ipu6_isys_video *av)
756 : {
757 1 : struct device *dev = &av->isys->adev->auxdev.dev;
758 1 : struct ipu6_isys_stream *stream = av->stream;
759 : int ret, tout;
760 :
761 : reinit_completion(&stream->stream_stop_completion);
762 :
763 1 : ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
764 : IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
765 :
766 1 : if (ret < 0) {
767 0 : dev_err(dev, "can't stop stream (%d)\n", ret);
768 0 : return;
769 : }
770 :
771 1 : tout = wait_for_completion_timeout(&stream->stream_stop_completion,
772 : IPU6_FW_CALL_TIMEOUT_JIFFIES);
773 1 : if (!tout)
774 0 : dev_warn(dev, "stream stop time out\n");
775 1 : else if (stream->error)
776 0 : dev_warn(dev, "stream stop error: %d\n", stream->error);
777 : else
778 1 : dev_dbg(dev, "stop stream: complete\n");
779 : }
780 :
781 1 : static void close_streaming_firmware(struct ipu6_isys_video *av)
782 : {
783 1 : struct ipu6_isys_stream *stream = av->stream;
784 1 : struct device *dev = &av->isys->adev->auxdev.dev;
785 : int ret, tout;
786 :
787 : reinit_completion(&stream->stream_close_completion);
788 :
789 1 : ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
790 : IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
791 1 : if (ret < 0) {
792 0 : dev_err(dev, "can't close stream (%d)\n", ret);
793 0 : return;
794 : }
795 :
796 1 : tout = wait_for_completion_timeout(&stream->stream_close_completion,
797 : IPU6_FW_CALL_TIMEOUT_JIFFIES);
798 1 : if (!tout)
799 0 : dev_warn(dev, "stream close time out\n");
800 1 : else if (stream->error)
801 0 : dev_warn(dev, "stream close error: %d\n", stream->error);
802 : else
803 1 : dev_dbg(dev, "close stream: complete\n");
804 :
805 1 : put_stream_opened(av);
806 : }
807 :
808 1 : int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
809 : struct media_entity *source_entity,
810 : int nr_queues)
811 : {
812 1 : struct ipu6_isys_stream *stream = av->stream;
813 : struct ipu6_isys_csi2 *csi2;
814 :
815 1 : if (WARN_ON(stream->nr_streaming))
816 0 : return -EINVAL;
817 :
818 1 : stream->nr_queues = nr_queues;
819 : atomic_set(&stream->sequence, 0);
820 :
821 1 : stream->seq_index = 0;
822 1 : memset(stream->seq, 0, sizeof(stream->seq));
823 :
824 1 : if (WARN_ON(!list_empty(&stream->queues)))
825 : return -EINVAL;
826 :
827 1 : stream->stream_source = stream->asd->source;
828 : csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
829 1 : csi2->receiver_errors = 0;
830 1 : stream->source_entity = source_entity;
831 :
832 1 : dev_dbg(&av->isys->adev->auxdev.dev,
833 : "prepare stream: external entity %s\n",
834 : stream->source_entity->name);
835 :
836 : return 0;
837 : }
838 :
839 : #ifdef IPU6 // Disabled for IPU4
840 : void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
841 : bool state)
842 : {
843 : struct ipu6_isys *isys = av->isys;
844 : struct ipu6_isys_csi2 *csi2 = NULL;
845 : struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
846 : struct device *dev = &isys->adev->auxdev.dev;
847 : struct v4l2_mbus_framefmt format;
848 : struct v4l2_subdev *esd;
849 : struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
850 : unsigned int bpp, lanes;
851 : s64 link_freq = 0;
852 : u64 pixel_rate = 0;
853 : int ret;
854 :
855 : if (!state)
856 : return;
857 :
858 : esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
859 :
860 : av->watermark.width = ipu6_isys_get_frame_width(av);
861 : av->watermark.height = ipu6_isys_get_frame_height(av);
862 : av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
863 : av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
864 :
865 : ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);
866 : if (!ret && hb.value >= 0)
867 : av->watermark.hblank = hb.value;
868 : else
869 : av->watermark.hblank = 0;
870 :
871 : csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
872 : link_freq = ipu6_isys_csi2_get_link_freq(csi2);
873 : if (link_freq > 0) {
874 : lanes = csi2->nlanes;
875 : ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
876 : av->source_stream, &format);
877 : if (!ret) {
878 : bpp = ipu6_isys_mbus_code_to_bpp(format.code);
879 : pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
880 : }
881 : }
882 :
883 : av->watermark.pixel_rate = pixel_rate;
884 :
885 : if (!pixel_rate) {
886 : mutex_lock(&iwake_watermark->mutex);
887 : iwake_watermark->force_iwake_disable = true;
888 : mutex_unlock(&iwake_watermark->mutex);
889 : dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
890 : av->stream->source_entity->name);
891 : }
892 : }
893 :
894 : static void calculate_stream_datarate(struct ipu6_isys_video *av)
895 : {
896 : struct video_stream_watermark *watermark = &av->watermark;
897 : const struct ipu6_isys_pixelformat *pfmt =
898 : ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
899 : u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;
900 : u64 line_time_ns, stream_data_rate;
901 : u16 shift, size;
902 :
903 : shift = watermark->sram_gran_shift;
904 : size = watermark->sram_gran_size;
905 :
906 : pixels_per_line = watermark->width + watermark->hblank;
907 : line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC,
908 : watermark->pixel_rate);
909 : bytes_per_line = watermark->width * pfmt->bpp / 8;
910 : pages_per_line = DIV_ROUND_UP(bytes_per_line, size);
911 : pb_bytes_per_line = pages_per_line << shift;
912 : stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);
913 :
914 : watermark->stream_data_rate = stream_data_rate;
915 : }
916 :
917 : void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)
918 : {
919 : struct isys_iwake_watermark *iwake_watermark =
920 : &av->isys->iwake_watermark;
921 :
922 : if (!av->watermark.pixel_rate)
923 : return;
924 :
925 : if (state) {
926 : calculate_stream_datarate(av);
927 : mutex_lock(&iwake_watermark->mutex);
928 : list_add(&av->watermark.stream_node,
929 : &iwake_watermark->video_list);
930 : mutex_unlock(&iwake_watermark->mutex);
931 : } else {
932 : av->watermark.stream_data_rate = 0;
933 : mutex_lock(&iwake_watermark->mutex);
934 : list_del(&av->watermark.stream_node);
935 : mutex_unlock(&iwake_watermark->mutex);
936 : }
937 :
938 : update_watermark_setting(av->isys);
939 : }
940 : #endif
941 :
942 9 : void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
943 : {
944 : struct device *dev;
945 : unsigned int i;
946 : unsigned long flags;
947 :
948 9 : if (!stream) {
949 0 : pr_err("ipu6-isys: no available stream\n");
950 0 : return;
951 : }
952 :
953 9 : dev = &stream->isys->adev->auxdev.dev;
954 :
955 9 : spin_lock_irqsave(&stream->isys->streams_lock, flags);
956 9 : for (i = 0; i < IPU4_ISYS_MAX_STREAMS; i++) {
957 9 : if (&stream->isys->streams[i] == stream) {
958 9 : if (stream->isys->streams_ref_count[i] > 0)
959 9 : stream->isys->streams_ref_count[i]--;
960 : else
961 0 : dev_warn(dev, "invalid stream %d\n", i);
962 :
963 : break;
964 : }
965 : }
966 9 : spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
967 : }
968 :
969 : static struct ipu6_isys_stream *
970 1 : ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)
971 : {
972 : struct ipu6_isys_stream *stream = NULL;
973 1 : struct ipu6_isys *isys = av->isys;
974 : unsigned long flags;
975 : unsigned int i;
976 1 : u8 vc = av->vc;
977 :
978 1 : if (!isys)
979 : return NULL;
980 :
981 1 : spin_lock_irqsave(&isys->streams_lock, flags);
982 9 : for (i = 0; i < IPU4_ISYS_MAX_STREAMS; i++) {
983 8 : if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
984 0 : isys->streams[i].asd == asd) {
985 0 : isys->streams_ref_count[i]++;
986 0 : stream = &isys->streams[i];
987 0 : break;
988 : }
989 : }
990 :
991 1 : if (!stream) {
992 1 : for (i = 0; i < IPU4_ISYS_MAX_STREAMS; i++) {
993 1 : if (!isys->streams_ref_count[i]) {
994 1 : isys->streams_ref_count[i]++;
995 1 : stream = &isys->streams[i];
996 1 : stream->vc = vc;
997 1 : stream->asd = asd;
998 1 : break;
999 : }
1000 : }
1001 : }
1002 : spin_unlock_irqrestore(&isys->streams_lock, flags);
1003 :
1004 1 : return stream;
1005 : }
1006 :
1007 : struct ipu6_isys_stream *
1008 8 : ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)
1009 : {
1010 : unsigned long flags;
1011 : struct ipu6_isys_stream *stream = NULL;
1012 :
1013 8 : if (!isys)
1014 : return NULL;
1015 :
1016 8 : if (stream_handle >= IPU4_ISYS_MAX_STREAMS) {
1017 0 : dev_err(&isys->adev->auxdev.dev,
1018 : "stream_handle %d is invalid\n", stream_handle);
1019 0 : return NULL;
1020 : }
1021 :
1022 8 : spin_lock_irqsave(&isys->streams_lock, flags);
1023 8 : if (isys->streams_ref_count[stream_handle] > 0) {
1024 8 : isys->streams_ref_count[stream_handle]++;
1025 8 : stream = &isys->streams[stream_handle];
1026 : }
1027 : spin_unlock_irqrestore(&isys->streams_lock, flags);
1028 :
1029 8 : return stream;
1030 : }
1031 :
1032 : struct ipu6_isys_stream *
1033 0 : ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)
1034 : {
1035 : struct ipu6_isys_stream *stream = NULL;
1036 : unsigned long flags;
1037 : unsigned int i;
1038 :
1039 0 : if (!isys)
1040 : return NULL;
1041 :
1042 0 : if (source < 0) {
1043 0 : dev_err(&isys->adev->auxdev.dev,
1044 : "query stream with invalid port number\n");
1045 0 : return NULL;
1046 : }
1047 :
1048 0 : spin_lock_irqsave(&isys->streams_lock, flags);
1049 0 : for (i = 0; i < IPU4_ISYS_MAX_STREAMS; i++) {
1050 0 : if (!isys->streams_ref_count[i])
1051 0 : continue;
1052 :
1053 0 : if (isys->streams[i].stream_source == source &&
1054 0 : isys->streams[i].vc == vc) {
1055 0 : stream = &isys->streams[i];
1056 0 : isys->streams_ref_count[i]++;
1057 0 : break;
1058 : }
1059 : }
1060 : spin_unlock_irqrestore(&isys->streams_lock, flags);
1061 :
1062 0 : return stream;
1063 : }
1064 :
1065 2 : static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av)
1066 : {
1067 : struct media_pipeline *pipeline =
1068 2 : media_entity_pipeline(&__av->vdev.entity);
1069 : struct media_entity *entity;
1070 : unsigned int i;
1071 : u64 stream_mask = 0;
1072 :
1073 64 : for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) {
1074 62 : entity = &__av->isys->av[i].vdev.entity;
1075 62 : if (pipeline == media_entity_pipeline(entity))
1076 2 : stream_mask |= BIT_ULL(__av->isys->av[i].source_stream);
1077 : }
1078 :
1079 2 : return stream_mask;
1080 : }
1081 :
1082 2 : int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
1083 : struct ipu6_isys_buffer_list *bl)
1084 : {
1085 : struct v4l2_subdev_krouting *routing;
1086 2 : struct ipu6_isys_stream *stream = av->stream;
1087 : struct v4l2_subdev_state *subdev_state;
1088 2 : struct device *dev = &av->isys->adev->auxdev.dev;
1089 : struct v4l2_subdev *sd;
1090 : struct media_pad *r_pad;
1091 2 : u32 sink_pad, sink_stream;
1092 : u64 r_stream;
1093 : u64 stream_mask = 0;
1094 : int ret = 0;
1095 :
1096 2 : dev_dbg(dev, "set stream: %d\n", state);
1097 :
1098 2 : if (WARN(!stream->source_entity, "No source entity for stream\n"))
1099 0 : return -ENODEV;
1100 :
1101 2 : sd = &stream->asd->sd;
1102 2 : r_pad = media_pad_remote_pad_first(&av->pad);
1103 2 : r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
1104 :
1105 : subdev_state = v4l2_subdev_lock_and_get_active_state(sd);
1106 2 : routing = &subdev_state->routing;
1107 2 : ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,
1108 : r_stream, &sink_pad,
1109 : &sink_stream);
1110 : v4l2_subdev_unlock_state(subdev_state);
1111 2 : if (ret)
1112 : return ret;
1113 :
1114 2 : stream_mask = get_stream_mask_by_pipeline(av);
1115 2 : if (!state) {
1116 1 : stop_streaming_firmware(av);
1117 :
1118 : /* stop sub-device which connects with video */
1119 1 : dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n",
1120 : sd->name, r_pad->index, stream_mask);
1121 1 : ret = v4l2_subdev_disable_streams(sd, r_pad->index,
1122 : stream_mask);
1123 1 : if (ret) {
1124 0 : dev_err(dev, "stream off %s failed with %d\n", sd->name,
1125 : ret);
1126 0 : return ret;
1127 : }
1128 1 : close_streaming_firmware(av);
1129 : } else {
1130 1 : ret = start_stream_firmware(av, bl);
1131 1 : if (ret) {
1132 0 : dev_err(dev, "start stream of firmware failed\n");
1133 0 : return ret;
1134 : }
1135 :
1136 : /* start sub-device which connects with video */
1137 1 : dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name,
1138 : r_pad->index, stream_mask);
1139 1 : ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask);
1140 1 : if (ret) {
1141 0 : dev_err(dev, "stream on %s failed with %d\n", sd->name,
1142 : ret);
1143 0 : goto out_media_entity_stop_streaming_firmware;
1144 : }
1145 : }
1146 :
1147 2 : av->streaming = state;
1148 :
1149 2 : return 0;
1150 :
1151 : out_media_entity_stop_streaming_firmware:
1152 0 : stop_streaming_firmware(av);
1153 :
1154 0 : return ret;
1155 : }
1156 :
1157 : static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = {
1158 : .vidioc_querycap = ipu6_isys_vidioc_querycap,
1159 : .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
1160 : .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,
1161 : .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
1162 : .vidioc_g_fmt_vid_cap = ipu6_isys_vidioc_g_fmt_vid_cap,
1163 : .vidioc_s_fmt_vid_cap = ipu6_isys_vidioc_s_fmt_vid_cap,
1164 : .vidioc_try_fmt_vid_cap = ipu6_isys_vidioc_try_fmt_vid_cap,
1165 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1166 : .vidioc_g_fmt_meta_cap = ipu6_isys_vidioc_g_fmt_meta_cap,
1167 : .vidioc_s_fmt_meta_cap = ipu6_isys_vidioc_s_fmt_meta_cap,
1168 : .vidioc_try_fmt_meta_cap = ipu6_isys_vidioc_try_fmt_meta_cap,
1169 : #endif
1170 : .vidioc_reqbufs = ipu6_isys_vidioc_reqbufs,
1171 : .vidioc_create_bufs = ipu6_isys_vidioc_create_bufs,
1172 : .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
1173 : .vidioc_querybuf = vb2_ioctl_querybuf,
1174 : .vidioc_qbuf = vb2_ioctl_qbuf,
1175 : .vidioc_dqbuf = vb2_ioctl_dqbuf,
1176 : .vidioc_streamon = vb2_ioctl_streamon,
1177 : .vidioc_streamoff = vb2_ioctl_streamoff,
1178 : .vidioc_expbuf = vb2_ioctl_expbuf,
1179 : };
1180 :
1181 : static const struct media_entity_operations entity_ops = {
1182 : .link_validate = link_validate,
1183 : };
1184 :
1185 : static const struct v4l2_file_operations isys_fops = {
1186 : .owner = THIS_MODULE,
1187 : .poll = vb2_fop_poll,
1188 : .unlocked_ioctl = video_ioctl2,
1189 : .mmap = vb2_fop_mmap,
1190 : .open = video_open,
1191 : .release = vb2_fop_release,
1192 : };
1193 :
1194 1 : int ipu6_isys_fw_get(struct ipu6_isys *isys)
1195 : {
1196 1 : struct ipu6_bus_device *adev = isys->adev;
1197 : int ret;
1198 :
1199 1 : ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
1200 1 : if (ret < 0)
1201 : return ret;
1202 :
1203 1 : mutex_lock(&isys->mutex);
1204 1 : wait_for_not_resetting(isys, __func__);
1205 :
1206 1 : if (isys->ref_count++)
1207 0 : goto unlock;
1208 :
1209 1 : ret = ipu6_fw_isys_open(isys);
1210 1 : if (ret < 0)
1211 0 : goto out;
1212 :
1213 1 : unlock:
1214 1 : mutex_unlock(&isys->mutex);
1215 :
1216 1 : return 0;
1217 :
1218 : out:
1219 0 : isys->ref_count--;
1220 0 : mutex_unlock(&isys->mutex);
1221 : pm_runtime_put(&adev->auxdev.dev);
1222 :
1223 0 : return ret;
1224 : }
1225 :
1226 1 : void ipu6_isys_fw_put(struct ipu6_isys *isys)
1227 : {
1228 1 : struct device *dev = &isys->adev->auxdev.dev;
1229 :
1230 1 : mutex_lock(&isys->mutex);
1231 1 : wait_for_not_resetting(isys, __func__);
1232 :
1233 1 : isys->ref_count--;
1234 1 : if (!isys->ref_count) {
1235 1 : ipu6_fw_isys_close(isys);
1236 1 : if (isys->fwcom) {
1237 0 : dev_info(dev, "%s: Setting need_reset\n", __func__);
1238 0 : isys->need_reset = true;
1239 0 : dev_warn(&isys->adev->auxdev.dev,
1240 : "failed to close fw isys\n");
1241 : }
1242 : }
1243 :
1244 1 : mutex_unlock(&isys->mutex);
1245 :
1246 1 : if (isys->need_reset)
1247 0 : pm_runtime_put_sync(&isys->adev->auxdev.dev);
1248 : else
1249 1 : pm_runtime_put(&isys->adev->auxdev.dev);
1250 1 : }
1251 :
1252 1 : int ipu6_isys_setup_video(struct ipu6_isys_video *av,
1253 : struct media_entity **source_entity, int *nr_queues)
1254 : {
1255 : const struct ipu6_isys_pixelformat *pfmt =
1256 : ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
1257 1 : struct device *dev = &av->isys->adev->auxdev.dev;
1258 1 : struct v4l2_mbus_frame_desc_entry entry;
1259 : struct v4l2_subdev_route *route = NULL;
1260 : struct v4l2_subdev_route *r;
1261 : struct v4l2_subdev_state *state;
1262 : struct ipu6_isys_subdev *asd;
1263 : struct v4l2_subdev *remote_sd;
1264 : struct media_pipeline *pipeline;
1265 : struct media_pad *source_pad, *remote_pad;
1266 : int ret = -EINVAL;
1267 :
1268 1 : *nr_queues = 0;
1269 :
1270 1 : remote_pad = media_pad_remote_pad_unique(&av->pad);
1271 1 : if (IS_ERR(remote_pad)) {
1272 0 : dev_dbg(dev, "failed to get remote pad\n");
1273 0 : return PTR_ERR(remote_pad);
1274 : }
1275 :
1276 1 : remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
1277 : asd = to_ipu6_isys_subdev(remote_sd);
1278 1 : source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
1279 1 : if (!source_pad) {
1280 0 : dev_dbg(dev, "No external source entity\n");
1281 0 : return -ENODEV;
1282 : }
1283 :
1284 1 : *source_entity = source_pad->entity;
1285 :
1286 : /* Find the root */
1287 : state = v4l2_subdev_lock_and_get_active_state(remote_sd);
1288 2 : for_each_active_route(&state->routing, r) {
1289 1 : (*nr_queues)++;
1290 :
1291 1 : if (r->source_pad == remote_pad->index)
1292 : route = r;
1293 : }
1294 :
1295 1 : if (!route) {
1296 : v4l2_subdev_unlock_state(state);
1297 0 : dev_dbg(dev, "Failed to find route\n");
1298 0 : return -ENODEV;
1299 : }
1300 1 : av->source_stream = route->sink_stream;
1301 : v4l2_subdev_unlock_state(state);
1302 :
1303 1 : ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
1304 : to_ipu6_isys_csi2(asd),
1305 : *source_entity, &entry);
1306 1 : if (ret == -ENOIOCTLCMD) {
1307 1 : av->vc = 0;
1308 1 : av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code);
1309 0 : } else if (!ret) {
1310 0 : dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
1311 : entry.stream, entry.length, entry.bus.csi2.vc,
1312 : entry.bus.csi2.dt);
1313 :
1314 0 : av->vc = entry.bus.csi2.vc;
1315 0 : av->dt = entry.bus.csi2.dt;
1316 : } else {
1317 0 : dev_err(dev, "failed to get remote frame desc\n");
1318 0 : return ret;
1319 : }
1320 :
1321 1 : pipeline = media_entity_pipeline(&av->vdev.entity);
1322 1 : if (!pipeline)
1323 1 : ret = video_device_pipeline_alloc_start(&av->vdev);
1324 : else
1325 0 : ret = video_device_pipeline_start(&av->vdev, pipeline);
1326 1 : if (ret < 0) {
1327 0 : dev_dbg(dev, "media pipeline start failed\n");
1328 0 : return ret;
1329 : }
1330 :
1331 1 : av->stream = ipu6_isys_get_stream(av, asd);
1332 1 : if (!av->stream) {
1333 0 : video_device_pipeline_stop(&av->vdev);
1334 0 : dev_err(dev, "no available stream for firmware\n");
1335 0 : return -EINVAL;
1336 : }
1337 :
1338 : return 0;
1339 : }
1340 :
1341 : /*
1342 : * Do everything that's needed to initialise things related to video
1343 : * buffer queue, video node, and the related media entity. The caller
1344 : * is expected to assign isys field and set the name of the video
1345 : * device.
1346 : */
1347 31 : int ipu6_isys_video_init(struct ipu6_isys_video *av)
1348 : {
1349 31 : struct v4l2_format format = {
1350 : .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
1351 : .fmt.pix = {
1352 : .width = 1920,
1353 : .height = 1080,
1354 : },
1355 : };
1356 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1357 31 : struct v4l2_format format_meta = {
1358 : .type = V4L2_BUF_TYPE_META_CAPTURE,
1359 : .fmt.meta = {
1360 : .width = 1920,
1361 : .height = 4,
1362 : },
1363 : };
1364 : #endif
1365 : int ret;
1366 :
1367 31 : mutex_init(&av->mutex);
1368 31 : av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
1369 : V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
1370 31 : av->vdev.vfl_dir = VFL_DIR_RX;
1371 :
1372 31 : ret = ipu6_isys_queue_init(&av->aq);
1373 31 : if (ret)
1374 0 : goto out_free_watermark;
1375 :
1376 31 : av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
1377 31 : ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
1378 31 : if (ret)
1379 0 : goto out_vb2_queue_release;
1380 :
1381 31 : av->vdev.entity.ops = &entity_ops;
1382 31 : av->vdev.release = video_device_release_empty;
1383 31 : av->vdev.fops = &isys_fops;
1384 31 : av->vdev.v4l2_dev = &av->isys->v4l2_dev;
1385 31 : av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev;
1386 31 : if (!av->vdev.ioctl_ops)
1387 31 : av->vdev.ioctl_ops = &ipu6_v4l2_ioctl_ops;
1388 31 : av->vdev.queue = &av->aq.vbq;
1389 31 : av->vdev.lock = &av->mutex;
1390 :
1391 31 : __ipu6_isys_vidioc_try_fmt_vid_cap(av, &format);
1392 31 : av->pix_fmt = format.fmt.pix;
1393 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1394 31 : __ipu6_isys_vidioc_try_fmt_meta_cap(av, &format_meta);
1395 31 : av->meta_fmt = format_meta.fmt.meta;
1396 : #endif
1397 :
1398 : video_set_drvdata(&av->vdev, av);
1399 :
1400 31 : ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
1401 31 : if (ret)
1402 0 : goto out_media_entity_cleanup;
1403 :
1404 : return ret;
1405 :
1406 : out_media_entity_cleanup:
1407 0 : vb2_video_unregister_device(&av->vdev);
1408 : media_entity_cleanup(&av->vdev.entity);
1409 :
1410 0 : out_vb2_queue_release:
1411 0 : vb2_queue_release(&av->aq.vbq);
1412 :
1413 : out_free_watermark:
1414 : mutex_destroy(&av->mutex);
1415 :
1416 : return ret;
1417 : }
1418 :
1419 0 : void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
1420 : {
1421 0 : vb2_video_unregister_device(&av->vdev);
1422 : media_entity_cleanup(&av->vdev.entity);
1423 : mutex_destroy(&av->mutex);
1424 0 : }
1425 :
1426 2 : u32 ipu6_isys_get_format(struct ipu6_isys_video *av)
1427 : {
1428 5 : if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1429 5 : return av->pix_fmt.pixelformat;
1430 :
1431 0 : if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1432 0 : return av->meta_fmt.dataformat;
1433 :
1434 : return 0;
1435 : }
1436 :
1437 3 : u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av)
1438 : {
1439 3 : if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1440 3 : return av->pix_fmt.sizeimage;
1441 :
1442 0 : if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1443 0 : return av->meta_fmt.buffersize;
1444 :
1445 : return 0;
1446 : }
1447 :
1448 2 : u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av)
1449 : {
1450 3 : if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1451 3 : return av->pix_fmt.bytesperline;
1452 :
1453 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1454 0 : if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1455 0 : return av->meta_fmt.bytesperline;
1456 : #endif
1457 :
1458 : return 0;
1459 : }
1460 :
1461 2 : u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av)
1462 : {
1463 4 : if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1464 4 : return av->pix_fmt.width;
1465 :
1466 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1467 0 : if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1468 0 : return av->meta_fmt.width;
1469 : #endif
1470 :
1471 : return 0;
1472 : }
1473 :
1474 4 : u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av)
1475 : {
1476 6 : if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1477 6 : return av->pix_fmt.height;
1478 :
1479 : #if KERNEL_VERSION(6, 10, 0) <= LINUX_VERSION_CODE
1480 0 : if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1481 0 : return av->meta_fmt.height;
1482 : #endif
1483 :
1484 : return 0;
1485 : }
|