1392 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/align.h>7#include <linux/bits.h>8#include <linux/bug.h>9#include <linux/completion.h>10#include <linux/container_of.h>11#include <linux/device.h>12#include <linux/list.h>13#include <linux/math64.h>14#include <linux/minmax.h>15#include <linux/module.h>16#include <linux/mutex.h>17#include <linux/pm_runtime.h>18#include <linux/spinlock.h>19#include <linux/string.h>20 21#include <media/media-entity.h>22#include <media/v4l2-ctrls.h>23#include <media/v4l2-dev.h>24#include <media/v4l2-fh.h>25#include <media/v4l2-ioctl.h>26#include <media/v4l2-subdev.h>27#include <media/videobuf2-v4l2.h>28 29#include "ipu6.h"30#include "ipu6-bus.h"31#include "ipu6-cpd.h"32#include "ipu6-fw-isys.h"33#include "ipu6-isys.h"34#include "ipu6-isys-csi2.h"35#include "ipu6-isys-queue.h"36#include "ipu6-isys-video.h"37#include "ipu6-platform-regs.h"38 39const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {40 { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,41 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },42 { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,43 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },44 { V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,45 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },46 { V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,47 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },48 { V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,49 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },50 { V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,51 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },52 { V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,53 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },54 { V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,55 IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },56 { V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,57 IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },58 { V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,59 IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },60 { V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,61 IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },62 { V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,63 IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },64 { V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,65 IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },66 { V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,67 IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },68 { V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,69 IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },70 { V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,71 IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },72 { V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,73 IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },74 { V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,75 IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },76 { V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,77 IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },78 { V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,79 IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },80 { V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,81 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},82 { V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,83 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},84 { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,85 IPU6_FW_ISYS_FRAME_FORMAT_RGB565 },86 { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,87 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 },88 { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8,89 IPU6_FW_ISYS_FRAME_FORMAT_RAW8, true },90 { V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10,91 IPU6_FW_ISYS_FRAME_FORMAT_RAW10, true },92 { V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12,93 IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true },94 { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16,95 IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true },96};97 98static int video_open(struct file *file)99{100 struct ipu6_isys_video *av = video_drvdata(file);101 struct ipu6_isys *isys = av->isys;102 struct ipu6_bus_device *adev = isys->adev;103 104 mutex_lock(&isys->mutex);105 if (isys->need_reset) {106 mutex_unlock(&isys->mutex);107 dev_warn(&adev->auxdev.dev, "isys power cycle required\n");108 return -EIO;109 }110 mutex_unlock(&isys->mutex);111 112 return v4l2_fh_open(file);113}114 115const struct ipu6_isys_pixelformat *116ipu6_isys_get_isys_format(u32 pixelformat, u32 type)117{118 const struct ipu6_isys_pixelformat *default_pfmt = NULL;119 unsigned int i;120 121 for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {122 const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];123 124 if (type && ((!pfmt->is_meta &&125 type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||126 (pfmt->is_meta &&127 type != V4L2_BUF_TYPE_META_CAPTURE)))128 continue;129 130 if (!default_pfmt)131 default_pfmt = pfmt;132 133 if (pfmt->pixelformat != pixelformat)134 continue;135 136 return pfmt;137 }138 139 return default_pfmt;140}141 142static int ipu6_isys_vidioc_querycap(struct file *file, void *fh,143 struct v4l2_capability *cap)144{145 struct ipu6_isys_video *av = video_drvdata(file);146 147 strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));148 strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));149 150 return 0;151}152 153static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,154 struct v4l2_fmtdesc *f)155{156 unsigned int i, num_found;157 158 for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {159 if ((ipu6_isys_pfmts[i].is_meta &&160 f->type != V4L2_BUF_TYPE_META_CAPTURE) ||161 (!ipu6_isys_pfmts[i].is_meta &&162 f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))163 continue;164 165 if (f->mbus_code && f->mbus_code != ipu6_isys_pfmts[i].code)166 continue;167 168 if (num_found < f->index) {169 num_found++;170 continue;171 }172 173 f->flags = 0;174 f->pixelformat = ipu6_isys_pfmts[i].pixelformat;175 176 return 0;177 }178 179 return -EINVAL;180}181 182static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,183 struct v4l2_frmsizeenum *fsize)184{185 unsigned int i;186 187 if (fsize->index > 0)188 return -EINVAL;189 190 for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {191 if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat)192 continue;193 194 fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;195 fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;196 fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;197 fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;198 fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;199 fsize->stepwise.step_width = 2;200 fsize->stepwise.step_height = 2;201 202 return 0;203 }204 205 return -EINVAL;206}207 208static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,209 struct v4l2_format *f)210{211 struct ipu6_isys_video *av = video_drvdata(file);212 213 f->fmt.pix = av->pix_fmt;214 215 return 0;216}217 218static int ipu6_isys_vidioc_g_fmt_meta_cap(struct file *file, void *fh,219 struct v4l2_format *f)220{221 struct ipu6_isys_video *av = video_drvdata(file);222 223 f->fmt.meta = av->meta_fmt;224 225 return 0;226}227 228static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type,229 u32 *format, u32 *width, u32 *height,230 u32 *bytesperline, u32 *sizeimage)231{232 const struct ipu6_isys_pixelformat *pfmt =233 ipu6_isys_get_isys_format(*format, type);234 235 *format = pfmt->pixelformat;236 *width = clamp(*width, IPU6_ISYS_MIN_WIDTH, IPU6_ISYS_MAX_WIDTH);237 *height = clamp(*height, IPU6_ISYS_MIN_HEIGHT, IPU6_ISYS_MAX_HEIGHT);238 239 if (pfmt->bpp != pfmt->bpp_packed)240 *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);241 else242 *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);243 244 *bytesperline = ALIGN(*bytesperline, av->isys->line_align);245 246 /*247 * (height + 1) * bytesperline due to a hardware issue: the DMA unit248 * is a power of two, and a line should be transferred as few units249 * as possible. The result is that up to line length more data than250 * the image size may be transferred to memory after the image.251 * Another limitation is the GDA allocation unit size. For low252 * resolution it gives a bigger number. Use larger one to avoid253 * memory corruption.254 */255 *sizeimage = *bytesperline * *height +256 max(*bytesperline,257 av->isys->pdata->ipdata->isys_dma_overshoot);258}259 260static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av,261 struct v4l2_format *f)262{263 ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,264 &f->fmt.pix.width, &f->fmt.pix.height,265 &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);266 267 f->fmt.pix.field = V4L2_FIELD_NONE;268 f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;269 f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;270 f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;271 f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;272}273 274static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,275 struct v4l2_format *f)276{277 struct ipu6_isys_video *av = video_drvdata(file);278 279 if (vb2_is_busy(&av->aq.vbq))280 return -EBUSY;281 282 __ipu6_isys_vidioc_try_fmt_vid_cap(av, f);283 284 return 0;285}286 287static int __ipu6_isys_vidioc_try_fmt_meta_cap(struct ipu6_isys_video *av,288 struct v4l2_format *f)289{290 ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.meta.dataformat,291 &f->fmt.meta.width, &f->fmt.meta.height,292 &f->fmt.meta.bytesperline,293 &f->fmt.meta.buffersize);294 295 return 0;296}297 298static int ipu6_isys_vidioc_try_fmt_meta_cap(struct file *file, void *fh,299 struct v4l2_format *f)300{301 struct ipu6_isys_video *av = video_drvdata(file);302 303 __ipu6_isys_vidioc_try_fmt_meta_cap(av, f);304 305 return 0;306}307 308static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,309 struct v4l2_format *f)310{311 struct ipu6_isys_video *av = video_drvdata(file);312 313 ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f);314 av->pix_fmt = f->fmt.pix;315 316 return 0;317}318 319static int ipu6_isys_vidioc_s_fmt_meta_cap(struct file *file, void *fh,320 struct v4l2_format *f)321{322 struct ipu6_isys_video *av = video_drvdata(file);323 324 if (vb2_is_busy(&av->aq.vbq))325 return -EBUSY;326 327 ipu6_isys_vidioc_try_fmt_meta_cap(file, fh, f);328 av->meta_fmt = f->fmt.meta;329 330 return 0;331}332 333static int ipu6_isys_vidioc_reqbufs(struct file *file, void *priv,334 struct v4l2_requestbuffers *p)335{336 struct ipu6_isys_video *av = video_drvdata(file);337 int ret;338 339 av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);340 av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);341 342 ret = vb2_queue_change_type(&av->aq.vbq, p->type);343 if (ret)344 return ret;345 346 return vb2_ioctl_reqbufs(file, priv, p);347}348 349static int ipu6_isys_vidioc_create_bufs(struct file *file, void *priv,350 struct v4l2_create_buffers *p)351{352 struct ipu6_isys_video *av = video_drvdata(file);353 int ret;354 355 av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);356 av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);357 358 ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);359 if (ret)360 return ret;361 362 return vb2_ioctl_create_bufs(file, priv, p);363}364 365static int link_validate(struct media_link *link)366{367 struct ipu6_isys_video *av =368 container_of(link->sink, struct ipu6_isys_video, pad);369 struct device *dev = &av->isys->adev->auxdev.dev;370 struct v4l2_subdev_state *s_state;371 struct v4l2_subdev *s_sd;372 struct v4l2_mbus_framefmt *s_fmt;373 struct media_pad *s_pad;374 u32 s_stream, code;375 int ret = -EPIPE;376 377 if (!link->source->entity)378 return ret;379 380 s_sd = media_entity_to_v4l2_subdev(link->source->entity);381 s_state = v4l2_subdev_get_unlocked_active_state(s_sd);382 if (!s_state)383 return ret;384 385 dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",386 link->source->entity->name, link->source->index,387 link->sink->entity->name);388 389 s_pad = media_pad_remote_pad_first(&av->pad);390 s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);391 392 v4l2_subdev_lock_state(s_state);393 394 s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);395 if (!s_fmt) {396 dev_err(dev, "failed to get source pad format\n");397 goto unlock;398 }399 400 code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;401 402 if (s_fmt->width != ipu6_isys_get_frame_width(av) ||403 s_fmt->height != ipu6_isys_get_frame_height(av) ||404 s_fmt->code != code) {405 dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",406 s_fmt->width, s_fmt->height, s_fmt->code,407 ipu6_isys_get_frame_width(av),408 ipu6_isys_get_frame_height(av), code);409 goto unlock;410 }411 412 v4l2_subdev_unlock_state(s_state);413 414 return 0;415unlock:416 v4l2_subdev_unlock_state(s_state);417 418 return ret;419}420 421static void get_stream_opened(struct ipu6_isys_video *av)422{423 unsigned long flags;424 425 spin_lock_irqsave(&av->isys->streams_lock, flags);426 av->isys->stream_opened++;427 spin_unlock_irqrestore(&av->isys->streams_lock, flags);428}429 430static void put_stream_opened(struct ipu6_isys_video *av)431{432 unsigned long flags;433 434 spin_lock_irqsave(&av->isys->streams_lock, flags);435 av->isys->stream_opened--;436 spin_unlock_irqrestore(&av->isys->streams_lock, flags);437}438 439static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,440 struct ipu6_fw_isys_stream_cfg_data_abi *cfg)441{442 struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);443 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);444 struct ipu6_fw_isys_input_pin_info_abi *input_pin;445 struct ipu6_fw_isys_output_pin_info_abi *output_pin;446 struct ipu6_isys_stream *stream = av->stream;447 struct ipu6_isys_queue *aq = &av->aq;448 struct v4l2_mbus_framefmt fmt;449 const struct ipu6_isys_pixelformat *pfmt =450 ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);451 struct v4l2_rect v4l2_crop;452 struct ipu6_isys *isys = av->isys;453 struct device *dev = &isys->adev->auxdev.dev;454 int input_pins = cfg->nof_input_pins++;455 int output_pins;456 u32 src_stream;457 int ret;458 459 src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);460 ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,461 &fmt);462 if (ret < 0) {463 dev_err(dev, "can't get stream format (%d)\n", ret);464 return ret;465 }466 467 ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,468 &v4l2_crop);469 if (ret < 0) {470 dev_err(dev, "can't get stream crop (%d)\n", ret);471 return ret;472 }473 474 input_pin = &cfg->input_pins[input_pins];475 input_pin->input_res.width = fmt.width;476 input_pin->input_res.height = fmt.height;477 input_pin->dt = av->dt;478 input_pin->bits_per_pix = pfmt->bpp_packed;479 input_pin->mapped_dt = 0x40; /* invalid mipi data type */480 input_pin->mipi_decompression = 0;481 input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;482 input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ?483 IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :484 IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;485 input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;486 487 output_pins = cfg->nof_output_pins++;488 aq->fw_output = output_pins;489 stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready;490 stream->output_pins[output_pins].aq = aq;491 492 output_pin = &cfg->output_pins[output_pins];493 output_pin->input_pin_id = input_pins;494 output_pin->output_res.width = ipu6_isys_get_frame_width(av);495 output_pin->output_res.height = ipu6_isys_get_frame_height(av);496 497 output_pin->stride = ipu6_isys_get_bytes_per_line(av);498 if (pfmt->bpp != pfmt->bpp_packed)499 output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;500 else501 output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;502 output_pin->ft = pfmt->css_pixelformat;503 output_pin->send_irq = 1;504 memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));505 output_pin->s2m_pixel_soc_pixel_remapping =506 S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;507 output_pin->csi_be_soc_pixel_remapping =508 CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;509 510 output_pin->snoopable = true;511 output_pin->error_handling_enable = false;512 output_pin->sensor_type = isys->sensor_type++;513 if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)514 isys->sensor_type = isys->pdata->ipdata->sensor_type_start;515 516 return 0;517}518 519static int start_stream_firmware(struct ipu6_isys_video *av,520 struct ipu6_isys_buffer_list *bl)521{522 struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;523 struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;524 struct ipu6_isys_stream *stream = av->stream;525 struct device *dev = &av->isys->adev->auxdev.dev;526 struct isys_fw_msgs *msg = NULL;527 struct ipu6_isys_queue *aq;528 int ret, retout, tout;529 u16 send_type;530 531 msg = ipu6_get_fw_msg_buf(stream);532 if (!msg)533 return -ENOMEM;534 535 stream_cfg = &msg->fw_msg.stream;536 stream_cfg->src = stream->stream_source;537 stream_cfg->vc = stream->vc;538 stream_cfg->isl_use = 0;539 stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;540 541 list_for_each_entry(aq, &stream->queues, node) {542 struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);543 544 ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);545 if (ret < 0) {546 ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);547 return ret;548 }549 }550 551 ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg);552 553 stream->nr_output_pins = stream_cfg->nof_output_pins;554 555 reinit_completion(&stream->stream_open_completion);556 557 ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,558 stream_cfg, msg->dma_addr,559 sizeof(*stream_cfg),560 IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);561 if (ret < 0) {562 dev_err(dev, "can't open stream (%d)\n", ret);563 ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);564 return ret;565 }566 567 get_stream_opened(av);568 569 tout = wait_for_completion_timeout(&stream->stream_open_completion,570 IPU6_FW_CALL_TIMEOUT_JIFFIES);571 572 ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);573 574 if (!tout) {575 dev_err(dev, "stream open time out\n");576 ret = -ETIMEDOUT;577 goto out_put_stream_opened;578 }579 if (stream->error) {580 dev_err(dev, "stream open error: %d\n", stream->error);581 ret = -EIO;582 goto out_put_stream_opened;583 }584 dev_dbg(dev, "start stream: open complete\n");585 586 if (bl) {587 msg = ipu6_get_fw_msg_buf(stream);588 if (!msg) {589 ret = -ENOMEM;590 goto out_put_stream_opened;591 }592 buf = &msg->fw_msg.frame;593 ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);594 ipu6_isys_buffer_list_queue(bl,595 IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);596 }597 598 reinit_completion(&stream->stream_start_completion);599 600 if (bl) {601 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;602 ipu6_fw_isys_dump_frame_buff_set(dev, buf,603 stream_cfg->nof_output_pins);604 ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,605 buf, msg->dma_addr,606 sizeof(*buf), send_type);607 } else {608 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;609 ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,610 send_type);611 }612 613 if (ret < 0) {614 dev_err(dev, "can't start streaming (%d)\n", ret);615 goto out_stream_close;616 }617 618 tout = wait_for_completion_timeout(&stream->stream_start_completion,619 IPU6_FW_CALL_TIMEOUT_JIFFIES);620 if (!tout) {621 dev_err(dev, "stream start time out\n");622 ret = -ETIMEDOUT;623 goto out_stream_close;624 }625 if (stream->error) {626 dev_err(dev, "stream start error: %d\n", stream->error);627 ret = -EIO;628 goto out_stream_close;629 }630 dev_dbg(dev, "start stream: complete\n");631 632 return 0;633 634out_stream_close:635 reinit_completion(&stream->stream_close_completion);636 637 retout = ipu6_fw_isys_simple_cmd(av->isys,638 stream->stream_handle,639 IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);640 if (retout < 0) {641 dev_dbg(dev, "can't close stream (%d)\n", retout);642 goto out_put_stream_opened;643 }644 645 tout = wait_for_completion_timeout(&stream->stream_close_completion,646 IPU6_FW_CALL_TIMEOUT_JIFFIES);647 if (!tout)648 dev_err(dev, "stream close time out\n");649 else if (stream->error)650 dev_err(dev, "stream close error: %d\n", stream->error);651 else652 dev_dbg(dev, "stream close complete\n");653 654out_put_stream_opened:655 put_stream_opened(av);656 657 return ret;658}659 660static void stop_streaming_firmware(struct ipu6_isys_video *av)661{662 struct device *dev = &av->isys->adev->auxdev.dev;663 struct ipu6_isys_stream *stream = av->stream;664 int ret, tout;665 666 reinit_completion(&stream->stream_stop_completion);667 668 ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,669 IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);670 671 if (ret < 0) {672 dev_err(dev, "can't stop stream (%d)\n", ret);673 return;674 }675 676 tout = wait_for_completion_timeout(&stream->stream_stop_completion,677 IPU6_FW_CALL_TIMEOUT_JIFFIES);678 if (!tout)679 dev_warn(dev, "stream stop time out\n");680 else if (stream->error)681 dev_warn(dev, "stream stop error: %d\n", stream->error);682 else683 dev_dbg(dev, "stop stream: complete\n");684}685 686static void close_streaming_firmware(struct ipu6_isys_video *av)687{688 struct ipu6_isys_stream *stream = av->stream;689 struct device *dev = &av->isys->adev->auxdev.dev;690 int ret, tout;691 692 reinit_completion(&stream->stream_close_completion);693 694 ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,695 IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);696 if (ret < 0) {697 dev_err(dev, "can't close stream (%d)\n", ret);698 return;699 }700 701 tout = wait_for_completion_timeout(&stream->stream_close_completion,702 IPU6_FW_CALL_TIMEOUT_JIFFIES);703 if (!tout)704 dev_warn(dev, "stream close time out\n");705 else if (stream->error)706 dev_warn(dev, "stream close error: %d\n", stream->error);707 else708 dev_dbg(dev, "close stream: complete\n");709 710 put_stream_opened(av);711}712 713int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,714 struct media_entity *source_entity,715 int nr_queues)716{717 struct ipu6_isys_stream *stream = av->stream;718 struct ipu6_isys_csi2 *csi2;719 720 if (WARN_ON(stream->nr_streaming))721 return -EINVAL;722 723 stream->nr_queues = nr_queues;724 atomic_set(&stream->sequence, 0);725 726 stream->seq_index = 0;727 memset(stream->seq, 0, sizeof(stream->seq));728 729 if (WARN_ON(!list_empty(&stream->queues)))730 return -EINVAL;731 732 stream->stream_source = stream->asd->source;733 csi2 = ipu6_isys_subdev_to_csi2(stream->asd);734 csi2->receiver_errors = 0;735 stream->source_entity = source_entity;736 737 dev_dbg(&av->isys->adev->auxdev.dev,738 "prepare stream: external entity %s\n",739 stream->source_entity->name);740 741 return 0;742}743 744void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,745 bool state)746{747 struct ipu6_isys *isys = av->isys;748 struct ipu6_isys_csi2 *csi2 = NULL;749 struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;750 struct device *dev = &isys->adev->auxdev.dev;751 struct v4l2_mbus_framefmt format;752 struct v4l2_subdev *esd;753 struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };754 unsigned int bpp, lanes;755 s64 link_freq = 0;756 u64 pixel_rate = 0;757 int ret;758 759 if (!state)760 return;761 762 esd = media_entity_to_v4l2_subdev(av->stream->source_entity);763 764 av->watermark.width = ipu6_isys_get_frame_width(av);765 av->watermark.height = ipu6_isys_get_frame_height(av);766 av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;767 av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;768 769 ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);770 if (!ret && hb.value >= 0)771 av->watermark.hblank = hb.value;772 else773 av->watermark.hblank = 0;774 775 csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);776 link_freq = ipu6_isys_csi2_get_link_freq(csi2);777 if (link_freq > 0) {778 lanes = csi2->nlanes;779 ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,780 av->source_stream, &format);781 if (!ret) {782 bpp = ipu6_isys_mbus_code_to_bpp(format.code);783 pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);784 }785 }786 787 av->watermark.pixel_rate = pixel_rate;788 789 if (!pixel_rate) {790 mutex_lock(&iwake_watermark->mutex);791 iwake_watermark->force_iwake_disable = true;792 mutex_unlock(&iwake_watermark->mutex);793 dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",794 av->stream->source_entity->name);795 }796}797 798static void calculate_stream_datarate(struct ipu6_isys_video *av)799{800 struct video_stream_watermark *watermark = &av->watermark;801 const struct ipu6_isys_pixelformat *pfmt =802 ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);803 u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;804 u64 line_time_ns, stream_data_rate;805 u16 shift, size;806 807 shift = watermark->sram_gran_shift;808 size = watermark->sram_gran_size;809 810 pixels_per_line = watermark->width + watermark->hblank;811 line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC,812 watermark->pixel_rate);813 bytes_per_line = watermark->width * pfmt->bpp / 8;814 pages_per_line = DIV_ROUND_UP(bytes_per_line, size);815 pb_bytes_per_line = pages_per_line << shift;816 stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);817 818 watermark->stream_data_rate = stream_data_rate;819}820 821void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)822{823 struct isys_iwake_watermark *iwake_watermark =824 &av->isys->iwake_watermark;825 826 if (!av->watermark.pixel_rate)827 return;828 829 if (state) {830 calculate_stream_datarate(av);831 mutex_lock(&iwake_watermark->mutex);832 list_add(&av->watermark.stream_node,833 &iwake_watermark->video_list);834 mutex_unlock(&iwake_watermark->mutex);835 } else {836 av->watermark.stream_data_rate = 0;837 mutex_lock(&iwake_watermark->mutex);838 list_del(&av->watermark.stream_node);839 mutex_unlock(&iwake_watermark->mutex);840 }841 842 update_watermark_setting(av->isys);843}844 845void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)846{847 struct device *dev;848 unsigned int i;849 unsigned long flags;850 851 if (!stream) {852 pr_err("ipu6-isys: no available stream\n");853 return;854 }855 856 dev = &stream->isys->adev->auxdev.dev;857 858 spin_lock_irqsave(&stream->isys->streams_lock, flags);859 for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {860 if (&stream->isys->streams[i] == stream) {861 if (stream->isys->streams_ref_count[i] > 0)862 stream->isys->streams_ref_count[i]--;863 else864 dev_warn(dev, "invalid stream %d\n", i);865 866 break;867 }868 }869 spin_unlock_irqrestore(&stream->isys->streams_lock, flags);870}871 872static struct ipu6_isys_stream *873ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)874{875 struct ipu6_isys_stream *stream = NULL;876 struct ipu6_isys *isys = av->isys;877 unsigned long flags;878 unsigned int i;879 u8 vc = av->vc;880 881 if (!isys)882 return NULL;883 884 spin_lock_irqsave(&isys->streams_lock, flags);885 for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {886 if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&887 isys->streams[i].asd == asd) {888 isys->streams_ref_count[i]++;889 stream = &isys->streams[i];890 break;891 }892 }893 894 if (!stream) {895 for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {896 if (!isys->streams_ref_count[i]) {897 isys->streams_ref_count[i]++;898 stream = &isys->streams[i];899 stream->vc = vc;900 stream->asd = asd;901 break;902 }903 }904 }905 spin_unlock_irqrestore(&isys->streams_lock, flags);906 907 return stream;908}909 910struct ipu6_isys_stream *911ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)912{913 unsigned long flags;914 struct ipu6_isys_stream *stream = NULL;915 916 if (!isys)917 return NULL;918 919 if (stream_handle >= IPU6_ISYS_MAX_STREAMS) {920 dev_err(&isys->adev->auxdev.dev,921 "stream_handle %d is invalid\n", stream_handle);922 return NULL;923 }924 925 spin_lock_irqsave(&isys->streams_lock, flags);926 if (isys->streams_ref_count[stream_handle] > 0) {927 isys->streams_ref_count[stream_handle]++;928 stream = &isys->streams[stream_handle];929 }930 spin_unlock_irqrestore(&isys->streams_lock, flags);931 932 return stream;933}934 935struct ipu6_isys_stream *936ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)937{938 struct ipu6_isys_stream *stream = NULL;939 unsigned long flags;940 unsigned int i;941 942 if (!isys)943 return NULL;944 945 if (source < 0) {946 dev_err(&isys->adev->auxdev.dev,947 "query stream with invalid port number\n");948 return NULL;949 }950 951 spin_lock_irqsave(&isys->streams_lock, flags);952 for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {953 if (!isys->streams_ref_count[i])954 continue;955 956 if (isys->streams[i].stream_source == source &&957 isys->streams[i].vc == vc) {958 stream = &isys->streams[i];959 isys->streams_ref_count[i]++;960 break;961 }962 }963 spin_unlock_irqrestore(&isys->streams_lock, flags);964 965 return stream;966}967 968static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av)969{970 struct media_pipeline *pipeline =971 media_entity_pipeline(&__av->vdev.entity);972 unsigned int i;973 u64 stream_mask = 0;974 975 for (i = 0; i < NR_OF_CSI2_SRC_PADS; i++) {976 struct ipu6_isys_video *av = &__av->csi2->av[i];977 978 if (pipeline == media_entity_pipeline(&av->vdev.entity))979 stream_mask |= BIT_ULL(av->source_stream);980 }981 982 return stream_mask;983}984 985int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,986 struct ipu6_isys_buffer_list *bl)987{988 struct v4l2_subdev_krouting *routing;989 struct ipu6_isys_stream *stream = av->stream;990 struct v4l2_subdev_state *subdev_state;991 struct device *dev = &av->isys->adev->auxdev.dev;992 struct v4l2_subdev *sd;993 struct media_pad *r_pad;994 u32 sink_pad, sink_stream;995 u64 r_stream;996 u64 stream_mask = 0;997 int ret = 0;998 999 dev_dbg(dev, "set stream: %d\n", state);1000 1001 if (WARN(!stream->source_entity, "No source entity for stream\n"))1002 return -ENODEV;1003 1004 sd = &stream->asd->sd;1005 r_pad = media_pad_remote_pad_first(&av->pad);1006 r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);1007 1008 subdev_state = v4l2_subdev_lock_and_get_active_state(sd);1009 routing = &subdev_state->routing;1010 ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,1011 r_stream, &sink_pad,1012 &sink_stream);1013 v4l2_subdev_unlock_state(subdev_state);1014 if (ret)1015 return ret;1016 1017 stream_mask = get_stream_mask_by_pipeline(av);1018 if (!state) {1019 stop_streaming_firmware(av);1020 1021 /* stop sub-device which connects with video */1022 dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n",1023 sd->name, r_pad->index, stream_mask);1024 ret = v4l2_subdev_disable_streams(sd, r_pad->index,1025 stream_mask);1026 if (ret) {1027 dev_err(dev, "stream off %s failed with %d\n", sd->name,1028 ret);1029 return ret;1030 }1031 close_streaming_firmware(av);1032 } else {1033 ret = start_stream_firmware(av, bl);1034 if (ret) {1035 dev_err(dev, "start stream of firmware failed\n");1036 return ret;1037 }1038 1039 /* start sub-device which connects with video */1040 dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name,1041 r_pad->index, stream_mask);1042 ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask);1043 if (ret) {1044 dev_err(dev, "stream on %s failed with %d\n", sd->name,1045 ret);1046 goto out_media_entity_stop_streaming_firmware;1047 }1048 }1049 1050 av->streaming = state;1051 1052 return 0;1053 1054out_media_entity_stop_streaming_firmware:1055 stop_streaming_firmware(av);1056 1057 return ret;1058}1059 1060static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = {1061 .vidioc_querycap = ipu6_isys_vidioc_querycap,1062 .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,1063 .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,1064 .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,1065 .vidioc_g_fmt_vid_cap = ipu6_isys_vidioc_g_fmt_vid_cap,1066 .vidioc_s_fmt_vid_cap = ipu6_isys_vidioc_s_fmt_vid_cap,1067 .vidioc_try_fmt_vid_cap = ipu6_isys_vidioc_try_fmt_vid_cap,1068 .vidioc_g_fmt_meta_cap = ipu6_isys_vidioc_g_fmt_meta_cap,1069 .vidioc_s_fmt_meta_cap = ipu6_isys_vidioc_s_fmt_meta_cap,1070 .vidioc_try_fmt_meta_cap = ipu6_isys_vidioc_try_fmt_meta_cap,1071 .vidioc_reqbufs = ipu6_isys_vidioc_reqbufs,1072 .vidioc_create_bufs = ipu6_isys_vidioc_create_bufs,1073 .vidioc_prepare_buf = vb2_ioctl_prepare_buf,1074 .vidioc_querybuf = vb2_ioctl_querybuf,1075 .vidioc_qbuf = vb2_ioctl_qbuf,1076 .vidioc_dqbuf = vb2_ioctl_dqbuf,1077 .vidioc_streamon = vb2_ioctl_streamon,1078 .vidioc_streamoff = vb2_ioctl_streamoff,1079 .vidioc_expbuf = vb2_ioctl_expbuf,1080};1081 1082static const struct media_entity_operations entity_ops = {1083 .link_validate = link_validate,1084};1085 1086static const struct v4l2_file_operations isys_fops = {1087 .owner = THIS_MODULE,1088 .poll = vb2_fop_poll,1089 .unlocked_ioctl = video_ioctl2,1090 .mmap = vb2_fop_mmap,1091 .open = video_open,1092 .release = vb2_fop_release,1093};1094 1095int ipu6_isys_fw_open(struct ipu6_isys *isys)1096{1097 struct ipu6_bus_device *adev = isys->adev;1098 const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;1099 int ret;1100 1101 ret = pm_runtime_resume_and_get(&adev->auxdev.dev);1102 if (ret < 0)1103 return ret;1104 1105 mutex_lock(&isys->mutex);1106 1107 if (isys->ref_count++)1108 goto unlock;1109 1110 ipu6_configure_spc(adev->isp, &ipdata->hw_variant,1111 IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,1112 adev->pkg_dir, adev->pkg_dir_dma_addr);1113 1114 /*1115 * Buffers could have been left to wrong queue at last closure.1116 * Move them now back to empty buffer queue.1117 */1118 ipu6_cleanup_fw_msg_bufs(isys);1119 1120 if (isys->fwcom) {1121 /*1122 * Something went wrong in previous shutdown. As we are now1123 * restarting isys we can safely delete old context.1124 */1125 dev_warn(&adev->auxdev.dev, "clearing old context\n");1126 ipu6_fw_isys_cleanup(isys);1127 }1128 1129 ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams);1130 if (ret < 0)1131 goto out;1132 1133unlock:1134 mutex_unlock(&isys->mutex);1135 1136 return 0;1137 1138out:1139 isys->ref_count--;1140 mutex_unlock(&isys->mutex);1141 pm_runtime_put(&adev->auxdev.dev);1142 1143 return ret;1144}1145 1146void ipu6_isys_fw_close(struct ipu6_isys *isys)1147{1148 mutex_lock(&isys->mutex);1149 1150 isys->ref_count--;1151 if (!isys->ref_count) {1152 ipu6_fw_isys_close(isys);1153 if (isys->fwcom) {1154 isys->need_reset = true;1155 dev_warn(&isys->adev->auxdev.dev,1156 "failed to close fw isys\n");1157 }1158 }1159 1160 mutex_unlock(&isys->mutex);1161 1162 if (isys->need_reset)1163 pm_runtime_put_sync(&isys->adev->auxdev.dev);1164 else1165 pm_runtime_put(&isys->adev->auxdev.dev);1166}1167 1168int ipu6_isys_setup_video(struct ipu6_isys_video *av,1169 struct media_entity **source_entity, int *nr_queues)1170{1171 const struct ipu6_isys_pixelformat *pfmt =1172 ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);1173 struct device *dev = &av->isys->adev->auxdev.dev;1174 struct v4l2_mbus_frame_desc_entry entry;1175 struct v4l2_subdev_route *route = NULL;1176 struct v4l2_subdev_route *r;1177 struct v4l2_subdev_state *state;1178 struct ipu6_isys_subdev *asd;1179 struct v4l2_subdev *remote_sd;1180 struct media_pipeline *pipeline;1181 struct media_pad *source_pad, *remote_pad;1182 int ret = -EINVAL;1183 1184 *nr_queues = 0;1185 1186 remote_pad = media_pad_remote_pad_unique(&av->pad);1187 if (IS_ERR(remote_pad)) {1188 dev_dbg(dev, "failed to get remote pad\n");1189 return PTR_ERR(remote_pad);1190 }1191 1192 remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);1193 asd = to_ipu6_isys_subdev(remote_sd);1194 source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);1195 if (!source_pad) {1196 dev_dbg(dev, "No external source entity\n");1197 return -ENODEV;1198 }1199 1200 *source_entity = source_pad->entity;1201 1202 /* Find the root */1203 state = v4l2_subdev_lock_and_get_active_state(remote_sd);1204 for_each_active_route(&state->routing, r) {1205 (*nr_queues)++;1206 1207 if (r->source_pad == remote_pad->index)1208 route = r;1209 }1210 1211 if (!route) {1212 v4l2_subdev_unlock_state(state);1213 dev_dbg(dev, "Failed to find route\n");1214 return -ENODEV;1215 }1216 av->source_stream = route->sink_stream;1217 v4l2_subdev_unlock_state(state);1218 1219 ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,1220 to_ipu6_isys_csi2(asd),1221 *source_entity, &entry);1222 if (ret == -ENOIOCTLCMD) {1223 av->vc = 0;1224 av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code);1225 } else if (!ret) {1226 dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",1227 entry.stream, entry.length, entry.bus.csi2.vc,1228 entry.bus.csi2.dt);1229 1230 av->vc = entry.bus.csi2.vc;1231 av->dt = entry.bus.csi2.dt;1232 } else {1233 dev_err(dev, "failed to get remote frame desc\n");1234 return ret;1235 }1236 1237 pipeline = media_entity_pipeline(&av->vdev.entity);1238 if (!pipeline)1239 ret = video_device_pipeline_alloc_start(&av->vdev);1240 else1241 ret = video_device_pipeline_start(&av->vdev, pipeline);1242 if (ret < 0) {1243 dev_dbg(dev, "media pipeline start failed\n");1244 return ret;1245 }1246 1247 av->stream = ipu6_isys_get_stream(av, asd);1248 if (!av->stream) {1249 video_device_pipeline_stop(&av->vdev);1250 dev_err(dev, "no available stream for firmware\n");1251 return -EINVAL;1252 }1253 1254 return 0;1255}1256 1257/*1258 * Do everything that's needed to initialise things related to video1259 * buffer queue, video node, and the related media entity. The caller1260 * is expected to assign isys field and set the name of the video1261 * device.1262 */1263int ipu6_isys_video_init(struct ipu6_isys_video *av)1264{1265 struct v4l2_format format = {1266 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,1267 .fmt.pix = {1268 .width = 1920,1269 .height = 1080,1270 },1271 };1272 struct v4l2_format format_meta = {1273 .type = V4L2_BUF_TYPE_META_CAPTURE,1274 .fmt.meta = {1275 .width = 1920,1276 .height = 4,1277 },1278 };1279 int ret;1280 1281 mutex_init(&av->mutex);1282 av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |1283 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;1284 av->vdev.vfl_dir = VFL_DIR_RX;1285 1286 ret = ipu6_isys_queue_init(&av->aq);1287 if (ret)1288 goto out_free_watermark;1289 1290 av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;1291 ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);1292 if (ret)1293 goto out_vb2_queue_release;1294 1295 av->vdev.entity.ops = &entity_ops;1296 av->vdev.release = video_device_release_empty;1297 av->vdev.fops = &isys_fops;1298 av->vdev.v4l2_dev = &av->isys->v4l2_dev;1299 if (!av->vdev.ioctl_ops)1300 av->vdev.ioctl_ops = &ipu6_v4l2_ioctl_ops;1301 av->vdev.queue = &av->aq.vbq;1302 av->vdev.lock = &av->mutex;1303 1304 __ipu6_isys_vidioc_try_fmt_vid_cap(av, &format);1305 av->pix_fmt = format.fmt.pix;1306 __ipu6_isys_vidioc_try_fmt_meta_cap(av, &format_meta);1307 av->meta_fmt = format_meta.fmt.meta;1308 1309 set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);1310 video_set_drvdata(&av->vdev, av);1311 1312 ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);1313 if (ret)1314 goto out_media_entity_cleanup;1315 1316 return ret;1317 1318out_media_entity_cleanup:1319 vb2_video_unregister_device(&av->vdev);1320 media_entity_cleanup(&av->vdev.entity);1321 1322out_vb2_queue_release:1323 vb2_queue_release(&av->aq.vbq);1324 1325out_free_watermark:1326 mutex_destroy(&av->mutex);1327 1328 return ret;1329}1330 1331void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)1332{1333 vb2_video_unregister_device(&av->vdev);1334 media_entity_cleanup(&av->vdev.entity);1335 mutex_destroy(&av->mutex);1336}1337 1338u32 ipu6_isys_get_format(struct ipu6_isys_video *av)1339{1340 if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)1341 return av->pix_fmt.pixelformat;1342 1343 if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)1344 return av->meta_fmt.dataformat;1345 1346 return 0;1347}1348 1349u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av)1350{1351 if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)1352 return av->pix_fmt.sizeimage;1353 1354 if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)1355 return av->meta_fmt.buffersize;1356 1357 return 0;1358}1359 1360u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av)1361{1362 if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)1363 return av->pix_fmt.bytesperline;1364 1365 if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)1366 return av->meta_fmt.bytesperline;1367 1368 return 0;1369}1370 1371u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av)1372{1373 if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)1374 return av->pix_fmt.width;1375 1376 if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)1377 return av->meta_fmt.width;1378 1379 return 0;1380}1381 1382u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av)1383{1384 if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)1385 return av->pix_fmt.height;1386 1387 if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)1388 return av->meta_fmt.height;1389 1390 return 0;1391}1392