File Index
Files and directories
Sign in
FILE

ipu7-isys-video.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/ipu7/ipu7-isys-video.c 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.