b4e49a0cdc672f86c0ea195d2dac7e72aacf4319
[linux-2.6.git] / drivers / video / tegra / host / nvhost_channel.c
1 /*
2  * drivers/video/tegra/host/nvhost_channel.c
3  *
4  * Tegra Graphics Host Channel
5  *
6  * Copyright (c) 2010, NVIDIA Corporation.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22
23 #include "nvhost_channel.h"
24 #include "dev.h"
25 #include "nvhost_hwctx.h"
26
27 #include <linux/platform_device.h>
28
29 #define NVMODMUTEX_2D_FULL   (1)
30 #define NVMODMUTEX_2D_SIMPLE (2)
31 #define NVMODMUTEX_2D_SB_A   (3)
32 #define NVMODMUTEX_2D_SB_B   (4)
33 #define NVMODMUTEX_3D        (5)
34 #define NVMODMUTEX_DISPLAYA  (6)
35 #define NVMODMUTEX_DISPLAYB  (7)
36 #define NVMODMUTEX_VI        (8)
37 #define NVMODMUTEX_DSI       (9)
38
39 static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action);
40 static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action);
41 static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action);
42
43 static const struct nvhost_channeldesc channelmap[] = {
44 {
45         /* channel 0 */
46         .name          = "display",
47         .syncpts       = BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) |
48                          BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1),
49         .modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB),
50 },
51 {
52         /* channel 1 */
53         .name          = "gr3d",
54         .syncpts       = BIT(NVSYNCPT_3D),
55         .waitbases     = BIT(NVWAITBASE_3D),
56         .modulemutexes = BIT(NVMODMUTEX_3D),
57         .class         = NV_GRAPHICS_3D_CLASS_ID,
58         .power         = power_3d,
59 },
60 {
61         /* channel 2 */
62         .name          = "gr2d",
63         .syncpts       = BIT(NVSYNCPT_2D_0) | BIT(NVSYNCPT_2D_1),
64         .waitbases     = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1),
65         .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) |
66                          BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B),
67         .power         = power_2d,
68 },
69 {
70         /* channel 3 */
71         .name    = "isp",
72         .syncpts = 0,
73 },
74 {
75         /* channel 4 */
76         .name          = "vi",
77         .syncpts       = BIT(NVSYNCPT_VI_ISP_0) | BIT(NVSYNCPT_VI_ISP_1) |
78                          BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) |
79                          BIT(NVSYNCPT_VI_ISP_4) | BIT(NVSYNCPT_VI_ISP_5),
80         .modulemutexes = BIT(NVMODMUTEX_VI),
81         .exclusive     = true,
82 },
83 {
84         /* channel 5 */
85         .name          = "mpe",
86         .syncpts       = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) |
87                          BIT(NVSYNCPT_MPE_WR_SAFE),
88         .waitbases     = BIT(NVWAITBASE_MPE),
89         .class         = NV_VIDEO_ENCODE_MPEG_CLASS_ID,
90         .power         = power_mpe,
91         .exclusive     = true,
92 },
93 {
94         /* channel 6 */
95         .name          = "dsi",
96         .syncpts       = BIT(NVSYNCPT_DSI),
97         .modulemutexes = BIT(NVMODMUTEX_DSI),
98 }};
99
100 static inline void __iomem *channel_aperture(void __iomem *p, int ndx)
101 {
102         ndx += NVHOST_CHANNEL_BASE;
103         p += NV_HOST1X_CHANNEL0_BASE;
104         p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES;
105         return p;
106 }
107
108 int __init nvhost_channel_init(struct nvhost_channel *ch,
109                         struct nvhost_master *dev, int index)
110 {
111         BUILD_BUG_ON(NVHOST_NUMCHANNELS != ARRAY_SIZE(channelmap));
112
113         ch->dev = dev;
114         ch->desc = &channelmap[index];
115         ch->aperture = channel_aperture(dev->aperture, index);
116         mutex_init(&ch->reflock);
117         mutex_init(&ch->submitlock);
118
119         return nvhost_hwctx_handler_init(&ch->ctxhandler, ch->desc->name);
120 }
121
122 struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch)
123 {
124         int err = 0;
125         mutex_lock(&ch->reflock);
126         if (ch->refcount == 0) {
127                 err = nvhost_module_init(&ch->mod, ch->desc->name,
128                                         ch->desc->power, &ch->dev->mod,
129                                         &ch->dev->pdev->dev);
130                 if (!err) {
131                         err = nvhost_cdma_init(&ch->cdma);
132                         if (err)
133                                 nvhost_module_deinit(&ch->mod);
134                 }
135         } else if (ch->desc->exclusive) {
136                 err = -EBUSY;
137         }
138         if (!err) {
139                 ch->refcount++;
140         }
141         mutex_unlock(&ch->reflock);
142
143         return err ? NULL : ch;
144 }
145
146 void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx)
147 {
148         if (ctx) {
149                 mutex_lock(&ch->submitlock);
150                 if (ch->cur_ctx == ctx)
151                         ch->cur_ctx = NULL;
152                 mutex_unlock(&ch->submitlock);
153         }
154
155         mutex_lock(&ch->reflock);
156         if (ch->refcount == 1) {
157                 nvhost_module_deinit(&ch->mod);
158                 /* cdma may already be stopped, that's ok */
159                 nvhost_cdma_stop(&ch->cdma);
160                 nvhost_cdma_deinit(&ch->cdma);
161         }
162         ch->refcount--;
163         mutex_unlock(&ch->reflock);
164 }
165
166 void nvhost_channel_suspend(struct nvhost_channel *ch)
167 {
168         mutex_lock(&ch->reflock);
169         BUG_ON(nvhost_module_powered(&ch->mod));
170         if (ch->refcount)
171                 nvhost_cdma_stop(&ch->cdma);
172         mutex_unlock(&ch->reflock);
173 }
174
175 void nvhost_channel_submit(struct nvhost_channel *ch,
176                         struct nvmap_client *user_nvmap,
177                         struct nvhost_op_pair *ops, int num_pairs,
178                         struct nvhost_cpuinterrupt *intrs, int num_intrs,
179                         struct nvmap_handle **unpins, int num_unpins,
180                         u32 syncpt_id, u32 syncpt_val,
181                         int num_nulled_incrs)
182 {
183         int i;
184         struct nvhost_op_pair* p;
185
186         /* schedule interrupts */
187         for (i = 0; i < num_intrs; i++) {
188                 nvhost_intr_add_action(&ch->dev->intr, syncpt_id, intrs[i].syncpt_val,
189                                 NVHOST_INTR_ACTION_CTXSAVE, intrs[i].intr_data, NULL);
190         }
191
192         /* begin a CDMA submit */
193         nvhost_cdma_begin(&ch->cdma);
194
195         /* push ops */
196         for (i = 0, p = ops; i < num_pairs; i++, p++)
197                 nvhost_cdma_push(&ch->cdma, p->op1, p->op2);
198
199         /* extra work to do for null kickoff */
200         if (num_nulled_incrs) {
201                 u32 incr;
202                 u32 op_incr;
203
204                 /* TODO ideally we'd also perform host waits here */
205
206                 /* push increments that correspond to nulled out commands */
207                 op_incr = nvhost_opcode_imm(0, 0x100 | syncpt_id);
208                 for (incr = 0; incr < (num_nulled_incrs >> 1); incr++)
209                         nvhost_cdma_push(&ch->cdma, op_incr, op_incr);
210                 if (num_nulled_incrs & 1)
211                         nvhost_cdma_push(&ch->cdma, op_incr, NVHOST_OPCODE_NOOP);
212
213                 /* for 3d, waitbase needs to be incremented after each submit */
214                 if (ch->desc->class == NV_GRAPHICS_3D_CLASS_ID) {
215                         u32 op1 = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
216                                         NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
217                         u32 op2 = nvhost_class_host_incr_syncpt_base(NVWAITBASE_3D,
218                                         num_nulled_incrs);
219
220                         nvhost_cdma_push(&ch->cdma, op1, op2);
221                 }
222         }
223
224         /* end CDMA submit & stash pinned hMems into sync queue for later cleanup */
225         nvhost_cdma_end(&ch->cdma, user_nvmap, syncpt_id, syncpt_val,
226                         unpins, num_unpins);
227 }
228
229 static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action)
230 {
231         /* TODO: [ahatala 2010-06-17] reimplement EPP hang war */
232         if (action == NVHOST_POWER_ACTION_OFF) {
233                 /* TODO: [ahatala 2010-06-17] reset EPP */
234         }
235 }
236
237 static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action)
238 {
239         struct nvhost_channel *ch = container_of(mod, struct nvhost_channel, mod);
240
241         if (action == NVHOST_POWER_ACTION_OFF) {
242                 mutex_lock(&ch->submitlock);
243                 if (ch->cur_ctx) {
244                         DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
245                         struct nvhost_op_pair save;
246                         struct nvhost_cpuinterrupt ctxsw;
247                         u32 syncval;
248                         void *ref;
249                         syncval = nvhost_syncpt_incr_max(&ch->dev->syncpt,
250                                                         NVSYNCPT_3D,
251                                                         ch->cur_ctx->save_incrs);
252                         save.op1 = nvhost_opcode_gather(0, ch->cur_ctx->save_size);
253                         save.op2 = ch->cur_ctx->save_phys;
254                         ctxsw.intr_data = ch->cur_ctx;
255                         ctxsw.syncpt_val = syncval - 1;
256                         ch->cur_ctx->valid = true;
257                         ch->ctxhandler.get(ch->cur_ctx);
258                         ch->cur_ctx = NULL;
259
260                         nvhost_channel_submit(ch, ch->dev->nvmap,
261                                               &save, 1, &ctxsw, 1, NULL, 0,
262                                               NVSYNCPT_3D, syncval, 0);
263
264                         nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D,
265                                                syncval,
266                                                NVHOST_INTR_ACTION_WAKEUP,
267                                                &wq, &ref);
268                         wait_event(wq,
269                                    nvhost_syncpt_min_cmp(&ch->dev->syncpt,
270                                                          NVSYNCPT_3D, syncval));
271                         nvhost_intr_put_ref(&ch->dev->intr, ref);
272                         nvhost_cdma_update(&ch->cdma);
273                 }
274                 mutex_unlock(&ch->submitlock);
275         }
276 }
277
278 static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action)
279 {
280 }