media: software_node: Add support for fwnode_graph*() family of functions
This implements the remaining .graph_*() callbacks in the fwnode operations structure for the software nodes. That makes the fwnode_graph_*() functions available in the drivers also when software nodes are used. The implementation tries to mimic the "OF graph" as much as possible, but there is no support for the "reg" device property. The ports will need to have the index in their name which starts with "port@" (for example "port@0", "port@1", ...) and endpoints will use the index of the software node that is given to them during creation. The port nodes can also be grouped under a specially named "ports" subnode, just like in DT, if necessary. The remote-endpoints are reference properties under the endpoint nodes that are named "remote-endpoint". Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Co-developed-by: Daniel Scally <djrscally@gmail.com> Signed-off-by: Daniel Scally <djrscally@gmail.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
parent
529b56a854
commit
000c08fda6
|
@ -540,6 +540,115 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
swnode_graph_find_next_port(const struct fwnode_handle *parent,
|
||||
struct fwnode_handle *port)
|
||||
{
|
||||
struct fwnode_handle *old = port;
|
||||
|
||||
while ((port = software_node_get_next_child(parent, old))) {
|
||||
/*
|
||||
* fwnode ports have naming style "port@", so we search for any
|
||||
* children that follow that convention.
|
||||
*/
|
||||
if (!strncmp(to_swnode(port)->node->name, "port@",
|
||||
strlen("port@")))
|
||||
return port;
|
||||
old = port;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
|
||||
struct fwnode_handle *endpoint)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
struct fwnode_handle *parent;
|
||||
struct fwnode_handle *port;
|
||||
|
||||
if (!swnode)
|
||||
return NULL;
|
||||
|
||||
if (endpoint) {
|
||||
port = software_node_get_parent(endpoint);
|
||||
parent = software_node_get_parent(port);
|
||||
} else {
|
||||
parent = software_node_get_named_child_node(fwnode, "ports");
|
||||
if (!parent)
|
||||
parent = software_node_get(&swnode->fwnode);
|
||||
|
||||
port = swnode_graph_find_next_port(parent, NULL);
|
||||
}
|
||||
|
||||
for (; port; port = swnode_graph_find_next_port(parent, port)) {
|
||||
endpoint = software_node_get_next_child(port, endpoint);
|
||||
if (endpoint) {
|
||||
fwnode_handle_put(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fwnode_handle_put(parent);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
const struct software_node_ref_args *ref;
|
||||
const struct property_entry *prop;
|
||||
|
||||
if (!swnode)
|
||||
return NULL;
|
||||
|
||||
prop = property_entry_get(swnode->node->properties, "remote-endpoint");
|
||||
if (!prop || prop->type != DEV_PROP_REF || prop->is_inline)
|
||||
return NULL;
|
||||
|
||||
ref = prop->pointer;
|
||||
|
||||
return software_node_get(software_node_fwnode(ref[0].node));
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
software_node_graph_get_port_parent(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
swnode = swnode->parent;
|
||||
if (swnode && !strcmp(swnode->node->name, "ports"))
|
||||
swnode = swnode->parent;
|
||||
|
||||
return swnode ? software_node_get(&swnode->fwnode) : NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
|
||||
struct fwnode_endpoint *endpoint)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
const char *parent_name = swnode->parent->node->name;
|
||||
int ret;
|
||||
|
||||
if (strlen("port@") >= strlen(parent_name) ||
|
||||
strncmp(parent_name, "port@", strlen("port@")))
|
||||
return -EINVAL;
|
||||
|
||||
/* Ports have naming style "port@n", we need to select the n */
|
||||
ret = kstrtou32(parent_name + strlen("port@"), 10, &endpoint->port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
endpoint->id = swnode->id;
|
||||
endpoint->local_fwnode = fwnode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fwnode_operations software_node_ops = {
|
||||
.get = software_node_get,
|
||||
.put = software_node_put,
|
||||
|
@ -551,7 +660,11 @@ static const struct fwnode_operations software_node_ops = {
|
|||
.get_parent = software_node_get_parent,
|
||||
.get_next_child_node = software_node_get_next_child,
|
||||
.get_named_child_node = software_node_get_named_child_node,
|
||||
.get_reference_args = software_node_get_reference_args
|
||||
.get_reference_args = software_node_get_reference_args,
|
||||
.graph_get_next_endpoint = software_node_graph_get_next_endpoint,
|
||||
.graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
|
||||
.graph_get_port_parent = software_node_graph_get_port_parent,
|
||||
.graph_parse_endpoint = software_node_graph_parse_endpoint,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
Loading…
Reference in New Issue