2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-10-14 16:43:22 +04:00
/*
* Copyright ( C ) 2012 Avionic Design GmbH
* Copyright ( C ) 2012 - 2013 , NVIDIA Corporation
*/
2019-01-23 14:34:05 +03:00
# include <linux/debugfs.h>
2013-10-14 16:43:22 +04:00
# include <linux/host1x.h>
# include <linux/of.h>
2019-01-23 14:34:05 +03:00
# include <linux/seq_file.h>
2013-10-14 16:43:22 +04:00
# include <linux/slab.h>
2016-02-26 12:06:53 +03:00
# include <linux/of_device.h>
2013-10-14 16:43:22 +04:00
2013-11-08 16:20:23 +04:00
# include "bus.h"
2013-10-14 16:43:22 +04:00
# include "dev.h"
static DEFINE_MUTEX ( clients_lock ) ;
static LIST_HEAD ( clients ) ;
static DEFINE_MUTEX ( drivers_lock ) ;
static LIST_HEAD ( drivers ) ;
static DEFINE_MUTEX ( devices_lock ) ;
static LIST_HEAD ( devices ) ;
struct host1x_subdev {
struct host1x_client * client ;
struct device_node * np ;
struct list_head list ;
} ;
/**
* host1x_subdev_add ( ) - add a new subdevice with an associated device node
2017-04-10 13:27:01 +03:00
* @ device : host1x device to add the subdevice to
* @ np : device node
2013-10-14 16:43:22 +04:00
*/
static int host1x_subdev_add ( struct host1x_device * device ,
2017-08-15 16:46:22 +03:00
struct host1x_driver * driver ,
2013-10-14 16:43:22 +04:00
struct device_node * np )
{
struct host1x_subdev * subdev ;
2017-08-15 16:46:22 +03:00
struct device_node * child ;
int err ;
2013-10-14 16:43:22 +04:00
subdev = kzalloc ( sizeof ( * subdev ) , GFP_KERNEL ) ;
if ( ! subdev )
return - ENOMEM ;
INIT_LIST_HEAD ( & subdev - > list ) ;
subdev - > np = of_node_get ( np ) ;
mutex_lock ( & device - > subdevs_lock ) ;
list_add_tail ( & subdev - > list , & device - > subdevs ) ;
mutex_unlock ( & device - > subdevs_lock ) ;
2017-08-15 16:46:22 +03:00
/* recursively add children */
for_each_child_of_node ( np , child ) {
if ( of_match_node ( driver - > subdevs , child ) & &
of_device_is_available ( child ) ) {
err = host1x_subdev_add ( device , driver , child ) ;
if ( err < 0 ) {
/* XXX cleanup? */
of_node_put ( child ) ;
return err ;
}
}
}
2013-10-14 16:43:22 +04:00
return 0 ;
}
/**
* host1x_subdev_del ( ) - remove subdevice
2017-04-10 13:27:01 +03:00
* @ subdev : subdevice to remove
2013-10-14 16:43:22 +04:00
*/
static void host1x_subdev_del ( struct host1x_subdev * subdev )
{
list_del ( & subdev - > list ) ;
of_node_put ( subdev - > np ) ;
kfree ( subdev ) ;
}
/**
* host1x_device_parse_dt ( ) - scan device tree and add matching subdevices
2017-04-10 13:27:01 +03:00
* @ device : host1x logical device
* @ driver : host1x driver
2013-10-14 16:43:22 +04:00
*/
2014-12-18 17:29:14 +03:00
static int host1x_device_parse_dt ( struct host1x_device * device ,
struct host1x_driver * driver )
2013-10-14 16:43:22 +04:00
{
struct device_node * np ;
int err ;
for_each_child_of_node ( device - > dev . parent - > of_node , np ) {
2014-12-18 17:29:14 +03:00
if ( of_match_node ( driver - > subdevs , np ) & &
2013-10-14 16:43:22 +04:00
of_device_is_available ( np ) ) {
2017-08-15 16:46:22 +03:00
err = host1x_subdev_add ( device , driver , np ) ;
2016-01-24 19:32:10 +03:00
if ( err < 0 ) {
of_node_put ( np ) ;
2013-10-14 16:43:22 +04:00
return err ;
2016-01-24 19:32:10 +03:00
}
2013-10-14 16:43:22 +04:00
}
}
return 0 ;
}
static void host1x_subdev_register ( struct host1x_device * device ,
struct host1x_subdev * subdev ,
struct host1x_client * client )
{
int err ;
/*
* Move the subdevice to the list of active ( registered ) subdevices
* and associate it with a client . At the same time , associate the
* client with its parent device .
*/
mutex_lock ( & device - > subdevs_lock ) ;
mutex_lock ( & device - > clients_lock ) ;
list_move_tail ( & client - > list , & device - > clients ) ;
list_move_tail ( & subdev - > list , & device - > active ) ;
client - > parent = & device - > dev ;
subdev - > client = client ;
mutex_unlock ( & device - > clients_lock ) ;
mutex_unlock ( & device - > subdevs_lock ) ;
if ( list_empty ( & device - > subdevs ) ) {
2014-12-18 17:29:14 +03:00
err = device_add ( & device - > dev ) ;
2013-10-14 16:43:22 +04:00
if ( err < 0 )
2014-12-18 17:29:14 +03:00
dev_err ( & device - > dev , " failed to add: %d \n " , err ) ;
2014-11-05 13:43:26 +03:00
else
2014-12-18 17:29:14 +03:00
device - > registered = true ;
2013-10-14 16:43:22 +04:00
}
}
static void __host1x_subdev_unregister ( struct host1x_device * device ,
struct host1x_subdev * subdev )
{
struct host1x_client * client = subdev - > client ;
/*
* If all subdevices have been activated , we ' re about to remove the
* first active subdevice , so unload the driver first .
*/
2014-12-18 17:29:14 +03:00
if ( list_empty ( & device - > subdevs ) ) {
if ( device - > registered ) {
device - > registered = false ;
device_del ( & device - > dev ) ;
}
2013-10-14 16:43:22 +04:00
}
/*
* Move the subdevice back to the list of idle subdevices and remove
* it from list of clients .
*/
mutex_lock ( & device - > clients_lock ) ;
subdev - > client = NULL ;
client - > parent = NULL ;
list_move_tail ( & subdev - > list , & device - > subdevs ) ;
/*
* XXX : Perhaps don ' t do this here , but rather explicitly remove it
* when the device is about to be deleted .
*
* This is somewhat complicated by the fact that this function is
* used to remove the subdevice when a client is unregistered but
* also when the composite device is about to be removed .
*/
list_del_init ( & client - > list ) ;
mutex_unlock ( & device - > clients_lock ) ;
}
static void host1x_subdev_unregister ( struct host1x_device * device ,
struct host1x_subdev * subdev )
{
mutex_lock ( & device - > subdevs_lock ) ;
__host1x_subdev_unregister ( device , subdev ) ;
mutex_unlock ( & device - > subdevs_lock ) ;
}
2017-04-10 13:27:01 +03:00
/**
* host1x_device_init ( ) - initialize a host1x logical device
* @ device : host1x logical device
*
* The driver for the host1x logical device can call this during execution of
* its & host1x_driver . probe implementation to initialize each of its clients .
* The client drivers access the subsystem specific driver data using the
* & host1x_client . parent field and driver data associated with it ( usually by
* calling dev_get_drvdata ( ) ) .
*/
2013-10-14 16:43:22 +04:00
int host1x_device_init ( struct host1x_device * device )
{
struct host1x_client * client ;
int err ;
mutex_lock ( & device - > clients_lock ) ;
list_for_each_entry ( client , & device - > clients , list ) {
if ( client - > ops & & client - > ops - > init ) {
err = client - > ops - > init ( client ) ;
if ( err < 0 ) {
dev_err ( & device - > dev ,
" failed to initialize %s: %d \n " ,
dev_name ( client - > dev ) , err ) ;
2017-11-08 15:08:21 +03:00
goto teardown ;
2013-10-14 16:43:22 +04:00
}
}
}
mutex_unlock ( & device - > clients_lock ) ;
return 0 ;
2017-11-08 15:08:21 +03:00
teardown :
list_for_each_entry_continue_reverse ( client , & device - > clients , list )
if ( client - > ops - > exit )
client - > ops - > exit ( client ) ;
mutex_unlock ( & device - > clients_lock ) ;
return err ;
2013-10-14 16:43:22 +04:00
}
2013-11-08 14:41:42 +04:00
EXPORT_SYMBOL ( host1x_device_init ) ;
2013-10-14 16:43:22 +04:00
2017-04-10 13:27:01 +03:00
/**
* host1x_device_exit ( ) - uninitialize host1x logical device
* @ device : host1x logical device
*
* When the driver for a host1x logical device is unloaded , it can call this
* function to tear down each of its clients . Typically this is done after a
* subsystem - specific data structure is removed and the functionality can no
* longer be used .
*/
2013-10-14 16:43:22 +04:00
int host1x_device_exit ( struct host1x_device * device )
{
struct host1x_client * client ;
int err ;
mutex_lock ( & device - > clients_lock ) ;
list_for_each_entry_reverse ( client , & device - > clients , list ) {
if ( client - > ops & & client - > ops - > exit ) {
err = client - > ops - > exit ( client ) ;
if ( err < 0 ) {
dev_err ( & device - > dev ,
" failed to cleanup %s: %d \n " ,
dev_name ( client - > dev ) , err ) ;
mutex_unlock ( & device - > clients_lock ) ;
return err ;
}
}
}
mutex_unlock ( & device - > clients_lock ) ;
return 0 ;
}
2013-11-08 14:41:42 +04:00
EXPORT_SYMBOL ( host1x_device_exit ) ;
2013-10-14 16:43:22 +04:00
2014-05-22 13:12:17 +04:00
static int host1x_add_client ( struct host1x * host1x ,
struct host1x_client * client )
2013-10-14 16:43:22 +04:00
{
struct host1x_device * device ;
struct host1x_subdev * subdev ;
mutex_lock ( & host1x - > devices_lock ) ;
list_for_each_entry ( device , & host1x - > devices , list ) {
list_for_each_entry ( subdev , & device - > subdevs , list ) {
if ( subdev - > np = = client - > dev - > of_node ) {
host1x_subdev_register ( device , subdev , client ) ;
mutex_unlock ( & host1x - > devices_lock ) ;
return 0 ;
}
}
}
mutex_unlock ( & host1x - > devices_lock ) ;
return - ENODEV ;
}
2014-05-22 13:12:17 +04:00
static int host1x_del_client ( struct host1x * host1x ,
struct host1x_client * client )
2013-10-14 16:43:22 +04:00
{
struct host1x_device * device , * dt ;
struct host1x_subdev * subdev ;
mutex_lock ( & host1x - > devices_lock ) ;
list_for_each_entry_safe ( device , dt , & host1x - > devices , list ) {
list_for_each_entry ( subdev , & device - > active , list ) {
if ( subdev - > client = = client ) {
host1x_subdev_unregister ( device , subdev ) ;
mutex_unlock ( & host1x - > devices_lock ) ;
return 0 ;
}
}
}
mutex_unlock ( & host1x - > devices_lock ) ;
return - ENODEV ;
}
2014-12-18 17:29:14 +03:00
static int host1x_device_match ( struct device * dev , struct device_driver * drv )
{
return strcmp ( dev_name ( dev ) , drv - > name ) = = 0 ;
}
2013-10-14 16:43:22 +04:00
2018-04-28 05:51:58 +03:00
static int host1x_dma_configure ( struct device * dev )
{
2018-05-03 17:25:08 +03:00
return of_dma_configure ( dev , dev - > of_node , true ) ;
2018-04-28 05:51:58 +03:00
}
2014-12-18 17:29:14 +03:00
static const struct dev_pm_ops host1x_device_pm_ops = {
. suspend = pm_generic_suspend ,
. resume = pm_generic_resume ,
. freeze = pm_generic_freeze ,
. thaw = pm_generic_thaw ,
. poweroff = pm_generic_poweroff ,
. restore = pm_generic_restore ,
} ;
struct bus_type host1x_bus_type = {
. name = " host1x " ,
. match = host1x_device_match ,
2018-09-26 17:09:46 +03:00
. dma_configure = host1x_dma_configure ,
2014-12-18 17:29:14 +03:00
. pm = & host1x_device_pm_ops ,
} ;
2014-12-18 17:26:02 +03:00
static void __host1x_device_del ( struct host1x_device * device )
{
struct host1x_subdev * subdev , * sd ;
struct host1x_client * client , * cl ;
mutex_lock ( & device - > subdevs_lock ) ;
/* unregister subdevices */
list_for_each_entry_safe ( subdev , sd , & device - > active , list ) {
/*
* host1x_subdev_unregister ( ) will remove the client from
* any lists , so we ' ll need to manually add it back to the
* list of idle clients .
*
* XXX : Alternatively , perhaps don ' t remove the client from
* any lists in host1x_subdev_unregister ( ) and instead do
* that explicitly from host1x_unregister_client ( ) ?
*/
client = subdev - > client ;
__host1x_subdev_unregister ( device , subdev ) ;
/* add the client to the list of idle clients */
mutex_lock ( & clients_lock ) ;
list_add_tail ( & client - > list , & clients ) ;
mutex_unlock ( & clients_lock ) ;
}
/* remove subdevices */
list_for_each_entry_safe ( subdev , sd , & device - > subdevs , list )
host1x_subdev_del ( subdev ) ;
mutex_unlock ( & device - > subdevs_lock ) ;
/* move clients to idle list */
mutex_lock ( & clients_lock ) ;
mutex_lock ( & device - > clients_lock ) ;
list_for_each_entry_safe ( client , cl , & device - > clients , list )
list_move_tail ( & client - > list , & clients ) ;
mutex_unlock ( & device - > clients_lock ) ;
mutex_unlock ( & clients_lock ) ;
/* finally remove the device */
list_del_init ( & device - > list ) ;
}
2013-10-14 16:43:22 +04:00
static void host1x_device_release ( struct device * dev )
{
struct host1x_device * device = to_host1x_device ( dev ) ;
2014-12-18 17:26:02 +03:00
__host1x_device_del ( device ) ;
2013-10-14 16:43:22 +04:00
kfree ( device ) ;
}
static int host1x_device_add ( struct host1x * host1x ,
struct host1x_driver * driver )
{
struct host1x_client * client , * tmp ;
struct host1x_subdev * subdev ;
struct host1x_device * device ;
int err ;
device = kzalloc ( sizeof ( * device ) , GFP_KERNEL ) ;
if ( ! device )
return - ENOMEM ;
2014-12-18 17:29:14 +03:00
device_initialize ( & device - > dev ) ;
2013-10-14 16:43:22 +04:00
mutex_init ( & device - > subdevs_lock ) ;
INIT_LIST_HEAD ( & device - > subdevs ) ;
INIT_LIST_HEAD ( & device - > active ) ;
mutex_init ( & device - > clients_lock ) ;
INIT_LIST_HEAD ( & device - > clients ) ;
INIT_LIST_HEAD ( & device - > list ) ;
device - > driver = driver ;
device - > dev . coherent_dma_mask = host1x - > dev - > coherent_dma_mask ;
device - > dev . dma_mask = & device - > dev . coherent_dma_mask ;
2014-12-18 17:29:14 +03:00
dev_set_name ( & device - > dev , " %s " , driver - > driver . name ) ;
2013-10-14 16:43:22 +04:00
device - > dev . release = host1x_device_release ;
2017-01-20 17:57:35 +03:00
device - > dev . of_node = host1x - > dev - > of_node ;
2013-10-14 16:43:22 +04:00
device - > dev . bus = & host1x_bus_type ;
device - > dev . parent = host1x - > dev ;
2018-05-03 17:25:08 +03:00
of_dma_configure ( & device - > dev , host1x - > dev - > of_node , true ) ;
2017-09-24 12:04:53 +03:00
2014-12-18 17:29:14 +03:00
err = host1x_device_parse_dt ( device , driver ) ;
2013-10-14 16:43:22 +04:00
if ( err < 0 ) {
2014-12-18 17:29:14 +03:00
kfree ( device ) ;
2013-10-14 16:43:22 +04:00
return err ;
}
list_add_tail ( & device - > list , & host1x - > devices ) ;
mutex_lock ( & clients_lock ) ;
list_for_each_entry_safe ( client , tmp , & clients , list ) {
list_for_each_entry ( subdev , & device - > subdevs , list ) {
if ( subdev - > np = = client - > dev - > of_node ) {
host1x_subdev_register ( device , subdev , client ) ;
break ;
}
}
}
mutex_unlock ( & clients_lock ) ;
return 0 ;
}
/*
* Removes a device by first unregistering any subdevices and then removing
* itself from the list of devices .
*
* This function must be called with the host1x - > devices_lock held .
*/
static void host1x_device_del ( struct host1x * host1x ,
struct host1x_device * device )
{
2014-12-18 17:29:14 +03:00
if ( device - > registered ) {
device - > registered = false ;
device_del ( & device - > dev ) ;
}
put_device ( & device - > dev ) ;
2013-10-14 16:43:22 +04:00
}
static void host1x_attach_driver ( struct host1x * host1x ,
struct host1x_driver * driver )
{
struct host1x_device * device ;
int err ;
mutex_lock ( & host1x - > devices_lock ) ;
list_for_each_entry ( device , & host1x - > devices , list ) {
if ( device - > driver = = driver ) {
mutex_unlock ( & host1x - > devices_lock ) ;
return ;
}
}
err = host1x_device_add ( host1x , driver ) ;
if ( err < 0 )
dev_err ( host1x - > dev , " failed to allocate device: %d \n " , err ) ;
2014-12-18 17:06:56 +03:00
mutex_unlock ( & host1x - > devices_lock ) ;
2013-10-14 16:43:22 +04:00
}
static void host1x_detach_driver ( struct host1x * host1x ,
struct host1x_driver * driver )
{
struct host1x_device * device , * tmp ;
mutex_lock ( & host1x - > devices_lock ) ;
list_for_each_entry_safe ( device , tmp , & host1x - > devices , list )
if ( device - > driver = = driver )
host1x_device_del ( host1x , device ) ;
mutex_unlock ( & host1x - > devices_lock ) ;
}
2019-01-23 14:34:05 +03:00
static int host1x_devices_show ( struct seq_file * s , void * data )
{
struct host1x * host1x = s - > private ;
struct host1x_device * device ;
mutex_lock ( & host1x - > devices_lock ) ;
list_for_each_entry ( device , & host1x - > devices , list ) {
struct host1x_subdev * subdev ;
seq_printf ( s , " %s \n " , dev_name ( & device - > dev ) ) ;
mutex_lock ( & device - > subdevs_lock ) ;
list_for_each_entry ( subdev , & device - > active , list )
seq_printf ( s , " %pOFf: %s \n " , subdev - > np ,
dev_name ( subdev - > client - > dev ) ) ;
list_for_each_entry ( subdev , & device - > subdevs , list )
seq_printf ( s , " %pOFf: \n " , subdev - > np ) ;
mutex_unlock ( & device - > subdevs_lock ) ;
}
mutex_unlock ( & host1x - > devices_lock ) ;
return 0 ;
}
DEFINE_SHOW_ATTRIBUTE ( host1x_devices ) ;
2017-04-10 13:27:01 +03:00
/**
* host1x_register ( ) - register a host1x controller
* @ host1x : host1x controller
*
* The host1x controller driver uses this to register a host1x controller with
* the infrastructure . Note that all Tegra SoC generations have only ever come
* with a single host1x instance , so this function is somewhat academic .
*/
2013-10-14 16:43:22 +04:00
int host1x_register ( struct host1x * host1x )
{
struct host1x_driver * driver ;
mutex_lock ( & devices_lock ) ;
list_add_tail ( & host1x - > list , & devices ) ;
mutex_unlock ( & devices_lock ) ;
mutex_lock ( & drivers_lock ) ;
list_for_each_entry ( driver , & drivers , list )
host1x_attach_driver ( host1x , driver ) ;
mutex_unlock ( & drivers_lock ) ;
2019-01-23 14:34:05 +03:00
debugfs_create_file ( " devices " , S_IRUGO , host1x - > debugfs , host1x ,
& host1x_devices_fops ) ;
2013-10-14 16:43:22 +04:00
return 0 ;
}
2017-04-10 13:27:01 +03:00
/**
* host1x_unregister ( ) - unregister a host1x controller
* @ host1x : host1x controller
*
* The host1x controller driver uses this to remove a host1x controller from
* the infrastructure .
*/
2013-10-14 16:43:22 +04:00
int host1x_unregister ( struct host1x * host1x )
{
struct host1x_driver * driver ;
mutex_lock ( & drivers_lock ) ;
list_for_each_entry ( driver , & drivers , list )
host1x_detach_driver ( host1x , driver ) ;
mutex_unlock ( & drivers_lock ) ;
mutex_lock ( & devices_lock ) ;
list_del_init ( & host1x - > list ) ;
mutex_unlock ( & devices_lock ) ;
return 0 ;
}
2017-03-22 21:15:18 +03:00
static int host1x_device_probe ( struct device * dev )
{
struct host1x_driver * driver = to_host1x_driver ( dev - > driver ) ;
struct host1x_device * device = to_host1x_device ( dev ) ;
if ( driver - > probe )
return driver - > probe ( device ) ;
return 0 ;
}
static int host1x_device_remove ( struct device * dev )
{
struct host1x_driver * driver = to_host1x_driver ( dev - > driver ) ;
struct host1x_device * device = to_host1x_device ( dev ) ;
if ( driver - > remove )
return driver - > remove ( device ) ;
return 0 ;
}
static void host1x_device_shutdown ( struct device * dev )
{
struct host1x_driver * driver = to_host1x_driver ( dev - > driver ) ;
struct host1x_device * device = to_host1x_device ( dev ) ;
if ( driver - > shutdown )
driver - > shutdown ( device ) ;
}
2017-04-10 13:27:01 +03:00
/**
* host1x_driver_register_full ( ) - register a host1x driver
* @ driver : host1x driver
* @ owner : owner module
*
* Drivers for host1x logical devices call this function to register a driver
* with the infrastructure . Note that since these drive logical devices , the
* registration of the driver actually triggers tho logical device creation .
* A logical device will be created for each host1x instance .
*/
2014-12-18 17:29:14 +03:00
int host1x_driver_register_full ( struct host1x_driver * driver ,
struct module * owner )
2013-10-14 16:43:22 +04:00
{
struct host1x * host1x ;
INIT_LIST_HEAD ( & driver - > list ) ;
mutex_lock ( & drivers_lock ) ;
list_add_tail ( & driver - > list , & drivers ) ;
mutex_unlock ( & drivers_lock ) ;
mutex_lock ( & devices_lock ) ;
list_for_each_entry ( host1x , & devices , list )
host1x_attach_driver ( host1x , driver ) ;
mutex_unlock ( & devices_lock ) ;
2014-12-18 17:29:14 +03:00
driver - > driver . bus = & host1x_bus_type ;
driver - > driver . owner = owner ;
2017-03-22 21:15:18 +03:00
driver - > driver . probe = host1x_device_probe ;
driver - > driver . remove = host1x_device_remove ;
driver - > driver . shutdown = host1x_device_shutdown ;
2014-12-18 17:29:14 +03:00
return driver_register ( & driver - > driver ) ;
2013-10-14 16:43:22 +04:00
}
2014-12-18 17:29:14 +03:00
EXPORT_SYMBOL ( host1x_driver_register_full ) ;
2013-10-14 16:43:22 +04:00
2017-04-10 13:27:01 +03:00
/**
* host1x_driver_unregister ( ) - unregister a host1x driver
* @ driver : host1x driver
*
* Unbinds the driver from each of the host1x logical devices that it is
* bound to , effectively removing the subsystem devices that they represent .
*/
2013-10-14 16:43:22 +04:00
void host1x_driver_unregister ( struct host1x_driver * driver )
{
2015-08-24 15:51:04 +03:00
driver_unregister ( & driver - > driver ) ;
2013-10-14 16:43:22 +04:00
mutex_lock ( & drivers_lock ) ;
list_del_init ( & driver - > list ) ;
mutex_unlock ( & drivers_lock ) ;
}
EXPORT_SYMBOL ( host1x_driver_unregister ) ;
2017-04-10 13:27:01 +03:00
/**
* host1x_client_register ( ) - register a host1x client
* @ client : host1x client
*
* Registers a host1x client with each host1x controller instance . Note that
* each client will only match their parent host1x controller and will only be
* associated with that instance . Once all clients have been registered with
* their parent host1x controller , the infrastructure will set up the logical
* device and call host1x_device_init ( ) , which will in turn call each client ' s
* & host1x_client_ops . init implementation .
*/
2013-10-14 16:43:22 +04:00
int host1x_client_register ( struct host1x_client * client )
{
struct host1x * host1x ;
int err ;
mutex_lock ( & devices_lock ) ;
list_for_each_entry ( host1x , & devices , list ) {
2014-05-22 13:12:17 +04:00
err = host1x_add_client ( host1x , client ) ;
2013-10-14 16:43:22 +04:00
if ( ! err ) {
mutex_unlock ( & devices_lock ) ;
return 0 ;
}
}
mutex_unlock ( & devices_lock ) ;
mutex_lock ( & clients_lock ) ;
list_add_tail ( & client - > list , & clients ) ;
mutex_unlock ( & clients_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( host1x_client_register ) ;
2017-04-10 13:27:01 +03:00
/**
* host1x_client_unregister ( ) - unregister a host1x client
* @ client : host1x client
*
* Removes a host1x client from its host1x controller instance . If a logical
* device has already been initialized , it will be torn down .
*/
2013-10-14 16:43:22 +04:00
int host1x_client_unregister ( struct host1x_client * client )
{
struct host1x_client * c ;
struct host1x * host1x ;
int err ;
mutex_lock ( & devices_lock ) ;
list_for_each_entry ( host1x , & devices , list ) {
2014-05-22 13:12:17 +04:00
err = host1x_del_client ( host1x , client ) ;
2013-10-14 16:43:22 +04:00
if ( ! err ) {
mutex_unlock ( & devices_lock ) ;
return 0 ;
}
}
mutex_unlock ( & devices_lock ) ;
mutex_lock ( & clients_lock ) ;
list_for_each_entry ( c , & clients , list ) {
if ( c = = client ) {
list_del_init ( & c - > list ) ;
break ;
}
}
mutex_unlock ( & clients_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( host1x_client_unregister ) ;