[RFC 12/12] staging: media: max96712: allow streaming from connected sensors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds support for starting/stopping streaming from connected
sensors as well. The user can also switch over to testing the test
pattern by configuring the routes accordingly.

Use the 'maxim,operation-mode' DT setting to allow the user to select
which operation mode the deserializer should run in, though only
tunneling mode is supported currently.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@xxxxxxxxxxx>
---
 drivers/staging/media/max96712/max96712.c | 426 +++++++++++++++++++++-
 1 file changed, 418 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index 0112052171b06..b4c3d1d3c9539 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -4,6 +4,7 @@
  *
  * Copyright (C) 2021 Renesas Electronics Corporation
  * Copyright (C) 2021 Niklas Söderlund
+ * Copyright 2025 NXP
  */
 
 #include <linux/delay.h>
@@ -30,6 +31,12 @@
 #define   DIS_REM_CC_C_SHIFT				4
 #define   DIS_REM_CC_D_MASK				GENMASK(7, 6)
 #define   DIS_REM_CC_D_SHIFT				6
+#define MAX96712_DEV_CTRL12				CCI_REG8(0x000a)
+#define   LOCKED_B					BIT(3)
+#define MAX96712_DEV_CTRL13				CCI_REG8(0x000b)
+#define   LOCKED_C					BIT(3)
+#define MAX96712_DEV_CTRL14				CCI_REG8(0x000c)
+#define   LOCKED_D					BIT(3)
 
 /* TOP_CTRL */
 #define MAX96712_DEBUG_EXTRA_REG			CCI_REG8(0x0009)
@@ -37,6 +44,11 @@
 #define   DEBUG_EXTRA_PCLK_75MHZ			0x01
 #define MAX96724_TOP_CTRL_PWR1				CCI_REG8(0x0013)
 #define   RESET_ALL					BIT(6)
+#define MAX96712_TOP_CTRL_CTRL3				CCI_REG8(0x001a)
+#define   LOCK_PIN					BIT(0)
+#define   CMU_LOCKED					BIT(1)
+#define   ERROR						BIT(2)
+#define   LOCKED_A					BIT(3)
 
 /* BACKTOP0 */
 #define MAX96712_BACKTOP0_12				CCI_REG8(0x040b)
@@ -110,6 +122,15 @@
 #define   T_T3_PREP_86_7NS				3
 #define   T_T3_POST_MASK				GENMASK(6, 2)
 #define   T_T3_POST_SHIFT				2
+#define MAX96712_MIPI_PHY_MIPI_CTRL_SEL			CCI_REG8(0x08ca)
+#define   MIPI_CTRL_SEL_0_MASK				GENMASK(1, 0)
+#define   MIPI_CTRL_SEL_0_SHIFT				0
+#define   MIPI_CTRL_SEL_1_MASK				GENMASK(3, 2)
+#define   MIPI_CTRL_SEL_1_SHIFT				2
+#define   MIPI_CTRL_SEL_2_MASK				GENMASK(5, 4)
+#define   MIPI_CTRL_SEL_2_SHIFT				4
+#define   MIPI_CTRL_SEL_3_MASK				GENMASK(7, 6)
+#define   MIPI_CTRL_SEL_3_SHIFT				6
 
 /* MIPI_TX: 0 <= phy < 4 */
 #define MAX96712_MIPI_TX_DESKEW_INIT(phy)		CCI_REG8(0x0903 + (phy) * 0x40)
@@ -123,6 +144,22 @@
 #define   CSI2_CPHY_EN					BIT(5)
 #define   CSI2_LANE_CNT_MASK				GENMASK(7, 6)
 #define   CSI2_LANE_CNT_SHIFT				6
