162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Tegra host1x Channel 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010-2013, NVIDIA Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "channel.h" 1262306a36Sopenharmony_ci#include "dev.h" 1362306a36Sopenharmony_ci#include "job.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Constructor for the host1x device list */ 1662306a36Sopenharmony_ciint host1x_channel_list_init(struct host1x_channel_list *chlist, 1762306a36Sopenharmony_ci unsigned int num_channels) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci chlist->channels = kcalloc(num_channels, sizeof(struct host1x_channel), 2062306a36Sopenharmony_ci GFP_KERNEL); 2162306a36Sopenharmony_ci if (!chlist->channels) 2262306a36Sopenharmony_ci return -ENOMEM; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci chlist->allocated_channels = bitmap_zalloc(num_channels, GFP_KERNEL); 2562306a36Sopenharmony_ci if (!chlist->allocated_channels) { 2662306a36Sopenharmony_ci kfree(chlist->channels); 2762306a36Sopenharmony_ci return -ENOMEM; 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid host1x_channel_list_free(struct host1x_channel_list *chlist) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci bitmap_free(chlist->allocated_channels); 3662306a36Sopenharmony_ci kfree(chlist->channels); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint host1x_job_submit(struct host1x_job *job) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct host1x *host = dev_get_drvdata(job->channel->dev->parent); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return host1x_hw_channel_submit(host, job); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_job_submit); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct host1x_channel *host1x_channel_get(struct host1x_channel *channel) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci kref_get(&channel->refcount); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return channel; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_channel_get); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * host1x_channel_get_index() - Attempt to get channel reference by index 5762306a36Sopenharmony_ci * @host: Host1x device object 5862306a36Sopenharmony_ci * @index: Index of channel 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * If channel number @index is currently allocated, increase its refcount 6162306a36Sopenharmony_ci * and return a pointer to it. Otherwise, return NULL. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct host1x_channel *host1x_channel_get_index(struct host1x *host, 6462306a36Sopenharmony_ci unsigned int index) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct host1x_channel *ch = &host->channel_list.channels[index]; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!kref_get_unless_zero(&ch->refcount)) 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return ch; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_civoid host1x_channel_stop(struct host1x_channel *channel) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct host1x *host = dev_get_drvdata(channel->dev->parent); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci host1x_hw_cdma_stop(host, &channel->cdma); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_channel_stop); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void release_channel(struct kref *kref) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct host1x_channel *channel = 8562306a36Sopenharmony_ci container_of(kref, struct host1x_channel, refcount); 8662306a36Sopenharmony_ci struct host1x *host = dev_get_drvdata(channel->dev->parent); 8762306a36Sopenharmony_ci struct host1x_channel_list *chlist = &host->channel_list; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci host1x_hw_cdma_stop(host, &channel->cdma); 9062306a36Sopenharmony_ci host1x_cdma_deinit(&channel->cdma); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci clear_bit(channel->id, chlist->allocated_channels); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_civoid host1x_channel_put(struct host1x_channel *channel) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci kref_put(&channel->refcount, release_channel); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_channel_put); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct host1x_channel *acquire_unused_channel(struct host1x *host) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct host1x_channel_list *chlist = &host->channel_list; 10462306a36Sopenharmony_ci unsigned int max_channels = host->info->nb_channels; 10562306a36Sopenharmony_ci unsigned int index; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci index = find_first_zero_bit(chlist->allocated_channels, max_channels); 10862306a36Sopenharmony_ci if (index >= max_channels) { 10962306a36Sopenharmony_ci dev_err(host->dev, "failed to find free channel\n"); 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci chlist->channels[index].id = index; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci set_bit(index, chlist->allocated_channels); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return &chlist->channels[index]; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * host1x_channel_request() - Allocate a channel 12262306a36Sopenharmony_ci * @client: Host1x client this channel will be used to send commands to 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Allocates a new host1x channel for @client. May return NULL if CDMA 12562306a36Sopenharmony_ci * initialization fails. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistruct host1x_channel *host1x_channel_request(struct host1x_client *client) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct host1x *host = dev_get_drvdata(client->dev->parent); 13062306a36Sopenharmony_ci struct host1x_channel_list *chlist = &host->channel_list; 13162306a36Sopenharmony_ci struct host1x_channel *channel; 13262306a36Sopenharmony_ci int err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci channel = acquire_unused_channel(host); 13562306a36Sopenharmony_ci if (!channel) 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci kref_init(&channel->refcount); 13962306a36Sopenharmony_ci mutex_init(&channel->submitlock); 14062306a36Sopenharmony_ci channel->client = client; 14162306a36Sopenharmony_ci channel->dev = client->dev; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci err = host1x_hw_channel_init(host, channel, channel->id); 14462306a36Sopenharmony_ci if (err < 0) 14562306a36Sopenharmony_ci goto fail; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci err = host1x_cdma_init(&channel->cdma); 14862306a36Sopenharmony_ci if (err < 0) 14962306a36Sopenharmony_ci goto fail; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return channel; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cifail: 15462306a36Sopenharmony_ci clear_bit(channel->id, chlist->allocated_channels); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev_err(client->dev, "failed to initialize channel\n"); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return NULL; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_channel_request); 161