1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module |
4 | * |
5 | * Copyright (C) 2012 Texas Instruments, Inc. |
6 | * |
7 | * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/uaccess.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/device.h> |
14 | #include <linux/dma-mapping.h> |
15 | #include <linux/mm.h> |
16 | #include <linux/sched.h> |
17 | |
18 | #include "iss.h" |
19 | #include "iss_regs.h" |
20 | #include "iss_ipipe.h" |
21 | |
22 | static struct v4l2_mbus_framefmt * |
23 | __ipipe_get_format(struct iss_ipipe_device *ipipe, |
24 | struct v4l2_subdev_state *sd_state, |
25 | unsigned int pad, |
26 | enum v4l2_subdev_format_whence which); |
27 | |
28 | static const unsigned int ipipe_fmts[] = { |
29 | MEDIA_BUS_FMT_SGRBG10_1X10, |
30 | MEDIA_BUS_FMT_SRGGB10_1X10, |
31 | MEDIA_BUS_FMT_SBGGR10_1X10, |
32 | MEDIA_BUS_FMT_SGBRG10_1X10, |
33 | }; |
34 | |
35 | /* |
36 | * ipipe_print_status - Print current IPIPE Module register values. |
37 | * @ipipe: Pointer to ISS ISP IPIPE device. |
38 | * |
39 | * Also prints other debug information stored in the IPIPE module. |
40 | */ |
41 | #define IPIPE_PRINT_REGISTER(iss, name)\ |
42 | dev_dbg(iss->dev, "###IPIPE " #name "=0x%08x\n", \ |
43 | iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_##name)) |
44 | |
45 | static void ipipe_print_status(struct iss_ipipe_device *ipipe) |
46 | { |
47 | struct iss_device *iss = to_iss_device(ipipe); |
48 | |
49 | dev_dbg(iss->dev, "-------------IPIPE Register dump-------------\n" ); |
50 | |
51 | IPIPE_PRINT_REGISTER(iss, SRC_EN); |
52 | IPIPE_PRINT_REGISTER(iss, SRC_MODE); |
53 | IPIPE_PRINT_REGISTER(iss, SRC_FMT); |
54 | IPIPE_PRINT_REGISTER(iss, SRC_COL); |
55 | IPIPE_PRINT_REGISTER(iss, SRC_VPS); |
56 | IPIPE_PRINT_REGISTER(iss, SRC_VSZ); |
57 | IPIPE_PRINT_REGISTER(iss, SRC_HPS); |
58 | IPIPE_PRINT_REGISTER(iss, SRC_HSZ); |
59 | IPIPE_PRINT_REGISTER(iss, GCK_MMR); |
60 | IPIPE_PRINT_REGISTER(iss, YUV_PHS); |
61 | |
62 | dev_dbg(iss->dev, "-----------------------------------------------\n" ); |
63 | } |
64 | |
65 | /* |
66 | * ipipe_enable - Enable/Disable IPIPE. |
67 | * @enable: enable flag |
68 | * |
69 | */ |
70 | static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable) |
71 | { |
72 | struct iss_device *iss = to_iss_device(ipipe); |
73 | |
74 | iss_reg_update(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_EN, |
75 | IPIPE_SRC_EN_EN, set: enable ? IPIPE_SRC_EN_EN : 0); |
76 | } |
77 | |
78 | /* ----------------------------------------------------------------------------- |
79 | * Format- and pipeline-related configuration helpers |
80 | */ |
81 | |
82 | static void ipipe_configure(struct iss_ipipe_device *ipipe) |
83 | { |
84 | struct iss_device *iss = to_iss_device(ipipe); |
85 | struct v4l2_mbus_framefmt *format; |
86 | |
87 | /* IPIPE_PAD_SINK */ |
88 | format = &ipipe->formats[IPIPE_PAD_SINK]; |
89 | |
90 | /* NOTE: Currently just supporting pipeline IN: RGB, OUT: YUV422 */ |
91 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_FMT, |
92 | IPIPE_SRC_FMT_RAW2YUV); |
93 | |
94 | /* Enable YUV444 -> YUV422 conversion */ |
95 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_YUV_PHS, |
96 | IPIPE_YUV_PHS_LPF); |
97 | |
98 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VPS, value: 0); |
99 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HPS, value: 0); |
100 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VSZ, |
101 | value: (format->height - 2) & IPIPE_SRC_VSZ_MASK); |
102 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HSZ, |
103 | value: (format->width - 1) & IPIPE_SRC_HSZ_MASK); |
104 | |
105 | /* Ignore ipipeif_wrt signal, and operate on-the-fly. */ |
106 | iss_reg_clr(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_MODE, |
107 | IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST); |
108 | |
109 | /* HACK: Values tuned for Ducati SW (OV) */ |
110 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_COL, |
111 | IPIPE_SRC_COL_EE_B | IPIPE_SRC_COL_EO_GB | |
112 | IPIPE_SRC_COL_OE_GR | IPIPE_SRC_COL_OO_R); |
113 | |
114 | /* IPIPE_PAD_SOURCE_VP */ |
115 | format = &ipipe->formats[IPIPE_PAD_SOURCE_VP]; |
116 | /* Do nothing? */ |
117 | } |
118 | |
119 | /* ----------------------------------------------------------------------------- |
120 | * V4L2 subdev operations |
121 | */ |
122 | |
123 | /* |
124 | * ipipe_set_stream - Enable/Disable streaming on the IPIPE module |
125 | * @sd: ISP IPIPE V4L2 subdevice |
126 | * @enable: Enable/disable stream |
127 | */ |
128 | static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) |
129 | { |
130 | struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); |
131 | struct iss_device *iss = to_iss_device(ipipe); |
132 | int ret = 0; |
133 | |
134 | if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) { |
135 | if (enable == ISS_PIPELINE_STREAM_STOPPED) |
136 | return 0; |
137 | |
138 | omap4iss_isp_subclk_enable(iss, res: OMAP4_ISS_ISP_SUBCLK_IPIPE); |
139 | |
140 | /* Enable clk_arm_g0 */ |
141 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_MMR, |
142 | IPIPE_GCK_MMR_REG); |
143 | |
144 | /* Enable clk_pix_g[3:0] */ |
145 | iss_reg_write(iss, res: OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_PIX, |
146 | IPIPE_GCK_PIX_G3 | IPIPE_GCK_PIX_G2 | |
147 | IPIPE_GCK_PIX_G1 | IPIPE_GCK_PIX_G0); |
148 | } |
149 | |
150 | switch (enable) { |
151 | case ISS_PIPELINE_STREAM_CONTINUOUS: |
152 | |
153 | ipipe_configure(ipipe); |
154 | ipipe_print_status(ipipe); |
155 | |
156 | atomic_set(v: &ipipe->stopping, i: 0); |
157 | ipipe_enable(ipipe, enable: 1); |
158 | break; |
159 | |
160 | case ISS_PIPELINE_STREAM_STOPPED: |
161 | if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) |
162 | return 0; |
163 | if (omap4iss_module_sync_idle(me: &sd->entity, wait: &ipipe->wait, |
164 | stopping: &ipipe->stopping)) |
165 | ret = -ETIMEDOUT; |
166 | |
167 | ipipe_enable(ipipe, enable: 0); |
168 | omap4iss_isp_subclk_disable(iss, res: OMAP4_ISS_ISP_SUBCLK_IPIPE); |
169 | break; |
170 | } |
171 | |
172 | ipipe->state = enable; |
173 | return ret; |
174 | } |
175 | |
176 | static struct v4l2_mbus_framefmt * |
177 | __ipipe_get_format(struct iss_ipipe_device *ipipe, |
178 | struct v4l2_subdev_state *sd_state, |
179 | unsigned int pad, |
180 | enum v4l2_subdev_format_whence which) |
181 | { |
182 | if (which == V4L2_SUBDEV_FORMAT_TRY) |
183 | return v4l2_subdev_state_get_format(sd_state, pad); |
184 | |
185 | return &ipipe->formats[pad]; |
186 | } |
187 | |
188 | /* |
189 | * ipipe_try_format - Try video format on a pad |
190 | * @ipipe: ISS IPIPE device |
191 | * @sd_state: V4L2 subdev state |
192 | * @pad: Pad number |
193 | * @fmt: Format |
194 | */ |
195 | static void |
196 | ipipe_try_format(struct iss_ipipe_device *ipipe, |
197 | struct v4l2_subdev_state *sd_state, |
198 | unsigned int pad, |
199 | struct v4l2_mbus_framefmt *fmt, |
200 | enum v4l2_subdev_format_whence which) |
201 | { |
202 | struct v4l2_mbus_framefmt *format; |
203 | unsigned int width = fmt->width; |
204 | unsigned int height = fmt->height; |
205 | unsigned int i; |
206 | |
207 | switch (pad) { |
208 | case IPIPE_PAD_SINK: |
209 | for (i = 0; i < ARRAY_SIZE(ipipe_fmts); i++) { |
210 | if (fmt->code == ipipe_fmts[i]) |
211 | break; |
212 | } |
213 | |
214 | /* If not found, use SGRBG10 as default */ |
215 | if (i >= ARRAY_SIZE(ipipe_fmts)) |
216 | fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
217 | |
218 | /* Clamp the input size. */ |
219 | fmt->width = clamp_t(u32, width, 1, 8192); |
220 | fmt->height = clamp_t(u32, height, 1, 8192); |
221 | fmt->colorspace = V4L2_COLORSPACE_SRGB; |
222 | break; |
223 | |
224 | case IPIPE_PAD_SOURCE_VP: |
225 | format = __ipipe_get_format(ipipe, sd_state, IPIPE_PAD_SINK, |
226 | which); |
227 | memcpy(fmt, format, sizeof(*fmt)); |
228 | |
229 | fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; |
230 | fmt->width = clamp_t(u32, width, 32, fmt->width); |
231 | fmt->height = clamp_t(u32, height, 32, fmt->height); |
232 | fmt->colorspace = V4L2_COLORSPACE_JPEG; |
233 | break; |
234 | } |
235 | |
236 | fmt->field = V4L2_FIELD_NONE; |
237 | } |
238 | |
239 | /* |
240 | * ipipe_enum_mbus_code - Handle pixel format enumeration |
241 | * @sd : pointer to v4l2 subdev structure |
242 | * @sd_state: V4L2 subdev state |
243 | * @code : pointer to v4l2_subdev_mbus_code_enum structure |
244 | * return -EINVAL or zero on success |
245 | */ |
246 | static int ipipe_enum_mbus_code(struct v4l2_subdev *sd, |
247 | struct v4l2_subdev_state *sd_state, |
248 | struct v4l2_subdev_mbus_code_enum *code) |
249 | { |
250 | switch (code->pad) { |
251 | case IPIPE_PAD_SINK: |
252 | if (code->index >= ARRAY_SIZE(ipipe_fmts)) |
253 | return -EINVAL; |
254 | |
255 | code->code = ipipe_fmts[code->index]; |
256 | break; |
257 | |
258 | case IPIPE_PAD_SOURCE_VP: |
259 | /* FIXME: Forced format conversion inside IPIPE ? */ |
260 | if (code->index != 0) |
261 | return -EINVAL; |
262 | |
263 | code->code = MEDIA_BUS_FMT_UYVY8_1X16; |
264 | break; |
265 | |
266 | default: |
267 | return -EINVAL; |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static int ipipe_enum_frame_size(struct v4l2_subdev *sd, |
274 | struct v4l2_subdev_state *sd_state, |
275 | struct v4l2_subdev_frame_size_enum *fse) |
276 | { |
277 | struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); |
278 | struct v4l2_mbus_framefmt format; |
279 | |
280 | if (fse->index != 0) |
281 | return -EINVAL; |
282 | |
283 | format.code = fse->code; |
284 | format.width = 1; |
285 | format.height = 1; |
286 | ipipe_try_format(ipipe, sd_state, pad: fse->pad, fmt: &format, which: fse->which); |
287 | fse->min_width = format.width; |
288 | fse->min_height = format.height; |
289 | |
290 | if (format.code != fse->code) |
291 | return -EINVAL; |
292 | |
293 | format.code = fse->code; |
294 | format.width = -1; |
295 | format.height = -1; |
296 | ipipe_try_format(ipipe, sd_state, pad: fse->pad, fmt: &format, which: fse->which); |
297 | fse->max_width = format.width; |
298 | fse->max_height = format.height; |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | /* |
304 | * ipipe_get_format - Retrieve the video format on a pad |
305 | * @sd : ISP IPIPE V4L2 subdevice |
306 | * @sd_state: V4L2 subdev state |
307 | * @fmt: Format |
308 | * |
309 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond |
310 | * to the format type. |
311 | */ |
312 | static int ipipe_get_format(struct v4l2_subdev *sd, |
313 | struct v4l2_subdev_state *sd_state, |
314 | struct v4l2_subdev_format *fmt) |
315 | { |
316 | struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); |
317 | struct v4l2_mbus_framefmt *format; |
318 | |
319 | format = __ipipe_get_format(ipipe, sd_state, pad: fmt->pad, which: fmt->which); |
320 | if (!format) |
321 | return -EINVAL; |
322 | |
323 | fmt->format = *format; |
324 | return 0; |
325 | } |
326 | |
327 | /* |
328 | * ipipe_set_format - Set the video format on a pad |
329 | * @sd : ISP IPIPE V4L2 subdevice |
330 | * @sd_state: V4L2 subdev state |
331 | * @fmt: Format |
332 | * |
333 | * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond |
334 | * to the format type. |
335 | */ |
336 | static int ipipe_set_format(struct v4l2_subdev *sd, |
337 | struct v4l2_subdev_state *sd_state, |
338 | struct v4l2_subdev_format *fmt) |
339 | { |
340 | struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); |
341 | struct v4l2_mbus_framefmt *format; |
342 | |
343 | format = __ipipe_get_format(ipipe, sd_state, pad: fmt->pad, which: fmt->which); |
344 | if (!format) |
345 | return -EINVAL; |
346 | |
347 | ipipe_try_format(ipipe, sd_state, pad: fmt->pad, fmt: &fmt->format, which: fmt->which); |
348 | *format = fmt->format; |
349 | |
350 | /* Propagate the format from sink to source */ |
351 | if (fmt->pad == IPIPE_PAD_SINK) { |
352 | format = __ipipe_get_format(ipipe, sd_state, |
353 | IPIPE_PAD_SOURCE_VP, |
354 | which: fmt->which); |
355 | *format = fmt->format; |
356 | ipipe_try_format(ipipe, sd_state, IPIPE_PAD_SOURCE_VP, fmt: format, |
357 | which: fmt->which); |
358 | } |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | static int ipipe_link_validate(struct v4l2_subdev *sd, struct media_link *link, |
364 | struct v4l2_subdev_format *source_fmt, |
365 | struct v4l2_subdev_format *sink_fmt) |
366 | { |
367 | /* Check if the two ends match */ |
368 | if (source_fmt->format.width != sink_fmt->format.width || |
369 | source_fmt->format.height != sink_fmt->format.height) |
370 | return -EPIPE; |
371 | |
372 | if (source_fmt->format.code != sink_fmt->format.code) |
373 | return -EPIPE; |
374 | |
375 | return 0; |
376 | } |
377 | |
378 | /* |
379 | * ipipe_init_formats - Initialize formats on all pads |
380 | * @sd: ISP IPIPE V4L2 subdevice |
381 | * @fh: V4L2 subdev file handle |
382 | * |
383 | * Initialize all pad formats with default values. If fh is not NULL, try |
384 | * formats are initialized on the file handle. Otherwise active formats are |
385 | * initialized on the device. |
386 | */ |
387 | static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
388 | { |
389 | struct v4l2_subdev_format format; |
390 | |
391 | memset(&format, 0, sizeof(format)); |
392 | format.pad = IPIPE_PAD_SINK; |
393 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; |
394 | format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
395 | format.format.width = 4096; |
396 | format.format.height = 4096; |
397 | ipipe_set_format(sd, sd_state: fh ? fh->state : NULL, fmt: &format); |
398 | |
399 | return 0; |
400 | } |
401 | |
402 | /* V4L2 subdev video operations */ |
403 | static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { |
404 | .s_stream = ipipe_set_stream, |
405 | }; |
406 | |
407 | /* V4L2 subdev pad operations */ |
408 | static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { |
409 | .enum_mbus_code = ipipe_enum_mbus_code, |
410 | .enum_frame_size = ipipe_enum_frame_size, |
411 | .get_fmt = ipipe_get_format, |
412 | .set_fmt = ipipe_set_format, |
413 | .link_validate = ipipe_link_validate, |
414 | }; |
415 | |
416 | /* V4L2 subdev operations */ |
417 | static const struct v4l2_subdev_ops ipipe_v4l2_ops = { |
418 | .video = &ipipe_v4l2_video_ops, |
419 | .pad = &ipipe_v4l2_pad_ops, |
420 | }; |
421 | |
422 | /* V4L2 subdev internal operations */ |
423 | static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { |
424 | .open = ipipe_init_formats, |
425 | }; |
426 | |
427 | /* ----------------------------------------------------------------------------- |
428 | * Media entity operations |
429 | */ |
430 | |
431 | /* |
432 | * ipipe_link_setup - Setup IPIPE connections |
433 | * @entity: IPIPE media entity |
434 | * @local: Pad at the local end of the link |
435 | * @remote: Pad at the remote end of the link |
436 | * @flags: Link flags |
437 | * |
438 | * return -EINVAL or zero on success |
439 | */ |
440 | static int ipipe_link_setup(struct media_entity *entity, |
441 | const struct media_pad *local, |
442 | const struct media_pad *remote, u32 flags) |
443 | { |
444 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); |
445 | struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); |
446 | struct iss_device *iss = to_iss_device(ipipe); |
447 | |
448 | if (!is_media_entity_v4l2_subdev(entity: remote->entity)) |
449 | return -EINVAL; |
450 | |
451 | switch (local->index) { |
452 | case IPIPE_PAD_SINK: |
453 | /* Read from IPIPEIF. */ |
454 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { |
455 | ipipe->input = IPIPE_INPUT_NONE; |
456 | break; |
457 | } |
458 | |
459 | if (ipipe->input != IPIPE_INPUT_NONE) |
460 | return -EBUSY; |
461 | |
462 | if (remote->entity == &iss->ipipeif.subdev.entity) |
463 | ipipe->input = IPIPE_INPUT_IPIPEIF; |
464 | |
465 | break; |
466 | |
467 | case IPIPE_PAD_SOURCE_VP: |
468 | /* Send to RESIZER */ |
469 | if (flags & MEDIA_LNK_FL_ENABLED) { |
470 | if (ipipe->output & ~IPIPE_OUTPUT_VP) |
471 | return -EBUSY; |
472 | ipipe->output |= IPIPE_OUTPUT_VP; |
473 | } else { |
474 | ipipe->output &= ~IPIPE_OUTPUT_VP; |
475 | } |
476 | break; |
477 | |
478 | default: |
479 | return -EINVAL; |
480 | } |
481 | |
482 | return 0; |
483 | } |
484 | |
485 | /* media operations */ |
486 | static const struct media_entity_operations ipipe_media_ops = { |
487 | .link_setup = ipipe_link_setup, |
488 | .link_validate = v4l2_subdev_link_validate, |
489 | }; |
490 | |
491 | /* |
492 | * ipipe_init_entities - Initialize V4L2 subdev and media entity |
493 | * @ipipe: ISS ISP IPIPE module |
494 | * |
495 | * Return 0 on success and a negative error code on failure. |
496 | */ |
497 | static int ipipe_init_entities(struct iss_ipipe_device *ipipe) |
498 | { |
499 | struct v4l2_subdev *sd = &ipipe->subdev; |
500 | struct media_pad *pads = ipipe->pads; |
501 | struct media_entity *me = &sd->entity; |
502 | int ret; |
503 | |
504 | ipipe->input = IPIPE_INPUT_NONE; |
505 | |
506 | v4l2_subdev_init(sd, ops: &ipipe_v4l2_ops); |
507 | sd->internal_ops = &ipipe_v4l2_internal_ops; |
508 | strscpy(sd->name, "OMAP4 ISS ISP IPIPE" , sizeof(sd->name)); |
509 | sd->grp_id = BIT(16); /* group ID for iss subdevs */ |
510 | v4l2_set_subdevdata(sd, p: ipipe); |
511 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
512 | |
513 | pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; |
514 | pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; |
515 | |
516 | me->ops = &ipipe_media_ops; |
517 | ret = media_entity_pads_init(entity: me, IPIPE_PADS_NUM, pads); |
518 | if (ret < 0) |
519 | return ret; |
520 | |
521 | ipipe_init_formats(sd, NULL); |
522 | |
523 | return 0; |
524 | } |
525 | |
526 | void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe) |
527 | { |
528 | v4l2_device_unregister_subdev(sd: &ipipe->subdev); |
529 | } |
530 | |
531 | int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe, |
532 | struct v4l2_device *vdev) |
533 | { |
534 | int ret; |
535 | |
536 | /* Register the subdev and video node. */ |
537 | ret = v4l2_device_register_subdev(v4l2_dev: vdev, sd: &ipipe->subdev); |
538 | if (ret < 0) |
539 | goto error; |
540 | |
541 | return 0; |
542 | |
543 | error: |
544 | omap4iss_ipipe_unregister_entities(ipipe); |
545 | return ret; |
546 | } |
547 | |
548 | /* ----------------------------------------------------------------------------- |
549 | * ISP IPIPE initialisation and cleanup |
550 | */ |
551 | |
552 | /* |
553 | * omap4iss_ipipe_init - IPIPE module initialization. |
554 | * @iss: Device pointer specific to the OMAP4 ISS. |
555 | * |
556 | * TODO: Get the initialisation values from platform data. |
557 | * |
558 | * Return 0 on success or a negative error code otherwise. |
559 | */ |
560 | int omap4iss_ipipe_init(struct iss_device *iss) |
561 | { |
562 | struct iss_ipipe_device *ipipe = &iss->ipipe; |
563 | |
564 | ipipe->state = ISS_PIPELINE_STREAM_STOPPED; |
565 | init_waitqueue_head(&ipipe->wait); |
566 | |
567 | return ipipe_init_entities(ipipe); |
568 | } |
569 | |
570 | /* |
571 | * omap4iss_ipipe_cleanup - IPIPE module cleanup. |
572 | * @iss: Device pointer specific to the OMAP4 ISS. |
573 | */ |
574 | void omap4iss_ipipe_cleanup(struct iss_device *iss) |
575 | { |
576 | struct iss_ipipe_device *ipipe = &iss->ipipe; |
577 | |
578 | media_entity_cleanup(entity: &ipipe->subdev.entity); |
579 | } |
580 | |