FILE
ipu7-isys-video.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/ipu7/ipu7-isys-video.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2013 - 2025 Intel Corporation 4 */ 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/compat.h> 12#include <linux/device.h> 13#include <linux/iopoll.h> 14#include <linux/list.h> 15#include <linux/minmax.h> 16#include <linux/module.h> 17#include <linux/mutex.h> 18#include <linux/pm_runtime.h> 19#include <linux/spinlock.h> 20#include <linux/string.h> 21 22#include <media/media-entity.h> 23#include <media/v4l2-dev.h> 24#include <media/v4l2-fh.h> 25#include <media/v4l2-ioctl.h> 26#include <media/v4l2-mc.h> 27#include <media/v4l2-subdev.h> 28#include <media/videobuf2-v4l2.h> 29 30#include "abi/ipu7_fw_isys_abi.h" 31 32#include "ipu7.h" 33#include "ipu7-bus.h" 34#include "ipu7-buttress-regs.h" 35#include "ipu7-fw-isys.h" 36#include "ipu7-isys.h" 37#include "ipu7-isys-video.h" 38#include "ipu7-platform-regs.h" 39 40const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { 41 {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, 42 IPU_INSYS_FRAME_FORMAT_RAW16}, 43 {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, 44 IPU_INSYS_FRAME_FORMAT_RAW16}, 45 {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, 46 IPU_INSYS_FRAME_FORMAT_RAW16}, 47 {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, 48 IPU_INSYS_FRAME_FORMAT_RAW16}, 49 {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, 50 IPU_INSYS_FRAME_FORMAT_RAW16}, 51 {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, 52 IPU_INSYS_FRAME_FORMAT_RAW16}, 53 {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, 54 IPU_INSYS_FRAME_FORMAT_RAW16}, 55 {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, 56 IPU_INSYS_FRAME_FORMAT_RAW16}, 57 {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, 58 IPU_INSYS_FRAME_FORMAT_RAW8}, 59 {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, 60 IPU_INSYS_FRAME_FORMAT_RAW8}, 61 {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, 62 IPU_INSYS_FRAME_FORMAT_RAW8}, 63 {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, 64 IPU_INSYS_FRAME_FORMAT_RAW8}, 65 {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, 66 IPU_INSYS_FRAME_FORMAT_RAW12}, 67 {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, 68 IPU_INSYS_FRAME_FORMAT_RAW12}, 69 {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, 70 IPU_INSYS_FRAME_FORMAT_RAW12}, 71 {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, 72 IPU_INSYS_FRAME_FORMAT_RAW12}, 73 {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, 74 IPU_INSYS_FRAME_FORMAT_RAW10}, 75 {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, 76 IPU_INSYS_FRAME_FORMAT_RAW10}, 77 {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, 78 IPU_INSYS_FRAME_FORMAT_RAW10}, 79 {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, 80 IPU_INSYS_FRAME_FORMAT_RAW10}, 81 {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, 82 IPU_INSYS_FRAME_FORMAT_UYVY}, 83 {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, 84 IPU_INSYS_FRAME_FORMAT_YUYV}, 85 {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, 86 IPU_INSYS_FRAME_FORMAT_RGB565}, 87 {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, 88 IPU_INSYS_FRAME_FORMAT_RGBA888}, 89}; 90 91const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) 92{ 93 unsigned int i; 94 95 for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { 96 const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; 97 98 if (pfmt->pixelformat == pixelformat) 99 return pfmt; 100 } 101 102 return &ipu7_isys_pfmts[0]; 103} 104 105static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, 106 struct v4l2_capability *cap) 107{ 108 struct ipu7_isys_video *av = video_drvdata(file); 109 110 strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); 111 strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); 112 113 return 0; 114} 115 116static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, 117 struct v4l2_fmtdesc *f) 118{ 119 unsigned int i, num_found; 120 121 for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { 122 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 123 continue; 124 125 if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) 126 continue; 127 128 if (num_found < f->index) { 129 num_found++; 130 continue; 131 } 132 133 f->flags = 0; 134 f->pixelformat = ipu7_isys_pfmts[i].pixelformat; 135 136 return 0; 137 } 138 139 return -EINVAL; 140} 141 142static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, 143 struct v4l2_frmsizeenum *fsize) 144{ 145 unsigned int i; 146 147 if (fsize->index > 0) 148 return -EINVAL; 149 150 for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { 151 if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) 152 continue; 153 154 fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 155 fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; 156 fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; 157 fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; 158 fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; 159 fsize->stepwise.step_width = 2; 160 fsize->stepwise.step_height = 2; 161 162 return 0; 163 } 164 165 return -EINVAL; 166} 167 168static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, 169 struct v4l2_format *f) 170{ 171 struct ipu7_isys_video *av = video_drvdata(file); 172 173 f->fmt.pix = av->pix_fmt; 174 175 return 0; 176} 177 178static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, 179 u32 *format, u32 *width, u32 *height, 180 u32 *bytesperline, u32 *sizeimage) 181{ 182 const struct ipu7_isys_pixelformat *pfmt = 183 ipu7_isys_get_isys_format(*format); 184 185 *format = pfmt->pixelformat; 186 *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); 187 *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); 188 189 if (pfmt->bpp != pfmt->bpp_packed) 190 *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); 191 else 192 *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); 193 194 *bytesperline = ALIGN(*bytesperline, 64U); 195 196 /* 197 * (height + 1) * bytesperline due to a hardware issue: the DMA unit 198 * is a power of two, and a line should be transferred as few units 199 * as possible. The result is that up to line length more data than 200 * the image size may be transferred to memory after the image. 201 * Another limitation is the GDA allocation unit size. For low 202 * resolution it gives a bigger number. Use larger one to avoid 203 * memory corruption. 204 */ 205 *sizeimage = *bytesperline * *height + 206 max(*bytesperline, 207 av->isys->pdata->ipdata->isys_dma_overshoot); 208} 209 210static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, 211 struct v4l2_format *f) 212{ 213 ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, 214 &f->fmt.pix.width, &f->fmt.pix.height, 215 &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); 216 217 f->fmt.pix.field = V4L2_FIELD_NONE; 218 f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; 219 f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 220 f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; 221 f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; 222} 223 224static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, 225 struct v4l2_format *f) 226{ 227 struct ipu7_isys_video *av = video_drvdata(file); 228 229 if (vb2_is_busy(&av->aq.vbq)) 230 return -EBUSY; 231 232 __ipu_isys_vidioc_try_fmt_vid_cap(av, f); 233 234 return 0; 235} 236 237static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, 238 struct v4l2_format *f) 239{ 240 struct ipu7_isys_video *av = video_drvdata(file); 241 242 ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); 243 av->pix_fmt = f->fmt.pix; 244 245 return 0; 246} 247 248static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, 249 struct v4l2_requestbuffers *p) 250{ 251 struct ipu7_isys_video *av = video_drvdata(file); 252 int ret; 253 254 av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); 255 av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); 256 257 ret = vb2_queue_change_type(&av->aq.vbq, p->type); 258 if (ret) 259 return ret; 260 261 return vb2_ioctl_reqbufs(file, priv, p); 262} 263 264static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, 265 struct v4l2_create_buffers *p) 266{ 267 struct ipu7_isys_video *av = video_drvdata(file); 268 int ret; 269 270 av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); 271 av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); 272 273 ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); 274 if (ret) 275 return ret; 276 277 return vb2_ioctl_create_bufs(file, priv, p); 278} 279 280static int link_validate(struct media_link *link) 281{ 282 struct ipu7_isys_video *av = 283 container_of(link->sink, struct ipu7_isys_video, pad); 284 struct device *dev = &av->isys->adev->auxdev.dev; 285 struct v4l2_subdev_state *s_state; 286 struct v4l2_mbus_framefmt *s_fmt; 287 struct v4l2_subdev *s_sd; 288 struct media_pad *s_pad; 289 u32 s_stream = 0, code; 290 int ret = -EPIPE; 291 292 if (!link->source->entity) 293 return ret; 294 295 s_sd = media_entity_to_v4l2_subdev(link->source->entity); 296 s_state = v4l2_subdev_get_unlocked_active_state(s_sd); 297 if (!s_state) 298 return ret; 299 300 dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", 301 link->source->entity->name, link->source->index, 302 link->sink->entity->name); 303 304 s_pad = media_pad_remote_pad_first(&av->pad); 305 306 v4l2_subdev_lock_state(s_state); 307 308 s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); 309 if (!s_fmt) { 310 dev_err(dev, "failed to get source pad format\n"); 311 goto unlock; 312 } 313 314 code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; 315 316 if (s_fmt->width != av->pix_fmt.width || 317 s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { 318 dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", 319 s_fmt->width, s_fmt->height, s_fmt->code, 320 av->pix_fmt.width, av->pix_fmt.height, code); 321 goto unlock; 322 } 323 324 v4l2_subdev_unlock_state(s_state); 325 326 return 0; 327unlock: 328 v4l2_subdev_unlock_state(s_state); 329 330 return ret; 331} 332 333static void get_stream_opened(struct ipu7_isys_video *av) 334{ 335 unsigned long flags; 336 337 spin_lock_irqsave(&av->isys->streams_lock, flags); 338 av->isys->stream_opened++; 339 spin_unlock_irqrestore(&av->isys->streams_lock, flags); 340} 341 342static void put_stream_opened(struct ipu7_isys_video *av) 343{ 344 unsigned long flags; 345 346 spin_lock_irqsave(&av->isys->streams_lock, flags); 347 av->isys->stream_opened--; 348 spin_unlock_irqrestore(&av->isys->streams_lock, flags); 349} 350 351static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, 352 struct ipu7_insys_stream_cfg *cfg) 353{ 354 struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); 355 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); 356 struct ipu7_isys_stream *stream = av->stream; 357 const struct ipu7_isys_pixelformat *pfmt = 358 ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); 359 struct ipu7_insys_output_pin *output_pin; 360 struct ipu7_insys_input_pin *input_pin; 361 int input_pins = cfg->nof_input_pins++; 362 struct ipu7_isys_queue *aq = &av->aq; 363 struct ipu7_isys *isys = av->isys; 364 struct device *dev = &isys->adev->auxdev.dev; 365 struct v4l2_mbus_framefmt fmt; 366 int output_pins; 367 u32 src_stream = 0; 368 int ret; 369 370 ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, 371 &fmt); 372 if (ret < 0) { 373 dev_err(dev, "can't get stream format (%d)\n", ret); 374 return ret; 375 } 376 377 input_pin = &cfg->input_pins[input_pins]; 378 input_pin->input_res.width = fmt.width; 379 input_pin->input_res.height = fmt.height; 380 input_pin->dt = av->dt; 381 input_pin->disable_mipi_unpacking = 0; 382 pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); 383 if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) 384 input_pin->disable_mipi_unpacking = 1; 385 input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; 386 input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; 387 /* if enable polling isys interrupt, the follow values maybe set */ 388 input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | 389 IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | 390 IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | 391 IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; 392 393 output_pins = cfg->nof_output_pins++; 394 aq->fw_output = output_pins; 395 stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; 396 stream->output_pins[output_pins].aq = aq; 397 398 output_pin = &cfg->output_pins[output_pins]; 399 /* output pin msg link */ 400 output_pin->link.buffer_lines = 0; 401 output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; 402 output_pin->link.granularity_pointer_update = 0; 403 output_pin->link.msg_link_streaming_mode = 404 IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; 405 406 output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; 407 output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; 408 output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; 409 output_pin->link.use_sw_managed = 1; 410 /* TODO: set the snoop bit for metadata capture */ 411 output_pin->link.is_snoop = 0; 412 413 /* output pin crop */ 414 output_pin->crop.line_top = 0; 415 output_pin->crop.line_bottom = 0; 416 417 /* output de-compression */ 418 output_pin->dpcm.enable = 0; 419 420 /* frame format type */ 421 pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); 422 output_pin->ft = (u16)pfmt->css_pixelformat; 423 424 /* stride in bytes */ 425 output_pin->stride = av->pix_fmt.bytesperline; 426 output_pin->send_irq = 1; 427 output_pin->early_ack_en = 0; 428 429 /* input pin id */ 430 output_pin->input_pin_id = input_pins; 431 432 return 0; 433} 434 435/* Create stream and start it using the CSS FW ABI. */ 436static int start_stream_firmware(struct ipu7_isys_video *av, 437 struct ipu7_isys_buffer_list *bl) 438{ 439 struct device *dev = &av->isys->adev->auxdev.dev; 440 struct ipu7_isys_stream *stream = av->stream; 441 struct ipu7_insys_stream_cfg *stream_cfg; 442 struct ipu7_insys_buffset *buf = NULL; 443 struct isys_fw_msgs *msg = NULL; 444 struct ipu7_isys_queue *aq; 445 int ret, retout, tout; 446 u16 send_type; 447 448 if (WARN_ON(!bl)) 449 return -EIO; 450 451 msg = ipu7_get_fw_msg_buf(stream); 452 if (!msg) 453 return -ENOMEM; 454 455 stream_cfg = &msg->fw_msg.stream; 456 stream_cfg->port_id = stream->stream_source; 457 stream_cfg->vc = stream->vc; 458 stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | 459 IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; 460 461 list_for_each_entry(aq, &stream->queues, node) { 462 struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); 463 464 ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); 465 if (ret < 0) { 466 ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); 467 return ret; 468 } 469 } 470 471 ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); 472 473 stream->nr_output_pins = stream_cfg->nof_output_pins; 474 475 reinit_completion(&stream->stream_open_completion); 476 477 ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, 478 stream_cfg, msg->dma_addr, 479 sizeof(*stream_cfg), 480 IPU_INSYS_SEND_TYPE_STREAM_OPEN); 481 if (ret < 0) { 482 dev_err(dev, "can't open stream (%d)\n", ret); 483 ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); 484 return ret; 485 } 486 487 get_stream_opened(av); 488 489 tout = wait_for_completion_timeout(&stream->stream_open_completion, 490 FW_CALL_TIMEOUT_JIFFIES); 491 492 ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); 493 494 if (!tout) { 495 dev_err(dev, "stream open time out\n"); 496 ret = -ETIMEDOUT; 497 goto out_put_stream_opened; 498 } 499 if (stream->error) { 500 dev_err(dev, "stream open error: %d\n", stream->error); 501 ret = -EIO; 502 goto out_put_stream_opened; 503 } 504 dev_dbg(dev, "start stream: open complete\n"); 505 506 msg = ipu7_get_fw_msg_buf(stream); 507 if (!msg) { 508 ret = -ENOMEM; 509 goto out_put_stream_opened; 510 } 511 buf = &msg->fw_msg.frame; 512 513 ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); 514 ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); 515 516 reinit_completion(&stream->stream_start_completion); 517 518 send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; 519 ipu7_fw_isys_dump_frame_buff_set(dev, buf, 520 stream_cfg->nof_output_pins); 521 ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, 522 msg->dma_addr, sizeof(*buf), 523 send_type); 524 if (ret < 0) { 525 dev_err(dev, "can't start streaming (%d)\n", ret); 526 goto out_stream_close; 527 } 528 529 tout = wait_for_completion_timeout(&stream->stream_start_completion, 530 FW_CALL_TIMEOUT_JIFFIES); 531 if (!tout) { 532 dev_err(dev, "stream start time out\n"); 533 ret = -ETIMEDOUT; 534 goto out_stream_close; 535 } 536 if (stream->error) { 537 dev_err(dev, "stream start error: %d\n", stream->error); 538 ret = -EIO; 539 goto out_stream_close; 540 } 541 dev_dbg(dev, "start stream: complete\n"); 542 543 return 0; 544 545out_stream_close: 546 reinit_completion(&stream->stream_close_completion); 547 548 retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, 549 IPU_INSYS_SEND_TYPE_STREAM_CLOSE); 550 if (retout < 0) { 551 dev_dbg(dev, "can't close stream (%d)\n", retout); 552 goto out_put_stream_opened; 553 } 554 555 tout = wait_for_completion_timeout(&stream->stream_close_completion, 556 FW_CALL_TIMEOUT_JIFFIES); 557 if (!tout) 558 dev_err(dev, "stream close time out with error %d\n", 559 stream->error); 560 else 561 dev_dbg(dev, "stream close complete\n"); 562 563out_put_stream_opened: 564 put_stream_opened(av); 565 566 return ret; 567} 568 569static void stop_streaming_firmware(struct ipu7_isys_video *av) 570{ 571 struct device *dev = &av->isys->adev->auxdev.dev; 572 struct ipu7_isys_stream *stream = av->stream; 573 int ret, tout; 574 575 reinit_completion(&stream->stream_stop_completion); 576 577 ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, 578 IPU_INSYS_SEND_TYPE_STREAM_FLUSH); 579 if (ret < 0) { 580 dev_err(dev, "can't stop stream (%d)\n", ret); 581 return; 582 } 583 584 tout = wait_for_completion_timeout(&stream->stream_stop_completion, 585 FW_CALL_TIMEOUT_JIFFIES); 586 if (!tout) 587 dev_warn(dev, "stream stop time out\n"); 588 else if (stream->error) 589 dev_warn(dev, "stream stop error: %d\n", stream->error); 590 else 591 dev_dbg(dev, "stop stream: complete\n"); 592} 593 594static void close_streaming_firmware(struct ipu7_isys_video *av) 595{ 596 struct device *dev = &av->isys->adev->auxdev.dev; 597 struct ipu7_isys_stream *stream = av->stream; 598 int ret, tout; 599 600 reinit_completion(&stream->stream_close_completion); 601 602 ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, 603 IPU_INSYS_SEND_TYPE_STREAM_CLOSE); 604 if (ret < 0) { 605 dev_err(dev, "can't close stream (%d)\n", ret); 606 return; 607 } 608 609 tout = wait_for_completion_timeout(&stream->stream_close_completion, 610 FW_CALL_TIMEOUT_JIFFIES); 611 if (!tout) 612 dev_warn(dev, "stream close time out\n"); 613 else if (stream->error) 614 dev_warn(dev, "stream close error: %d\n", stream->error); 615 else 616 dev_dbg(dev, "close stream: complete\n"); 617 618 put_stream_opened(av); 619} 620 621int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, 622 struct media_entity *source_entity, 623 int nr_queues) 624{ 625 struct ipu7_isys_stream *stream = av->stream; 626 struct ipu7_isys_csi2 *csi2; 627 628 if (WARN_ON(stream->nr_streaming)) 629 return -EINVAL; 630 631 stream->nr_queues = nr_queues; 632 atomic_set(&stream->sequence, 0); 633 atomic_set(&stream->buf_id, 0); 634 635 stream->seq_index = 0; 636 memset(stream->seq, 0, sizeof(stream->seq)); 637 638 if (WARN_ON(!list_empty(&stream->queues))) 639 return -EINVAL; 640 641 stream->stream_source = stream->asd->source; 642 643 csi2 = ipu7_isys_subdev_to_csi2(stream->asd); 644 csi2->receiver_errors = 0; 645 stream->source_entity = source_entity; 646 647 dev_dbg(&av->isys->adev->auxdev.dev, 648 "prepare stream: external entity %s\n", 649 stream->source_entity->name); 650 651 return 0; 652} 653 654void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) 655{ 656 unsigned long flags; 657 struct device *dev; 658 unsigned int i; 659 660 if (!stream) { 661 pr_err("ipu7-isys: no available stream\n"); 662 return; 663 } 664 665 dev = &stream->isys->adev->auxdev.dev; 666 667 spin_lock_irqsave(&stream->isys->streams_lock, flags); 668 for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { 669 if (&stream->isys->streams[i] == stream) { 670 if (stream->isys->streams_ref_count[i] > 0) 671 stream->isys->streams_ref_count[i]--; 672 else 673 dev_warn(dev, "invalid stream %d\n", i); 674 675 break; 676 } 677 } 678 spin_unlock_irqrestore(&stream->isys->streams_lock, flags); 679} 680 681static struct ipu7_isys_stream * 682ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) 683{ 684 struct ipu7_isys_stream *stream = NULL; 685 struct ipu7_isys *isys = av->isys; 686 unsigned long flags; 687 unsigned int i; 688 u8 vc = av->vc; 689 690 if (!isys) 691 return NULL; 692 693 spin_lock_irqsave(&isys->streams_lock, flags); 694 for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { 695 if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && 696 isys->streams[i].asd == asd) { 697 isys->streams_ref_count[i]++; 698 stream = &isys->streams[i]; 699 break; 700 } 701 } 702 703 if (!stream) { 704 for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { 705 if (!isys->streams_ref_count[i]) { 706 isys->streams_ref_count[i]++; 707 stream = &isys->streams[i]; 708 stream->vc = vc; 709 stream->asd = asd; 710 break; 711 } 712 } 713 } 714 spin_unlock_irqrestore(&isys->streams_lock, flags); 715 716 return stream; 717} 718 719struct ipu7_isys_stream * 720ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) 721{ 722 unsigned long flags; 723 struct ipu7_isys_stream *stream = NULL; 724 725 if (!isys) 726 return NULL; 727 728 if (stream_handle >= IPU_ISYS_MAX_STREAMS) { 729 dev_err(&isys->adev->auxdev.dev, 730 "stream_handle %d is invalid\n", stream_handle); 731 return NULL; 732 } 733 734 spin_lock_irqsave(&isys->streams_lock, flags); 735 if (isys->streams_ref_count[stream_handle] > 0) { 736 isys->streams_ref_count[stream_handle]++; 737 stream = &isys->streams[stream_handle]; 738 } 739 spin_unlock_irqrestore(&isys->streams_lock, flags); 740 741 return stream; 742} 743 744struct ipu7_isys_stream * 745ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) 746{ 747 struct ipu7_isys_stream *stream = NULL; 748 unsigned long flags; 749 unsigned int i; 750 751 if (!isys) 752 return NULL; 753 754 if (source < 0) { 755 dev_err(&isys->adev->auxdev.dev, 756 "query stream with invalid port number\n"); 757 return NULL; 758 } 759 760 spin_lock_irqsave(&isys->streams_lock, flags); 761 for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { 762 if (!isys->streams_ref_count[i]) 763 continue; 764 765 if (isys->streams[i].stream_source == source && 766 isys->streams[i].vc == vc) { 767 stream = &isys->streams[i]; 768 isys->streams_ref_count[i]++; 769 break; 770 } 771 } 772 spin_unlock_irqrestore(&isys->streams_lock, flags); 773 774 return stream; 775} 776 777int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, 778 struct ipu7_isys_buffer_list *bl) 779{ 780 struct ipu7_isys_stream *stream = av->stream; 781 struct device *dev = &av->isys->adev->auxdev.dev; 782 struct media_pad *r_pad; 783 struct v4l2_subdev *sd; 784 u32 r_stream = 0; 785 int ret = 0; 786 787 dev_dbg(dev, "set stream: %d\n", state); 788 789 if (WARN(!stream->source_entity, "No source entity for stream\n")) 790 return -ENODEV; 791 792 sd = &stream->asd->sd; 793 r_pad = media_pad_remote_pad_first(&av->pad); 794 if (!state) { 795 stop_streaming_firmware(av); 796 797 /* stop sub-device which connects with video */ 798 dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", 799 sd->name, r_pad->index, BIT_ULL(r_stream)); 800 ret = v4l2_subdev_disable_streams(sd, r_pad->index, 801 BIT_ULL(r_stream)); 802 if (ret) { 803 dev_err(dev, "disable streams %s failed with %d\n", 804 sd->name, ret); 805 return ret; 806 } 807 808 close_streaming_firmware(av); 809 } else { 810 ret = start_stream_firmware(av, bl); 811 if (ret) { 812 dev_err(dev, "start stream of firmware failed\n"); 813 return ret; 814 } 815 816 /* start sub-device which connects with video */ 817 dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", 818 sd->name, r_pad->index, BIT_ULL(r_stream)); 819 ret = v4l2_subdev_enable_streams(sd, r_pad->index, 820 BIT_ULL(r_stream)); 821 if (ret) { 822 dev_err(dev, "enable streams %s failed with %d\n", 823 sd->name, ret); 824 goto out_media_entity_stop_streaming_firmware; 825 } 826 } 827 828 av->streaming = state; 829 830 return 0; 831 832out_media_entity_stop_streaming_firmware: 833 stop_streaming_firmware(av); 834 835 return ret; 836} 837 838static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { 839 .vidioc_querycap = ipu7_isys_vidioc_querycap, 840 .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, 841 .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, 842 .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, 843 .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, 844 .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, 845 .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, 846 .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, 847 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 848 .vidioc_querybuf = vb2_ioctl_querybuf, 849 .vidioc_qbuf = vb2_ioctl_qbuf, 850 .vidioc_dqbuf = vb2_ioctl_dqbuf, 851 .vidioc_streamon = vb2_ioctl_streamon, 852 .vidioc_streamoff = vb2_ioctl_streamoff, 853 .vidioc_expbuf = vb2_ioctl_expbuf, 854}; 855 856static const struct media_entity_operations entity_ops = { 857 .link_validate = link_validate, 858}; 859 860static const struct v4l2_file_operations isys_fops = { 861 .owner = THIS_MODULE, 862 .poll = vb2_fop_poll, 863 .unlocked_ioctl = video_ioctl2, 864 .mmap = vb2_fop_mmap, 865 .open = v4l2_fh_open, 866 .release = vb2_fop_release, 867}; 868 869int ipu7_isys_fw_open(struct ipu7_isys *isys) 870{ 871 struct ipu7_bus_device *adev = isys->adev; 872 int ret; 873 874 ret = pm_runtime_resume_and_get(&adev->auxdev.dev); 875 if (ret < 0) 876 return ret; 877 878 mutex_lock(&isys->mutex); 879 880 if (isys->ref_count++) 881 goto unlock; 882 883 /* 884 * Buffers could have been left to wrong queue at last closure. 885 * Move them now back to empty buffer queue. 886 */ 887 ipu7_cleanup_fw_msg_bufs(isys); 888 889 ret = ipu7_fw_isys_open(isys); 890 if (ret < 0) 891 goto out; 892 893unlock: 894 mutex_unlock(&isys->mutex); 895 896 return 0; 897out: 898 isys->ref_count--; 899 mutex_unlock(&isys->mutex); 900 pm_runtime_put(&adev->auxdev.dev); 901 902 return ret; 903} 904 905void ipu7_isys_fw_close(struct ipu7_isys *isys) 906{ 907 mutex_lock(&isys->mutex); 908 909 isys->ref_count--; 910 911 if (!isys->ref_count) 912 ipu7_fw_isys_close(isys); 913 914 mutex_unlock(&isys->mutex); 915 pm_runtime_put(&isys->adev->auxdev.dev); 916} 917 918int ipu7_isys_setup_video(struct ipu7_isys_video *av, 919 struct media_entity **source_entity, int *nr_queues) 920{ 921 const struct ipu7_isys_pixelformat *pfmt = 922 ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); 923 struct device *dev = &av->isys->adev->auxdev.dev; 924 struct media_pad *source_pad, *remote_pad; 925 struct v4l2_mbus_frame_desc_entry entry; 926 struct v4l2_subdev_route *route = NULL; 927 struct v4l2_subdev_route *r; 928 struct v4l2_subdev_state *state; 929 struct ipu7_isys_subdev *asd; 930 struct v4l2_subdev *remote_sd; 931 struct media_pipeline *pipeline; 932 int ret = -EINVAL; 933 934 *nr_queues = 0; 935 936 remote_pad = media_pad_remote_pad_unique(&av->pad); 937 if (IS_ERR(remote_pad)) { 938 dev_dbg(dev, "failed to get remote pad\n"); 939 return PTR_ERR(remote_pad); 940 } 941 942 remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); 943 asd = to_ipu7_isys_subdev(remote_sd); 944 945 source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); 946 if (!source_pad) { 947 dev_dbg(dev, "No external source entity\n"); 948 return -ENODEV; 949 } 950 951 *source_entity = source_pad->entity; 952 953 state = v4l2_subdev_lock_and_get_active_state(remote_sd); 954 for_each_active_route(&state->routing, r) { 955 if (r->source_pad == remote_pad->index) 956 route = r; 957 } 958 959 if (!route) { 960 v4l2_subdev_unlock_state(state); 961 dev_dbg(dev, "Failed to find route\n"); 962 return -ENODEV; 963 } 964 965 v4l2_subdev_unlock_state(state); 966 967 ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, 968 to_ipu7_isys_csi2(asd), 969 *source_entity, &entry, 970 nr_queues); 971 if (ret == -ENOIOCTLCMD) { 972 av->vc = 0; 973 av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); 974 if (av->dt == 0xff) 975 return -EINVAL; 976 *nr_queues = 1; 977 } else if (*nr_queues && !ret) { 978 dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", 979 entry.stream, entry.length, entry.bus.csi2.vc, 980 entry.bus.csi2.dt); 981 982 av->vc = entry.bus.csi2.vc; 983 av->dt = entry.bus.csi2.dt; 984 } else { 985 dev_err(dev, "failed to get remote frame desc\n"); 986 return ret; 987 } 988 989 pipeline = media_entity_pipeline(&av->vdev.entity); 990 if (!pipeline) 991 ret = video_device_pipeline_alloc_start(&av->vdev); 992 else 993 ret = video_device_pipeline_start(&av->vdev, pipeline); 994 if (ret < 0) { 995 dev_dbg(dev, "media pipeline start failed\n"); 996 return ret; 997 } 998 999 av->stream = ipu7_isys_get_stream(av, asd); 1000 if (!av->stream) { 1001 video_device_pipeline_stop(&av->vdev); 1002 dev_err(dev, "no available stream for firmware\n"); 1003 return -EINVAL; 1004 } 1005 1006 return 0; 1007} 1008 1009/* 1010 * Do everything that's needed to initialise things related to video 1011 * buffer queue, video node, and the related media entity. The caller 1012 * is expected to assign isys field and set the name of the video 1013 * device. 1014 */ 1015int ipu7_isys_video_init(struct ipu7_isys_video *av) 1016{ 1017 struct v4l2_format format = { 1018 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 1019 .fmt.pix = { 1020 .width = 1920, 1021 .height = 1080, 1022 }, 1023 }; 1024 int ret; 1025 1026 mutex_init(&av->mutex); 1027 av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | 1028 V4L2_CAP_VIDEO_CAPTURE; 1029 av->vdev.vfl_dir = VFL_DIR_RX; 1030 1031 ret = ipu7_isys_queue_init(&av->aq); 1032 if (ret) 1033 goto out_mutex_destroy; 1034 1035 av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 1036 ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); 1037 if (ret) 1038 goto out_vb2_queue_cleanup; 1039 1040 av->vdev.entity.ops = &entity_ops; 1041 av->vdev.release = video_device_release_empty; 1042 av->vdev.fops = &isys_fops; 1043 av->vdev.v4l2_dev = &av->isys->v4l2_dev; 1044 av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; 1045 av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; 1046 av->vdev.queue = &av->aq.vbq; 1047 av->vdev.lock = &av->mutex; 1048 1049 __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); 1050 av->pix_fmt = format.fmt.pix; 1051 1052 video_set_drvdata(&av->vdev, av); 1053 1054 ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); 1055 if (ret) 1056 goto out_media_entity_cleanup; 1057 1058 return ret; 1059 1060out_media_entity_cleanup: 1061 vb2_video_unregister_device(&av->vdev); 1062 media_entity_cleanup(&av->vdev.entity); 1063 1064out_vb2_queue_cleanup: 1065 vb2_queue_release(&av->aq.vbq); 1066 1067out_mutex_destroy: 1068 mutex_destroy(&av->mutex); 1069 1070 return ret; 1071} 1072 1073void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) 1074{ 1075 vb2_video_unregister_device(&av->vdev); 1076 media_entity_cleanup(&av->vdev.entity); 1077 mutex_destroy(&av->mutex); 1078}
~/
Read-only file preview.