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