Commit | Line | Data |
---|---|---|
6579324a TB |
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 */ | |
27 | int host1x_channel_list_init(struct host1x *host) | |
28 | { | |
29 | INIT_LIST_HEAD(&host->chlist.list); | |
30 | mutex_init(&host->chlist_mutex); | |
31 | ||
32 | if (host->info->nb_channels > BITS_PER_LONG) { | |
33 | WARN(1, "host1x hardware has more channels than supported by the driver\n"); | |
34 | return -ENOSYS; | |
35 | } | |
36 | ||
37 | return 0; | |
38 | } | |
39 | ||
40 | int host1x_job_submit(struct host1x_job *job) | |
41 | { | |
42 | struct host1x *host = dev_get_drvdata(job->channel->dev->parent); | |
43 | ||
44 | return host1x_hw_channel_submit(host, job); | |
45 | } | |
46 | ||
47 | struct host1x_channel *host1x_channel_get(struct host1x_channel *channel) | |
48 | { | |
49 | int err = 0; | |
50 | ||
51 | mutex_lock(&channel->reflock); | |
52 | ||
53 | if (channel->refcount == 0) | |
54 | err = host1x_cdma_init(&channel->cdma); | |
55 | ||
56 | if (!err) | |
57 | channel->refcount++; | |
58 | ||
59 | mutex_unlock(&channel->reflock); | |
60 | ||
61 | return err ? NULL : channel; | |
62 | } | |
63 | ||
64 | void host1x_channel_put(struct host1x_channel *channel) | |
65 | { | |
66 | mutex_lock(&channel->reflock); | |
67 | ||
68 | if (channel->refcount == 1) { | |
69 | struct host1x *host = dev_get_drvdata(channel->dev->parent); | |
70 | ||
71 | host1x_hw_cdma_stop(host, &channel->cdma); | |
72 | host1x_cdma_deinit(&channel->cdma); | |
73 | } | |
74 | ||
75 | channel->refcount--; | |
76 | ||
77 | mutex_unlock(&channel->reflock); | |
78 | } | |
79 | ||
80 | struct host1x_channel *host1x_channel_request(struct device *dev) | |
81 | { | |
82 | struct host1x *host = dev_get_drvdata(dev->parent); | |
83 | int max_channels = host->info->nb_channels; | |
84 | struct host1x_channel *channel = NULL; | |
85 | int index, err; | |
86 | ||
87 | mutex_lock(&host->chlist_mutex); | |
88 | ||
89 | index = find_first_zero_bit(&host->allocated_channels, max_channels); | |
90 | if (index >= max_channels) | |
91 | goto fail; | |
92 | ||
93 | channel = kzalloc(sizeof(*channel), GFP_KERNEL); | |
94 | if (!channel) | |
95 | goto fail; | |
96 | ||
97 | err = host1x_hw_channel_init(host, channel, index); | |
98 | if (err < 0) | |
99 | goto fail; | |
100 | ||
101 | /* Link device to host1x_channel */ | |
102 | channel->dev = dev; | |
103 | ||
104 | /* Add to channel list */ | |
105 | list_add_tail(&channel->list, &host->chlist.list); | |
106 | ||
107 | host->allocated_channels |= BIT(index); | |
108 | ||
109 | mutex_unlock(&host->chlist_mutex); | |
110 | return channel; | |
111 | ||
112 | fail: | |
113 | dev_err(dev, "failed to init channel\n"); | |
114 | kfree(channel); | |
115 | mutex_unlock(&host->chlist_mutex); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | void host1x_channel_free(struct host1x_channel *channel) | |
120 | { | |
121 | struct host1x *host = dev_get_drvdata(channel->dev->parent); | |
122 | ||
123 | host->allocated_channels &= ~BIT(channel->id); | |
124 | list_del(&channel->list); | |
125 | kfree(channel); | |
126 | } |