18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Componentized device handling. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This is work in progress. We gather up the component devices into a list, 68c2ecf20Sopenharmony_ci * and bind them when instructed. At the moment, we're specific to the DRM 78c2ecf20Sopenharmony_ci * subsystem, and only handles one master device, but this doesn't have to be 88c2ecf20Sopenharmony_ci * the case. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/component.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/kref.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * DOC: overview 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The component helper allows drivers to collect a pile of sub-devices, 228c2ecf20Sopenharmony_ci * including their bound drivers, into an aggregate driver. Various subsystems 238c2ecf20Sopenharmony_ci * already provide functions to get hold of such components, e.g. 248c2ecf20Sopenharmony_ci * of_clk_get_by_name(). The component helper can be used when such a 258c2ecf20Sopenharmony_ci * subsystem-specific way to find a device is not available: The component 268c2ecf20Sopenharmony_ci * helper fills the niche of aggregate drivers for specific hardware, where 278c2ecf20Sopenharmony_ci * further standardization into a subsystem would not be practical. The common 288c2ecf20Sopenharmony_ci * example is when a logical device (e.g. a DRM display driver) is spread around 298c2ecf20Sopenharmony_ci * the SoC on various components (scanout engines, blending blocks, transcoders 308c2ecf20Sopenharmony_ci * for various outputs and so on). 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * The component helper also doesn't solve runtime dependencies, e.g. for system 338c2ecf20Sopenharmony_ci * suspend and resume operations. See also :ref:`device links<device_link>`. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Components are registered using component_add() and unregistered with 368c2ecf20Sopenharmony_ci * component_del(), usually from the driver's probe and disconnect functions. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Aggregate drivers first assemble a component match list of what they need 398c2ecf20Sopenharmony_ci * using component_match_add(). This is then registered as an aggregate driver 408c2ecf20Sopenharmony_ci * using component_master_add_with_match(), and unregistered using 418c2ecf20Sopenharmony_ci * component_master_del(). 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct component; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct component_match_array { 478c2ecf20Sopenharmony_ci void *data; 488c2ecf20Sopenharmony_ci int (*compare)(struct device *, void *); 498c2ecf20Sopenharmony_ci int (*compare_typed)(struct device *, int, void *); 508c2ecf20Sopenharmony_ci void (*release)(struct device *, void *); 518c2ecf20Sopenharmony_ci struct component *component; 528c2ecf20Sopenharmony_ci bool duplicate; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct component_match { 568c2ecf20Sopenharmony_ci size_t alloc; 578c2ecf20Sopenharmony_ci size_t num; 588c2ecf20Sopenharmony_ci struct component_match_array *compare; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct master { 628c2ecf20Sopenharmony_ci struct list_head node; 638c2ecf20Sopenharmony_ci bool bound; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci const struct component_master_ops *ops; 668c2ecf20Sopenharmony_ci struct device *dev; 678c2ecf20Sopenharmony_ci struct component_match *match; 688c2ecf20Sopenharmony_ci struct dentry *dentry; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct component { 728c2ecf20Sopenharmony_ci struct list_head node; 738c2ecf20Sopenharmony_ci struct master *master; 748c2ecf20Sopenharmony_ci bool bound; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci const struct component_ops *ops; 778c2ecf20Sopenharmony_ci int subcomponent; 788c2ecf20Sopenharmony_ci struct device *dev; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(component_mutex); 828c2ecf20Sopenharmony_cistatic LIST_HEAD(component_list); 838c2ecf20Sopenharmony_cistatic LIST_HEAD(masters); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic struct dentry *component_debugfs_dir; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int component_devices_show(struct seq_file *s, void *data) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct master *m = s->private; 928c2ecf20Sopenharmony_ci struct component_match *match = m->match; 938c2ecf20Sopenharmony_ci size_t i; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci mutex_lock(&component_mutex); 968c2ecf20Sopenharmony_ci seq_printf(s, "%-40s %20s\n", "master name", "status"); 978c2ecf20Sopenharmony_ci seq_puts(s, "-------------------------------------------------------------\n"); 988c2ecf20Sopenharmony_ci seq_printf(s, "%-40s %20s\n\n", 998c2ecf20Sopenharmony_ci dev_name(m->dev), m->bound ? "bound" : "not bound"); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci seq_printf(s, "%-40s %20s\n", "device name", "status"); 1028c2ecf20Sopenharmony_ci seq_puts(s, "-------------------------------------------------------------\n"); 1038c2ecf20Sopenharmony_ci for (i = 0; i < match->num; i++) { 1048c2ecf20Sopenharmony_ci struct component *component = match->compare[i].component; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci seq_printf(s, "%-40s %20s\n", 1078c2ecf20Sopenharmony_ci component ? dev_name(component->dev) : "(unknown)", 1088c2ecf20Sopenharmony_ci component ? (component->bound ? "bound" : "not bound") : "not registered"); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci mutex_unlock(&component_mutex); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(component_devices); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int __init component_debug_init(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci component_debugfs_dir = debugfs_create_dir("device_component", NULL); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cicore_initcall(component_debug_init); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void component_master_debugfs_add(struct master *m) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci m->dentry = debugfs_create_file(dev_name(m->dev), 0444, 1298c2ecf20Sopenharmony_ci component_debugfs_dir, 1308c2ecf20Sopenharmony_ci m, &component_devices_fops); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void component_master_debugfs_del(struct master *m) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci debugfs_remove(m->dentry); 1368c2ecf20Sopenharmony_ci m->dentry = NULL; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#else 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void component_master_debugfs_add(struct master *m) 1428c2ecf20Sopenharmony_ci{ } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void component_master_debugfs_del(struct master *m) 1458c2ecf20Sopenharmony_ci{ } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#endif 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct master *__master_find(struct device *dev, 1508c2ecf20Sopenharmony_ci const struct component_master_ops *ops) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct master *m; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci list_for_each_entry(m, &masters, node) 1558c2ecf20Sopenharmony_ci if (m->dev == dev && (!ops || m->ops == ops)) 1568c2ecf20Sopenharmony_ci return m; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return NULL; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct component *find_component(struct master *master, 1628c2ecf20Sopenharmony_ci struct component_match_array *mc) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct component *c; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci list_for_each_entry(c, &component_list, node) { 1678c2ecf20Sopenharmony_ci if (c->master && c->master != master) 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (mc->compare && mc->compare(c->dev, mc->data)) 1718c2ecf20Sopenharmony_ci return c; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (mc->compare_typed && 1748c2ecf20Sopenharmony_ci mc->compare_typed(c->dev, c->subcomponent, mc->data)) 1758c2ecf20Sopenharmony_ci return c; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return NULL; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int find_components(struct master *master) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct component_match *match = master->match; 1848c2ecf20Sopenharmony_ci size_t i; 1858c2ecf20Sopenharmony_ci int ret = 0; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * Scan the array of match functions and attach 1898c2ecf20Sopenharmony_ci * any components which are found to this master. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci for (i = 0; i < match->num; i++) { 1928c2ecf20Sopenharmony_ci struct component_match_array *mc = &match->compare[i]; 1938c2ecf20Sopenharmony_ci struct component *c; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dev_dbg(master->dev, "Looking for component %zu\n", i); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (match->compare[i].component) 1988c2ecf20Sopenharmony_ci continue; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci c = find_component(master, mc); 2018c2ecf20Sopenharmony_ci if (!c) { 2028c2ecf20Sopenharmony_ci ret = -ENXIO; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Attach this component to the master */ 2098c2ecf20Sopenharmony_ci match->compare[i].duplicate = !!c->master; 2108c2ecf20Sopenharmony_ci match->compare[i].component = c; 2118c2ecf20Sopenharmony_ci c->master = master; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Detach component from associated master */ 2178c2ecf20Sopenharmony_cistatic void remove_component(struct master *master, struct component *c) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci size_t i; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Detach the component from this master. */ 2228c2ecf20Sopenharmony_ci for (i = 0; i < master->match->num; i++) 2238c2ecf20Sopenharmony_ci if (master->match->compare[i].component == c) 2248c2ecf20Sopenharmony_ci master->match->compare[i].component = NULL; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * Try to bring up a master. If component is NULL, we're interested in 2298c2ecf20Sopenharmony_ci * this master, otherwise it's a component which must be present to try 2308c2ecf20Sopenharmony_ci * and bring up the master. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Returns 1 for successful bringup, 0 if not ready, or -ve errno. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic int try_to_bring_up_master(struct master *master, 2358c2ecf20Sopenharmony_ci struct component *component) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci int ret; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci dev_dbg(master->dev, "trying to bring up master\n"); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (find_components(master)) { 2428c2ecf20Sopenharmony_ci dev_dbg(master->dev, "master has incomplete components\n"); 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (component && component->master != master) { 2478c2ecf20Sopenharmony_ci dev_dbg(master->dev, "master is not for this component (%s)\n", 2488c2ecf20Sopenharmony_ci dev_name(component->dev)); 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Found all components */ 2568c2ecf20Sopenharmony_ci ret = master->ops->bind(master->dev); 2578c2ecf20Sopenharmony_ci if (ret < 0) { 2588c2ecf20Sopenharmony_ci devres_release_group(master->dev, NULL); 2598c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 2608c2ecf20Sopenharmony_ci dev_info(master->dev, "master bind failed: %d\n", ret); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci master->bound = true; 2658c2ecf20Sopenharmony_ci return 1; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int try_to_bring_up_masters(struct component *component) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct master *m; 2718c2ecf20Sopenharmony_ci int ret = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci list_for_each_entry(m, &masters, node) { 2748c2ecf20Sopenharmony_ci if (!m->bound) { 2758c2ecf20Sopenharmony_ci ret = try_to_bring_up_master(m, component); 2768c2ecf20Sopenharmony_ci if (ret != 0) 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return ret; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void take_down_master(struct master *master) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci if (master->bound) { 2878c2ecf20Sopenharmony_ci master->ops->unbind(master->dev); 2888c2ecf20Sopenharmony_ci devres_release_group(master->dev, NULL); 2898c2ecf20Sopenharmony_ci master->bound = false; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void component_match_release(struct device *master, 2948c2ecf20Sopenharmony_ci struct component_match *match) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci unsigned int i; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci for (i = 0; i < match->num; i++) { 2998c2ecf20Sopenharmony_ci struct component_match_array *mc = &match->compare[i]; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (mc->release) 3028c2ecf20Sopenharmony_ci mc->release(master, mc->data); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci kfree(match->compare); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void devm_component_match_release(struct device *dev, void *res) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci component_match_release(dev, res); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int component_match_realloc(struct device *dev, 3148c2ecf20Sopenharmony_ci struct component_match *match, size_t num) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct component_match_array *new; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (match->alloc == num) 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci new = kmalloc_array(num, sizeof(*new), GFP_KERNEL); 3228c2ecf20Sopenharmony_ci if (!new) 3238c2ecf20Sopenharmony_ci return -ENOMEM; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (match->compare) { 3268c2ecf20Sopenharmony_ci memcpy(new, match->compare, sizeof(*new) * 3278c2ecf20Sopenharmony_ci min(match->num, num)); 3288c2ecf20Sopenharmony_ci kfree(match->compare); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci match->compare = new; 3318c2ecf20Sopenharmony_ci match->alloc = num; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void __component_match_add(struct device *master, 3378c2ecf20Sopenharmony_ci struct component_match **matchptr, 3388c2ecf20Sopenharmony_ci void (*release)(struct device *, void *), 3398c2ecf20Sopenharmony_ci int (*compare)(struct device *, void *), 3408c2ecf20Sopenharmony_ci int (*compare_typed)(struct device *, int, void *), 3418c2ecf20Sopenharmony_ci void *compare_data) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct component_match *match = *matchptr; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (IS_ERR(match)) 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!match) { 3498c2ecf20Sopenharmony_ci match = devres_alloc(devm_component_match_release, 3508c2ecf20Sopenharmony_ci sizeof(*match), GFP_KERNEL); 3518c2ecf20Sopenharmony_ci if (!match) { 3528c2ecf20Sopenharmony_ci *matchptr = ERR_PTR(-ENOMEM); 3538c2ecf20Sopenharmony_ci return; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci devres_add(master, match); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci *matchptr = match; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (match->num == match->alloc) { 3628c2ecf20Sopenharmony_ci size_t new_size = match->alloc + 16; 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = component_match_realloc(master, match, new_size); 3668c2ecf20Sopenharmony_ci if (ret) { 3678c2ecf20Sopenharmony_ci *matchptr = ERR_PTR(ret); 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci match->compare[match->num].compare = compare; 3738c2ecf20Sopenharmony_ci match->compare[match->num].compare_typed = compare_typed; 3748c2ecf20Sopenharmony_ci match->compare[match->num].release = release; 3758c2ecf20Sopenharmony_ci match->compare[match->num].data = compare_data; 3768c2ecf20Sopenharmony_ci match->compare[match->num].component = NULL; 3778c2ecf20Sopenharmony_ci match->num++; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/** 3818c2ecf20Sopenharmony_ci * component_match_add_release - add a component match entry with release callback 3828c2ecf20Sopenharmony_ci * @master: device with the aggregate driver 3838c2ecf20Sopenharmony_ci * @matchptr: pointer to the list of component matches 3848c2ecf20Sopenharmony_ci * @release: release function for @compare_data 3858c2ecf20Sopenharmony_ci * @compare: compare function to match against all components 3868c2ecf20Sopenharmony_ci * @compare_data: opaque pointer passed to the @compare function 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * Adds a new component match to the list stored in @matchptr, which the @master 3898c2ecf20Sopenharmony_ci * aggregate driver needs to function. The list of component matches pointed to 3908c2ecf20Sopenharmony_ci * by @matchptr must be initialized to NULL before adding the first match. This 3918c2ecf20Sopenharmony_ci * only matches against components added with component_add(). 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * The allocated match list in @matchptr is automatically released using devm 3948c2ecf20Sopenharmony_ci * actions, where upon @release will be called to free any references held by 3958c2ecf20Sopenharmony_ci * @compare_data, e.g. when @compare_data is a &device_node that must be 3968c2ecf20Sopenharmony_ci * released with of_node_put(). 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * See also component_match_add() and component_match_add_typed(). 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_civoid component_match_add_release(struct device *master, 4018c2ecf20Sopenharmony_ci struct component_match **matchptr, 4028c2ecf20Sopenharmony_ci void (*release)(struct device *, void *), 4038c2ecf20Sopenharmony_ci int (*compare)(struct device *, void *), void *compare_data) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci __component_match_add(master, matchptr, release, compare, NULL, 4068c2ecf20Sopenharmony_ci compare_data); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(component_match_add_release); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/** 4118c2ecf20Sopenharmony_ci * component_match_add_typed - add a component match entry for a typed component 4128c2ecf20Sopenharmony_ci * @master: device with the aggregate driver 4138c2ecf20Sopenharmony_ci * @matchptr: pointer to the list of component matches 4148c2ecf20Sopenharmony_ci * @compare_typed: compare function to match against all typed components 4158c2ecf20Sopenharmony_ci * @compare_data: opaque pointer passed to the @compare function 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * Adds a new component match to the list stored in @matchptr, which the @master 4188c2ecf20Sopenharmony_ci * aggregate driver needs to function. The list of component matches pointed to 4198c2ecf20Sopenharmony_ci * by @matchptr must be initialized to NULL before adding the first match. This 4208c2ecf20Sopenharmony_ci * only matches against components added with component_add_typed(). 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * The allocated match list in @matchptr is automatically released using devm 4238c2ecf20Sopenharmony_ci * actions. 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * See also component_match_add_release() and component_match_add_typed(). 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_civoid component_match_add_typed(struct device *master, 4288c2ecf20Sopenharmony_ci struct component_match **matchptr, 4298c2ecf20Sopenharmony_ci int (*compare_typed)(struct device *, int, void *), void *compare_data) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci __component_match_add(master, matchptr, NULL, NULL, compare_typed, 4328c2ecf20Sopenharmony_ci compare_data); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(component_match_add_typed); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void free_master(struct master *master) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct component_match *match = master->match; 4398c2ecf20Sopenharmony_ci int i; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci component_master_debugfs_del(master); 4428c2ecf20Sopenharmony_ci list_del(&master->node); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (match) { 4458c2ecf20Sopenharmony_ci for (i = 0; i < match->num; i++) { 4468c2ecf20Sopenharmony_ci struct component *c = match->compare[i].component; 4478c2ecf20Sopenharmony_ci if (c) 4488c2ecf20Sopenharmony_ci c->master = NULL; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci kfree(master); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/** 4568c2ecf20Sopenharmony_ci * component_master_add_with_match - register an aggregate driver 4578c2ecf20Sopenharmony_ci * @dev: device with the aggregate driver 4588c2ecf20Sopenharmony_ci * @ops: callbacks for the aggregate driver 4598c2ecf20Sopenharmony_ci * @match: component match list for the aggregate driver 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci * Registers a new aggregate driver consisting of the components added to @match 4628c2ecf20Sopenharmony_ci * by calling one of the component_match_add() functions. Once all components in 4638c2ecf20Sopenharmony_ci * @match are available, it will be assembled by calling 4648c2ecf20Sopenharmony_ci * &component_master_ops.bind from @ops. Must be unregistered by calling 4658c2ecf20Sopenharmony_ci * component_master_del(). 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ciint component_master_add_with_match(struct device *dev, 4688c2ecf20Sopenharmony_ci const struct component_master_ops *ops, 4698c2ecf20Sopenharmony_ci struct component_match *match) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct master *master; 4728c2ecf20Sopenharmony_ci int ret; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Reallocate the match array for its true size */ 4758c2ecf20Sopenharmony_ci ret = component_match_realloc(dev, match, match->num); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 4808c2ecf20Sopenharmony_ci if (!master) 4818c2ecf20Sopenharmony_ci return -ENOMEM; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci master->dev = dev; 4848c2ecf20Sopenharmony_ci master->ops = ops; 4858c2ecf20Sopenharmony_ci master->match = match; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci component_master_debugfs_add(master); 4888c2ecf20Sopenharmony_ci /* Add to the list of available masters. */ 4898c2ecf20Sopenharmony_ci mutex_lock(&component_mutex); 4908c2ecf20Sopenharmony_ci list_add(&master->node, &masters); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = try_to_bring_up_master(master, NULL); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ret < 0) 4958c2ecf20Sopenharmony_ci free_master(master); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci mutex_unlock(&component_mutex); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_master_add_with_match); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * component_master_del - unregister an aggregate driver 5058c2ecf20Sopenharmony_ci * @dev: device with the aggregate driver 5068c2ecf20Sopenharmony_ci * @ops: callbacks for the aggregate driver 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Unregisters an aggregate driver registered with 5098c2ecf20Sopenharmony_ci * component_master_add_with_match(). If necessary the aggregate driver is first 5108c2ecf20Sopenharmony_ci * disassembled by calling &component_master_ops.unbind from @ops. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_civoid component_master_del(struct device *dev, 5138c2ecf20Sopenharmony_ci const struct component_master_ops *ops) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct master *master; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci mutex_lock(&component_mutex); 5188c2ecf20Sopenharmony_ci master = __master_find(dev, ops); 5198c2ecf20Sopenharmony_ci if (master) { 5208c2ecf20Sopenharmony_ci take_down_master(master); 5218c2ecf20Sopenharmony_ci free_master(master); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci mutex_unlock(&component_mutex); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_master_del); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void component_unbind(struct component *component, 5288c2ecf20Sopenharmony_ci struct master *master, void *data) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci WARN_ON(!component->bound); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (component->ops && component->ops->unbind) 5338c2ecf20Sopenharmony_ci component->ops->unbind(component->dev, master->dev, data); 5348c2ecf20Sopenharmony_ci component->bound = false; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Release all resources claimed in the binding of this component */ 5378c2ecf20Sopenharmony_ci devres_release_group(component->dev, component); 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/** 5418c2ecf20Sopenharmony_ci * component_unbind_all - unbind all components of an aggregate driver 5428c2ecf20Sopenharmony_ci * @master_dev: device with the aggregate driver 5438c2ecf20Sopenharmony_ci * @data: opaque pointer, passed to all components 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * Unbinds all components of the aggregate @dev by passing @data to their 5468c2ecf20Sopenharmony_ci * &component_ops.unbind functions. Should be called from 5478c2ecf20Sopenharmony_ci * &component_master_ops.unbind. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_civoid component_unbind_all(struct device *master_dev, void *data) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct master *master; 5528c2ecf20Sopenharmony_ci struct component *c; 5538c2ecf20Sopenharmony_ci size_t i; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&component_mutex)); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci master = __master_find(master_dev, NULL); 5588c2ecf20Sopenharmony_ci if (!master) 5598c2ecf20Sopenharmony_ci return; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Unbind components in reverse order */ 5628c2ecf20Sopenharmony_ci for (i = master->match->num; i--; ) 5638c2ecf20Sopenharmony_ci if (!master->match->compare[i].duplicate) { 5648c2ecf20Sopenharmony_ci c = master->match->compare[i].component; 5658c2ecf20Sopenharmony_ci component_unbind(c, master, data); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_unbind_all); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int component_bind(struct component *component, struct master *master, 5718c2ecf20Sopenharmony_ci void *data) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci int ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* 5768c2ecf20Sopenharmony_ci * Each component initialises inside its own devres group. 5778c2ecf20Sopenharmony_ci * This allows us to roll-back a failed component without 5788c2ecf20Sopenharmony_ci * affecting anything else. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) 5818c2ecf20Sopenharmony_ci return -ENOMEM; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* 5848c2ecf20Sopenharmony_ci * Also open a group for the device itself: this allows us 5858c2ecf20Sopenharmony_ci * to release the resources claimed against the sub-device 5868c2ecf20Sopenharmony_ci * at the appropriate moment. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (!devres_open_group(component->dev, component, GFP_KERNEL)) { 5898c2ecf20Sopenharmony_ci devres_release_group(master->dev, NULL); 5908c2ecf20Sopenharmony_ci return -ENOMEM; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dev_dbg(master->dev, "binding %s (ops %ps)\n", 5948c2ecf20Sopenharmony_ci dev_name(component->dev), component->ops); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ret = component->ops->bind(component->dev, master->dev, data); 5978c2ecf20Sopenharmony_ci if (!ret) { 5988c2ecf20Sopenharmony_ci component->bound = true; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* 6018c2ecf20Sopenharmony_ci * Close the component device's group so that resources 6028c2ecf20Sopenharmony_ci * allocated in the binding are encapsulated for removal 6038c2ecf20Sopenharmony_ci * at unbind. Remove the group on the DRM device as we 6048c2ecf20Sopenharmony_ci * can clean those resources up independently. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci devres_close_group(component->dev, NULL); 6078c2ecf20Sopenharmony_ci devres_remove_group(master->dev, NULL); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci dev_info(master->dev, "bound %s (ops %ps)\n", 6108c2ecf20Sopenharmony_ci dev_name(component->dev), component->ops); 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci devres_release_group(component->dev, NULL); 6138c2ecf20Sopenharmony_ci devres_release_group(master->dev, NULL); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 6168c2ecf20Sopenharmony_ci dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", 6178c2ecf20Sopenharmony_ci dev_name(component->dev), component->ops, ret); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return ret; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/** 6248c2ecf20Sopenharmony_ci * component_bind_all - bind all components of an aggregate driver 6258c2ecf20Sopenharmony_ci * @master_dev: device with the aggregate driver 6268c2ecf20Sopenharmony_ci * @data: opaque pointer, passed to all components 6278c2ecf20Sopenharmony_ci * 6288c2ecf20Sopenharmony_ci * Binds all components of the aggregate @dev by passing @data to their 6298c2ecf20Sopenharmony_ci * &component_ops.bind functions. Should be called from 6308c2ecf20Sopenharmony_ci * &component_master_ops.bind. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ciint component_bind_all(struct device *master_dev, void *data) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct master *master; 6358c2ecf20Sopenharmony_ci struct component *c; 6368c2ecf20Sopenharmony_ci size_t i; 6378c2ecf20Sopenharmony_ci int ret = 0; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&component_mutex)); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci master = __master_find(master_dev, NULL); 6428c2ecf20Sopenharmony_ci if (!master) 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Bind components in match order */ 6468c2ecf20Sopenharmony_ci for (i = 0; i < master->match->num; i++) 6478c2ecf20Sopenharmony_ci if (!master->match->compare[i].duplicate) { 6488c2ecf20Sopenharmony_ci c = master->match->compare[i].component; 6498c2ecf20Sopenharmony_ci ret = component_bind(c, master, data); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (ret != 0) { 6558c2ecf20Sopenharmony_ci for (; i > 0; i--) 6568c2ecf20Sopenharmony_ci if (!master->match->compare[i - 1].duplicate) { 6578c2ecf20Sopenharmony_ci c = master->match->compare[i - 1].component; 6588c2ecf20Sopenharmony_ci component_unbind(c, master, data); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_bind_all); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic int __component_add(struct device *dev, const struct component_ops *ops, 6678c2ecf20Sopenharmony_ci int subcomponent) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct component *component; 6708c2ecf20Sopenharmony_ci int ret; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci component = kzalloc(sizeof(*component), GFP_KERNEL); 6738c2ecf20Sopenharmony_ci if (!component) 6748c2ecf20Sopenharmony_ci return -ENOMEM; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci component->ops = ops; 6778c2ecf20Sopenharmony_ci component->dev = dev; 6788c2ecf20Sopenharmony_ci component->subcomponent = subcomponent; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev_dbg(dev, "adding component (ops %ps)\n", ops); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci mutex_lock(&component_mutex); 6838c2ecf20Sopenharmony_ci list_add_tail(&component->node, &component_list); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = try_to_bring_up_masters(component); 6868c2ecf20Sopenharmony_ci if (ret < 0) { 6878c2ecf20Sopenharmony_ci if (component->master) 6888c2ecf20Sopenharmony_ci remove_component(component->master, component); 6898c2ecf20Sopenharmony_ci list_del(&component->node); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci kfree(component); 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci mutex_unlock(&component_mutex); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/** 6998c2ecf20Sopenharmony_ci * component_add_typed - register a component 7008c2ecf20Sopenharmony_ci * @dev: component device 7018c2ecf20Sopenharmony_ci * @ops: component callbacks 7028c2ecf20Sopenharmony_ci * @subcomponent: nonzero identifier for subcomponents 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Register a new component for @dev. Functions in @ops will be call when the 7058c2ecf20Sopenharmony_ci * aggregate driver is ready to bind the overall driver by calling 7068c2ecf20Sopenharmony_ci * component_bind_all(). See also &struct component_ops. 7078c2ecf20Sopenharmony_ci * 7088c2ecf20Sopenharmony_ci * @subcomponent must be nonzero and is used to differentiate between multiple 7098c2ecf20Sopenharmony_ci * components registerd on the same device @dev. These components are match 7108c2ecf20Sopenharmony_ci * using component_match_add_typed(). 7118c2ecf20Sopenharmony_ci * 7128c2ecf20Sopenharmony_ci * The component needs to be unregistered at driver unload/disconnect by 7138c2ecf20Sopenharmony_ci * calling component_del(). 7148c2ecf20Sopenharmony_ci * 7158c2ecf20Sopenharmony_ci * See also component_add(). 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ciint component_add_typed(struct device *dev, const struct component_ops *ops, 7188c2ecf20Sopenharmony_ci int subcomponent) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci if (WARN_ON(subcomponent == 0)) 7218c2ecf20Sopenharmony_ci return -EINVAL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return __component_add(dev, ops, subcomponent); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_add_typed); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/** 7288c2ecf20Sopenharmony_ci * component_add - register a component 7298c2ecf20Sopenharmony_ci * @dev: component device 7308c2ecf20Sopenharmony_ci * @ops: component callbacks 7318c2ecf20Sopenharmony_ci * 7328c2ecf20Sopenharmony_ci * Register a new component for @dev. Functions in @ops will be called when the 7338c2ecf20Sopenharmony_ci * aggregate driver is ready to bind the overall driver by calling 7348c2ecf20Sopenharmony_ci * component_bind_all(). See also &struct component_ops. 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * The component needs to be unregistered at driver unload/disconnect by 7378c2ecf20Sopenharmony_ci * calling component_del(). 7388c2ecf20Sopenharmony_ci * 7398c2ecf20Sopenharmony_ci * See also component_add_typed() for a variant that allows multipled different 7408c2ecf20Sopenharmony_ci * components on the same device. 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ciint component_add(struct device *dev, const struct component_ops *ops) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci return __component_add(dev, ops, 0); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_add); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/** 7498c2ecf20Sopenharmony_ci * component_del - unregister a component 7508c2ecf20Sopenharmony_ci * @dev: component device 7518c2ecf20Sopenharmony_ci * @ops: component callbacks 7528c2ecf20Sopenharmony_ci * 7538c2ecf20Sopenharmony_ci * Unregister a component added with component_add(). If the component is bound 7548c2ecf20Sopenharmony_ci * into an aggregate driver, this will force the entire aggregate driver, including 7558c2ecf20Sopenharmony_ci * all its components, to be unbound. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_civoid component_del(struct device *dev, const struct component_ops *ops) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct component *c, *component = NULL; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci mutex_lock(&component_mutex); 7628c2ecf20Sopenharmony_ci list_for_each_entry(c, &component_list, node) 7638c2ecf20Sopenharmony_ci if (c->dev == dev && c->ops == ops) { 7648c2ecf20Sopenharmony_ci list_del(&c->node); 7658c2ecf20Sopenharmony_ci component = c; 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (component && component->master) { 7708c2ecf20Sopenharmony_ci take_down_master(component->master); 7718c2ecf20Sopenharmony_ci remove_component(component->master, component); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci mutex_unlock(&component_mutex); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci WARN_ON(!component); 7778c2ecf20Sopenharmony_ci kfree(component); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(component_del); 780