linux/drivers/gpu/host1x/channel.c
<<
>>
Prefs
   1/*
   2 * Tegra host1x Channel
   3 *
   4 * Copyright (c) 2010-2013, NVIDIA Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include <linux/slab.h>
  20#include <linux/module.h>
  21
  22#include "channel.h"
  23#include "dev.h"
  24#include "job.h"
  25
  26/* Constructor for the host1x device list */
  27int host1x_channel_list_init(struct host1x_channel_list *chlist,
  28                             unsigned int num_channels)
  29{
  30        chlist->channels = kcalloc(num_channels, sizeof(struct host1x_channel),
  31                                   GFP_KERNEL);
  32        if (!chlist->channels)
  33                return -ENOMEM;
  34
  35        chlist->allocated_channels =
  36                kcalloc(BITS_TO_LONGS(num_channels), sizeof(unsigned long),
  37                        GFP_KERNEL);
  38        if (!chlist->allocated_channels) {
  39                kfree(chlist->channels);
  40                return -ENOMEM;
  41        }
  42
  43        bitmap_zero(chlist->allocated_channels, num_channels);
  44
  45        return 0;
  46}
  47
  48void host1x_channel_list_free(struct host1x_channel_list *chlist)
  49{
  50        kfree(chlist->allocated_channels);
  51        kfree(chlist->channels);
  52}
  53
  54int host1x_job_submit(struct host1x_job *job)
  55{
  56        struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
  57
  58        return host1x_hw_channel_submit(host, job);
  59}
  60EXPORT_SYMBOL(host1x_job_submit);
  61
  62struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
  63{
  64        kref_get(&channel->refcount);
  65
  66        return channel;
  67}
  68EXPORT_SYMBOL(host1x_channel_get);
  69
  70/**
  71 * host1x_channel_get_index() - Attempt to get channel reference by index
  72 * @host: Host1x device object
  73 * @index: Index of channel
  74 *
  75 * If channel number @index is currently allocated, increase its refcount
  76 * and return a pointer to it. Otherwise, return NULL.
  77 */
  78struct host1x_channel *host1x_channel_get_index(struct host1x *host,
  79                                                unsigned int index)
  80{
  81        struct host1x_channel *ch = &host->channel_list.channels[index];
  82
  83        if (!kref_get_unless_zero(&ch->refcount))
  84                return NULL;
  85
  86        return ch;
  87}
  88
  89static void release_channel(struct kref *kref)
  90{
  91        struct host1x_channel *channel =
  92                container_of(kref, struct host1x_channel, refcount);
  93        struct host1x *host = dev_get_drvdata(channel->dev->parent);
  94        struct host1x_channel_list *chlist = &host->channel_list;
  95
  96        host1x_hw_cdma_stop(host, &channel->cdma);
  97        host1x_cdma_deinit(&channel->cdma);
  98
  99        clear_bit(channel->id, chlist->allocated_channels);
 100}
 101
 102void host1x_channel_put(struct host1x_channel *channel)
 103{
 104        kref_put(&channel->refcount, release_channel);
 105}
 106EXPORT_SYMBOL(host1x_channel_put);
 107
 108static struct host1x_channel *acquire_unused_channel(struct host1x *host)
 109{
 110        struct host1x_channel_list *chlist = &host->channel_list;
 111        unsigned int max_channels = host->info->nb_channels;
 112        unsigned int index;
 113
 114        index = find_first_zero_bit(chlist->allocated_channels, max_channels);
 115        if (index >= max_channels) {
 116                dev_err(host->dev, "failed to find free channel\n");
 117                return NULL;
 118        }
 119
 120        chlist->channels[index].id = index;
 121
 122        set_bit(index, chlist->allocated_channels);
 123
 124        return &chlist->channels[index];
 125}
 126
 127/**
 128 * host1x_channel_request() - Allocate a channel
 129 * @device: Host1x unit this channel will be used to send commands to
 130 *
 131 * Allocates a new host1x channel for @device. If there are no free channels,
 132 * this will sleep until one becomes available. May return NULL if CDMA
 133 * initialization fails.
 134 */
 135struct host1x_channel *host1x_channel_request(struct device *dev)
 136{
 137        struct host1x *host = dev_get_drvdata(dev->parent);
 138        struct host1x_channel_list *chlist = &host->channel_list;
 139        struct host1x_channel *channel;
 140        int err;
 141
 142        channel = acquire_unused_channel(host);
 143        if (!channel)
 144                return NULL;
 145
 146        kref_init(&channel->refcount);
 147        mutex_init(&channel->submitlock);
 148        channel->dev = dev;
 149
 150        err = host1x_hw_channel_init(host, channel, channel->id);
 151        if (err < 0)
 152                goto fail;
 153
 154        err = host1x_cdma_init(&channel->cdma);
 155        if (err < 0)
 156                goto fail;
 157
 158        return channel;
 159
 160fail:
 161        clear_bit(channel->id, chlist->allocated_channels);
 162
 163        dev_err(dev, "failed to initialize channel\n");
 164
 165        return NULL;
 166}
 167EXPORT_SYMBOL(host1x_channel_request);
 168