[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250131163408.2019144-12-laurentiu.palcu@oss.nxp.com>
Date: Fri, 31 Jan 2025 18:34:05 +0200
From: Laurentiu Palcu <laurentiu.palcu@....nxp.com>
To: Niklas Söderlund <niklas.soderlund@...natech.se>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Cc: Laurentiu Palcu <laurentiu.palcu@....nxp.com>,
linux-kernel@...r.kernel.org,
linux-media@...r.kernel.org,
linux-staging@...ts.linux.dev
Subject: [RFC 11/12] staging: media: max96712: add fsync support
FSYNC is used to align images sent from multiple sensors.
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@....nxp.com>
---
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
Powered by blists - more mailing lists