1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * vsp1_entity.c -- R-Car VSP1 Base Entity |
4 | * |
5 | * Copyright (C) 2013-2014 Renesas Electronics Corporation |
6 | * |
7 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
8 | */ |
9 | |
10 | #include <linux/device.h> |
11 | #include <linux/gfp.h> |
12 | |
13 | #include <media/media-entity.h> |
14 | #include <media/v4l2-ctrls.h> |
15 | #include <media/v4l2-subdev.h> |
16 | |
17 | #include "vsp1.h" |
18 | #include "vsp1_dl.h" |
19 | #include "vsp1_entity.h" |
20 | #include "vsp1_pipe.h" |
21 | #include "vsp1_rwpf.h" |
22 | |
23 | void vsp1_entity_route_setup(struct vsp1_entity *entity, |
24 | struct vsp1_pipeline *pipe, |
25 | struct vsp1_dl_body *dlb) |
26 | { |
27 | struct vsp1_entity *source; |
28 | u32 route; |
29 | |
30 | if (entity->type == VSP1_ENTITY_HGO) { |
31 | u32 smppt; |
32 | |
33 | /* |
34 | * The HGO is a special case, its routing is configured on the |
35 | * sink pad. |
36 | */ |
37 | source = entity->sources[0]; |
38 | smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) |
39 | | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); |
40 | |
41 | vsp1_dl_body_write(dlb, VI6_DPR_HGO_SMPPT, data: smppt); |
42 | return; |
43 | } else if (entity->type == VSP1_ENTITY_HGT) { |
44 | u32 smppt; |
45 | |
46 | /* |
47 | * The HGT is a special case, its routing is configured on the |
48 | * sink pad. |
49 | */ |
50 | source = entity->sources[0]; |
51 | smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) |
52 | | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); |
53 | |
54 | vsp1_dl_body_write(dlb, VI6_DPR_HGT_SMPPT, data: smppt); |
55 | return; |
56 | } |
57 | |
58 | source = entity; |
59 | if (source->route->reg == 0) |
60 | return; |
61 | |
62 | route = source->sink->route->inputs[source->sink_pad]; |
63 | /* |
64 | * The ILV and BRS share the same data path route. The extra BRSSEL bit |
65 | * selects between the ILV and BRS. |
66 | */ |
67 | if (source->type == VSP1_ENTITY_BRS) |
68 | route |= VI6_DPR_ROUTE_BRSSEL; |
69 | vsp1_dl_body_write(dlb, reg: source->route->reg, data: route); |
70 | } |
71 | |
72 | void vsp1_entity_configure_stream(struct vsp1_entity *entity, |
73 | struct vsp1_pipeline *pipe, |
74 | struct vsp1_dl_list *dl, |
75 | struct vsp1_dl_body *dlb) |
76 | { |
77 | if (entity->ops->configure_stream) |
78 | entity->ops->configure_stream(entity, pipe, dl, dlb); |
79 | } |
80 | |
81 | void vsp1_entity_configure_frame(struct vsp1_entity *entity, |
82 | struct vsp1_pipeline *pipe, |
83 | struct vsp1_dl_list *dl, |
84 | struct vsp1_dl_body *dlb) |
85 | { |
86 | if (entity->ops->configure_frame) |
87 | entity->ops->configure_frame(entity, pipe, dl, dlb); |
88 | } |
89 | |
90 | void vsp1_entity_configure_partition(struct vsp1_entity *entity, |
91 | struct vsp1_pipeline *pipe, |
92 | struct vsp1_dl_list *dl, |
93 | struct vsp1_dl_body *dlb) |
94 | { |
95 | if (entity->ops->configure_partition) |
96 | entity->ops->configure_partition(entity, pipe, dl, dlb); |
97 | } |
98 | |
99 | /* ----------------------------------------------------------------------------- |
100 | * V4L2 Subdevice Operations |
101 | */ |
102 | |
103 | /** |
104 | * vsp1_entity_get_state - Get the subdev state for an entity |
105 | * @entity: the entity |
106 | * @sd_state: the TRY state |
107 | * @which: state selector (ACTIVE or TRY) |
108 | * |
109 | * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold |
110 | * the entity lock to access the returned configuration. |
111 | * |
112 | * Return the subdev state requested by the which argument. The TRY state is |
113 | * passed explicitly to the function through the sd_state argument and simply |
114 | * returned when requested. The ACTIVE state comes from the entity structure. |
115 | */ |
116 | struct v4l2_subdev_state * |
117 | vsp1_entity_get_state(struct vsp1_entity *entity, |
118 | struct v4l2_subdev_state *sd_state, |
119 | enum v4l2_subdev_format_whence which) |
120 | { |
121 | switch (which) { |
122 | case V4L2_SUBDEV_FORMAT_ACTIVE: |
123 | return entity->state; |
124 | case V4L2_SUBDEV_FORMAT_TRY: |
125 | default: |
126 | return sd_state; |
127 | } |
128 | } |
129 | |
130 | /** |
131 | * vsp1_entity_get_pad_format - Get a pad format from storage for an entity |
132 | * @entity: the entity |
133 | * @sd_state: the state storage |
134 | * @pad: the pad number |
135 | * |
136 | * Return the format stored in the given configuration for an entity's pad. The |
137 | * configuration can be an ACTIVE or TRY configuration. |
138 | */ |
139 | struct v4l2_mbus_framefmt * |
140 | vsp1_entity_get_pad_format(struct vsp1_entity *entity, |
141 | struct v4l2_subdev_state *sd_state, |
142 | unsigned int pad) |
143 | { |
144 | return v4l2_subdev_state_get_format(sd_state, pad); |
145 | } |
146 | |
147 | /** |
148 | * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity |
149 | * @entity: the entity |
150 | * @sd_state: the state storage |
151 | * @pad: the pad number |
152 | * @target: the selection target |
153 | * |
154 | * Return the selection rectangle stored in the given configuration for an |
155 | * entity's pad. The configuration can be an ACTIVE or TRY configuration. The |
156 | * selection target can be COMPOSE or CROP. |
157 | */ |
158 | struct v4l2_rect * |
159 | vsp1_entity_get_pad_selection(struct vsp1_entity *entity, |
160 | struct v4l2_subdev_state *sd_state, |
161 | unsigned int pad, unsigned int target) |
162 | { |
163 | switch (target) { |
164 | case V4L2_SEL_TGT_COMPOSE: |
165 | return v4l2_subdev_state_get_compose(sd_state, pad); |
166 | case V4L2_SEL_TGT_CROP: |
167 | return v4l2_subdev_state_get_crop(sd_state, pad); |
168 | default: |
169 | return NULL; |
170 | } |
171 | } |
172 | |
173 | /* |
174 | * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler |
175 | * @subdev: V4L2 subdevice |
176 | * @sd_state: V4L2 subdev state |
177 | * @fmt: V4L2 subdev format |
178 | * |
179 | * This function implements the subdev get_fmt pad operation. It can be used as |
180 | * a direct drop-in for the operation handler. |
181 | */ |
182 | int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, |
183 | struct v4l2_subdev_state *sd_state, |
184 | struct v4l2_subdev_format *fmt) |
185 | { |
186 | struct vsp1_entity *entity = to_vsp1_entity(subdev); |
187 | struct v4l2_subdev_state *state; |
188 | |
189 | state = vsp1_entity_get_state(entity, sd_state, which: fmt->which); |
190 | if (!state) |
191 | return -EINVAL; |
192 | |
193 | mutex_lock(&entity->lock); |
194 | fmt->format = *vsp1_entity_get_pad_format(entity, sd_state: state, pad: fmt->pad); |
195 | mutex_unlock(lock: &entity->lock); |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | /* |
201 | * vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler |
202 | * @subdev: V4L2 subdevice |
203 | * @sd_state: V4L2 subdev state |
204 | * @code: Media bus code enumeration |
205 | * @codes: Array of supported media bus codes |
206 | * @ncodes: Number of supported media bus codes |
207 | * |
208 | * This function implements the subdev enum_mbus_code pad operation for entities |
209 | * that do not support format conversion. It enumerates the given supported |
210 | * media bus codes on the sink pad and reports a source pad format identical to |
211 | * the sink pad. |
212 | */ |
213 | int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, |
214 | struct v4l2_subdev_state *sd_state, |
215 | struct v4l2_subdev_mbus_code_enum *code, |
216 | const unsigned int *codes, unsigned int ncodes) |
217 | { |
218 | struct vsp1_entity *entity = to_vsp1_entity(subdev); |
219 | |
220 | if (code->pad == 0) { |
221 | if (code->index >= ncodes) |
222 | return -EINVAL; |
223 | |
224 | code->code = codes[code->index]; |
225 | } else { |
226 | struct v4l2_subdev_state *state; |
227 | struct v4l2_mbus_framefmt *format; |
228 | |
229 | /* |
230 | * The entity can't perform format conversion, the sink format |
231 | * is always identical to the source format. |
232 | */ |
233 | if (code->index) |
234 | return -EINVAL; |
235 | |
236 | state = vsp1_entity_get_state(entity, sd_state, which: code->which); |
237 | if (!state) |
238 | return -EINVAL; |
239 | |
240 | mutex_lock(&entity->lock); |
241 | format = vsp1_entity_get_pad_format(entity, sd_state: state, pad: 0); |
242 | code->code = format->code; |
243 | mutex_unlock(lock: &entity->lock); |
244 | } |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | /* |
250 | * vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler |
251 | * @subdev: V4L2 subdevice |
252 | * @sd_state: V4L2 subdev state |
253 | * @fse: Frame size enumeration |
254 | * @min_width: Minimum image width |
255 | * @min_height: Minimum image height |
256 | * @max_width: Maximum image width |
257 | * @max_height: Maximum image height |
258 | * |
259 | * This function implements the subdev enum_frame_size pad operation for |
260 | * entities that do not support scaling or cropping. It reports the given |
261 | * minimum and maximum frame width and height on the sink pad, and a fixed |
262 | * source pad size identical to the sink pad. |
263 | */ |
264 | int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, |
265 | struct v4l2_subdev_state *sd_state, |
266 | struct v4l2_subdev_frame_size_enum *fse, |
267 | unsigned int min_width, unsigned int min_height, |
268 | unsigned int max_width, unsigned int max_height) |
269 | { |
270 | struct vsp1_entity *entity = to_vsp1_entity(subdev); |
271 | struct v4l2_subdev_state *state; |
272 | struct v4l2_mbus_framefmt *format; |
273 | int ret = 0; |
274 | |
275 | state = vsp1_entity_get_state(entity, sd_state, which: fse->which); |
276 | if (!state) |
277 | return -EINVAL; |
278 | |
279 | format = vsp1_entity_get_pad_format(entity, sd_state: state, pad: fse->pad); |
280 | |
281 | mutex_lock(&entity->lock); |
282 | |
283 | if (fse->index || fse->code != format->code) { |
284 | ret = -EINVAL; |
285 | goto done; |
286 | } |
287 | |
288 | if (fse->pad == 0) { |
289 | fse->min_width = min_width; |
290 | fse->max_width = max_width; |
291 | fse->min_height = min_height; |
292 | fse->max_height = max_height; |
293 | } else { |
294 | /* |
295 | * The size on the source pad are fixed and always identical to |
296 | * the size on the sink pad. |
297 | */ |
298 | fse->min_width = format->width; |
299 | fse->max_width = format->width; |
300 | fse->min_height = format->height; |
301 | fse->max_height = format->height; |
302 | } |
303 | |
304 | done: |
305 | mutex_unlock(lock: &entity->lock); |
306 | return ret; |
307 | } |
308 | |
309 | /* |
310 | * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler |
311 | * @subdev: V4L2 subdevice |
312 | * @sd_state: V4L2 subdev state |
313 | * @fmt: V4L2 subdev format |
314 | * @codes: Array of supported media bus codes |
315 | * @ncodes: Number of supported media bus codes |
316 | * @min_width: Minimum image width |
317 | * @min_height: Minimum image height |
318 | * @max_width: Maximum image width |
319 | * @max_height: Maximum image height |
320 | * |
321 | * This function implements the subdev set_fmt pad operation for entities that |
322 | * do not support scaling or cropping. It defaults to the first supplied media |
323 | * bus code if the requested code isn't supported, clamps the size to the |
324 | * supplied minimum and maximum, and propagates the sink pad format to the |
325 | * source pad. |
326 | */ |
327 | int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, |
328 | struct v4l2_subdev_state *sd_state, |
329 | struct v4l2_subdev_format *fmt, |
330 | const unsigned int *codes, unsigned int ncodes, |
331 | unsigned int min_width, unsigned int min_height, |
332 | unsigned int max_width, unsigned int max_height) |
333 | { |
334 | struct vsp1_entity *entity = to_vsp1_entity(subdev); |
335 | struct v4l2_subdev_state *state; |
336 | struct v4l2_mbus_framefmt *format; |
337 | struct v4l2_rect *selection; |
338 | unsigned int i; |
339 | int ret = 0; |
340 | |
341 | mutex_lock(&entity->lock); |
342 | |
343 | state = vsp1_entity_get_state(entity, sd_state, which: fmt->which); |
344 | if (!state) { |
345 | ret = -EINVAL; |
346 | goto done; |
347 | } |
348 | |
349 | format = vsp1_entity_get_pad_format(entity, sd_state: state, pad: fmt->pad); |
350 | |
351 | if (fmt->pad == entity->source_pad) { |
352 | /* The output format can't be modified. */ |
353 | fmt->format = *format; |
354 | goto done; |
355 | } |
356 | |
357 | /* |
358 | * Default to the first media bus code if the requested format is not |
359 | * supported. |
360 | */ |
361 | for (i = 0; i < ncodes; ++i) { |
362 | if (fmt->format.code == codes[i]) |
363 | break; |
364 | } |
365 | |
366 | format->code = i < ncodes ? codes[i] : codes[0]; |
367 | format->width = clamp_t(unsigned int, fmt->format.width, |
368 | min_width, max_width); |
369 | format->height = clamp_t(unsigned int, fmt->format.height, |
370 | min_height, max_height); |
371 | format->field = V4L2_FIELD_NONE; |
372 | format->colorspace = V4L2_COLORSPACE_SRGB; |
373 | |
374 | fmt->format = *format; |
375 | |
376 | /* Propagate the format to the source pad. */ |
377 | format = vsp1_entity_get_pad_format(entity, sd_state: state, pad: entity->source_pad); |
378 | *format = fmt->format; |
379 | |
380 | /* Reset the crop and compose rectangles. */ |
381 | selection = vsp1_entity_get_pad_selection(entity, sd_state: state, pad: fmt->pad, |
382 | V4L2_SEL_TGT_CROP); |
383 | selection->left = 0; |
384 | selection->top = 0; |
385 | selection->width = format->width; |
386 | selection->height = format->height; |
387 | |
388 | selection = vsp1_entity_get_pad_selection(entity, sd_state: state, pad: fmt->pad, |
389 | V4L2_SEL_TGT_COMPOSE); |
390 | selection->left = 0; |
391 | selection->top = 0; |
392 | selection->width = format->width; |
393 | selection->height = format->height; |
394 | |
395 | done: |
396 | mutex_unlock(lock: &entity->lock); |
397 | return ret; |
398 | } |
399 | |
400 | static int vsp1_entity_init_state(struct v4l2_subdev *subdev, |
401 | struct v4l2_subdev_state *sd_state) |
402 | { |
403 | unsigned int pad; |
404 | |
405 | /* Initialize all pad formats with default values. */ |
406 | for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { |
407 | struct v4l2_subdev_format format = { |
408 | .pad = pad, |
409 | .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY |
410 | : V4L2_SUBDEV_FORMAT_ACTIVE, |
411 | }; |
412 | |
413 | v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format); |
414 | } |
415 | |
416 | return 0; |
417 | } |
418 | |
419 | static const struct v4l2_subdev_internal_ops vsp1_entity_internal_ops = { |
420 | .init_state = vsp1_entity_init_state, |
421 | }; |
422 | |
423 | /* ----------------------------------------------------------------------------- |
424 | * Media Operations |
425 | */ |
426 | |
427 | static inline struct vsp1_entity * |
428 | media_entity_to_vsp1_entity(struct media_entity *entity) |
429 | { |
430 | return container_of(entity, struct vsp1_entity, subdev.entity); |
431 | } |
432 | |
433 | static int vsp1_entity_link_setup_source(const struct media_pad *source_pad, |
434 | const struct media_pad *sink_pad, |
435 | u32 flags) |
436 | { |
437 | struct vsp1_entity *source; |
438 | |
439 | source = media_entity_to_vsp1_entity(entity: source_pad->entity); |
440 | |
441 | if (!source->route) |
442 | return 0; |
443 | |
444 | if (flags & MEDIA_LNK_FL_ENABLED) { |
445 | struct vsp1_entity *sink |
446 | = media_entity_to_vsp1_entity(entity: sink_pad->entity); |
447 | |
448 | /* |
449 | * Fan-out is limited to one for the normal data path plus |
450 | * optional HGO and HGT. We ignore the HGO and HGT here. |
451 | */ |
452 | if (sink->type != VSP1_ENTITY_HGO && |
453 | sink->type != VSP1_ENTITY_HGT) { |
454 | if (source->sink) |
455 | return -EBUSY; |
456 | source->sink = sink; |
457 | source->sink_pad = sink_pad->index; |
458 | } |
459 | } else { |
460 | source->sink = NULL; |
461 | source->sink_pad = 0; |
462 | } |
463 | |
464 | return 0; |
465 | } |
466 | |
467 | static int vsp1_entity_link_setup_sink(const struct media_pad *source_pad, |
468 | const struct media_pad *sink_pad, |
469 | u32 flags) |
470 | { |
471 | struct vsp1_entity *sink; |
472 | struct vsp1_entity *source; |
473 | |
474 | sink = media_entity_to_vsp1_entity(entity: sink_pad->entity); |
475 | source = media_entity_to_vsp1_entity(entity: source_pad->entity); |
476 | |
477 | if (flags & MEDIA_LNK_FL_ENABLED) { |
478 | /* Fan-in is limited to one. */ |
479 | if (sink->sources[sink_pad->index]) |
480 | return -EBUSY; |
481 | |
482 | sink->sources[sink_pad->index] = source; |
483 | } else { |
484 | sink->sources[sink_pad->index] = NULL; |
485 | } |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | int vsp1_entity_link_setup(struct media_entity *entity, |
491 | const struct media_pad *local, |
492 | const struct media_pad *remote, u32 flags) |
493 | { |
494 | if (local->flags & MEDIA_PAD_FL_SOURCE) |
495 | return vsp1_entity_link_setup_source(source_pad: local, sink_pad: remote, flags); |
496 | else |
497 | return vsp1_entity_link_setup_sink(source_pad: remote, sink_pad: local, flags); |
498 | } |
499 | |
500 | /** |
501 | * vsp1_entity_remote_pad - Find the pad at the remote end of a link |
502 | * @pad: Pad at the local end of the link |
503 | * |
504 | * Search for a remote pad connected to the given pad by iterating over all |
505 | * links originating or terminating at that pad until an enabled link is found. |
506 | * |
507 | * Our link setup implementation guarantees that the output fan-out will not be |
508 | * higher than one for the data pipelines, except for the links to the HGO and |
509 | * HGT that can be enabled in addition to a regular data link. When traversing |
510 | * outgoing links this function ignores HGO and HGT entities and should thus be |
511 | * used in place of the generic media_pad_remote_pad_first() function to |
512 | * traverse data pipelines. |
513 | * |
514 | * Return a pointer to the pad at the remote end of the first found enabled |
515 | * link, or NULL if no enabled link has been found. |
516 | */ |
517 | struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad) |
518 | { |
519 | struct media_link *link; |
520 | |
521 | list_for_each_entry(link, &pad->entity->links, list) { |
522 | struct vsp1_entity *entity; |
523 | |
524 | if (!(link->flags & MEDIA_LNK_FL_ENABLED)) |
525 | continue; |
526 | |
527 | /* If we're the sink the source will never be an HGO or HGT. */ |
528 | if (link->sink == pad) |
529 | return link->source; |
530 | |
531 | if (link->source != pad) |
532 | continue; |
533 | |
534 | /* If the sink isn't a subdevice it can't be an HGO or HGT. */ |
535 | if (!is_media_entity_v4l2_subdev(entity: link->sink->entity)) |
536 | return link->sink; |
537 | |
538 | entity = media_entity_to_vsp1_entity(entity: link->sink->entity); |
539 | if (entity->type != VSP1_ENTITY_HGO && |
540 | entity->type != VSP1_ENTITY_HGT) |
541 | return link->sink; |
542 | } |
543 | |
544 | return NULL; |
545 | |
546 | } |
547 | |
548 | /* ----------------------------------------------------------------------------- |
549 | * Initialization |
550 | */ |
551 | |
552 | #define VSP1_ENTITY_ROUTE(ent) \ |
553 | { VSP1_ENTITY_##ent, 0, VI6_DPR_##ent##_ROUTE, \ |
554 | { VI6_DPR_NODE_##ent }, VI6_DPR_NODE_##ent } |
555 | |
556 | #define VSP1_ENTITY_ROUTE_RPF(idx) \ |
557 | { VSP1_ENTITY_RPF, idx, VI6_DPR_RPF_ROUTE(idx), \ |
558 | { 0, }, VI6_DPR_NODE_RPF(idx) } |
559 | |
560 | #define VSP1_ENTITY_ROUTE_UDS(idx) \ |
561 | { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \ |
562 | { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) } |
563 | |
564 | #define VSP1_ENTITY_ROUTE_UIF(idx) \ |
565 | { VSP1_ENTITY_UIF, idx, VI6_DPR_UIF_ROUTE(idx), \ |
566 | { VI6_DPR_NODE_UIF(idx) }, VI6_DPR_NODE_UIF(idx) } |
567 | |
568 | #define VSP1_ENTITY_ROUTE_WPF(idx) \ |
569 | { VSP1_ENTITY_WPF, idx, 0, \ |
570 | { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } |
571 | |
572 | static const struct vsp1_route vsp1_routes[] = { |
573 | { VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE, |
574 | { VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 }, |
575 | { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, |
576 | { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), |
577 | VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), |
578 | VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT }, |
579 | VSP1_ENTITY_ROUTE(CLU), |
580 | { VSP1_ENTITY_HGO, 0, 0, { 0, }, 0 }, |
581 | { VSP1_ENTITY_HGT, 0, 0, { 0, }, 0 }, |
582 | VSP1_ENTITY_ROUTE(HSI), |
583 | VSP1_ENTITY_ROUTE(HST), |
584 | { VSP1_ENTITY_LIF, 0, 0, { 0, }, 0 }, |
585 | { VSP1_ENTITY_LIF, 1, 0, { 0, }, 0 }, |
586 | VSP1_ENTITY_ROUTE(LUT), |
587 | VSP1_ENTITY_ROUTE_RPF(0), |
588 | VSP1_ENTITY_ROUTE_RPF(1), |
589 | VSP1_ENTITY_ROUTE_RPF(2), |
590 | VSP1_ENTITY_ROUTE_RPF(3), |
591 | VSP1_ENTITY_ROUTE_RPF(4), |
592 | VSP1_ENTITY_ROUTE(SRU), |
593 | VSP1_ENTITY_ROUTE_UDS(0), |
594 | VSP1_ENTITY_ROUTE_UDS(1), |
595 | VSP1_ENTITY_ROUTE_UDS(2), |
596 | VSP1_ENTITY_ROUTE_UIF(0), /* Named UIF4 in the documentation */ |
597 | VSP1_ENTITY_ROUTE_UIF(1), /* Named UIF5 in the documentation */ |
598 | VSP1_ENTITY_ROUTE_WPF(0), |
599 | VSP1_ENTITY_ROUTE_WPF(1), |
600 | VSP1_ENTITY_ROUTE_WPF(2), |
601 | VSP1_ENTITY_ROUTE_WPF(3), |
602 | }; |
603 | |
604 | int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, |
605 | const char *name, unsigned int num_pads, |
606 | const struct v4l2_subdev_ops *ops, u32 function) |
607 | { |
608 | static struct lock_class_key key; |
609 | struct v4l2_subdev *subdev; |
610 | unsigned int i; |
611 | int ret; |
612 | |
613 | for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { |
614 | if (vsp1_routes[i].type == entity->type && |
615 | vsp1_routes[i].index == entity->index) { |
616 | entity->route = &vsp1_routes[i]; |
617 | break; |
618 | } |
619 | } |
620 | |
621 | if (i == ARRAY_SIZE(vsp1_routes)) |
622 | return -EINVAL; |
623 | |
624 | mutex_init(&entity->lock); |
625 | |
626 | entity->vsp1 = vsp1; |
627 | entity->source_pad = num_pads - 1; |
628 | |
629 | /* Allocate and initialize pads. */ |
630 | entity->pads = devm_kcalloc(dev: vsp1->dev, |
631 | n: num_pads, size: sizeof(*entity->pads), |
632 | GFP_KERNEL); |
633 | if (entity->pads == NULL) |
634 | return -ENOMEM; |
635 | |
636 | for (i = 0; i < num_pads - 1; ++i) |
637 | entity->pads[i].flags = MEDIA_PAD_FL_SINK; |
638 | |
639 | entity->sources = devm_kcalloc(dev: vsp1->dev, max(num_pads - 1, 1U), |
640 | size: sizeof(*entity->sources), GFP_KERNEL); |
641 | if (entity->sources == NULL) |
642 | return -ENOMEM; |
643 | |
644 | /* Single-pad entities only have a sink. */ |
645 | entity->pads[num_pads - 1].flags = num_pads > 1 ? MEDIA_PAD_FL_SOURCE |
646 | : MEDIA_PAD_FL_SINK; |
647 | |
648 | /* Initialize the media entity. */ |
649 | ret = media_entity_pads_init(entity: &entity->subdev.entity, num_pads, |
650 | pads: entity->pads); |
651 | if (ret < 0) |
652 | return ret; |
653 | |
654 | /* Initialize the V4L2 subdev. */ |
655 | subdev = &entity->subdev; |
656 | v4l2_subdev_init(sd: subdev, ops); |
657 | subdev->internal_ops = &vsp1_entity_internal_ops; |
658 | |
659 | subdev->entity.function = function; |
660 | subdev->entity.ops = &vsp1->media_ops; |
661 | subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
662 | |
663 | snprintf(buf: subdev->name, size: sizeof(subdev->name), fmt: "%s %s" , |
664 | dev_name(dev: vsp1->dev), name); |
665 | |
666 | vsp1_entity_init_state(subdev, NULL); |
667 | |
668 | /* |
669 | * Allocate the subdev state to store formats and selection |
670 | * rectangles. |
671 | */ |
672 | /* |
673 | * FIXME: Drop this call, drivers are not supposed to use |
674 | * __v4l2_subdev_state_alloc(). |
675 | */ |
676 | entity->state = __v4l2_subdev_state_alloc(sd: &entity->subdev, |
677 | lock_name: "vsp1:state->lock" , key: &key); |
678 | if (IS_ERR(ptr: entity->state)) { |
679 | media_entity_cleanup(entity: &entity->subdev.entity); |
680 | return PTR_ERR(ptr: entity->state); |
681 | } |
682 | |
683 | return 0; |
684 | } |
685 | |
686 | void vsp1_entity_destroy(struct vsp1_entity *entity) |
687 | { |
688 | if (entity->ops && entity->ops->destroy) |
689 | entity->ops->destroy(entity); |
690 | if (entity->subdev.ctrl_handler) |
691 | v4l2_ctrl_handler_free(hdl: entity->subdev.ctrl_handler); |
692 | __v4l2_subdev_state_free(state: entity->state); |
693 | media_entity_cleanup(entity: &entity->subdev.entity); |
694 | } |
695 | |