]> Git Repo - linux.git/blob - drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
arm64: avoid prototype warnings for syscalls
[linux.git] / drivers / media / platform / nxp / imx8-isi / imx8-isi-pipe.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform
4  *
5  * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which
6  * used to process image from camera sensor to memory or DC
7  *
8  * Copyright (c) 2019 NXP Semiconductor
9  */
10
11 #include <linux/device.h>
12 #include <linux/errno.h>
13 #include <linux/interrupt.h>
14 #include <linux/kernel.h>
15 #include <linux/minmax.h>
16 #include <linux/mutex.h>
17 #include <linux/of.h>
18 #include <linux/platform_device.h>
19 #include <linux/types.h>
20 #include <linux/videodev2.h>
21
22 #include <media/media-entity.h>
23 #include <media/v4l2-subdev.h>
24 #include <media/videobuf2-v4l2.h>
25
26 #include "imx8-isi-core.h"
27 #include "imx8-isi-regs.h"
28
29 /*
30  * While the ISI receives data from the gasket on a 3x12-bit bus, the pipeline
31  * subdev conceptually includes the gasket in order to avoid exposing an extra
32  * subdev between the CSIS and the ISI. We thus need to expose media bus codes
33  * corresponding to the CSIS output, which is narrower.
34  */
35 static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {
36         /* YUV formats */
37         {
38                 .mbus_code      = MEDIA_BUS_FMT_UYVY8_1X16,
39                 .output         = MEDIA_BUS_FMT_YUV8_1X24,
40                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK),
41                 .encoding       = MXC_ISI_ENC_YUV,
42         }, {
43                 .mbus_code      = MEDIA_BUS_FMT_YUV8_1X24,
44                 .output         = MEDIA_BUS_FMT_YUV8_1X24,
45                 .pads           = BIT(MXC_ISI_PIPE_PAD_SOURCE),
46                 .encoding       = MXC_ISI_ENC_YUV,
47         },
48         /* RGB formats */
49         {
50                 .mbus_code      = MEDIA_BUS_FMT_RGB565_1X16,
51                 .output         = MEDIA_BUS_FMT_RGB888_1X24,
52                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK),
53                 .encoding       = MXC_ISI_ENC_RGB,
54         }, {
55                 .mbus_code      = MEDIA_BUS_FMT_RGB888_1X24,
56                 .output         = MEDIA_BUS_FMT_RGB888_1X24,
57                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
58                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
59                 .encoding       = MXC_ISI_ENC_RGB,
60         },
61         /* RAW formats */
62         {
63                 .mbus_code      = MEDIA_BUS_FMT_Y8_1X8,
64                 .output         = MEDIA_BUS_FMT_Y8_1X8,
65                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
66                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
67                 .encoding       = MXC_ISI_ENC_RAW,
68         }, {
69                 .mbus_code      = MEDIA_BUS_FMT_Y10_1X10,
70                 .output         = MEDIA_BUS_FMT_Y10_1X10,
71                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
72                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
73                 .encoding       = MXC_ISI_ENC_RAW,
74         }, {
75                 .mbus_code      = MEDIA_BUS_FMT_Y12_1X12,
76                 .output         = MEDIA_BUS_FMT_Y12_1X12,
77                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
78                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
79                 .encoding       = MXC_ISI_ENC_RAW,
80         }, {
81                 .mbus_code      = MEDIA_BUS_FMT_Y14_1X14,
82                 .output         = MEDIA_BUS_FMT_Y14_1X14,
83                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
84                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
85                 .encoding       = MXC_ISI_ENC_RAW,
86         }, {
87                 .mbus_code      = MEDIA_BUS_FMT_SBGGR8_1X8,
88                 .output         = MEDIA_BUS_FMT_SBGGR8_1X8,
89                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
90                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
91                 .encoding       = MXC_ISI_ENC_RAW,
92         }, {
93                 .mbus_code      = MEDIA_BUS_FMT_SGBRG8_1X8,
94                 .output         = MEDIA_BUS_FMT_SGBRG8_1X8,
95                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
96                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
97                 .encoding       = MXC_ISI_ENC_RAW,
98         }, {
99                 .mbus_code      = MEDIA_BUS_FMT_SGRBG8_1X8,
100                 .output         = MEDIA_BUS_FMT_SGRBG8_1X8,
101                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
102                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
103                 .encoding       = MXC_ISI_ENC_RAW,
104         }, {
105                 .mbus_code      = MEDIA_BUS_FMT_SRGGB8_1X8,
106                 .output         = MEDIA_BUS_FMT_SRGGB8_1X8,
107                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
108                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
109                 .encoding       = MXC_ISI_ENC_RAW,
110         }, {
111                 .mbus_code      = MEDIA_BUS_FMT_SBGGR10_1X10,
112                 .output         = MEDIA_BUS_FMT_SBGGR10_1X10,
113                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
114                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
115                 .encoding       = MXC_ISI_ENC_RAW,
116         }, {
117                 .mbus_code      = MEDIA_BUS_FMT_SGBRG10_1X10,
118                 .output         = MEDIA_BUS_FMT_SGBRG10_1X10,
119                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
120                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
121                 .encoding       = MXC_ISI_ENC_RAW,
122         }, {
123                 .mbus_code      = MEDIA_BUS_FMT_SGRBG10_1X10,
124                 .output         = MEDIA_BUS_FMT_SGRBG10_1X10,
125                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
126                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
127                 .encoding       = MXC_ISI_ENC_RAW,
128         }, {
129                 .mbus_code      = MEDIA_BUS_FMT_SRGGB10_1X10,
130                 .output         = MEDIA_BUS_FMT_SRGGB10_1X10,
131                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
132                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
133                 .encoding       = MXC_ISI_ENC_RAW,
134         }, {
135                 .mbus_code      = MEDIA_BUS_FMT_SBGGR12_1X12,
136                 .output         = MEDIA_BUS_FMT_SBGGR12_1X12,
137                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
138                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
139                 .encoding       = MXC_ISI_ENC_RAW,
140         }, {
141                 .mbus_code      = MEDIA_BUS_FMT_SGBRG12_1X12,
142                 .output         = MEDIA_BUS_FMT_SGBRG12_1X12,
143                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
144                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
145                 .encoding       = MXC_ISI_ENC_RAW,
146         }, {
147                 .mbus_code      = MEDIA_BUS_FMT_SGRBG12_1X12,
148                 .output         = MEDIA_BUS_FMT_SGRBG12_1X12,
149                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
150                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
151                 .encoding       = MXC_ISI_ENC_RAW,
152         }, {
153                 .mbus_code      = MEDIA_BUS_FMT_SRGGB12_1X12,
154                 .output         = MEDIA_BUS_FMT_SRGGB12_1X12,
155                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
156                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
157                 .encoding       = MXC_ISI_ENC_RAW,
158         }, {
159                 .mbus_code      = MEDIA_BUS_FMT_SBGGR14_1X14,
160                 .output         = MEDIA_BUS_FMT_SBGGR14_1X14,
161                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
162                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
163                 .encoding       = MXC_ISI_ENC_RAW,
164         }, {
165                 .mbus_code      = MEDIA_BUS_FMT_SGBRG14_1X14,
166                 .output         = MEDIA_BUS_FMT_SGBRG14_1X14,
167                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
168                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
169                 .encoding       = MXC_ISI_ENC_RAW,
170         }, {
171                 .mbus_code      = MEDIA_BUS_FMT_SGRBG14_1X14,
172                 .output         = MEDIA_BUS_FMT_SGRBG14_1X14,
173                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
174                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
175                 .encoding       = MXC_ISI_ENC_RAW,
176         }, {
177                 .mbus_code      = MEDIA_BUS_FMT_SRGGB14_1X14,
178                 .output         = MEDIA_BUS_FMT_SRGGB14_1X14,
179                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
180                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
181                 .encoding       = MXC_ISI_ENC_RAW,
182         },
183         /* JPEG */
184         {
185                 .mbus_code      = MEDIA_BUS_FMT_JPEG_1X8,
186                 .output         = MEDIA_BUS_FMT_JPEG_1X8,
187                 .pads           = BIT(MXC_ISI_PIPE_PAD_SINK)
188                                 | BIT(MXC_ISI_PIPE_PAD_SOURCE),
189                 .encoding       = MXC_ISI_ENC_RAW,
190         }
191 };
192
193 const struct mxc_isi_bus_format_info *
194 mxc_isi_bus_format_by_code(u32 code, unsigned int pad)
195 {
196         unsigned int i;
197
198         for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) {
199                 const struct mxc_isi_bus_format_info *info =
200                         &mxc_isi_bus_formats[i];
201
202                 if (info->mbus_code == code && info->pads & BIT(pad))
203                         return info;
204         }
205
206         return NULL;
207 }
208
209 const struct mxc_isi_bus_format_info *
210 mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad)
211 {
212         unsigned int i;
213
214         for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) {
215                 const struct mxc_isi_bus_format_info *info =
216                         &mxc_isi_bus_formats[i];
217
218                 if (!(info->pads & BIT(pad)))
219                         continue;
220
221                 if (!index)
222                         return info;
223
224                 index--;
225         }
226
227         return NULL;
228 }
229
230 static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)
231 {
232         return container_of(sd, struct mxc_isi_pipe, sd);
233 }
234
235 int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
236 {
237         struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
238         const struct mxc_isi_bus_format_info *sink_info;
239         const struct mxc_isi_bus_format_info *src_info;
240         const struct v4l2_mbus_framefmt *sink_fmt;
241         const struct v4l2_mbus_framefmt *src_fmt;
242         const struct v4l2_rect *compose;
243         struct v4l2_subdev_state *state;
244         struct v4l2_subdev *sd = &pipe->sd;
245         struct v4l2_area in_size, scale;
246         struct v4l2_rect crop;
247         u32 input;
248         int ret;
249
250         /*
251          * Find the connected input by inspecting the crossbar switch routing
252          * table.
253          */
254         state = v4l2_subdev_lock_and_get_active_state(&xbar->sd);
255         ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
256                                                     xbar->num_sinks + pipe->id,
257                                                     0, &input, NULL);
258         v4l2_subdev_unlock_state(state);
259
260         if (ret)
261                 return -EPIPE;
262
263         /* Configure the pipeline. */
264         state = v4l2_subdev_lock_and_get_active_state(sd);
265
266         sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK);
267         src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE);
268         compose = v4l2_subdev_get_try_compose(sd, state, MXC_ISI_PIPE_PAD_SINK);
269         crop = *v4l2_subdev_get_try_crop(sd, state, MXC_ISI_PIPE_PAD_SOURCE);
270
271         sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
272                                                MXC_ISI_PIPE_PAD_SINK);
273         src_info = mxc_isi_bus_format_by_code(src_fmt->code,
274                                               MXC_ISI_PIPE_PAD_SOURCE);
275
276         in_size.width = sink_fmt->width;
277         in_size.height = sink_fmt->height;
278         scale.width = compose->width;
279         scale.height = compose->height;
280
281         v4l2_subdev_unlock_state(state);
282
283         /* Configure the ISI channel. */
284         mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
285                                sink_info->encoding, src_info->encoding);
286
287         mxc_isi_channel_enable(pipe);
288
289         /* Enable streams on the crossbar switch. */
290         ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
291                                          BIT(0));
292         if (ret) {
293                 mxc_isi_channel_disable(pipe);
294                 dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",
295                         pipe->id);
296                 return ret;
297         }
298
299         return 0;
300 }
301
302 void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)
303 {
304         struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
305         int ret;
306
307         ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
308                                           BIT(0));
309         if (ret)
310                 dev_err(pipe->isi->dev, "Failed to disable pipe %u\n",
311                         pipe->id);
312
313         mxc_isi_channel_disable(pipe);
314 }
315
316 /* -----------------------------------------------------------------------------
317  * V4L2 subdev operations
318  */
319
320 static struct v4l2_mbus_framefmt *
321 mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe,
322                             struct v4l2_subdev_state *state,
323                             unsigned int pad)
324 {
325         return v4l2_subdev_get_try_format(&pipe->sd, state, pad);
326 }
327
328 static struct v4l2_rect *
329 mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe,
330                           struct v4l2_subdev_state *state,
331                           unsigned int pad)
332 {
333         return v4l2_subdev_get_try_crop(&pipe->sd, state, pad);
334 }
335
336 static struct v4l2_rect *
337 mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe,
338                              struct v4l2_subdev_state *state,
339                              unsigned int pad)
340 {
341         return v4l2_subdev_get_try_compose(&pipe->sd, state, pad);
342 }
343
344 static int mxc_isi_pipe_init_cfg(struct v4l2_subdev *sd,
345                                  struct v4l2_subdev_state *state)
346 {
347         struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
348         struct v4l2_mbus_framefmt *fmt_source;
349         struct v4l2_mbus_framefmt *fmt_sink;
350         struct v4l2_rect *compose;
351         struct v4l2_rect *crop;
352
353         fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state,
354                                                MXC_ISI_PIPE_PAD_SINK);
355         fmt_source = mxc_isi_pipe_get_pad_format(pipe, state,
356                                                  MXC_ISI_PIPE_PAD_SOURCE);
357
358         fmt_sink->width = MXC_ISI_DEF_WIDTH;
359         fmt_sink->height = MXC_ISI_DEF_HEIGHT;
360         fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK;
361         fmt_sink->field = V4L2_FIELD_NONE;
362         fmt_sink->colorspace = V4L2_COLORSPACE_JPEG;
363         fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
364         fmt_sink->quantization =
365                 V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
366                                               fmt_sink->ycbcr_enc);
367         fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
368
369         *fmt_source = *fmt_sink;
370         fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;
371
372         compose = mxc_isi_pipe_get_pad_compose(pipe, state,
373                                                MXC_ISI_PIPE_PAD_SINK);
374         crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);
375
376         compose->left = 0;
377         compose->top = 0;
378         compose->width = MXC_ISI_DEF_WIDTH;
379         compose->height = MXC_ISI_DEF_HEIGHT;
380
381         *crop = *compose;
382
383         return 0;
384 }
385
386 static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd,
387                                        struct v4l2_subdev_state *state,
388                                        struct v4l2_subdev_mbus_code_enum *code)
389 {
390         static const u32 output_codes[] = {
391                 MEDIA_BUS_FMT_YUV8_1X24,
392                 MEDIA_BUS_FMT_RGB888_1X24,
393         };
394         struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
395         const struct mxc_isi_bus_format_info *info;
396         unsigned int index;
397         unsigned int i;
398
399         if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) {
400                 const struct v4l2_mbus_framefmt *format;
401
402                 format = mxc_isi_pipe_get_pad_format(pipe, state,
403                                                      MXC_ISI_PIPE_PAD_SINK);
404                 info = mxc_isi_bus_format_by_code(format->code,
405                                                   MXC_ISI_PIPE_PAD_SINK);
406
407                 if (info->encoding == MXC_ISI_ENC_RAW) {
408                         /*
409                          * For RAW formats, the sink and source media bus codes
410                          * must match.
411                          */
412                         if (code->index)
413                                 return -EINVAL;
414
415                         code->code = info->output;
416                 } else {
417                         /*
418                          * For RGB or YUV formats, the ISI supports format
419                          * conversion. Either of the two output formats can be
420                          * used regardless of the input.
421                          */
422                         if (code->index > 1)
423                                 return -EINVAL;
424
425                         code->code = output_codes[code->index];
426                 }
427
428                 return 0;
429         }
430
431         index = code->index;
432
433         for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) {
434                 info = &mxc_isi_bus_formats[i];
435
436                 if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK)))
437                         continue;
438
439                 if (index == 0) {
440                         code->code = info->mbus_code;
441                         return 0;
442                 }
443
444                 index--;
445         }
446
447         return -EINVAL;
448 }
449
450 static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
451                                 struct v4l2_subdev_state *state,
452                                 struct v4l2_subdev_format *fmt)
453 {
454         struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
455         struct v4l2_mbus_framefmt *mf = &fmt->format;
456         const struct mxc_isi_bus_format_info *info;
457         struct v4l2_mbus_framefmt *format;
458         struct v4l2_rect *rect;
459
460         if (vb2_is_busy(&pipe->video.vb2_q))
461                 return -EBUSY;
462
463         if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) {
464                 unsigned int max_width;
465
466                 info = mxc_isi_bus_format_by_code(mf->code,
467                                                   MXC_ISI_PIPE_PAD_SINK);
468                 if (!info)
469                         info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK,
470                                                           MXC_ISI_PIPE_PAD_SINK);
471
472                 /*
473                  * Limit the max line length if there's no adjacent pipe to
474                  * chain with.
475                  */
476                 max_width = pipe->id == pipe->isi->pdata->num_channels - 1
477                           ? MXC_ISI_MAX_WIDTH_UNCHAINED
478                           : MXC_ISI_MAX_WIDTH_CHAINED;
479
480                 mf->code = info->mbus_code;
481                 mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH, max_width);
482                 mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT,
483                                    MXC_ISI_MAX_HEIGHT);
484
485                 /* Propagate the format to the source pad. */
486                 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
487                                                     MXC_ISI_PIPE_PAD_SINK);
488                 rect->width = mf->width;
489                 rect->height = mf->height;
490
491                 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
492                                                  MXC_ISI_PIPE_PAD_SOURCE);
493                 rect->left = 0;
494                 rect->top = 0;
495                 rect->width = mf->width;
496                 rect->height = mf->height;
497
498                 format = mxc_isi_pipe_get_pad_format(pipe, state,
499                                                      MXC_ISI_PIPE_PAD_SOURCE);
500                 format->code = info->output;
501                 format->width = mf->width;
502                 format->height = mf->height;
503         } else {
504                 /*
505                  * For RGB or YUV formats, the ISI supports RGB <-> YUV format
506                  * conversion. For RAW formats, the sink and source media bus
507                  * codes must match.
508                  */
509                 format = mxc_isi_pipe_get_pad_format(pipe, state,
510                                                      MXC_ISI_PIPE_PAD_SINK);
511                 info = mxc_isi_bus_format_by_code(format->code,
512                                                   MXC_ISI_PIPE_PAD_SINK);
513
514                 if (info->encoding != MXC_ISI_ENC_RAW) {
515                         if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 &&
516                             mf->code != MEDIA_BUS_FMT_RGB888_1X24)
517                                 mf->code = info->output;
518
519                         info = mxc_isi_bus_format_by_code(mf->code,
520                                                           MXC_ISI_PIPE_PAD_SOURCE);
521                 }
522
523                 mf->code = info->output;
524
525                 /*
526                  * The width and height on the source can't be changed, they
527                  * must match the crop rectangle size.
528                  */
529                 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
530                                                  MXC_ISI_PIPE_PAD_SOURCE);
531
532                 mf->width = rect->width;
533                 mf->height = rect->height;
534         }
535
536         format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
537         *format = *mf;
538
539         dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",
540                 fmt->pad, mf->code, mf->width, mf->height);
541
542         return 0;
543 }
544
545 static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd,
546                                       struct v4l2_subdev_state *state,
547                                       struct v4l2_subdev_selection *sel)
548 {
549         struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
550         const struct v4l2_mbus_framefmt *format;
551         const struct v4l2_rect *rect;
552
553         switch (sel->target) {
554         case V4L2_SEL_TGT_COMPOSE_BOUNDS:
555                 if (sel->pad != MXC_ISI_PIPE_PAD_SINK)
556                         /* No compose rectangle on source pad. */
557                         return -EINVAL;
558
559                 /* The sink compose is bound by the sink format. */
560                 format = mxc_isi_pipe_get_pad_format(pipe, state,
561                                                      MXC_ISI_PIPE_PAD_SINK);
562                 sel->r.left = 0;
563                 sel->r.top = 0;
564                 sel->r.width = format->width;
565                 sel->r.height = format->height;
566                 break;
567
568         case V4L2_SEL_TGT_CROP_BOUNDS:
569                 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)
570                         /* No crop rectangle on sink pad. */
571                         return -EINVAL;
572
573                 /* The source crop is bound by the sink compose. */
574                 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
575                                                     MXC_ISI_PIPE_PAD_SINK);
576                 sel->r = *rect;
577                 break;
578
579         case V4L2_SEL_TGT_CROP:
580                 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)
581                         /* No crop rectangle on sink pad. */
582                         return -EINVAL;
583
584                 rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad);
585                 sel->r = *rect;
586                 break;
587
588         case V4L2_SEL_TGT_COMPOSE:
589                 if (sel->pad != MXC_ISI_PIPE_PAD_SINK)
590                         /* No compose rectangle on source pad. */
591                         return -EINVAL;
592
593                 rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad);
594                 sel->r = *rect;
595                 break;
596
597         default:
598                 return -EINVAL;
599         }
600
601         return 0;
602 }
603
604 static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,
605                                       struct v4l2_subdev_state *state,
606                                       struct v4l2_subdev_selection *sel)
607 {
608         struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
609         struct v4l2_mbus_framefmt *format;
610         struct v4l2_rect *rect;
611
612         switch (sel->target) {
613         case V4L2_SEL_TGT_CROP:
614                 if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)
615                         /* The pipeline support cropping on the source only. */
616                         return -EINVAL;
617
618                 /* The source crop is bound by the sink compose. */
619                 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
620                                                     MXC_ISI_PIPE_PAD_SINK);
621                 sel->r.left = clamp_t(s32, sel->r.left, 0, rect->width - 1);
622                 sel->r.top = clamp_t(s32, sel->r.top, 0, rect->height - 1);
623                 sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
624                                      rect->width - sel->r.left);
625                 sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
626                                       rect->height - sel->r.top);
627
628                 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
629                                                  MXC_ISI_PIPE_PAD_SOURCE);
630                 *rect = sel->r;
631
632                 /* Propagate the crop rectangle to the source pad. */
633                 format = mxc_isi_pipe_get_pad_format(pipe, state,
634                                                      MXC_ISI_PIPE_PAD_SOURCE);
635                 format->width = sel->r.width;
636                 format->height = sel->r.height;
637                 break;
638
639         case V4L2_SEL_TGT_COMPOSE:
640                 if (sel->pad != MXC_ISI_PIPE_PAD_SINK)
641                         /* Composing is supported on the sink only. */
642                         return -EINVAL;
643
644                 /* The sink crop is bound by the sink format downscaling only). */
645                 format = mxc_isi_pipe_get_pad_format(pipe, state,
646                                                      MXC_ISI_PIPE_PAD_SINK);
647
648                 sel->r.left = 0;
649                 sel->r.top = 0;
650                 sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
651                                      format->width);
652                 sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
653                                       format->height);
654
655                 rect = mxc_isi_pipe_get_pad_compose(pipe, state,
656                                                     MXC_ISI_PIPE_PAD_SINK);
657                 *rect = sel->r;
658
659                 /* Propagate the compose rectangle to the source pad. */
660                 rect = mxc_isi_pipe_get_pad_crop(pipe, state,
661                                                  MXC_ISI_PIPE_PAD_SOURCE);
662                 rect->left = 0;
663                 rect->top = 0;
664                 rect->width = sel->r.width;
665                 rect->height = sel->r.height;
666
667                 format = mxc_isi_pipe_get_pad_format(pipe, state,
668                                                      MXC_ISI_PIPE_PAD_SOURCE);
669                 format->width = sel->r.width;
670                 format->height = sel->r.height;
671                 break;
672
673         default:
674                 return -EINVAL;
675         }
676
677         dev_dbg(pipe->isi->dev, "%s, target %#x: (%d,%d)/%dx%d", __func__,
678                 sel->target, sel->r.left, sel->r.top, sel->r.width,
679                 sel->r.height);
680
681         return 0;
682 }
683
684 static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = {
685         .init_cfg = mxc_isi_pipe_init_cfg,
686         .enum_mbus_code = mxc_isi_pipe_enum_mbus_code,
687         .get_fmt = v4l2_subdev_get_fmt,
688         .set_fmt = mxc_isi_pipe_set_fmt,
689         .get_selection = mxc_isi_pipe_get_selection,
690         .set_selection = mxc_isi_pipe_set_selection,
691 };
692
693 static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {
694         .pad = &mxc_isi_pipe_subdev_pad_ops,
695 };
696
697 /* -----------------------------------------------------------------------------
698  * IRQ handling
699  */
700
701 static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv)
702 {
703         struct mxc_isi_pipe *pipe = priv;
704         const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;
705         u32 status;
706
707         status = mxc_isi_channel_irq_status(pipe, true);
708
709         if (status & CHNL_STS_FRM_STRD) {
710                 if (!WARN_ON(!pipe->irq_handler))
711                         pipe->irq_handler(pipe, status);
712         }
713
714         if (status & (CHNL_STS_AXI_WR_ERR_Y |
715                       CHNL_STS_AXI_WR_ERR_U |
716                       CHNL_STS_AXI_WR_ERR_V))
717                 dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n",
718                         __func__, status);
719
720         if (status & (ier_reg->panic_y_buf_en.mask |
721                       ier_reg->panic_u_buf_en.mask |
722                       ier_reg->panic_v_buf_en.mask))
723                 dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n",
724                         __func__, status);
725
726         if (status & (ier_reg->oflw_y_buf_en.mask |
727                       ier_reg->oflw_u_buf_en.mask |
728                       ier_reg->oflw_v_buf_en.mask))
729                 dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n",
730                         __func__, status);
731
732         if (status & (ier_reg->excs_oflw_y_buf_en.mask |
733                       ier_reg->excs_oflw_u_buf_en.mask |
734                       ier_reg->excs_oflw_v_buf_en.mask))
735                 dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n",
736                         __func__, status);
737
738         return IRQ_HANDLED;
739 }
740
741 /* -----------------------------------------------------------------------------
742  * Init & cleanup
743  */
744
745 static const struct media_entity_operations mxc_isi_pipe_entity_ops = {
746         .link_validate  = v4l2_subdev_link_validate,
747 };
748
749 int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
750 {
751         struct mxc_isi_pipe *pipe = &isi->pipes[id];
752         struct v4l2_subdev *sd;
753         int irq;
754         int ret;
755
756         pipe->id = id;
757         pipe->isi = isi;
758         pipe->regs = isi->regs + id * isi->pdata->reg_offset;
759
760         mutex_init(&pipe->lock);
761
762         pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF
763                             | MXC_ISI_CHANNEL_RES_OUTPUT_BUF;
764         pipe->acquired_res = 0;
765         pipe->chained_res = 0;
766         pipe->chained = false;
767
768         sd = &pipe->sd;
769         v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);
770         sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
771         snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id);
772         sd->dev = isi->dev;
773
774         sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
775         sd->entity.ops = &mxc_isi_pipe_entity_ops;
776
777         pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
778         pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
779
780         ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM,
781                                      pipe->pads);
782         if (ret)
783                 goto error;
784
785         ret = v4l2_subdev_init_finalize(sd);
786         if (ret < 0)
787                 goto error;
788
789         /* Register IRQ handler. */
790         mxc_isi_channel_irq_clear(pipe);
791
792         irq = platform_get_irq(to_platform_device(isi->dev), id);
793         if (irq < 0) {
794                 dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq);
795                 ret = irq;
796                 goto error;
797         }
798
799         ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
800                                0, dev_name(isi->dev), pipe);
801         if (ret < 0) {
802                 dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
803                 goto error;
804         }
805
806         return 0;
807
808 error:
809         media_entity_cleanup(&sd->entity);
810         mutex_destroy(&pipe->lock);
811
812         return ret;
813 }
814
815 void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)
816 {
817         struct v4l2_subdev *sd = &pipe->sd;
818
819         media_entity_cleanup(&sd->entity);
820         mutex_destroy(&pipe->lock);
821 }
822
823 int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe,
824                          mxc_isi_pipe_irq_t irq_handler)
825 {
826         const struct mxc_isi_bus_format_info *sink_info;
827         const struct mxc_isi_bus_format_info *src_info;
828         struct v4l2_mbus_framefmt *sink_fmt;
829         const struct v4l2_mbus_framefmt *src_fmt;
830         struct v4l2_subdev *sd = &pipe->sd;
831         struct v4l2_subdev_state *state;
832         bool bypass;
833         int ret;
834
835         state = v4l2_subdev_lock_and_get_active_state(sd);
836         sink_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SINK);
837         src_fmt = v4l2_subdev_get_try_format(sd, state, MXC_ISI_PIPE_PAD_SOURCE);
838         v4l2_subdev_unlock_state(state);
839
840         sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
841                                                MXC_ISI_PIPE_PAD_SINK);
842         src_info = mxc_isi_bus_format_by_code(src_fmt->code,
843                                               MXC_ISI_PIPE_PAD_SOURCE);
844
845         bypass = sink_fmt->width == src_fmt->width &&
846                  sink_fmt->height == src_fmt->height &&
847                  sink_info->encoding == src_info->encoding;
848
849         ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass);
850         if (ret)
851                 return ret;
852
853         /* Chain the channel if needed for wide resolutions. */
854         if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
855                 ret = mxc_isi_channel_chain(pipe, bypass);
856                 if (ret)
857                         mxc_isi_channel_release(pipe);
858         }
859
860         return ret;
861 }
862
863 void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)
864 {
865         mxc_isi_channel_release(pipe);
866         mxc_isi_channel_unchain(pipe);
867 }
This page took 0.085326 seconds and 4 git commands to generate.