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