77f514eb4e01353bf0f48b52ea755c4337303fcc
[linux-2.6.git] / drivers / video / tegra / host / t20 / cdma_t20.c
1 /*
2  * drivers/video/tegra/host/t20/cdma_t20.c
3  *
4  * Tegra Graphics Host Command DMA
5  *
6  * Copyright (c) 2010-2011, 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_cdma.h"
24 #include "../dev.h"
25
26 #include "hardware_t20.h"
27
28 /*
29  * push_buffer
30  *
31  * The push buffer is a circular array of words to be fetched by command DMA.
32  * Note that it works slightly differently to the sync queue; fence == cur
33  * means that the push buffer is full, not empty.
34  */
35
36
37 /**
38  * Reset to empty push buffer
39  */
40 static void t20_push_buffer_reset(struct push_buffer *pb)
41 {
42         pb->fence = PUSH_BUFFER_SIZE - 8;
43         pb->cur = 0;
44 }
45
46 /**
47  * Init push buffer resources
48  */
49 static int t20_push_buffer_init(struct push_buffer *pb)
50 {
51         struct nvhost_cdma *cdma = pb_to_cdma(pb);
52         struct nvmap_client *nvmap = cdma_to_nvmap(cdma);
53         pb->mem = NULL;
54         pb->mapped = NULL;
55         pb->phys = 0;
56
57         BUG_ON(!cdma_pb_op(cdma).reset);
58         cdma_pb_op(cdma).reset(pb);
59
60         /* allocate and map pushbuffer memory */
61         pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32,
62                               NVMAP_HANDLE_WRITE_COMBINE);
63         if (IS_ERR_OR_NULL(pb->mem)) {
64                 pb->mem = NULL;
65                 goto fail;
66         }
67         pb->mapped = nvmap_mmap(pb->mem);
68         if (pb->mapped == NULL)
69                 goto fail;
70
71         /* pin pushbuffer and get physical address */
72         pb->phys = nvmap_pin(nvmap, pb->mem);
73         if (pb->phys >= 0xfffff000) {
74                 pb->phys = 0;
75                 goto fail;
76         }
77
78         /* put the restart at the end of pushbuffer memory */
79         *(pb->mapped + (PUSH_BUFFER_SIZE >> 2)) = nvhost_opcode_restart(pb->phys);
80
81         return 0;
82
83 fail:
84         cdma_pb_op(cdma).destroy(pb);
85         return -ENOMEM;
86 }
87
88 /**
89  * Clean up push buffer resources
90  */
91 static void t20_push_buffer_destroy(struct push_buffer *pb)
92 {
93         struct nvhost_cdma *cdma = pb_to_cdma(pb);
94         struct nvmap_client *nvmap = cdma_to_nvmap(cdma);
95         if (pb->mapped)
96                 nvmap_munmap(pb->mem, pb->mapped);
97
98         if (pb->phys != 0)
99                 nvmap_unpin(nvmap, pb->mem);
100
101         if (pb->mem)
102                 nvmap_free(nvmap, pb->mem);
103
104         pb->mem = NULL;
105         pb->mapped = NULL;
106         pb->phys = 0;
107 }
108
109 /**
110  * Push two words to the push buffer
111  * Caller must ensure push buffer is not full
112  */
113 static void t20_push_buffer_push_to(struct push_buffer *pb, u32 op1, u32 op2)
114 {
115         u32 cur = pb->cur;
116         u32 *p = (u32*)((u32)pb->mapped + cur);
117         BUG_ON(cur == pb->fence);
118         *(p++) = op1;
119         *(p++) = op2;
120         pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1);
121         /* printk("push_to_push_buffer: op1=%08x; op2=%08x; cur=%x\n", op1, op2, pb->cur); */
122 }
123
124 /**
125  * Pop a number of two word slots from the push buffer
126  * Caller must ensure push buffer is not empty
127  */
128 static void t20_push_buffer_pop_from(struct push_buffer *pb, unsigned int slots)
129 {
130         pb->fence = (pb->fence + slots * 8) & (PUSH_BUFFER_SIZE - 1);
131 }
132
133 /**
134  * Return the number of two word slots free in the push buffer
135  */
136 static u32 t20_push_buffer_space(struct push_buffer *pb)
137 {
138         return ((pb->fence - pb->cur) & (PUSH_BUFFER_SIZE - 1)) / 8;
139 }
140
141 static u32 t20_push_buffer_putptr(struct push_buffer *pb)
142 {
143         return pb->phys + pb->cur;
144 }
145
146
147 /**
148  * Start channel DMA
149  */
150 static void t20_cdma_start(struct nvhost_cdma *cdma)
151 {
152         void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
153
154         if (cdma->running)
155                 return;
156
157         BUG_ON(!cdma_pb_op(cdma).putptr);
158
159         cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer);
160
161         writel(nvhost_channel_dmactrl(true, false, false),
162                 chan_regs + HOST1X_CHANNEL_DMACTRL);
163
164         /* set base, put, end pointer (all of memory) */
165         writel(0, chan_regs + HOST1X_CHANNEL_DMASTART);
166         writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT);
167         writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND);
168
169         /* reset GET */
170         writel(nvhost_channel_dmactrl(true, true, true),
171                 chan_regs + HOST1X_CHANNEL_DMACTRL);
172
173         /* start the command DMA */
174         writel(nvhost_channel_dmactrl(false, false, false),
175                 chan_regs + HOST1X_CHANNEL_DMACTRL);
176
177         cdma->running = true;
178 }
179
180 /**
181  * Kick channel DMA into action by writing its PUT offset (if it has changed)
182  */
183 static void t20_cdma_kick(struct nvhost_cdma *cdma)
184 {
185         u32 put;
186         BUG_ON(!cdma_pb_op(cdma).putptr);
187
188         put = cdma_pb_op(cdma).putptr(&cdma->push_buffer);
189
190         if (put != cdma->last_put) {
191                 void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
192                 wmb();
193                 writel(put, chan_regs + HOST1X_CHANNEL_DMAPUT);
194                 cdma->last_put = put;
195         }
196 }
197
198 static void t20_cdma_stop(struct nvhost_cdma *cdma)
199 {
200         void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
201
202         mutex_lock(&cdma->lock);
203         if (cdma->running) {
204                 nvhost_cdma_wait(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY);
205                 writel(nvhost_channel_dmactrl(true, false, false),
206                         chan_regs + HOST1X_CHANNEL_DMACTRL);
207                 cdma->running = false;
208         }
209         mutex_unlock(&cdma->lock);
210 }
211
212 /**
213  * Retrieve the op pair at a slot offset from a DMA address
214  */
215 void t20_cdma_peek(struct nvhost_cdma *cdma,
216                           u32 dmaget, int slot, u32 *out)
217 {
218         u32 offset = dmaget - cdma->push_buffer.phys;
219         u32 *p = cdma->push_buffer.mapped;
220
221         offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2;
222         out[0] = p[offset];
223         out[1] = p[offset + 1];
224 }
225
226 int nvhost_init_t20_cdma_support(struct nvhost_master *host)
227 {
228         host->op.cdma.start = t20_cdma_start;
229         host->op.cdma.stop = t20_cdma_stop;
230         host->op.cdma.kick = t20_cdma_kick;
231
232         host->sync_queue_size = NVHOST_SYNC_QUEUE_SIZE;
233
234         host->op.push_buffer.reset = t20_push_buffer_reset;
235         host->op.push_buffer.init = t20_push_buffer_init;
236         host->op.push_buffer.destroy = t20_push_buffer_destroy;
237         host->op.push_buffer.push_to = t20_push_buffer_push_to;
238         host->op.push_buffer.pop_from = t20_push_buffer_pop_from;
239         host->op.push_buffer.space = t20_push_buffer_space;
240         host->op.push_buffer.putptr = t20_push_buffer_putptr;
241
242         return 0;
243 }