2013-03-22 16:34:03 +02:00
/*
* Tegra host1x Channel
*
* Copyright ( c ) 2010 - 2013 , NVIDIA Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/slab.h>
# include <linux/module.h>
# include "channel.h"
# include "dev.h"
# include "job.h"
/* Constructor for the host1x device list */
2017-06-15 02:18:42 +03:00
int host1x_channel_list_init ( struct host1x_channel_list * chlist ,
unsigned int num_channels )
2013-03-22 16:34:03 +02:00
{
2017-06-15 02:18:42 +03:00
chlist - > channels = kcalloc ( num_channels , sizeof ( struct host1x_channel ) ,
GFP_KERNEL ) ;
if ( ! chlist - > channels )
return - ENOMEM ;
chlist - > allocated_channels =
kcalloc ( BITS_TO_LONGS ( num_channels ) , sizeof ( unsigned long ) ,
GFP_KERNEL ) ;
if ( ! chlist - > allocated_channels ) {
kfree ( chlist - > channels ) ;
return - ENOMEM ;
2013-03-22 16:34:03 +02:00
}
2017-06-15 02:18:42 +03:00
bitmap_zero ( chlist - > allocated_channels , num_channels ) ;
2013-03-22 16:34:03 +02:00
return 0 ;
}
2017-06-15 02:18:42 +03:00
void host1x_channel_list_free ( struct host1x_channel_list * chlist )
{
kfree ( chlist - > allocated_channels ) ;
kfree ( chlist - > channels ) ;
}
2013-03-22 16:34:03 +02:00
int host1x_job_submit ( struct host1x_job * job )
{
struct host1x * host = dev_get_drvdata ( job - > channel - > dev - > parent ) ;
return host1x_hw_channel_submit ( host , job ) ;
}
2013-11-08 11:41:42 +01:00
EXPORT_SYMBOL ( host1x_job_submit ) ;
2013-03-22 16:34:03 +02:00
struct host1x_channel * host1x_channel_get ( struct host1x_channel * channel )
{
2017-06-15 02:18:42 +03:00
kref_get ( & channel - > refcount ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
return channel ;
}
EXPORT_SYMBOL ( host1x_channel_get ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
/**
* host1x_channel_get_index ( ) - Attempt to get channel reference by index
* @ host : Host1x device object
* @ index : Index of channel
*
* If channel number @ index is currently allocated , increase its refcount
* and return a pointer to it . Otherwise , return NULL .
*/
struct host1x_channel * host1x_channel_get_index ( struct host1x * host ,
unsigned int index )
{
struct host1x_channel * ch = & host - > channel_list . channels [ index ] ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
if ( ! kref_get_unless_zero ( & ch - > refcount ) )
return NULL ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
return ch ;
}
static void release_channel ( struct kref * kref )
{
struct host1x_channel * channel =
container_of ( kref , struct host1x_channel , refcount ) ;
struct host1x * host = dev_get_drvdata ( channel - > dev - > parent ) ;
struct host1x_channel_list * chlist = & host - > channel_list ;
host1x_hw_cdma_stop ( host , & channel - > cdma ) ;
host1x_cdma_deinit ( & channel - > cdma ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
clear_bit ( channel - > id , chlist - > allocated_channels ) ;
2013-03-22 16:34:03 +02:00
}
void host1x_channel_put ( struct host1x_channel * channel )
{
2017-06-15 02:18:42 +03:00
kref_put ( & channel - > refcount , release_channel ) ;
}
EXPORT_SYMBOL ( host1x_channel_put ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
static struct host1x_channel * acquire_unused_channel ( struct host1x * host )
{
struct host1x_channel_list * chlist = & host - > channel_list ;
unsigned int max_channels = host - > info - > nb_channels ;
unsigned int index ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
index = find_first_zero_bit ( chlist - > allocated_channels , max_channels ) ;
if ( index > = max_channels ) {
dev_err ( host - > dev , " failed to find free channel \n " ) ;
return NULL ;
2013-03-22 16:34:03 +02:00
}
2017-06-15 02:18:42 +03:00
chlist - > channels [ index ] . id = index ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
set_bit ( index , chlist - > allocated_channels ) ;
return & chlist - > channels [ index ] ;
2013-03-22 16:34:03 +02:00
}
2017-06-15 02:18:42 +03:00
/**
* host1x_channel_request ( ) - Allocate a channel
* @ device : Host1x unit this channel will be used to send commands to
*
2017-09-28 15:50:43 +03:00
* Allocates a new host1x channel for @ device . May return NULL if CDMA
2017-06-15 02:18:42 +03:00
* initialization fails .
*/
2013-03-22 16:34:03 +02:00
struct host1x_channel * host1x_channel_request ( struct device * dev )
{
struct host1x * host = dev_get_drvdata ( dev - > parent ) ;
2017-06-15 02:18:42 +03:00
struct host1x_channel_list * chlist = & host - > channel_list ;
struct host1x_channel * channel ;
2016-06-22 16:47:30 +02:00
int err ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
channel = acquire_unused_channel ( host ) ;
if ( ! channel )
return NULL ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
kref_init ( & channel - > refcount ) ;
mutex_init ( & channel - > submitlock ) ;
channel - > dev = dev ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
err = host1x_hw_channel_init ( host , channel , channel - > id ) ;
if ( err < 0 )
2013-03-22 16:34:03 +02:00
goto fail ;
2017-06-15 02:18:42 +03:00
err = host1x_cdma_init ( & channel - > cdma ) ;
2013-03-22 16:34:03 +02:00
if ( err < 0 )
goto fail ;
return channel ;
fail :
2017-06-15 02:18:42 +03:00
clear_bit ( channel - > id , chlist - > allocated_channels ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
dev_err ( dev , " failed to initialize channel \n " ) ;
2013-03-22 16:34:03 +02:00
2017-06-15 02:18:42 +03:00
return NULL ;
2013-03-22 16:34:03 +02:00
}
2017-06-15 02:18:42 +03:00
EXPORT_SYMBOL ( host1x_channel_request ) ;