[RFC 11/12] staging: media: max96712: add fsync support

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

 



FSYNC is used to align images sent from multiple sensors.

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

diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index 307b2f1d3a6be..0112052171b06 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -51,6 +51,21 @@
 #define   OVERRIDE_BPP_VC_DT_0_2			BIT(6)
 #define   OVERRIDE_BPP_VC_DT_1_3			BIT(7)
 
+/* FSYNC */
+#define MAX96712_FSYNC_0				CCI_REG8(0x04a0)
+#define   FSYNC_METH_MASK				GENMASK(1, 0)
+#define   FSYNC_METH_SHIFT				0
+#define   FSYNC_MODE_MASK				GENMASK(3, 2)
+#define   FSYNC_MODE_SHIFT				2
+#define   EN_VS_GEN					BIT(4)
+#define   FSYNC_OUT_PIN					BIT(5)
+#define MAX96712_FSYNC_PERIOD				CCI_REG24_LE(0x04a5)
+#define MAX96712_FSYNC_17				CCI_REG8(0x04b1)
+#define   FSYNC_ERR_THR_MASK				GENMASK(2, 0)
+#define   FSYNC_ERR_THR_SHIFT				0
+#define   FSYNC_TX_ID_MASK				GENMASK(7, 3)
+#define   FSYNC_TX_ID_SHIFT				3
+
 /* MIPI_PHY */
 #define MAX96712_MIPI_PHY_0				CCI_REG8(0x08a0)
 #define   PHY_4X2					BIT(0)
@@ -194,6 +209,7 @@
 #define MAX96712_VPG_PAD				(MAX96712_FIRST_SOURCE_PAD + \
 							 MAX96712_MAX_TX_PORTS)
 
+#define MAX96712_XTAL_CLOCK				25000000ULL
 #define MHZ(f)						((f) * 1000000U)
 
 #define MAX96712_NUM_GPIO				12
@@ -207,6 +223,19 @@ struct max96712_info {
 	unsigned int dpllfreq;
 };
 
+enum max96712_fsync_mode {
+	MAX96712_FSYNC_OFF = 0,
+	MAX96712_FSYNC_INTERNAL,
+	MAX96712_FSYNC_MASTER,
+	MAX96712_FSYNC_SLAVE,
+};
+
+struct max96712_fsync {
+	int pin;
+	enum max96712_fsync_mode mode;
+	int tx_id;
+};
+
 struct max96712_rx_port {
 	struct v4l2_subdev *sd;
 	struct fwnode_handle *fwnode;
@@ -237,6 +266,9 @@ struct max96712_priv {
 	unsigned int n_rx_ports;
 
 	enum max96712_pattern pattern;
+
+	struct max96712_fsync fsync;
+	struct v4l2_fract interval;
 };
 
 struct max96712_format_info {
@@ -523,6 +555,68 @@ static void max96712_pattern_enable(struct max96712_priv *priv, struct v4l2_subd
 	}
 }
 
+static int __maybe_unused max96712_fsync_set(struct max96712_priv *priv)
+{
+	u32 fsync;
+	int ret;
+	u8 mode_map[4] = {3, 0, 1, 2};
+
+	if (priv->fsync.mode == MAX96712_FSYNC_OFF)
+		return 0;
+
+	if (!priv->interval.numerator || !priv->interval.denominator)
+		return max96712_update_bits(priv, MAX96712_FSYNC_0,
+					    FSYNC_METH_MASK | FSYNC_MODE_MASK,
+					    0x3 << FSYNC_MODE_SHIFT);
+
+	/*
+	 * According to Max96724 users guide, "sync signal frequency must be specified in terms of
+	 * the onboard crystal clock (25MHz)"
+	 */
+	fsync = div_u64(MAX96712_XTAL_CLOCK * priv->interval.numerator, priv->interval.denominator);
+
+	ret = max96712_write(priv, MAX96712_FSYNC_PERIOD, fsync);
+
+	ret |= max96712_update_bits(priv, MAX96712_FSYNC_0,
+				    FSYNC_OUT_PIN | FSYNC_METH_MASK | FSYNC_MODE_MASK,
+				    (priv->fsync.pin ? FSYNC_OUT_PIN : 0) |
+				    (0x0 << FSYNC_METH_SHIFT) |
+				    (mode_map[priv->fsync.mode] << FSYNC_MODE_SHIFT));
+
+	ret |= max96712_update_bits(priv, MAX96712_FSYNC_17, FSYNC_TX_ID_MASK,
+				    priv->fsync.tx_id << FSYNC_TX_ID_SHIFT);
+
+	return ret ? -EIO : 0;
+}
+
+static int max96712_get_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_state *state,
+				       struct v4l2_subdev_frame_interval *interval)
+{
+	struct max96712_priv *priv = container_of(sd, struct max96712_priv, sd);
+
+	if (!max96712_pad_is_source(interval->pad))
+		return -EINVAL;
+
+	interval->interval = priv->interval;
+
+	return 0;
+}
+
+static int max96712_set_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_state *state,
+				       struct v4l2_subdev_frame_interval *interval)
+{
+	struct max96712_priv *priv = container_of(sd, struct max96712_priv, sd);
+
+	if (!max96712_pad_is_source(interval->pad))
+		return -EINVAL;
+
+	priv->interval = interval->interval;
+
+	return 0;
+}
+
 static int max96712_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state,
 				   struct v4l2_subdev_mbus_code_enum *code)
 {
@@ -780,6 +874,8 @@ static const struct v4l2_subdev_pad_ops max96712_pad_ops = {
 	.disable_streams = max96712_disable_streams,
 	.set_routing = max96712_set_routing,
 	.get_frame_desc = max96712_get_frame_desc,
+	.get_frame_interval	= max96712_get_frame_interval,
+	.set_frame_interval	= max96712_set_frame_interval,
 };
 
 static const struct v4l2_subdev_ops max96712_subdev_ops = {
@@ -1124,7 +1220,28 @@ static int max96712_parse_dt(struct max96712_priv *priv)
 {
 	struct device *dev = &priv->client->dev;
 	struct device_node *node = NULL;
-	int ret = 0;
+	int ret = 0, count;
+	u32 dt_val[3];
+
+	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",
+						     dt_val, count);
+		if (ret) {
+			dev_err(dev, "Unable to read FSYNC config from DT.\n");
+			return ret;
+		}
+
+		priv->fsync.mode = dt_val[0];
+		priv->fsync.tx_id = priv->fsync.mode ? dt_val[1] : 0;
+
+		if (priv->fsync.mode >= MAX96712_FSYNC_MASTER && count == 2) {
+			dev_err(dev, "No FSYNC pin provided in DT for the given mode.\n");
+			return -EINVAL;
+		}
+
+		priv->fsync.pin = dt_val[2];
+	}
 
 	for_each_endpoint_of_node(dev->of_node, node) {
 		struct of_endpoint ep;
-- 
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