mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 19:07:52 +00:00
Merge branch 'component-for-staging' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into staging-next
Russell writes: This updates imx-drm for the recently merged updates to the component helper, and as such is based upon the previously pulled updates (including the recent fix) into the the driver tree.
This commit is contained in:
commit
4aa42291b0
3 changed files with 171 additions and 91 deletions
|
@ -18,6 +18,15 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct component_match {
|
||||
size_t alloc;
|
||||
size_t num;
|
||||
struct {
|
||||
void *data;
|
||||
int (*fn)(struct device *, void *);
|
||||
} compare[0];
|
||||
};
|
||||
|
||||
struct master {
|
||||
struct list_head node;
|
||||
struct list_head components;
|
||||
|
@ -25,6 +34,7 @@ struct master {
|
|||
|
||||
const struct component_master_ops *ops;
|
||||
struct device *dev;
|
||||
struct component_match *match;
|
||||
};
|
||||
|
||||
struct component {
|
||||
|
@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
|
|||
c->master = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to a master, finding the component via the compare
|
||||
* function and compare data. This is safe to call for duplicate matches
|
||||
* and will not result in the same component being added multiple times.
|
||||
*/
|
||||
int component_master_add_child(struct master *master,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
|
@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
|
|||
int ret = -ENXIO;
|
||||
|
||||
list_for_each_entry(c, &component_list, node) {
|
||||
if (c->master)
|
||||
if (c->master && c->master != master)
|
||||
continue;
|
||||
|
||||
if (compare(c->dev, compare_data)) {
|
||||
component_attach_master(master, c);
|
||||
if (!c->master)
|
||||
component_attach_master(master, c);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_child);
|
||||
|
||||
static int find_components(struct master *master)
|
||||
{
|
||||
struct component_match *match = master->match;
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!match) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
return master->ops->add_components(master->dev, master);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the array of match functions and attach
|
||||
* any components which are found to this master.
|
||||
*/
|
||||
for (i = 0; i < match->num; i++) {
|
||||
ret = component_master_add_child(master,
|
||||
match->compare[i].fn,
|
||||
match->compare[i].data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Detach all attached components from this master */
|
||||
static void master_remove_components(struct master *master)
|
||||
{
|
||||
|
@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
|
|||
static int try_to_bring_up_master(struct master *master,
|
||||
struct component *component)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!master->bound) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (master->ops->add_components(master->dev, master)) {
|
||||
/* Failed to find all components */
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (master->bound)
|
||||
return 0;
|
||||
|
||||
if (component && component->master != master) {
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
master_remove_components(master);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
ret = 1;
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (find_components(master)) {
|
||||
/* Failed to find all components */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (component && component->master != master) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
return 1;
|
||||
|
||||
out:
|
||||
master_remove_components(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
|
|||
master_remove_components(master);
|
||||
}
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
static size_t component_match_size(size_t num)
|
||||
{
|
||||
return offsetof(struct component_match, compare[num]);
|
||||
}
|
||||
|
||||
static struct component_match *component_match_realloc(struct device *dev,
|
||||
struct component_match *match, size_t num)
|
||||
{
|
||||
struct component_match *new;
|
||||
|
||||
if (match && match->alloc == num)
|
||||
return match;
|
||||
|
||||
new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
|
||||
if (!new)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (match) {
|
||||
memcpy(new, match, component_match_size(min(match->num, num)));
|
||||
devm_kfree(dev, match);
|
||||
} else {
|
||||
new->num = 0;
|
||||
}
|
||||
|
||||
new->alloc = num;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to be matched.
|
||||
*
|
||||
* The match array is first created or extended if necessary.
|
||||
*/
|
||||
void component_match_add(struct device *dev, struct component_match **matchptr,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
struct component_match *match = *matchptr;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
|
||||
if (!match || match->num == match->alloc) {
|
||||
size_t new_size = match ? match->alloc + 16 : 15;
|
||||
|
||||
match = component_match_realloc(dev, match, new_size);
|
||||
|
||||
*matchptr = match;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
}
|
||||
|
||||
match->compare[match->num].fn = compare;
|
||||
match->compare[match->num].data = compare_data;
|
||||
match->num++;
|
||||
}
|
||||
EXPORT_SYMBOL(component_match_add);
|
||||
|
||||
int component_master_add_with_match(struct device *dev,
|
||||
const struct component_master_ops *ops,
|
||||
struct component_match *match)
|
||||
{
|
||||
struct master *master;
|
||||
int ret;
|
||||
|
||||
if (ops->add_components && match)
|
||||
return -EINVAL;
|
||||
|
||||
if (match) {
|
||||
/* Reallocate the match array for its true size */
|
||||
match = component_match_realloc(dev, match, match->num);
|
||||
if (IS_ERR(match))
|
||||
return PTR_ERR(match);
|
||||
}
|
||||
|
||||
master = kzalloc(sizeof(*master), GFP_KERNEL);
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->dev = dev;
|
||||
master->ops = ops;
|
||||
master->match = match;
|
||||
INIT_LIST_HEAD(&master->components);
|
||||
|
||||
/* Add to the list of available masters. */
|
||||
|
@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
|
|||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_with_match);
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
{
|
||||
return component_master_add_with_match(dev, ops, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add);
|
||||
|
||||
void component_master_del(struct device *dev,
|
||||
|
|
|
@ -570,22 +570,6 @@ static int compare_of(struct device *dev, void *data)
|
|||
return dev->of_node == np;
|
||||
}
|
||||
|
||||
static LIST_HEAD(imx_drm_components);
|
||||
|
||||
static int imx_drm_add_components(struct device *master, struct master *m)
|
||||
{
|
||||
struct imx_drm_component *component;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(component, &imx_drm_components, list) {
|
||||
ret = component_master_add_child(m, compare_of,
|
||||
component->of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_drm_bind(struct device *dev)
|
||||
{
|
||||
return drm_platform_init(&imx_drm_driver, to_platform_device(dev));
|
||||
|
@ -597,43 +581,14 @@ static void imx_drm_unbind(struct device *dev)
|
|||
}
|
||||
|
||||
static const struct component_master_ops imx_drm_ops = {
|
||||
.add_components = imx_drm_add_components,
|
||||
.bind = imx_drm_bind,
|
||||
.unbind = imx_drm_unbind,
|
||||
};
|
||||
|
||||
static struct imx_drm_component *imx_drm_find_component(struct device *dev,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct imx_drm_component *component;
|
||||
|
||||
list_for_each_entry(component, &imx_drm_components, list)
|
||||
if (component->of_node == node)
|
||||
return component;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int imx_drm_add_component(struct device *dev, struct device_node *node)
|
||||
{
|
||||
struct imx_drm_component *component;
|
||||
|
||||
if (imx_drm_find_component(dev, node))
|
||||
return 0;
|
||||
|
||||
component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);
|
||||
if (!component)
|
||||
return -ENOMEM;
|
||||
|
||||
component->of_node = node;
|
||||
list_add_tail(&component->list, &imx_drm_components);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_drm_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *ep, *port, *remote;
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
@ -647,9 +602,7 @@ static int imx_drm_platform_probe(struct platform_device *pdev)
|
|||
if (!port)
|
||||
break;
|
||||
|
||||
ret = imx_drm_add_component(&pdev->dev, port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
component_match_add(&pdev->dev, &match, compare_of, port);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
|
@ -675,10 +628,8 @@ static int imx_drm_platform_probe(struct platform_device *pdev)
|
|||
continue;
|
||||
}
|
||||
|
||||
ret = imx_drm_add_component(&pdev->dev, remote);
|
||||
component_match_add(&pdev->dev, &match, compare_of, remote);
|
||||
of_node_put(remote);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
of_node_put(port);
|
||||
}
|
||||
|
@ -687,7 +638,7 @@ static int imx_drm_platform_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return component_master_add(&pdev->dev, &imx_drm_ops);
|
||||
return component_master_add_with_match(&pdev->dev, &imx_drm_ops, match);
|
||||
}
|
||||
|
||||
static int imx_drm_platform_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -29,4 +29,11 @@ void component_master_del(struct device *,
|
|||
int component_master_add_child(struct master *master,
|
||||
int (*compare)(struct device *, void *), void *compare_data);
|
||||
|
||||
struct component_match;
|
||||
|
||||
int component_master_add_with_match(struct device *,
|
||||
const struct component_master_ops *, struct component_match *);
|
||||
void component_match_add(struct device *, struct component_match **,
|
||||
int (*compare)(struct device *, void *), void *compare_data);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue