Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: imx: add a driver for i.MX8MQ mipi csi rx phy and controller

Add a driver to support the i.MX8MQ MIPI CSI receiver. The hardware side
is based on
https://source.codeaurora.org/external/imx/linux-imx/tree/drivers/media/platform/imx8/mxc-mipi-csi2_yav.c?h=imx_5.4.70_2.3.0

It's built as part of VIDEO_IMX7_CSI because that's documented to support
i.MX8M platforms. This driver adds i.MX8MQ support where currently only the
i.MX8MM platform has been supported.

Signed-off-by: Martin Kepplinger <martin.kepplinger@puri.sm>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Martin Kepplinger and committed by
Mauro Carvalho Chehab
f33fd8d7 37255747

+992
+1
drivers/staging/media/imx/Makefile
··· 16 16 17 17 obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o 18 18 obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o 19 + obj-$(CONFIG_VIDEO_IMX7_CSI) += imx8mq-mipi-csi2.o
+991
drivers/staging/media/imx/imx8mq-mipi-csi2.c
··· 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 + 66 + enum { 67 + ST_POWERED = 1, 68 + ST_STREAMING = 2, 69 + ST_SUSPENDED = 4, 70 + }; 71 + 72 + enum imx8mq_mipi_csi_clk { 73 + CSI2_CLK_CORE, 74 + CSI2_CLK_ESC, 75 + CSI2_CLK_UI, 76 + CSI2_NUM_CLKS, 77 + }; 78 + 79 + static 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 + 108 + struct 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_fwnode_bus_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 + 139 + struct csi2_pix_format { 140 + u32 code; 141 + u8 width; 142 + }; 143 + 144 + static 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 + .code = MEDIA_BUS_FMT_YUYV8_2X8, 206 + .width = 16, 207 + }, { 208 + .code = MEDIA_BUS_FMT_YUYV8_1X16, 209 + .width = 16, 210 + } 211 + }; 212 + 213 + static const struct csi2_pix_format *find_csi2_format(u32 code) 214 + { 215 + unsigned int i; 216 + 217 + for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++) 218 + if (code == imx8mq_mipi_csi_formats[i].code) 219 + return &imx8mq_mipi_csi_formats[i]; 220 + return NULL; 221 + } 222 + 223 + /* ----------------------------------------------------------------------------- 224 + * Hardware configuration 225 + */ 226 + 227 + static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val) 228 + { 229 + writel(val, state->regs + reg); 230 + } 231 + 232 + static int imx8mq_mipi_csi_sw_reset(struct csi_state *state) 233 + { 234 + int ret; 235 + 236 + /* 237 + * these are most likely self-clearing reset bits. to make it 238 + * more clear, the reset-imx7 driver should implement the 239 + * .reset() operation. 240 + */ 241 + ret = reset_control_assert(state->rst); 242 + if (ret < 0) { 243 + dev_err(state->dev, "Failed to assert resets: %d\n", ret); 244 + return ret; 245 + } 246 + 247 + return 0; 248 + } 249 + 250 + static void imx8mq_mipi_csi_system_enable(struct csi_state *state, int on) 251 + { 252 + if (!on) { 253 + imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf); 254 + return; 255 + } 256 + 257 + regmap_update_bits(state->phy_gpr, 258 + state->phy_gpr_reg, 259 + 0x3fff, 260 + GPR_CSI2_1_RX_ENABLE | 261 + GPR_CSI2_1_VID_INTFC_ENB | 262 + GPR_CSI2_1_HSEL | 263 + GPR_CSI2_1_CONT_CLK_MODE | 264 + GPR_CSI2_1_S_PRG_RXHS_SETTLE(state->hs_settle)); 265 + } 266 + 267 + static void imx8mq_mipi_csi_set_params(struct csi_state *state) 268 + { 269 + int lanes = state->bus.num_data_lanes; 270 + 271 + imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1); 272 + imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 273 + (0xf << lanes) & 0xf); 274 + imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL); 275 + /* 276 + * 0x180 bit 0 controls the Virtual Channel behaviour: when set the 277 + * interface ignores the Virtual Channel (VC) field in received packets; 278 + * when cleared it causes the interface to only accept packets whose VC 279 + * matches the value to which VC is set at offset 0x184. 280 + */ 281 + imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1); 282 + imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL, 283 + CSI2RX_SEND_LEVEL); 284 + } 285 + 286 + static int imx8mq_mipi_csi_clk_enable(struct csi_state *state) 287 + { 288 + return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks); 289 + } 290 + 291 + static void imx8mq_mipi_csi_clk_disable(struct csi_state *state) 292 + { 293 + clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks); 294 + } 295 + 296 + static int imx8mq_mipi_csi_clk_get(struct csi_state *state) 297 + { 298 + unsigned int i; 299 + 300 + for (i = 0; i < CSI2_NUM_CLKS; i++) 301 + state->clks[i].id = imx8mq_mipi_csi_clk_id[i]; 302 + 303 + return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks); 304 + } 305 + 306 + static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state) 307 + { 308 + s64 link_freq; 309 + u32 lane_rate; 310 + unsigned long esc_clk_rate; 311 + u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns; 312 + 313 + /* Calculate the line rate from the pixel rate. */ 314 + link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler, 315 + state->csi2_fmt->width, 316 + state->bus.num_data_lanes * 2); 317 + if (link_freq < 0) { 318 + dev_err(state->dev, "Unable to obtain link frequency: %d\n", 319 + (int)link_freq); 320 + return link_freq; 321 + } 322 + 323 + lane_rate = link_freq * 2; 324 + if (lane_rate < 80000000 || lane_rate > 1500000000) { 325 + dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate); 326 + return -EINVAL; 327 + } 328 + 329 + /* 330 + * The D-PHY specification requires Ths-settle to be in the range 331 + * 85ns + 6*UI to 140ns + 10*UI, with the unit interval UI being half 332 + * the clock period. 333 + * 334 + * The Ths-settle value is expressed in the hardware as a multiple of 335 + * the Esc clock period: 336 + * 337 + * Ths-settle = (PRG_RXHS_SETTLE + 1) * Tperiod of RxClkInEsc 338 + * 339 + * Due to the one cycle inaccuracy introduced by rounding, the 340 + * documentation recommends picking a value away from the boundaries. 341 + * Let's pick the average. 342 + */ 343 + esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk); 344 + if (!esc_clk_rate) { 345 + dev_err(state->dev, "Could not get esc clock rate.\n"); 346 + return -EINVAL; 347 + } 348 + 349 + dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate); 350 + esc_clk_period_ns = 1000000000 / esc_clk_rate; 351 + 352 + min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000); 353 + max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000); 354 + ths_settle_ns = (min_ths_settle + max_ths_settle) / 2; 355 + 356 + state->hs_settle = ths_settle_ns / esc_clk_period_ns - 1; 357 + 358 + dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n", 359 + lane_rate, ths_settle_ns, state->hs_settle); 360 + 361 + return 0; 362 + } 363 + 364 + static int imx8mq_mipi_csi_start_stream(struct csi_state *state) 365 + { 366 + int ret; 367 + 368 + ret = imx8mq_mipi_csi_sw_reset(state); 369 + if (ret) 370 + return ret; 371 + 372 + imx8mq_mipi_csi_set_params(state); 373 + ret = imx8mq_mipi_csi_calc_hs_settle(state); 374 + if (ret) 375 + return ret; 376 + 377 + imx8mq_mipi_csi_system_enable(state, true); 378 + 379 + return 0; 380 + } 381 + 382 + static void imx8mq_mipi_csi_stop_stream(struct csi_state *state) 383 + { 384 + imx8mq_mipi_csi_system_enable(state, false); 385 + } 386 + 387 + /* ----------------------------------------------------------------------------- 388 + * V4L2 subdev operations 389 + */ 390 + 391 + static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev) 392 + { 393 + return container_of(sdev, struct csi_state, sd); 394 + } 395 + 396 + static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable) 397 + { 398 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 399 + int ret = 0; 400 + 401 + imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, 402 + CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE); 403 + 404 + if (enable) { 405 + ret = pm_runtime_resume_and_get(state->dev); 406 + if (ret < 0) 407 + return ret; 408 + } 409 + 410 + mutex_lock(&state->lock); 411 + 412 + if (enable) { 413 + if (state->state & ST_SUSPENDED) { 414 + ret = -EBUSY; 415 + goto unlock; 416 + } 417 + 418 + ret = imx8mq_mipi_csi_start_stream(state); 419 + if (ret < 0) 420 + goto unlock; 421 + 422 + ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1); 423 + if (ret < 0) 424 + goto unlock; 425 + 426 + state->state |= ST_STREAMING; 427 + } else { 428 + v4l2_subdev_call(state->src_sd, video, s_stream, 0); 429 + imx8mq_mipi_csi_stop_stream(state); 430 + state->state &= ~ST_STREAMING; 431 + } 432 + 433 + unlock: 434 + mutex_unlock(&state->lock); 435 + 436 + if (!enable || ret < 0) 437 + pm_runtime_put(state->dev); 438 + 439 + return ret; 440 + } 441 + 442 + static struct v4l2_mbus_framefmt * 443 + imx8mq_mipi_csi_get_format(struct csi_state *state, 444 + struct v4l2_subdev_state *sd_state, 445 + enum v4l2_subdev_format_whence which, 446 + unsigned int pad) 447 + { 448 + if (which == V4L2_SUBDEV_FORMAT_TRY) 449 + return v4l2_subdev_get_try_format(&state->sd, sd_state, pad); 450 + 451 + return &state->format_mbus[pad]; 452 + } 453 + 454 + static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd, 455 + struct v4l2_subdev_state *sd_state) 456 + { 457 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 458 + struct v4l2_mbus_framefmt *fmt_sink; 459 + struct v4l2_mbus_framefmt *fmt_source; 460 + enum v4l2_subdev_format_whence which; 461 + 462 + which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 463 + fmt_sink = imx8mq_mipi_csi_get_format(state, sd_state, which, 464 + MIPI_CSI2_PAD_SINK); 465 + 466 + fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10; 467 + fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH; 468 + fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT; 469 + fmt_sink->field = V4L2_FIELD_NONE; 470 + 471 + fmt_sink->colorspace = V4L2_COLORSPACE_RAW; 472 + fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace); 473 + fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace); 474 + fmt_sink->quantization = 475 + V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace, 476 + fmt_sink->ycbcr_enc); 477 + 478 + fmt_source = imx8mq_mipi_csi_get_format(state, sd_state, which, 479 + MIPI_CSI2_PAD_SOURCE); 480 + *fmt_source = *fmt_sink; 481 + 482 + return 0; 483 + } 484 + 485 + static int imx8mq_mipi_csi_get_fmt(struct v4l2_subdev *sd, 486 + struct v4l2_subdev_state *sd_state, 487 + struct v4l2_subdev_format *sdformat) 488 + { 489 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 490 + struct v4l2_mbus_framefmt *fmt; 491 + 492 + fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which, 493 + sdformat->pad); 494 + 495 + mutex_lock(&state->lock); 496 + 497 + sdformat->format = *fmt; 498 + 499 + mutex_unlock(&state->lock); 500 + 501 + return 0; 502 + } 503 + 504 + static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, 505 + struct v4l2_subdev_state *sd_state, 506 + struct v4l2_subdev_mbus_code_enum *code) 507 + { 508 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 509 + 510 + /* 511 + * We can't transcode in any way, the source format is identical 512 + * to the sink format. 513 + */ 514 + if (code->pad == MIPI_CSI2_PAD_SOURCE) { 515 + struct v4l2_mbus_framefmt *fmt; 516 + 517 + if (code->index > 0) 518 + return -EINVAL; 519 + 520 + fmt = imx8mq_mipi_csi_get_format(state, sd_state, code->which, 521 + code->pad); 522 + code->code = fmt->code; 523 + return 0; 524 + } 525 + 526 + if (code->pad != MIPI_CSI2_PAD_SINK) 527 + return -EINVAL; 528 + 529 + if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats)) 530 + return -EINVAL; 531 + 532 + code->code = imx8mq_mipi_csi_formats[code->index].code; 533 + 534 + return 0; 535 + } 536 + 537 + static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd, 538 + struct v4l2_subdev_state *sd_state, 539 + struct v4l2_subdev_format *sdformat) 540 + { 541 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 542 + struct csi2_pix_format const *csi2_fmt; 543 + struct v4l2_mbus_framefmt *fmt; 544 + 545 + /* 546 + * The device can't transcode in any way, the source format can't be 547 + * modified. 548 + */ 549 + if (sdformat->pad == MIPI_CSI2_PAD_SOURCE) 550 + return imx8mq_mipi_csi_get_fmt(sd, sd_state, sdformat); 551 + 552 + if (sdformat->pad != MIPI_CSI2_PAD_SINK) 553 + return -EINVAL; 554 + 555 + csi2_fmt = find_csi2_format(sdformat->format.code); 556 + if (!csi2_fmt) 557 + csi2_fmt = &imx8mq_mipi_csi_formats[0]; 558 + 559 + fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which, 560 + sdformat->pad); 561 + 562 + mutex_lock(&state->lock); 563 + 564 + fmt->code = csi2_fmt->code; 565 + fmt->width = sdformat->format.width; 566 + fmt->height = sdformat->format.height; 567 + 568 + sdformat->format = *fmt; 569 + 570 + /* Propagate the format from sink to source. */ 571 + fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which, 572 + MIPI_CSI2_PAD_SOURCE); 573 + *fmt = sdformat->format; 574 + 575 + /* Store the CSI2 format descriptor for active formats. */ 576 + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) 577 + state->csi2_fmt = csi2_fmt; 578 + 579 + mutex_unlock(&state->lock); 580 + 581 + return 0; 582 + } 583 + 584 + static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = { 585 + .s_stream = imx8mq_mipi_csi_s_stream, 586 + }; 587 + 588 + static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = { 589 + .init_cfg = imx8mq_mipi_csi_init_cfg, 590 + .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code, 591 + .get_fmt = imx8mq_mipi_csi_get_fmt, 592 + .set_fmt = imx8mq_mipi_csi_set_fmt, 593 + }; 594 + 595 + static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = { 596 + .video = &imx8mq_mipi_csi_video_ops, 597 + .pad = &imx8mq_mipi_csi_pad_ops, 598 + }; 599 + 600 + /* ----------------------------------------------------------------------------- 601 + * Media entity operations 602 + */ 603 + 604 + static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = { 605 + .link_validate = v4l2_subdev_link_validate, 606 + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, 607 + }; 608 + 609 + /* ----------------------------------------------------------------------------- 610 + * Async subdev notifier 611 + */ 612 + 613 + static struct csi_state * 614 + mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n) 615 + { 616 + return container_of(n, struct csi_state, notifier); 617 + } 618 + 619 + static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier, 620 + struct v4l2_subdev *sd, 621 + struct v4l2_async_subdev *asd) 622 + { 623 + struct csi_state *state = mipi_notifier_to_csi2_state(notifier); 624 + struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK]; 625 + 626 + state->src_sd = sd; 627 + 628 + return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | 629 + MEDIA_LNK_FL_IMMUTABLE); 630 + } 631 + 632 + static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = { 633 + .bound = imx8mq_mipi_csi_notify_bound, 634 + }; 635 + 636 + static int imx8mq_mipi_csi_async_register(struct csi_state *state) 637 + { 638 + struct v4l2_fwnode_endpoint vep = { 639 + .bus_type = V4L2_MBUS_CSI2_DPHY, 640 + }; 641 + struct v4l2_async_subdev *asd; 642 + struct fwnode_handle *ep; 643 + unsigned int i; 644 + int ret; 645 + 646 + v4l2_async_notifier_init(&state->notifier); 647 + 648 + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, 649 + FWNODE_GRAPH_ENDPOINT_NEXT); 650 + if (!ep) 651 + return -ENOTCONN; 652 + 653 + ret = v4l2_fwnode_endpoint_parse(ep, &vep); 654 + if (ret) 655 + goto err_parse; 656 + 657 + for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { 658 + if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { 659 + dev_err(state->dev, 660 + "data lanes reordering is not supported"); 661 + ret = -EINVAL; 662 + goto err_parse; 663 + } 664 + } 665 + 666 + state->bus = vep.bus.mipi_csi2; 667 + 668 + dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n", 669 + state->bus.num_data_lanes, 670 + state->bus.flags); 671 + 672 + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&state->notifier, 673 + ep, struct v4l2_async_subdev); 674 + if (IS_ERR(asd)) { 675 + ret = PTR_ERR(asd); 676 + goto err_parse; 677 + } 678 + 679 + fwnode_handle_put(ep); 680 + 681 + state->notifier.ops = &imx8mq_mipi_csi_notify_ops; 682 + 683 + ret = v4l2_async_subdev_notifier_register(&state->sd, &state->notifier); 684 + if (ret) 685 + return ret; 686 + 687 + return v4l2_async_register_subdev(&state->sd); 688 + 689 + err_parse: 690 + fwnode_handle_put(ep); 691 + 692 + return ret; 693 + } 694 + 695 + /* ----------------------------------------------------------------------------- 696 + * Suspend/resume 697 + */ 698 + 699 + static int imx8mq_mipi_csi_pm_suspend(struct device *dev, bool runtime) 700 + { 701 + struct v4l2_subdev *sd = dev_get_drvdata(dev); 702 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 703 + int ret = 0; 704 + 705 + mutex_lock(&state->lock); 706 + 707 + if (state->state & ST_POWERED) { 708 + imx8mq_mipi_csi_stop_stream(state); 709 + imx8mq_mipi_csi_clk_disable(state); 710 + state->state &= ~ST_POWERED; 711 + if (!runtime) 712 + state->state |= ST_SUSPENDED; 713 + } 714 + 715 + mutex_unlock(&state->lock); 716 + 717 + ret = icc_set_bw(state->icc_path, 0, 0); 718 + if (ret) 719 + dev_err(dev, "icc_set_bw failed with %d\n", ret); 720 + 721 + return ret ? -EAGAIN : 0; 722 + } 723 + 724 + static int imx8mq_mipi_csi_pm_resume(struct device *dev, bool runtime) 725 + { 726 + struct v4l2_subdev *sd = dev_get_drvdata(dev); 727 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 728 + int ret = 0; 729 + 730 + ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw); 731 + if (ret) { 732 + dev_err(dev, "icc_set_bw failed with %d\n", ret); 733 + return ret; 734 + } 735 + 736 + mutex_lock(&state->lock); 737 + 738 + if (!runtime && !(state->state & ST_SUSPENDED)) 739 + goto unlock; 740 + 741 + if (!(state->state & ST_POWERED)) { 742 + state->state |= ST_POWERED; 743 + ret = imx8mq_mipi_csi_clk_enable(state); 744 + } 745 + if (state->state & ST_STREAMING) { 746 + ret = imx8mq_mipi_csi_start_stream(state); 747 + if (ret) 748 + goto unlock; 749 + } 750 + 751 + state->state &= ~ST_SUSPENDED; 752 + 753 + unlock: 754 + mutex_unlock(&state->lock); 755 + 756 + return ret ? -EAGAIN : 0; 757 + } 758 + 759 + static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) 760 + { 761 + return imx8mq_mipi_csi_pm_suspend(dev, false); 762 + } 763 + 764 + static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) 765 + { 766 + return imx8mq_mipi_csi_pm_resume(dev, false); 767 + } 768 + 769 + static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) 770 + { 771 + return imx8mq_mipi_csi_pm_suspend(dev, true); 772 + } 773 + 774 + static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) 775 + { 776 + return imx8mq_mipi_csi_pm_resume(dev, true); 777 + } 778 + 779 + static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = { 780 + SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend, 781 + imx8mq_mipi_csi_runtime_resume, 782 + NULL) 783 + SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume) 784 + }; 785 + 786 + /* ----------------------------------------------------------------------------- 787 + * Probe/remove & platform driver 788 + */ 789 + 790 + static int imx8mq_mipi_csi_subdev_init(struct csi_state *state) 791 + { 792 + struct v4l2_subdev *sd = &state->sd; 793 + 794 + v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops); 795 + sd->owner = THIS_MODULE; 796 + snprintf(sd->name, sizeof(sd->name), "%s %s", 797 + MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev)); 798 + 799 + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 800 + 801 + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 802 + sd->entity.ops = &imx8mq_mipi_csi_entity_ops; 803 + 804 + sd->dev = state->dev; 805 + 806 + state->csi2_fmt = &imx8mq_mipi_csi_formats[0]; 807 + imx8mq_mipi_csi_init_cfg(sd, NULL); 808 + 809 + state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK 810 + | MEDIA_PAD_FL_MUST_CONNECT; 811 + state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE 812 + | MEDIA_PAD_FL_MUST_CONNECT; 813 + return media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM, 814 + state->pads); 815 + } 816 + 817 + static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev) 818 + { 819 + struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); 820 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 821 + 822 + icc_put(state->icc_path); 823 + } 824 + 825 + static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev) 826 + { 827 + struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); 828 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 829 + 830 + /* Optional interconnect request */ 831 + state->icc_path = of_icc_get(&pdev->dev, "dram"); 832 + if (IS_ERR_OR_NULL(state->icc_path)) 833 + return PTR_ERR_OR_ZERO(state->icc_path); 834 + 835 + state->icc_path_bw = MBps_to_icc(700); 836 + 837 + return 0; 838 + } 839 + 840 + static int imx8mq_mipi_csi_parse_dt(struct csi_state *state) 841 + { 842 + struct device *dev = state->dev; 843 + struct device_node *np = state->dev->of_node; 844 + struct device_node *node; 845 + phandle ph; 846 + u32 out_val[2]; 847 + int ret = 0; 848 + 849 + state->rst = devm_reset_control_array_get_exclusive(dev); 850 + if (IS_ERR(state->rst)) { 851 + dev_err(dev, "Failed to get reset: %pe\n", state->rst); 852 + return PTR_ERR(state->rst); 853 + } 854 + 855 + ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val, 856 + ARRAY_SIZE(out_val)); 857 + if (ret) { 858 + dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret); 859 + return ret; 860 + } 861 + 862 + ph = *out_val; 863 + 864 + node = of_find_node_by_phandle(ph); 865 + if (!node) { 866 + dev_err(dev, "Error finding node by phandle\n"); 867 + return -ENODEV; 868 + } 869 + state->phy_gpr = syscon_node_to_regmap(node); 870 + of_node_put(node); 871 + if (IS_ERR(state->phy_gpr)) { 872 + dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr); 873 + return PTR_ERR(state->phy_gpr); 874 + } 875 + 876 + state->phy_gpr_reg = out_val[1]; 877 + dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg); 878 + 879 + return ret; 880 + } 881 + 882 + static int imx8mq_mipi_csi_probe(struct platform_device *pdev) 883 + { 884 + struct device *dev = &pdev->dev; 885 + struct csi_state *state; 886 + int ret; 887 + 888 + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 889 + if (!state) 890 + return -ENOMEM; 891 + 892 + state->dev = dev; 893 + 894 + ret = imx8mq_mipi_csi_parse_dt(state); 895 + if (ret < 0) { 896 + dev_err(dev, "Failed to parse device tree: %d\n", ret); 897 + return ret; 898 + } 899 + 900 + /* Acquire resources. */ 901 + state->regs = devm_platform_ioremap_resource(pdev, 0); 902 + if (IS_ERR(state->regs)) 903 + return PTR_ERR(state->regs); 904 + 905 + ret = imx8mq_mipi_csi_clk_get(state); 906 + if (ret < 0) 907 + return ret; 908 + 909 + platform_set_drvdata(pdev, &state->sd); 910 + 911 + mutex_init(&state->lock); 912 + 913 + ret = imx8mq_mipi_csi_subdev_init(state); 914 + if (ret < 0) 915 + goto mutex; 916 + 917 + ret = imx8mq_mipi_csi_init_icc(pdev); 918 + if (ret) 919 + goto mutex; 920 + 921 + /* Enable runtime PM. */ 922 + pm_runtime_enable(dev); 923 + if (!pm_runtime_enabled(dev)) { 924 + ret = imx8mq_mipi_csi_pm_resume(dev, true); 925 + if (ret < 0) 926 + goto icc; 927 + } 928 + 929 + ret = imx8mq_mipi_csi_async_register(state); 930 + if (ret < 0) 931 + goto cleanup; 932 + 933 + return 0; 934 + 935 + cleanup: 936 + pm_runtime_disable(&pdev->dev); 937 + imx8mq_mipi_csi_pm_suspend(&pdev->dev, true); 938 + 939 + media_entity_cleanup(&state->sd.entity); 940 + v4l2_async_notifier_unregister(&state->notifier); 941 + v4l2_async_notifier_cleanup(&state->notifier); 942 + v4l2_async_unregister_subdev(&state->sd); 943 + icc: 944 + imx8mq_mipi_csi_release_icc(pdev); 945 + mutex: 946 + mutex_destroy(&state->lock); 947 + 948 + return ret; 949 + } 950 + 951 + static int imx8mq_mipi_csi_remove(struct platform_device *pdev) 952 + { 953 + struct v4l2_subdev *sd = platform_get_drvdata(pdev); 954 + struct csi_state *state = mipi_sd_to_csi2_state(sd); 955 + 956 + v4l2_async_notifier_unregister(&state->notifier); 957 + v4l2_async_notifier_cleanup(&state->notifier); 958 + v4l2_async_unregister_subdev(&state->sd); 959 + 960 + pm_runtime_disable(&pdev->dev); 961 + imx8mq_mipi_csi_pm_suspend(&pdev->dev, true); 962 + media_entity_cleanup(&state->sd.entity); 963 + mutex_destroy(&state->lock); 964 + pm_runtime_set_suspended(&pdev->dev); 965 + imx8mq_mipi_csi_release_icc(pdev); 966 + 967 + return 0; 968 + } 969 + 970 + static const struct of_device_id imx8mq_mipi_csi_of_match[] = { 971 + { .compatible = "fsl,imx8mq-mipi-csi2", }, 972 + { /* sentinel */ }, 973 + }; 974 + MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match); 975 + 976 + static struct platform_driver imx8mq_mipi_csi_driver = { 977 + .probe = imx8mq_mipi_csi_probe, 978 + .remove = imx8mq_mipi_csi_remove, 979 + .driver = { 980 + .of_match_table = imx8mq_mipi_csi_of_match, 981 + .name = MIPI_CSI2_DRIVER_NAME, 982 + .pm = &imx8mq_mipi_csi_pm_ops, 983 + }, 984 + }; 985 + 986 + module_platform_driver(imx8mq_mipi_csi_driver); 987 + 988 + MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver"); 989 + MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>"); 990 + MODULE_LICENSE("GPL v2"); 991 + MODULE_ALIAS("platform:imx8mq-mipi-csi2");