[ARM/tegra] nvhost: Tegra3 support
[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 int nvhost_channel_submit(struct nvhost_channel *channel,
176                         struct nvhost_hwctx *hwctx,
177                         struct nvmap_client *user_nvmap,
178                         u32 *gather,
179                         u32 *gather_end,
180                         struct nvmap_handle **unpins,
181                         int nr_unpins,
182                         u32 syncpt_id,
183                         u32 syncpt_incrs,
184                         u32 *syncpt_value,
185                         bool null_kickoff)
186 {
187         struct nvhost_hwctx *hwctx_to_save = NULL;
188         u32 user_syncpt_incrs = syncpt_incrs;
189         bool need_restore = false;
190         u32 syncval;
191         int err;
192
193         /* keep module powered */
194         nvhost_module_busy(&channel->mod);
195
196         /* get submit lock */
197         err = mutex_lock_interruptible(&channel->submitlock);
198         if (err) {
199                 nvhost_module_idle(&channel->mod);
200                 return err;
201         }
202
203         /* context switch */
204         if (channel->cur_ctx != hwctx) {
205                 hwctx_to_save = channel->cur_ctx;
206                 if (hwctx_to_save) {
207                         syncpt_incrs += hwctx_to_save->save_incrs;
208                         hwctx_to_save->valid = true;
209                         channel->ctxhandler.get(hwctx_to_save);
210                 }
211                 channel->cur_ctx = hwctx;
212                 if (channel->cur_ctx && channel->cur_ctx->valid) {
213                         need_restore = true;
214                         syncpt_incrs += channel->cur_ctx->restore_incrs;
215                 }
216         }
217
218         /* get absolute sync value */
219         if (BIT(syncpt_id) & NVSYNCPTS_CLIENT_MANAGED)
220                 syncval = nvhost_syncpt_set_max(&channel->dev->syncpt,
221                                                 syncpt_id, syncpt_incrs);
222         else
223                 syncval = nvhost_syncpt_incr_max(&channel->dev->syncpt,
224                                                 syncpt_id, syncpt_incrs);
225
226         /* begin a CDMA submit */
227         nvhost_cdma_begin(&channel->cdma);
228
229         /* push save buffer (pre-gather setup depends on unit) */
230         if (hwctx_to_save)
231                 channel->ctxhandler.save_push(&channel->cdma, hwctx_to_save);
232
233         /* gather restore buffer */
234         if (need_restore)
235                 nvhost_cdma_push(&channel->cdma,
236                         nvhost_opcode_gather(channel->cur_ctx->restore_size),
237                         channel->cur_ctx->restore_phys);
238
239         /* add a setclass for modules that require it (unless ctxsw added it) */
240         if (!hwctx_to_save && !need_restore && channel->desc->class)
241                 nvhost_cdma_push(&channel->cdma,
242                         nvhost_opcode_setclass(channel->desc->class, 0, 0),
243                         NVHOST_OPCODE_NOOP);
244
245         if (null_kickoff) {
246                 int incr;
247                 u32 op_incr;
248
249                 /* TODO ideally we'd also perform host waits here */
250
251                 /* push increments that correspond to nulled out commands */
252                 op_incr = nvhost_opcode_imm(0, 0x100 | syncpt_id);
253                 for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++)
254                         nvhost_cdma_push(&channel->cdma, op_incr, op_incr);
255                 if (user_syncpt_incrs & 1)
256                         nvhost_cdma_push(&channel->cdma,
257                                         op_incr, NVHOST_OPCODE_NOOP);
258
259                 /* for 3d, waitbase needs to be incremented after each submit */
260                 if (channel->desc->class == NV_GRAPHICS_3D_CLASS_ID)
261                         nvhost_cdma_push(&channel->cdma,
262                                         nvhost_opcode_setclass(
263                                                 NV_HOST1X_CLASS_ID,
264                                                 NV_CLASS_HOST_INCR_SYNCPT_BASE,
265                                                 1),
266                                         nvhost_class_host_incr_syncpt_base(
267                                                 NVWAITBASE_3D,
268                                                 user_syncpt_incrs));
269         }
270         else {
271                 /* push user gathers */
272                 for ( ; gather != gather_end; gather += 2)
273                         nvhost_cdma_push(&channel->cdma,
274                                         nvhost_opcode_gather(gather[0]),
275                                         gather[1]);
276         }
277
278         /* end CDMA submit & stash pinned hMems into sync queue */
279         nvhost_cdma_end(&channel->cdma, user_nvmap,
280                         syncpt_id, syncval, unpins, nr_unpins);
281
282         /*
283          * schedule a context save interrupt (to drain the host FIFO
284          * if necessary, and to release the restore buffer)
285          */
286         if (hwctx_to_save)
287                 nvhost_intr_add_action(&channel->dev->intr, syncpt_id,
288                         syncval - syncpt_incrs + hwctx_to_save->save_thresh,
289                         NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
290
291         /* schedule a submit complete interrupt */
292         nvhost_intr_add_action(&channel->dev->intr, syncpt_id, syncval,
293                         NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, NULL);
294
295         mutex_unlock(&channel->submitlock);
296
297         *syncpt_value = syncval;
298         return 0;
299 }
300
301 static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action)
302 {
303         /* TODO: [ahatala 2010-06-17] reimplement EPP hang war */
304         if (action == NVHOST_POWER_ACTION_OFF) {
305                 /* TODO: [ahatala 2010-06-17] reset EPP */
306         }
307 }
308
309 static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action)
310 {
311         struct nvhost_channel *ch = container_of(mod, struct nvhost_channel, mod);
312         struct nvhost_hwctx *hwctx_to_save;
313         DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
314         u32 syncpt_incrs, syncpt_val;
315         void *ref;
316
317         if (action != NVHOST_POWER_ACTION_OFF)
318                 return;
319
320         mutex_lock(&ch->submitlock);
321         hwctx_to_save = ch->cur_ctx;
322         if (!hwctx_to_save) {
323                 mutex_unlock(&ch->submitlock);
324                 return;
325         }
326
327         hwctx_to_save->valid = true;
328         ch->ctxhandler.get(hwctx_to_save);
329         ch->cur_ctx = NULL;
330
331         syncpt_incrs = hwctx_to_save->save_incrs;
332         syncpt_val = nvhost_syncpt_incr_max(&ch->dev->syncpt,
333                                         NVSYNCPT_3D, syncpt_incrs);
334
335         nvhost_cdma_begin(&ch->cdma);
336         ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save);
337         nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, NVSYNCPT_3D, syncpt_val, NULL, 0);
338
339         nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D,
340                         syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh,
341                         NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
342
343         nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, syncpt_val,
344                         NVHOST_INTR_ACTION_WAKEUP, &wq, &ref);
345         wait_event(wq,
346                 nvhost_syncpt_min_cmp(&ch->dev->syncpt,
347                                 NVSYNCPT_3D, syncpt_val));
348
349         nvhost_intr_put_ref(&ch->dev->intr, ref);
350
351         nvhost_cdma_update(&ch->cdma);
352
353         mutex_unlock(&ch->submitlock);
354 }
355
356 static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action)
357 {
358 }