1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2013 - 2025 Intel Corporation
4 */
5
6 #include <linux/atomic.h>
7 #include <linux/bits.h>
8 #include <linux/bug.h>
9 #include <linux/delay.h>
10 #include <linux/device.h>
11 #include <linux/io.h>
12 #include <linux/minmax.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
15
16 #include <media/media-entity.h>
17 #include <media/v4l2-ctrls.h>
18 #include <media/v4l2-device.h>
19 #include <media/v4l2-event.h>
20 #include <media/v4l2-subdev.h>
21
22 #include "ipu7.h"
23 #include "ipu7-bus.h"
24 #include "ipu7-isys.h"
25 #include "ipu7-isys-csi2.h"
26 #include "ipu7-isys-csi2-regs.h"
27 #include "ipu7-isys-csi-phy.h"
28
29 static const u32 csi2_supported_codes[] = {
30 MEDIA_BUS_FMT_Y10_1X10,
31 MEDIA_BUS_FMT_RGB565_1X16,
32 MEDIA_BUS_FMT_RGB888_1X24,
33 MEDIA_BUS_FMT_UYVY8_1X16,
34 MEDIA_BUS_FMT_YUYV8_1X16,
35 MEDIA_BUS_FMT_YUYV10_1X20,
36 MEDIA_BUS_FMT_SBGGR10_1X10,
37 MEDIA_BUS_FMT_SGBRG10_1X10,
38 MEDIA_BUS_FMT_SGRBG10_1X10,
39 MEDIA_BUS_FMT_SRGGB10_1X10,
40 MEDIA_BUS_FMT_SBGGR12_1X12,
41 MEDIA_BUS_FMT_SGBRG12_1X12,
42 MEDIA_BUS_FMT_SGRBG12_1X12,
43 MEDIA_BUS_FMT_SRGGB12_1X12,
44 MEDIA_BUS_FMT_SBGGR8_1X8,
45 MEDIA_BUS_FMT_SGBRG8_1X8,
46 MEDIA_BUS_FMT_SGRBG8_1X8,
47 MEDIA_BUS_FMT_SRGGB8_1X8,
48 0,
49 };
50
ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 * csi2)51 s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2)
52 {
53 struct media_pad *src_pad;
54
55 src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
56 if (IS_ERR(src_pad)) {
57 dev_err(&csi2->isys->adev->auxdev.dev,
58 "can't get source pad of %s (%ld)\n",
59 csi2->asd.sd.name, PTR_ERR(src_pad));
60 return PTR_ERR(src_pad);
61 }
62
63 return v4l2_get_link_freq(src_pad, 0, 0);
64 }
65
csi2_subscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)66 static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
67 struct v4l2_event_subscription *sub)
68 {
69 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
70 struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
71 struct device *dev = &csi2->isys->adev->auxdev.dev;
72
73 dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
74 sub->type, sub->id);
75
76 switch (sub->type) {
77 case V4L2_EVENT_FRAME_SYNC:
78 return v4l2_event_subscribe(fh, sub, 10, NULL);
79 case V4L2_EVENT_CTRL:
80 return v4l2_ctrl_subscribe_event(fh, sub);
81 default:
82 return -EINVAL;
83 }
84 }
85
86 static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
87 .subscribe_event = csi2_subscribe_event,
88 .unsubscribe_event = v4l2_event_subdev_unsubscribe,
89 };
90
csi2_irq_enable(struct ipu7_isys_csi2 * csi2)91 static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2)
92 {
93 struct ipu7_device *isp = csi2->isys->adev->isp;
94 unsigned int offset, mask;
95
96 /* enable CSI2 legacy error irq */
97 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
98 mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
99 writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
100 writel(mask, csi2->base + offset + IRQ_CTL_MASK);
101 writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
102
103 /* enable CSI2 legacy sync irq */
104 offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
105 mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
106 writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
107 writel(mask, csi2->base + offset + IRQ_CTL_MASK);
108 writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
109
110 mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK;
111 if (!is_ipu7(isp->hw_ver)) {
112 writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
113 writel(mask, csi2->base + offset + IRQ1_CTL_MASK);
114 writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE);
115 }
116 }
117
csi2_irq_disable(struct ipu7_isys_csi2 * csi2)118 static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2)
119 {
120 struct ipu7_device *isp = csi2->isys->adev->isp;
121 unsigned int offset, mask;
122
123 /* disable CSI2 legacy error irq */
124 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
125 mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
126 writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
127 writel(0, csi2->base + offset + IRQ_CTL_MASK);
128 writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
129
130 /* disable CSI2 legacy sync irq */
131 offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
132 mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
133 writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
134 writel(0, csi2->base + offset + IRQ_CTL_MASK);
135 writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
136
137 if (!is_ipu7(isp->hw_ver)) {
138 writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
139 writel(0, csi2->base + offset + IRQ1_CTL_MASK);
140 writel(0, csi2->base + offset + IRQ1_CTL_ENABLE);
141 }
142 }
143
ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 * csi2)144 static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2)
145 {
146 struct ipu7_isys *isys = csi2->isys;
147 void __iomem *isys_base = isys->pdata->base;
148
149 ipu7_isys_csi_phy_powerdown(csi2);
150
151 writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK);
152 csi2_irq_disable(csi2);
153 }
154
ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 * csi2)155 static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2)
156 {
157 struct ipu7_isys *isys = csi2->isys;
158 struct device *dev = &isys->adev->auxdev.dev;
159 void __iomem *isys_base = isys->pdata->base;
160 unsigned int port, nlanes, offset;
161 int ret;
162
163 port = csi2->port;
164 nlanes = csi2->nlanes;
165
166 offset = IS_IO_GPREGS_BASE;
167 writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK);
168 dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n",
169 port, readl(isys_base + offset + CSI_PORT_CLK_GATE),
170 readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK));
171 if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) {
172 dev_dbg(dev, "CSI port %u in aggregation mode\n", port);
173 writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION);
174 }
175
176 /* input is coming from CSI receiver (sensor) */
177 offset = IS_IO_CSI2_ADPL_PORT_BASE(port);
178 writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE);
179 writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN);
180
181 ret = ipu7_isys_csi_phy_powerup(csi2);
182 if (ret) {
183 dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret);
184 return ret;
185 }
186
187 csi2_irq_enable(csi2);
188
189 return 0;
190 }
191
ipu7_isys_csi2_set_sel(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_selection * sel)192 static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd,
193 struct v4l2_subdev_state *state,
194 struct v4l2_subdev_selection *sel)
195 {
196 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
197 struct device *dev = &asd->isys->adev->auxdev.dev;
198 struct v4l2_mbus_framefmt *sink_ffmt;
199 struct v4l2_mbus_framefmt *src_ffmt;
200 struct v4l2_rect *crop;
201
202 if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
203 return -EINVAL;
204
205 sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
206 sel->pad,
207 sel->stream);
208 if (!sink_ffmt)
209 return -EINVAL;
210
211 src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
212 if (!src_ffmt)
213 return -EINVAL;
214
215 crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
216 if (!crop)
217 return -EINVAL;
218
219 /* Only vertical cropping is supported */
220 sel->r.left = 0;
221 sel->r.width = sink_ffmt->width;
222 /* Non-bayer formats can't be single line cropped */
223 if (!ipu7_isys_is_bayer_format(sink_ffmt->code))
224 sel->r.top &= ~1U;
225 sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT,
226 sink_ffmt->height - sel->r.top);
227 *crop = sel->r;
228
229 /* update source pad format */
230 src_ffmt->width = sel->r.width;
231 src_ffmt->height = sel->r.height;
232 if (ipu7_isys_is_bayer_format(sink_ffmt->code))
233 src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code,
234 sel->r.left,
235 sel->r.top);
236 dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
237 sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
238 src_ffmt->code);
239
240 return 0;
241 }
242
ipu7_isys_csi2_get_sel(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_selection * sel)243 static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd,
244 struct v4l2_subdev_state *state,
245 struct v4l2_subdev_selection *sel)
246 {
247 struct v4l2_mbus_framefmt *sink_ffmt;
248 struct v4l2_rect *crop;
249 int ret = 0;
250
251 if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
252 return -EINVAL;
253
254 sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
255 sel->pad,
256 sel->stream);
257 if (!sink_ffmt)
258 return -EINVAL;
259
260 crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
261 if (!crop)
262 return -EINVAL;
263
264 switch (sel->target) {
265 case V4L2_SEL_TGT_CROP_DEFAULT:
266 case V4L2_SEL_TGT_CROP_BOUNDS:
267 sel->r.left = 0;
268 sel->r.top = 0;
269 sel->r.width = sink_ffmt->width;
270 sel->r.height = sink_ffmt->height;
271 break;
272 case V4L2_SEL_TGT_CROP:
273 sel->r = *crop;
274 break;
275 default:
276 ret = -EINVAL;
277 }
278
279 return ret;
280 }
281
282 /*
283 * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set
284 * of streams.
285 */
286 #define CSI2_SUBDEV_MAX_STREAM_ID 63
287
ipu7_isys_csi2_enable_streams(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,u32 pad,u64 streams_mask)288 static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd,
289 struct v4l2_subdev_state *state,
290 u32 pad, u64 streams_mask)
291 {
292 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
293 struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
294 struct v4l2_subdev *r_sd;
295 struct media_pad *rp;
296 u32 sink_pad, sink_stream;
297 int ret, i;
298
299 if (!csi2->stream_count) {
300 dev_dbg(&csi2->isys->adev->auxdev.dev,
301 "stream on CSI2-%u with %u lanes\n", csi2->port,
302 csi2->nlanes);
303 ret = ipu7_isys_csi2_enable_stream(csi2);
304 if (ret)
305 return ret;
306 }
307
308 for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
309 if (streams_mask & BIT_ULL(i))
310 break;
311 }
312
313 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
314 &sink_pad, &sink_stream);
315 if (ret)
316 return ret;
317
318 rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
319 r_sd = media_entity_to_v4l2_subdev(rp->entity);
320
321 ret = v4l2_subdev_enable_streams(r_sd, rp->index,
322 BIT_ULL(sink_stream));
323 if (!ret) {
324 csi2->stream_count++;
325 return 0;
326 }
327
328 if (!csi2->stream_count)
329 ipu7_isys_csi2_disable_stream(csi2);
330
331 return ret;
332 }
333
ipu7_isys_csi2_disable_streams(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,u32 pad,u64 streams_mask)334 static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd,
335 struct v4l2_subdev_state *state,
336 u32 pad, u64 streams_mask)
337 {
338 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
339 struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
340 struct v4l2_subdev *r_sd;
341 struct media_pad *rp;
342 u32 sink_pad, sink_stream;
343 int ret, i;
344
345 for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
346 if (streams_mask & BIT_ULL(i))
347 break;
348 }
349
350 ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
351 &sink_pad, &sink_stream);
352 if (ret)
353 return ret;
354
355 rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
356 r_sd = media_entity_to_v4l2_subdev(rp->entity);
357
358 v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream));
359
360 if (--csi2->stream_count)
361 return 0;
362
363 dev_dbg(&csi2->isys->adev->auxdev.dev,
364 "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes);
365
366 ipu7_isys_csi2_disable_stream(csi2);
367
368 return 0;
369 }
370
371 static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
372 .get_fmt = v4l2_subdev_get_fmt,
373 .set_fmt = ipu7_isys_subdev_set_fmt,
374 .get_selection = ipu7_isys_csi2_get_sel,
375 .set_selection = ipu7_isys_csi2_set_sel,
376 .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code,
377 .enable_streams = ipu7_isys_csi2_enable_streams,
378 .disable_streams = ipu7_isys_csi2_disable_streams,
379 .set_routing = ipu7_isys_subdev_set_routing,
380 };
381
382 static const struct v4l2_subdev_ops csi2_sd_ops = {
383 .core = &csi2_sd_core_ops,
384 .pad = &csi2_sd_pad_ops,
385 };
386
387 static const struct media_entity_operations csi2_entity_ops = {
388 .link_validate = v4l2_subdev_link_validate,
389 .has_pad_interdep = v4l2_subdev_has_pad_interdep,
390 };
391
ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 * csi2)392 void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2)
393 {
394 if (!csi2->isys)
395 return;
396
397 v4l2_device_unregister_subdev(&csi2->asd.sd);
398 v4l2_subdev_cleanup(&csi2->asd.sd);
399 ipu7_isys_subdev_cleanup(&csi2->asd);
400 csi2->isys = NULL;
401 }
402
ipu7_isys_csi2_init(struct ipu7_isys_csi2 * csi2,struct ipu7_isys * isys,void __iomem * base,unsigned int index)403 int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2,
404 struct ipu7_isys *isys,
405 void __iomem *base, unsigned int index)
406 {
407 struct device *dev = &isys->adev->auxdev.dev;
408 int ret;
409
410 csi2->isys = isys;
411 csi2->base = base;
412 csi2->port = index;
413
414 if (!is_ipu7(isys->adev->isp->hw_ver))
415 csi2->legacy_irq_mask = 0x7U << (index * 3U);
416 else
417 csi2->legacy_irq_mask = 0x3U << (index * 2U);
418
419 dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index,
420 csi2->legacy_irq_mask);
421
422 csi2->asd.sd.entity.ops = &csi2_entity_ops;
423 csi2->asd.isys = isys;
424
425 ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
426 IPU7_NR_OF_CSI2_SINK_PADS,
427 IPU7_NR_OF_CSI2_SRC_PADS);
428 if (ret)
429 return ret;
430
431 csi2->asd.source = (int)index;
432 csi2->asd.supported_codes = csi2_supported_codes;
433 snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
434 IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index);
435 v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
436
437 ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
438 if (ret) {
439 dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret);
440 goto isys_subdev_cleanup;
441 }
442
443 ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
444 if (ret) {
445 dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret);
446 goto v4l2_subdev_cleanup;
447 }
448
449 return 0;
450
451 v4l2_subdev_cleanup:
452 v4l2_subdev_cleanup(&csi2->asd.sd);
453 isys_subdev_cleanup:
454 ipu7_isys_subdev_cleanup(&csi2->asd);
455
456 return ret;
457 }
458
ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream * stream)459 void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream)
460 {
461 struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
462 struct device *dev = &stream->isys->adev->auxdev.dev;
463 struct video_device *vdev = csi2->asd.sd.devnode;
464 struct v4l2_event ev = {
465 .type = V4L2_EVENT_FRAME_SYNC,
466 };
467
468 ev.id = stream->vc;
469 ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
470 v4l2_event_queue(vdev, &ev);
471
472 dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
473 csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
474 }
475
ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream * stream)476 void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream)
477 {
478 struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
479 struct device *dev = &stream->isys->adev->auxdev.dev;
480 u32 frame_sequence = atomic_read(&stream->sequence);
481
482 dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
483 csi2->port, frame_sequence);
484 }
485
ipu7_isys_csi2_get_remote_desc(u32 source_stream,struct ipu7_isys_csi2 * csi2,struct media_entity * source_entity,struct v4l2_mbus_frame_desc_entry * entry,int * nr_queues)486 int ipu7_isys_csi2_get_remote_desc(u32 source_stream,
487 struct ipu7_isys_csi2 *csi2,
488 struct media_entity *source_entity,
489 struct v4l2_mbus_frame_desc_entry *entry,
490 int *nr_queues)
491 {
492 struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
493 struct device *dev = &csi2->isys->adev->auxdev.dev;
494 struct v4l2_mbus_frame_desc desc;
495 struct v4l2_subdev *source;
496 struct media_pad *pad;
497 unsigned int i;
498 int ret;
499
500 source = media_entity_to_v4l2_subdev(source_entity);
501 if (!source)
502 return -EPIPE;
503
504 pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]);
505 if (!pad)
506 return -EPIPE;
507
508 ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
509 if (ret)
510 return ret;
511
512 if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
513 dev_err(dev, "Unsupported frame descriptor type\n");
514 return -EINVAL;
515 }
516
517 for (i = 0; i < desc.num_entries; i++) {
518 if (source_stream == desc.entry[i].stream) {
519 desc_entry = &desc.entry[i];
520 break;
521 }
522 }
523
524 if (!desc_entry) {
525 dev_err(dev, "Failed to find stream %u from remote subdev\n",
526 source_stream);
527 return -EINVAL;
528 }
529
530 if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) {
531 dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
532 return -EINVAL;
533 }
534
535 *entry = *desc_entry;
536
537 for (i = 0; i < desc.num_entries; i++) {
538 if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc)
539 (*nr_queues)++;
540 }
541
542 return 0;
543 }
544