+#define MAX96712_MIPI_TX_54(phy)			CCI_REG8(0x0936 + (phy) * 0x40)
+#define   TUN_EN					BIT(0)
+#define   DESKEW_TUN_SRC_MASK				GENMASK(2, 1)
+#define   DESKEW_TUN_SRC_SHIFT				1
+#define   TUN_SER_LANE_NUM_MASK				GENMASK(4, 3)
+#define   TUN_SER_LANE_NUM_SHIFT			3
+#define   DESKEW_TUN_MASK				GENMASK(6, 5)
+#define   DESKEW_TUN_SHIFT				5
+#define   TUN_NO_CORR					BIT(7)
+#define MAX96712_MIPI_TX_57(phy)			CCI_REG8(0x0939 + (phy) * 0x40)
+#define   TUN_DPHY_TO_CPHY_CONV_OVRD			BIT(1)
+#define   TUN_DPHY_TO_CPHY_CONV				BIT(2)
+#define   TUN_DEST_MASK					GENMASK(5, 4)
+#define   TUN_DEST_SHIFT				4
+#define   DIS_AUTO_TUN_DET				BIT(6)
+#define   DIS_AUTO_SER_LANE_DET				BIT(7)
 
 /* GPIO_A: 0 <= gpio < 11 */
 #define MAX96712_GPIO_A_A(gpio)				CCI_REG8(0x0300 + (gpio) * 0x03)
@@ -241,6 +278,16 @@ struct max96712_rx_port {
 	struct fwnode_handle *fwnode;
 };
 
+struct max96712_asc {
+	struct v4l2_async_connection base;
+	struct max96712_rx_port *rx_port;
+};
+
+enum max96712_operation_mode {
+	MAX96712_TUNNEL_MODE,
+	MAX96712_PIXEL_MODE,
+};
+
 struct max96712_priv {
 	struct i2c_client *client;
 	struct regmap *regmap;
@@ -253,6 +300,8 @@ struct max96712_priv {
 
 	const struct max96712_info *info;
 
+	enum max96712_operation_mode operation_mode;
+
 	bool cphy;
 	struct v4l2_mbus_config_mipi_csi2 mipi;
 	s64 link_freq;
@@ -260,12 +309,15 @@ struct max96712_priv {
 	struct v4l2_subdev sd;
 	struct v4l2_ctrl_handler ctrl_handler;
 	struct media_pad pads[MAX96712_MAX_PORTS];
+	struct v4l2_async_notifier notifier;
+	u32 enabled_streams;
 
 	struct max96712_rx_port rx_ports[MAX96712_MAX_RX_PORTS];
 	unsigned int rx_port_mask;
 	unsigned int n_rx_ports;
 
 	enum max96712_pattern pattern;
+	bool vpg_started;
 
 	struct max96712_fsync fsync;
 	struct v4l2_fract interval;
@@ -382,6 +434,17 @@ static inline bool max96712_pad_is_source(u32 pad)
 	return pad >= MAX96712_FIRST_SOURCE_PAD && pad < MAX96712_VPG_PAD;
 }
 
+static int max96712_read(struct max96712_priv *priv, unsigned int reg, u64 *val)
+{
+	int ret;
+
+	ret = cci_read(priv->regmap, reg, val, NULL);
+	if (ret)
+		dev_err(&priv->client->dev, "read 0x%04x failed\n", reg);
+
+	return ret;
+}
+
 static int max96712_write(struct max96712_priv *priv, unsigned int reg, u64 val)
 {
 	int ret;
@@ -422,6 +485,14 @@ static void max96712_mipi_enable(struct max96712_priv *priv, bool enable)
 	}
 }
 
+static void max96712_tunneling_enable(struct max96712_priv *priv, bool enable)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		max96712_update_bits(priv, MAX96712_MIPI_TX_54(i), TUN_EN, enable ? TUN_EN : 0);
+}
+
 static void max96712_mipi_configure(struct max96712_priv *priv)
 {
 	unsigned int i;
@@ -485,14 +556,26 @@ static void max96712_mipi_configure(struct max96712_priv *priv)
 				     PERIODIC_DESKEW_CALIBRATION_EN, auto_deskew_calib_en);
 	}
 
+	if (priv->operation_mode == MAX96712_TUNNEL_MODE) {
+		int i;
+		/*
+		 * Disable tunnel auto-detection for all phys, will enable tunnelling
+		 * explicitly when needed.
+		 */
+		for (i = 0; i < 4; i++)
+			max96712_update_bits(priv, MAX96712_MIPI_TX_57(i),
+					     DIS_AUTO_TUN_DET, DIS_AUTO_TUN_DET);
+	}
+
 	/* Enable PHY0 and PHY1 */
 	max96712_update_bits(priv, MAX96712_MIPI_PHY_2, PHY_STDBY_N_MASK, PHY0_EN | PHY1_EN);
 }
 
