cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

imx8mq-mipi-csi2.c (25558B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * NXP i.MX8MQ SoC series MIPI-CSI2 receiver driver
      4 *
      5 * Copyright (C) 2021 Purism SPC
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/clk-provider.h>
     10#include <linux/delay.h>
     11#include <linux/errno.h>
     12#include <linux/interconnect.h>
     13#include <linux/interrupt.h>
     14#include <linux/io.h>
     15#include <linux/kernel.h>
     16#include <linux/mfd/syscon.h>
     17#include <linux/module.h>
     18#include <linux/mutex.h>
     19#include <linux/of.h>
     20#include <linux/of_device.h>
     21#include <linux/platform_device.h>
     22#include <linux/pm_runtime.h>
     23#include <linux/regmap.h>
     24#include <linux/regulator/consumer.h>
     25#include <linux/reset.h>
     26#include <linux/spinlock.h>
     27
     28#include <media/v4l2-common.h>
     29#include <media/v4l2-device.h>
     30#include <media/v4l2-fwnode.h>
     31#include <media/v4l2-mc.h>
     32#include <media/v4l2-subdev.h>
     33
     34#define MIPI_CSI2_DRIVER_NAME			"imx8mq-mipi-csi2"
     35#define MIPI_CSI2_SUBDEV_NAME			MIPI_CSI2_DRIVER_NAME
     36
     37#define MIPI_CSI2_PAD_SINK			0
     38#define MIPI_CSI2_PAD_SOURCE			1
     39#define MIPI_CSI2_PADS_NUM			2
     40
     41#define MIPI_CSI2_DEF_PIX_WIDTH			640
     42#define MIPI_CSI2_DEF_PIX_HEIGHT		480
     43
     44/* Register map definition */
     45
     46/* i.MX8MQ CSI-2 controller CSR */
     47#define CSI2RX_CFG_NUM_LANES			0x100
     48#define CSI2RX_CFG_DISABLE_DATA_LANES		0x104
     49#define CSI2RX_BIT_ERR				0x108
     50#define CSI2RX_IRQ_STATUS			0x10c
     51#define CSI2RX_IRQ_MASK				0x110
     52#define CSI2RX_IRQ_MASK_ALL			0x1ff
     53#define CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE	0x8
     54#define CSI2RX_ULPS_STATUS			0x114
     55#define CSI2RX_PPI_ERRSOT_HS			0x118
     56#define CSI2RX_PPI_ERRSOTSYNC_HS		0x11c
     57#define CSI2RX_PPI_ERRESC			0x120
     58#define CSI2RX_PPI_ERRSYNCESC			0x124
     59#define CSI2RX_PPI_ERRCONTROL			0x128
     60#define CSI2RX_CFG_DISABLE_PAYLOAD_0		0x12c
     61#define CSI2RX_CFG_VID_VC_IGNORE		0x180
     62#define CSI2RX_CFG_VID_VC			0x184
     63#define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL	0x188
     64#define CSI2RX_CFG_DISABLE_PAYLOAD_1		0x130
     65
     66enum {
     67	ST_POWERED	= 1,
     68	ST_STREAMING	= 2,
     69	ST_SUSPENDED	= 4,
     70};
     71
     72enum imx8mq_mipi_csi_clk {
     73	CSI2_CLK_CORE,
     74	CSI2_CLK_ESC,
     75	CSI2_CLK_UI,
     76	CSI2_NUM_CLKS,
     77};
     78
     79static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
     80	[CSI2_CLK_CORE] = "core",
     81	[CSI2_CLK_ESC] = "esc",
     82	[CSI2_CLK_UI] = "ui",
     83};
     84
     85#define CSI2_NUM_CLKS	ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
     86
     87#define	GPR_CSI2_1_RX_ENABLE		BIT(13)
     88#define	GPR_CSI2_1_VID_INTFC_ENB	BIT(12)
     89#define	GPR_CSI2_1_HSEL			BIT(10)
     90#define	GPR_CSI2_1_CONT_CLK_MODE	BIT(8)
     91#define	GPR_CSI2_1_S_PRG_RXHS_SETTLE(x)	(((x) & 0x3f) << 2)
     92
     93/*
     94 * The send level configures the number of entries that must accumulate in
     95 * the Pixel FIFO before the data will be transferred to the video output.
     96 * The exact value needed for this configuration is dependent on the rate at
     97 * which the sensor transfers data to the CSI-2 Controller and the user
     98 * video clock.
     99 *
    100 * The calculation is the classical rate-in rate-out type of problem: If the
    101 * video bandwidth is 10% faster than the incoming mipi data and the video
    102 * line length is 500 pixels, then the fifo should be allowed to fill
    103 * 10% of the line length or 50 pixels. If the gap data is ok, then the level
    104 * can be set to 16 and ignored.
    105 */
    106#define CSI2RX_SEND_LEVEL			64
    107
    108struct csi_state {
    109	struct device *dev;
    110	void __iomem *regs;
    111	struct clk_bulk_data clks[CSI2_NUM_CLKS];
    112	struct reset_control *rst;
    113	struct regulator *mipi_phy_regulator;
    114
    115	struct v4l2_subdev sd;
    116	struct media_pad pads[MIPI_CSI2_PADS_NUM];
    117	struct v4l2_async_notifier notifier;
    118	struct v4l2_subdev *src_sd;
    119
    120	struct v4l2_mbus_config_mipi_csi2 bus;
    121
    122	struct mutex lock; /* Protect csi2_fmt, format_mbus, state, hs_settle */
    123	const struct csi2_pix_format *csi2_fmt;
    124	struct v4l2_mbus_framefmt format_mbus[MIPI_CSI2_PADS_NUM];
    125	u32 state;
    126	u32 hs_settle;
    127
    128	struct regmap *phy_gpr;
    129	u8 phy_gpr_reg;
    130
    131	struct icc_path			*icc_path;
    132	s32				icc_path_bw;
    133};
    134
    135/* -----------------------------------------------------------------------------
    136 * Format helpers
    137 */
    138
    139struct csi2_pix_format {
    140	u32 code;
    141	u8 width;
    142};
    143
    144static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
    145	/* RAW (Bayer and greyscale) formats. */
    146	{
    147		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
    148		.width = 8,
    149	}, {
    150		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
    151		.width = 8,
    152	}, {
    153		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
    154		.width = 8,
    155	}, {
    156		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
    157		.width = 8,
    158	}, {
    159		.code = MEDIA_BUS_FMT_Y8_1X8,
    160		.width = 8,
    161	}, {
    162		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
    163		.width = 10,
    164	}, {
    165		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
    166		.width = 10,
    167	}, {
    168		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
    169		.width = 10,
    170	}, {
    171		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
    172		.width = 10,
    173	}, {
    174		.code = MEDIA_BUS_FMT_Y10_1X10,
    175		.width = 10,
    176	}, {
    177		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
    178		.width = 12,
    179	}, {
    180		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
    181		.width = 12,
    182	}, {
    183		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
    184		.width = 12,
    185	}, {
    186		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
    187		.width = 12,
    188	}, {
    189		.code = MEDIA_BUS_FMT_Y12_1X12,
    190		.width = 12,
    191	}, {
    192		.code = MEDIA_BUS_FMT_SBGGR14_1X14,
    193		.width = 14,
    194	}, {
    195		.code = MEDIA_BUS_FMT_SGBRG14_1X14,
    196		.width = 14,
    197	}, {
    198		.code = MEDIA_BUS_FMT_SGRBG14_1X14,
    199		.width = 14,
    200	}, {
    201		.code = MEDIA_BUS_FMT_SRGGB14_1X14,
    202		.width = 14,
    203	},
    204	/* YUV formats */
    205	{
    206		.code = MEDIA_BUS_FMT_YUYV8_1X16,
    207		.width = 16,
    208	}, {
    209		.code = MEDIA_BUS_FMT_UYVY8_1X16,
    210		.width = 16,
    211	}
    212};
    213
    214static const struct csi2_pix_format *find_csi2_format(u32 code)
    215{
    216	unsigned int i;
    217
    218	for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++)
    219		if (code == imx8mq_mipi_csi_formats[i].code)
    220			return &imx8mq_mipi_csi_formats[i];
    221	return NULL;
    222}
    223
    224/* -----------------------------------------------------------------------------
    225 * Hardware configuration
    226 */
    227
    228static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val)
    229{
    230	writel(val, state->regs + reg);
    231}
    232
    233static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
    234{
    235	int ret;
    236
    237	/*
    238	 * these are most likely self-clearing reset bits. to make it
    239	 * more clear, the reset-imx7 driver should implement the
    240	 * .reset() operation.
    241	 */
    242	ret = reset_control_assert(state->rst);
    243	if (ret < 0) {
    244		dev_err(state->dev, "Failed to assert resets: %d\n", ret);
    245		return ret;
    246	}
    247
    248	return 0;
    249}
    250
    251static void imx8mq_mipi_csi_system_enable(struct csi_state *state, int on)
    252{
    253	if (!on) {
    254		imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
    255		return;
    256	}
    257
    258	regmap_update_bits(state->phy_gpr,
    259			   state->phy_gpr_reg,
    260			   0x3fff,
    261			   GPR_CSI2_1_RX_ENABLE |
    262			   GPR_CSI2_1_VID_INTFC_ENB |
    263			   GPR_CSI2_1_HSEL |
    264			   GPR_CSI2_1_CONT_CLK_MODE |
    265			   GPR_CSI2_1_S_PRG_RXHS_SETTLE(state->hs_settle));
    266}
    267
    268static void imx8mq_mipi_csi_set_params(struct csi_state *state)
    269{
    270	int lanes = state->bus.num_data_lanes;
    271
    272	imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1);
    273	imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES,
    274			      (0xf << lanes) & 0xf);
    275	imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL);
    276	/*
    277	 * 0x180 bit 0 controls the Virtual Channel behaviour: when set the
    278	 * interface ignores the Virtual Channel (VC) field in received packets;
    279	 * when cleared it causes the interface to only accept packets whose VC
    280	 * matches the value to which VC is set at offset 0x184.
    281	 */
    282	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1);
    283	imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL,
    284			      CSI2RX_SEND_LEVEL);
    285}
    286
    287static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
    288{
    289	return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
    290}
    291
    292static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
    293{
    294	clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
    295}
    296
    297static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
    298{
    299	unsigned int i;
    300
    301	for (i = 0; i < CSI2_NUM_CLKS; i++)
    302		state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
    303
    304	return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
    305}
    306
    307static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state)
    308{
    309	s64 link_freq;
    310	u32 lane_rate;
    311	unsigned long esc_clk_rate;
    312	u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns;
    313
    314	/* Calculate the line rate from the pixel rate. */
    315	link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
    316				       state->csi2_fmt->width,
    317				       state->bus.num_data_lanes * 2);
    318	if (link_freq < 0) {
    319		dev_err(state->dev, "Unable to obtain link frequency: %d\n",
    320			(int)link_freq);
    321		return link_freq;
    322	}
    323
    324	lane_rate = link_freq * 2;
    325	if (lane_rate < 80000000 || lane_rate > 1500000000) {
    326		dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
    327		return -EINVAL;
    328	}
    329
    330	/*
    331	 * The D-PHY specification requires Ths-settle to be in the range
    332	 * 85ns + 6*UI to 140ns + 10*UI, with the unit interval UI being half
    333	 * the clock period.
    334	 *
    335	 * The Ths-settle value is expressed in the hardware as a multiple of
    336	 * the Esc clock period:
    337	 *
    338	 * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc
    339	 *
    340	 * Due to the one cycle inaccuracy introduced by rounding, the
    341	 * documentation recommends picking a value away from the boundaries.
    342	 * Let's pick the average.
    343	 */
    344	esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
    345	if (!esc_clk_rate) {
    346		dev_err(state->dev, "Could not get esc clock rate.\n");
    347		return -EINVAL;
    348	}
    349
    350	dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate);
    351	esc_clk_period_ns = 1000000000 / esc_clk_rate;
    352
    353	min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000);
    354	max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000);
    355	ths_settle_ns = (min_ths_settle + max_ths_settle) / 2;
    356
    357	state->hs_settle = ths_settle_ns / esc_clk_period_ns - 1;
    358
    359	dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n",
    360		lane_rate, ths_settle_ns, state->hs_settle);
    361
    362	return 0;
    363}
    364
    365static int imx8mq_mipi_csi_start_stream(struct csi_state *state)
    366{
    367	int ret;
    368
    369	ret = imx8mq_mipi_csi_sw_reset(state);
    370	if (ret)
    371		return ret;
    372
    373	imx8mq_mipi_csi_set_params(state);
    374	ret = imx8mq_mipi_csi_calc_hs_settle(state);
    375	if (ret)
    376		return ret;
    377
    378	imx8mq_mipi_csi_system_enable(state, true);
    379
    380	return 0;
    381}
    382
    383static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
    384{
    385	imx8mq_mipi_csi_system_enable(state, false);
    386}
    387
    388/* -----------------------------------------------------------------------------
    389 * V4L2 subdev operations
    390 */
    391
    392static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev)
    393{
    394	return container_of(sdev, struct csi_state, sd);
    395}
    396
    397static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable)
    398{
    399	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    400	int ret = 0;
    401
    402	if (enable) {
    403		ret = pm_runtime_resume_and_get(state->dev);
    404		if (ret < 0)
    405			return ret;
    406	}
    407
    408	mutex_lock(&state->lock);
    409
    410	if (enable) {
    411		if (state->state & ST_SUSPENDED) {
    412			ret = -EBUSY;
    413			goto unlock;
    414		}
    415
    416		ret = imx8mq_mipi_csi_start_stream(state);
    417		if (ret < 0)
    418			goto unlock;
    419
    420		ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
    421		if (ret < 0)
    422			goto unlock;
    423
    424		state->state |= ST_STREAMING;
    425	} else {
    426		v4l2_subdev_call(state->src_sd, video, s_stream, 0);
    427		imx8mq_mipi_csi_stop_stream(state);
    428		state->state &= ~ST_STREAMING;
    429	}
    430
    431unlock:
    432	mutex_unlock(&state->lock);
    433
    434	if (!enable || ret < 0)
    435		pm_runtime_put(state->dev);
    436
    437	return ret;
    438}
    439
    440static struct v4l2_mbus_framefmt *
    441imx8mq_mipi_csi_get_format(struct csi_state *state,
    442			   struct v4l2_subdev_state *sd_state,
    443			   enum v4l2_subdev_format_whence which,
    444			   unsigned int pad)
    445{
    446	if (which == V4L2_SUBDEV_FORMAT_TRY)
    447		return v4l2_subdev_get_try_format(&state->sd, sd_state, pad);
    448
    449	return &state->format_mbus[pad];
    450}
    451
    452static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd,
    453				    struct v4l2_subdev_state *sd_state)
    454{
    455	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    456	struct v4l2_mbus_framefmt *fmt_sink;
    457	struct v4l2_mbus_framefmt *fmt_source;
    458	enum v4l2_subdev_format_whence which;
    459
    460	which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
    461	fmt_sink = imx8mq_mipi_csi_get_format(state, sd_state, which,
    462					      MIPI_CSI2_PAD_SINK);
    463
    464	fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10;
    465	fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH;
    466	fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT;
    467	fmt_sink->field = V4L2_FIELD_NONE;
    468
    469	fmt_sink->colorspace = V4L2_COLORSPACE_RAW;
    470	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
    471	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
    472	fmt_sink->quantization =
    473		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
    474					      fmt_sink->ycbcr_enc);
    475
    476	fmt_source = imx8mq_mipi_csi_get_format(state, sd_state, which,
    477						MIPI_CSI2_PAD_SOURCE);
    478	*fmt_source = *fmt_sink;
    479
    480	return 0;
    481}
    482
    483static int imx8mq_mipi_csi_get_fmt(struct v4l2_subdev *sd,
    484				   struct v4l2_subdev_state *sd_state,
    485				   struct v4l2_subdev_format *sdformat)
    486{
    487	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    488	struct v4l2_mbus_framefmt *fmt;
    489
    490	fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
    491					 sdformat->pad);
    492
    493	mutex_lock(&state->lock);
    494
    495	sdformat->format = *fmt;
    496
    497	mutex_unlock(&state->lock);
    498
    499	return 0;
    500}
    501
    502static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
    503					  struct v4l2_subdev_state *sd_state,
    504					  struct v4l2_subdev_mbus_code_enum *code)
    505{
    506	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    507
    508	/*
    509	 * We can't transcode in any way, the source format is identical
    510	 * to the sink format.
    511	 */
    512	if (code->pad == MIPI_CSI2_PAD_SOURCE) {
    513		struct v4l2_mbus_framefmt *fmt;
    514
    515		if (code->index > 0)
    516			return -EINVAL;
    517
    518		fmt = imx8mq_mipi_csi_get_format(state, sd_state, code->which,
    519						 code->pad);
    520		code->code = fmt->code;
    521		return 0;
    522	}
    523
    524	if (code->pad != MIPI_CSI2_PAD_SINK)
    525		return -EINVAL;
    526
    527	if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats))
    528		return -EINVAL;
    529
    530	code->code = imx8mq_mipi_csi_formats[code->index].code;
    531
    532	return 0;
    533}
    534
    535static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
    536				   struct v4l2_subdev_state *sd_state,
    537				   struct v4l2_subdev_format *sdformat)
    538{
    539	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    540	struct csi2_pix_format const *csi2_fmt;
    541	struct v4l2_mbus_framefmt *fmt;
    542
    543	/*
    544	 * The device can't transcode in any way, the source format can't be
    545	 * modified.
    546	 */
    547	if (sdformat->pad == MIPI_CSI2_PAD_SOURCE)
    548		return imx8mq_mipi_csi_get_fmt(sd, sd_state, sdformat);
    549
    550	if (sdformat->pad != MIPI_CSI2_PAD_SINK)
    551		return -EINVAL;
    552
    553	csi2_fmt = find_csi2_format(sdformat->format.code);
    554	if (!csi2_fmt)
    555		csi2_fmt = &imx8mq_mipi_csi_formats[0];
    556
    557	fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
    558					 sdformat->pad);
    559
    560	mutex_lock(&state->lock);
    561
    562	fmt->code = csi2_fmt->code;
    563	fmt->width = sdformat->format.width;
    564	fmt->height = sdformat->format.height;
    565
    566	sdformat->format = *fmt;
    567
    568	/* Propagate the format from sink to source. */
    569	fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
    570					 MIPI_CSI2_PAD_SOURCE);
    571	*fmt = sdformat->format;
    572
    573	/* Store the CSI2 format descriptor for active formats. */
    574	if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
    575		state->csi2_fmt = csi2_fmt;
    576
    577	mutex_unlock(&state->lock);
    578
    579	return 0;
    580}
    581
    582static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = {
    583	.s_stream	= imx8mq_mipi_csi_s_stream,
    584};
    585
    586static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = {
    587	.init_cfg		= imx8mq_mipi_csi_init_cfg,
    588	.enum_mbus_code		= imx8mq_mipi_csi_enum_mbus_code,
    589	.get_fmt		= imx8mq_mipi_csi_get_fmt,
    590	.set_fmt		= imx8mq_mipi_csi_set_fmt,
    591};
    592
    593static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = {
    594	.video	= &imx8mq_mipi_csi_video_ops,
    595	.pad	= &imx8mq_mipi_csi_pad_ops,
    596};
    597
    598/* -----------------------------------------------------------------------------
    599 * Media entity operations
    600 */
    601
    602static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = {
    603	.link_validate	= v4l2_subdev_link_validate,
    604	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
    605};
    606
    607/* -----------------------------------------------------------------------------
    608 * Async subdev notifier
    609 */
    610
    611static struct csi_state *
    612mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n)
    613{
    614	return container_of(n, struct csi_state, notifier);
    615}
    616
    617static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
    618					struct v4l2_subdev *sd,
    619					struct v4l2_async_subdev *asd)
    620{
    621	struct csi_state *state = mipi_notifier_to_csi2_state(notifier);
    622	struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK];
    623
    624	state->src_sd = sd;
    625
    626	return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
    627					       MEDIA_LNK_FL_IMMUTABLE);
    628}
    629
    630static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = {
    631	.bound = imx8mq_mipi_csi_notify_bound,
    632};
    633
    634static int imx8mq_mipi_csi_async_register(struct csi_state *state)
    635{
    636	struct v4l2_fwnode_endpoint vep = {
    637		.bus_type = V4L2_MBUS_CSI2_DPHY,
    638	};
    639	struct v4l2_async_subdev *asd;
    640	struct fwnode_handle *ep;
    641	unsigned int i;
    642	int ret;
    643
    644	v4l2_async_nf_init(&state->notifier);
    645
    646	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
    647					     FWNODE_GRAPH_ENDPOINT_NEXT);
    648	if (!ep)
    649		return -ENOTCONN;
    650
    651	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
    652	if (ret)
    653		goto err_parse;
    654
    655	for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
    656		if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
    657			dev_err(state->dev,
    658				"data lanes reordering is not supported");
    659			ret = -EINVAL;
    660			goto err_parse;
    661		}
    662	}
    663
    664	state->bus = vep.bus.mipi_csi2;
    665
    666	dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n",
    667		state->bus.num_data_lanes,
    668		state->bus.flags);
    669
    670	asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
    671					      struct v4l2_async_subdev);
    672	if (IS_ERR(asd)) {
    673		ret = PTR_ERR(asd);
    674		goto err_parse;
    675	}
    676
    677	fwnode_handle_put(ep);
    678
    679	state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
    680
    681	ret = v4l2_async_subdev_nf_register(&state->sd, &state->notifier);
    682	if (ret)
    683		return ret;
    684
    685	return v4l2_async_register_subdev(&state->sd);
    686
    687err_parse:
    688	fwnode_handle_put(ep);
    689
    690	return ret;
    691}
    692
    693/* -----------------------------------------------------------------------------
    694 * Suspend/resume
    695 */
    696
    697static void imx8mq_mipi_csi_pm_suspend(struct device *dev)
    698{
    699	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    700	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    701
    702	mutex_lock(&state->lock);
    703
    704	if (state->state & ST_POWERED) {
    705		imx8mq_mipi_csi_stop_stream(state);
    706		imx8mq_mipi_csi_clk_disable(state);
    707		state->state &= ~ST_POWERED;
    708	}
    709
    710	mutex_unlock(&state->lock);
    711}
    712
    713static int imx8mq_mipi_csi_pm_resume(struct device *dev)
    714{
    715	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    716	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    717	int ret = 0;
    718
    719	mutex_lock(&state->lock);
    720
    721	if (!(state->state & ST_POWERED)) {
    722		state->state |= ST_POWERED;
    723		ret = imx8mq_mipi_csi_clk_enable(state);
    724	}
    725	if (state->state & ST_STREAMING) {
    726		ret = imx8mq_mipi_csi_start_stream(state);
    727		if (ret)
    728			goto unlock;
    729	}
    730
    731	state->state &= ~ST_SUSPENDED;
    732
    733unlock:
    734	mutex_unlock(&state->lock);
    735
    736	return ret ? -EAGAIN : 0;
    737}
    738
    739static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
    740{
    741	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    742	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    743
    744	imx8mq_mipi_csi_pm_suspend(dev);
    745
    746	state->state |= ST_SUSPENDED;
    747
    748	return 0;
    749}
    750
    751static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
    752{
    753	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    754	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    755
    756	if (!(state->state & ST_SUSPENDED))
    757		return 0;
    758
    759	return imx8mq_mipi_csi_pm_resume(dev);
    760}
    761
    762static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
    763{
    764	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    765	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    766	int ret;
    767
    768	imx8mq_mipi_csi_pm_suspend(dev);
    769
    770	ret = icc_set_bw(state->icc_path, 0, 0);
    771	if (ret)
    772		dev_err(dev, "icc_set_bw failed with %d\n", ret);
    773
    774	return ret;
    775}
    776
    777static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
    778{
    779	struct v4l2_subdev *sd = dev_get_drvdata(dev);
    780	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    781	int ret;
    782
    783	ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw);
    784	if (ret) {
    785		dev_err(dev, "icc_set_bw failed with %d\n", ret);
    786		return ret;
    787	}
    788
    789	return imx8mq_mipi_csi_pm_resume(dev);
    790}
    791
    792static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
    793	SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
    794			   imx8mq_mipi_csi_runtime_resume,
    795			   NULL)
    796	SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
    797};
    798
    799/* -----------------------------------------------------------------------------
    800 * Probe/remove & platform driver
    801 */
    802
    803static int imx8mq_mipi_csi_subdev_init(struct csi_state *state)
    804{
    805	struct v4l2_subdev *sd = &state->sd;
    806
    807	v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops);
    808	sd->owner = THIS_MODULE;
    809	snprintf(sd->name, sizeof(sd->name), "%s %s",
    810		 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev));
    811
    812	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    813
    814	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
    815	sd->entity.ops = &imx8mq_mipi_csi_entity_ops;
    816
    817	sd->dev = state->dev;
    818
    819	state->csi2_fmt = &imx8mq_mipi_csi_formats[0];
    820	imx8mq_mipi_csi_init_cfg(sd, NULL);
    821
    822	state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
    823					 | MEDIA_PAD_FL_MUST_CONNECT;
    824	state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
    825					   | MEDIA_PAD_FL_MUST_CONNECT;
    826	return media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM,
    827				      state->pads);
    828}
    829
    830static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev)
    831{
    832	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
    833	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    834
    835	icc_put(state->icc_path);
    836}
    837
    838static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev)
    839{
    840	struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
    841	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    842
    843	/* Optional interconnect request */
    844	state->icc_path = of_icc_get(&pdev->dev, "dram");
    845	if (IS_ERR_OR_NULL(state->icc_path))
    846		return PTR_ERR_OR_ZERO(state->icc_path);
    847
    848	state->icc_path_bw = MBps_to_icc(700);
    849
    850	return 0;
    851}
    852
    853static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
    854{
    855	struct device *dev = state->dev;
    856	struct device_node *np = state->dev->of_node;
    857	struct device_node *node;
    858	phandle ph;
    859	u32 out_val[2];
    860	int ret = 0;
    861
    862	state->rst = devm_reset_control_array_get_exclusive(dev);
    863	if (IS_ERR(state->rst)) {
    864		dev_err(dev, "Failed to get reset: %pe\n", state->rst);
    865		return PTR_ERR(state->rst);
    866	}
    867
    868	ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
    869					 ARRAY_SIZE(out_val));
    870	if (ret) {
    871		dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
    872		return ret;
    873	}
    874
    875	ph = *out_val;
    876
    877	node = of_find_node_by_phandle(ph);
    878	if (!node) {
    879		dev_err(dev, "Error finding node by phandle\n");
    880		return -ENODEV;
    881	}
    882	state->phy_gpr = syscon_node_to_regmap(node);
    883	of_node_put(node);
    884	if (IS_ERR(state->phy_gpr)) {
    885		dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
    886		return PTR_ERR(state->phy_gpr);
    887	}
    888
    889	state->phy_gpr_reg = out_val[1];
    890	dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
    891
    892	return ret;
    893}
    894
    895static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
    896{
    897	struct device *dev = &pdev->dev;
    898	struct csi_state *state;
    899	int ret;
    900
    901	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
    902	if (!state)
    903		return -ENOMEM;
    904
    905	state->dev = dev;
    906
    907	ret = imx8mq_mipi_csi_parse_dt(state);
    908	if (ret < 0) {
    909		dev_err(dev, "Failed to parse device tree: %d\n", ret);
    910		return ret;
    911	}
    912
    913	/* Acquire resources. */
    914	state->regs = devm_platform_ioremap_resource(pdev, 0);
    915	if (IS_ERR(state->regs))
    916		return PTR_ERR(state->regs);
    917
    918	ret = imx8mq_mipi_csi_clk_get(state);
    919	if (ret < 0)
    920		return ret;
    921
    922	platform_set_drvdata(pdev, &state->sd);
    923
    924	mutex_init(&state->lock);
    925
    926	ret = imx8mq_mipi_csi_subdev_init(state);
    927	if (ret < 0)
    928		goto mutex;
    929
    930	ret = imx8mq_mipi_csi_init_icc(pdev);
    931	if (ret)
    932		goto mutex;
    933
    934	/* Enable runtime PM. */
    935	pm_runtime_enable(dev);
    936	if (!pm_runtime_enabled(dev)) {
    937		ret = imx8mq_mipi_csi_runtime_resume(dev);
    938		if (ret < 0)
    939			goto icc;
    940	}
    941
    942	ret = imx8mq_mipi_csi_async_register(state);
    943	if (ret < 0)
    944		goto cleanup;
    945
    946	return 0;
    947
    948cleanup:
    949	pm_runtime_disable(&pdev->dev);
    950	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
    951
    952	media_entity_cleanup(&state->sd.entity);
    953	v4l2_async_nf_unregister(&state->notifier);
    954	v4l2_async_nf_cleanup(&state->notifier);
    955	v4l2_async_unregister_subdev(&state->sd);
    956icc:
    957	imx8mq_mipi_csi_release_icc(pdev);
    958mutex:
    959	mutex_destroy(&state->lock);
    960
    961	return ret;
    962}
    963
    964static int imx8mq_mipi_csi_remove(struct platform_device *pdev)
    965{
    966	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
    967	struct csi_state *state = mipi_sd_to_csi2_state(sd);
    968
    969	v4l2_async_nf_unregister(&state->notifier);
    970	v4l2_async_nf_cleanup(&state->notifier);
    971	v4l2_async_unregister_subdev(&state->sd);
    972
    973	pm_runtime_disable(&pdev->dev);
    974	imx8mq_mipi_csi_runtime_suspend(&pdev->dev);
    975	media_entity_cleanup(&state->sd.entity);
    976	mutex_destroy(&state->lock);
    977	pm_runtime_set_suspended(&pdev->dev);
    978	imx8mq_mipi_csi_release_icc(pdev);
    979
    980	return 0;
    981}
    982
    983static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
    984	{ .compatible = "fsl,imx8mq-mipi-csi2", },
    985	{ /* sentinel */ },
    986};
    987MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
    988
    989static struct platform_driver imx8mq_mipi_csi_driver = {
    990	.probe		= imx8mq_mipi_csi_probe,
    991	.remove		= imx8mq_mipi_csi_remove,
    992	.driver		= {
    993		.of_match_table = imx8mq_mipi_csi_of_match,
    994		.name		= MIPI_CSI2_DRIVER_NAME,
    995		.pm		= &imx8mq_mipi_csi_pm_ops,
    996	},
    997};
    998
    999module_platform_driver(imx8mq_mipi_csi_driver);
   1000
   1001MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
   1002MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
   1003MODULE_LICENSE("GPL v2");
   1004MODULE_ALIAS("platform:imx8mq-mipi-csi2");