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

media: synopsys: add driver for the designware mipi csi-2 receiver

The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with
one input port and one output port. It receives the data with the
help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g.,
the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs.

Add a V4L2 subdevice driver for this unit.

Signed-off-by: Michael Riesch <michael.riesch@wolfvision.net>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com>
Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
[Sakari Ailus: Make sparse and smatch happy.]
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>

authored by

Michael Riesch and committed by
Hans Verkuil
355a1100 a1da27d0

+743
+1
MAINTAINERS
··· 25369 25369 L: linux-media@vger.kernel.org 25370 25370 S: Maintained 25371 25371 F: Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml 25372 + F: drivers/media/platform/synopsys/dw-mipi-csi2rx.c 25372 25373 25373 25374 SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER 25374 25375 M: Jaehoon Chung <jh80.chung@samsung.com>
+18
drivers/media/platform/synopsys/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 3 3 source "drivers/media/platform/synopsys/hdmirx/Kconfig" 4 + 5 + config VIDEO_DW_MIPI_CSI2RX 6 + tristate "Synopsys DesignWare MIPI CSI-2 Receiver" 7 + depends on VIDEO_DEV 8 + depends on V4L_PLATFORM_DRIVERS 9 + depends on PM && COMMON_CLK 10 + select MEDIA_CONTROLLER 11 + select V4L2_FWNODE 12 + select VIDEO_V4L2_SUBDEV_API 13 + help 14 + The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with 15 + one input port and one output port. It receives the data with the 16 + help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g., 17 + the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs. 18 + This is a driver for this unit. 19 + 20 + To compile this driver as a module, choose M here: the module 21 + will be called dw-mipi-csi2rx.
+2
drivers/media/platform/synopsys/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 obj-y += hdmirx/ 3 + 4 + obj-$(CONFIG_VIDEO_DW_MIPI_CSI2RX) += dw-mipi-csi2rx.o
+722
drivers/media/platform/synopsys/dw-mipi-csi2rx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Synopsys DesignWare MIPI CSI-2 Receiver Driver 4 + * 5 + * Copyright (C) 2019 Rockchip Electronics Co., Ltd. 6 + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> 7 + * Copyright (C) 2026 Collabora, Ltd. 8 + */ 9 + 10 + #include <linux/clk.h> 11 + #include <linux/delay.h> 12 + #include <linux/io.h> 13 + #include <linux/module.h> 14 + #include <linux/of.h> 15 + #include <linux/phy/phy.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm_runtime.h> 18 + #include <linux/property.h> 19 + #include <linux/reset.h> 20 + 21 + #include <media/mipi-csi2.h> 22 + #include <media/v4l2-ctrls.h> 23 + #include <media/v4l2-fwnode.h> 24 + #include <media/v4l2-mc.h> 25 + #include <media/v4l2-subdev.h> 26 + 27 + #define DW_MIPI_CSI2RX_N_LANES 0x04 28 + #define DW_MIPI_CSI2RX_RESETN 0x10 29 + #define DW_MIPI_CSI2RX_PHY_STATE 0x14 30 + #define DW_MIPI_CSI2RX_ERR1 0x20 31 + #define DW_MIPI_CSI2RX_ERR2 0x24 32 + #define DW_MIPI_CSI2RX_MSK1 0x28 33 + #define DW_MIPI_CSI2RX_MSK2 0x2c 34 + #define DW_MIPI_CSI2RX_CONTROL 0x40 35 + 36 + #define SW_CPHY_EN(x) ((x) << 0) 37 + #define SW_DSI_EN(x) ((x) << 4) 38 + #define SW_DATATYPE_FS(x) ((x) << 8) 39 + #define SW_DATATYPE_FE(x) ((x) << 14) 40 + #define SW_DATATYPE_LS(x) ((x) << 20) 41 + #define SW_DATATYPE_LE(x) ((x) << 26) 42 + 43 + #define DW_MIPI_CSI2RX_CLKS_MAX 1 44 + 45 + enum { 46 + DW_MIPI_CSI2RX_PAD_SINK, 47 + DW_MIPI_CSI2RX_PAD_SRC, 48 + DW_MIPI_CSI2RX_PAD_MAX, 49 + }; 50 + 51 + struct dw_mipi_csi2rx_format { 52 + u32 code; 53 + u8 depth; 54 + u8 csi_dt; 55 + }; 56 + 57 + struct dw_mipi_csi2rx_device { 58 + struct device *dev; 59 + 60 + void __iomem *base_addr; 61 + struct clk_bulk_data *clks; 62 + unsigned int clks_num; 63 + struct phy *phy; 64 + struct reset_control *reset; 65 + 66 + const struct dw_mipi_csi2rx_format *formats; 67 + unsigned int formats_num; 68 + 69 + struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX]; 70 + struct v4l2_async_notifier notifier; 71 + struct v4l2_subdev sd; 72 + 73 + enum v4l2_mbus_type bus_type; 74 + u32 lanes_num; 75 + }; 76 + 77 + static const struct v4l2_mbus_framefmt default_format = { 78 + .width = 3840, 79 + .height = 2160, 80 + .code = MEDIA_BUS_FMT_SRGGB10_1X10, 81 + .field = V4L2_FIELD_NONE, 82 + .colorspace = V4L2_COLORSPACE_RAW, 83 + .ycbcr_enc = V4L2_YCBCR_ENC_601, 84 + .quantization = V4L2_QUANTIZATION_FULL_RANGE, 85 + .xfer_func = V4L2_XFER_FUNC_NONE, 86 + }; 87 + 88 + static const struct dw_mipi_csi2rx_format formats[] = { 89 + /* YUV formats */ 90 + { 91 + .code = MEDIA_BUS_FMT_YUYV8_1X16, 92 + .depth = 16, 93 + .csi_dt = MIPI_CSI2_DT_YUV422_8B, 94 + }, 95 + { 96 + .code = MEDIA_BUS_FMT_UYVY8_1X16, 97 + .depth = 16, 98 + .csi_dt = MIPI_CSI2_DT_YUV422_8B, 99 + }, 100 + { 101 + .code = MEDIA_BUS_FMT_YVYU8_1X16, 102 + .depth = 16, 103 + .csi_dt = MIPI_CSI2_DT_YUV422_8B, 104 + }, 105 + { 106 + .code = MEDIA_BUS_FMT_VYUY8_1X16, 107 + .depth = 16, 108 + .csi_dt = MIPI_CSI2_DT_YUV422_8B, 109 + }, 110 + /* RGB formats */ 111 + { 112 + .code = MEDIA_BUS_FMT_RGB888_1X24, 113 + .depth = 24, 114 + .csi_dt = MIPI_CSI2_DT_RGB888, 115 + }, 116 + { 117 + .code = MEDIA_BUS_FMT_BGR888_1X24, 118 + .depth = 24, 119 + .csi_dt = MIPI_CSI2_DT_RGB888, 120 + }, 121 + /* Bayer formats */ 122 + { 123 + .code = MEDIA_BUS_FMT_SBGGR8_1X8, 124 + .depth = 8, 125 + .csi_dt = MIPI_CSI2_DT_RAW8, 126 + }, 127 + { 128 + .code = MEDIA_BUS_FMT_SGBRG8_1X8, 129 + .depth = 8, 130 + .csi_dt = MIPI_CSI2_DT_RAW8, 131 + }, 132 + { 133 + .code = MEDIA_BUS_FMT_SGRBG8_1X8, 134 + .depth = 8, 135 + .csi_dt = MIPI_CSI2_DT_RAW8, 136 + }, 137 + { 138 + .code = MEDIA_BUS_FMT_SRGGB8_1X8, 139 + .depth = 8, 140 + .csi_dt = MIPI_CSI2_DT_RAW8, 141 + }, 142 + { 143 + .code = MEDIA_BUS_FMT_SBGGR10_1X10, 144 + .depth = 10, 145 + .csi_dt = MIPI_CSI2_DT_RAW10, 146 + }, 147 + { 148 + .code = MEDIA_BUS_FMT_SGBRG10_1X10, 149 + .depth = 10, 150 + .csi_dt = MIPI_CSI2_DT_RAW10, 151 + }, 152 + { 153 + .code = MEDIA_BUS_FMT_SGRBG10_1X10, 154 + .depth = 10, 155 + .csi_dt = MIPI_CSI2_DT_RAW10, 156 + }, 157 + { 158 + .code = MEDIA_BUS_FMT_SRGGB10_1X10, 159 + .depth = 10, 160 + .csi_dt = MIPI_CSI2_DT_RAW10, 161 + }, 162 + { 163 + .code = MEDIA_BUS_FMT_SBGGR12_1X12, 164 + .depth = 12, 165 + .csi_dt = MIPI_CSI2_DT_RAW12, 166 + }, 167 + { 168 + .code = MEDIA_BUS_FMT_SGBRG12_1X12, 169 + .depth = 12, 170 + .csi_dt = MIPI_CSI2_DT_RAW12, 171 + }, 172 + { 173 + .code = MEDIA_BUS_FMT_SGRBG12_1X12, 174 + .depth = 12, 175 + .csi_dt = MIPI_CSI2_DT_RAW12, 176 + }, 177 + { 178 + .code = MEDIA_BUS_FMT_SRGGB12_1X12, 179 + .depth = 12, 180 + .csi_dt = MIPI_CSI2_DT_RAW12, 181 + }, 182 + }; 183 + 184 + static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) 185 + { 186 + return container_of(sd, struct dw_mipi_csi2rx_device, sd); 187 + } 188 + 189 + static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2, 190 + unsigned int addr, u32 val) 191 + { 192 + writel(val, csi2->base_addr + addr); 193 + } 194 + 195 + static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2, 196 + unsigned int addr) 197 + { 198 + return readl(csi2->base_addr + addr); 199 + } 200 + 201 + static const struct dw_mipi_csi2rx_format * 202 + dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) 203 + { 204 + WARN_ON(csi2->formats_num == 0); 205 + 206 + for (unsigned int i = 0; i < csi2->formats_num; i++) { 207 + const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; 208 + 209 + if (format->code == mbus_code) 210 + return format; 211 + } 212 + 213 + return NULL; 214 + } 215 + 216 + static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2) 217 + { 218 + struct media_pad *source_pad; 219 + union phy_configure_opts opts; 220 + u32 lanes = csi2->lanes_num; 221 + u32 control = 0; 222 + s64 link_freq; 223 + int ret; 224 + 225 + if (lanes < 1 || lanes > 4) 226 + return -EINVAL; 227 + 228 + source_pad = media_pad_remote_pad_unique( 229 + &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]); 230 + if (IS_ERR(source_pad)) 231 + return PTR_ERR(source_pad); 232 + 233 + /* set mult and div to 0, thus completely rely on V4L2_CID_LINK_FREQ */ 234 + link_freq = v4l2_get_link_freq(source_pad, 0, 0); 235 + if (link_freq < 0) 236 + return link_freq; 237 + 238 + switch (csi2->bus_type) { 239 + case V4L2_MBUS_CSI2_DPHY: 240 + ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2, 241 + lanes, &opts.mipi_dphy); 242 + if (ret) 243 + return ret; 244 + 245 + ret = phy_set_mode(csi2->phy, PHY_MODE_MIPI_DPHY); 246 + if (ret) 247 + return ret; 248 + 249 + ret = phy_configure(csi2->phy, &opts); 250 + if (ret) 251 + return ret; 252 + 253 + control |= SW_CPHY_EN(0); 254 + break; 255 + 256 + case V4L2_MBUS_CSI2_CPHY: 257 + /* TODO: implement CPHY configuration */ 258 + return -EOPNOTSUPP; 259 + default: 260 + return -EINVAL; 261 + } 262 + 263 + control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) | 264 + SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03); 265 + 266 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1); 267 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control); 268 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1); 269 + 270 + return phy_power_on(csi2->phy); 271 + } 272 + 273 + static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2) 274 + { 275 + phy_power_off(csi2->phy); 276 + 277 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0); 278 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0); 279 + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0); 280 + } 281 + 282 + static const struct media_entity_operations dw_mipi_csi2rx_media_ops = { 283 + .link_validate = v4l2_subdev_link_validate, 284 + }; 285 + 286 + static int 287 + dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, 288 + struct v4l2_subdev_state *sd_state, 289 + struct v4l2_subdev_mbus_code_enum *code) 290 + { 291 + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); 292 + 293 + switch (code->pad) { 294 + case DW_MIPI_CSI2RX_PAD_SRC: 295 + if (code->index) 296 + return -EINVAL; 297 + 298 + code->code = 299 + v4l2_subdev_state_get_format(sd_state, 300 + DW_MIPI_CSI2RX_PAD_SINK)->code; 301 + 302 + return 0; 303 + case DW_MIPI_CSI2RX_PAD_SINK: 304 + if (code->index > csi2->formats_num) 305 + return -EINVAL; 306 + 307 + code->code = csi2->formats[code->index].code; 308 + return 0; 309 + default: 310 + return -EINVAL; 311 + } 312 + } 313 + 314 + static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, 315 + struct v4l2_subdev_state *state, 316 + struct v4l2_subdev_format *format) 317 + { 318 + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); 319 + const struct dw_mipi_csi2rx_format *fmt; 320 + struct v4l2_mbus_framefmt *sink, *src; 321 + 322 + /* the format on the source pad always matches the sink pad */ 323 + if (format->pad == DW_MIPI_CSI2RX_PAD_SRC) 324 + return v4l2_subdev_get_fmt(sd, state, format); 325 + 326 + sink = v4l2_subdev_state_get_format(state, format->pad, format->stream); 327 + if (!sink) 328 + return -EINVAL; 329 + 330 + fmt = dw_mipi_csi2rx_find_format(csi2, format->format.code); 331 + if (!fmt) 332 + format->format = default_format; 333 + 334 + *sink = format->format; 335 + 336 + /* propagate the format to the source pad */ 337 + src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 338 + format->stream); 339 + if (!src) 340 + return -EINVAL; 341 + 342 + *src = *sink; 343 + 344 + return 0; 345 + } 346 + 347 + static int dw_mipi_csi2rx_set_routing(struct v4l2_subdev *sd, 348 + struct v4l2_subdev_state *state, 349 + enum v4l2_subdev_format_whence which, 350 + struct v4l2_subdev_krouting *routing) 351 + { 352 + int ret; 353 + 354 + ret = v4l2_subdev_routing_validate(sd, routing, 355 + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 356 + if (ret) 357 + return ret; 358 + 359 + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, 360 + &default_format); 361 + } 362 + 363 + static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, 364 + struct v4l2_subdev_state *state, 365 + u32 pad, u64 streams_mask) 366 + { 367 + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); 368 + struct v4l2_subdev *remote_sd; 369 + struct media_pad *sink_pad, *remote_pad; 370 + struct device *dev = csi2->dev; 371 + u64 mask; 372 + int ret; 373 + 374 + sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK]; 375 + remote_pad = media_pad_remote_pad_first(sink_pad); 376 + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); 377 + 378 + mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK, 379 + DW_MIPI_CSI2RX_PAD_SRC, 380 + &streams_mask); 381 + 382 + ret = pm_runtime_resume_and_get(dev); 383 + if (ret) 384 + goto err; 385 + 386 + ret = dw_mipi_csi2rx_start(csi2); 387 + if (ret) { 388 + dev_err(dev, "failed to enable CSI hardware\n"); 389 + goto err_pm_runtime_put; 390 + } 391 + 392 + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); 393 + if (ret) 394 + goto err_csi_stop; 395 + 396 + return 0; 397 + 398 + err_csi_stop: 399 + dw_mipi_csi2rx_stop(csi2); 400 + err_pm_runtime_put: 401 + pm_runtime_put(dev); 402 + err: 403 + return ret; 404 + } 405 + 406 + static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, 407 + struct v4l2_subdev_state *state, 408 + u32 pad, u64 streams_mask) 409 + { 410 + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); 411 + struct v4l2_subdev *remote_sd; 412 + struct media_pad *sink_pad, *remote_pad; 413 + struct device *dev = csi2->dev; 414 + u64 mask; 415 + int ret; 416 + 417 + sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK]; 418 + remote_pad = media_pad_remote_pad_first(sink_pad); 419 + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); 420 + 421 + mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK, 422 + DW_MIPI_CSI2RX_PAD_SRC, 423 + &streams_mask); 424 + 425 + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); 426 + 427 + dw_mipi_csi2rx_stop(csi2); 428 + 429 + pm_runtime_put(dev); 430 + 431 + return ret; 432 + } 433 + 434 + static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = { 435 + .enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code, 436 + .get_fmt = v4l2_subdev_get_fmt, 437 + .set_fmt = dw_mipi_csi2rx_set_fmt, 438 + .set_routing = dw_mipi_csi2rx_set_routing, 439 + .enable_streams = dw_mipi_csi2rx_enable_streams, 440 + .disable_streams = dw_mipi_csi2rx_disable_streams, 441 + }; 442 + 443 + static const struct v4l2_subdev_ops dw_mipi_csi2rx_ops = { 444 + .pad = &dw_mipi_csi2rx_pad_ops, 445 + }; 446 + 447 + static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd, 448 + struct v4l2_subdev_state *state) 449 + { 450 + struct v4l2_subdev_route routes[] = { 451 + { 452 + .sink_pad = DW_MIPI_CSI2RX_PAD_SINK, 453 + .sink_stream = 0, 454 + .source_pad = DW_MIPI_CSI2RX_PAD_SRC, 455 + .source_stream = 0, 456 + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 457 + }, 458 + }; 459 + struct v4l2_subdev_krouting routing = { 460 + .len_routes = ARRAY_SIZE(routes), 461 + .num_routes = ARRAY_SIZE(routes), 462 + .routes = routes, 463 + }; 464 + 465 + return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, 466 + &default_format); 467 + } 468 + 469 + static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = { 470 + .init_state = dw_mipi_csi2rx_init_state, 471 + }; 472 + 473 + static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier, 474 + struct v4l2_subdev *sd, 475 + struct v4l2_async_connection *asd) 476 + { 477 + struct dw_mipi_csi2rx_device *csi2 = 478 + container_of(notifier, struct dw_mipi_csi2rx_device, notifier); 479 + struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]; 480 + int ret; 481 + 482 + ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad, 483 + MEDIA_LNK_FL_ENABLED); 484 + if (ret) { 485 + dev_err(csi2->dev, "failed to link source pad of %s\n", 486 + sd->name); 487 + return ret; 488 + } 489 + 490 + return 0; 491 + } 492 + 493 + static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = { 494 + .bound = dw_mipi_csi2rx_notifier_bound, 495 + }; 496 + 497 + static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2) 498 + { 499 + struct v4l2_async_connection *asd; 500 + struct v4l2_async_notifier *ntf = &csi2->notifier; 501 + struct v4l2_fwnode_endpoint vep; 502 + struct v4l2_subdev *sd = &csi2->sd; 503 + struct device *dev = csi2->dev; 504 + int ret; 505 + 506 + struct fwnode_handle *ep __free(fwnode_handle) = 507 + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); 508 + if (!ep) 509 + return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n"); 510 + 511 + vep.bus_type = V4L2_MBUS_UNKNOWN; 512 + ret = v4l2_fwnode_endpoint_parse(ep, &vep); 513 + if (ret) 514 + return dev_err_probe(dev, ret, "failed to parse endpoint\n"); 515 + 516 + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && 517 + vep.bus_type != V4L2_MBUS_CSI2_CPHY) 518 + return dev_err_probe(dev, -EINVAL, 519 + "invalid bus type of endpoint\n"); 520 + 521 + csi2->bus_type = vep.bus_type; 522 + csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes; 523 + 524 + v4l2_async_subdev_nf_init(ntf, sd); 525 + ntf->ops = &dw_mipi_csi2rx_notifier_ops; 526 + 527 + asd = v4l2_async_nf_add_fwnode_remote(ntf, ep, 528 + struct v4l2_async_connection); 529 + if (IS_ERR(asd)) { 530 + ret = PTR_ERR(asd); 531 + goto err_nf_cleanup; 532 + } 533 + 534 + ret = v4l2_async_nf_register(ntf); 535 + if (ret) { 536 + ret = dev_err_probe(dev, ret, "failed to register notifier\n"); 537 + goto err_nf_cleanup; 538 + } 539 + 540 + return 0; 541 + 542 + err_nf_cleanup: 543 + v4l2_async_nf_cleanup(ntf); 544 + 545 + return ret; 546 + } 547 + 548 + static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2) 549 + { 550 + struct media_pad *pads = csi2->pads; 551 + struct v4l2_subdev *sd = &csi2->sd; 552 + int ret; 553 + 554 + ret = dw_mipi_csi2rx_register_notifier(csi2); 555 + if (ret) 556 + goto err; 557 + 558 + v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops); 559 + sd->dev = csi2->dev; 560 + sd->entity.ops = &dw_mipi_csi2rx_media_ops; 561 + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 562 + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; 563 + sd->internal_ops = &dw_mipi_csi2rx_internal_ops; 564 + snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s", 565 + dev_name(csi2->dev)); 566 + 567 + pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK | 568 + MEDIA_PAD_FL_MUST_CONNECT; 569 + pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 570 + ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads); 571 + if (ret) 572 + goto err_notifier_unregister; 573 + 574 + ret = v4l2_subdev_init_finalize(sd); 575 + if (ret) 576 + goto err_entity_cleanup; 577 + 578 + ret = v4l2_async_register_subdev(sd); 579 + if (ret) { 580 + dev_err(sd->dev, "failed to register CSI-2 subdev\n"); 581 + goto err_subdev_cleanup; 582 + } 583 + 584 + return 0; 585 + 586 + err_subdev_cleanup: 587 + v4l2_subdev_cleanup(sd); 588 + err_entity_cleanup: 589 + media_entity_cleanup(&sd->entity); 590 + err_notifier_unregister: 591 + v4l2_async_nf_unregister(&csi2->notifier); 592 + v4l2_async_nf_cleanup(&csi2->notifier); 593 + err: 594 + return ret; 595 + } 596 + 597 + static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2) 598 + { 599 + struct v4l2_subdev *sd = &csi2->sd; 600 + 601 + v4l2_async_unregister_subdev(sd); 602 + v4l2_subdev_cleanup(sd); 603 + media_entity_cleanup(&sd->entity); 604 + v4l2_async_nf_unregister(&csi2->notifier); 605 + v4l2_async_nf_cleanup(&csi2->notifier); 606 + } 607 + 608 + static const struct of_device_id dw_mipi_csi2rx_of_match[] = { 609 + { 610 + .compatible = "rockchip,rk3568-mipi-csi2", 611 + }, 612 + {} 613 + }; 614 + MODULE_DEVICE_TABLE(of, dw_mipi_csi2rx_of_match); 615 + 616 + static int dw_mipi_csi2rx_probe(struct platform_device *pdev) 617 + { 618 + struct device *dev = &pdev->dev; 619 + struct dw_mipi_csi2rx_device *csi2; 620 + int ret; 621 + 622 + csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL); 623 + if (!csi2) 624 + return -ENOMEM; 625 + csi2->dev = dev; 626 + dev_set_drvdata(dev, csi2); 627 + 628 + csi2->base_addr = devm_platform_ioremap_resource(pdev, 0); 629 + if (IS_ERR(csi2->base_addr)) 630 + return PTR_ERR(csi2->base_addr); 631 + 632 + ret = devm_clk_bulk_get_all(dev, &csi2->clks); 633 + if (ret != DW_MIPI_CSI2RX_CLKS_MAX) 634 + return dev_err_probe(dev, -ENODEV, "failed to get clocks\n"); 635 + csi2->clks_num = ret; 636 + 637 + csi2->phy = devm_phy_get(dev, NULL); 638 + if (IS_ERR(csi2->phy)) 639 + return dev_err_probe(dev, PTR_ERR(csi2->phy), 640 + "failed to get MIPI CSI-2 PHY\n"); 641 + 642 + csi2->reset = devm_reset_control_get_exclusive(dev, NULL); 643 + if (IS_ERR(csi2->reset)) 644 + return dev_err_probe(dev, PTR_ERR(csi2->reset), 645 + "failed to get reset\n"); 646 + 647 + csi2->formats = formats; 648 + csi2->formats_num = ARRAY_SIZE(formats); 649 + 650 + ret = devm_pm_runtime_enable(dev); 651 + if (ret) 652 + return dev_err_probe(dev, ret, "failed to enable pm runtime\n"); 653 + 654 + ret = phy_init(csi2->phy); 655 + if (ret) 656 + return dev_err_probe(dev, ret, 657 + "failed to initialize MIPI CSI-2 PHY\n"); 658 + 659 + ret = dw_mipi_csi2rx_register(csi2); 660 + if (ret) 661 + goto err_phy_exit; 662 + 663 + return 0; 664 + 665 + err_phy_exit: 666 + phy_exit(csi2->phy); 667 + 668 + return ret; 669 + } 670 + 671 + static void dw_mipi_csi2rx_remove(struct platform_device *pdev) 672 + { 673 + struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev); 674 + 675 + dw_mipi_csi2rx_unregister(csi2); 676 + phy_exit(csi2->phy); 677 + } 678 + 679 + static int dw_mipi_csi2rx_runtime_suspend(struct device *dev) 680 + { 681 + struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev); 682 + 683 + clk_bulk_disable_unprepare(csi2->clks_num, csi2->clks); 684 + 685 + return 0; 686 + } 687 + 688 + static int dw_mipi_csi2rx_runtime_resume(struct device *dev) 689 + { 690 + struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev); 691 + int ret; 692 + 693 + reset_control_assert(csi2->reset); 694 + udelay(5); 695 + reset_control_deassert(csi2->reset); 696 + 697 + ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks); 698 + if (ret) { 699 + dev_err(dev, "failed to enable clocks\n"); 700 + return ret; 701 + } 702 + 703 + return 0; 704 + } 705 + 706 + static DEFINE_RUNTIME_DEV_PM_OPS(dw_mipi_csi2rx_pm_ops, 707 + dw_mipi_csi2rx_runtime_suspend, 708 + dw_mipi_csi2rx_runtime_resume, NULL); 709 + 710 + static struct platform_driver dw_mipi_csi2rx_drv = { 711 + .driver = { 712 + .name = "dw-mipi-csi2rx", 713 + .of_match_table = dw_mipi_csi2rx_of_match, 714 + .pm = pm_ptr(&dw_mipi_csi2rx_pm_ops), 715 + }, 716 + .probe = dw_mipi_csi2rx_probe, 717 + .remove = dw_mipi_csi2rx_remove, 718 + }; 719 + module_platform_driver(dw_mipi_csi2rx_drv); 720 + 721 + MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Receiver platform driver"); 722 + MODULE_LICENSE("GPL");