-static void max96712_pattern_enable(struct max96712_priv *priv, struct v4l2_subdev_state *state,
-				    bool enable)
+static int max96712_pattern_enable(struct max96712_priv *priv, struct v4l2_subdev_state *state,
+				   bool enable)
 {
 	struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(state, MAX96712_VPG_PAD);
+	struct device *dev = &priv->client->dev;
 
 	const u32 h_active = fmt->width;
 	const u32 h_fp = 88;
@@ -506,9 +589,16 @@ static void max96712_pattern_enable(struct max96712_priv *priv, struct v4l2_subd
 	const u32 v_bp = 36;
 	const u32 v_tot = v_active + v_fp + v_sw + v_bp;
 
+	priv->vpg_started = enable;
+
 	if (!enable) {
 		max96712_write(priv, MAX96712_VRX_PATGEN_1, 0x00);
-		return;
+		return 0;
+	}
+
+	if (priv->enabled_streams) {
+		dev_err(dev, "Cannot enable VPG when other streams are enabled.\n");
+		return -EINVAL;
 	}
 
 	max96712_write(priv, MAX96712_DEBUG_EXTRA_REG, DEBUG_EXTRA_PCLK_75MHZ);
@@ -553,14 +643,44 @@ static void max96712_pattern_enable(struct max96712_priv *priv, struct v4l2_subd
 		/* Generate gradient pattern. */
 		max96712_write(priv, MAX96712_VRX_PATGEN_1, PATGEN_MODE_GRADIENT);
 	}
+
+	return 0;
+}
+
+static u8 max96712_get_link_status(struct max96712_priv *priv)
+{
+	u32 link_lock_addr[4] = {
+		MAX96712_TOP_CTRL_CTRL3,
+		MAX96712_DEV_CTRL12,
+		MAX96712_DEV_CTRL13,
+		MAX96712_DEV_CTRL14
+	};
+	int nport;
+	u8 link_status_mask = 0;
+
+	for (nport = 0; nport < MAX96712_MAX_RX_PORTS; nport++) {
+		u64 reg_val = 0;
+
+		max96712_read(priv, link_lock_addr[nport], &reg_val);
+
+		link_status_mask |= reg_val & BIT(3) ? (1 << nport) : 0;
+	}
+
+	return link_status_mask;
 }
 
-static int __maybe_unused max96712_fsync_set(struct max96712_priv *priv)
+static int max96712_fsync_enable(struct max96712_priv *priv, bool enable)
 {
 	u32 fsync;
 	int ret;
 	u8 mode_map[4] = {3, 0, 1, 2};
 
+	if (!enable) {
+		max96712_update_bits(priv, MAX96712_FSYNC_0,
+				     FSYNC_MODE_MASK, 0x3 << FSYNC_MODE_SHIFT);
+		return 0;
+	}
+
 	if (priv->fsync.mode == MAX96712_FSYNC_OFF)
 		return 0;
 
@@ -739,14 +859,117 @@ static int max96712_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
 	return ret;
 }
 
+static struct v4l2_subdev *max96712_xlate_streams(struct max96712_priv *priv,
+						  struct v4l2_subdev_state *state, u32 src_pad,
+						  u64 src_streams, u32 sink_pad, u64 *sink_streams,
+						  u32 *remote_pad)
+{
+	struct device *dev = &priv->client->dev;
+	u64 streams;
+	struct v4l2_subdev *remote_sd;
+	struct media_pad *pad;
+
+	streams = v4l2_subdev_state_xlate_streams(state, src_pad, sink_pad, &src_streams);
+	if (!streams)
+		dev_dbg(dev, "no streams found on sink pad\n");
+
+	pad = media_pad_remote_pad_first(&priv->pads[sink_pad]);
+	if (!pad) {
+		dev_dbg(dev, "no remote pad found for sink pad\n");
+		return ERR_PTR(-EPIPE);
+	}
+
+	remote_sd = media_entity_to_v4l2_subdev(pad->entity);
+	if (!remote_sd) {
+		dev_dbg(dev, "no entity connected to CSI2 input\n");
+		return ERR_PTR(-EPIPE);
+	}
+
+	*sink_streams = streams;
+	*remote_pad = pad->index;
+
+	return remote_sd;
+}
+
+static int max96712_enable_remote_stream(struct max96712_priv *priv,
+					 struct v4l2_subdev_state *state,
+					 u32 source_pad, u32 stream, u32 sink_pad,
+					 bool enable)
+{
+	struct device *dev = &priv->client->dev;
+	struct v4l2_subdev *remote_sd;
+	u64 sink_streams = 0;
+	u32 remote_pad = 0;
+	int ret = 0;
+
+	if (enable && priv->vpg_started) {
+		dev_err(dev, "Cannot enable remote streams while VPG is enabled.\n");
+		return -EINVAL;
+	}
+
+	remote_sd = max96712_xlate_streams(priv, state, source_pad, BIT(stream), sink_pad,
+					   &sink_streams, &remote_pad);
+	if (IS_ERR(remote_sd))
+		return PTR_ERR(remote_sd);
+
+	ret = enable ? v4l2_subdev_enable_streams(remote_sd, remote_pad, 0x1) :
+		       v4l2_subdev_disable_streams(remote_sd, remote_pad, 0x1);
+
+	if (ret)
+		dev_err(&priv->client->dev, "failed to %s streams 0x%llx on '%s':%u: %d\n",
+			enable ? "enable" : "disable",
+			sink_streams, remote_sd->name, remote_pad, ret);
+
+	return ret;
+}
+
 static int max96712_enable_streams(struct v4l2_subdev *sd,
 				   struct v4l2_subdev_state *state,
 				   u32 source_pad, u64 streams_mask)
 {
 	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
+	u64 sources_mask = streams_mask;
+	u32 sink_pad, sink_stream;
+	int ret = 0;
+
+	if (!priv->enabled_streams)
+		max96712_mipi_enable(priv, true);
+
+	while (true) {
+		int pos = ffs(sources_mask) - 1;
 
-	max96712_pattern_enable(priv, state, true);
-	max96712_mipi_enable(priv, true);
+		if (pos == -1)
+			break;
+
+		ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+							    source_pad, pos,
+							    &sink_pad, &sink_stream);
+		if (ret)
+			return ret;
+
+		if (sink_pad == MAX96712_VPG_PAD && sink_stream == 0) {
+			/* need to have tunneling disabled for VPG to work */
+			max96712_tunneling_enable(priv, false);
+
+			ret = max96712_pattern_enable(priv, state, true);
+		} else {
+			if (!priv->enabled_streams) {
+				max96712_fsync_enable(priv, true);
+				if (priv->operation_mode == MAX96712_TUNNEL_MODE)
+					max96712_tunneling_enable(priv, true);
+			}
+
+			ret = max96712_enable_remote_stream(priv, state, source_pad, pos,
+							    sink_pad, true);
+		}
+
+		if (ret)
+			return ret;
+
+		sources_mask &= ~BIT(pos);
+	}
+
+	priv->enabled_streams |= streams_mask;
 
 	return 0;
 }
