xref: /openbmc/linux/drivers/gpu/host1x/channel.c (revision caccddcf)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26579324aSTerje Bergstrom /*
36579324aSTerje Bergstrom  * Tegra host1x Channel
46579324aSTerje Bergstrom  *
56579324aSTerje Bergstrom  * Copyright (c) 2010-2013, NVIDIA Corporation.
66579324aSTerje Bergstrom  */
76579324aSTerje Bergstrom 
86579324aSTerje Bergstrom #include <linux/slab.h>
96579324aSTerje Bergstrom #include <linux/module.h>
106579324aSTerje Bergstrom 
116579324aSTerje Bergstrom #include "channel.h"
126579324aSTerje Bergstrom #include "dev.h"
136579324aSTerje Bergstrom #include "job.h"
146579324aSTerje Bergstrom 
156579324aSTerje Bergstrom /* Constructor for the host1x device list */
168474b025SMikko Perttunen int host1x_channel_list_init(struct host1x_channel_list *chlist,
178474b025SMikko Perttunen 			     unsigned int num_channels)
186579324aSTerje Bergstrom {
198474b025SMikko Perttunen 	chlist->channels = kcalloc(num_channels, sizeof(struct host1x_channel),
208474b025SMikko Perttunen 				   GFP_KERNEL);
218474b025SMikko Perttunen 	if (!chlist->channels)
228474b025SMikko Perttunen 		return -ENOMEM;
236579324aSTerje Bergstrom 
248474b025SMikko Perttunen 	chlist->allocated_channels =
258474b025SMikko Perttunen 		kcalloc(BITS_TO_LONGS(num_channels), sizeof(unsigned long),
268474b025SMikko Perttunen 			GFP_KERNEL);
278474b025SMikko Perttunen 	if (!chlist->allocated_channels) {
288474b025SMikko Perttunen 		kfree(chlist->channels);
298474b025SMikko Perttunen 		return -ENOMEM;
306579324aSTerje Bergstrom 	}
316579324aSTerje Bergstrom 
328474b025SMikko Perttunen 	bitmap_zero(chlist->allocated_channels, num_channels);
338474b025SMikko Perttunen 
346579324aSTerje Bergstrom 	return 0;
356579324aSTerje Bergstrom }
366579324aSTerje Bergstrom 
378474b025SMikko Perttunen void host1x_channel_list_free(struct host1x_channel_list *chlist)
388474b025SMikko Perttunen {
398474b025SMikko Perttunen 	kfree(chlist->allocated_channels);
408474b025SMikko Perttunen 	kfree(chlist->channels);
418474b025SMikko Perttunen }
428474b025SMikko Perttunen 
436579324aSTerje Bergstrom int host1x_job_submit(struct host1x_job *job)
446579324aSTerje Bergstrom {
456579324aSTerje Bergstrom 	struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
466579324aSTerje Bergstrom 
476579324aSTerje Bergstrom 	return host1x_hw_channel_submit(host, job);
486579324aSTerje Bergstrom }
49fae798a1SThierry Reding EXPORT_SYMBOL(host1x_job_submit);
506579324aSTerje Bergstrom 
516579324aSTerje Bergstrom struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
526579324aSTerje Bergstrom {
538474b025SMikko Perttunen 	kref_get(&channel->refcount);
546579324aSTerje Bergstrom 
558474b025SMikko Perttunen 	return channel;
566579324aSTerje Bergstrom }
57fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_get);
586579324aSTerje Bergstrom 
598474b025SMikko Perttunen /**
608474b025SMikko Perttunen  * host1x_channel_get_index() - Attempt to get channel reference by index
618474b025SMikko Perttunen  * @host: Host1x device object
628474b025SMikko Perttunen  * @index: Index of channel
638474b025SMikko Perttunen  *
648474b025SMikko Perttunen  * If channel number @index is currently allocated, increase its refcount
658474b025SMikko Perttunen  * and return a pointer to it. Otherwise, return NULL.
668474b025SMikko Perttunen  */
678474b025SMikko Perttunen struct host1x_channel *host1x_channel_get_index(struct host1x *host,
688474b025SMikko Perttunen 						unsigned int index)
696579324aSTerje Bergstrom {
708474b025SMikko Perttunen 	struct host1x_channel *ch = &host->channel_list.channels[index];
716579324aSTerje Bergstrom 
728474b025SMikko Perttunen 	if (!kref_get_unless_zero(&ch->refcount))
738474b025SMikko Perttunen 		return NULL;
748474b025SMikko Perttunen 
758474b025SMikko Perttunen 	return ch;
768474b025SMikko Perttunen }
778474b025SMikko Perttunen 
788474b025SMikko Perttunen static void release_channel(struct kref *kref)
798474b025SMikko Perttunen {
808474b025SMikko Perttunen 	struct host1x_channel *channel =
818474b025SMikko Perttunen 		container_of(kref, struct host1x_channel, refcount);
826579324aSTerje Bergstrom 	struct host1x *host = dev_get_drvdata(channel->dev->parent);
838474b025SMikko Perttunen 	struct host1x_channel_list *chlist = &host->channel_list;
846579324aSTerje Bergstrom 
856579324aSTerje Bergstrom 	host1x_hw_cdma_stop(host, &channel->cdma);
866579324aSTerje Bergstrom 	host1x_cdma_deinit(&channel->cdma);
878474b025SMikko Perttunen 
888474b025SMikko Perttunen 	clear_bit(channel->id, chlist->allocated_channels);
896579324aSTerje Bergstrom }
906579324aSTerje Bergstrom 
918474b025SMikko Perttunen void host1x_channel_put(struct host1x_channel *channel)
928474b025SMikko Perttunen {
938474b025SMikko Perttunen 	kref_put(&channel->refcount, release_channel);
946579324aSTerje Bergstrom }
95fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_put);
966579324aSTerje Bergstrom 
978474b025SMikko Perttunen static struct host1x_channel *acquire_unused_channel(struct host1x *host)
988474b025SMikko Perttunen {
998474b025SMikko Perttunen 	struct host1x_channel_list *chlist = &host->channel_list;
1008474b025SMikko Perttunen 	unsigned int max_channels = host->info->nb_channels;
1018474b025SMikko Perttunen 	unsigned int index;
1028474b025SMikko Perttunen 
1038474b025SMikko Perttunen 	index = find_first_zero_bit(chlist->allocated_channels, max_channels);
1048474b025SMikko Perttunen 	if (index >= max_channels) {
1058474b025SMikko Perttunen 		dev_err(host->dev, "failed to find free channel\n");
1068474b025SMikko Perttunen 		return NULL;
1078474b025SMikko Perttunen 	}
1088474b025SMikko Perttunen 
1098474b025SMikko Perttunen 	chlist->channels[index].id = index;
1108474b025SMikko Perttunen 
1118474b025SMikko Perttunen 	set_bit(index, chlist->allocated_channels);
1128474b025SMikko Perttunen 
1138474b025SMikko Perttunen 	return &chlist->channels[index];
1148474b025SMikko Perttunen }
1158474b025SMikko Perttunen 
1168474b025SMikko Perttunen /**
1178474b025SMikko Perttunen  * host1x_channel_request() - Allocate a channel
118caccddcfSThierry Reding  * @client: Host1x client this channel will be used to send commands to
1198474b025SMikko Perttunen  *
120caccddcfSThierry Reding  * Allocates a new host1x channel for @client. May return NULL if CDMA
1218474b025SMikko Perttunen  * initialization fails.
1228474b025SMikko Perttunen  */
123caccddcfSThierry Reding struct host1x_channel *host1x_channel_request(struct host1x_client *client)
1246579324aSTerje Bergstrom {
125caccddcfSThierry Reding 	struct host1x *host = dev_get_drvdata(client->dev->parent);
1268474b025SMikko Perttunen 	struct host1x_channel_list *chlist = &host->channel_list;
1278474b025SMikko Perttunen 	struct host1x_channel *channel;
128e18e33afSThierry Reding 	int err;
1296579324aSTerje Bergstrom 
1308474b025SMikko Perttunen 	channel = acquire_unused_channel(host);
1316579324aSTerje Bergstrom 	if (!channel)
1328474b025SMikko Perttunen 		return NULL;
1336579324aSTerje Bergstrom 
1348474b025SMikko Perttunen 	kref_init(&channel->refcount);
1358474b025SMikko Perttunen 	mutex_init(&channel->submitlock);
136caccddcfSThierry Reding 	channel->client = client;
137caccddcfSThierry Reding 	channel->dev = client->dev;
1388474b025SMikko Perttunen 
1398474b025SMikko Perttunen 	err = host1x_hw_channel_init(host, channel, channel->id);
1406579324aSTerje Bergstrom 	if (err < 0)
1416579324aSTerje Bergstrom 		goto fail;
1426579324aSTerje Bergstrom 
1438474b025SMikko Perttunen 	err = host1x_cdma_init(&channel->cdma);
1448474b025SMikko Perttunen 	if (err < 0)
1458474b025SMikko Perttunen 		goto fail;
1466579324aSTerje Bergstrom 
1476579324aSTerje Bergstrom 	return channel;
1486579324aSTerje Bergstrom 
1496579324aSTerje Bergstrom fail:
1508474b025SMikko Perttunen 	clear_bit(channel->id, chlist->allocated_channels);
1518474b025SMikko Perttunen 
152caccddcfSThierry Reding 	dev_err(client->dev, "failed to initialize channel\n");
1538474b025SMikko Perttunen 
1546579324aSTerje Bergstrom 	return NULL;
1556579324aSTerje Bergstrom }
156fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_request);
157