162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microchip CSI2 Demux Controller (CSI2DC) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Microchip Technology, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of_graph.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/videodev2.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 2062306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Global configuration register */ 2362306a36Sopenharmony_ci#define CSI2DC_GCFG 0x0 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* MIPI sensor pixel clock is free running */ 2662306a36Sopenharmony_ci#define CSI2DC_GCFG_MIPIFRN BIT(0) 2762306a36Sopenharmony_ci/* GPIO parallel interface selection */ 2862306a36Sopenharmony_ci#define CSI2DC_GCFG_GPIOSEL BIT(1) 2962306a36Sopenharmony_ci/* Output waveform inter-line minimum delay */ 3062306a36Sopenharmony_ci#define CSI2DC_GCFG_HLC(v) ((v) << 4) 3162306a36Sopenharmony_ci#define CSI2DC_GCFG_HLC_MASK GENMASK(7, 4) 3262306a36Sopenharmony_ci/* SAMA7G5 requires a HLC delay of 15 */ 3362306a36Sopenharmony_ci#define SAMA7G5_HLC (15) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Global control register */ 3662306a36Sopenharmony_ci#define CSI2DC_GCTLR 0x04 3762306a36Sopenharmony_ci#define CSI2DC_GCTLR_SWRST BIT(0) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Global status register */ 4062306a36Sopenharmony_ci#define CSI2DC_GS 0x08 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* SSP interrupt status register */ 4362306a36Sopenharmony_ci#define CSI2DC_SSPIS 0x28 4462306a36Sopenharmony_ci/* Pipe update register */ 4562306a36Sopenharmony_ci#define CSI2DC_PU 0xc0 4662306a36Sopenharmony_ci/* Video pipe attributes update */ 4762306a36Sopenharmony_ci#define CSI2DC_PU_VP BIT(0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Pipe update status register */ 5062306a36Sopenharmony_ci#define CSI2DC_PUS 0xc4 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Video pipeline Interrupt Status Register */ 5362306a36Sopenharmony_ci#define CSI2DC_VPISR 0xf4 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Video pipeline enable register */ 5662306a36Sopenharmony_ci#define CSI2DC_VPE 0xf8 5762306a36Sopenharmony_ci#define CSI2DC_VPE_ENABLE BIT(0) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Video pipeline configuration register */ 6062306a36Sopenharmony_ci#define CSI2DC_VPCFG 0xfc 6162306a36Sopenharmony_ci/* Data type */ 6262306a36Sopenharmony_ci#define CSI2DC_VPCFG_DT(v) ((v) << 0) 6362306a36Sopenharmony_ci#define CSI2DC_VPCFG_DT_MASK GENMASK(5, 0) 6462306a36Sopenharmony_ci/* Virtual channel identifier */ 6562306a36Sopenharmony_ci#define CSI2DC_VPCFG_VC(v) ((v) << 6) 6662306a36Sopenharmony_ci#define CSI2DC_VPCFG_VC_MASK GENMASK(7, 6) 6762306a36Sopenharmony_ci/* Decompression enable */ 6862306a36Sopenharmony_ci#define CSI2DC_VPCFG_DE BIT(8) 6962306a36Sopenharmony_ci/* Decoder mode */ 7062306a36Sopenharmony_ci#define CSI2DC_VPCFG_DM(v) ((v) << 9) 7162306a36Sopenharmony_ci#define CSI2DC_VPCFG_DM_DECODER8TO12 0 7262306a36Sopenharmony_ci/* Decoder predictor 2 selection */ 7362306a36Sopenharmony_ci#define CSI2DC_VPCFG_DP2 BIT(12) 7462306a36Sopenharmony_ci/* Recommended memory storage */ 7562306a36Sopenharmony_ci#define CSI2DC_VPCFG_RMS BIT(13) 7662306a36Sopenharmony_ci/* Post adjustment */ 7762306a36Sopenharmony_ci#define CSI2DC_VPCFG_PA BIT(14) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Video pipeline column register */ 8062306a36Sopenharmony_ci#define CSI2DC_VPCOL 0x100 8162306a36Sopenharmony_ci/* Column number */ 8262306a36Sopenharmony_ci#define CSI2DC_VPCOL_COL(v) ((v) << 0) 8362306a36Sopenharmony_ci#define CSI2DC_VPCOL_COL_MASK GENMASK(15, 0) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Video pipeline row register */ 8662306a36Sopenharmony_ci#define CSI2DC_VPROW 0x104 8762306a36Sopenharmony_ci/* Row number */ 8862306a36Sopenharmony_ci#define CSI2DC_VPROW_ROW(v) ((v) << 0) 8962306a36Sopenharmony_ci#define CSI2DC_VPROW_ROW_MASK GENMASK(15, 0) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Version register */ 9262306a36Sopenharmony_ci#define CSI2DC_VERSION 0x1fc 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* register read/write helpers */ 9562306a36Sopenharmony_ci#define csi2dc_readl(st, reg) readl_relaxed((st)->base + (reg)) 9662306a36Sopenharmony_ci#define csi2dc_writel(st, reg, val) writel_relaxed((val), \ 9762306a36Sopenharmony_ci (st)->base + (reg)) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* supported RAW data types */ 10062306a36Sopenharmony_ci#define CSI2DC_DT_RAW6 0x28 10162306a36Sopenharmony_ci#define CSI2DC_DT_RAW7 0x29 10262306a36Sopenharmony_ci#define CSI2DC_DT_RAW8 0x2a 10362306a36Sopenharmony_ci#define CSI2DC_DT_RAW10 0x2b 10462306a36Sopenharmony_ci#define CSI2DC_DT_RAW12 0x2c 10562306a36Sopenharmony_ci#define CSI2DC_DT_RAW14 0x2d 10662306a36Sopenharmony_ci/* YUV data types */ 10762306a36Sopenharmony_ci#define CSI2DC_DT_YUV422_8B 0x1e 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * struct csi2dc_format - CSI2DC format type struct 11162306a36Sopenharmony_ci * @mbus_code: Media bus code for the format 11262306a36Sopenharmony_ci * @dt: Data type constant for this format 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_cistruct csi2dc_format { 11562306a36Sopenharmony_ci u32 mbus_code; 11662306a36Sopenharmony_ci u32 dt; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct csi2dc_format csi2dc_formats[] = { 12062306a36Sopenharmony_ci { 12162306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, 12262306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW8, 12362306a36Sopenharmony_ci }, { 12462306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 12562306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW8, 12662306a36Sopenharmony_ci }, { 12762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 12862306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW8, 12962306a36Sopenharmony_ci }, { 13062306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, 13162306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW8, 13262306a36Sopenharmony_ci }, { 13362306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 13462306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW10, 13562306a36Sopenharmony_ci }, { 13662306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 13762306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW10, 13862306a36Sopenharmony_ci }, { 13962306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 14062306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW10, 14162306a36Sopenharmony_ci }, { 14262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 14362306a36Sopenharmony_ci .dt = CSI2DC_DT_RAW10, 14462306a36Sopenharmony_ci }, { 14562306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 14662306a36Sopenharmony_ci .dt = CSI2DC_DT_YUV422_8B, 14762306a36Sopenharmony_ci }, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cienum mipi_csi_pads { 15162306a36Sopenharmony_ci CSI2DC_PAD_SINK = 0, 15262306a36Sopenharmony_ci CSI2DC_PAD_SOURCE = 1, 15362306a36Sopenharmony_ci CSI2DC_PADS_NUM = 2, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * struct csi2dc_device - CSI2DC device driver data/config struct 15862306a36Sopenharmony_ci * @base: Register map base address 15962306a36Sopenharmony_ci * @csi2dc_sd: v4l2 subdevice for the csi2dc device 16062306a36Sopenharmony_ci * This is the subdevice that the csi2dc device itself 16162306a36Sopenharmony_ci * registers in v4l2 subsystem 16262306a36Sopenharmony_ci * @dev: struct device for this csi2dc device 16362306a36Sopenharmony_ci * @pclk: Peripheral clock reference 16462306a36Sopenharmony_ci * Input clock that clocks the hardware block internal 16562306a36Sopenharmony_ci * logic 16662306a36Sopenharmony_ci * @scck: Sensor Controller clock reference 16762306a36Sopenharmony_ci * Input clock that is used to generate the pixel clock 16862306a36Sopenharmony_ci * @format: Current saved format used in g/s fmt 16962306a36Sopenharmony_ci * @cur_fmt: Current state format 17062306a36Sopenharmony_ci * @try_fmt: Try format that is being tried 17162306a36Sopenharmony_ci * @pads: Media entity pads for the csi2dc subdevice 17262306a36Sopenharmony_ci * @clk_gated: Whether the clock is gated or free running 17362306a36Sopenharmony_ci * @video_pipe: Whether video pipeline is configured 17462306a36Sopenharmony_ci * @parallel_mode: The underlying subdevice is connected on a parallel bus 17562306a36Sopenharmony_ci * @vc: Current set virtual channel 17662306a36Sopenharmony_ci * @notifier: Async notifier that is used to bound the underlying 17762306a36Sopenharmony_ci * subdevice to the csi2dc subdevice 17862306a36Sopenharmony_ci * @input_sd: Reference to the underlying subdevice bound to the 17962306a36Sopenharmony_ci * csi2dc subdevice 18062306a36Sopenharmony_ci * @remote_pad: Pad number of the underlying subdevice that is linked 18162306a36Sopenharmony_ci * to the csi2dc subdevice sink pad. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistruct csi2dc_device { 18462306a36Sopenharmony_ci void __iomem *base; 18562306a36Sopenharmony_ci struct v4l2_subdev csi2dc_sd; 18662306a36Sopenharmony_ci struct device *dev; 18762306a36Sopenharmony_ci struct clk *pclk; 18862306a36Sopenharmony_ci struct clk *scck; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci struct v4l2_mbus_framefmt format; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci const struct csi2dc_format *cur_fmt; 19362306a36Sopenharmony_ci const struct csi2dc_format *try_fmt; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci struct media_pad pads[CSI2DC_PADS_NUM]; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci bool clk_gated; 19862306a36Sopenharmony_ci bool video_pipe; 19962306a36Sopenharmony_ci bool parallel_mode; 20062306a36Sopenharmony_ci u32 vc; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci struct v4l2_async_notifier notifier; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci struct v4l2_subdev *input_sd; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci u32 remote_pad; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline struct csi2dc_device * 21062306a36Sopenharmony_cicsi2dc_sd_to_csi2dc_device(struct v4l2_subdev *csi2dc_sd) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci return container_of(csi2dc_sd, struct csi2dc_device, csi2dc_sd); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int csi2dc_enum_mbus_code(struct v4l2_subdev *csi2dc_sd, 21662306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 21762306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (code->index >= ARRAY_SIZE(csi2dc_formats)) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci code->code = csi2dc_formats[code->index].mbus_code; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd, 22862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 22962306a36Sopenharmony_ci struct v4l2_subdev_format *format) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); 23262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *v4l2_try_fmt; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 23562306a36Sopenharmony_ci v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 23662306a36Sopenharmony_ci format->pad); 23762306a36Sopenharmony_ci format->format = *v4l2_try_fmt; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci format->format = csi2dc->format; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd, 24862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 24962306a36Sopenharmony_ci struct v4l2_subdev_format *req_fmt) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); 25262306a36Sopenharmony_ci const struct csi2dc_format *fmt, *try_fmt = NULL; 25362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *v4l2_try_fmt; 25462306a36Sopenharmony_ci unsigned int i; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Setting the source pad is disabled. 25862306a36Sopenharmony_ci * The same format is being propagated from the sink to source. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci if (req_fmt->pad == CSI2DC_PAD_SOURCE) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(csi2dc_formats); i++) { 26462306a36Sopenharmony_ci fmt = &csi2dc_formats[i]; 26562306a36Sopenharmony_ci if (req_fmt->format.code == fmt->mbus_code) 26662306a36Sopenharmony_ci try_fmt = fmt; 26762306a36Sopenharmony_ci fmt++; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* in case we could not find the desired format, default to something */ 27162306a36Sopenharmony_ci if (!try_fmt) { 27262306a36Sopenharmony_ci try_fmt = &csi2dc_formats[0]; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 27562306a36Sopenharmony_ci "CSI2DC unsupported format 0x%x, defaulting to 0x%x\n", 27662306a36Sopenharmony_ci req_fmt->format.code, csi2dc_formats[0].mbus_code); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci req_fmt->format.code = try_fmt->mbus_code; 28062306a36Sopenharmony_ci req_fmt->format.colorspace = V4L2_COLORSPACE_SRGB; 28162306a36Sopenharmony_ci req_fmt->format.field = V4L2_FIELD_NONE; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 28462306a36Sopenharmony_ci v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 28562306a36Sopenharmony_ci req_fmt->pad); 28662306a36Sopenharmony_ci *v4l2_try_fmt = req_fmt->format; 28762306a36Sopenharmony_ci /* Trying on the sink pad makes the source pad change too */ 28862306a36Sopenharmony_ci v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, 28962306a36Sopenharmony_ci sd_state, 29062306a36Sopenharmony_ci CSI2DC_PAD_SOURCE); 29162306a36Sopenharmony_ci *v4l2_try_fmt = req_fmt->format; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* if we are just trying, we are done */ 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* save the format for later requests */ 29862306a36Sopenharmony_ci csi2dc->format = req_fmt->format; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* update config */ 30162306a36Sopenharmony_ci csi2dc->cur_fmt = try_fmt; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci dev_dbg(csi2dc->dev, "new format set: 0x%x @%dx%d\n", 30462306a36Sopenharmony_ci csi2dc->format.code, csi2dc->format.width, 30562306a36Sopenharmony_ci csi2dc->format.height); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int csi2dc_power(struct csi2dc_device *csi2dc, int on) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci int ret = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (on) { 31562306a36Sopenharmony_ci ret = clk_prepare_enable(csi2dc->pclk); 31662306a36Sopenharmony_ci if (ret) { 31762306a36Sopenharmony_ci dev_err(csi2dc->dev, "failed to enable pclk:%d\n", ret); 31862306a36Sopenharmony_ci return ret; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = clk_prepare_enable(csi2dc->scck); 32262306a36Sopenharmony_ci if (ret) { 32362306a36Sopenharmony_ci dev_err(csi2dc->dev, "failed to enable scck:%d\n", ret); 32462306a36Sopenharmony_ci clk_disable_unprepare(csi2dc->pclk); 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* if powering up, deassert reset line */ 32962306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_GCTLR, CSI2DC_GCTLR_SWRST); 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci /* if powering down, assert reset line */ 33262306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_GCTLR, 0); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci clk_disable_unprepare(csi2dc->scck); 33562306a36Sopenharmony_ci clk_disable_unprepare(csi2dc->pclk); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int csi2dc_get_mbus_config(struct csi2dc_device *csi2dc) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct v4l2_mbus_config mbus_config = { 0 }; 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = v4l2_subdev_call(csi2dc->input_sd, pad, get_mbus_config, 34762306a36Sopenharmony_ci csi2dc->remote_pad, &mbus_config); 34862306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD) { 34962306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 35062306a36Sopenharmony_ci "no remote mbus configuration available\n"); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (ret) { 35562306a36Sopenharmony_ci dev_err(csi2dc->dev, 35662306a36Sopenharmony_ci "failed to get remote mbus configuration\n"); 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci dev_dbg(csi2dc->dev, "subdev sending on channel %d\n", csi2dc->vc); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci csi2dc->clk_gated = mbus_config.bus.parallel.flags & 36362306a36Sopenharmony_ci V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci dev_dbg(csi2dc->dev, "mbus_config: %s clock\n", 36662306a36Sopenharmony_ci csi2dc->clk_gated ? "gated" : "free running"); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void csi2dc_vp_update(struct csi2dc_device *csi2dc) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci u32 vp, gcfg; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!csi2dc->video_pipe) { 37662306a36Sopenharmony_ci dev_err(csi2dc->dev, "video pipeline unavailable\n"); 37762306a36Sopenharmony_ci return; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (csi2dc->parallel_mode) { 38162306a36Sopenharmony_ci /* In parallel mode, GPIO parallel interface must be selected */ 38262306a36Sopenharmony_ci gcfg = csi2dc_readl(csi2dc, CSI2DC_GCFG); 38362306a36Sopenharmony_ci gcfg |= CSI2DC_GCFG_GPIOSEL; 38462306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_GCFG, gcfg); 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* serial video pipeline */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_GCFG, 39162306a36Sopenharmony_ci (SAMA7G5_HLC & CSI2DC_GCFG_HLC_MASK) | 39262306a36Sopenharmony_ci (csi2dc->clk_gated ? 0 : CSI2DC_GCFG_MIPIFRN)); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci vp = CSI2DC_VPCFG_DT(csi2dc->cur_fmt->dt) & CSI2DC_VPCFG_DT_MASK; 39562306a36Sopenharmony_ci vp |= CSI2DC_VPCFG_VC(csi2dc->vc) & CSI2DC_VPCFG_VC_MASK; 39662306a36Sopenharmony_ci vp &= ~CSI2DC_VPCFG_DE; 39762306a36Sopenharmony_ci vp |= CSI2DC_VPCFG_DM(CSI2DC_VPCFG_DM_DECODER8TO12); 39862306a36Sopenharmony_ci vp &= ~CSI2DC_VPCFG_DP2; 39962306a36Sopenharmony_ci vp &= ~CSI2DC_VPCFG_RMS; 40062306a36Sopenharmony_ci vp |= CSI2DC_VPCFG_PA; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_VPCFG, vp); 40362306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_VPE, CSI2DC_VPE_ENABLE); 40462306a36Sopenharmony_ci csi2dc_writel(csi2dc, CSI2DC_PU, CSI2DC_PU_VP); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd); 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (enable) { 41362306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(csi2dc->dev); 41462306a36Sopenharmony_ci if (ret < 0) 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci csi2dc_get_mbus_config(csi2dc); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci csi2dc_vp_update(csi2dc); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return v4l2_subdev_call(csi2dc->input_sd, video, s_stream, 42262306a36Sopenharmony_ci true); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 42662306a36Sopenharmony_ci "Last frame received: VPCOLR = %u, VPROWR= %u, VPISR = %x\n", 42762306a36Sopenharmony_ci csi2dc_readl(csi2dc, CSI2DC_VPCOL), 42862306a36Sopenharmony_ci csi2dc_readl(csi2dc, CSI2DC_VPROW), 42962306a36Sopenharmony_ci csi2dc_readl(csi2dc, CSI2DC_VPISR)); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* stop streaming scenario */ 43262306a36Sopenharmony_ci ret = v4l2_subdev_call(csi2dc->input_sd, video, s_stream, false); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci pm_runtime_put_sync(csi2dc->dev); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd, 44062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *v4l2_try_fmt = 44362306a36Sopenharmony_ci v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci v4l2_try_fmt->height = 480; 44662306a36Sopenharmony_ci v4l2_try_fmt->width = 640; 44762306a36Sopenharmony_ci v4l2_try_fmt->code = csi2dc_formats[0].mbus_code; 44862306a36Sopenharmony_ci v4l2_try_fmt->colorspace = V4L2_COLORSPACE_SRGB; 44962306a36Sopenharmony_ci v4l2_try_fmt->field = V4L2_FIELD_NONE; 45062306a36Sopenharmony_ci v4l2_try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 45162306a36Sopenharmony_ci v4l2_try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; 45262306a36Sopenharmony_ci v4l2_try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct media_entity_operations csi2dc_entity_ops = { 45862306a36Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops csi2dc_pad_ops = { 46262306a36Sopenharmony_ci .enum_mbus_code = csi2dc_enum_mbus_code, 46362306a36Sopenharmony_ci .set_fmt = csi2dc_set_fmt, 46462306a36Sopenharmony_ci .get_fmt = csi2dc_get_fmt, 46562306a36Sopenharmony_ci .init_cfg = csi2dc_init_cfg, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops csi2dc_video_ops = { 46962306a36Sopenharmony_ci .s_stream = csi2dc_s_stream, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic const struct v4l2_subdev_ops csi2dc_subdev_ops = { 47362306a36Sopenharmony_ci .pad = &csi2dc_pad_ops, 47462306a36Sopenharmony_ci .video = &csi2dc_video_ops, 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int csi2dc_async_bound(struct v4l2_async_notifier *notifier, 47862306a36Sopenharmony_ci struct v4l2_subdev *subdev, 47962306a36Sopenharmony_ci struct v4l2_async_connection *asd) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct csi2dc_device *csi2dc = container_of(notifier, 48262306a36Sopenharmony_ci struct csi2dc_device, notifier); 48362306a36Sopenharmony_ci int pad; 48462306a36Sopenharmony_ci int ret; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci csi2dc->input_sd = subdev; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, 48962306a36Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 49062306a36Sopenharmony_ci if (pad < 0) { 49162306a36Sopenharmony_ci dev_err(csi2dc->dev, "Failed to find pad for %s\n", 49262306a36Sopenharmony_ci subdev->name); 49362306a36Sopenharmony_ci return pad; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci csi2dc->remote_pad = pad; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = media_create_pad_link(&csi2dc->input_sd->entity, 49962306a36Sopenharmony_ci csi2dc->remote_pad, 50062306a36Sopenharmony_ci &csi2dc->csi2dc_sd.entity, 0, 50162306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 50262306a36Sopenharmony_ci if (ret) { 50362306a36Sopenharmony_ci dev_err(csi2dc->dev, 50462306a36Sopenharmony_ci "Failed to create pad link: %s to %s\n", 50562306a36Sopenharmony_ci csi2dc->input_sd->entity.name, 50662306a36Sopenharmony_ci csi2dc->csi2dc_sd.entity.name); 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_dbg(csi2dc->dev, "link with %s pad: %d\n", 51162306a36Sopenharmony_ci csi2dc->input_sd->name, csi2dc->remote_pad); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations csi2dc_async_ops = { 51762306a36Sopenharmony_ci .bound = csi2dc_async_bound, 51862306a36Sopenharmony_ci}; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int csi2dc_prepare_notifier(struct csi2dc_device *csi2dc, 52162306a36Sopenharmony_ci struct fwnode_handle *input_fwnode) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct v4l2_async_connection *asd; 52462306a36Sopenharmony_ci int ret = 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci v4l2_async_subdev_nf_init(&csi2dc->notifier, &csi2dc->csi2dc_sd); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci asd = v4l2_async_nf_add_fwnode_remote(&csi2dc->notifier, 52962306a36Sopenharmony_ci input_fwnode, 53062306a36Sopenharmony_ci struct v4l2_async_connection); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci fwnode_handle_put(input_fwnode); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (IS_ERR(asd)) { 53562306a36Sopenharmony_ci ret = PTR_ERR(asd); 53662306a36Sopenharmony_ci dev_err(csi2dc->dev, 53762306a36Sopenharmony_ci "failed to add async notifier for node %pOF: %d\n", 53862306a36Sopenharmony_ci to_of_node(input_fwnode), ret); 53962306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi2dc->notifier); 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci csi2dc->notifier.ops = &csi2dc_async_ops; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ret = v4l2_async_nf_register(&csi2dc->notifier); 54662306a36Sopenharmony_ci if (ret) { 54762306a36Sopenharmony_ci dev_err(csi2dc->dev, "fail to register async notifier: %d\n", 54862306a36Sopenharmony_ci ret); 54962306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi2dc->notifier); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return ret; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int csi2dc_of_parse(struct csi2dc_device *csi2dc, 55662306a36Sopenharmony_ci struct device_node *of_node) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct fwnode_handle *input_fwnode, *output_fwnode; 55962306a36Sopenharmony_ci struct v4l2_fwnode_endpoint input_endpoint = { 0 }, 56062306a36Sopenharmony_ci output_endpoint = { 0 }; 56162306a36Sopenharmony_ci int ret; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci input_fwnode = fwnode_graph_get_next_endpoint(of_fwnode_handle(of_node), 56462306a36Sopenharmony_ci NULL); 56562306a36Sopenharmony_ci if (!input_fwnode) { 56662306a36Sopenharmony_ci dev_err(csi2dc->dev, 56762306a36Sopenharmony_ci "missing port node at %pOF, input node is mandatory.\n", 56862306a36Sopenharmony_ci of_node); 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(input_fwnode, &input_endpoint); 57362306a36Sopenharmony_ci if (ret) { 57462306a36Sopenharmony_ci dev_err(csi2dc->dev, "endpoint not defined at %pOF\n", of_node); 57562306a36Sopenharmony_ci goto csi2dc_of_parse_err; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (input_endpoint.bus_type == V4L2_MBUS_PARALLEL || 57962306a36Sopenharmony_ci input_endpoint.bus_type == V4L2_MBUS_BT656) { 58062306a36Sopenharmony_ci csi2dc->parallel_mode = true; 58162306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 58262306a36Sopenharmony_ci "subdevice connected on parallel interface\n"); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (input_endpoint.bus_type == V4L2_MBUS_CSI2_DPHY) { 58662306a36Sopenharmony_ci csi2dc->clk_gated = input_endpoint.bus.mipi_csi2.flags & 58762306a36Sopenharmony_ci V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; 58862306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 58962306a36Sopenharmony_ci "subdevice connected on serial interface\n"); 59062306a36Sopenharmony_ci dev_dbg(csi2dc->dev, "DT: %s clock\n", 59162306a36Sopenharmony_ci csi2dc->clk_gated ? "gated" : "free running"); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci output_fwnode = fwnode_graph_get_next_endpoint 59562306a36Sopenharmony_ci (of_fwnode_handle(of_node), input_fwnode); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (output_fwnode) 59862306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(output_fwnode, 59962306a36Sopenharmony_ci &output_endpoint); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci fwnode_handle_put(output_fwnode); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!output_fwnode || ret) { 60462306a36Sopenharmony_ci dev_info(csi2dc->dev, 60562306a36Sopenharmony_ci "missing output node at %pOF, data pipe available only.\n", 60662306a36Sopenharmony_ci of_node); 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci if (output_endpoint.bus_type != V4L2_MBUS_PARALLEL && 60962306a36Sopenharmony_ci output_endpoint.bus_type != V4L2_MBUS_BT656) { 61062306a36Sopenharmony_ci dev_err(csi2dc->dev, 61162306a36Sopenharmony_ci "output port must be parallel/bt656.\n"); 61262306a36Sopenharmony_ci ret = -EINVAL; 61362306a36Sopenharmony_ci goto csi2dc_of_parse_err; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci csi2dc->video_pipe = true; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci dev_dbg(csi2dc->dev, 61962306a36Sopenharmony_ci "block %pOF [%d.%d]->[%d.%d] video pipeline\n", 62062306a36Sopenharmony_ci of_node, input_endpoint.base.port, 62162306a36Sopenharmony_ci input_endpoint.base.id, output_endpoint.base.port, 62262306a36Sopenharmony_ci output_endpoint.base.id); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* prepare async notifier for subdevice completion */ 62662306a36Sopenharmony_ci return csi2dc_prepare_notifier(csi2dc, input_fwnode); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cicsi2dc_of_parse_err: 62962306a36Sopenharmony_ci fwnode_handle_put(input_fwnode); 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void csi2dc_default_format(struct csi2dc_device *csi2dc) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci csi2dc->cur_fmt = &csi2dc_formats[0]; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci csi2dc->format.height = 480; 63862306a36Sopenharmony_ci csi2dc->format.width = 640; 63962306a36Sopenharmony_ci csi2dc->format.code = csi2dc_formats[0].mbus_code; 64062306a36Sopenharmony_ci csi2dc->format.colorspace = V4L2_COLORSPACE_SRGB; 64162306a36Sopenharmony_ci csi2dc->format.field = V4L2_FIELD_NONE; 64262306a36Sopenharmony_ci csi2dc->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 64362306a36Sopenharmony_ci csi2dc->format.quantization = V4L2_QUANTIZATION_DEFAULT; 64462306a36Sopenharmony_ci csi2dc->format.xfer_func = V4L2_XFER_FUNC_DEFAULT; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int csi2dc_probe(struct platform_device *pdev) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 65062306a36Sopenharmony_ci struct csi2dc_device *csi2dc; 65162306a36Sopenharmony_ci int ret = 0; 65262306a36Sopenharmony_ci u32 ver; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci csi2dc = devm_kzalloc(dev, sizeof(*csi2dc), GFP_KERNEL); 65562306a36Sopenharmony_ci if (!csi2dc) 65662306a36Sopenharmony_ci return -ENOMEM; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci csi2dc->dev = dev; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci csi2dc->base = devm_platform_ioremap_resource(pdev, 0); 66162306a36Sopenharmony_ci if (IS_ERR(csi2dc->base)) { 66262306a36Sopenharmony_ci dev_err(dev, "base address not set\n"); 66362306a36Sopenharmony_ci return PTR_ERR(csi2dc->base); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci csi2dc->pclk = devm_clk_get(dev, "pclk"); 66762306a36Sopenharmony_ci if (IS_ERR(csi2dc->pclk)) { 66862306a36Sopenharmony_ci ret = PTR_ERR(csi2dc->pclk); 66962306a36Sopenharmony_ci dev_err(dev, "failed to get pclk: %d\n", ret); 67062306a36Sopenharmony_ci return ret; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci csi2dc->scck = devm_clk_get(dev, "scck"); 67462306a36Sopenharmony_ci if (IS_ERR(csi2dc->scck)) { 67562306a36Sopenharmony_ci ret = PTR_ERR(csi2dc->scck); 67662306a36Sopenharmony_ci dev_err(dev, "failed to get scck: %d\n", ret); 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci csi2dc->csi2dc_sd.owner = THIS_MODULE; 68362306a36Sopenharmony_ci csi2dc->csi2dc_sd.dev = dev; 68462306a36Sopenharmony_ci snprintf(csi2dc->csi2dc_sd.name, sizeof(csi2dc->csi2dc_sd.name), 68562306a36Sopenharmony_ci "csi2dc"); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci csi2dc->csi2dc_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 68862306a36Sopenharmony_ci csi2dc->csi2dc_sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 68962306a36Sopenharmony_ci csi2dc->csi2dc_sd.entity.ops = &csi2dc_entity_ops; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci platform_set_drvdata(pdev, csi2dc); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ret = csi2dc_of_parse(csi2dc, dev->of_node); 69462306a36Sopenharmony_ci if (ret) 69562306a36Sopenharmony_ci goto csi2dc_probe_cleanup_entity; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci csi2dc->pads[CSI2DC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 69862306a36Sopenharmony_ci if (csi2dc->video_pipe) 69962306a36Sopenharmony_ci csi2dc->pads[CSI2DC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ret = media_entity_pads_init(&csi2dc->csi2dc_sd.entity, 70262306a36Sopenharmony_ci csi2dc->video_pipe ? CSI2DC_PADS_NUM : 1, 70362306a36Sopenharmony_ci csi2dc->pads); 70462306a36Sopenharmony_ci if (ret < 0) { 70562306a36Sopenharmony_ci dev_err(dev, "media entity init failed\n"); 70662306a36Sopenharmony_ci goto csi2dc_probe_cleanup_notifier; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci csi2dc_default_format(csi2dc); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* turn power on to validate capabilities */ 71262306a36Sopenharmony_ci ret = csi2dc_power(csi2dc, true); 71362306a36Sopenharmony_ci if (ret < 0) 71462306a36Sopenharmony_ci goto csi2dc_probe_cleanup_notifier; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci pm_runtime_set_active(dev); 71762306a36Sopenharmony_ci pm_runtime_enable(dev); 71862306a36Sopenharmony_ci ver = csi2dc_readl(csi2dc, CSI2DC_VERSION); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * we must register the subdev after PM runtime has been requested, 72262306a36Sopenharmony_ci * otherwise we might bound immediately and request pm_runtime_resume 72362306a36Sopenharmony_ci * before runtime_enable. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci ret = v4l2_async_register_subdev(&csi2dc->csi2dc_sd); 72662306a36Sopenharmony_ci if (ret) { 72762306a36Sopenharmony_ci dev_err(csi2dc->dev, "failed to register the subdevice\n"); 72862306a36Sopenharmony_ci goto csi2dc_probe_cleanup_notifier; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci dev_info(dev, "Microchip CSI2DC version %x\n", ver); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cicsi2dc_probe_cleanup_notifier: 73662306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi2dc->notifier); 73762306a36Sopenharmony_cicsi2dc_probe_cleanup_entity: 73862306a36Sopenharmony_ci media_entity_cleanup(&csi2dc->csi2dc_sd.entity); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void csi2dc_remove(struct platform_device *pdev) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct csi2dc_device *csi2dc = platform_get_drvdata(pdev); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci v4l2_async_unregister_subdev(&csi2dc->csi2dc_sd); 75062306a36Sopenharmony_ci v4l2_async_nf_unregister(&csi2dc->notifier); 75162306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi2dc->notifier); 75262306a36Sopenharmony_ci media_entity_cleanup(&csi2dc->csi2dc_sd.entity); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int __maybe_unused csi2dc_runtime_suspend(struct device *dev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct csi2dc_device *csi2dc = dev_get_drvdata(dev); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return csi2dc_power(csi2dc, false); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int __maybe_unused csi2dc_runtime_resume(struct device *dev) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct csi2dc_device *csi2dc = dev_get_drvdata(dev); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return csi2dc_power(csi2dc, true); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic const struct dev_pm_ops csi2dc_dev_pm_ops = { 77062306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(csi2dc_runtime_suspend, csi2dc_runtime_resume, NULL) 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct of_device_id csi2dc_of_match[] = { 77462306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-csi2dc" }, 77562306a36Sopenharmony_ci { } 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, csi2dc_of_match); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic struct platform_driver csi2dc_driver = { 78162306a36Sopenharmony_ci .probe = csi2dc_probe, 78262306a36Sopenharmony_ci .remove_new = csi2dc_remove, 78362306a36Sopenharmony_ci .driver = { 78462306a36Sopenharmony_ci .name = "microchip-csi2dc", 78562306a36Sopenharmony_ci .pm = &csi2dc_dev_pm_ops, 78662306a36Sopenharmony_ci .of_match_table = of_match_ptr(csi2dc_of_match), 78762306a36Sopenharmony_ci }, 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cimodule_platform_driver(csi2dc_driver); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ciMODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); 79362306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip CSI2 Demux Controller driver"); 79462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 795