@@ -756,9 +979,42 @@ static int max96712_disable_streams(struct v4l2_subdev *sd,
 				    u32 source_pad, u64 streams_mask)
 {
 	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
+	u64 sources_mask = streams_mask;
+	u32 sink_pad, sink_stream;
+	int ret = 0;
 
-	max96712_mipi_enable(priv, false);
-	max96712_pattern_enable(priv, state, false);
+	while (true) {
+		int pos = ffs(sources_mask) - 1;
+
+		if (pos == -1)
+			break;
+
+		ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+							    source_pad, pos,
+							    &sink_pad, &sink_stream);
+		if (ret)
+			return ret;
+
+		max96712_update_bits(priv, MAX96712_MIPI_PHY_0, FORCE_CSI_OUT_EN, 0x00);
+
+		if (sink_pad == MAX96712_VPG_PAD && sink_stream == 0)
+			ret = max96712_pattern_enable(priv, state, false);
+		else
+			ret = max96712_enable_remote_stream(priv, state, source_pad, pos,
+							    sink_pad, false);
+
+		if (ret)
+			return ret;
+
+		sources_mask &= ~BIT(pos);
+	}
+
+	priv->enabled_streams &= ~streams_mask;
+
+	if (!priv->enabled_streams) {
+		max96712_fsync_enable(priv, false);
+		max96712_mipi_enable(priv, false);
+	}
 
 	return 0;
 }
