162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TI Camera Access Layer (CAL) - Driver
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/clk.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/videodev2.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <media/media-device.h>
2462306a36Sopenharmony_ci#include <media/v4l2-async.h>
2562306a36Sopenharmony_ci#include <media/v4l2-common.h>
2662306a36Sopenharmony_ci#include <media/v4l2-device.h>
2762306a36Sopenharmony_ci#include <media/videobuf2-core.h>
2862306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "cal.h"
3162306a36Sopenharmony_ci#include "cal_regs.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciMODULE_DESCRIPTION("TI CAL driver");
3462306a36Sopenharmony_ciMODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
3562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3662306a36Sopenharmony_ciMODULE_VERSION("0.1.0");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciint cal_video_nr = -1;
3962306a36Sopenharmony_cimodule_param_named(video_nr, cal_video_nr, uint, 0644);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciunsigned int cal_debug;
4362306a36Sopenharmony_cimodule_param_named(debug, cal_debug, uint, 0644);
4462306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "activates debug info");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_TI_CAL_MC
4762306a36Sopenharmony_ci#define CAL_MC_API_DEFAULT 1
4862306a36Sopenharmony_ci#else
4962306a36Sopenharmony_ci#define CAL_MC_API_DEFAULT 0
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cibool cal_mc_api = CAL_MC_API_DEFAULT;
5362306a36Sopenharmony_cimodule_param_named(mc_api, cal_mc_api, bool, 0444);
5462306a36Sopenharmony_ciMODULE_PARM_DESC(mc_api, "activates the MC API");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* ------------------------------------------------------------------
5762306a36Sopenharmony_ci *	Format Handling
5862306a36Sopenharmony_ci * ------------------------------------------------------------------
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciconst struct cal_format_info cal_formats[] = {
6262306a36Sopenharmony_ci	{
6362306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_YUYV,
6462306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_YUYV8_1X16,
6562306a36Sopenharmony_ci		.bpp		= 16,
6662306a36Sopenharmony_ci	}, {
6762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_UYVY,
6862306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
6962306a36Sopenharmony_ci		.bpp		= 16,
7062306a36Sopenharmony_ci	}, {
7162306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_YVYU,
7262306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_YVYU8_1X16,
7362306a36Sopenharmony_ci		.bpp		= 16,
7462306a36Sopenharmony_ci	}, {
7562306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_VYUY,
7662306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_VYUY8_1X16,
7762306a36Sopenharmony_ci		.bpp		= 16,
7862306a36Sopenharmony_ci	}, {
7962306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB565,
8062306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_RGB565_1X16,
8162306a36Sopenharmony_ci		.bpp		= 16,
8262306a36Sopenharmony_ci	}, {
8362306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SBGGR8,
8462306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
8562306a36Sopenharmony_ci		.bpp		= 8,
8662306a36Sopenharmony_ci	}, {
8762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGBRG8,
8862306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
8962306a36Sopenharmony_ci		.bpp		= 8,
9062306a36Sopenharmony_ci	}, {
9162306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGRBG8,
9262306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
9362306a36Sopenharmony_ci		.bpp		= 8,
9462306a36Sopenharmony_ci	}, {
9562306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SRGGB8,
9662306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
9762306a36Sopenharmony_ci		.bpp		= 8,
9862306a36Sopenharmony_ci	}, {
9962306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SBGGR10,
10062306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
10162306a36Sopenharmony_ci		.bpp		= 10,
10262306a36Sopenharmony_ci	}, {
10362306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGBRG10,
10462306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGBRG10_1X10,
10562306a36Sopenharmony_ci		.bpp		= 10,
10662306a36Sopenharmony_ci	}, {
10762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGRBG10,
10862306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGRBG10_1X10,
10962306a36Sopenharmony_ci		.bpp		= 10,
11062306a36Sopenharmony_ci	}, {
11162306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SRGGB10,
11262306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SRGGB10_1X10,
11362306a36Sopenharmony_ci		.bpp		= 10,
11462306a36Sopenharmony_ci	}, {
11562306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SBGGR12,
11662306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SBGGR12_1X12,
11762306a36Sopenharmony_ci		.bpp		= 12,
11862306a36Sopenharmony_ci	}, {
11962306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGBRG12,
12062306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGBRG12_1X12,
12162306a36Sopenharmony_ci		.bpp		= 12,
12262306a36Sopenharmony_ci	}, {
12362306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SGRBG12,
12462306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SGRBG12_1X12,
12562306a36Sopenharmony_ci		.bpp		= 12,
12662306a36Sopenharmony_ci	}, {
12762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_SRGGB12,
12862306a36Sopenharmony_ci		.code		= MEDIA_BUS_FMT_SRGGB12_1X12,
12962306a36Sopenharmony_ci		.bpp		= 12,
13062306a36Sopenharmony_ci	},
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciconst unsigned int cal_num_formats = ARRAY_SIZE(cal_formats);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciconst struct cal_format_info *cal_format_by_fourcc(u32 fourcc)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned int i;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) {
14062306a36Sopenharmony_ci		if (cal_formats[i].fourcc == fourcc)
14162306a36Sopenharmony_ci			return &cal_formats[i];
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return NULL;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciconst struct cal_format_info *cal_format_by_code(u32 code)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	unsigned int i;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) {
15262306a36Sopenharmony_ci		if (cal_formats[i].code == code)
15362306a36Sopenharmony_ci			return &cal_formats[i];
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return NULL;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* ------------------------------------------------------------------
16062306a36Sopenharmony_ci *	Platform Data
16162306a36Sopenharmony_ci * ------------------------------------------------------------------
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const struct cal_camerarx_data dra72x_cal_camerarx[] = {
16562306a36Sopenharmony_ci	{
16662306a36Sopenharmony_ci		.fields = {
16762306a36Sopenharmony_ci			[F_CTRLCLKEN] = { 10, 10 },
16862306a36Sopenharmony_ci			[F_CAMMODE] = { 11, 12 },
16962306a36Sopenharmony_ci			[F_LANEENABLE] = { 13, 16 },
17062306a36Sopenharmony_ci			[F_CSI_MODE] = { 17, 17 },
17162306a36Sopenharmony_ci		},
17262306a36Sopenharmony_ci		.num_lanes = 4,
17362306a36Sopenharmony_ci	},
17462306a36Sopenharmony_ci	{
17562306a36Sopenharmony_ci		.fields = {
17662306a36Sopenharmony_ci			[F_CTRLCLKEN] = { 0, 0 },
17762306a36Sopenharmony_ci			[F_CAMMODE] = { 1, 2 },
17862306a36Sopenharmony_ci			[F_LANEENABLE] = { 3, 4 },
17962306a36Sopenharmony_ci			[F_CSI_MODE] = { 5, 5 },
18062306a36Sopenharmony_ci		},
18162306a36Sopenharmony_ci		.num_lanes = 2,
18262306a36Sopenharmony_ci	},
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct cal_data dra72x_cal_data = {
18662306a36Sopenharmony_ci	.camerarx = dra72x_cal_camerarx,
18762306a36Sopenharmony_ci	.num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx),
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic const struct cal_data dra72x_es1_cal_data = {
19162306a36Sopenharmony_ci	.camerarx = dra72x_cal_camerarx,
19262306a36Sopenharmony_ci	.num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx),
19362306a36Sopenharmony_ci	.flags = DRA72_CAL_PRE_ES2_LDO_DISABLE,
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic const struct cal_camerarx_data dra76x_cal_csi_phy[] = {
19762306a36Sopenharmony_ci	{
19862306a36Sopenharmony_ci		.fields = {
19962306a36Sopenharmony_ci			[F_CTRLCLKEN] = { 8, 8 },
20062306a36Sopenharmony_ci			[F_CAMMODE] = { 9, 10 },
20162306a36Sopenharmony_ci			[F_CSI_MODE] = { 11, 11 },
20262306a36Sopenharmony_ci			[F_LANEENABLE] = { 27, 31 },
20362306a36Sopenharmony_ci		},
20462306a36Sopenharmony_ci		.num_lanes = 5,
20562306a36Sopenharmony_ci	},
20662306a36Sopenharmony_ci	{
20762306a36Sopenharmony_ci		.fields = {
20862306a36Sopenharmony_ci			[F_CTRLCLKEN] = { 0, 0 },
20962306a36Sopenharmony_ci			[F_CAMMODE] = { 1, 2 },
21062306a36Sopenharmony_ci			[F_CSI_MODE] = { 3, 3 },
21162306a36Sopenharmony_ci			[F_LANEENABLE] = { 24, 26 },
21262306a36Sopenharmony_ci		},
21362306a36Sopenharmony_ci		.num_lanes = 3,
21462306a36Sopenharmony_ci	},
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic const struct cal_data dra76x_cal_data = {
21862306a36Sopenharmony_ci	.camerarx = dra76x_cal_csi_phy,
21962306a36Sopenharmony_ci	.num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy),
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const struct cal_camerarx_data am654_cal_csi_phy[] = {
22362306a36Sopenharmony_ci	{
22462306a36Sopenharmony_ci		.fields = {
22562306a36Sopenharmony_ci			[F_CTRLCLKEN] = { 15, 15 },
22662306a36Sopenharmony_ci			[F_CAMMODE] = { 24, 25 },
22762306a36Sopenharmony_ci			[F_LANEENABLE] = { 0, 4 },
22862306a36Sopenharmony_ci		},
22962306a36Sopenharmony_ci		.num_lanes = 5,
23062306a36Sopenharmony_ci	},
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic const struct cal_data am654_cal_data = {
23462306a36Sopenharmony_ci	.camerarx = am654_cal_csi_phy,
23562306a36Sopenharmony_ci	.num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy),
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* ------------------------------------------------------------------
23962306a36Sopenharmony_ci *	I/O Register Accessors
24062306a36Sopenharmony_ci * ------------------------------------------------------------------
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_civoid cal_quickdump_regs(struct cal_dev *cal)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned int i;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start);
24862306a36Sopenharmony_ci	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
24962306a36Sopenharmony_ci		       (__force const void *)cal->base,
25062306a36Sopenharmony_ci		       resource_size(cal->res), false);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; ++i) {
25362306a36Sopenharmony_ci		struct cal_camerarx *phy = cal->phy[i];
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i,
25662306a36Sopenharmony_ci			 &phy->res->start);
25762306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
25862306a36Sopenharmony_ci			       (__force const void *)phy->base,
25962306a36Sopenharmony_ci			       resource_size(phy->res),
26062306a36Sopenharmony_ci			       false);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/* ------------------------------------------------------------------
26562306a36Sopenharmony_ci *	Context Management
26662306a36Sopenharmony_ci * ------------------------------------------------------------------
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci#define CAL_MAX_PIX_PROC 4
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int cal_reserve_pix_proc(struct cal_dev *cal)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	unsigned long ret;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	spin_lock(&cal->v4l2_dev.lock);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ret = find_first_zero_bit(&cal->reserved_pix_proc_mask, CAL_MAX_PIX_PROC);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (ret == CAL_MAX_PIX_PROC) {
28062306a36Sopenharmony_ci		spin_unlock(&cal->v4l2_dev.lock);
28162306a36Sopenharmony_ci		return -ENOSPC;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	cal->reserved_pix_proc_mask |= BIT(ret);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	spin_unlock(&cal->v4l2_dev.lock);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return ret;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void cal_release_pix_proc(struct cal_dev *cal, unsigned int pix_proc_num)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	spin_lock(&cal->v4l2_dev.lock);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	cal->reserved_pix_proc_mask &= ~BIT(pix_proc_num);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	spin_unlock(&cal->v4l2_dev.lock);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic void cal_ctx_csi2_config(struct cal_ctx *ctx)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	u32 val;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	val = cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx));
30562306a36Sopenharmony_ci	cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK);
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * DT type: MIPI CSI-2 Specs
30862306a36Sopenharmony_ci	 *   0x1: All - DT filter is disabled
30962306a36Sopenharmony_ci	 *  0x24: RGB888 1 pixel  = 3 bytes
31062306a36Sopenharmony_ci	 *  0x2B: RAW10  4 pixels = 5 bytes
31162306a36Sopenharmony_ci	 *  0x2A: RAW8   1 pixel  = 1 byte
31262306a36Sopenharmony_ci	 *  0x1E: YUV422 2 pixels = 4 bytes
31362306a36Sopenharmony_ci	 */
31462306a36Sopenharmony_ci	cal_set_field(&val, ctx->datatype, CAL_CSI2_CTX_DT_MASK);
31562306a36Sopenharmony_ci	cal_set_field(&val, ctx->vc, CAL_CSI2_CTX_VC_MASK);
31662306a36Sopenharmony_ci	cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK);
31762306a36Sopenharmony_ci	cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK);
31862306a36Sopenharmony_ci	cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
31962306a36Sopenharmony_ci		      CAL_CSI2_CTX_PACK_MODE_MASK);
32062306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), val);
32162306a36Sopenharmony_ci	ctx_dbg(3, ctx, "CAL_CSI2_CTX(%u, %u) = 0x%08x\n",
32262306a36Sopenharmony_ci		ctx->phy->instance, ctx->csi2_ctx,
32362306a36Sopenharmony_ci		cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx)));
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void cal_ctx_pix_proc_config(struct cal_ctx *ctx)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	u32 val, extract, pack;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	switch (ctx->fmtinfo->bpp) {
33162306a36Sopenharmony_ci	case 8:
33262306a36Sopenharmony_ci		extract = CAL_PIX_PROC_EXTRACT_B8;
33362306a36Sopenharmony_ci		pack = CAL_PIX_PROC_PACK_B8;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case 10:
33662306a36Sopenharmony_ci		extract = CAL_PIX_PROC_EXTRACT_B10_MIPI;
33762306a36Sopenharmony_ci		pack = CAL_PIX_PROC_PACK_B16;
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	case 12:
34062306a36Sopenharmony_ci		extract = CAL_PIX_PROC_EXTRACT_B12_MIPI;
34162306a36Sopenharmony_ci		pack = CAL_PIX_PROC_PACK_B16;
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	case 16:
34462306a36Sopenharmony_ci		extract = CAL_PIX_PROC_EXTRACT_B16_LE;
34562306a36Sopenharmony_ci		pack = CAL_PIX_PROC_PACK_B16;
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		/*
34962306a36Sopenharmony_ci		 * If you see this warning then it means that you added
35062306a36Sopenharmony_ci		 * some new entry in the cal_formats[] array with a different
35162306a36Sopenharmony_ci		 * bit per pixel values then the one supported below.
35262306a36Sopenharmony_ci		 * Either add support for the new bpp value below or adjust
35362306a36Sopenharmony_ci		 * the new entry to use one of the value below.
35462306a36Sopenharmony_ci		 *
35562306a36Sopenharmony_ci		 * Instead of failing here just use 8 bpp as a default.
35662306a36Sopenharmony_ci		 */
35762306a36Sopenharmony_ci		dev_warn_once(ctx->cal->dev,
35862306a36Sopenharmony_ci			      "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n",
35962306a36Sopenharmony_ci			      __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp);
36062306a36Sopenharmony_ci		extract = CAL_PIX_PROC_EXTRACT_B8;
36162306a36Sopenharmony_ci		pack = CAL_PIX_PROC_PACK_B8;
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc));
36662306a36Sopenharmony_ci	cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK);
36762306a36Sopenharmony_ci	cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
36862306a36Sopenharmony_ci	cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
36962306a36Sopenharmony_ci	cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK);
37062306a36Sopenharmony_ci	cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK);
37162306a36Sopenharmony_ci	cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK);
37262306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), val);
37362306a36Sopenharmony_ci	ctx_dbg(3, ctx, "CAL_PIX_PROC(%u) = 0x%08x\n", ctx->pix_proc,
37462306a36Sopenharmony_ci		cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc)));
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void cal_ctx_wr_dma_config(struct cal_ctx *ctx)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline;
38062306a36Sopenharmony_ci	u32 val;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
38362306a36Sopenharmony_ci	cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK);
38462306a36Sopenharmony_ci	cal_set_field(&val, ctx->v_fmt.fmt.pix.height,
38562306a36Sopenharmony_ci		      CAL_WR_DMA_CTRL_YSIZE_MASK);
38662306a36Sopenharmony_ci	cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
38762306a36Sopenharmony_ci		      CAL_WR_DMA_CTRL_DTAG_MASK);
38862306a36Sopenharmony_ci	cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
38962306a36Sopenharmony_ci		      CAL_WR_DMA_CTRL_PATTERN_MASK);
39062306a36Sopenharmony_ci	cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK);
39162306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
39262306a36Sopenharmony_ci	ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->dma_ctx,
39362306a36Sopenharmony_ci		cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)));
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx),
39662306a36Sopenharmony_ci			stride / 16, CAL_WR_DMA_OFST_MASK);
39762306a36Sopenharmony_ci	ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->dma_ctx,
39862306a36Sopenharmony_ci		cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx)));
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx));
40162306a36Sopenharmony_ci	/* 64 bit word means no skipping */
40262306a36Sopenharmony_ci	cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK);
40362306a36Sopenharmony_ci	/*
40462306a36Sopenharmony_ci	 * The XSIZE field is expressed in 64-bit units and prevents overflows
40562306a36Sopenharmony_ci	 * in case of synchronization issues by limiting the number of bytes
40662306a36Sopenharmony_ci	 * written per line.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK);
40962306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx), val);
41062306a36Sopenharmony_ci	ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->dma_ctx,
41162306a36Sopenharmony_ci		cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx)));
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_civoid cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->dma_ctx), addr);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void cal_ctx_wr_dma_enable(struct cal_ctx *ctx)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
42462306a36Sopenharmony_ci		      CAL_WR_DMA_CTRL_MODE_MASK);
42562306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void cal_ctx_wr_dma_disable(struct cal_ctx *ctx)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx));
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS,
43362306a36Sopenharmony_ci		      CAL_WR_DMA_CTRL_MODE_MASK);
43462306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	bool stopped;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	spin_lock_irq(&ctx->dma.lock);
44262306a36Sopenharmony_ci	stopped = ctx->dma.state == CAL_DMA_STOPPED;
44362306a36Sopenharmony_ci	spin_unlock_irq(&ctx->dma.lock);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return stopped;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int
44962306a36Sopenharmony_cical_get_remote_frame_desc_entry(struct cal_ctx *ctx,
45062306a36Sopenharmony_ci				struct v4l2_mbus_frame_desc_entry *entry)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct v4l2_mbus_frame_desc fd;
45362306a36Sopenharmony_ci	struct media_pad *phy_source_pad;
45462306a36Sopenharmony_ci	int ret;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
45762306a36Sopenharmony_ci	if (!phy_source_pad)
45862306a36Sopenharmony_ci		return -ENODEV;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ret = v4l2_subdev_call(&ctx->phy->subdev, pad, get_frame_desc,
46162306a36Sopenharmony_ci			       phy_source_pad->index, &fd);
46262306a36Sopenharmony_ci	if (ret)
46362306a36Sopenharmony_ci		return ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (fd.num_entries != 1)
46662306a36Sopenharmony_ci		return -EINVAL;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	*entry = fd.entry[0];
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ciint cal_ctx_prepare(struct cal_ctx *ctx)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct v4l2_mbus_frame_desc_entry entry;
47662306a36Sopenharmony_ci	int ret;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	ret = cal_get_remote_frame_desc_entry(ctx, &entry);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (ret == -ENOIOCTLCMD) {
48162306a36Sopenharmony_ci		ctx->vc = 0;
48262306a36Sopenharmony_ci		ctx->datatype = CAL_CSI2_CTX_DT_ANY;
48362306a36Sopenharmony_ci	} else if (!ret) {
48462306a36Sopenharmony_ci		ctx_dbg(2, ctx, "Framedesc: len %u, vc %u, dt %#x\n",
48562306a36Sopenharmony_ci			entry.length, entry.bus.csi2.vc, entry.bus.csi2.dt);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		ctx->vc = entry.bus.csi2.vc;
48862306a36Sopenharmony_ci		ctx->datatype = entry.bus.csi2.dt;
48962306a36Sopenharmony_ci	} else {
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ctx->use_pix_proc = !ctx->fmtinfo->meta;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (ctx->use_pix_proc) {
49662306a36Sopenharmony_ci		ret = cal_reserve_pix_proc(ctx->cal);
49762306a36Sopenharmony_ci		if (ret < 0) {
49862306a36Sopenharmony_ci			ctx_err(ctx, "Failed to reserve pix proc: %d\n", ret);
49962306a36Sopenharmony_ci			return ret;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		ctx->pix_proc = ret;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return 0;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_civoid cal_ctx_unprepare(struct cal_ctx *ctx)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	if (ctx->use_pix_proc)
51162306a36Sopenharmony_ci		cal_release_pix_proc(ctx->cal, ctx->pix_proc);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_civoid cal_ctx_start(struct cal_ctx *ctx)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct cal_camerarx *phy = ctx->phy;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/*
51962306a36Sopenharmony_ci	 * Reset the frame number & sequence number, but only if the
52062306a36Sopenharmony_ci	 * virtual channel is not already in use.
52162306a36Sopenharmony_ci	 */
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	spin_lock(&phy->vc_lock);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (phy->vc_enable_count[ctx->vc]++ == 0) {
52662306a36Sopenharmony_ci		phy->vc_frame_number[ctx->vc] = 0;
52762306a36Sopenharmony_ci		phy->vc_sequence[ctx->vc] = 0;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	spin_unlock(&phy->vc_lock);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ctx->dma.state = CAL_DMA_RUNNING;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* Configure the CSI-2, pixel processing and write DMA contexts. */
53562306a36Sopenharmony_ci	cal_ctx_csi2_config(ctx);
53662306a36Sopenharmony_ci	if (ctx->use_pix_proc)
53762306a36Sopenharmony_ci		cal_ctx_pix_proc_config(ctx);
53862306a36Sopenharmony_ci	cal_ctx_wr_dma_config(ctx);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Enable IRQ_WDMA_END and IRQ_WDMA_START. */
54162306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1),
54262306a36Sopenharmony_ci		  CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx));
54362306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2),
54462306a36Sopenharmony_ci		  CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx));
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	cal_ctx_wr_dma_enable(ctx);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_civoid cal_ctx_stop(struct cal_ctx *ctx)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct cal_camerarx *phy = ctx->phy;
55262306a36Sopenharmony_ci	long timeout;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	WARN_ON(phy->vc_enable_count[ctx->vc] == 0);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	spin_lock(&phy->vc_lock);
55762306a36Sopenharmony_ci	phy->vc_enable_count[ctx->vc]--;
55862306a36Sopenharmony_ci	spin_unlock(&phy->vc_lock);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/*
56162306a36Sopenharmony_ci	 * Request DMA stop and wait until it completes. If completion times
56262306a36Sopenharmony_ci	 * out, forcefully disable the DMA.
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	spin_lock_irq(&ctx->dma.lock);
56562306a36Sopenharmony_ci	ctx->dma.state = CAL_DMA_STOP_REQUESTED;
56662306a36Sopenharmony_ci	spin_unlock_irq(&ctx->dma.lock);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx),
56962306a36Sopenharmony_ci				     msecs_to_jiffies(500));
57062306a36Sopenharmony_ci	if (!timeout) {
57162306a36Sopenharmony_ci		ctx_err(ctx, "failed to disable dma cleanly\n");
57262306a36Sopenharmony_ci		cal_ctx_wr_dma_disable(ctx);
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Disable IRQ_WDMA_END and IRQ_WDMA_START. */
57662306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1),
57762306a36Sopenharmony_ci		  CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx));
57862306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2),
57962306a36Sopenharmony_ci		  CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx));
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	ctx->dma.state = CAL_DMA_STOPPED;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Disable CSI2 context */
58462306a36Sopenharmony_ci	cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), 0);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* Disable pix proc */
58762306a36Sopenharmony_ci	if (ctx->use_pix_proc)
58862306a36Sopenharmony_ci		cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), 0);
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/* ------------------------------------------------------------------
59262306a36Sopenharmony_ci *	IRQ Handling
59362306a36Sopenharmony_ci * ------------------------------------------------------------------
59462306a36Sopenharmony_ci */
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/*
59762306a36Sopenharmony_ci * Track a sequence number for each virtual channel, which is shared by
59862306a36Sopenharmony_ci * all contexts using the same virtual channel. This is done using the
59962306a36Sopenharmony_ci * CSI-2 frame number as a base.
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_cistatic void cal_update_seq_number(struct cal_ctx *ctx)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct cal_dev *cal = ctx->cal;
60462306a36Sopenharmony_ci	struct cal_camerarx *phy = ctx->phy;
60562306a36Sopenharmony_ci	u16 prev_frame_num, frame_num;
60662306a36Sopenharmony_ci	u8 vc = ctx->vc;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	frame_num =
60962306a36Sopenharmony_ci		cal_read(cal, CAL_CSI2_STATUS(phy->instance, ctx->csi2_ctx)) &
61062306a36Sopenharmony_ci		0xffff;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (phy->vc_frame_number[vc] != frame_num) {
61362306a36Sopenharmony_ci		prev_frame_num = phy->vc_frame_number[vc];
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		if (prev_frame_num >= frame_num)
61662306a36Sopenharmony_ci			phy->vc_sequence[vc] += 1;
61762306a36Sopenharmony_ci		else
61862306a36Sopenharmony_ci			phy->vc_sequence[vc] += frame_num - prev_frame_num;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		phy->vc_frame_number[vc] = frame_num;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic inline void cal_irq_wdma_start(struct cal_ctx *ctx)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	spin_lock(&ctx->dma.lock);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) {
62962306a36Sopenharmony_ci		/*
63062306a36Sopenharmony_ci		 * If a stop is requested, disable the write DMA context
63162306a36Sopenharmony_ci		 * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed,
63262306a36Sopenharmony_ci		 * the current frame will complete and the DMA will then stop.
63362306a36Sopenharmony_ci		 */
63462306a36Sopenharmony_ci		cal_ctx_wr_dma_disable(ctx);
63562306a36Sopenharmony_ci		ctx->dma.state = CAL_DMA_STOP_PENDING;
63662306a36Sopenharmony_ci	} else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) {
63762306a36Sopenharmony_ci		/*
63862306a36Sopenharmony_ci		 * Otherwise, if a new buffer is available, queue it to the
63962306a36Sopenharmony_ci		 * hardware.
64062306a36Sopenharmony_ci		 */
64162306a36Sopenharmony_ci		struct cal_buffer *buf;
64262306a36Sopenharmony_ci		dma_addr_t addr;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		buf = list_first_entry(&ctx->dma.queue, struct cal_buffer,
64562306a36Sopenharmony_ci				       list);
64662306a36Sopenharmony_ci		addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
64762306a36Sopenharmony_ci		cal_ctx_set_dma_addr(ctx, addr);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		ctx->dma.pending = buf;
65062306a36Sopenharmony_ci		list_del(&buf->list);
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	spin_unlock(&ctx->dma.lock);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	cal_update_seq_number(ctx);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic inline void cal_irq_wdma_end(struct cal_ctx *ctx)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct cal_buffer *buf = NULL;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	spin_lock(&ctx->dma.lock);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* If the DMA context was stopping, it is now stopped. */
66562306a36Sopenharmony_ci	if (ctx->dma.state == CAL_DMA_STOP_PENDING) {
66662306a36Sopenharmony_ci		ctx->dma.state = CAL_DMA_STOPPED;
66762306a36Sopenharmony_ci		wake_up(&ctx->dma.wait);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* If a new buffer was queued, complete the current buffer. */
67162306a36Sopenharmony_ci	if (ctx->dma.pending) {
67262306a36Sopenharmony_ci		buf = ctx->dma.active;
67362306a36Sopenharmony_ci		ctx->dma.active = ctx->dma.pending;
67462306a36Sopenharmony_ci		ctx->dma.pending = NULL;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	spin_unlock(&ctx->dma.lock);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (buf) {
68062306a36Sopenharmony_ci		buf->vb.vb2_buf.timestamp = ktime_get_ns();
68162306a36Sopenharmony_ci		buf->vb.field = ctx->v_fmt.fmt.pix.field;
68262306a36Sopenharmony_ci		buf->vb.sequence = ctx->phy->vc_sequence[ctx->vc];
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void cal_irq_handle_wdma(struct cal_ctx *ctx, bool start, bool end)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	/*
69162306a36Sopenharmony_ci	 * CAL HW interrupts are inherently racy. If we get both start and end
69262306a36Sopenharmony_ci	 * interrupts, we don't know what has happened: did the DMA for a single
69362306a36Sopenharmony_ci	 * frame start and end, or did one frame end and a new frame start?
69462306a36Sopenharmony_ci	 *
69562306a36Sopenharmony_ci	 * Usually for normal pixel frames we get the interrupts separately. If
69662306a36Sopenharmony_ci	 * we do get both, we have to guess. The assumption in the code below is
69762306a36Sopenharmony_ci	 * that the active vertical area is larger than the blanking vertical
69862306a36Sopenharmony_ci	 * area, and thus it is more likely that we get the end of the old frame
69962306a36Sopenharmony_ci	 * and the start of a new frame.
70062306a36Sopenharmony_ci	 *
70162306a36Sopenharmony_ci	 * However, for embedded data, which is only a few lines high, we always
70262306a36Sopenharmony_ci	 * get both interrupts. Here the assumption is that we get both for the
70362306a36Sopenharmony_ci	 * same frame.
70462306a36Sopenharmony_ci	 */
70562306a36Sopenharmony_ci	if (ctx->v_fmt.fmt.pix.height < 10) {
70662306a36Sopenharmony_ci		if (start)
70762306a36Sopenharmony_ci			cal_irq_wdma_start(ctx);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		if (end)
71062306a36Sopenharmony_ci			cal_irq_wdma_end(ctx);
71162306a36Sopenharmony_ci	} else {
71262306a36Sopenharmony_ci		if (end)
71362306a36Sopenharmony_ci			cal_irq_wdma_end(ctx);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (start)
71662306a36Sopenharmony_ci			cal_irq_wdma_start(ctx);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic irqreturn_t cal_irq(int irq_cal, void *data)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct cal_dev *cal = data;
72362306a36Sopenharmony_ci	u32 status[3];
72462306a36Sopenharmony_ci	unsigned int i;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	for (i = 0; i < 3; ++i) {
72762306a36Sopenharmony_ci		status[i] = cal_read(cal, CAL_HL_IRQSTATUS(i));
72862306a36Sopenharmony_ci		if (status[i])
72962306a36Sopenharmony_ci			cal_write(cal, CAL_HL_IRQSTATUS(i), status[i]);
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (status[0]) {
73362306a36Sopenharmony_ci		if (status[0] & CAL_HL_IRQ_OCPO_ERR_MASK)
73462306a36Sopenharmony_ci			dev_err_ratelimited(cal->dev, "OCPO ERROR\n");
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		for (i = 0; i < cal->data->num_csi2_phy; ++i) {
73762306a36Sopenharmony_ci			if (status[0] & CAL_HL_IRQ_CIO_MASK(i)) {
73862306a36Sopenharmony_ci				u32 cio_stat = cal_read(cal,
73962306a36Sopenharmony_ci							CAL_CSI2_COMPLEXIO_IRQSTATUS(i));
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci				dev_err_ratelimited(cal->dev,
74262306a36Sopenharmony_ci						    "CIO%u error: %#08x\n", i, cio_stat);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci				cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i),
74562306a36Sopenharmony_ci					  cio_stat);
74662306a36Sopenharmony_ci			}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci			if (status[0] & CAL_HL_IRQ_VC_MASK(i)) {
74962306a36Sopenharmony_ci				u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i));
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci				dev_err_ratelimited(cal->dev,
75262306a36Sopenharmony_ci						    "CIO%u VC error: %#08x\n",
75362306a36Sopenharmony_ci						    i, vc_stat);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci				cal_write(cal, CAL_CSI2_VC_IRQSTATUS(i), vc_stat);
75662306a36Sopenharmony_ci			}
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	for (i = 0; i < cal->num_contexts; ++i) {
76162306a36Sopenharmony_ci		bool end = !!(status[1] & CAL_HL_IRQ_WDMA_END_MASK(i));
76262306a36Sopenharmony_ci		bool start = !!(status[2] & CAL_HL_IRQ_WDMA_START_MASK(i));
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (start || end)
76562306a36Sopenharmony_ci			cal_irq_handle_wdma(cal->ctx[i], start, end);
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return IRQ_HANDLED;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/* ------------------------------------------------------------------
77262306a36Sopenharmony_ci *	Asynchronous V4L2 subdev binding
77362306a36Sopenharmony_ci * ------------------------------------------------------------------
77462306a36Sopenharmony_ci */
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistruct cal_v4l2_async_subdev {
77762306a36Sopenharmony_ci	struct v4l2_async_connection asd; /* Must be first */
77862306a36Sopenharmony_ci	struct cal_camerarx *phy;
77962306a36Sopenharmony_ci};
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic inline struct cal_v4l2_async_subdev *
78262306a36Sopenharmony_cito_cal_asd(struct v4l2_async_connection *asd)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	return container_of(asd, struct cal_v4l2_async_subdev, asd);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
78862306a36Sopenharmony_ci				    struct v4l2_subdev *subdev,
78962306a36Sopenharmony_ci				    struct v4l2_async_connection *asd)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct cal_camerarx *phy = to_cal_asd(asd)->phy;
79262306a36Sopenharmony_ci	int pad;
79362306a36Sopenharmony_ci	int ret;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (phy->source) {
79662306a36Sopenharmony_ci		phy_info(phy, "Rejecting subdev %s (Already set!!)",
79762306a36Sopenharmony_ci			 subdev->name);
79862306a36Sopenharmony_ci		return 0;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	phy->source = subdev;
80262306a36Sopenharmony_ci	phy_dbg(1, phy, "Using source %s for capture\n", subdev->name);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	pad = media_entity_get_fwnode_pad(&subdev->entity,
80562306a36Sopenharmony_ci					  of_fwnode_handle(phy->source_ep_node),
80662306a36Sopenharmony_ci					  MEDIA_PAD_FL_SOURCE);
80762306a36Sopenharmony_ci	if (pad < 0) {
80862306a36Sopenharmony_ci		phy_err(phy, "Source %s has no connected source pad\n",
80962306a36Sopenharmony_ci			subdev->name);
81062306a36Sopenharmony_ci		return pad;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = media_create_pad_link(&subdev->entity, pad,
81462306a36Sopenharmony_ci				    &phy->subdev.entity, CAL_CAMERARX_PAD_SINK,
81562306a36Sopenharmony_ci				    MEDIA_LNK_FL_IMMUTABLE |
81662306a36Sopenharmony_ci				    MEDIA_LNK_FL_ENABLED);
81762306a36Sopenharmony_ci	if (ret) {
81862306a36Sopenharmony_ci		phy_err(phy, "Failed to create media link for source %s\n",
81962306a36Sopenharmony_ci			subdev->name);
82062306a36Sopenharmony_ci		return ret;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return 0;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int cal_async_notifier_complete(struct v4l2_async_notifier *notifier)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier);
82962306a36Sopenharmony_ci	unsigned int i;
83062306a36Sopenharmony_ci	int ret;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	for (i = 0; i < cal->num_contexts; ++i) {
83362306a36Sopenharmony_ci		ret = cal_ctx_v4l2_register(cal->ctx[i]);
83462306a36Sopenharmony_ci		if (ret)
83562306a36Sopenharmony_ci			goto err_ctx_unreg;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (!cal_mc_api)
83962306a36Sopenharmony_ci		return 0;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev);
84262306a36Sopenharmony_ci	if (ret)
84362306a36Sopenharmony_ci		goto err_ctx_unreg;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return 0;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cierr_ctx_unreg:
84862306a36Sopenharmony_ci	for (; i > 0; --i) {
84962306a36Sopenharmony_ci		if (!cal->ctx[i - 1])
85062306a36Sopenharmony_ci			continue;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		cal_ctx_v4l2_unregister(cal->ctx[i - 1]);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	return ret;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations cal_async_notifier_ops = {
85962306a36Sopenharmony_ci	.bound = cal_async_notifier_bound,
86062306a36Sopenharmony_ci	.complete = cal_async_notifier_complete,
86162306a36Sopenharmony_ci};
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic int cal_async_notifier_register(struct cal_dev *cal)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	unsigned int i;
86662306a36Sopenharmony_ci	int ret;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	v4l2_async_nf_init(&cal->notifier, &cal->v4l2_dev);
86962306a36Sopenharmony_ci	cal->notifier.ops = &cal_async_notifier_ops;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; ++i) {
87262306a36Sopenharmony_ci		struct cal_camerarx *phy = cal->phy[i];
87362306a36Sopenharmony_ci		struct cal_v4l2_async_subdev *casd;
87462306a36Sopenharmony_ci		struct fwnode_handle *fwnode;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		if (!phy->source_node)
87762306a36Sopenharmony_ci			continue;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		fwnode = of_fwnode_handle(phy->source_node);
88062306a36Sopenharmony_ci		casd = v4l2_async_nf_add_fwnode(&cal->notifier,
88162306a36Sopenharmony_ci						fwnode,
88262306a36Sopenharmony_ci						struct cal_v4l2_async_subdev);
88362306a36Sopenharmony_ci		if (IS_ERR(casd)) {
88462306a36Sopenharmony_ci			phy_err(phy, "Failed to add subdev to notifier\n");
88562306a36Sopenharmony_ci			ret = PTR_ERR(casd);
88662306a36Sopenharmony_ci			goto error;
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		casd->phy = phy;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ret = v4l2_async_nf_register(&cal->notifier);
89362306a36Sopenharmony_ci	if (ret) {
89462306a36Sopenharmony_ci		cal_err(cal, "Error registering async notifier\n");
89562306a36Sopenharmony_ci		goto error;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cierror:
90162306a36Sopenharmony_ci	v4l2_async_nf_cleanup(&cal->notifier);
90262306a36Sopenharmony_ci	return ret;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void cal_async_notifier_unregister(struct cal_dev *cal)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	v4l2_async_nf_unregister(&cal->notifier);
90862306a36Sopenharmony_ci	v4l2_async_nf_cleanup(&cal->notifier);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci/* ------------------------------------------------------------------
91262306a36Sopenharmony_ci *	Media and V4L2 device handling
91362306a36Sopenharmony_ci * ------------------------------------------------------------------
91462306a36Sopenharmony_ci */
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/*
91762306a36Sopenharmony_ci * Register user-facing devices. To be called at the end of the probe function
91862306a36Sopenharmony_ci * when all resources are initialized and ready.
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_cistatic int cal_media_register(struct cal_dev *cal)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	int ret;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	ret = media_device_register(&cal->mdev);
92562306a36Sopenharmony_ci	if (ret) {
92662306a36Sopenharmony_ci		cal_err(cal, "Failed to register media device\n");
92762306a36Sopenharmony_ci		return ret;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/*
93162306a36Sopenharmony_ci	 * Register the async notifier. This may trigger registration of the
93262306a36Sopenharmony_ci	 * V4L2 video devices if all subdevs are ready.
93362306a36Sopenharmony_ci	 */
93462306a36Sopenharmony_ci	ret = cal_async_notifier_register(cal);
93562306a36Sopenharmony_ci	if (ret) {
93662306a36Sopenharmony_ci		media_device_unregister(&cal->mdev);
93762306a36Sopenharmony_ci		return ret;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return 0;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci/*
94462306a36Sopenharmony_ci * Unregister the user-facing devices, but don't free memory yet. To be called
94562306a36Sopenharmony_ci * at the beginning of the remove function, to disallow access from userspace.
94662306a36Sopenharmony_ci */
94762306a36Sopenharmony_cistatic void cal_media_unregister(struct cal_dev *cal)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	unsigned int i;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* Unregister all the V4L2 video devices. */
95262306a36Sopenharmony_ci	for (i = 0; i < cal->num_contexts; i++)
95362306a36Sopenharmony_ci		cal_ctx_v4l2_unregister(cal->ctx[i]);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	cal_async_notifier_unregister(cal);
95662306a36Sopenharmony_ci	media_device_unregister(&cal->mdev);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/*
96062306a36Sopenharmony_ci * Initialize the in-kernel objects. To be called at the beginning of the probe
96162306a36Sopenharmony_ci * function, before the V4L2 device is used by the driver.
96262306a36Sopenharmony_ci */
96362306a36Sopenharmony_cistatic int cal_media_init(struct cal_dev *cal)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct media_device *mdev = &cal->mdev;
96662306a36Sopenharmony_ci	int ret;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	mdev->dev = cal->dev;
96962306a36Sopenharmony_ci	mdev->hw_revision = cal->revision;
97062306a36Sopenharmony_ci	strscpy(mdev->model, "CAL", sizeof(mdev->model));
97162306a36Sopenharmony_ci	media_device_init(mdev);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/*
97462306a36Sopenharmony_ci	 * Initialize the V4L2 device (despite the function name, this performs
97562306a36Sopenharmony_ci	 * initialization, not registration).
97662306a36Sopenharmony_ci	 */
97762306a36Sopenharmony_ci	cal->v4l2_dev.mdev = mdev;
97862306a36Sopenharmony_ci	ret = v4l2_device_register(cal->dev, &cal->v4l2_dev);
97962306a36Sopenharmony_ci	if (ret) {
98062306a36Sopenharmony_ci		cal_err(cal, "Failed to register V4L2 device\n");
98162306a36Sopenharmony_ci		return ret;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32));
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	return 0;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci/*
99062306a36Sopenharmony_ci * Cleanup the in-kernel objects, freeing memory. To be called at the very end
99162306a36Sopenharmony_ci * of the remove sequence, when nothing (including userspace) can access the
99262306a36Sopenharmony_ci * objects anymore.
99362306a36Sopenharmony_ci */
99462306a36Sopenharmony_cistatic void cal_media_cleanup(struct cal_dev *cal)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	v4l2_device_unregister(&cal->v4l2_dev);
99762306a36Sopenharmony_ci	media_device_cleanup(&cal->mdev);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	vb2_dma_contig_clear_max_seg_size(cal->dev);
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci/* ------------------------------------------------------------------
100362306a36Sopenharmony_ci *	Initialization and module stuff
100462306a36Sopenharmony_ci * ------------------------------------------------------------------
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct cal_ctx *ctx;
101062306a36Sopenharmony_ci	int ret;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
101362306a36Sopenharmony_ci	if (!ctx)
101462306a36Sopenharmony_ci		return NULL;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	ctx->cal = cal;
101762306a36Sopenharmony_ci	ctx->phy = cal->phy[inst];
101862306a36Sopenharmony_ci	ctx->dma_ctx = inst;
101962306a36Sopenharmony_ci	ctx->csi2_ctx = inst;
102062306a36Sopenharmony_ci	ctx->cport = inst;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	ret = cal_ctx_v4l2_init(ctx);
102362306a36Sopenharmony_ci	if (ret) {
102462306a36Sopenharmony_ci		kfree(ctx);
102562306a36Sopenharmony_ci		return NULL;
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return ctx;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic void cal_ctx_destroy(struct cal_ctx *ctx)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	cal_ctx_v4l2_cleanup(ctx);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	kfree(ctx);
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic const struct of_device_id cal_of_match[] = {
103962306a36Sopenharmony_ci	{
104062306a36Sopenharmony_ci		.compatible = "ti,dra72-cal",
104162306a36Sopenharmony_ci		.data = (void *)&dra72x_cal_data,
104262306a36Sopenharmony_ci	},
104362306a36Sopenharmony_ci	{
104462306a36Sopenharmony_ci		.compatible = "ti,dra72-pre-es2-cal",
104562306a36Sopenharmony_ci		.data = (void *)&dra72x_es1_cal_data,
104662306a36Sopenharmony_ci	},
104762306a36Sopenharmony_ci	{
104862306a36Sopenharmony_ci		.compatible = "ti,dra76-cal",
104962306a36Sopenharmony_ci		.data = (void *)&dra76x_cal_data,
105062306a36Sopenharmony_ci	},
105162306a36Sopenharmony_ci	{
105262306a36Sopenharmony_ci		.compatible = "ti,am654-cal",
105362306a36Sopenharmony_ci		.data = (void *)&am654_cal_data,
105462306a36Sopenharmony_ci	},
105562306a36Sopenharmony_ci	{},
105662306a36Sopenharmony_ci};
105762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cal_of_match);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/* Get hardware revision and info. */
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci#define CAL_HL_HWINFO_VALUE		0xa3c90469
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic void cal_get_hwinfo(struct cal_dev *cal)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	u32 hwinfo;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	cal->revision = cal_read(cal, CAL_HL_REVISION);
106862306a36Sopenharmony_ci	switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) {
106962306a36Sopenharmony_ci	case CAL_HL_REVISION_SCHEME_H08:
107062306a36Sopenharmony_ci		cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n",
107162306a36Sopenharmony_ci			FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision),
107262306a36Sopenharmony_ci			FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision),
107362306a36Sopenharmony_ci			FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision),
107462306a36Sopenharmony_ci			cal->revision);
107562306a36Sopenharmony_ci		break;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	case CAL_HL_REVISION_SCHEME_LEGACY:
107862306a36Sopenharmony_ci	default:
107962306a36Sopenharmony_ci		cal_info(cal, "Unexpected CAL HW revision 0x%08x\n",
108062306a36Sopenharmony_ci			 cal->revision);
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	hwinfo = cal_read(cal, CAL_HL_HWINFO);
108562306a36Sopenharmony_ci	if (hwinfo != CAL_HL_HWINFO_VALUE)
108662306a36Sopenharmony_ci		cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n",
108762306a36Sopenharmony_ci			 hwinfo, CAL_HL_HWINFO_VALUE);
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic int cal_init_camerarx_regmap(struct cal_dev *cal)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(cal->dev);
109362306a36Sopenharmony_ci	struct device_node *np = cal->dev->of_node;
109462306a36Sopenharmony_ci	struct regmap_config config = { };
109562306a36Sopenharmony_ci	struct regmap *syscon;
109662306a36Sopenharmony_ci	struct resource *res;
109762306a36Sopenharmony_ci	unsigned int offset;
109862306a36Sopenharmony_ci	void __iomem *base;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control",
110162306a36Sopenharmony_ci						      1, &offset);
110262306a36Sopenharmony_ci	if (!IS_ERR(syscon)) {
110362306a36Sopenharmony_ci		cal->syscon_camerrx = syscon;
110462306a36Sopenharmony_ci		cal->syscon_camerrx_offset = offset;
110562306a36Sopenharmony_ci		return 0;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n",
110962306a36Sopenharmony_ci		 PTR_ERR(syscon));
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/*
111262306a36Sopenharmony_ci	 * Backward DTS compatibility. If syscon entry is not present then
111362306a36Sopenharmony_ci	 * check if the camerrx_control resource is present.
111462306a36Sopenharmony_ci	 */
111562306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
111662306a36Sopenharmony_ci					   "camerrx_control");
111762306a36Sopenharmony_ci	base = devm_ioremap_resource(cal->dev, res);
111862306a36Sopenharmony_ci	if (IS_ERR(base)) {
111962306a36Sopenharmony_ci		cal_err(cal, "failed to ioremap camerrx_control\n");
112062306a36Sopenharmony_ci		return PTR_ERR(base);
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
112462306a36Sopenharmony_ci		res->name, &res->start, &res->end);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	config.reg_bits = 32;
112762306a36Sopenharmony_ci	config.reg_stride = 4;
112862306a36Sopenharmony_ci	config.val_bits = 32;
112962306a36Sopenharmony_ci	config.max_register = resource_size(res) - 4;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	syscon = regmap_init_mmio(NULL, base, &config);
113262306a36Sopenharmony_ci	if (IS_ERR(syscon)) {
113362306a36Sopenharmony_ci		pr_err("regmap init failed\n");
113462306a36Sopenharmony_ci		return PTR_ERR(syscon);
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/*
113862306a36Sopenharmony_ci	 * In this case the base already point to the direct CM register so no
113962306a36Sopenharmony_ci	 * need for an offset.
114062306a36Sopenharmony_ci	 */
114162306a36Sopenharmony_ci	cal->syscon_camerrx = syscon;
114262306a36Sopenharmony_ci	cal->syscon_camerrx_offset = 0;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	return 0;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic int cal_probe(struct platform_device *pdev)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct cal_dev *cal;
115062306a36Sopenharmony_ci	bool connected = false;
115162306a36Sopenharmony_ci	unsigned int i;
115262306a36Sopenharmony_ci	int ret;
115362306a36Sopenharmony_ci	int irq;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL);
115662306a36Sopenharmony_ci	if (!cal)
115762306a36Sopenharmony_ci		return -ENOMEM;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	cal->data = of_device_get_match_data(&pdev->dev);
116062306a36Sopenharmony_ci	if (!cal->data) {
116162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Could not get feature data based on compatible version\n");
116262306a36Sopenharmony_ci		return -ENODEV;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	cal->dev = &pdev->dev;
116662306a36Sopenharmony_ci	platform_set_drvdata(pdev, cal);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	/* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */
116962306a36Sopenharmony_ci	cal->fclk = devm_clk_get(&pdev->dev, "fck");
117062306a36Sopenharmony_ci	if (IS_ERR(cal->fclk)) {
117162306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot get CAL fclk\n");
117262306a36Sopenharmony_ci		return PTR_ERR(cal->fclk);
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	ret = cal_init_camerarx_regmap(cal);
117662306a36Sopenharmony_ci	if (ret < 0)
117762306a36Sopenharmony_ci		return ret;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
118062306a36Sopenharmony_ci						"cal_top");
118162306a36Sopenharmony_ci	cal->base = devm_ioremap_resource(&pdev->dev, cal->res);
118262306a36Sopenharmony_ci	if (IS_ERR(cal->base))
118362306a36Sopenharmony_ci		return PTR_ERR(cal->base);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
118662306a36Sopenharmony_ci		cal->res->name, &cal->res->start, &cal->res->end);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
118962306a36Sopenharmony_ci	cal_dbg(1, cal, "got irq# %d\n", irq);
119062306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
119162306a36Sopenharmony_ci			       cal);
119262306a36Sopenharmony_ci	if (ret)
119362306a36Sopenharmony_ci		return ret;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	/* Read the revision and hardware info to verify hardware access. */
119662306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
119762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
119862306a36Sopenharmony_ci	if (ret)
119962306a36Sopenharmony_ci		goto error_pm_runtime;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	cal_get_hwinfo(cal);
120262306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	/* Initialize the media device. */
120562306a36Sopenharmony_ci	ret = cal_media_init(cal);
120662306a36Sopenharmony_ci	if (ret < 0)
120762306a36Sopenharmony_ci		goto error_pm_runtime;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* Create CAMERARX PHYs. */
121062306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; ++i) {
121162306a36Sopenharmony_ci		cal->phy[i] = cal_camerarx_create(cal, i);
121262306a36Sopenharmony_ci		if (IS_ERR(cal->phy[i])) {
121362306a36Sopenharmony_ci			ret = PTR_ERR(cal->phy[i]);
121462306a36Sopenharmony_ci			cal->phy[i] = NULL;
121562306a36Sopenharmony_ci			goto error_camerarx;
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		if (cal->phy[i]->source_node)
121962306a36Sopenharmony_ci			connected = true;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (!connected) {
122362306a36Sopenharmony_ci		cal_err(cal, "Neither port is configured, no point in staying up\n");
122462306a36Sopenharmony_ci		ret = -ENODEV;
122562306a36Sopenharmony_ci		goto error_camerarx;
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	/* Create contexts. */
122962306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; ++i) {
123062306a36Sopenharmony_ci		if (!cal->phy[i]->source_node)
123162306a36Sopenharmony_ci			continue;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i);
123462306a36Sopenharmony_ci		if (!cal->ctx[cal->num_contexts]) {
123562306a36Sopenharmony_ci			cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
123662306a36Sopenharmony_ci			ret = -ENODEV;
123762306a36Sopenharmony_ci			goto error_context;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		cal->num_contexts++;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* Register the media device. */
124462306a36Sopenharmony_ci	ret = cal_media_register(cal);
124562306a36Sopenharmony_ci	if (ret)
124662306a36Sopenharmony_ci		goto error_context;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cierror_context:
125162306a36Sopenharmony_ci	for (i = 0; i < cal->num_contexts; i++)
125262306a36Sopenharmony_ci		cal_ctx_destroy(cal->ctx[i]);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cierror_camerarx:
125562306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; i++)
125662306a36Sopenharmony_ci		cal_camerarx_destroy(cal->phy[i]);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	cal_media_cleanup(cal);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cierror_pm_runtime:
126162306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	return ret;
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic void cal_remove(struct platform_device *pdev)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct cal_dev *cal = platform_get_drvdata(pdev);
126962306a36Sopenharmony_ci	unsigned int i;
127062306a36Sopenharmony_ci	int ret;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	cal_media_unregister(cal);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; i++)
127962306a36Sopenharmony_ci		cal_camerarx_disable(cal->phy[i]);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	for (i = 0; i < cal->num_contexts; i++)
128262306a36Sopenharmony_ci		cal_ctx_destroy(cal->ctx[i]);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	for (i = 0; i < cal->data->num_csi2_phy; i++)
128562306a36Sopenharmony_ci		cal_camerarx_destroy(cal->phy[i]);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	cal_media_cleanup(cal);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (ret >= 0)
129062306a36Sopenharmony_ci		pm_runtime_put_sync(&pdev->dev);
129162306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic int cal_runtime_resume(struct device *dev)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	struct cal_dev *cal = dev_get_drvdata(dev);
129762306a36Sopenharmony_ci	unsigned int i;
129862306a36Sopenharmony_ci	u32 val;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) {
130162306a36Sopenharmony_ci		/*
130262306a36Sopenharmony_ci		 * Apply errata on both port everytime we (re-)enable
130362306a36Sopenharmony_ci		 * the clock
130462306a36Sopenharmony_ci		 */
130562306a36Sopenharmony_ci		for (i = 0; i < cal->data->num_csi2_phy; i++)
130662306a36Sopenharmony_ci			cal_camerarx_i913_errata(cal->phy[i]);
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/*
131062306a36Sopenharmony_ci	 * Enable global interrupts that are not related to a particular
131162306a36Sopenharmony_ci	 * CAMERARAX or context.
131262306a36Sopenharmony_ci	 */
131362306a36Sopenharmony_ci	cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	val = cal_read(cal, CAL_CTRL);
131662306a36Sopenharmony_ci	cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
131762306a36Sopenharmony_ci		      CAL_CTRL_BURSTSIZE_MASK);
131862306a36Sopenharmony_ci	cal_set_field(&val, 0xf, CAL_CTRL_TAGCNT_MASK);
131962306a36Sopenharmony_ci	cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
132062306a36Sopenharmony_ci		      CAL_CTRL_POSTED_WRITES_MASK);
132162306a36Sopenharmony_ci	cal_set_field(&val, 0xff, CAL_CTRL_MFLAGL_MASK);
132262306a36Sopenharmony_ci	cal_set_field(&val, 0xff, CAL_CTRL_MFLAGH_MASK);
132362306a36Sopenharmony_ci	cal_write(cal, CAL_CTRL, val);
132462306a36Sopenharmony_ci	cal_dbg(3, cal, "CAL_CTRL = 0x%08x\n", cal_read(cal, CAL_CTRL));
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	return 0;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic const struct dev_pm_ops cal_pm_ops = {
133062306a36Sopenharmony_ci	.runtime_resume = cal_runtime_resume,
133162306a36Sopenharmony_ci};
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic struct platform_driver cal_pdrv = {
133462306a36Sopenharmony_ci	.probe		= cal_probe,
133562306a36Sopenharmony_ci	.remove_new	= cal_remove,
133662306a36Sopenharmony_ci	.driver		= {
133762306a36Sopenharmony_ci		.name	= CAL_MODULE_NAME,
133862306a36Sopenharmony_ci		.pm	= &cal_pm_ops,
133962306a36Sopenharmony_ci		.of_match_table = cal_of_match,
134062306a36Sopenharmony_ci	},
134162306a36Sopenharmony_ci};
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cimodule_platform_driver(cal_pdrv);
1344