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 */
host1x_channel_list_init(struct host1x_channel_list * chlist,unsigned int num_channels)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
242e1bfb31SChristophe JAILLET chlist->allocated_channels = bitmap_zalloc(num_channels, GFP_KERNEL);
258474b025SMikko Perttunen if (!chlist->allocated_channels) {
268474b025SMikko Perttunen kfree(chlist->channels);
278474b025SMikko Perttunen return -ENOMEM;
286579324aSTerje Bergstrom }
296579324aSTerje Bergstrom
309764723dSMikko Perttunen mutex_init(&chlist->lock);
319764723dSMikko Perttunen
326579324aSTerje Bergstrom return 0;
336579324aSTerje Bergstrom }
346579324aSTerje Bergstrom
host1x_channel_list_free(struct host1x_channel_list * chlist)358474b025SMikko Perttunen void host1x_channel_list_free(struct host1x_channel_list *chlist)
368474b025SMikko Perttunen {
372e1bfb31SChristophe JAILLET bitmap_free(chlist->allocated_channels);
388474b025SMikko Perttunen kfree(chlist->channels);
398474b025SMikko Perttunen }
408474b025SMikko Perttunen
host1x_job_submit(struct host1x_job * job)416579324aSTerje Bergstrom int host1x_job_submit(struct host1x_job *job)
426579324aSTerje Bergstrom {
436579324aSTerje Bergstrom struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
446579324aSTerje Bergstrom
456579324aSTerje Bergstrom return host1x_hw_channel_submit(host, job);
466579324aSTerje Bergstrom }
47fae798a1SThierry Reding EXPORT_SYMBOL(host1x_job_submit);
486579324aSTerje Bergstrom
host1x_channel_get(struct host1x_channel * channel)496579324aSTerje Bergstrom struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
506579324aSTerje Bergstrom {
518474b025SMikko Perttunen kref_get(&channel->refcount);
526579324aSTerje Bergstrom
538474b025SMikko Perttunen return channel;
546579324aSTerje Bergstrom }
55fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_get);
566579324aSTerje Bergstrom
578474b025SMikko Perttunen /**
588474b025SMikko Perttunen * host1x_channel_get_index() - Attempt to get channel reference by index
598474b025SMikko Perttunen * @host: Host1x device object
608474b025SMikko Perttunen * @index: Index of channel
618474b025SMikko Perttunen *
628474b025SMikko Perttunen * If channel number @index is currently allocated, increase its refcount
638474b025SMikko Perttunen * and return a pointer to it. Otherwise, return NULL.
648474b025SMikko Perttunen */
host1x_channel_get_index(struct host1x * host,unsigned int index)658474b025SMikko Perttunen struct host1x_channel *host1x_channel_get_index(struct host1x *host,
668474b025SMikko Perttunen unsigned int index)
676579324aSTerje Bergstrom {
688474b025SMikko Perttunen struct host1x_channel *ch = &host->channel_list.channels[index];
696579324aSTerje Bergstrom
708474b025SMikko Perttunen if (!kref_get_unless_zero(&ch->refcount))
718474b025SMikko Perttunen return NULL;
728474b025SMikko Perttunen
738474b025SMikko Perttunen return ch;
748474b025SMikko Perttunen }
758474b025SMikko Perttunen
host1x_channel_stop(struct host1x_channel * channel)769ca790f4SDmitry Osipenko void host1x_channel_stop(struct host1x_channel *channel)
779ca790f4SDmitry Osipenko {
789ca790f4SDmitry Osipenko struct host1x *host = dev_get_drvdata(channel->dev->parent);
799ca790f4SDmitry Osipenko
809ca790f4SDmitry Osipenko host1x_hw_cdma_stop(host, &channel->cdma);
819ca790f4SDmitry Osipenko }
829ca790f4SDmitry Osipenko EXPORT_SYMBOL(host1x_channel_stop);
839ca790f4SDmitry Osipenko
84*87fafcd5SMikko Perttunen /**
85*87fafcd5SMikko Perttunen * host1x_channel_stop_all() - disable CDMA on allocated channels
86*87fafcd5SMikko Perttunen * @host: host1x instance
87*87fafcd5SMikko Perttunen *
88*87fafcd5SMikko Perttunen * Stop CDMA on allocated channels
89*87fafcd5SMikko Perttunen */
host1x_channel_stop_all(struct host1x * host)90*87fafcd5SMikko Perttunen void host1x_channel_stop_all(struct host1x *host)
91*87fafcd5SMikko Perttunen {
92*87fafcd5SMikko Perttunen struct host1x_channel_list *chlist = &host->channel_list;
93*87fafcd5SMikko Perttunen int bit;
94*87fafcd5SMikko Perttunen
95*87fafcd5SMikko Perttunen mutex_lock(&chlist->lock);
96*87fafcd5SMikko Perttunen
97*87fafcd5SMikko Perttunen for_each_set_bit(bit, chlist->allocated_channels, host->info->nb_channels)
98*87fafcd5SMikko Perttunen host1x_channel_stop(&chlist->channels[bit]);
99*87fafcd5SMikko Perttunen
100*87fafcd5SMikko Perttunen mutex_unlock(&chlist->lock);
101*87fafcd5SMikko Perttunen }
102*87fafcd5SMikko Perttunen
release_channel(struct kref * kref)1038474b025SMikko Perttunen static void release_channel(struct kref *kref)
1048474b025SMikko Perttunen {
1058474b025SMikko Perttunen struct host1x_channel *channel =
1068474b025SMikko Perttunen container_of(kref, struct host1x_channel, refcount);
1076579324aSTerje Bergstrom struct host1x *host = dev_get_drvdata(channel->dev->parent);
1088474b025SMikko Perttunen struct host1x_channel_list *chlist = &host->channel_list;
1096579324aSTerje Bergstrom
1106579324aSTerje Bergstrom host1x_hw_cdma_stop(host, &channel->cdma);
1116579324aSTerje Bergstrom host1x_cdma_deinit(&channel->cdma);
1128474b025SMikko Perttunen
1138474b025SMikko Perttunen clear_bit(channel->id, chlist->allocated_channels);
1146579324aSTerje Bergstrom }
1156579324aSTerje Bergstrom
host1x_channel_put(struct host1x_channel * channel)1168474b025SMikko Perttunen void host1x_channel_put(struct host1x_channel *channel)
1178474b025SMikko Perttunen {
1188474b025SMikko Perttunen kref_put(&channel->refcount, release_channel);
1196579324aSTerje Bergstrom }
120fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_put);
1216579324aSTerje Bergstrom
acquire_unused_channel(struct host1x * host)1228474b025SMikko Perttunen static struct host1x_channel *acquire_unused_channel(struct host1x *host)
1238474b025SMikko Perttunen {
1248474b025SMikko Perttunen struct host1x_channel_list *chlist = &host->channel_list;
1258474b025SMikko Perttunen unsigned int max_channels = host->info->nb_channels;
1268474b025SMikko Perttunen unsigned int index;
1278474b025SMikko Perttunen
1289764723dSMikko Perttunen mutex_lock(&chlist->lock);
1299764723dSMikko Perttunen
1308474b025SMikko Perttunen index = find_first_zero_bit(chlist->allocated_channels, max_channels);
1318474b025SMikko Perttunen if (index >= max_channels) {
1329764723dSMikko Perttunen mutex_unlock(&chlist->lock);
1338474b025SMikko Perttunen dev_err(host->dev, "failed to find free channel\n");
1348474b025SMikko Perttunen return NULL;
1358474b025SMikko Perttunen }
1368474b025SMikko Perttunen
1378474b025SMikko Perttunen chlist->channels[index].id = index;
1388474b025SMikko Perttunen
1398474b025SMikko Perttunen set_bit(index, chlist->allocated_channels);
1408474b025SMikko Perttunen
1419764723dSMikko Perttunen mutex_unlock(&chlist->lock);
1429764723dSMikko Perttunen
1438474b025SMikko Perttunen return &chlist->channels[index];
1448474b025SMikko Perttunen }
1458474b025SMikko Perttunen
1468474b025SMikko Perttunen /**
1478474b025SMikko Perttunen * host1x_channel_request() - Allocate a channel
148caccddcfSThierry Reding * @client: Host1x client this channel will be used to send commands to
1498474b025SMikko Perttunen *
150caccddcfSThierry Reding * Allocates a new host1x channel for @client. May return NULL if CDMA
1518474b025SMikko Perttunen * initialization fails.
1528474b025SMikko Perttunen */
host1x_channel_request(struct host1x_client * client)153caccddcfSThierry Reding struct host1x_channel *host1x_channel_request(struct host1x_client *client)
1546579324aSTerje Bergstrom {
155caccddcfSThierry Reding struct host1x *host = dev_get_drvdata(client->dev->parent);
1568474b025SMikko Perttunen struct host1x_channel_list *chlist = &host->channel_list;
1578474b025SMikko Perttunen struct host1x_channel *channel;
158e18e33afSThierry Reding int err;
1596579324aSTerje Bergstrom
1608474b025SMikko Perttunen channel = acquire_unused_channel(host);
1616579324aSTerje Bergstrom if (!channel)
1628474b025SMikko Perttunen return NULL;
1636579324aSTerje Bergstrom
1648474b025SMikko Perttunen kref_init(&channel->refcount);
1658474b025SMikko Perttunen mutex_init(&channel->submitlock);
166caccddcfSThierry Reding channel->client = client;
167caccddcfSThierry Reding channel->dev = client->dev;
1688474b025SMikko Perttunen
1698474b025SMikko Perttunen err = host1x_hw_channel_init(host, channel, channel->id);
1706579324aSTerje Bergstrom if (err < 0)
1716579324aSTerje Bergstrom goto fail;
1726579324aSTerje Bergstrom
1738474b025SMikko Perttunen err = host1x_cdma_init(&channel->cdma);
1748474b025SMikko Perttunen if (err < 0)
1758474b025SMikko Perttunen goto fail;
1766579324aSTerje Bergstrom
1776579324aSTerje Bergstrom return channel;
1786579324aSTerje Bergstrom
1796579324aSTerje Bergstrom fail:
1808474b025SMikko Perttunen clear_bit(channel->id, chlist->allocated_channels);
1818474b025SMikko Perttunen
182caccddcfSThierry Reding dev_err(client->dev, "failed to initialize channel\n");
1838474b025SMikko Perttunen
1846579324aSTerje Bergstrom return NULL;
1856579324aSTerje Bergstrom }
186fae798a1SThierry Reding EXPORT_SYMBOL(host1x_channel_request);
187