@@ -130,6 +130,13 @@
#define MAX96712_VRX_PATGEN_CHKR_RPT_B CCI_REG8(0x1075)
#define MAX96712_VRX_PATGEN_CHKR_ALT CCI_REG8(0x1076)
+#define MAX96712_MAX_RX_PORTS 4
+#define MAX96712_MAX_TX_PORTS 2
+#define MAX96712_MAX_VPG_PORTS 1
+#define MAX96712_MAX_PORTS (MAX96712_MAX_RX_PORTS + \
+ MAX96712_MAX_TX_PORTS + \
+ MAX96712_MAX_VPG_PORTS)
+
enum max96712_pattern {
MAX96712_PATTERN_CHECKERBOARD = 0,
MAX96712_PATTERN_GRADIENT,
@@ -139,6 +146,11 @@ struct max96712_info {
unsigned int dpllfreq;
};
+struct max96712_rx_port {
+ struct v4l2_subdev *sd;
+ struct fwnode_handle *fwnode;
+};
+
struct max96712_priv {
struct i2c_client *client;
struct regmap *regmap;
@@ -151,7 +163,11 @@ struct max96712_priv {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler ctrl_handler;
- struct media_pad pads[1];
+ struct media_pad pads[MAX96712_MAX_PORTS];
+
+ struct max96712_rx_port rx_ports[MAX96712_MAX_RX_PORTS];
+ unsigned int rx_port_mask;
+ unsigned int n_rx_ports;
enum max96712_pattern pattern;
};
@@ -343,9 +359,12 @@ static int max96712_init_state(struct v4l2_subdev *sd,
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
};
struct v4l2_mbus_framefmt *fmt;
+ int i;
- fmt = v4l2_subdev_state_get_format(state, 0);
- *fmt = default_fmt;
+ for (i = 0; i < MAX96712_MAX_PORTS; i++) {
+ fmt = v4l2_subdev_state_get_format(state, i);
+ *fmt = default_fmt;
+ }
return 0;
}
@@ -392,6 +411,7 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
{
long pixel_rate;
int ret;
+ int i;
priv->sd.internal_ops = &max96712_internal_ops;
v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96712_subdev_ops);
@@ -418,8 +438,14 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
if (ret)
goto error;
- priv->pads[0].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&priv->sd.entity, 1, priv->pads);
+ for (i = 0; i < MAX96712_MAX_RX_PORTS + MAX96712_MAX_TX_PORTS; i++)
+ priv->pads[i].flags = i < MAX96712_MAX_RX_PORTS ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ /* The last pad is the VPG pad. */
+ priv->pads[i].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL;
+
+ ret = media_entity_pads_init(&priv->sd.entity, MAX96712_MAX_PORTS, priv->pads);
if (ret)
goto error;
@@ -443,24 +469,53 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
return ret;
}
-static int max96712_parse_dt(struct max96712_priv *priv)
+static int max96712_parse_rx_ports(struct max96712_priv *priv, struct device_node *node,
+ struct of_endpoint *ep)
+{
+ struct device *dev = &priv->client->dev;
+ struct max96712_rx_port *source;
+ struct fwnode_handle *remote_port_parent;
+
+ if (priv->rx_ports[ep->port].fwnode) {
+ dev_info(dev, "Multiple port endpoints are not supported: %d", ep->port);
+ return 0;
+ }
+
+ source = &priv->rx_ports[ep->port];
+ source->fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(node));
+ if (!source->fwnode) {
+ dev_info(dev, "Endpoint %pOF has no remote endpoint connection\n", ep->local_node);
+ return 0;
+ }
+
+ remote_port_parent = fwnode_graph_get_remote_port_parent(of_fwnode_handle(node));
+
+ if (!fwnode_device_is_available(remote_port_parent)) {
+ dev_dbg(dev, "Skipping port %d as remote port parent is disabled.\n",
+ ep->port);
+ source->fwnode = NULL;
+ goto fwnode_put;
+ }
+
+ priv->rx_port_mask |= BIT(ep->port);
+ priv->n_rx_ports++;
+
+fwnode_put:
+ fwnode_handle_put(remote_port_parent);
+ fwnode_handle_put(source->fwnode);
+ return 0;
+}
+
+static int max96712_parse_tx_ports(struct max96712_priv *priv, struct device_node *node,
+ struct of_endpoint *ep)
{
- struct fwnode_handle *ep;
struct v4l2_fwnode_endpoint v4l2_ep = {
.bus_type = V4L2_MBUS_UNKNOWN,
};
unsigned int supported_lanes;
int ret;
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(&priv->client->dev), 4,
- 0, 0);
- if (!ep) {
- dev_err(&priv->client->dev, "Not connected to subdevice\n");
- return -EINVAL;
- }
-
- ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
- fwnode_handle_put(ep);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &v4l2_ep);
if (ret) {
dev_err(&priv->client->dev, "Could not parse v4l2 endpoint\n");
return -EINVAL;
@@ -492,6 +547,41 @@ static int max96712_parse_dt(struct max96712_priv *priv)
return 0;
}
+static int max96712_parse_dt(struct max96712_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct device_node *node = NULL;
+ int ret = 0;
+
+ for_each_endpoint_of_node(dev->of_node, node) {
+ struct of_endpoint ep;
+
+ of_graph_parse_endpoint(node, &ep);
+
+ if (ep.port >= MAX96712_MAX_PORTS) {
+ dev_err(dev, "Invalid endpoint %s on port %d",
+ of_node_full_name(ep.local_node), ep.port);
+ continue;
+ }
+
+ if (ep.port >= MAX96712_MAX_RX_PORTS) {
+ ret = max96712_parse_tx_ports(priv, node, &ep);
+ if (ret)
+ goto exit;
+
+ continue;
+ }
+
+ ret = max96712_parse_rx_ports(priv, node, &ep);
+ if (ret)
+ goto exit;
+ }
+
+exit:
+ of_node_put(node);
+ return ret;
+}
+
static const struct regmap_config max96712_i2c_regmap = {
.reg_bits = 16,
.val_bits = 8,
Currently, the driver supports only one source media pad. In order to be able to use the driver with other serializers we need sink media pads as well. This patch adds support for parsing the source endpoints and create the corresponding sink pads associated with the endpoints in DT. The driver functionality is not changed at this point: only sink and source pads are created. However, since the first source pad index is 4, the user needs to make sure to link the source pad correctly to the next downstream node. Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com> --- drivers/staging/media/max96712/max96712.c | 122 +++++++++++++++++++--- 1 file changed, 106 insertions(+), 16 deletions(-)