@@ -842,6 +1098,104 @@ static int max96712_set_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
+#define to_index(priv, rx_port) ((rx_port) - &(priv)->rx_ports[0])
+
+static int max96712_notify_bound(struct v4l2_async_notifier *notifier,
+				 struct v4l2_subdev *subdev,
+				 struct v4l2_async_connection *asc)
+{
+	struct max96712_priv *priv = container_of(notifier->sd, struct max96712_priv, sd);
+	struct max96712_asc *async_conn = container_of(asc, struct max96712_asc, base);
+	struct max96712_rx_port *rx_port = async_conn->rx_port;
+	unsigned int index = to_index(priv, rx_port);
+	struct device *dev = &priv->client->dev;
+	unsigned int src_pad;
+	int ret;
+
+	ret = media_entity_get_fwnode_pad(&subdev->entity, rx_port->fwnode, MEDIA_PAD_FL_SOURCE);
+	if (ret < 0) {
+		dev_err(dev, "Failed to find pad for %s\n", subdev->name);
+		return ret;
+	}
+
+	rx_port->sd = subdev;
+	src_pad = ret;
+
+	ret = media_create_pad_link(&rx_port->sd->entity, src_pad, &priv->sd.entity, index, 0);
+	if (ret) {
+		dev_err(dev, "Unable to link %s:%u -> %s:%u\n",
+			rx_port->sd->name, src_pad, priv->sd.name, index);
+		return ret;
+	}
+
+	dev_dbg(dev, "Bound %s pad: %u on index %u\n", subdev->name, src_pad, index);
+
+	return 0;
+}
+
+static void max96712_notify_unbind(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_connection *asc)
+{
+	struct max96712_asc *async_conn = container_of(asc, struct max96712_asc, base);
+	struct max96712_rx_port *rx_port = async_conn->rx_port;
+
+	rx_port->sd = NULL;
+}
+
+static const struct v4l2_async_notifier_operations max96724_notify_ops = {
+	.bound = max96712_notify_bound,
+	.unbind = max96712_notify_unbind,
+};
+
+static int max96712_v4l2_notifier_register(struct max96712_priv *priv)
+{
+	int i, ret;
+	struct device *dev = &priv->client->dev;
+	struct max96712_rx_port *rx_port = NULL;
+	u32 rx_port_mask = priv->rx_port_mask;
+
+	if (!priv->n_rx_ports)
+		return 0;
+
+	v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd);
+
+	while (true) {
+		int pos = ffs(rx_port_mask) - 1;
+		struct max96712_asc *asc;
+
+		if (pos == -1)
+			break;
+
+		rx_port = &priv->rx_ports[pos];
+		rx_port_mask &= ~BIT(pos);
+
+		if (!rx_port->fwnode)
+			continue;
+
+		asc = v4l2_async_nf_add_fwnode(&priv->notifier, rx_port->fwnode,
+					       struct max96712_asc);
+		if (IS_ERR(asc)) {
+			dev_err(dev, "Failed to add subdev for source %u: %ld", i, PTR_ERR(asc));
+			v4l2_async_nf_cleanup(&priv->notifier);
+			return PTR_ERR(asc);
+		}
+
+		asc->rx_port = rx_port;
+	}
+
+	priv->notifier.ops = &max96724_notify_ops;
+
+	ret = v4l2_async_nf_register(&priv->notifier);
+	if (ret) {
+		dev_err(dev, "Failed to register subdev_notifier");
+		v4l2_async_nf_cleanup(&priv->notifier);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int max96712_init_state(struct v4l2_subdev *sd,
 			       struct v4l2_subdev_state *state)
 {
@@ -862,6 +1216,37 @@ static int max96712_init_state(struct v4l2_subdev *sd,
 	return _max96712_set_routing(sd, state, &routing);
 }
 
+static int max96712_log_status(struct v4l2_subdev *sd)
+{
+	struct max96712_priv *priv = container_of(sd, struct max96712_priv, sd);
+	struct device *dev = &priv->client->dev;
+	u8 gmsl_link_status_mask;
+	char hdr[64];
+	int nport;
+
+	gmsl_link_status_mask = max96712_get_link_status(priv);
+
+	dev_info(dev, "Deserializer status:\n");
+
+	dev_info(dev, "RX ports:\n");
+
+	for (nport = 0; nport < MAX96712_MAX_RX_PORTS; nport++) {
+		struct max96712_rx_port *rx_port = &priv->rx_ports[nport];
+
+		sprintf(hdr, "\t* RX %d:", nport);
+
+		if (!rx_port->fwnode) {
+			dev_info(dev, "%s Not Configured\n", hdr);
+			continue;
+		}
+
+		dev_info(dev, "%s Link %s\n", hdr,
+			 gmsl_link_status_mask & BIT(nport) ? "locked" : "not locked");
+	}
+
+	return 0;
+}
+
 static const struct v4l2_subdev_internal_ops max96712_internal_ops = {
 	.init_state = max96712_init_state,
 };
@@ -878,8 +1263,13 @@ static const struct v4l2_subdev_pad_ops max96712_pad_ops = {
 	.set_frame_interval	= max96712_set_frame_interval,
 };
 
+static const struct v4l2_subdev_core_ops max96712_subdev_core_ops = {
+	.log_status = max96712_log_status,
+};
+
 static const struct v4l2_subdev_ops max96712_subdev_ops = {
 	.video = &max96712_video_ops,
+	.core = &max96712_subdev_core_ops,
 	.pad = &max96712_pad_ops,
 };
 
@@ -907,6 +1297,10 @@ static const struct v4l2_ctrl_ops max96712_ctrl_ops = {
 	.s_ctrl = max96712_s_ctrl,
 };
 
+static const struct media_entity_operations max96712_v4l2_media_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
 static int max96712_v4l2_register(struct max96712_priv *priv)
 {
 	struct v4l2_ctrl *link_freq_ctrl;
@@ -917,6 +1311,7 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
 	v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96712_subdev_ops);
 	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
 	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+	priv->sd.entity.ops = &max96712_v4l2_media_ops;
 
 	v4l2_ctrl_handler_init(&priv->ctrl_handler, 2);
 
@@ -953,6 +1348,12 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
 	if (ret)
 		goto error;
 
+	ret = max96712_v4l2_notifier_register(priv);
+	if (ret) {
+		dev_err(&priv->client->dev, "Unable to register v4l2 async notifiers\n");
+		goto error;
+	}
+
 	ret = v4l2_async_register_subdev(&priv->sd);
 	if (ret < 0) {
 		dev_err(&priv->client->dev, "Unable to register subdevice\n");
@@ -960,6 +1361,7 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
 	}
 
 	return 0;
+
 error:
 	v4l2_ctrl_handler_free(&priv->ctrl_handler);
 
@@ -1223,6 +1625,14 @@ static int max96712_parse_dt(struct max96712_priv *priv)
 	int ret = 0, count;
 	u32 dt_val[3];
 
+	if (!fwnode_property_read_u32(dev_fwnode(dev), "maxim,operation-mode", &dt_val[0]))
+		priv->operation_mode = dt_val[0];
+
+	if (priv->operation_mode != MAX96712_TUNNEL_MODE) {
+		dev_err(dev, "Unsupported mode, only tunneling mode is supported currently.\n");
+		return -EINVAL;
+	}
+
 	count = fwnode_property_count_u32(dev_fwnode(dev), "maxim,fsync-config");
 	if (count > 0) {
 		ret = fwnode_property_read_u32_array(dev_fwnode(dev), "maxim,fsync-config",
-- 
2.44.1





[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux