mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 10:11:26 +03:00
Add the upstream host1x driver with the 'Host1x/Tegra UAPI' series [0] applied. This driver will be built as an external module for use with the NVGPU driver on upstream Linux kernels. The following modifications have been made to the series posted upstream 1. Update the Makefile to always build the driver as a module 2. Remove the tests to see if CONFIG_DRM_TEGRA_STAGING is enabled 3. Rename the include/linux/host1x.h to include/linux/host1x-next.h to avoid conflicts with upstream headers when building as an external module. 4. Rename the include/uapi/linux/host1x.h to include/uapi/linux/host1x-next.h to avoid conflicts with upstream headers when building as an external module. 5. Rename the module that is built to be host1x-next.ko instead of host1x.ko to avoid any depmod conflicts with the upstream driver. [0] https://patchwork.ozlabs.org/project/linux-tegra/list/?series=215770 Bug 3156385 Change-Id: Ic60299546809097dd0e4a9a7157bce1491d9f794 Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2435801 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: automaticguardword <automaticguardword@nvidia.com> Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit
157 lines
3.8 KiB
C
157 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* 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 */
|
|
int host1x_channel_list_init(struct host1x_channel_list *chlist,
|
|
unsigned int num_channels)
|
|
{
|
|
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;
|
|
}
|
|
|
|
bitmap_zero(chlist->allocated_channels, num_channels);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void host1x_channel_list_free(struct host1x_channel_list *chlist)
|
|
{
|
|
kfree(chlist->allocated_channels);
|
|
kfree(chlist->channels);
|
|
}
|
|
|
|
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);
|
|
}
|
|
EXPORT_SYMBOL(host1x_job_submit);
|
|
|
|
struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
|
|
{
|
|
kref_get(&channel->refcount);
|
|
|
|
return channel;
|
|
}
|
|
EXPORT_SYMBOL(host1x_channel_get);
|
|
|
|
/**
|
|
* 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];
|
|
|
|
if (!kref_get_unless_zero(&ch->refcount))
|
|
return NULL;
|
|
|
|
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);
|
|
|
|
clear_bit(channel->id, chlist->allocated_channels);
|
|
}
|
|
|
|
void host1x_channel_put(struct host1x_channel *channel)
|
|
{
|
|
kref_put(&channel->refcount, release_channel);
|
|
}
|
|
EXPORT_SYMBOL(host1x_channel_put);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
chlist->channels[index].id = index;
|
|
|
|
set_bit(index, chlist->allocated_channels);
|
|
|
|
return &chlist->channels[index];
|
|
}
|
|
|
|
/**
|
|
* host1x_channel_request() - Allocate a channel
|
|
* @client: Host1x client this channel will be used to send commands to
|
|
*
|
|
* Allocates a new host1x channel for @client. May return NULL if CDMA
|
|
* initialization fails.
|
|
*/
|
|
struct host1x_channel *host1x_channel_request(struct host1x_client *client)
|
|
{
|
|
struct host1x *host = dev_get_drvdata(client->dev->parent);
|
|
struct host1x_channel_list *chlist = &host->channel_list;
|
|
struct host1x_channel *channel;
|
|
int err;
|
|
|
|
channel = acquire_unused_channel(host);
|
|
if (!channel)
|
|
return NULL;
|
|
|
|
kref_init(&channel->refcount);
|
|
mutex_init(&channel->submitlock);
|
|
channel->client = client;
|
|
channel->dev = client->dev;
|
|
|
|
err = host1x_hw_channel_init(host, channel, channel->id);
|
|
if (err < 0)
|
|
goto fail;
|
|
|
|
err = host1x_cdma_init(&channel->cdma);
|
|
if (err < 0)
|
|
goto fail;
|
|
|
|
return channel;
|
|
|
|
fail:
|
|
clear_bit(channel->id, chlist->allocated_channels);
|
|
|
|
dev_err(client->dev, "failed to initialize channel\n");
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(host1x_channel_request);
|