From: Thierry Reding <tred...@nvidia.com> Convert to the new generic object registry and introduce proper object and module reference counting. This should make panel registration and removal a lot more robust.
Signed-off-by: Thierry Reding <tred...@nvidia.com> --- drivers/gpu/drm/drm_panel.c | 100 ++++++++++++++++++++++++++++++++++---------- include/drm/drm_panel.h | 13 ++++-- 2 files changed, 86 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 3dfe3c886502..c1b58bd421a3 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -23,13 +23,11 @@ #include <linux/err.h> #include <linux/module.h> +#include <linux/registry.h> #include <drm/drm_crtc.h> #include <drm/drm_panel.h> -static DEFINE_MUTEX(panel_lock); -static LIST_HEAD(panel_list); - /** * DOC: drm panel * @@ -38,6 +36,33 @@ static LIST_HEAD(panel_list); * drivers. */ +/* + * DRM panel registry + */ +static struct registry panels = { + .lock = __MUTEX_INITIALIZER(panels.lock), + .list = LIST_HEAD_INIT(panels.list), + .owner = THIS_MODULE, +}; + +static inline struct drm_panel *to_drm_panel(struct registry_record *record) +{ + return container_of(record, struct drm_panel, record); +} + +static void drm_panel_release(struct registry_record *record) +{ + struct drm_panel *panel = to_drm_panel(record); + + /* + * The .release() callback is optional because drivers may not need + * to manually release any resources (e.g. if they've used devm_*() + * helper functions). + */ + if (panel->funcs && panel->funcs->release) + panel->funcs->release(panel); +} + /** * drm_panel_init - initialize a panel * @panel: DRM panel @@ -47,11 +72,48 @@ static LIST_HEAD(panel_list); */ void drm_panel_init(struct drm_panel *panel) { - INIT_LIST_HEAD(&panel->list); + registry_record_init(&panel->record); + panel->record.release = drm_panel_release; } EXPORT_SYMBOL(drm_panel_init); /** + * drm_panel_ref - acquire a reference to a panel + * @panel: DRM panel + * + * Increases the reference on a panel and returns a pointer to it. + * + * Return: A reference to the panel on success or NULL on failure. + */ +struct drm_panel *drm_panel_ref(struct drm_panel *panel) +{ + if (panel) { + struct registry_record *record; + + record = registry_record_ref(&panel->record); + if (!record) + panel = NULL; + } + + return panel; +} +EXPORT_SYMBOL(drm_panel_ref); + +/** + * drm_panel_unref - release a reference to a panel + * @panel: DRM panel + * + * Decreases the reference count on a panel. If the reference count reaches 0 + * the panel is destroyed. + */ +void drm_panel_unref(struct drm_panel *panel) +{ + if (panel) + registry_record_unref(&panel->record); +} +EXPORT_SYMBOL(drm_panel_unref); + +/** * drm_panel_add - add a panel to the global registry * @panel: panel to add * @@ -62,11 +124,10 @@ EXPORT_SYMBOL(drm_panel_init); */ int drm_panel_add(struct drm_panel *panel) { - mutex_lock(&panel_lock); - list_add_tail(&panel->list, &panel_list); - mutex_unlock(&panel_lock); + panel->record.owner = panel->dev->driver->owner; + panel->record.dev = panel->dev; - return 0; + return registry_add(&panels, &panel->record); } EXPORT_SYMBOL(drm_panel_add); @@ -74,13 +135,12 @@ EXPORT_SYMBOL(drm_panel_add); * drm_panel_remove - remove a panel from the global registry * @panel: DRM panel * - * Removes a panel from the global registry. + * Removes a panel from the global registry. References to the object can + * still exist, but drivers won't be able to look the panel up again. */ void drm_panel_remove(struct drm_panel *panel) { - mutex_lock(&panel_lock); - list_del_init(&panel->list); - mutex_unlock(&panel_lock); + registry_remove(&panels, &panel->record); } EXPORT_SYMBOL(drm_panel_remove); @@ -132,25 +192,19 @@ EXPORT_SYMBOL(drm_panel_detach); * @np: device tree node of the panel * * Searches the set of registered panels for one that matches the given device - * tree node. If a matching panel is found, return a pointer to it. + * tree node. If a matching panel is found, return a reference to it. * * Return: A pointer to the panel registered for the specified device tree * node or NULL if no panel matching the device tree node can be found. */ struct drm_panel *of_drm_find_panel(struct device_node *np) { - struct drm_panel *panel; - - mutex_lock(&panel_lock); + struct registry_record *record; - list_for_each_entry(panel, &panel_list, list) { - if (panel->dev->of_node == np) { - mutex_unlock(&panel_lock); - return panel; - } - } + record = registry_find_by_of_node(&panels, np); + if (record) + return container_of(record, struct drm_panel, record); - mutex_unlock(&panel_lock); return NULL; } EXPORT_SYMBOL(of_drm_find_panel); diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index b88b4dcd698b..117e221dbc08 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -24,7 +24,8 @@ #ifndef __DRM_PANEL_H__ #define __DRM_PANEL_H__ -#include <linux/list.h> +#include <linux/module.h> +#include <linux/registry.h> struct drm_connector; struct drm_device; @@ -32,6 +33,7 @@ struct drm_panel; /** * struct drm_panel_funcs - perform operations on a given panel + * @release: called when the final reference is dropped * @disable: disable panel (turn off back light, etc.) * @unprepare: turn off panel * @prepare: turn on panel and perform set up @@ -63,6 +65,7 @@ struct drm_panel; * the panel. This is the job of the .unprepare() function. */ struct drm_panel_funcs { + void (*release)(struct drm_panel *panel); int (*disable)(struct drm_panel *panel); int (*unprepare)(struct drm_panel *panel); int (*prepare)(struct drm_panel *panel); @@ -72,20 +75,20 @@ struct drm_panel_funcs { /** * struct drm_panel - DRM panel object + * @record: registry record * @drm: DRM device owning the panel * @connector: DRM connector that the panel is attached to * @dev: parent device of the panel * @funcs: operations that can be performed on the panel - * @list: panel entry in registry */ struct drm_panel { + struct registry_record record; + struct drm_device *drm; struct drm_connector *connector; struct device *dev; const struct drm_panel_funcs *funcs; - - struct list_head list; }; /** @@ -180,6 +183,8 @@ static inline int drm_panel_get_modes(struct drm_panel *panel) } void drm_panel_init(struct drm_panel *panel); +struct drm_panel *drm_panel_ref(struct drm_panel *panel); +void drm_panel_unref(struct drm_panel *panel); int drm_panel_add(struct drm_panel *panel); void drm_panel_remove(struct drm_panel *panel); -- 2.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/