media: v4l: Add support for the Cadence MIPI-CSI2 TX controller
Subsystems (3)
v10 cover media: v4l: Add support for the Cadence MIPI-CSI2 TX controller
Hi,
Here is an attempt at supporting the MIPI-CSI2 TX block from Cadence.
This IP block is able to receive 4 video streams and stream them over a MIPI-CSI2 link using up to 4 lanes. Those streams are basically the interfaces to controllers generating some video signals, like a camera or a pattern generator.
It is able to map input streams to CSI2 virtual channels and datatypes dynamically. The streaming devices choose their virtual channels through an additional signal that is transparent to the CSI2-TX. The datatypes however are yet another additional input signal, and can be mapped to any CSI2 datatypes.
Since v4l2 doesn't really allow for that setup at the moment, this preliminary version is a rather dumb one in order to start the discussion on how to address this properly.
Let me know what you think!
Maxime
Changes from v9:
- Prevent the format to be retrieved on the CSI2 pad
- Set the default format on the pads when the user tries to set an unsupported format
Changes from v8:
- Updated the copyright notice
- Used masks for the device config fields
- Used the mbus code directly in our format lookup function
- Removed the pad index check in pads get/set_format
- Prevent the format to be changed on the CSI2 pad
- Check for supported formats in set_format
- Rebased on 4.17-rc1
Changes from v7:
- Removed unused headers
- Fixed a function prefix inconsistency
- Added Niklas' Reviewed-by
Changes from v6:
- Added Niklas Reviewed-by on the bindings
- Added MODULE_LICENSE, MODULE_DESCRIPTION and MODULE_AUTHOR
Changes from v5:
- Changed the return type to void for csi2tx_stop
- Checked for the pad index in get/set_pad_format
- Made a comment that the DPHY registers are for the DPHY *interface* register
Changes from v4:
- After playing a bit with the pad multiplexing patches, found that it was making much more sense to have the subdev notifiers for the source subdev rather for the sink that might even be outside of Linux control. Removed the notifier for now.
Changes from v3:
- Added a comment about entity links walk concurrency
- Changed the default resolution to 1280x720
- Changed usleep_range calls to udelay
- Reworked the reference counting mechanism to remove a race condition by adding a mutex instead of an atomic count
- Changed the entity function to MEDIA_ENT_F_VID_IF_BRIDGE
- Changed the name of the reg variable in _get_resources to dev_cfg
- Removed the redundant error message in the devm_ioremap_resource error path
- Moved the subdev s_stream call before enabling the TX bridge
- Changed some int types to unsigned
- Init'd the pad formats properly
- Fixed typo in the CSI2TX_LANES_MAX define name
- Added Sakari Acked-by
Changes from v2:
- Use SPDX license header
- Use the lane mapping from DT
Changes from v1:
- Add a subdev notifier and start our downstream subdevice in s_stream
- Based the decision to enable the stream or not on the link state instead of whether a format was being set on the pad
- Put the controller back in reset when stopping the pipeline
- Clarified the enpoints number in the DT binding
- Added a default format for the pads
- Added some missing const
- Added more explicit comments
- Rebased on 4.15
Maxime Ripard (2):
dt-bindings: media: Add Cadence MIPI-CSI2 TX Device Tree bindings
v4l: cadence: Add Cadence MIPI-CSI2 TX driver
.../devicetree/bindings/media/cdns,csi2tx.txt | 98 ++++ drivers/media/platform/cadence/Kconfig | 11 + drivers/media/platform/cadence/Makefile | 1 + drivers/media/platform/cadence/cdns-csi2tx.c | 541 ++++++++++++++++++ 4 files changed, 651 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/cdns,csi2tx.txt create mode 100644 drivers/media/platform/cadence/cdns-csi2tx.c
Signature · 1 line
2.17.0
PATCH v10 1/2 dt-bindings: media: Add Cadence MIPI-CSI2 TX Device Tree bindings
The Cadence MIPI-CSI2 TX controller is a CSI2 bridge that supports up to 4 video streams and can output on up to 4 CSI-2 lanes, depending on the hardware implementation.
It can operate with an external D-PHY, an internal one or no D-PHY at all in some configurations.
--- .../devicetree/bindings/media/cdns,csi2tx.txt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/cdns,csi2tx.txt
Documentation/devicetree/bindings/media/cdns,csi2tx.txt new file +98 -0
@@ -0,0 +1,98 @@ +Cadence MIPI-CSI2 TX controller +=============================== + +The Cadence MIPI-CSI2 TX controller is a CSI-2 bridge supporting up to +4 CSI lanes in output, and up to 4 different pixel streams in input. + +Required properties: + - compatible: must be set to "cdns,csi2tx" + - reg: base address and size of the memory mapped region + - clocks: phandles to the clocks driving the controller + - clock-names: must contain: + * esc_clk: escape mode clock + * p_clk: register bank clock + * pixel_if[0-3]_clk: pixel stream output clock, one for each stream + implemented in hardware, between 0 and 3 + +Optional properties + - phys: phandle to the D-PHY. If it is set, phy-names need to be set + - phy-names: must contain "dphy" + +Required subnodes: + - ports: A ports node with one port child node per device input and output + port, in accordance with the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The + port nodes are numbered as follows. + + Port Description + ----------------------------- + 0 CSI-2 output + 1 Stream 0 input + 2 Stream 1 input + 3 Stream 2 input + 4 Stream 3 input + + The stream input port nodes are optional if they are not + connected to anything at the hardware level or implemented + in the design. Since there is only one endpoint per port, + the endpoints are not numbered. + +Example: + +csi2tx: csi-bridge@0d0e1000 { + compatible = "cdns,csi2tx"; + reg = <0x0d0e1000 0x1000>; + clocks = <&byteclock>, <&byteclock>, + <&coreclock>, <&coreclock>, + <&coreclock>, <&coreclock>; + clock-names = "p_clk", "esc_clk", + "pixel_if0_clk", "pixel_if1_clk", + "pixel_if2_clk", "pixel_if3_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2tx_out: endpoint { + remote-endpoint = <&remote_in>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2tx_in_stream0: endpoint { + remote-endpoint = <&stream0_out>; + }; + }; + + port@2 { + reg = <2>; + + csi2tx_in_stream1: endpoint { + remote-endpoint = <&stream1_out>; + }; + }; + + port@3 { + reg = <3>; + + csi2tx_in_stream2: endpoint { + remote-endpoint = <&stream2_out>; + }; + }; + + port@4 { + reg = <4>; + + csi2tx_in_stream3: endpoint { + remote-endpoint = <&stream3_out>; + }; + }; + }; +};
Signature · 1 line
2.17.0
PATCH v10 2/2 v4l: cadence: Add Cadence MIPI-CSI2 TX driver
In reply to Maxime RipardThe Cadence MIPI-CSI2 TX controller is an hardware block meant to be used as a bridge between pixel interfaces and a CSI-2 bus.
It supports operating with an internal or external D-PHY, with up to 4 lanes, or without any D-PHY. The current code only supports the latter case.
While the virtual channel input on the pixel interface can be directly mapped to CSI2, the datatype input is actually a selection signal (3-bits) mapping to a table of up to 8 preconfigured datatypes/formats (programmed at start-up)
The block supports up to 8 input datatypes.
--- drivers/media/platform/cadence/Kconfig | 11 + drivers/media/platform/cadence/Makefile | 1 + drivers/media/platform/cadence/cdns-csi2tx.c | 541 +++++++++++++++++++ 3 files changed, 553 insertions(+) create mode 100644 drivers/media/platform/cadence/cdns-csi2tx.c
drivers/media/platform/cadence/Kconfig +11 -0
@@ -20,4 +20,15 @@ config VIDEO_CADENCE_CSI2RX To compile this driver as a module, choose M here: the module will be called cdns-csi2rx. +config VIDEO_CADENCE_CSI2TX + tristate "Cadence MIPI-CSI2 TX Controller" + depends on MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for the Cadence MIPI CSI2 Transceiver controller. + + To compile this driver as a module, choose M here: the module will be + called cdns-csi2tx. + endif
drivers/media/platform/cadence/Makefile +1 -0
@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_CADENCE_CSI2RX) += cdns-csi2rx.o +obj-$(CONFIG_VIDEO_CADENCE_CSI2TX) += cdns-csi2tx.o
drivers/media/platform/cadence/cdns-csi2tx.c new file +541 -0
541 lines not on this page · raw message
Signature · 1 line
2.17.0
Re: [PATCH v10 2/2] v4l: cadence: Add Cadence MIPI-CSI2 TX driver
Hi Maxime,
A few minor comments below...
Quoted 201 lines · Maxime Ripard
The Cadence MIPI-CSI2 TX controller is an hardware block meant to be used as a bridge between pixel interfaces and a CSI-2 bus.
It supports operating with an internal or external D-PHY, with up to 4 lanes, or without any D-PHY. The current code only supports the latter case.
While the virtual channel input on the pixel interface can be directly mapped to CSI2, the datatype input is actually a selection signal (3-bits) mapping to a table of up to 8 preconfigured datatypes/formats (programmed at start-up)
The block supports up to 8 input datatypes.
Reviewed-by Niklas S�derlund <[email protected]>Signed-off-by Maxime Ripard <[email protected]>--- drivers/media/platform/cadence/Kconfig | 11 + drivers/media/platform/cadence/Makefile | 1 + drivers/media/platform/cadence/cdns-csi2tx.c | 541 +++++++++++++++++++ 3 files changed, 553 insertions(+) create mode 100644 drivers/media/platform/cadence/cdns-csi2tx.cdrivers/media/platform/cadence/Kconfig +11
-0@@ -20,4 +20,15 @@ config VIDEO_CADENCE_CSI2RX To compile this driver as a module, choose M here: the module will be called cdns-csi2rx. +config VIDEO_CADENCE_CSI2TX + tristate "Cadence MIPI-CSI2 TX Controller" + depends on MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for the Cadence MIPI CSI2 Transceiver controller. + + To compile this driver as a module, choose M here: the module will be + called cdns-csi2tx. + endifdrivers/media/platform/cadence/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_CADENCE_CSI2RX) += cdns-csi2rx.o +obj-$(CONFIG_VIDEO_CADENCE_CSI2TX) += cdns-csi2tx.odrivers/media/platform/cadence/cdns-csi2tx.c new file +145
-0@@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Driver for Cadence MIPI-CSI2 TX Controller +* +* Copyright (C) 2017-2018 Cadence Design Systems Inc. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define CSI2TX_DEVICE_CONFIG_REG 0x00 +#define CSI2TX_DEVICE_CONFIG_STREAMS_MASK GENMASK(6, 4) +#define CSI2TX_DEVICE_CONFIG_HAS_DPHY BIT(3) +#define CSI2TX_DEVICE_CONFIG_LANES_MASK GENMASK(2, 0) + +#define CSI2TX_CONFIG_REG 0x20 +#define CSI2TX_CONFIG_CFG_REQ BIT(2) +#define CSI2TX_CONFIG_SRST_REQ BIT(1) + +#define CSI2TX_DPHY_CFG_REG 0x28 +#define CSI2TX_DPHY_CFG_CLK_RESET BIT(16) +#define CSI2TX_DPHY_CFG_LANE_RESET (n) BIT((n) + 12) +#define CSI2TX_DPHY_CFG_MODE_MASK GENMASK(9, 8) +#define CSI2TX_DPHY_CFG_MODE_LPDT (2 << 8) +#define CSI2TX_DPHY_CFG_MODE_HS (1 << 8) +#define CSI2TX_DPHY_CFG_MODE_ULPS (0 << 8) +#define CSI2TX_DPHY_CFG_CLK_ENABLE BIT(4) +#define CSI2TX_DPHY_CFG_LANE_ENABLE (n) BIT(n) + +#define CSI2TX_DPHY_CLK_WAKEUP_REG 0x2c +#define CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES (n) ((n) & 0xffff) + +#define CSI2TX_DT_CFG_REG (n) (0x80 + (n) * 8) +#define CSI2TX_DT_CFG_DT (n) (((n) & 0x3f) << 2) + +#define CSI2TX_DT_FORMAT_REG (n) (0x84 + (n) * 8) +#define CSI2TX_DT_FORMAT_BYTES_PER_LINE (n) (((n) & 0xffff) << 16) +#define CSI2TX_DT_FORMAT_MAX_LINE_NUM (n) ((n) & 0xffff) + +#define CSI2TX_STREAM_IF_CFG_REG (n) (0x100 + (n) * 4) +#define CSI2TX_STREAM_IF_CFG_FILL_LEVEL (n) ((n) & 0x1f) + +#define CSI2TX_LANES_MAX 4 +#define CSI2TX_STREAMS_MAX 4 + +enum csi2tx_pads { +CSI2TX_PAD_SOURCE , +CSI2TX_PAD_SINK_STREAM0 , +CSI2TX_PAD_SINK_STREAM1 , +CSI2TX_PAD_SINK_STREAM2 , +CSI2TX_PAD_SINK_STREAM3 , +CSI2TX_PAD_MAX , +}; + +struct csi2tx_fmt { +u32 mbus; +u32 dt; +u32 bpp; +}; + +struct csi2tx_priv { +struct device *dev; +unsigned int count; + +/* +* Used to prevent race conditions between multiple, +* concurrent calls to start and stop. +*/ +struct mutex lock; + +void __iomem *base; + +struct clk *esc_clk; +struct clk *p_clk; +struct clk *pixel_clk[CSI2TX_STREAMS_MAX ]; + +struct v4l2_subdev subdev; +struct media_pad pads[CSI2TX_PAD_MAX ]; +struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX ]; + +bool has_internal_dphy; +u8 lanes[CSI2TX_LANES_MAX ]; +unsigned int num_lanes; +unsigned int max_lanes; +unsigned int max_streams; +}; + +static const struct csi2tx_fmt csi2tx_formats[] = { + { + .mbus =MEDIA_BUS_FMT_UYVY8_1X16 , + .bpp =2 , + .dt =0x1e , + }, + { + .mbus =MEDIA_BUS_FMT_RGB888_1X24 , + .bpp =3 , + .dt =0x24 , + }, +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width =1280 , + .height =720 , + .code =MEDIA_BUS_FMT_RGB888_1X24 , + .field =V4L2_FIELD_NONE , + .colorspace =V4L2_COLORSPACE_DEFAULT , +}; + +static inline +struct csi2tx_priv *v4l2_subdev_to_csi2tx (struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct csi2tx_priv, subdev); +} + +static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus (u32 mbus) +{ +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (csi2tx_formats); i++) +if (csi2tx_formats[i].mbus == mbus) +return &csi2tx_formats[i]; + +return NULL ; +} + +static int csi2tx_enum_mbus_code (struct v4l2_subdev *subdev, +struct v4l2_subdev_pad_config *cfg, +struct v4l2_subdev_mbus_code_enum *code) +{ +if (code->pad || code->index >=ARRAY_SIZE (csi2tx_formats)) +return -EINVAL ; + + code->code = csi2tx_formats[code->index].mbus; +
drivers/media/platform/cadence/cdns-csi2tx.c line 155
Quoted 10 lines
drivers/media/platform/cadence/cdns-csi2tx.c new file +10
-0+return 0 ; +} + +static int csi2tx_get_pad_format (struct v4l2_subdev *subdev, +struct v4l2_subdev_pad_config *cfg, +struct v4l2_subdev_format *fmt) +{ +struct csi2tx_priv *csi2tx =v4l2_subdev_to_csi2tx (subdev); + +/* The CSI-2 TX pad doesn't really have a format (yet) */
It won't: there are no plans AFAIK to add formats to multiplexed pads. How about just:
/* Multiplexed pad? */
Quoted 4 lines
drivers/media/platform/cadence/cdns-csi2tx.c new file +4
-0+if (fmt->pad ==CSI2TX_PAD_SOURCE ) +return -EINVAL ; + + fmt->format = csi2tx->pad_fmts[fmt->pad];
drivers/media/platform/cadence/cdns-csi2tx.c line 169
Quoted 10 lines
drivers/media/platform/cadence/cdns-csi2tx.c new file +10
-0+ +return 0 ; +} + +static int csi2tx_set_pad_format (struct v4l2_subdev *subdev, +struct v4l2_subdev_pad_config *cfg, +struct v4l2_subdev_format *fmt) +{ +struct csi2tx_priv *csi2tx =v4l2_subdev_to_csi2tx (subdev); +const struct v4l2_mbus_framefmt *format = &fmt->format;
When fmt->which == V4L2_SUBDEV_FORMAT_TRY, you'll have to use v4l2_subdev_get_try_format. Same for csi2tx_get_pad_format(). I missed this earlier, see e.g. the smiapp driver's __smiapp_get_format().
drivers/media/platform/cadence/cdns-csi2tx.c line 171
Quoted 2 lines
drivers/media/platform/cadence/cdns-csi2tx.c new file +2
-0+ +/* The CSI-2 TX pad format cannot be changed (yet) */
Same comment as for csi2tx_get_pad_format().
With the above addressed,
Quoted 370 lines
drivers/media/platform/cadence/cdns-csi2tx.c new file +370
-0+if (fmt->pad ==CSI2TX_PAD_SOURCE ) +return -EINVAL ; + +if (!csi2tx_get_fmt_from_mbus (fmt->format.code)) + format = &fmt_default; + + csi2tx->pad_fmts[fmt->pad] = *format; + +return 0 ; +} + +static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { + .enum_mbus_code = csi2tx_enum_mbus_code, + .get_fmt = csi2tx_get_pad_format, + .set_fmt = csi2tx_set_pad_format, +}; + +static void csi2tx_reset (struct csi2tx_priv *csi2tx) +{ +writel (CSI2TX_CONFIG_SRST_REQ , csi2tx->base +CSI2TX_CONFIG_REG ); + +udelay (10 ); +} + +static int csi2tx_start (struct csi2tx_priv *csi2tx) +{ +struct media_entity *entity = &csi2tx->subdev.entity; +struct media_link *link; +unsigned int i; +u32 reg; + +csi2tx_reset (csi2tx); + +writel (CSI2TX_CONFIG_CFG_REQ , csi2tx->base +CSI2TX_CONFIG_REG ); + +udelay (10 ); + +/* Configure our PPI interface with the D-PHY */ +writel (CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES (32 ), + csi2tx->base +CSI2TX_DPHY_CLK_WAKEUP_REG ); + +/* Put our lanes (clock and data) out of reset */ + reg =CSI2TX_DPHY_CFG_CLK_RESET |CSI2TX_DPHY_CFG_MODE_LPDT ; +for (i =0 ; i < csi2tx->num_lanes; i++) + reg |=CSI2TX_DPHY_CFG_LANE_RESET (csi2tx->lanes[i]); +writel (reg, csi2tx->base +CSI2TX_DPHY_CFG_REG ); + +udelay (10 ); + +/* Enable our (clock and data) lanes */ + reg |=CSI2TX_DPHY_CFG_CLK_ENABLE ; +for (i =0 ; i < csi2tx->num_lanes; i++) + reg |=CSI2TX_DPHY_CFG_LANE_ENABLE (csi2tx->lanes[i]); +writel (reg, csi2tx->base +CSI2TX_DPHY_CFG_REG ); + +udelay (10 ); + +/* Switch to HS mode */ + reg &= ~CSI2TX_DPHY_CFG_MODE_MASK ; +writel (reg |CSI2TX_DPHY_CFG_MODE_HS , + csi2tx->base +CSI2TX_DPHY_CFG_REG ); + +udelay (10 ); + +/* +* Create a static mapping between the CSI virtual channels +* and the input streams. +* +* This should be enhanced, but v4l2 lacks the support for +* changing that mapping dynamically at the moment. +* +* We're protected from the userspace setting up links at the +* same time by the upper layer having called +* media_pipeline_start(). +*/ +list_for_each_entry (link, &entity->links, list) { +struct v4l2_mbus_framefmt *mfmt; +const struct csi2tx_fmt *fmt; +unsigned int stream; +int pad_idx =-1 ; + +/* Only consider our enabled input pads */ +for (i =CSI2TX_PAD_SINK_STREAM0 ; i <CSI2TX_PAD_MAX ; i++) { +struct media_pad *pad = &csi2tx->pads[i]; + +if ((pad == link->sink) && + (link->flags &MEDIA_LNK_FL_ENABLED )) { + pad_idx = i; +break ; + } + } + +if (pad_idx <0 ) +continue ; + + mfmt = &csi2tx->pad_fmts[pad_idx]; + fmt =csi2tx_get_fmt_from_mbus (mfmt->code); +if (!fmt) +continue ; + + stream = pad_idx -CSI2TX_PAD_SINK_STREAM0 ; + +/* +* We use the stream ID there, but it's wrong. +* +* A stream could very well send a data type that is +* not equal to its stream ID. We need to find a +* proper way to address it. +*/ +writel (CSI2TX_DT_CFG_DT (fmt->dt), + csi2tx->base +CSI2TX_DT_CFG_REG (stream)); + +writel (CSI2TX_DT_FORMAT_BYTES_PER_LINE (mfmt->width * fmt->bpp) | +CSI2TX_DT_FORMAT_MAX_LINE_NUM (mfmt->height +1 ), + csi2tx->base +CSI2TX_DT_FORMAT_REG (stream)); + +/* +* TODO: This needs to be calculated based on the +* output CSI2 clock rate. +*/ +writel (CSI2TX_STREAM_IF_CFG_FILL_LEVEL (4 ), + csi2tx->base +CSI2TX_STREAM_IF_CFG_REG (stream)); + } + +/* Disable the configuration mode */ +writel (0 , csi2tx->base +CSI2TX_CONFIG_REG ); + +return 0 ; +} + +static void csi2tx_stop (struct csi2tx_priv *csi2tx) +{ +writel (CSI2TX_CONFIG_CFG_REQ |CSI2TX_CONFIG_SRST_REQ , + csi2tx->base +CSI2TX_CONFIG_REG ); +} + +static int csi2tx_s_stream (struct v4l2_subdev *subdev,int enable) +{ +struct csi2tx_priv *csi2tx =v4l2_subdev_to_csi2tx (subdev); +int ret =0 ; + +mutex_lock (&csi2tx->lock); + +if (enable) { +/* +* If we're not the first users, there's no need to +* enable the whole controller. +*/ +if (!csi2tx->count) { + ret =csi2tx_start (csi2tx); +if (ret) + goto out; + } + + csi2tx->count++; + }else { + csi2tx->count--; + +/* +* Let the last user turn off the lights. +*/ +if (!csi2tx->count) +csi2tx_stop (csi2tx); + } + +out: +mutex_unlock (&csi2tx->lock); +return ret; +} + +static const struct v4l2_subdev_video_ops csi2tx_video_ops = { + .s_stream = csi2tx_s_stream, +}; + +static const struct v4l2_subdev_ops csi2tx_subdev_ops = { + .pad = &csi2tx_pad_ops, + .video = &csi2tx_video_ops, +}; + +static int csi2tx_get_resources (struct csi2tx_priv *csi2tx, +struct platform_device *pdev) +{ +struct resource *res; +unsigned int i; +u32 dev_cfg; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + csi2tx->base =devm_ioremap_resource (&pdev->dev, res); +if (IS_ERR (csi2tx->base)) +return PTR_ERR (csi2tx->base); + + csi2tx->p_clk =devm_clk_get (&pdev->dev,"p_clk" ); +if (IS_ERR (csi2tx->p_clk)) { +dev_err (&pdev->dev,"Couldn't get p_clk\n" ); +return PTR_ERR (csi2tx->p_clk); + } + + csi2tx->esc_clk =devm_clk_get (&pdev->dev,"esc_clk" ); +if (IS_ERR (csi2tx->esc_clk)) { +dev_err (&pdev->dev,"Couldn't get the esc_clk\n" ); +return PTR_ERR (csi2tx->esc_clk); + } + +clk_prepare_enable (csi2tx->p_clk); + dev_cfg =readl (csi2tx->base +CSI2TX_DEVICE_CONFIG_REG ); +clk_disable_unprepare (csi2tx->p_clk); + + csi2tx->max_lanes = dev_cfg &CSI2TX_DEVICE_CONFIG_LANES_MASK ; +if (csi2tx->max_lanes >CSI2TX_LANES_MAX ) { +dev_err (&pdev->dev,"Invalid number of lanes: %u\n" , + csi2tx->max_lanes); +return -EINVAL ; + } + + csi2tx->max_streams = (dev_cfg &CSI2TX_DEVICE_CONFIG_STREAMS_MASK ) >>4 ; +if (csi2tx->max_streams >CSI2TX_STREAMS_MAX ) { +dev_err (&pdev->dev,"Invalid number of streams: %u\n" , + csi2tx->max_streams); +return -EINVAL ; + } + + csi2tx->has_internal_dphy = !!(dev_cfg &CSI2TX_DEVICE_CONFIG_HAS_DPHY ); + +for (i =0 ; i < csi2tx->max_streams; i++) { +char clk_name[16 ]; + +snprintf (clk_name,sizeof (clk_name),"pixel_if%u_clk" , i); + csi2tx->pixel_clk[i] =devm_clk_get (&pdev->dev, clk_name); +if (IS_ERR (csi2tx->pixel_clk[i])) { +dev_err (&pdev->dev,"Couldn't get clock %s\n" , + clk_name); +return PTR_ERR (csi2tx->pixel_clk[i]); + } + } + +return 0 ; +} + +static int csi2tx_check_lanes (struct csi2tx_priv *csi2tx) +{ +struct v4l2_fwnode_endpoint v4l2_ep; +struct device_node *ep; +int ret; + + ep =of_graph_get_endpoint_by_regs (csi2tx->dev->of_node,0 ,0 ); +if (!ep) +return -EINVAL ; + + ret =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), &v4l2_ep); +if (ret) { +dev_err (csi2tx->dev,"Could not parse v4l2 endpoint\n" ); + goto out; + } + +if (v4l2_ep.bus_type !=V4L2_MBUS_CSI2 ) { +dev_err (csi2tx->dev,"Unsupported media bus type: 0x%x\n" , + v4l2_ep.bus_type); + ret = -EINVAL ; + goto out; + } + + csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; +if (csi2tx->num_lanes > csi2tx->max_lanes) { +dev_err (csi2tx->dev, +"Current configuration uses more lanes than supported\n" ); + ret = -EINVAL ; + goto out; + } + +memcpy (csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, +sizeof (csi2tx->lanes)); + +out: +of_node_put (ep); +return ret; +} + +static int csi2tx_probe (struct platform_device *pdev) +{ +struct csi2tx_priv *csi2tx; +unsigned int i; +int ret; + + csi2tx =kzalloc (sizeof (*csi2tx),GFP_KERNEL ); +if (!csi2tx) +return -ENOMEM ; +platform_set_drvdata (pdev, csi2tx); +mutex_init (&csi2tx->lock); + csi2tx->dev = &pdev->dev; + + ret =csi2tx_get_resources (csi2tx, pdev); +if (ret) + goto err_free_priv; + +v4l2_subdev_init (&csi2tx->subdev, &csi2tx_subdev_ops); + csi2tx->subdev.owner =THIS_MODULE ; + csi2tx->subdev.dev = &pdev->dev; + csi2tx->subdev.flags |=V4L2_SUBDEV_FL_HAS_DEVNODE ; +snprintf (csi2tx->subdev.name,V4L2_SUBDEV_NAME_SIZE ,"%s.%s" , +KBUILD_MODNAME ,dev_name (&pdev->dev)); + + ret =csi2tx_check_lanes (csi2tx); +if (ret) + goto err_free_priv; + +/* Create our media pads */ + csi2tx->subdev.entity.function =MEDIA_ENT_F_VID_IF_BRIDGE ; + csi2tx->pads[CSI2TX_PAD_SOURCE ].flags =MEDIA_PAD_FL_SOURCE ; +for (i =CSI2TX_PAD_SINK_STREAM0 ; i <CSI2TX_PAD_MAX ; i++) + csi2tx->pads[i].flags =MEDIA_PAD_FL_SINK ; + +/* +* Only the input pads are considered to have a format at the +* moment. The CSI link can multiplex various streams with +* different formats, and we can't expose this in v4l2 right +* now. +*/ +for (i =CSI2TX_PAD_SINK_STREAM0 ; i <CSI2TX_PAD_MAX ; i++) + csi2tx->pad_fmts[i] = fmt_default; + + ret =media_entity_pads_init (&csi2tx->subdev.entity,CSI2TX_PAD_MAX , + csi2tx->pads); +if (ret) + goto err_free_priv; + + ret =v4l2_async_register_subdev (&csi2tx->subdev); +if (ret <0 ) + goto err_free_priv; + +dev_info (&pdev->dev, +"Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n" , + csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams, + csi2tx->has_internal_dphy ?"internal" :"no" ); + +return 0 ; + +err_free_priv: +kfree (csi2tx); +return ret; +} + +static int csi2tx_remove (struct platform_device *pdev) +{ +struct csi2tx_priv *csi2tx =platform_get_drvdata (pdev); + +v4l2_async_unregister_subdev (&csi2tx->subdev); +kfree (csi2tx); + +return 0 ; +} + +static const struct of_device_id csi2tx_of_table[] = { + { .compatible ="cdns,csi2tx" }, + { }, +}; +MODULE_DEVICE_TABLE (of, csi2tx_of_table); + +static struct platform_driver csi2tx_driver = { + .probe = csi2tx_probe, + .remove = csi2tx_remove, + + .driver = { + .name ="cdns-csi2tx" , + .of_match_table = csi2tx_of_table, + }, +}; +module_platform_driver (csi2tx_driver); +MODULE_AUTHOR ("Maxime Ripard <[email protected]>" ); +MODULE_DESCRIPTION ("Cadence CSI2-TX controller" ); +MODULE_LICENSE ("GPL" );
Signature · 4 lines
Kind regards, Sakari Ailus [email protected]