2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-22 18:34:03 +04:00
/*
* Tegra host1x Channel
*
* Copyright ( c ) 2010 - 2013 , NVIDIA Corporation .
*/
# 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 18:34:03 +04: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 ;
2022-07-04 23:31:51 +03:00
chlist - > allocated_channels = bitmap_zalloc ( num_channels , GFP_KERNEL ) ;
2017-06-15 02:18:42 +03:00
if ( ! chlist - > allocated_channels ) {
kfree ( chlist - > channels ) ;
return - ENOMEM ;
2013-03-22 18:34:03 +04:00
}
return 0 ;
}
2017-06-15 02:18:42 +03:00
void host1x_channel_list_free ( struct host1x_channel_list * chlist )
{
2022-07-04 23:31:51 +03:00
bitmap_free ( chlist - > allocated_channels ) ;
2017-06-15 02:18:42 +03:00
kfree ( chlist - > channels ) ;
}
2013-03-22 18:34:03 +04: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 14:41:42 +04:00
EXPORT_SYMBOL ( host1x_job_submit ) ;
2013-03-22 18:34:03 +04: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 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
return channel ;
}
EXPORT_SYMBOL ( host1x_channel_get ) ;
2013-03-22 18:34:03 +04: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 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
if ( ! kref_get_unless_zero ( & ch - > refcount ) )
return NULL ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
return ch ;
}
2021-12-01 02:23:16 +03:00
void host1x_channel_stop ( struct host1x_channel * channel )
{
struct host1x * host = dev_get_drvdata ( channel - > dev - > parent ) ;
host1x_hw_cdma_stop ( host , & channel - > cdma ) ;
}
EXPORT_SYMBOL ( host1x_channel_stop ) ;
2017-06-15 02:18:42 +03:00
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 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
clear_bit ( channel - > id , chlist - > allocated_channels ) ;
2013-03-22 18:34:03 +04: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 18:34:03 +04: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 18:34:03 +04: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 18:34:03 +04:00
}
2017-06-15 02:18:42 +03:00
chlist - > channels [ index ] . id = index ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
set_bit ( index , chlist - > allocated_channels ) ;
return & chlist - > channels [ index ] ;
2013-03-22 18:34:03 +04:00
}
2017-06-15 02:18:42 +03:00
/**
* host1x_channel_request ( ) - Allocate a channel
2018-06-18 15:01:51 +03:00
* @ client : Host1x client this channel will be used to send commands to
2017-06-15 02:18:42 +03:00
*
2018-06-18 15:01:51 +03:00
* Allocates a new host1x channel for @ client . May return NULL if CDMA
2017-06-15 02:18:42 +03:00
* initialization fails .
*/
2018-06-18 15:01:51 +03:00
struct host1x_channel * host1x_channel_request ( struct host1x_client * client )
2013-03-22 18:34:03 +04:00
{
2018-06-18 15:01:51 +03:00
struct host1x * host = dev_get_drvdata ( client - > 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 17:47:30 +03:00
int err ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
channel = acquire_unused_channel ( host ) ;
if ( ! channel )
return NULL ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
kref_init ( & channel - > refcount ) ;
mutex_init ( & channel - > submitlock ) ;
2018-06-18 15:01:51 +03:00
channel - > client = client ;
channel - > dev = client - > dev ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
err = host1x_hw_channel_init ( host , channel , channel - > id ) ;
if ( err < 0 )
2013-03-22 18:34:03 +04:00
goto fail ;
2017-06-15 02:18:42 +03:00
err = host1x_cdma_init ( & channel - > cdma ) ;
2013-03-22 18:34:03 +04: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 18:34:03 +04:00
2018-06-18 15:01:51 +03:00
dev_err ( client - > dev , " failed to initialize channel \n " ) ;
2013-03-22 18:34:03 +04:00
2017-06-15 02:18:42 +03:00
return NULL ;
2013-03-22 18:34:03 +04:00
}
2017-06-15 02:18:42 +03:00
EXPORT_SYMBOL ( host1x_channel_request ) ;