55186ed21fb002d16d6ce9b63a833d481274b0ce
[kernel.git] / drivers / mfd / glamo / glamo-cmdq.c
1 /*
2  * SMedia Glamo 336x/337x command queue handling
3  *
4  * Copyright (C) 2008-2009 Thomas White <taw@bitwiz.org.uk>
5  * Copyright (C) 2009 Andreas Pokorny <andreas.pokorny@gmail.com>
6  * Based on xf86-video-glamo (see below for details)
7  *
8  * All rights reserved.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  * MA 02111-1307 USA
24  *
25  * Command queue handling functions based on those from xf86-video-glamo, to
26  * which the following licence applies:
27  *
28  * Copyright  2007 OpenMoko, Inc.
29  * Copyright © 2009 Lars-Peter Clausen <lars@metafoo.de>
30  *
31  * This driver is based on Xati,
32  * Copyright  2004 Eric Anholt
33  *
34  * Permission to use, copy, modify, distribute, and sell this software and its
35  * documentation for any purpose is hereby granted without fee, provided that
36  * the above copyright notice appear in all copies and that both that copyright
37  * notice and this permission notice appear in supporting documentation, and
38  * that the name of the copyright holders not be used in advertising or
39  * publicity pertaining to distribution of the software without specific,
40  * written prior permission.  The copyright holders make no representations
41  * about the suitability of this software for any purpose.  It is provided "as
42  * is" without express or implied warranty.
43  *
44  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
45  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
46  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
47  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
48  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
49  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
50  * OF THIS SOFTWARE.
51  */
52
53 #include <drm/drmP.h>
54 #include <drm/glamo_drm.h>
55
56 #include "glamo-core.h"
57 #include "glamo-drm-private.h"
58 #include "glamo-regs.h"
59
60
61 static inline void reg_write(struct glamodrm_handle *gdrm,
62                       u_int16_t reg, u_int16_t val)
63 {
64         iowrite16(val, gdrm->reg_base + reg);
65 }
66
67
68 static inline u16 reg_read(struct glamodrm_handle *gdrm, u_int16_t reg)
69 {
70         return ioread16(gdrm->reg_base + reg);
71 }
72
73
74 static u32 glamo_get_read(struct glamodrm_handle *gdrm)
75 {
76         /* we could turn off clock here */
77         u32 ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
78         ring_read |= ((reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH)
79                                 & 0x7) << 16);
80
81         return ring_read;
82 }
83
84
85 static u32 glamo_get_write(struct glamodrm_handle *gdrm)
86 {
87         u32 ring_write = reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL);
88         ring_write |= ((reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH)
89                                 & 0x7) << 16);
90
91         return ring_write;
92 }
93
94
95 /* Add commands to the ring buffer */
96 static int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr,
97                              unsigned int count)
98 {
99         size_t ring_write, ring_read;
100         size_t new_ring_write;
101
102         down(&gdrm->add_to_ring);
103
104         ring_write = glamo_get_write(gdrm);
105
106         /* Calculate where we'll end up */
107         new_ring_write = (ring_write + count) % GLAMO_CMDQ_SIZE;
108
109         /* Wait until there is enough space to queue the cmd buffer */
110         if (new_ring_write > ring_write) {
111                 /* Loop while the read pointer is between the old and new
112                  * positions */
113                 do {
114                         ring_read = glamo_get_read(gdrm);
115                 } while (ring_read > ring_write && ring_read < new_ring_write);
116         } else {
117                 /* Same, but kind of inside-out */
118                 do {
119                         ring_read = glamo_get_read(gdrm);
120                 } while (ring_read > ring_write || ring_read < new_ring_write);
121         }
122
123         /* Are we about to wrap around? */
124         if (ring_write >= new_ring_write) {
125
126                 u32 rest_size;
127
128                 /* Wrap around */
129                 rest_size = GLAMO_CMDQ_SIZE - ring_write; /* Space left */
130
131                 /* Write from current position to end */
132                 memcpy_toio(gdrm->cmdq_base+ring_write, addr, rest_size);
133
134                 /* Write from start */
135                 memcpy_toio(gdrm->cmdq_base, addr+(rest_size>>1),
136                             count - rest_size);
137
138                 /* ring_write being 0 will result in a deadlock because the
139                  * cmdq read will never stop. To avoid such an behaviour insert
140                  * an empty instruction. */
141                 if (new_ring_write == 0) {
142                         iowrite16(0x0000, gdrm->cmdq_base);
143                         iowrite16(0x0000, gdrm->cmdq_base + 2);
144                         new_ring_write = 4;
145                 }
146
147                 /* Suppose we just filled the WHOLE ring buffer, and so the
148                  * write position ends up in the same place as it started.
149                  * No change in poginter means no activity from the command
150                  * queue engine.  So, insert a no-op */
151                 if (ring_write == new_ring_write) {
152                         iowrite16(0x0000, gdrm->cmdq_base + new_ring_write);
153                         iowrite16(0x0000, gdrm->cmdq_base + new_ring_write + 2);
154                         new_ring_write += 4;
155                 }
156
157         } else {
158
159                 memcpy_toio(gdrm->cmdq_base+ring_write, addr, count);
160
161         }
162
163         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH,
164                         (new_ring_write >> 16) & 0x7f);
165         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL,
166                         new_ring_write & 0xffff);
167
168         up(&gdrm->add_to_ring);
169
170         return 0;
171 }
172
173
174 /* Return true for a legal sequence of commands, otherwise false */
175 static int glamo_sanitize_buffer(u16 *cmds, unsigned int count)
176 {
177         /* XXX FIXME TODO: Implementation... */
178         return 1;
179 }
180
181
182 /* Substitute the real addresses in VRAM for any required buffer objects */
183 static int glamo_do_relocation(struct glamodrm_handle *gdrm,
184                                drm_glamo_cmd_buffer_t *cbuf, u16 *cmds,
185                                struct drm_device *dev,
186                                struct drm_file *file_priv)
187 {
188         u32 *handles;
189         int *offsets;
190         int nobjs =  cbuf->nobjs;
191         int i;
192
193         if ( nobjs > 32 ) return -EINVAL;       /* Get real... */
194
195         handles = drm_alloc(nobjs*sizeof(u32), DRM_MEM_DRIVER);
196         if ( handles == NULL ) return -1;
197         if ( copy_from_user(handles, cbuf->objs, nobjs*sizeof(u32)) )
198                 return -1;
199
200         offsets = drm_alloc(nobjs*sizeof(int), DRM_MEM_DRIVER);
201         if ( offsets == NULL ) return -1;
202         if ( copy_from_user(offsets, cbuf->obj_pos, nobjs*sizeof(int)) )
203                 return -1;
204
205         for ( i=0; i<nobjs; i++ ) {
206
207                 u32 handle = handles[i];
208                 int offset = offsets[i];
209                 struct drm_gem_object *obj;
210                 struct drm_glamo_gem_object *gobj;
211                 u32 addr;
212                 u16 addr_low, addr_high;
213
214                 if ( offset > cbuf->bufsz ) {
215                         printk(KERN_WARNING "[glamo-drm] Offset out of range "
216                                             "for this relocation!\n");
217                         goto fail;
218                 }
219
220                 obj = drm_gem_object_lookup(dev, file_priv, handle);
221                 if ( obj == NULL ) return -1;
222
223                 /* Unref the object now, or it'll never get freed.
224                  * This should really happen after the GPU has finished
225                  * the commands which are about to be submitted. */
226                 drm_gem_object_unreference(obj);
227
228                 gobj = obj->driver_private;
229                 if ( gobj == NULL ) {
230                         printk(KERN_WARNING "[glamo-drm] This object has no "
231                                             "private data!\n");
232                         goto fail;
233                 }
234
235                 addr = GLAMO_OFFSET_FB + gobj->block->start;
236                 addr_low = addr & 0xffff;
237                 addr_high = (addr >> 16) & 0x7f;
238
239                 /* FIXME: Should really check that the register is a
240                  * valid one for this relocation. */
241
242                 *(cmds+(offset/2)+1) = addr_low;
243                 *(cmds+(offset/2)+3) = addr_high;
244
245         }
246
247         drm_free(handles, 1, DRM_MEM_DRIVER);
248         drm_free(offsets, 1, DRM_MEM_DRIVER);
249         return 0;
250
251 fail:
252         drm_free(handles, 1, DRM_MEM_DRIVER);
253         drm_free(offsets, 1, DRM_MEM_DRIVER);
254         return -1;
255 }
256
257
258 /* This is DRM_IOCTL_GLAMO_CMDBUF */
259 int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data,
260                        struct drm_file *file_priv)
261 {
262         int ret = 0;
263         struct glamodrm_handle *gdrm;
264         unsigned int count;
265         drm_glamo_cmd_buffer_t *cbuf = data;
266         u16 *cmds;
267
268         gdrm = dev->dev_private;
269
270         count = cbuf->bufsz;
271
272         if ( count > PAGE_SIZE ) return -EINVAL;
273
274         cmds = drm_alloc(count, DRM_MEM_DRIVER);
275         if ( cmds == NULL ) return -ENOMEM;
276         if ( copy_from_user(cmds, cbuf->buf, count) )   {
277                 printk(KERN_WARNING "[glamo-drm] copy from user failed\n");
278                 ret = -EINVAL;
279                 goto cleanup;
280         }
281
282         /* Check the buffer isn't going to tell Glamo to enact naughtiness */
283         if ( !glamo_sanitize_buffer(cmds, count) ) {
284                 printk(KERN_WARNING "[glamo-drm] sanitize buffer failed\n");
285                 ret = -EINVAL;
286                 goto cleanup;
287         }
288
289         /* Perform relocation, if necessary */
290         if ( cbuf->nobjs ) {
291                 if ( glamo_do_relocation(gdrm, cbuf, cmds, dev, file_priv) )
292                 {
293                         printk(KERN_WARNING "[glamo-drm] Relocation failed\n");
294                         ret = -EINVAL;
295                         goto cleanup;
296                 }
297         }
298
299         glamo_add_to_ring(gdrm, cmds, count);
300
301
302 cleanup:
303         drm_free(cmds, 1, DRM_MEM_DRIVER);
304
305         return ret;
306 }
307
308
309 int glamo_cmdq_init(struct glamodrm_handle *gdrm)
310 {
311         unsigned int i;
312
313         init_MUTEX(&gdrm->add_to_ring);
314
315         /* Enable 2D and 3D */
316         glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_2D);
317         glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D);
318
319         /* Start by zeroing the command queue memory */
320         for ( i=0; i<GLAMO_CMDQ_SIZE; i+=2 ) {
321                 iowrite16(0x0000, gdrm->cmdq_base+i);
322         }
323
324         glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
325         glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
326
327         /* Set up command queue location */
328         reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRL,
329                                         GLAMO_OFFSET_CMDQ & 0xffff);
330         reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRH,
331                                         (GLAMO_OFFSET_CMDQ >> 16) & 0x7f);
332
333         /* Length of command queue in 1k blocks, minus one */
334         reg_write(gdrm, GLAMO_REG_CMDQ_LEN, (GLAMO_CMDQ_SIZE >> 10)-1);
335         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH, 0);
336         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL, 0);
337         reg_write(gdrm, GLAMO_REG_CMDQ_CONTROL,
338                                          1 << 12 |      /* Turbo flip (?) */
339                                          5 << 8 |       /* no interrupt */
340                                          8 << 4);       /* HQ threshold */
341
342         return 0;
343 }
344
345
346 int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm)
347 {
348         return 0;
349 }
350
351
352 void glamo_cmdq_suspend(struct glamodrm_handle *gdrm)
353 {
354         /* Placeholder... */
355 }
356
357
358 void glamo_cmdq_resume(struct glamodrm_handle *gdrm)
359 {
360         glamo_cmdq_init(gdrm);
361 }