162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TI Camera Access Layer (CAL) - Video Device
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015-2020 Texas Instruments Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors:
862306a36Sopenharmony_ci *	Benoit Parrot <bparrot@ti.com>
962306a36Sopenharmony_ci *	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/ioctl.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/videodev2.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <media/media-device.h>
1762306a36Sopenharmony_ci#include <media/v4l2-common.h>
1862306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
1962306a36Sopenharmony_ci#include <media/v4l2-device.h>
2062306a36Sopenharmony_ci#include <media/v4l2-event.h>
2162306a36Sopenharmony_ci#include <media/v4l2-fh.h>
2262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2362306a36Sopenharmony_ci#include <media/videobuf2-core.h>
2462306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "cal.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*  Print Four-character-code (FOURCC) */
2962306a36Sopenharmony_cistatic char *fourcc_to_str(u32 fmt)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	static char code[5];
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	code[0] = (unsigned char)(fmt & 0xff);
3462306a36Sopenharmony_ci	code[1] = (unsigned char)((fmt >> 8) & 0xff);
3562306a36Sopenharmony_ci	code[2] = (unsigned char)((fmt >> 16) & 0xff);
3662306a36Sopenharmony_ci	code[3] = (unsigned char)((fmt >> 24) & 0xff);
3762306a36Sopenharmony_ci	code[4] = '\0';
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return code;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* ------------------------------------------------------------------
4362306a36Sopenharmony_ci *	V4L2 Common IOCTLs
4462306a36Sopenharmony_ci * ------------------------------------------------------------------
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int cal_querycap(struct file *file, void *priv,
4862306a36Sopenharmony_ci			struct v4l2_capability *cap)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
5162306a36Sopenharmony_ci	strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int cal_g_fmt_vid_cap(struct file *file, void *priv,
5762306a36Sopenharmony_ci			     struct v4l2_format *f)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	*f = ctx->v_fmt;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* ------------------------------------------------------------------
6762306a36Sopenharmony_ci *	V4L2 Video Node Centric IOCTLs
6862306a36Sopenharmony_ci * ------------------------------------------------------------------
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct cal_format_info *find_format_by_pix(struct cal_ctx *ctx,
7262306a36Sopenharmony_ci							u32 pixelformat)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
7562306a36Sopenharmony_ci	unsigned int k;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	for (k = 0; k < ctx->num_active_fmt; k++) {
7862306a36Sopenharmony_ci		fmtinfo = ctx->active_fmt[k];
7962306a36Sopenharmony_ci		if (fmtinfo->fourcc == pixelformat)
8062306a36Sopenharmony_ci			return fmtinfo;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return NULL;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx,
8762306a36Sopenharmony_ci							 u32 code)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
9062306a36Sopenharmony_ci	unsigned int k;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	for (k = 0; k < ctx->num_active_fmt; k++) {
9362306a36Sopenharmony_ci		fmtinfo = ctx->active_fmt[k];
9462306a36Sopenharmony_ci		if (fmtinfo->code == code)
9562306a36Sopenharmony_ci			return fmtinfo;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return NULL;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int cal_legacy_enum_fmt_vid_cap(struct file *file, void *priv,
10262306a36Sopenharmony_ci				       struct v4l2_fmtdesc *f)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
10562306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (f->index >= ctx->num_active_fmt)
10862306a36Sopenharmony_ci		return -EINVAL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	fmtinfo = ctx->active_fmt[f->index];
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	f->pixelformat = fmtinfo->fourcc;
11362306a36Sopenharmony_ci	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int __subdev_get_format(struct cal_ctx *ctx,
11862306a36Sopenharmony_ci			       struct v4l2_mbus_framefmt *fmt)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct v4l2_subdev_format sd_fmt = {
12162306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
12262306a36Sopenharmony_ci		.pad = 0,
12362306a36Sopenharmony_ci	};
12462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt);
12862306a36Sopenharmony_ci	if (ret)
12962306a36Sopenharmony_ci		return ret;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	*fmt = *mbus_fmt;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
13462306a36Sopenharmony_ci		fmt->width, fmt->height, fmt->code);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int __subdev_set_format(struct cal_ctx *ctx,
14062306a36Sopenharmony_ci			       struct v4l2_mbus_framefmt *fmt)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct v4l2_subdev_format sd_fmt = {
14362306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
14462306a36Sopenharmony_ci		.pad = 0,
14562306a36Sopenharmony_ci	};
14662306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
14762306a36Sopenharmony_ci	int ret;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	*mbus_fmt = *fmt;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt);
15262306a36Sopenharmony_ci	if (ret)
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
15662306a36Sopenharmony_ci		fmt->width, fmt->height, fmt->code);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void cal_calc_format_size(struct cal_ctx *ctx,
16262306a36Sopenharmony_ci				 const struct cal_format_info *fmtinfo,
16362306a36Sopenharmony_ci				 struct v4l2_format *f)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u32 bpl, max_width;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/*
16862306a36Sopenharmony_ci	 * Maximum width is bound by the DMA max width in bytes.
16962306a36Sopenharmony_ci	 * We need to recalculate the actual maxi width depending on the
17062306a36Sopenharmony_ci	 * number of bytes per pixels required.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmtinfo->bpp, 8) >> 3);
17362306a36Sopenharmony_ci	v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2,
17462306a36Sopenharmony_ci			      &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES,
17562306a36Sopenharmony_ci			      0, 0);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	bpl = (f->fmt.pix.width * ALIGN(fmtinfo->bpp, 8)) >> 3;
17862306a36Sopenharmony_ci	f->fmt.pix.bytesperline = ALIGN(bpl, 16);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height *
18162306a36Sopenharmony_ci			       f->fmt.pix.bytesperline;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
18462306a36Sopenharmony_ci		__func__, fourcc_to_str(f->fmt.pix.pixelformat),
18562306a36Sopenharmony_ci		f->fmt.pix.width, f->fmt.pix.height,
18662306a36Sopenharmony_ci		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
19062306a36Sopenharmony_ci				      struct v4l2_format *f)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
19362306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
19462306a36Sopenharmony_ci	struct v4l2_subdev_frame_size_enum fse = {
19562306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
19662306a36Sopenharmony_ci	};
19762306a36Sopenharmony_ci	int found;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
20062306a36Sopenharmony_ci	if (!fmtinfo) {
20162306a36Sopenharmony_ci		ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
20262306a36Sopenharmony_ci			f->fmt.pix.pixelformat);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		/* Just get the first one enumerated */
20562306a36Sopenharmony_ci		fmtinfo = ctx->active_fmt[0];
20662306a36Sopenharmony_ci		f->fmt.pix.pixelformat = fmtinfo->fourcc;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* check for/find a valid width/height */
21262306a36Sopenharmony_ci	found = false;
21362306a36Sopenharmony_ci	fse.pad = 0;
21462306a36Sopenharmony_ci	fse.code = fmtinfo->code;
21562306a36Sopenharmony_ci	for (fse.index = 0; ; fse.index++) {
21662306a36Sopenharmony_ci		int ret;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size,
21962306a36Sopenharmony_ci				       NULL, &fse);
22062306a36Sopenharmony_ci		if (ret)
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		if ((f->fmt.pix.width == fse.max_width) &&
22462306a36Sopenharmony_ci		    (f->fmt.pix.height == fse.max_height)) {
22562306a36Sopenharmony_ci			found = true;
22662306a36Sopenharmony_ci			break;
22762306a36Sopenharmony_ci		} else if ((f->fmt.pix.width >= fse.min_width) &&
22862306a36Sopenharmony_ci			 (f->fmt.pix.width <= fse.max_width) &&
22962306a36Sopenharmony_ci			 (f->fmt.pix.height >= fse.min_height) &&
23062306a36Sopenharmony_ci			 (f->fmt.pix.height <= fse.max_height)) {
23162306a36Sopenharmony_ci			found = true;
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (!found) {
23762306a36Sopenharmony_ci		/* use existing values as default */
23862306a36Sopenharmony_ci		f->fmt.pix.width = ctx->v_fmt.fmt.pix.width;
23962306a36Sopenharmony_ci		f->fmt.pix.height =  ctx->v_fmt.fmt.pix.height;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/*
24362306a36Sopenharmony_ci	 * Use current colorspace for now, it will get
24462306a36Sopenharmony_ci	 * updated properly during s_fmt
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace;
24762306a36Sopenharmony_ci	cal_calc_format_size(ctx, fmtinfo, f);
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
25262306a36Sopenharmony_ci				    struct v4l2_format *f)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
25562306a36Sopenharmony_ci	struct vb2_queue *q = &ctx->vb_vidq;
25662306a36Sopenharmony_ci	struct v4l2_subdev_format sd_fmt = {
25762306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
25862306a36Sopenharmony_ci		.pad = CAL_CAMERARX_PAD_SINK,
25962306a36Sopenharmony_ci	};
26062306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
26162306a36Sopenharmony_ci	int ret;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (vb2_is_busy(q)) {
26462306a36Sopenharmony_ci		ctx_dbg(3, ctx, "%s device busy\n", __func__);
26562306a36Sopenharmony_ci		return -EBUSY;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ret = cal_legacy_try_fmt_vid_cap(file, priv, f);
26962306a36Sopenharmony_ci	if (ret < 0)
27062306a36Sopenharmony_ci		return ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmtinfo->code);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	ret = __subdev_set_format(ctx, &sd_fmt.format);
27762306a36Sopenharmony_ci	if (ret)
27862306a36Sopenharmony_ci		return ret;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Just double check nothing has gone wrong */
28162306a36Sopenharmony_ci	if (sd_fmt.format.code != fmtinfo->code) {
28262306a36Sopenharmony_ci		ctx_dbg(3, ctx,
28362306a36Sopenharmony_ci			"%s subdev changed format on us, this should not happen\n",
28462306a36Sopenharmony_ci			__func__);
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format);
28962306a36Sopenharmony_ci	ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
29062306a36Sopenharmony_ci	ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc;
29162306a36Sopenharmony_ci	ctx->v_fmt.fmt.pix.field = sd_fmt.format.field;
29262306a36Sopenharmony_ci	cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ctx->fmtinfo = fmtinfo;
29762306a36Sopenharmony_ci	*f = ctx->v_fmt;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int cal_legacy_enum_framesizes(struct file *file, void *fh,
30362306a36Sopenharmony_ci				      struct v4l2_frmsizeenum *fsize)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
30662306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
30762306a36Sopenharmony_ci	struct v4l2_subdev_frame_size_enum fse = {
30862306a36Sopenharmony_ci		.index = fsize->index,
30962306a36Sopenharmony_ci		.pad = 0,
31062306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
31162306a36Sopenharmony_ci	};
31262306a36Sopenharmony_ci	int ret;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* check for valid format */
31562306a36Sopenharmony_ci	fmtinfo = find_format_by_pix(ctx, fsize->pixel_format);
31662306a36Sopenharmony_ci	if (!fmtinfo) {
31762306a36Sopenharmony_ci		ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
31862306a36Sopenharmony_ci			fsize->pixel_format);
31962306a36Sopenharmony_ci		return -EINVAL;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	fse.code = fmtinfo->code;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL,
32562306a36Sopenharmony_ci			       &fse);
32662306a36Sopenharmony_ci	if (ret)
32762306a36Sopenharmony_ci		return ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
33062306a36Sopenharmony_ci		__func__, fse.index, fse.code, fse.min_width, fse.max_width,
33162306a36Sopenharmony_ci		fse.min_height, fse.max_height);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
33462306a36Sopenharmony_ci	fsize->discrete.width = fse.max_width;
33562306a36Sopenharmony_ci	fsize->discrete.height = fse.max_height;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int cal_legacy_enum_input(struct file *file, void *priv,
34162306a36Sopenharmony_ci				 struct v4l2_input *inp)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	if (inp->index > 0)
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
34762306a36Sopenharmony_ci	sprintf(inp->name, "Camera %u", inp->index);
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int cal_legacy_g_input(struct file *file, void *priv, unsigned int *i)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	*i = 0;
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int cal_legacy_s_input(struct file *file, void *priv, unsigned int i)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	return i > 0 ? -EINVAL : 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* timeperframe is arbitrary and continuous */
36362306a36Sopenharmony_cistatic int cal_legacy_enum_frameintervals(struct file *file, void *priv,
36462306a36Sopenharmony_ci					  struct v4l2_frmivalenum *fival)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
36762306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
36862306a36Sopenharmony_ci	struct v4l2_subdev_frame_interval_enum fie = {
36962306a36Sopenharmony_ci		.index = fival->index,
37062306a36Sopenharmony_ci		.width = fival->width,
37162306a36Sopenharmony_ci		.height = fival->height,
37262306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
37362306a36Sopenharmony_ci	};
37462306a36Sopenharmony_ci	int ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	fmtinfo = find_format_by_pix(ctx, fival->pixel_format);
37762306a36Sopenharmony_ci	if (!fmtinfo)
37862306a36Sopenharmony_ci		return -EINVAL;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	fie.code = fmtinfo->code;
38162306a36Sopenharmony_ci	ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval,
38262306a36Sopenharmony_ci			       NULL, &fie);
38362306a36Sopenharmony_ci	if (ret)
38462306a36Sopenharmony_ci		return ret;
38562306a36Sopenharmony_ci	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
38662306a36Sopenharmony_ci	fival->discrete = fie.interval;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int cal_legacy_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return v4l2_g_parm_cap(video_devdata(file), ctx->phy->source, a);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int cal_legacy_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return v4l2_s_parm_cap(video_devdata(file), ctx->phy->source, a);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cal_ioctl_legacy_ops = {
40662306a36Sopenharmony_ci	.vidioc_querycap      = cal_querycap,
40762306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap  = cal_legacy_enum_fmt_vid_cap,
40862306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap     = cal_g_fmt_vid_cap,
40962306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap   = cal_legacy_try_fmt_vid_cap,
41062306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap     = cal_legacy_s_fmt_vid_cap,
41162306a36Sopenharmony_ci	.vidioc_enum_framesizes   = cal_legacy_enum_framesizes,
41262306a36Sopenharmony_ci	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
41362306a36Sopenharmony_ci	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
41462306a36Sopenharmony_ci	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
41562306a36Sopenharmony_ci	.vidioc_querybuf      = vb2_ioctl_querybuf,
41662306a36Sopenharmony_ci	.vidioc_qbuf          = vb2_ioctl_qbuf,
41762306a36Sopenharmony_ci	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
41862306a36Sopenharmony_ci	.vidioc_expbuf        = vb2_ioctl_expbuf,
41962306a36Sopenharmony_ci	.vidioc_enum_input    = cal_legacy_enum_input,
42062306a36Sopenharmony_ci	.vidioc_g_input       = cal_legacy_g_input,
42162306a36Sopenharmony_ci	.vidioc_s_input       = cal_legacy_s_input,
42262306a36Sopenharmony_ci	.vidioc_enum_frameintervals = cal_legacy_enum_frameintervals,
42362306a36Sopenharmony_ci	.vidioc_streamon      = vb2_ioctl_streamon,
42462306a36Sopenharmony_ci	.vidioc_streamoff     = vb2_ioctl_streamoff,
42562306a36Sopenharmony_ci	.vidioc_log_status    = v4l2_ctrl_log_status,
42662306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
42762306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
42862306a36Sopenharmony_ci	.vidioc_g_parm		= cal_legacy_g_parm,
42962306a36Sopenharmony_ci	.vidioc_s_parm		= cal_legacy_s_parm,
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/* ------------------------------------------------------------------
43362306a36Sopenharmony_ci *	V4L2 Media Controller Centric IOCTLs
43462306a36Sopenharmony_ci * ------------------------------------------------------------------
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int cal_mc_enum_fmt_vid_cap(struct file *file, void  *priv,
43862306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	unsigned int i;
44162306a36Sopenharmony_ci	unsigned int idx;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (f->index >= cal_num_formats)
44462306a36Sopenharmony_ci		return -EINVAL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	idx = 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	for (i = 0; i < cal_num_formats; ++i) {
44962306a36Sopenharmony_ci		if (f->mbus_code && cal_formats[i].code != f->mbus_code)
45062306a36Sopenharmony_ci			continue;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		if (idx == f->index) {
45362306a36Sopenharmony_ci			f->pixelformat = cal_formats[i].fourcc;
45462306a36Sopenharmony_ci			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
45562306a36Sopenharmony_ci			return 0;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		idx++;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return -EINVAL;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
46562306a36Sopenharmony_ci			   const struct cal_format_info **info)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct v4l2_pix_format *format = &f->fmt.pix;
46862306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
46962306a36Sopenharmony_ci	unsigned int bpp;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/*
47262306a36Sopenharmony_ci	 * Default to the first format if the requested pixel format code isn't
47362306a36Sopenharmony_ci	 * supported.
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat);
47662306a36Sopenharmony_ci	if (!fmtinfo)
47762306a36Sopenharmony_ci		fmtinfo = &cal_formats[0];
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/*
48062306a36Sopenharmony_ci	 * Clamp the size, update the pixel format. The field and colorspace are
48162306a36Sopenharmony_ci	 * accepted as-is, except for V4L2_FIELD_ANY that is turned into
48262306a36Sopenharmony_ci	 * V4L2_FIELD_NONE.
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci	bpp = ALIGN(fmtinfo->bpp, 8);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	format->width = clamp_t(unsigned int, format->width,
48762306a36Sopenharmony_ci				CAL_MIN_WIDTH_BYTES * 8 / bpp,
48862306a36Sopenharmony_ci				CAL_MAX_WIDTH_BYTES * 8 / bpp);
48962306a36Sopenharmony_ci	format->height = clamp_t(unsigned int, format->height,
49062306a36Sopenharmony_ci				 CAL_MIN_HEIGHT_LINES, CAL_MAX_HEIGHT_LINES);
49162306a36Sopenharmony_ci	format->pixelformat = fmtinfo->fourcc;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (format->field == V4L2_FIELD_ANY)
49462306a36Sopenharmony_ci		format->field = V4L2_FIELD_NONE;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * Calculate the number of bytes per line and the image size. The
49862306a36Sopenharmony_ci	 * hardware stores the stride as a number of 16 bytes words, in a
49962306a36Sopenharmony_ci	 * signed 15-bit value. Only 14 bits are thus usable.
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	format->bytesperline = ALIGN(clamp(format->bytesperline,
50262306a36Sopenharmony_ci					   format->width * bpp / 8,
50362306a36Sopenharmony_ci					   ((1U << 14) - 1) * 16), 16);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	format->sizeimage = format->height * format->bytesperline;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	format->colorspace = ctx->v_fmt.fmt.pix.colorspace;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (info)
51062306a36Sopenharmony_ci		*info = fmtinfo;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n",
51362306a36Sopenharmony_ci		__func__, fourcc_to_str(format->pixelformat),
51462306a36Sopenharmony_ci		format->width, format->height,
51562306a36Sopenharmony_ci		format->bytesperline, format->sizeimage);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int cal_mc_try_fmt_vid_cap(struct file *file, void *priv,
51962306a36Sopenharmony_ci				  struct v4l2_format *f)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	cal_mc_try_fmt(ctx, f, NULL);
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int cal_mc_s_fmt_vid_cap(struct file *file, void *priv,
52862306a36Sopenharmony_ci				struct v4l2_format *f)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
53162306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (vb2_is_busy(&ctx->vb_vidq)) {
53462306a36Sopenharmony_ci		ctx_dbg(3, ctx, "%s device busy\n", __func__);
53562306a36Sopenharmony_ci		return -EBUSY;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	cal_mc_try_fmt(ctx, f, &fmtinfo);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ctx->v_fmt = *f;
54162306a36Sopenharmony_ci	ctx->fmtinfo = fmtinfo;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int cal_mc_enum_framesizes(struct file *file, void *fh,
54762306a36Sopenharmony_ci				  struct v4l2_frmsizeenum *fsize)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct cal_ctx *ctx = video_drvdata(file);
55062306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
55162306a36Sopenharmony_ci	unsigned int bpp;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (fsize->index > 0)
55462306a36Sopenharmony_ci		return -EINVAL;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	fmtinfo = cal_format_by_fourcc(fsize->pixel_format);
55762306a36Sopenharmony_ci	if (!fmtinfo) {
55862306a36Sopenharmony_ci		ctx_dbg(3, ctx, "Invalid pixel format 0x%08x\n",
55962306a36Sopenharmony_ci			fsize->pixel_format);
56062306a36Sopenharmony_ci		return -EINVAL;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	bpp = ALIGN(fmtinfo->bpp, 8);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
56662306a36Sopenharmony_ci	fsize->stepwise.min_width = CAL_MIN_WIDTH_BYTES * 8 / bpp;
56762306a36Sopenharmony_ci	fsize->stepwise.max_width = CAL_MAX_WIDTH_BYTES * 8 / bpp;
56862306a36Sopenharmony_ci	fsize->stepwise.step_width = 64 / bpp;
56962306a36Sopenharmony_ci	fsize->stepwise.min_height = CAL_MIN_HEIGHT_LINES;
57062306a36Sopenharmony_ci	fsize->stepwise.max_height = CAL_MAX_HEIGHT_LINES;
57162306a36Sopenharmony_ci	fsize->stepwise.step_height = 1;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return 0;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cal_ioctl_mc_ops = {
57762306a36Sopenharmony_ci	.vidioc_querycap      = cal_querycap,
57862306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap  = cal_mc_enum_fmt_vid_cap,
57962306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap     = cal_g_fmt_vid_cap,
58062306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap   = cal_mc_try_fmt_vid_cap,
58162306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap     = cal_mc_s_fmt_vid_cap,
58262306a36Sopenharmony_ci	.vidioc_enum_framesizes   = cal_mc_enum_framesizes,
58362306a36Sopenharmony_ci	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
58462306a36Sopenharmony_ci	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
58562306a36Sopenharmony_ci	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
58662306a36Sopenharmony_ci	.vidioc_querybuf      = vb2_ioctl_querybuf,
58762306a36Sopenharmony_ci	.vidioc_qbuf          = vb2_ioctl_qbuf,
58862306a36Sopenharmony_ci	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
58962306a36Sopenharmony_ci	.vidioc_expbuf        = vb2_ioctl_expbuf,
59062306a36Sopenharmony_ci	.vidioc_streamon      = vb2_ioctl_streamon,
59162306a36Sopenharmony_ci	.vidioc_streamoff     = vb2_ioctl_streamoff,
59262306a36Sopenharmony_ci	.vidioc_log_status    = v4l2_ctrl_log_status,
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/* ------------------------------------------------------------------
59662306a36Sopenharmony_ci *	videobuf2 Common Operations
59762306a36Sopenharmony_ci * ------------------------------------------------------------------
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int cal_queue_setup(struct vb2_queue *vq,
60162306a36Sopenharmony_ci			   unsigned int *nbuffers, unsigned int *nplanes,
60262306a36Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct cal_ctx *ctx = vb2_get_drv_priv(vq);
60562306a36Sopenharmony_ci	unsigned int size = ctx->v_fmt.fmt.pix.sizeimage;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (vq->num_buffers + *nbuffers < 3)
60862306a36Sopenharmony_ci		*nbuffers = 3 - vq->num_buffers;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (*nplanes) {
61162306a36Sopenharmony_ci		if (sizes[0] < size)
61262306a36Sopenharmony_ci			return -EINVAL;
61362306a36Sopenharmony_ci		size = sizes[0];
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	*nplanes = 1;
61762306a36Sopenharmony_ci	sizes[0] = size;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int cal_buffer_prepare(struct vb2_buffer *vb)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
62762306a36Sopenharmony_ci	struct cal_buffer *buf = container_of(vb, struct cal_buffer,
62862306a36Sopenharmony_ci					      vb.vb2_buf);
62962306a36Sopenharmony_ci	unsigned long size;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	size = ctx->v_fmt.fmt.pix.sizeimage;
63262306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
63362306a36Sopenharmony_ci		ctx_err(ctx,
63462306a36Sopenharmony_ci			"data will not fit into plane (%lu < %lu)\n",
63562306a36Sopenharmony_ci			vb2_plane_size(vb, 0), size);
63662306a36Sopenharmony_ci		return -EINVAL;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void cal_buffer_queue(struct vb2_buffer *vb)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
64662306a36Sopenharmony_ci	struct cal_buffer *buf = container_of(vb, struct cal_buffer,
64762306a36Sopenharmony_ci					      vb.vb2_buf);
64862306a36Sopenharmony_ci	unsigned long flags;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* recheck locking */
65162306a36Sopenharmony_ci	spin_lock_irqsave(&ctx->dma.lock, flags);
65262306a36Sopenharmony_ci	list_add_tail(&buf->list, &ctx->dma.queue);
65362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ctx->dma.lock, flags);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void cal_release_buffers(struct cal_ctx *ctx,
65762306a36Sopenharmony_ci				enum vb2_buffer_state state)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct cal_buffer *buf, *tmp;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Release all queued buffers. */
66262306a36Sopenharmony_ci	spin_lock_irq(&ctx->dma.lock);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) {
66562306a36Sopenharmony_ci		list_del(&buf->list);
66662306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (ctx->dma.pending) {
67062306a36Sopenharmony_ci		vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state);
67162306a36Sopenharmony_ci		ctx->dma.pending = NULL;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (ctx->dma.active) {
67562306a36Sopenharmony_ci		vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state);
67662306a36Sopenharmony_ci		ctx->dma.active = NULL;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	spin_unlock_irq(&ctx->dma.lock);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/* ------------------------------------------------------------------
68362306a36Sopenharmony_ci *	videobuf2 Operations
68462306a36Sopenharmony_ci * ------------------------------------------------------------------
68562306a36Sopenharmony_ci */
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic int cal_video_check_format(struct cal_ctx *ctx)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	const struct v4l2_mbus_framefmt *format;
69062306a36Sopenharmony_ci	struct v4l2_subdev_state *state;
69162306a36Sopenharmony_ci	struct media_pad *remote_pad;
69262306a36Sopenharmony_ci	int ret = 0;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	remote_pad = media_pad_remote_pad_first(&ctx->pad);
69562306a36Sopenharmony_ci	if (!remote_pad)
69662306a36Sopenharmony_ci		return -ENODEV;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index);
70162306a36Sopenharmony_ci	if (!format) {
70262306a36Sopenharmony_ci		ret = -EINVAL;
70362306a36Sopenharmony_ci		goto out;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (ctx->fmtinfo->code != format->code ||
70762306a36Sopenharmony_ci	    ctx->v_fmt.fmt.pix.height != format->height ||
70862306a36Sopenharmony_ci	    ctx->v_fmt.fmt.pix.width != format->width ||
70962306a36Sopenharmony_ci	    ctx->v_fmt.fmt.pix.field != format->field) {
71062306a36Sopenharmony_ci		ret = -EPIPE;
71162306a36Sopenharmony_ci		goto out;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ciout:
71562306a36Sopenharmony_ci	v4l2_subdev_unlock_state(state);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return ret;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct cal_ctx *ctx = vb2_get_drv_priv(vq);
72362306a36Sopenharmony_ci	struct cal_buffer *buf;
72462306a36Sopenharmony_ci	dma_addr_t addr;
72562306a36Sopenharmony_ci	int ret;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	ret = video_device_pipeline_alloc_start(&ctx->vdev);
72862306a36Sopenharmony_ci	if (ret < 0) {
72962306a36Sopenharmony_ci		ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
73062306a36Sopenharmony_ci		goto error_release_buffers;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/*
73462306a36Sopenharmony_ci	 * Verify that the currently configured format matches the output of
73562306a36Sopenharmony_ci	 * the connected CAMERARX.
73662306a36Sopenharmony_ci	 */
73762306a36Sopenharmony_ci	ret = cal_video_check_format(ctx);
73862306a36Sopenharmony_ci	if (ret < 0) {
73962306a36Sopenharmony_ci		ctx_dbg(3, ctx,
74062306a36Sopenharmony_ci			"Format mismatch between CAMERARX and video node\n");
74162306a36Sopenharmony_ci		goto error_pipeline;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	ret = cal_ctx_prepare(ctx);
74562306a36Sopenharmony_ci	if (ret) {
74662306a36Sopenharmony_ci		ctx_err(ctx, "Failed to prepare context: %d\n", ret);
74762306a36Sopenharmony_ci		goto error_pipeline;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	spin_lock_irq(&ctx->dma.lock);
75162306a36Sopenharmony_ci	buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list);
75262306a36Sopenharmony_ci	ctx->dma.active = buf;
75362306a36Sopenharmony_ci	list_del(&buf->list);
75462306a36Sopenharmony_ci	spin_unlock_irq(&ctx->dma.lock);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(ctx->cal->dev);
75962306a36Sopenharmony_ci	if (ret < 0)
76062306a36Sopenharmony_ci		goto error_pipeline;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	cal_ctx_set_dma_addr(ctx, addr);
76362306a36Sopenharmony_ci	cal_ctx_start(ctx);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1);
76662306a36Sopenharmony_ci	if (ret)
76762306a36Sopenharmony_ci		goto error_stop;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (cal_debug >= 4)
77062306a36Sopenharmony_ci		cal_quickdump_regs(ctx->cal);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	return 0;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cierror_stop:
77562306a36Sopenharmony_ci	cal_ctx_stop(ctx);
77662306a36Sopenharmony_ci	pm_runtime_put_sync(ctx->cal->dev);
77762306a36Sopenharmony_ci	cal_ctx_unprepare(ctx);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cierror_pipeline:
78062306a36Sopenharmony_ci	video_device_pipeline_stop(&ctx->vdev);
78162306a36Sopenharmony_cierror_release_buffers:
78262306a36Sopenharmony_ci	cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return ret;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic void cal_stop_streaming(struct vb2_queue *vq)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct cal_ctx *ctx = vb2_get_drv_priv(vq);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	cal_ctx_stop(ctx);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	pm_runtime_put_sync(ctx->cal->dev);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	cal_ctx_unprepare(ctx);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	video_device_pipeline_stop(&ctx->vdev);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic const struct vb2_ops cal_video_qops = {
80562306a36Sopenharmony_ci	.queue_setup		= cal_queue_setup,
80662306a36Sopenharmony_ci	.buf_prepare		= cal_buffer_prepare,
80762306a36Sopenharmony_ci	.buf_queue		= cal_buffer_queue,
80862306a36Sopenharmony_ci	.start_streaming	= cal_start_streaming,
80962306a36Sopenharmony_ci	.stop_streaming		= cal_stop_streaming,
81062306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
81162306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
81262306a36Sopenharmony_ci};
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/* ------------------------------------------------------------------
81562306a36Sopenharmony_ci *	V4L2 Initialization and Registration
81662306a36Sopenharmony_ci * ------------------------------------------------------------------
81762306a36Sopenharmony_ci */
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic const struct v4l2_file_operations cal_fops = {
82062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
82162306a36Sopenharmony_ci	.open           = v4l2_fh_open,
82262306a36Sopenharmony_ci	.release        = vb2_fop_release,
82362306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
82462306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
82562306a36Sopenharmony_ci	.mmap           = vb2_fop_mmap,
82662306a36Sopenharmony_ci};
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct v4l2_mbus_framefmt mbus_fmt;
83162306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
83262306a36Sopenharmony_ci	unsigned int i, j, k;
83362306a36Sopenharmony_ci	int ret = 0;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Enumerate sub device formats and enable all matching local formats */
83662306a36Sopenharmony_ci	ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats,
83762306a36Sopenharmony_ci				       sizeof(*ctx->active_fmt), GFP_KERNEL);
83862306a36Sopenharmony_ci	if (!ctx->active_fmt)
83962306a36Sopenharmony_ci		return -ENOMEM;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	ctx->num_active_fmt = 0;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	for (j = 0, i = 0; ; ++j) {
84462306a36Sopenharmony_ci		struct v4l2_subdev_mbus_code_enum mbus_code = {
84562306a36Sopenharmony_ci			.index = j,
84662306a36Sopenharmony_ci			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
84762306a36Sopenharmony_ci		};
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code,
85062306a36Sopenharmony_ci				       NULL, &mbus_code);
85162306a36Sopenharmony_ci		if (ret == -EINVAL)
85262306a36Sopenharmony_ci			break;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		if (ret) {
85562306a36Sopenharmony_ci			ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n",
85662306a36Sopenharmony_ci				ctx->phy->source->name, ret);
85762306a36Sopenharmony_ci			return ret;
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		ctx_dbg(2, ctx,
86162306a36Sopenharmony_ci			"subdev %s: code: %04x idx: %u\n",
86262306a36Sopenharmony_ci			ctx->phy->source->name, mbus_code.code, j);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		for (k = 0; k < cal_num_formats; k++) {
86562306a36Sopenharmony_ci			fmtinfo = &cal_formats[k];
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci			if (mbus_code.code == fmtinfo->code) {
86862306a36Sopenharmony_ci				ctx->active_fmt[i] = fmtinfo;
86962306a36Sopenharmony_ci				ctx_dbg(2, ctx,
87062306a36Sopenharmony_ci					"matched fourcc: %s: code: %04x idx: %u\n",
87162306a36Sopenharmony_ci					fourcc_to_str(fmtinfo->fourcc),
87262306a36Sopenharmony_ci					fmtinfo->code, i);
87362306a36Sopenharmony_ci				ctx->num_active_fmt = ++i;
87462306a36Sopenharmony_ci			}
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (i == 0) {
87962306a36Sopenharmony_ci		ctx_err(ctx, "No suitable format reported by subdev %s\n",
88062306a36Sopenharmony_ci			ctx->phy->source->name);
88162306a36Sopenharmony_ci		return -EINVAL;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	ret = __subdev_get_format(ctx, &mbus_fmt);
88562306a36Sopenharmony_ci	if (ret)
88662306a36Sopenharmony_ci		return ret;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	fmtinfo = find_format_by_code(ctx, mbus_fmt.code);
88962306a36Sopenharmony_ci	if (!fmtinfo) {
89062306a36Sopenharmony_ci		ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
89162306a36Sopenharmony_ci			mbus_fmt.code);
89262306a36Sopenharmony_ci		return -EINVAL;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* Save current format */
89662306a36Sopenharmony_ci	v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
89762306a36Sopenharmony_ci	ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
89862306a36Sopenharmony_ci	ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc;
89962306a36Sopenharmony_ci	cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
90062306a36Sopenharmony_ci	ctx->fmtinfo = fmtinfo;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic int cal_ctx_v4l2_init_mc_format(struct cal_ctx *ctx)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
90862306a36Sopenharmony_ci	struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_1X16);
91162306a36Sopenharmony_ci	if (!fmtinfo)
91262306a36Sopenharmony_ci		return -EINVAL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	pix_fmt->width = 640;
91562306a36Sopenharmony_ci	pix_fmt->height = 480;
91662306a36Sopenharmony_ci	pix_fmt->field = V4L2_FIELD_NONE;
91762306a36Sopenharmony_ci	pix_fmt->colorspace = V4L2_COLORSPACE_SRGB;
91862306a36Sopenharmony_ci	pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
91962306a36Sopenharmony_ci	pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
92062306a36Sopenharmony_ci	pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
92162306a36Sopenharmony_ci	pix_fmt->pixelformat = fmtinfo->fourcc;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* Save current format */
92662306a36Sopenharmony_ci	cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
92762306a36Sopenharmony_ci	ctx->fmtinfo = fmtinfo;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return 0;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciint cal_ctx_v4l2_register(struct cal_ctx *ctx)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct video_device *vfd = &ctx->vdev;
93562306a36Sopenharmony_ci	int ret;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (!cal_mc_api) {
93862306a36Sopenharmony_ci		struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		ret = cal_ctx_v4l2_init_formats(ctx);
94162306a36Sopenharmony_ci		if (ret) {
94262306a36Sopenharmony_ci			ctx_err(ctx, "Failed to init formats: %d\n", ret);
94362306a36Sopenharmony_ci			return ret;
94462306a36Sopenharmony_ci		}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		ret = v4l2_ctrl_add_handler(hdl, ctx->phy->source->ctrl_handler,
94762306a36Sopenharmony_ci					    NULL, true);
94862306a36Sopenharmony_ci		if (ret < 0) {
94962306a36Sopenharmony_ci			ctx_err(ctx, "Failed to add source ctrl handler\n");
95062306a36Sopenharmony_ci			return ret;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci	} else {
95362306a36Sopenharmony_ci		ret = cal_ctx_v4l2_init_mc_format(ctx);
95462306a36Sopenharmony_ci		if (ret) {
95562306a36Sopenharmony_ci			ctx_err(ctx, "Failed to init format: %d\n", ret);
95662306a36Sopenharmony_ci			return ret;
95762306a36Sopenharmony_ci		}
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr);
96162306a36Sopenharmony_ci	if (ret < 0) {
96262306a36Sopenharmony_ci		ctx_err(ctx, "Failed to register video device\n");
96362306a36Sopenharmony_ci		return ret;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	ret = media_create_pad_link(&ctx->phy->subdev.entity,
96762306a36Sopenharmony_ci				    CAL_CAMERARX_PAD_FIRST_SOURCE,
96862306a36Sopenharmony_ci				    &vfd->entity, 0,
96962306a36Sopenharmony_ci				    MEDIA_LNK_FL_IMMUTABLE |
97062306a36Sopenharmony_ci				    MEDIA_LNK_FL_ENABLED);
97162306a36Sopenharmony_ci	if (ret) {
97262306a36Sopenharmony_ci		ctx_err(ctx, "Failed to create media link for context %u\n",
97362306a36Sopenharmony_ci			ctx->dma_ctx);
97462306a36Sopenharmony_ci		video_unregister_device(vfd);
97562306a36Sopenharmony_ci		return ret;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	ctx_info(ctx, "V4L2 device registered as %s\n",
97962306a36Sopenharmony_ci		 video_device_node_name(vfd));
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	return 0;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_civoid cal_ctx_v4l2_unregister(struct cal_ctx *ctx)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	ctx_dbg(1, ctx, "unregistering %s\n",
98762306a36Sopenharmony_ci		video_device_node_name(&ctx->vdev));
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	video_unregister_device(&ctx->vdev);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ciint cal_ctx_v4l2_init(struct cal_ctx *ctx)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct video_device *vfd = &ctx->vdev;
99562306a36Sopenharmony_ci	struct vb2_queue *q = &ctx->vb_vidq;
99662306a36Sopenharmony_ci	int ret;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->dma.queue);
99962306a36Sopenharmony_ci	spin_lock_init(&ctx->dma.lock);
100062306a36Sopenharmony_ci	mutex_init(&ctx->mutex);
100162306a36Sopenharmony_ci	init_waitqueue_head(&ctx->dma.wait);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/* Initialize the vb2 queue. */
100462306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
100562306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_DMABUF;
100662306a36Sopenharmony_ci	q->drv_priv = ctx;
100762306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cal_buffer);
100862306a36Sopenharmony_ci	q->ops = &cal_video_qops;
100962306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_contig_memops;
101062306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
101162306a36Sopenharmony_ci	q->lock = &ctx->mutex;
101262306a36Sopenharmony_ci	q->min_buffers_needed = 3;
101362306a36Sopenharmony_ci	q->dev = ctx->cal->dev;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	ret = vb2_queue_init(q);
101662306a36Sopenharmony_ci	if (ret)
101762306a36Sopenharmony_ci		return ret;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* Initialize the video device and media entity. */
102062306a36Sopenharmony_ci	vfd->fops = &cal_fops;
102162306a36Sopenharmony_ci	vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
102262306a36Sopenharmony_ci			 | (cal_mc_api ? V4L2_CAP_IO_MC : 0);
102362306a36Sopenharmony_ci	vfd->v4l2_dev = &ctx->cal->v4l2_dev;
102462306a36Sopenharmony_ci	vfd->queue = q;
102562306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->dma_ctx);
102662306a36Sopenharmony_ci	vfd->release = video_device_release_empty;
102762306a36Sopenharmony_ci	vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_legacy_ops;
102862306a36Sopenharmony_ci	vfd->lock = &ctx->mutex;
102962306a36Sopenharmony_ci	video_set_drvdata(vfd, ctx);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	ctx->pad.flags = MEDIA_PAD_FL_SINK;
103262306a36Sopenharmony_ci	ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad);
103362306a36Sopenharmony_ci	if (ret < 0)
103462306a36Sopenharmony_ci		return ret;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (!cal_mc_api) {
103762306a36Sopenharmony_ci		/* Initialize the control handler. */
103862306a36Sopenharmony_ci		struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		ret = v4l2_ctrl_handler_init(hdl, 11);
104162306a36Sopenharmony_ci		if (ret < 0) {
104262306a36Sopenharmony_ci			ctx_err(ctx, "Failed to init ctrl handler\n");
104362306a36Sopenharmony_ci			goto error;
104462306a36Sopenharmony_ci		}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		vfd->ctrl_handler = hdl;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	return 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cierror:
105262306a36Sopenharmony_ci	media_entity_cleanup(&vfd->entity);
105362306a36Sopenharmony_ci	return ret;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_civoid cal_ctx_v4l2_cleanup(struct cal_ctx *ctx)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	if (!cal_mc_api)
105962306a36Sopenharmony_ci		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	media_entity_cleanup(&ctx->vdev.entity);
106262306a36Sopenharmony_ci}
1063