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