1513 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform4 *5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which6 * used to process image from camera sensor to memory or DC7 *8 * Copyright (c) 2019 NXP Semiconductor9 */10 11#include <linux/device.h>12#include <linux/dma-mapping.h>13#include <linux/errno.h>14#include <linux/kernel.h>15#include <linux/media-bus-format.h>16#include <linux/minmax.h>17#include <linux/pm_runtime.h>18#include <linux/string.h>19#include <linux/types.h>20#include <linux/videodev2.h>21 22#include <media/media-entity.h>23#include <media/v4l2-ctrls.h>24#include <media/v4l2-dev.h>25#include <media/v4l2-event.h>26#include <media/v4l2-fh.h>27#include <media/v4l2-ioctl.h>28#include <media/v4l2-subdev.h>29#include <media/videobuf2-core.h>30#include <media/videobuf2-dma-contig.h>31#include <media/videobuf2-v4l2.h>32 33#include "imx8-isi-core.h"34#include "imx8-isi-regs.h"35 36/* Keep the first entry matching MXC_ISI_DEF_PIXEL_FORMAT */37static const struct mxc_isi_format_info mxc_isi_formats[] = {38 /* YUV formats */39 {40 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,41 .fourcc = V4L2_PIX_FMT_YUYV,42 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT43 | MXC_ISI_VIDEO_M2M_CAP,44 .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_YUV422_1P8P,45 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_1P8P,46 .mem_planes = 1,47 .color_planes = 1,48 .depth = { 16 },49 .encoding = MXC_ISI_ENC_YUV,50 }, {51 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,52 .fourcc = V4L2_PIX_FMT_YUVA32,53 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,54 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV444_1P8,55 .mem_planes = 1,56 .color_planes = 1,57 .depth = { 32 },58 .encoding = MXC_ISI_ENC_YUV,59 }, {60 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,61 .fourcc = V4L2_PIX_FMT_NV12,62 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,63 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV420_2P8P,64 .color_planes = 2,65 .mem_planes = 1,66 .depth = { 8, 16 },67 .hsub = 2,68 .vsub = 2,69 .encoding = MXC_ISI_ENC_YUV,70 }, {71 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,72 .fourcc = V4L2_PIX_FMT_NV12M,73 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,74 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV420_2P8P,75 .mem_planes = 2,76 .color_planes = 2,77 .depth = { 8, 16 },78 .hsub = 2,79 .vsub = 2,80 .encoding = MXC_ISI_ENC_YUV,81 }, {82 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,83 .fourcc = V4L2_PIX_FMT_NV16,84 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,85 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_2P8P,86 .color_planes = 2,87 .mem_planes = 1,88 .depth = { 8, 16 },89 .hsub = 2,90 .vsub = 1,91 .encoding = MXC_ISI_ENC_YUV,92 }, {93 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,94 .fourcc = V4L2_PIX_FMT_NV16M,95 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,96 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV422_2P8P,97 .mem_planes = 2,98 .color_planes = 2,99 .depth = { 8, 16 },100 .hsub = 2,101 .vsub = 1,102 .encoding = MXC_ISI_ENC_YUV,103 }, {104 .mbus_code = MEDIA_BUS_FMT_YUV8_1X24,105 .fourcc = V4L2_PIX_FMT_YUV444M,106 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,107 .isi_out_format = CHNL_IMG_CTRL_FORMAT_YUV444_3P8P,108 .mem_planes = 3,109 .color_planes = 3,110 .depth = { 8, 8, 8 },111 .hsub = 1,112 .vsub = 1,113 .encoding = MXC_ISI_ENC_YUV,114 },115 /* RGB formats */116 {117 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,118 .fourcc = V4L2_PIX_FMT_RGB565,119 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT120 | MXC_ISI_VIDEO_M2M_CAP,121 .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGB565,122 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGB565,123 .mem_planes = 1,124 .color_planes = 1,125 .depth = { 16 },126 .encoding = MXC_ISI_ENC_RGB,127 }, {128 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,129 .fourcc = V4L2_PIX_FMT_RGB24,130 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT131 | MXC_ISI_VIDEO_M2M_CAP,132 .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_BGR8P,133 .isi_out_format = CHNL_IMG_CTRL_FORMAT_BGR888P,134 .mem_planes = 1,135 .color_planes = 1,136 .depth = { 24 },137 .encoding = MXC_ISI_ENC_RGB,138 }, {139 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,140 .fourcc = V4L2_PIX_FMT_BGR24,141 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT142 | MXC_ISI_VIDEO_M2M_CAP,143 .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGB8P,144 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGB888P,145 .mem_planes = 1,146 .color_planes = 1,147 .depth = { 24 },148 .encoding = MXC_ISI_ENC_RGB,149 }, {150 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,151 .fourcc = V4L2_PIX_FMT_XBGR32,152 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT153 | MXC_ISI_VIDEO_M2M_CAP,154 .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,155 .isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888,156 .mem_planes = 1,157 .color_planes = 1,158 .depth = { 32 },159 .encoding = MXC_ISI_ENC_RGB,160 }, {161 .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,162 .fourcc = V4L2_PIX_FMT_ABGR32,163 .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,164 .isi_out_format = CHNL_IMG_CTRL_FORMAT_ARGB8888,165 .mem_planes = 1,166 .color_planes = 1,167 .depth = { 32 },168 .encoding = MXC_ISI_ENC_RGB,169 },170 /*171 * RAW formats172 *173 * The ISI shifts the 10-bit and 12-bit formats left by 6 and 4 bits174 * when using CHNL_IMG_CTRL_FORMAT_RAW10 or MXC_ISI_OUT_FMT_RAW12175 * respectively, to align the bits to the left and pad with zeros in176 * the LSBs. The corresponding V4L2 formats are however right-aligned,177 * we have to use CHNL_IMG_CTRL_FORMAT_RAW16 to avoid the left shift.178 */179 {180 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,181 .fourcc = V4L2_PIX_FMT_GREY,182 .type = MXC_ISI_VIDEO_CAP,183 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,184 .mem_planes = 1,185 .color_planes = 1,186 .depth = { 8 },187 .encoding = MXC_ISI_ENC_RAW,188 }, {189 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,190 .fourcc = V4L2_PIX_FMT_Y10,191 .type = MXC_ISI_VIDEO_CAP,192 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,193 .mem_planes = 1,194 .color_planes = 1,195 .depth = { 16 },196 .encoding = MXC_ISI_ENC_RAW,197 }, {198 .mbus_code = MEDIA_BUS_FMT_Y12_1X12,199 .fourcc = V4L2_PIX_FMT_Y12,200 .type = MXC_ISI_VIDEO_CAP,201 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,202 .mem_planes = 1,203 .color_planes = 1,204 .depth = { 16 },205 .encoding = MXC_ISI_ENC_RAW,206 }, {207 .mbus_code = MEDIA_BUS_FMT_Y14_1X14,208 .fourcc = V4L2_PIX_FMT_Y14,209 .type = MXC_ISI_VIDEO_CAP,210 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,211 .mem_planes = 1,212 .color_planes = 1,213 .depth = { 16 },214 .encoding = MXC_ISI_ENC_RAW,215 }, {216 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,217 .fourcc = V4L2_PIX_FMT_SBGGR8,218 .type = MXC_ISI_VIDEO_CAP,219 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,220 .mem_planes = 1,221 .color_planes = 1,222 .depth = { 8 },223 .encoding = MXC_ISI_ENC_RAW,224 }, {225 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,226 .fourcc = V4L2_PIX_FMT_SGBRG8,227 .type = MXC_ISI_VIDEO_CAP,228 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,229 .mem_planes = 1,230 .color_planes = 1,231 .depth = { 8 },232 .encoding = MXC_ISI_ENC_RAW,233 }, {234 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,235 .fourcc = V4L2_PIX_FMT_SGRBG8,236 .type = MXC_ISI_VIDEO_CAP,237 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,238 .mem_planes = 1,239 .color_planes = 1,240 .depth = { 8 },241 .encoding = MXC_ISI_ENC_RAW,242 }, {243 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,244 .fourcc = V4L2_PIX_FMT_SRGGB8,245 .type = MXC_ISI_VIDEO_CAP,246 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,247 .mem_planes = 1,248 .color_planes = 1,249 .depth = { 8 },250 .encoding = MXC_ISI_ENC_RAW,251 }, {252 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,253 .fourcc = V4L2_PIX_FMT_SBGGR10,254 .type = MXC_ISI_VIDEO_CAP,255 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,256 .mem_planes = 1,257 .color_planes = 1,258 .depth = { 16 },259 .encoding = MXC_ISI_ENC_RAW,260 }, {261 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,262 .fourcc = V4L2_PIX_FMT_SGBRG10,263 .type = MXC_ISI_VIDEO_CAP,264 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,265 .mem_planes = 1,266 .color_planes = 1,267 .depth = { 16 },268 .encoding = MXC_ISI_ENC_RAW,269 }, {270 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,271 .fourcc = V4L2_PIX_FMT_SGRBG10,272 .type = MXC_ISI_VIDEO_CAP,273 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,274 .mem_planes = 1,275 .color_planes = 1,276 .depth = { 16 },277 .encoding = MXC_ISI_ENC_RAW,278 }, {279 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,280 .fourcc = V4L2_PIX_FMT_SRGGB10,281 .type = MXC_ISI_VIDEO_CAP,282 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,283 .mem_planes = 1,284 .color_planes = 1,285 .depth = { 16 },286 .encoding = MXC_ISI_ENC_RAW,287 }, {288 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,289 .fourcc = V4L2_PIX_FMT_SBGGR12,290 .type = MXC_ISI_VIDEO_CAP,291 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,292 .mem_planes = 1,293 .color_planes = 1,294 .depth = { 16 },295 .encoding = MXC_ISI_ENC_RAW,296 }, {297 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,298 .fourcc = V4L2_PIX_FMT_SGBRG12,299 .type = MXC_ISI_VIDEO_CAP,300 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,301 .mem_planes = 1,302 .color_planes = 1,303 .depth = { 16 },304 .encoding = MXC_ISI_ENC_RAW,305 }, {306 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,307 .fourcc = V4L2_PIX_FMT_SGRBG12,308 .type = MXC_ISI_VIDEO_CAP,309 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,310 .mem_planes = 1,311 .color_planes = 1,312 .depth = { 16 },313 .encoding = MXC_ISI_ENC_RAW,314 }, {315 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,316 .fourcc = V4L2_PIX_FMT_SRGGB12,317 .type = MXC_ISI_VIDEO_CAP,318 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,319 .mem_planes = 1,320 .color_planes = 1,321 .depth = { 16 },322 .encoding = MXC_ISI_ENC_RAW,323 }, {324 .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14,325 .fourcc = V4L2_PIX_FMT_SBGGR14,326 .type = MXC_ISI_VIDEO_CAP,327 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,328 .mem_planes = 1,329 .color_planes = 1,330 .depth = { 16 },331 .encoding = MXC_ISI_ENC_RAW,332 }, {333 .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14,334 .fourcc = V4L2_PIX_FMT_SGBRG14,335 .type = MXC_ISI_VIDEO_CAP,336 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,337 .mem_planes = 1,338 .color_planes = 1,339 .depth = { 16 },340 .encoding = MXC_ISI_ENC_RAW,341 }, {342 .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14,343 .fourcc = V4L2_PIX_FMT_SGRBG14,344 .type = MXC_ISI_VIDEO_CAP,345 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,346 .mem_planes = 1,347 .color_planes = 1,348 .depth = { 16 },349 .encoding = MXC_ISI_ENC_RAW,350 }, {351 .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14,352 .fourcc = V4L2_PIX_FMT_SRGGB14,353 .type = MXC_ISI_VIDEO_CAP,354 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,355 .mem_planes = 1,356 .color_planes = 1,357 .depth = { 16 },358 .encoding = MXC_ISI_ENC_RAW,359 },360 /* JPEG */361 {362 .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,363 .fourcc = V4L2_PIX_FMT_MJPEG,364 .type = MXC_ISI_VIDEO_CAP,365 .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW8,366 .mem_planes = 1,367 .color_planes = 1,368 .depth = { 8 },369 .encoding = MXC_ISI_ENC_RAW,370 }371};372 373const struct mxc_isi_format_info *374mxc_isi_format_by_fourcc(u32 fourcc, enum mxc_isi_video_type type)375{376 unsigned int i;377 378 for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {379 const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];380 381 if (fmt->fourcc == fourcc && fmt->type & type)382 return fmt;383 }384 385 return NULL;386}387 388const struct mxc_isi_format_info *389mxc_isi_format_enum(unsigned int index, enum mxc_isi_video_type type)390{391 unsigned int i;392 393 for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {394 const struct mxc_isi_format_info *fmt = &mxc_isi_formats[i];395 396 if (!(fmt->type & type))397 continue;398 399 if (!index)400 return fmt;401 402 index--;403 }404 405 return NULL;406}407 408const struct mxc_isi_format_info *409mxc_isi_format_try(struct mxc_isi_pipe *pipe, struct v4l2_pix_format_mplane *pix,410 enum mxc_isi_video_type type)411{412 const struct mxc_isi_format_info *fmt;413 unsigned int max_width;414 unsigned int i;415 416 max_width = pipe->id == pipe->isi->pdata->num_channels - 1417 ? MXC_ISI_MAX_WIDTH_UNCHAINED418 : MXC_ISI_MAX_WIDTH_CHAINED;419 420 fmt = mxc_isi_format_by_fourcc(pix->pixelformat, type);421 if (!fmt)422 fmt = &mxc_isi_formats[0];423 424 pix->width = clamp(pix->width, MXC_ISI_MIN_WIDTH, max_width);425 pix->height = clamp(pix->height, MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT);426 pix->pixelformat = fmt->fourcc;427 pix->field = V4L2_FIELD_NONE;428 429 if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) {430 pix->colorspace = MXC_ISI_DEF_COLOR_SPACE;431 pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC;432 pix->quantization = MXC_ISI_DEF_QUANTIZATION;433 pix->xfer_func = MXC_ISI_DEF_XFER_FUNC;434 }435 436 if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)437 pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);438 if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) {439 bool is_rgb = fmt->encoding == MXC_ISI_ENC_RGB;440 441 pix->quantization =442 V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, pix->colorspace,443 pix->ycbcr_enc);444 }445 if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT)446 pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);447 448 pix->num_planes = fmt->mem_planes;449 450 for (i = 0; i < fmt->color_planes; ++i) {451 struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];452 unsigned int bpl;453 454 /* The pitch must be identical for all planes. */455 if (i == 0)456 bpl = clamp(plane->bytesperline,457 pix->width * fmt->depth[0] / 8,458 65535U);459 else460 bpl = pix->plane_fmt[0].bytesperline;461 462 plane->bytesperline = bpl;463 464 plane->sizeimage = plane->bytesperline * pix->height;465 if (i >= 1)466 plane->sizeimage /= fmt->vsub;467 }468 469 /*470 * For single-planar pixel formats with multiple color planes,471 * concatenate the size of all planes and clear all planes but the472 * first one.473 */474 if (fmt->color_planes != fmt->mem_planes) {475 for (i = 1; i < fmt->color_planes; ++i) {476 struct v4l2_plane_pix_format *plane = &pix->plane_fmt[i];477 478 pix->plane_fmt[0].sizeimage += plane->sizeimage;479 plane->bytesperline = 0;480 plane->sizeimage = 0;481 }482 }483 484 return fmt;485}486 487/* -----------------------------------------------------------------------------488 * videobuf2 queue operations489 */490 491static void mxc_isi_video_frame_write_done(struct mxc_isi_pipe *pipe,492 u32 status)493{494 struct mxc_isi_video *video = &pipe->video;495 struct device *dev = pipe->isi->dev;496 struct mxc_isi_buffer *next_buf;497 struct mxc_isi_buffer *buf;498 enum mxc_isi_buf_id buf_id;499 500 spin_lock(&video->buf_lock);501 502 /*503 * The ISI hardware handles buffers using a ping-pong mechanism with504 * two sets of destination addresses (with shadow registers to allow505 * programming addresses for all planes atomically) named BUF1 and506 * BUF2. Addresses can be loaded and copied to shadow registers at any507 * at any time.508 *509 * The hardware keeps track of which buffer is being written to and510 * automatically switches to the other buffer at frame end, copying the511 * corresponding address to another set of shadow registers that track512 * the address being written to. The active buffer tracking bits are513 * accessible through the CHNL_STS register.514 *515 * BUF1 BUF2 | Event | Action516 * | |517 * | | Program initial buffers518 * | | B0 in BUF1, B1 in BUF2519 * | Start ISI |520 * +----+ | |521 * | B0 | | |522 * +----+ | |523 * +----+ | FRM IRQ 0 | B0 complete, BUF2 now active524 * | B1 | | | Program B2 in BUF1525 * +----+ | |526 * +----+ | FRM IRQ 1 | B1 complete, BUF1 now active527 * | B2 | | | Program B3 in BUF2528 * +----+ | |529 * +----+ | FRM IRQ 2 | B2 complete, BUF2 now active530 * | B3 | | | Program B4 in BUF1531 * +----+ | |532 * +----+ | FRM IRQ 3 | B3 complete, BUF1 now active533 * | B4 | | | Program B5 in BUF2534 * +----+ | |535 * ... | |536 *537 * Races between address programming and buffer switching can be538 * detected by checking if a frame end interrupt occurred after539 * programming the addresses.540 *541 * As none of the shadow registers are accessible, races can occur542 * between address programming and buffer switching. It is possible to543 * detect the race condition by checking if a frame end interrupt544 * occurred after programming the addresses, but impossible to545 * determine if the race has been won or lost.546 *547 * In addition to this, we need to use discard buffers if no pending548 * buffers are available. To simplify handling of discard buffer, we549 * need to allocate three of them, as two can be active concurrently550 * and we need to still be able to get hold of a next buffer. The logic551 * could be improved to use two buffers only, but as all discard552 * buffers share the same memory, an additional buffer is cheap.553 */554 555 /* Check which buffer has just completed. */556 buf_id = pipe->isi->pdata->buf_active_reverse557 ? (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF2 : MXC_ISI_BUF1)558 : (status & CHNL_STS_BUF1_ACTIVE ? MXC_ISI_BUF1 : MXC_ISI_BUF2);559 560 buf = list_first_entry_or_null(&video->out_active,561 struct mxc_isi_buffer, list);562 563 /* Safety check, this should really never happen. */564 if (!buf) {565 dev_warn(dev, "trying to access empty active list\n");566 goto done;567 }568 569 /*570 * If the buffer that has completed doesn't match the buffer on the571 * front of the active list, it means we have lost one frame end572 * interrupt (or possibly a large odd number of interrupts, although573 * quite unlikely).574 *575 * For instance, if IRQ1 is lost and we handle IRQ2, both B1 and B2576 * have been completed, but B3 hasn't been programmed, BUF2 still577 * addresses B1 and the ISI is now writing in B1 instead of B3. We578 * can't complete B2 as that would result in out-of-order completion.579 *580 * The only option is to ignore this interrupt and try again. When IRQ3581 * will be handled, we will complete B1 and be in sync again.582 */583 if (buf->id != buf_id) {584 dev_dbg(dev, "buffer ID mismatch (expected %u, got %u), skipping\n",585 buf->id, buf_id);586 587 /*588 * Increment the frame count by two to account for the missed589 * and the ignored interrupts.590 */591 video->frame_count += 2;592 goto done;593 }594 595 /* Pick the next buffer and queue it to the hardware. */596 next_buf = list_first_entry_or_null(&video->out_pending,597 struct mxc_isi_buffer, list);598 if (!next_buf) {599 next_buf = list_first_entry_or_null(&video->out_discard,600 struct mxc_isi_buffer, list);601 602 /* Safety check, this should never happen. */603 if (!next_buf) {604 dev_warn(dev, "trying to access empty discard list\n");605 goto done;606 }607 }608 609 mxc_isi_channel_set_outbuf(pipe, next_buf->dma_addrs, buf_id);610 next_buf->id = buf_id;611 612 /*613 * Check if we have raced with the end of frame interrupt. If so, we614 * can't tell if the ISI has recorded the new address, or is still615 * using the previous buffer. We must assume the latter as that is the616 * worst case.617 *618 * For instance, if we are handling IRQ1 and now detect the FRM619 * interrupt, assume B2 has completed and the ISI has switched to BUF2620 * using B1 just before we programmed B3. Unlike in the previous race621 * condition, B3 has been programmed and will be written to the next622 * time the ISI switches to BUF2. We can however handle this exactly as623 * the first race condition, as we'll program B3 (still at the head of624 * the pending list) when handling IRQ3.625 */626 status = mxc_isi_channel_irq_status(pipe, false);627 if (status & CHNL_STS_FRM_STRD) {628 dev_dbg(dev, "raced with frame end interrupt\n");629 video->frame_count += 2;630 goto done;631 }632 633 /*634 * The next buffer has been queued successfully, move it to the active635 * list, and complete the current buffer.636 */637 list_move_tail(&next_buf->list, &video->out_active);638 639 if (!buf->discard) {640 list_del_init(&buf->list);641 buf->v4l2_buf.sequence = video->frame_count;642 buf->v4l2_buf.vb2_buf.timestamp = ktime_get_ns();643 vb2_buffer_done(&buf->v4l2_buf.vb2_buf, VB2_BUF_STATE_DONE);644 } else {645 list_move_tail(&buf->list, &video->out_discard);646 }647 648 video->frame_count++;649 650done:651 spin_unlock(&video->buf_lock);652}653 654static void mxc_isi_video_free_discard_buffers(struct mxc_isi_video *video)655{656 unsigned int i;657 658 for (i = 0; i < video->pix.num_planes; i++) {659 struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];660 661 if (!buf->addr)662 continue;663 664 dma_free_coherent(video->pipe->isi->dev, buf->size, buf->addr,665 buf->dma);666 buf->addr = NULL;667 }668}669 670static int mxc_isi_video_alloc_discard_buffers(struct mxc_isi_video *video)671{672 unsigned int i, j;673 674 /* Allocate memory for each plane. */675 for (i = 0; i < video->pix.num_planes; i++) {676 struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];677 678 buf->size = PAGE_ALIGN(video->pix.plane_fmt[i].sizeimage);679 buf->addr = dma_alloc_coherent(video->pipe->isi->dev, buf->size,680 &buf->dma, GFP_DMA | GFP_KERNEL);681 if (!buf->addr) {682 mxc_isi_video_free_discard_buffers(video);683 return -ENOMEM;684 }685 686 dev_dbg(video->pipe->isi->dev,687 "discard buffer plane %u: %zu bytes @%pad (CPU address %p)\n",688 i, buf->size, &buf->dma, buf->addr);689 }690 691 /* Fill the DMA addresses in the discard buffers. */692 for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {693 struct mxc_isi_buffer *buf = &video->buf_discard[i];694 695 buf->discard = true;696 697 for (j = 0; j < video->pix.num_planes; ++j)698 buf->dma_addrs[j] = video->discard_buffer[j].dma;699 }700 701 return 0;702}703 704static int mxc_isi_video_validate_format(struct mxc_isi_video *video)705{706 const struct v4l2_mbus_framefmt *format;707 const struct mxc_isi_format_info *info;708 struct v4l2_subdev_state *state;709 struct v4l2_subdev *sd = &video->pipe->sd;710 int ret = 0;711 712 state = v4l2_subdev_lock_and_get_active_state(sd);713 714 info = mxc_isi_format_by_fourcc(video->pix.pixelformat,715 MXC_ISI_VIDEO_CAP);716 format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);717 718 if (format->code != info->mbus_code ||719 format->width != video->pix.width ||720 format->height != video->pix.height) {721 dev_dbg(video->pipe->isi->dev,722 "%s: configuration mismatch, 0x%04x/%ux%u != 0x%04x/%ux%u\n",723 __func__, format->code, format->width, format->height,724 info->mbus_code, video->pix.width, video->pix.height);725 ret = -EINVAL;726 }727 728 v4l2_subdev_unlock_state(state);729 730 return ret;731}732 733static void mxc_isi_video_return_buffers(struct mxc_isi_video *video,734 enum vb2_buffer_state state)735{736 struct mxc_isi_buffer *buf;737 738 spin_lock_irq(&video->buf_lock);739 740 while (!list_empty(&video->out_active)) {741 buf = list_first_entry(&video->out_active,742 struct mxc_isi_buffer, list);743 list_del_init(&buf->list);744 if (buf->discard)745 continue;746 747 vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);748 }749 750 while (!list_empty(&video->out_pending)) {751 buf = list_first_entry(&video->out_pending,752 struct mxc_isi_buffer, list);753 list_del_init(&buf->list);754 vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);755 }756 757 while (!list_empty(&video->out_discard)) {758 buf = list_first_entry(&video->out_discard,759 struct mxc_isi_buffer, list);760 list_del_init(&buf->list);761 }762 763 INIT_LIST_HEAD(&video->out_active);764 INIT_LIST_HEAD(&video->out_pending);765 INIT_LIST_HEAD(&video->out_discard);766 767 spin_unlock_irq(&video->buf_lock);768}769 770static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video)771{772 unsigned int discard;773 unsigned int i;774 775 lockdep_assert_held(&video->buf_lock);776 777 /*778 * Queue two ISI channel output buffers. We are not guaranteed to have779 * any buffer in the pending list when this function is called from the780 * system resume handler. Use pending buffers as much as possible, and781 * use discard buffers to fill the remaining slots.782 */783 784 /* How many discard buffers do we need to queue first ? */785 discard = list_empty(&video->out_pending) ? 2786 : list_is_singular(&video->out_pending) ? 1787 : 0;788 789 for (i = 0; i < 2; ++i) {790 enum mxc_isi_buf_id buf_id = i == 0 ? MXC_ISI_BUF1791 : MXC_ISI_BUF2;792 struct mxc_isi_buffer *buf;793 struct list_head *list;794 795 list = i < discard ? &video->out_discard : &video->out_pending;796 buf = list_first_entry(list, struct mxc_isi_buffer, list);797 798 mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id);799 buf->id = buf_id;800 list_move_tail(&buf->list, &video->out_active);801 }802}803 804static inline struct mxc_isi_buffer *to_isi_buffer(struct vb2_v4l2_buffer *v4l2_buf)805{806 return container_of(v4l2_buf, struct mxc_isi_buffer, v4l2_buf);807}808 809int mxc_isi_video_queue_setup(const struct v4l2_pix_format_mplane *format,810 const struct mxc_isi_format_info *info,811 unsigned int *num_buffers,812 unsigned int *num_planes, unsigned int sizes[])813{814 unsigned int i;815 816 if (*num_planes) {817 if (*num_planes != info->mem_planes)818 return -EINVAL;819 820 for (i = 0; i < info->mem_planes; ++i) {821 if (sizes[i] < format->plane_fmt[i].sizeimage)822 return -EINVAL;823 }824 825 return 0;826 }827 828 *num_planes = info->mem_planes;829 830 for (i = 0; i < info->mem_planes; ++i)831 sizes[i] = format->plane_fmt[i].sizeimage;832 833 return 0;834}835 836void mxc_isi_video_buffer_init(struct vb2_buffer *vb2, dma_addr_t dma_addrs[3],837 const struct mxc_isi_format_info *info,838 const struct v4l2_pix_format_mplane *pix)839{840 unsigned int i;841 842 for (i = 0; i < info->mem_planes; ++i)843 dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb2, i);844 845 /*846 * For single-planar pixel formats with multiple color planes, split847 * the buffer into color planes.848 */849 if (info->color_planes != info->mem_planes) {850 unsigned int size = pix->plane_fmt[0].bytesperline * pix->height;851 852 for (i = 1; i < info->color_planes; ++i) {853 unsigned int vsub = i > 1 ? info->vsub : 1;854 855 dma_addrs[i] = dma_addrs[i - 1] + size / vsub;856 }857 }858}859 860int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2,861 const struct mxc_isi_format_info *info,862 const struct v4l2_pix_format_mplane *pix)863{864 unsigned int i;865 866 for (i = 0; i < info->mem_planes; i++) {867 unsigned long size = pix->plane_fmt[i].sizeimage;868 869 if (vb2_plane_size(vb2, i) < size) {870 dev_err(isi->dev, "User buffer too small (%ld < %ld)\n",871 vb2_plane_size(vb2, i), size);872 return -EINVAL;873 }874 875 vb2_set_plane_payload(vb2, i, size);876 }877 878 return 0;879}880 881static int mxc_isi_vb2_queue_setup(struct vb2_queue *q,882 unsigned int *num_buffers,883 unsigned int *num_planes,884 unsigned int sizes[],885 struct device *alloc_devs[])886{887 struct mxc_isi_video *video = vb2_get_drv_priv(q);888 889 return mxc_isi_video_queue_setup(&video->pix, video->fmtinfo,890 num_buffers, num_planes, sizes);891}892 893static int mxc_isi_vb2_buffer_init(struct vb2_buffer *vb2)894{895 struct mxc_isi_buffer *buf = to_isi_buffer(to_vb2_v4l2_buffer(vb2));896 struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);897 898 mxc_isi_video_buffer_init(vb2, buf->dma_addrs, video->fmtinfo,899 &video->pix);900 901 return 0;902}903 904static int mxc_isi_vb2_buffer_prepare(struct vb2_buffer *vb2)905{906 struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);907 908 return mxc_isi_video_buffer_prepare(video->pipe->isi, vb2,909 video->fmtinfo, &video->pix);910}911 912static void mxc_isi_vb2_buffer_queue(struct vb2_buffer *vb2)913{914 struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2);915 struct mxc_isi_buffer *buf = to_isi_buffer(v4l2_buf);916 struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);917 918 spin_lock_irq(&video->buf_lock);919 list_add_tail(&buf->list, &video->out_pending);920 spin_unlock_irq(&video->buf_lock);921}922 923static void mxc_isi_video_init_channel(struct mxc_isi_video *video)924{925 struct mxc_isi_pipe *pipe = video->pipe;926 927 mxc_isi_channel_get(pipe);928 929 mutex_lock(video->ctrls.handler.lock);930 mxc_isi_channel_set_alpha(pipe, video->ctrls.alpha);931 mxc_isi_channel_set_flip(pipe, video->ctrls.hflip, video->ctrls.vflip);932 mutex_unlock(video->ctrls.handler.lock);933 934 mxc_isi_channel_set_output_format(pipe, video->fmtinfo, &video->pix);935}936 937static int mxc_isi_vb2_start_streaming(struct vb2_queue *q, unsigned int count)938{939 struct mxc_isi_video *video = vb2_get_drv_priv(q);940 unsigned int i;941 int ret;942 943 /* Initialize the ISI channel. */944 mxc_isi_video_init_channel(video);945 946 spin_lock_irq(&video->buf_lock);947 948 /* Add the discard buffers to the out_discard list. */949 for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {950 struct mxc_isi_buffer *buf = &video->buf_discard[i];951 952 list_add_tail(&buf->list, &video->out_discard);953 }954 955 /* Queue the first buffers. */956 mxc_isi_video_queue_first_buffers(video);957 958 /* Clear frame count */959 video->frame_count = 0;960 961 spin_unlock_irq(&video->buf_lock);962 963 ret = mxc_isi_pipe_enable(video->pipe);964 if (ret)965 goto error;966 967 return 0;968 969error:970 mxc_isi_channel_put(video->pipe);971 mxc_isi_video_return_buffers(video, VB2_BUF_STATE_QUEUED);972 return ret;973}974 975static void mxc_isi_vb2_stop_streaming(struct vb2_queue *q)976{977 struct mxc_isi_video *video = vb2_get_drv_priv(q);978 979 mxc_isi_pipe_disable(video->pipe);980 mxc_isi_channel_put(video->pipe);981 982 mxc_isi_video_return_buffers(video, VB2_BUF_STATE_ERROR);983}984 985static const struct vb2_ops mxc_isi_vb2_qops = {986 .queue_setup = mxc_isi_vb2_queue_setup,987 .buf_init = mxc_isi_vb2_buffer_init,988 .buf_prepare = mxc_isi_vb2_buffer_prepare,989 .buf_queue = mxc_isi_vb2_buffer_queue,990 .wait_prepare = vb2_ops_wait_prepare,991 .wait_finish = vb2_ops_wait_finish,992 .start_streaming = mxc_isi_vb2_start_streaming,993 .stop_streaming = mxc_isi_vb2_stop_streaming,994};995 996/* -----------------------------------------------------------------------------997 * V4L2 controls998 */999 1000static inline struct mxc_isi_video *ctrl_to_isi_video(struct v4l2_ctrl *ctrl)1001{1002 return container_of(ctrl->handler, struct mxc_isi_video, ctrls.handler);1003}1004 1005static int mxc_isi_video_s_ctrl(struct v4l2_ctrl *ctrl)1006{1007 struct mxc_isi_video *video = ctrl_to_isi_video(ctrl);1008 1009 switch (ctrl->id) {1010 case V4L2_CID_ALPHA_COMPONENT:1011 video->ctrls.alpha = ctrl->val;1012 break;1013 case V4L2_CID_VFLIP:1014 video->ctrls.vflip = ctrl->val;1015 break;1016 case V4L2_CID_HFLIP:1017 video->ctrls.hflip = ctrl->val;1018 break;1019 }1020 1021 return 0;1022}1023 1024static const struct v4l2_ctrl_ops mxc_isi_video_ctrl_ops = {1025 .s_ctrl = mxc_isi_video_s_ctrl,1026};1027 1028static int mxc_isi_video_ctrls_create(struct mxc_isi_video *video)1029{1030 struct v4l2_ctrl_handler *handler = &video->ctrls.handler;1031 int ret;1032 1033 v4l2_ctrl_handler_init(handler, 3);1034 1035 v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1036 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);1037 1038 v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1039 V4L2_CID_VFLIP, 0, 1, 1, 0);1040 1041 v4l2_ctrl_new_std(handler, &mxc_isi_video_ctrl_ops,1042 V4L2_CID_HFLIP, 0, 1, 1, 0);1043 1044 if (handler->error) {1045 ret = handler->error;1046 v4l2_ctrl_handler_free(handler);1047 return ret;1048 }1049 1050 video->vdev.ctrl_handler = handler;1051 1052 return 0;1053}1054 1055static void mxc_isi_video_ctrls_delete(struct mxc_isi_video *video)1056{1057 v4l2_ctrl_handler_free(&video->ctrls.handler);1058}1059 1060/* -----------------------------------------------------------------------------1061 * V4L2 ioctls1062 */1063 1064static int mxc_isi_video_querycap(struct file *file, void *priv,1065 struct v4l2_capability *cap)1066{1067 strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));1068 strscpy(cap->card, MXC_ISI_CAPTURE, sizeof(cap->card));1069 1070 return 0;1071}1072 1073static int mxc_isi_video_enum_fmt(struct file *file, void *priv,1074 struct v4l2_fmtdesc *f)1075{1076 const struct mxc_isi_format_info *fmt;1077 unsigned int index = f->index;1078 unsigned int i;1079 1080 if (f->mbus_code) {1081 /*1082 * If a media bus code is specified, only enumerate formats1083 * compatible with it.1084 */1085 for (i = 0; i < ARRAY_SIZE(mxc_isi_formats); i++) {1086 fmt = &mxc_isi_formats[i];1087 if (fmt->mbus_code != f->mbus_code)1088 continue;1089 1090 if (index == 0)1091 break;1092 1093 index--;1094 }1095 1096 if (i == ARRAY_SIZE(mxc_isi_formats))1097 return -EINVAL;1098 } else {1099 /* Otherwise, enumerate all formatS. */1100 if (f->index >= ARRAY_SIZE(mxc_isi_formats))1101 return -EINVAL;1102 1103 fmt = &mxc_isi_formats[f->index];1104 }1105 1106 f->pixelformat = fmt->fourcc;1107 f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC1108 | V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;1109 1110 return 0;1111}1112 1113static int mxc_isi_video_g_fmt(struct file *file, void *fh,1114 struct v4l2_format *f)1115{1116 struct mxc_isi_video *video = video_drvdata(file);1117 1118 f->fmt.pix_mp = video->pix;1119 1120 return 0;1121}1122 1123static int mxc_isi_video_try_fmt(struct file *file, void *fh,1124 struct v4l2_format *f)1125{1126 struct mxc_isi_video *video = video_drvdata(file);1127 1128 mxc_isi_format_try(video->pipe, &f->fmt.pix_mp, MXC_ISI_VIDEO_CAP);1129 return 0;1130}1131 1132static int mxc_isi_video_s_fmt(struct file *file, void *priv,1133 struct v4l2_format *f)1134{1135 struct mxc_isi_video *video = video_drvdata(file);1136 struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;1137 1138 if (vb2_is_busy(&video->vb2_q))1139 return -EBUSY;1140 1141 video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP);1142 video->pix = *pix;1143 1144 return 0;1145}1146 1147static int mxc_isi_video_streamon(struct file *file, void *priv,1148 enum v4l2_buf_type type)1149{1150 struct mxc_isi_video *video = video_drvdata(file);1151 struct media_device *mdev = &video->pipe->isi->media_dev;1152 struct media_pipeline *pipe;1153 int ret;1154 1155 if (vb2_queue_is_busy(&video->vb2_q, file))1156 return -EBUSY;1157 1158 /*1159 * Get a pipeline for the video node and start it. This must be done1160 * here and not in the queue .start_streaming() handler, so that1161 * pipeline start errors can be reported from VIDIOC_STREAMON and not1162 * delayed until subsequent VIDIOC_QBUF calls.1163 */1164 mutex_lock(&mdev->graph_mutex);1165 1166 ret = mxc_isi_pipe_acquire(video->pipe, &mxc_isi_video_frame_write_done);1167 if (ret) {1168 mutex_unlock(&mdev->graph_mutex);1169 return ret;1170 }1171 1172 pipe = media_entity_pipeline(&video->vdev.entity) ? : &video->pipe->pipe;1173 1174 ret = __video_device_pipeline_start(&video->vdev, pipe);1175 if (ret) {1176 mutex_unlock(&mdev->graph_mutex);1177 goto err_release;1178 }1179 1180 mutex_unlock(&mdev->graph_mutex);1181 1182 /* Verify that the video format matches the output of the subdev. */1183 ret = mxc_isi_video_validate_format(video);1184 if (ret)1185 goto err_stop;1186 1187 /* Allocate buffers for discard operation. */1188 ret = mxc_isi_video_alloc_discard_buffers(video);1189 if (ret)1190 goto err_stop;1191 1192 ret = vb2_streamon(&video->vb2_q, type);1193 if (ret)1194 goto err_free;1195 1196 video->is_streaming = true;1197 1198 return 0;1199 1200err_free:1201 mxc_isi_video_free_discard_buffers(video);1202err_stop:1203 video_device_pipeline_stop(&video->vdev);1204err_release:1205 mxc_isi_pipe_release(video->pipe);1206 return ret;1207}1208 1209static void mxc_isi_video_cleanup_streaming(struct mxc_isi_video *video)1210{1211 lockdep_assert_held(&video->lock);1212 1213 if (!video->is_streaming)1214 return;1215 1216 mxc_isi_video_free_discard_buffers(video);1217 video_device_pipeline_stop(&video->vdev);1218 mxc_isi_pipe_release(video->pipe);1219 1220 video->is_streaming = false;1221}1222 1223static int mxc_isi_video_streamoff(struct file *file, void *priv,1224 enum v4l2_buf_type type)1225{1226 struct mxc_isi_video *video = video_drvdata(file);1227 int ret;1228 1229 ret = vb2_ioctl_streamoff(file, priv, type);1230 if (ret)1231 return ret;1232 1233 mxc_isi_video_cleanup_streaming(video);1234 1235 return 0;1236}1237 1238static int mxc_isi_video_enum_framesizes(struct file *file, void *priv,1239 struct v4l2_frmsizeenum *fsize)1240{1241 struct mxc_isi_video *video = video_drvdata(file);1242 const struct mxc_isi_format_info *info;1243 unsigned int max_width;1244 unsigned int h_align;1245 unsigned int v_align;1246 1247 if (fsize->index)1248 return -EINVAL;1249 1250 info = mxc_isi_format_by_fourcc(fsize->pixel_format, MXC_ISI_VIDEO_CAP);1251 if (!info)1252 return -EINVAL;1253 1254 h_align = max_t(unsigned int, info->hsub, 1);1255 v_align = max_t(unsigned int, info->vsub, 1);1256 1257 max_width = video->pipe->id == video->pipe->isi->pdata->num_channels - 11258 ? MXC_ISI_MAX_WIDTH_UNCHAINED1259 : MXC_ISI_MAX_WIDTH_CHAINED;1260 1261 fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;1262 fsize->stepwise.min_width = ALIGN(MXC_ISI_MIN_WIDTH, h_align);1263 fsize->stepwise.min_height = ALIGN(MXC_ISI_MIN_HEIGHT, v_align);1264 fsize->stepwise.max_width = ALIGN_DOWN(max_width, h_align);1265 fsize->stepwise.max_height = ALIGN_DOWN(MXC_ISI_MAX_HEIGHT, v_align);1266 fsize->stepwise.step_width = h_align;1267 fsize->stepwise.step_height = v_align;1268 1269 /*1270 * The width can be further restricted due to line buffer sharing1271 * between pipelines when scaling, but we have no way to know here if1272 * the scaler will be used.1273 */1274 1275 return 0;1276}1277 1278static const struct v4l2_ioctl_ops mxc_isi_video_ioctl_ops = {1279 .vidioc_querycap = mxc_isi_video_querycap,1280 1281 .vidioc_enum_fmt_vid_cap = mxc_isi_video_enum_fmt,1282 .vidioc_try_fmt_vid_cap_mplane = mxc_isi_video_try_fmt,1283 .vidioc_s_fmt_vid_cap_mplane = mxc_isi_video_s_fmt,1284 .vidioc_g_fmt_vid_cap_mplane = mxc_isi_video_g_fmt,1285 1286 .vidioc_reqbufs = vb2_ioctl_reqbufs,1287 .vidioc_querybuf = vb2_ioctl_querybuf,1288 .vidioc_qbuf = vb2_ioctl_qbuf,1289 .vidioc_dqbuf = vb2_ioctl_dqbuf,1290 .vidioc_expbuf = vb2_ioctl_expbuf,1291 .vidioc_prepare_buf = vb2_ioctl_prepare_buf,1292 .vidioc_create_bufs = vb2_ioctl_create_bufs,1293 1294 .vidioc_streamon = mxc_isi_video_streamon,1295 .vidioc_streamoff = mxc_isi_video_streamoff,1296 1297 .vidioc_enum_framesizes = mxc_isi_video_enum_framesizes,1298 1299 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,1300 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,1301};1302 1303/* -----------------------------------------------------------------------------1304 * Video device file operations1305 */1306 1307static int mxc_isi_video_open(struct file *file)1308{1309 struct mxc_isi_video *video = video_drvdata(file);1310 int ret;1311 1312 ret = v4l2_fh_open(file);1313 if (ret)1314 return ret;1315 1316 ret = pm_runtime_resume_and_get(video->pipe->isi->dev);1317 if (ret) {1318 v4l2_fh_release(file);1319 return ret;1320 }1321 1322 return 0;1323}1324 1325static int mxc_isi_video_release(struct file *file)1326{1327 struct mxc_isi_video *video = video_drvdata(file);1328 int ret;1329 1330 ret = vb2_fop_release(file);1331 if (ret)1332 dev_err(video->pipe->isi->dev, "%s fail\n", __func__);1333 1334 mutex_lock(&video->lock);1335 mxc_isi_video_cleanup_streaming(video);1336 mutex_unlock(&video->lock);1337 1338 pm_runtime_put(video->pipe->isi->dev);1339 return ret;1340}1341 1342static const struct v4l2_file_operations mxc_isi_video_fops = {1343 .owner = THIS_MODULE,1344 .open = mxc_isi_video_open,1345 .release = mxc_isi_video_release,1346 .poll = vb2_fop_poll,1347 .unlocked_ioctl = video_ioctl2,1348 .mmap = vb2_fop_mmap,1349};1350 1351/* -----------------------------------------------------------------------------1352 * Suspend & resume1353 */1354 1355void mxc_isi_video_suspend(struct mxc_isi_pipe *pipe)1356{1357 struct mxc_isi_video *video = &pipe->video;1358 1359 if (!video->is_streaming)1360 return;1361 1362 mxc_isi_pipe_disable(pipe);1363 mxc_isi_channel_put(pipe);1364 1365 spin_lock_irq(&video->buf_lock);1366 1367 /*1368 * Move the active buffers back to the pending or discard list. We must1369 * iterate the active list backward and move the buffers to the head of1370 * the pending list to preserve the buffer queueing order.1371 */1372 while (!list_empty(&video->out_active)) {1373 struct mxc_isi_buffer *buf =1374 list_last_entry(&video->out_active,1375 struct mxc_isi_buffer, list);1376 1377 if (buf->discard)1378 list_move(&buf->list, &video->out_discard);1379 else1380 list_move(&buf->list, &video->out_pending);1381 }1382 1383 spin_unlock_irq(&video->buf_lock);1384}1385 1386int mxc_isi_video_resume(struct mxc_isi_pipe *pipe)1387{1388 struct mxc_isi_video *video = &pipe->video;1389 1390 if (!video->is_streaming)1391 return 0;1392 1393 mxc_isi_video_init_channel(video);1394 1395 spin_lock_irq(&video->buf_lock);1396 mxc_isi_video_queue_first_buffers(video);1397 spin_unlock_irq(&video->buf_lock);1398 1399 return mxc_isi_pipe_enable(pipe);1400}1401 1402/* -----------------------------------------------------------------------------1403 * Registration1404 */1405 1406int mxc_isi_video_register(struct mxc_isi_pipe *pipe,1407 struct v4l2_device *v4l2_dev)1408{1409 struct mxc_isi_video *video = &pipe->video;1410 struct v4l2_pix_format_mplane *pix = &video->pix;1411 struct video_device *vdev = &video->vdev;1412 struct vb2_queue *q = &video->vb2_q;1413 int ret = -ENOMEM;1414 1415 video->pipe = pipe;1416 1417 mutex_init(&video->lock);1418 spin_lock_init(&video->buf_lock);1419 1420 pix->width = MXC_ISI_DEF_WIDTH;1421 pix->height = MXC_ISI_DEF_HEIGHT;1422 pix->pixelformat = MXC_ISI_DEF_PIXEL_FORMAT;1423 pix->colorspace = MXC_ISI_DEF_COLOR_SPACE;1424 pix->ycbcr_enc = MXC_ISI_DEF_YCBCR_ENC;1425 pix->quantization = MXC_ISI_DEF_QUANTIZATION;1426 pix->xfer_func = MXC_ISI_DEF_XFER_FUNC;1427 video->fmtinfo = mxc_isi_format_try(video->pipe, pix, MXC_ISI_VIDEO_CAP);1428 1429 memset(vdev, 0, sizeof(*vdev));1430 snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.%d.capture", pipe->id);1431 1432 vdev->fops = &mxc_isi_video_fops;1433 vdev->ioctl_ops = &mxc_isi_video_ioctl_ops;1434 vdev->v4l2_dev = v4l2_dev;1435 vdev->minor = -1;1436 vdev->release = video_device_release_empty;1437 vdev->queue = q;1438 vdev->lock = &video->lock;1439 1440 vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE1441 | V4L2_CAP_IO_MC;1442 video_set_drvdata(vdev, video);1443 1444 INIT_LIST_HEAD(&video->out_pending);1445 INIT_LIST_HEAD(&video->out_active);1446 INIT_LIST_HEAD(&video->out_discard);1447 1448 memset(q, 0, sizeof(*q));1449 q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;1450 q->io_modes = VB2_MMAP | VB2_DMABUF;1451 q->drv_priv = video;1452 q->ops = &mxc_isi_vb2_qops;1453 q->mem_ops = &vb2_dma_contig_memops;1454 q->buf_struct_size = sizeof(struct mxc_isi_buffer);1455 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;1456 q->min_queued_buffers = 2;1457 q->lock = &video->lock;1458 q->dev = pipe->isi->dev;1459 1460 ret = vb2_queue_init(q);1461 if (ret)1462 goto err_free_ctx;1463 1464 video->pad.flags = MEDIA_PAD_FL_SINK;1465 vdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;1466 ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);1467 if (ret)1468 goto err_free_ctx;1469 1470 ret = mxc_isi_video_ctrls_create(video);1471 if (ret)1472 goto err_me_cleanup;1473 1474 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);1475 if (ret)1476 goto err_ctrl_free;1477 1478 ret = media_create_pad_link(&pipe->sd.entity,1479 MXC_ISI_PIPE_PAD_SOURCE,1480 &vdev->entity, 0,1481 MEDIA_LNK_FL_IMMUTABLE |1482 MEDIA_LNK_FL_ENABLED);1483 if (ret)1484 goto err_video_unreg;1485 1486 return 0;1487 1488err_video_unreg:1489 video_unregister_device(vdev);1490err_ctrl_free:1491 mxc_isi_video_ctrls_delete(video);1492err_me_cleanup:1493 media_entity_cleanup(&vdev->entity);1494err_free_ctx:1495 return ret;1496}1497 1498void mxc_isi_video_unregister(struct mxc_isi_pipe *pipe)1499{1500 struct mxc_isi_video *video = &pipe->video;1501 struct video_device *vdev = &video->vdev;1502 1503 mutex_lock(&video->lock);1504 1505 if (video_is_registered(vdev)) {1506 video_unregister_device(vdev);1507 mxc_isi_video_ctrls_delete(video);1508 media_entity_cleanup(&vdev->entity);1509 }1510 1511 mutex_unlock(&video->lock);1512}1513