1103 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)4 * Author: Yong Deng <yong.deng@magewell.com>5 * Copyright 2021-2022 Bootlin6 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>7 */8 9#include <linux/of.h>10#include <linux/regmap.h>11#include <media/v4l2-device.h>12#include <media/v4l2-event.h>13#include <media/v4l2-ioctl.h>14#include <media/v4l2-mc.h>15#include <media/videobuf2-dma-contig.h>16#include <media/videobuf2-v4l2.h>17 18#include "sun6i_csi.h"19#include "sun6i_csi_bridge.h"20#include "sun6i_csi_capture.h"21#include "sun6i_csi_reg.h"22 23/* Helpers */24 25void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev,26 unsigned int *width, unsigned int *height)27{28 if (width)29 *width = csi_dev->capture.format.fmt.pix.width;30 if (height)31 *height = csi_dev->capture.format.fmt.pix.height;32}33 34void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev,35 u32 *pixelformat, u32 *field)36{37 if (pixelformat)38 *pixelformat = csi_dev->capture.format.fmt.pix.pixelformat;39 40 if (field)41 *field = csi_dev->capture.format.fmt.pix.field;42}43 44/* Format */45 46static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = {47 /* Bayer */48 {49 .pixelformat = V4L2_PIX_FMT_SBGGR8,50 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,51 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,52 },53 {54 .pixelformat = V4L2_PIX_FMT_SGBRG8,55 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,56 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,57 },58 {59 .pixelformat = V4L2_PIX_FMT_SGRBG8,60 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,61 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,62 },63 {64 .pixelformat = V4L2_PIX_FMT_SRGGB8,65 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,66 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,67 },68 {69 .pixelformat = V4L2_PIX_FMT_SBGGR10,70 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,71 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,72 },73 {74 .pixelformat = V4L2_PIX_FMT_SGBRG10,75 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,76 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,77 },78 {79 .pixelformat = V4L2_PIX_FMT_SGRBG10,80 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,81 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,82 },83 {84 .pixelformat = V4L2_PIX_FMT_SRGGB10,85 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10,86 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10,87 },88 {89 .pixelformat = V4L2_PIX_FMT_SBGGR12,90 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,91 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,92 },93 {94 .pixelformat = V4L2_PIX_FMT_SGBRG12,95 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,96 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,97 },98 {99 .pixelformat = V4L2_PIX_FMT_SGRBG12,100 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,101 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,102 },103 {104 .pixelformat = V4L2_PIX_FMT_SRGGB12,105 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12,106 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12,107 },108 /* RGB */109 {110 .pixelformat = V4L2_PIX_FMT_RGB565,111 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,112 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,113 },114 {115 .pixelformat = V4L2_PIX_FMT_RGB565X,116 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565,117 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565,118 },119 /* YUV422 */120 {121 .pixelformat = V4L2_PIX_FMT_YUYV,122 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,123 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,124 .input_format_raw = true,125 .hsize_len_factor = 2,126 },127 {128 .pixelformat = V4L2_PIX_FMT_YVYU,129 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,130 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,131 .input_format_raw = true,132 .hsize_len_factor = 2,133 },134 {135 .pixelformat = V4L2_PIX_FMT_UYVY,136 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,137 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,138 .input_format_raw = true,139 .hsize_len_factor = 2,140 },141 {142 .pixelformat = V4L2_PIX_FMT_VYUY,143 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,144 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,145 .input_format_raw = true,146 .hsize_len_factor = 2,147 },148 {149 .pixelformat = V4L2_PIX_FMT_NV16,150 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,151 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,152 },153 {154 .pixelformat = V4L2_PIX_FMT_NV61,155 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP,156 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP,157 .input_yuv_seq_invert = true,158 },159 {160 .pixelformat = V4L2_PIX_FMT_YUV422P,161 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P,162 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P,163 },164 /* YUV420 */165 {166 .pixelformat = V4L2_PIX_FMT_NV12_16L16,167 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB,168 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB,169 },170 {171 .pixelformat = V4L2_PIX_FMT_NV12,172 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,173 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,174 },175 {176 .pixelformat = V4L2_PIX_FMT_NV21,177 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP,178 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP,179 .input_yuv_seq_invert = true,180 },181 182 {183 .pixelformat = V4L2_PIX_FMT_YUV420,184 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,185 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,186 },187 {188 .pixelformat = V4L2_PIX_FMT_YVU420,189 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P,190 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P,191 .input_yuv_seq_invert = true,192 },193 /* Compressed */194 {195 .pixelformat = V4L2_PIX_FMT_JPEG,196 .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8,197 .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8,198 },199};200 201const202struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat)203{204 unsigned int i;205 206 for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++)207 if (sun6i_csi_capture_formats[i].pixelformat == pixelformat)208 return &sun6i_csi_capture_formats[i];209 210 return NULL;211}212 213/* RAW formats need an exact match between pixel and mbus formats. */214static const215struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = {216 /* YUV420 */217 {218 .pixelformat = V4L2_PIX_FMT_YUYV,219 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,220 },221 {222 .pixelformat = V4L2_PIX_FMT_YUYV,223 .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,224 },225 {226 .pixelformat = V4L2_PIX_FMT_YVYU,227 .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,228 },229 {230 .pixelformat = V4L2_PIX_FMT_YVYU,231 .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,232 },233 {234 .pixelformat = V4L2_PIX_FMT_UYVY,235 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,236 },237 {238 .pixelformat = V4L2_PIX_FMT_UYVY,239 .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,240 },241 {242 .pixelformat = V4L2_PIX_FMT_VYUY,243 .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,244 },245 {246 .pixelformat = V4L2_PIX_FMT_VYUY,247 .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,248 },249 /* RGB */250 {251 .pixelformat = V4L2_PIX_FMT_RGB565,252 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,253 },254 {255 .pixelformat = V4L2_PIX_FMT_RGB565X,256 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE,257 },258 /* Bayer */259 {260 .pixelformat = V4L2_PIX_FMT_SBGGR8,261 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,262 },263 {264 .pixelformat = V4L2_PIX_FMT_SGBRG8,265 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,266 },267 {268 .pixelformat = V4L2_PIX_FMT_SGRBG8,269 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,270 },271 {272 .pixelformat = V4L2_PIX_FMT_SRGGB8,273 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,274 },275 {276 .pixelformat = V4L2_PIX_FMT_SBGGR10,277 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,278 },279 {280 .pixelformat = V4L2_PIX_FMT_SGBRG10,281 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,282 },283 {284 .pixelformat = V4L2_PIX_FMT_SGRBG10,285 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,286 },287 {288 .pixelformat = V4L2_PIX_FMT_SRGGB10,289 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,290 },291 {292 .pixelformat = V4L2_PIX_FMT_SBGGR12,293 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,294 },295 {296 .pixelformat = V4L2_PIX_FMT_SGBRG12,297 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,298 },299 {300 .pixelformat = V4L2_PIX_FMT_SGRBG12,301 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,302 },303 {304 .pixelformat = V4L2_PIX_FMT_SRGGB12,305 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,306 },307 /* Compressed */308 {309 .pixelformat = V4L2_PIX_FMT_JPEG,310 .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,311 },312};313 314static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code)315{316 unsigned int i;317 318 for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) {319 const struct sun6i_csi_capture_format_match *match =320 &sun6i_csi_capture_format_matches[i];321 322 if (match->pixelformat == pixelformat &&323 match->mbus_code == mbus_code)324 return true;325 }326 327 return false;328}329 330/* Capture */331 332static void333sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev,334 struct sun6i_csi_buffer *csi_buffer)335{336 struct regmap *regmap = csi_dev->regmap;337 const struct v4l2_format_info *info;338 struct vb2_buffer *vb2_buffer;339 unsigned int width, height;340 dma_addr_t address;341 u32 pixelformat;342 343 vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;344 address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0);345 346 regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG,347 SUN6I_CSI_ADDR_VALUE(address));348 349 sun6i_csi_capture_dimensions(csi_dev, &width, &height);350 sun6i_csi_capture_format(csi_dev, &pixelformat, NULL);351 352 info = v4l2_format_info(pixelformat);353 /* Unsupported formats are single-plane, so we can stop here. */354 if (!info)355 return;356 357 if (info->comp_planes > 1) {358 address += info->bpp[0] * width * height;359 360 regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG,361 SUN6I_CSI_ADDR_VALUE(address));362 }363 364 if (info->comp_planes > 2) {365 address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) *366 DIV_ROUND_UP(height, info->vdiv);367 368 regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG,369 SUN6I_CSI_ADDR_VALUE(address));370 }371}372 373void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev)374{375 struct regmap *regmap = csi_dev->regmap;376 const struct sun6i_csi_capture_format *format;377 const struct v4l2_format_info *info;378 u32 hsize_len, vsize_len;379 u32 luma_line, chroma_line = 0;380 u32 pixelformat, field;381 u32 width, height;382 383 sun6i_csi_capture_dimensions(csi_dev, &width, &height);384 sun6i_csi_capture_format(csi_dev, &pixelformat, &field);385 386 format = sun6i_csi_capture_format_find(pixelformat);387 if (WARN_ON(!format))388 return;389 390 hsize_len = width;391 vsize_len = height;392 393 /*394 * When using 8-bit raw input/output (for packed YUV), we need to adapt395 * the width to account for the difference in bpp when it's not 8-bit.396 */397 if (format->hsize_len_factor)398 hsize_len *= format->hsize_len_factor;399 400 regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG,401 SUN6I_CSI_CH_HSIZE_LEN(hsize_len) |402 SUN6I_CSI_CH_HSIZE_START(0));403 404 regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG,405 SUN6I_CSI_CH_VSIZE_LEN(vsize_len) |406 SUN6I_CSI_CH_VSIZE_START(0));407 408 switch (pixelformat) {409 case V4L2_PIX_FMT_RGB565X:410 luma_line = width * 2;411 break;412 case V4L2_PIX_FMT_NV12_16L16:413 luma_line = width;414 chroma_line = width;415 break;416 case V4L2_PIX_FMT_JPEG:417 luma_line = width;418 break;419 default:420 info = v4l2_format_info(pixelformat);421 if (WARN_ON(!info))422 return;423 424 luma_line = width * info->bpp[0];425 426 if (info->comp_planes > 1)427 chroma_line = width * info->bpp[1] / info->hdiv;428 break;429 }430 431 regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG,432 SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) |433 SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line));434}435 436/* State */437 438static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev,439 bool error)440{441 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;442 struct sun6i_csi_buffer **csi_buffer_states[] = {443 &state->pending, &state->current, &state->complete,444 };445 struct sun6i_csi_buffer *csi_buffer;446 struct vb2_buffer *vb2_buffer;447 unsigned long flags;448 unsigned int i;449 450 spin_lock_irqsave(&state->lock, flags);451 452 for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) {453 csi_buffer = *csi_buffer_states[i];454 if (!csi_buffer)455 continue;456 457 vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;458 vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :459 VB2_BUF_STATE_QUEUED);460 461 *csi_buffer_states[i] = NULL;462 }463 464 list_for_each_entry(csi_buffer, &state->queue, list) {465 vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf;466 vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR :467 VB2_BUF_STATE_QUEUED);468 }469 470 INIT_LIST_HEAD(&state->queue);471 472 spin_unlock_irqrestore(&state->lock, flags);473}474 475void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev)476{477 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;478 struct sun6i_csi_buffer *csi_buffer;479 unsigned long flags;480 481 spin_lock_irqsave(&state->lock, flags);482 483 if (list_empty(&state->queue))484 goto complete;485 486 if (state->pending)487 goto complete;488 489 csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer,490 list);491 492 sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer);493 494 list_del(&csi_buffer->list);495 496 state->pending = csi_buffer;497 498complete:499 spin_unlock_irqrestore(&state->lock, flags);500}501 502static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev)503{504 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;505 unsigned long flags;506 507 spin_lock_irqsave(&state->lock, flags);508 509 if (!state->pending)510 goto complete;511 512 state->complete = state->current;513 state->current = state->pending;514 state->pending = NULL;515 516 if (state->complete) {517 struct sun6i_csi_buffer *csi_buffer = state->complete;518 struct vb2_buffer *vb2_buffer =519 &csi_buffer->v4l2_buffer.vb2_buf;520 521 vb2_buffer->timestamp = ktime_get_ns();522 csi_buffer->v4l2_buffer.sequence = state->sequence;523 524 vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE);525 526 state->complete = NULL;527 }528 529complete:530 spin_unlock_irqrestore(&state->lock, flags);531}532 533void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev)534{535 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;536 unsigned long flags;537 538 spin_lock_irqsave(&state->lock, flags);539 state->sequence++;540 spin_unlock_irqrestore(&state->lock, flags);541}542 543void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev)544{545 sun6i_csi_capture_state_complete(csi_dev);546 sun6i_csi_capture_state_update(csi_dev);547}548 549/* Queue */550 551static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue,552 unsigned int *buffers_count,553 unsigned int *planes_count,554 unsigned int sizes[],555 struct device *alloc_devs[])556{557 struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);558 unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage;559 560 if (*planes_count)561 return sizes[0] < size ? -EINVAL : 0;562 563 *planes_count = 1;564 sizes[0] = size;565 566 return 0;567}568 569static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer)570{571 struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);572 struct sun6i_csi_capture *capture = &csi_dev->capture;573 struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;574 struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);575 unsigned long size = capture->format.fmt.pix.sizeimage;576 577 if (vb2_plane_size(buffer, 0) < size) {578 v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",579 vb2_plane_size(buffer, 0), size);580 return -EINVAL;581 }582 583 vb2_set_plane_payload(buffer, 0, size);584 585 v4l2_buffer->field = capture->format.fmt.pix.field;586 587 return 0;588}589 590static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer)591{592 struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);593 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;594 struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);595 struct sun6i_csi_buffer *csi_buffer =596 container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);597 unsigned long flags;598 599 spin_lock_irqsave(&state->lock, flags);600 list_add_tail(&csi_buffer->list, &state->queue);601 spin_unlock_irqrestore(&state->lock, flags);602}603 604static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue,605 unsigned int count)606{607 struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);608 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;609 struct video_device *video_dev = &csi_dev->capture.video_dev;610 struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;611 int ret;612 613 state->sequence = 0;614 615 ret = video_device_pipeline_alloc_start(video_dev);616 if (ret < 0)617 goto error_state;618 619 state->streaming = true;620 621 ret = v4l2_subdev_call(subdev, video, s_stream, 1);622 if (ret && ret != -ENOIOCTLCMD)623 goto error_streaming;624 625 return 0;626 627error_streaming:628 state->streaming = false;629 630 video_device_pipeline_stop(video_dev);631 632error_state:633 sun6i_csi_capture_state_cleanup(csi_dev, false);634 635 return ret;636}637 638static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue)639{640 struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);641 struct sun6i_csi_capture_state *state = &csi_dev->capture.state;642 struct video_device *video_dev = &csi_dev->capture.video_dev;643 struct v4l2_subdev *subdev = &csi_dev->bridge.subdev;644 645 v4l2_subdev_call(subdev, video, s_stream, 0);646 647 state->streaming = false;648 649 video_device_pipeline_stop(video_dev);650 651 sun6i_csi_capture_state_cleanup(csi_dev, true);652}653 654static const struct vb2_ops sun6i_csi_capture_queue_ops = {655 .queue_setup = sun6i_csi_capture_queue_setup,656 .buf_prepare = sun6i_csi_capture_buffer_prepare,657 .buf_queue = sun6i_csi_capture_buffer_queue,658 .start_streaming = sun6i_csi_capture_start_streaming,659 .stop_streaming = sun6i_csi_capture_stop_streaming,660 .wait_prepare = vb2_ops_wait_prepare,661 .wait_finish = vb2_ops_wait_finish,662};663 664/* V4L2 Device */665 666static void sun6i_csi_capture_format_prepare(struct v4l2_format *format)667{668 struct v4l2_pix_format *pix_format = &format->fmt.pix;669 const struct v4l2_format_info *info;670 unsigned int width, height;671 672 v4l_bound_align_image(&pix_format->width, SUN6I_CSI_CAPTURE_WIDTH_MIN,673 SUN6I_CSI_CAPTURE_WIDTH_MAX, 1,674 &pix_format->height, SUN6I_CSI_CAPTURE_HEIGHT_MIN,675 SUN6I_CSI_CAPTURE_HEIGHT_MAX, 1, 0);676 677 if (!sun6i_csi_capture_format_find(pix_format->pixelformat))678 pix_format->pixelformat =679 sun6i_csi_capture_formats[0].pixelformat;680 681 width = pix_format->width;682 height = pix_format->height;683 684 info = v4l2_format_info(pix_format->pixelformat);685 686 switch (pix_format->pixelformat) {687 case V4L2_PIX_FMT_NV12_16L16:688 pix_format->bytesperline = width * 12 / 8;689 pix_format->sizeimage = pix_format->bytesperline * height;690 break;691 case V4L2_PIX_FMT_JPEG:692 pix_format->bytesperline = width;693 pix_format->sizeimage = pix_format->bytesperline * height;694 break;695 default:696 v4l2_fill_pixfmt(pix_format, pix_format->pixelformat,697 width, height);698 break;699 }700 701 if (pix_format->field == V4L2_FIELD_ANY)702 pix_format->field = V4L2_FIELD_NONE;703 704 if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)705 pix_format->colorspace = V4L2_COLORSPACE_JPEG;706 else if (info && info->pixel_enc == V4L2_PIXEL_ENC_BAYER)707 pix_format->colorspace = V4L2_COLORSPACE_RAW;708 else709 pix_format->colorspace = V4L2_COLORSPACE_SRGB;710 711 pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;712 pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;713 pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;714}715 716static int sun6i_csi_capture_querycap(struct file *file, void *private,717 struct v4l2_capability *capability)718{719 struct sun6i_csi_device *csi_dev = video_drvdata(file);720 struct video_device *video_dev = &csi_dev->capture.video_dev;721 722 strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));723 strscpy(capability->card, video_dev->name, sizeof(capability->card));724 snprintf(capability->bus_info, sizeof(capability->bus_info),725 "platform:%s", dev_name(csi_dev->dev));726 727 return 0;728}729 730static int sun6i_csi_capture_enum_fmt(struct file *file, void *private,731 struct v4l2_fmtdesc *fmtdesc)732{733 u32 index = fmtdesc->index;734 735 if (index >= ARRAY_SIZE(sun6i_csi_capture_formats))736 return -EINVAL;737 738 fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat;739 740 return 0;741}742 743static int sun6i_csi_capture_g_fmt(struct file *file, void *private,744 struct v4l2_format *format)745{746 struct sun6i_csi_device *csi_dev = video_drvdata(file);747 748 *format = csi_dev->capture.format;749 750 return 0;751}752 753static int sun6i_csi_capture_s_fmt(struct file *file, void *private,754 struct v4l2_format *format)755{756 struct sun6i_csi_device *csi_dev = video_drvdata(file);757 struct sun6i_csi_capture *capture = &csi_dev->capture;758 759 if (vb2_is_busy(&capture->queue))760 return -EBUSY;761 762 sun6i_csi_capture_format_prepare(format);763 764 csi_dev->capture.format = *format;765 766 return 0;767}768 769static int sun6i_csi_capture_try_fmt(struct file *file, void *private,770 struct v4l2_format *format)771{772 sun6i_csi_capture_format_prepare(format);773 774 return 0;775}776 777static int sun6i_csi_capture_enum_input(struct file *file, void *private,778 struct v4l2_input *input)779{780 if (input->index != 0)781 return -EINVAL;782 783 input->type = V4L2_INPUT_TYPE_CAMERA;784 strscpy(input->name, "Camera", sizeof(input->name));785 786 return 0;787}788 789static int sun6i_csi_capture_g_input(struct file *file, void *private,790 unsigned int *index)791{792 *index = 0;793 794 return 0;795}796 797static int sun6i_csi_capture_s_input(struct file *file, void *private,798 unsigned int index)799{800 if (index != 0)801 return -EINVAL;802 803 return 0;804}805 806static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = {807 .vidioc_querycap = sun6i_csi_capture_querycap,808 809 .vidioc_enum_fmt_vid_cap = sun6i_csi_capture_enum_fmt,810 .vidioc_g_fmt_vid_cap = sun6i_csi_capture_g_fmt,811 .vidioc_s_fmt_vid_cap = sun6i_csi_capture_s_fmt,812 .vidioc_try_fmt_vid_cap = sun6i_csi_capture_try_fmt,813 814 .vidioc_enum_input = sun6i_csi_capture_enum_input,815 .vidioc_g_input = sun6i_csi_capture_g_input,816 .vidioc_s_input = sun6i_csi_capture_s_input,817 818 .vidioc_create_bufs = vb2_ioctl_create_bufs,819 .vidioc_prepare_buf = vb2_ioctl_prepare_buf,820 .vidioc_reqbufs = vb2_ioctl_reqbufs,821 .vidioc_querybuf = vb2_ioctl_querybuf,822 .vidioc_expbuf = vb2_ioctl_expbuf,823 .vidioc_qbuf = vb2_ioctl_qbuf,824 .vidioc_dqbuf = vb2_ioctl_dqbuf,825 .vidioc_streamon = vb2_ioctl_streamon,826 .vidioc_streamoff = vb2_ioctl_streamoff,827};828 829/* V4L2 File */830 831static int sun6i_csi_capture_open(struct file *file)832{833 struct sun6i_csi_device *csi_dev = video_drvdata(file);834 struct sun6i_csi_capture *capture = &csi_dev->capture;835 int ret;836 837 if (mutex_lock_interruptible(&capture->lock))838 return -ERESTARTSYS;839 840 ret = v4l2_pipeline_pm_get(&capture->video_dev.entity);841 if (ret < 0)842 goto error_lock;843 844 ret = v4l2_fh_open(file);845 if (ret < 0)846 goto error_pipeline;847 848 mutex_unlock(&capture->lock);849 850 return 0;851 852error_pipeline:853 v4l2_pipeline_pm_put(&capture->video_dev.entity);854 855error_lock:856 mutex_unlock(&capture->lock);857 858 return ret;859}860 861static int sun6i_csi_capture_close(struct file *file)862{863 struct sun6i_csi_device *csi_dev = video_drvdata(file);864 struct sun6i_csi_capture *capture = &csi_dev->capture;865 866 mutex_lock(&capture->lock);867 868 _vb2_fop_release(file, NULL);869 v4l2_pipeline_pm_put(&capture->video_dev.entity);870 871 mutex_unlock(&capture->lock);872 873 return 0;874}875 876static const struct v4l2_file_operations sun6i_csi_capture_fops = {877 .owner = THIS_MODULE,878 .open = sun6i_csi_capture_open,879 .release = sun6i_csi_capture_close,880 .unlocked_ioctl = video_ioctl2,881 .mmap = vb2_fop_mmap,882 .poll = vb2_fop_poll883};884 885/* Media Entity */886 887static int sun6i_csi_capture_link_validate(struct media_link *link)888{889 struct video_device *video_dev =890 media_entity_to_video_device(link->sink->entity);891 struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);892 struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;893 const struct sun6i_csi_capture_format *capture_format;894 const struct sun6i_csi_bridge_format *bridge_format;895 unsigned int capture_width, capture_height;896 unsigned int bridge_width, bridge_height;897 const struct v4l2_format_info *format_info;898 u32 pixelformat, capture_field;899 u32 mbus_code, bridge_field;900 bool match;901 902 sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);903 904 sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field);905 capture_format = sun6i_csi_capture_format_find(pixelformat);906 if (WARN_ON(!capture_format))907 return -EINVAL;908 909 sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);910 911 sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);912 bridge_format = sun6i_csi_bridge_format_find(mbus_code);913 if (WARN_ON(!bridge_format))914 return -EINVAL;915 916 /* No cropping/scaling is supported. */917 if (capture_width != bridge_width || capture_height != bridge_height) {918 v4l2_err(v4l2_dev,919 "invalid input/output dimensions: %ux%u/%ux%u\n",920 bridge_width, bridge_height, capture_width,921 capture_height);922 return -EINVAL;923 }924 925 format_info = v4l2_format_info(pixelformat);926 /* Some formats are not listed. */927 if (!format_info)928 return 0;929 930 if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&931 bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)932 goto invalid;933 934 if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB &&935 bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW)936 goto invalid;937 938 if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {939 if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 &&940 bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422)941 goto invalid;942 943 /* YUV420 input can't produce YUV422 output. */944 if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 &&945 format_info->vdiv == 1)946 goto invalid;947 }948 949 /* With raw input mode, we need a 1:1 match between input and output. */950 if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||951 capture_format->input_format_raw) {952 match = sun6i_csi_capture_format_match(pixelformat, mbus_code);953 if (!match)954 goto invalid;955 }956 957 return 0;958 959invalid:960 v4l2_err(v4l2_dev, "invalid input/output format combination\n");961 return -EINVAL;962}963 964static const struct media_entity_operations sun6i_csi_capture_media_ops = {965 .link_validate = sun6i_csi_capture_link_validate966};967 968/* Capture */969 970int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev)971{972 struct sun6i_csi_capture *capture = &csi_dev->capture;973 struct sun6i_csi_capture_state *state = &capture->state;974 struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;975 struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev;976 struct video_device *video_dev = &capture->video_dev;977 struct vb2_queue *queue = &capture->queue;978 struct media_pad *pad = &capture->pad;979 struct v4l2_format *format = &csi_dev->capture.format;980 struct v4l2_pix_format *pix_format = &format->fmt.pix;981 int ret;982 983 /* This may happen with multiple bridge notifier bound calls. */984 if (state->setup)985 return 0;986 987 /* State */988 989 INIT_LIST_HEAD(&state->queue);990 spin_lock_init(&state->lock);991 992 /* Media Entity */993 994 video_dev->entity.ops = &sun6i_csi_capture_media_ops;995 996 /* Media Pad */997 998 pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;999 1000 ret = media_entity_pads_init(&video_dev->entity, 1, pad);1001 if (ret < 0)1002 return ret;1003 1004 /* Queue */1005 1006 mutex_init(&capture->lock);1007 1008 queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;1009 queue->io_modes = VB2_MMAP | VB2_DMABUF;1010 queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);1011 queue->ops = &sun6i_csi_capture_queue_ops;1012 queue->mem_ops = &vb2_dma_contig_memops;1013 queue->min_queued_buffers = 2;1014 queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;1015 queue->lock = &capture->lock;1016 queue->dev = csi_dev->dev;1017 queue->drv_priv = csi_dev;1018 1019 ret = vb2_queue_init(queue);1020 if (ret) {1021 v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);1022 goto error_media_entity;1023 }1024 1025 /* V4L2 Format */1026 1027 format->type = queue->type;1028 pix_format->pixelformat = sun6i_csi_capture_formats[0].pixelformat;1029 pix_format->width = 1280;1030 pix_format->height = 720;1031 pix_format->field = V4L2_FIELD_NONE;1032 1033 sun6i_csi_capture_format_prepare(format);1034 1035 /* Video Device */1036 1037 strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME,1038 sizeof(video_dev->name));1039 video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;1040 video_dev->vfl_dir = VFL_DIR_RX;1041 video_dev->release = video_device_release_empty;1042 video_dev->fops = &sun6i_csi_capture_fops;1043 video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops;1044 video_dev->v4l2_dev = v4l2_dev;1045 video_dev->queue = queue;1046 video_dev->lock = &capture->lock;1047 1048 video_set_drvdata(video_dev, csi_dev);1049 1050 ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);1051 if (ret < 0) {1052 v4l2_err(v4l2_dev, "failed to register video device: %d\n",1053 ret);1054 goto error_media_entity;1055 }1056 1057 /* Media Pad Link */1058 1059 ret = media_create_pad_link(&bridge_subdev->entity,1060 SUN6I_CSI_BRIDGE_PAD_SOURCE,1061 &video_dev->entity, 0,1062 csi_dev->isp_available ? 0 :1063 MEDIA_LNK_FL_ENABLED |1064 MEDIA_LNK_FL_IMMUTABLE);1065 if (ret < 0) {1066 v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n",1067 bridge_subdev->entity.name,1068 SUN6I_CSI_BRIDGE_PAD_SOURCE,1069 video_dev->entity.name, 0);1070 goto error_video_device;1071 }1072 1073 state->setup = true;1074 1075 return 0;1076 1077error_video_device:1078 vb2_video_unregister_device(video_dev);1079 1080error_media_entity:1081 media_entity_cleanup(&video_dev->entity);1082 1083 mutex_destroy(&capture->lock);1084 1085 return ret;1086}1087 1088void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev)1089{1090 struct sun6i_csi_capture *capture = &csi_dev->capture;1091 struct video_device *video_dev = &capture->video_dev;1092 1093 /* This may happen if async registration failed to complete. */1094 if (!capture->state.setup)1095 return;1096 1097 vb2_video_unregister_device(video_dev);1098 media_entity_cleanup(&video_dev->entity);1099 mutex_destroy(&capture->lock);1100 1101 capture->state.setup = false;1102}1103