Formatting fussiness
[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
54 #include <drm/drmP.h>
55 #include <drm/glamo_drm.h>
56
57 #include "glamo-core.h"
58 #include "glamo-drm-private.h"
59 #include "glamo-regs.h"
60
61
62 static inline void reg_write(struct glamodrm_handle *gdrm,
63                              u_int16_t reg, u_int16_t val)
64 {
65         iowrite16(val, gdrm->reg_base + reg);
66 }
67
68
69 static inline u16 reg_read(struct glamodrm_handle *gdrm, u_int16_t reg)
70 {
71         return ioread16(gdrm->reg_base + reg);
72 }
73
74
75 static u32 glamo_get_read(struct glamodrm_handle *gdrm)
76 {
77         /* we could turn off clock here */
78         u32 ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
79         ring_read |= (reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH) & 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) & 0x7) << 16;
89
90         return ring_write;
91 }
92
93
94 /* Add commands to the ring buffer */
95 static int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr,
96                              unsigned int count)
97 {
98         size_t ring_write, ring_read;
99         size_t new_ring_write;
100
101         down(&gdrm->add_to_ring);
102
103         ring_write = glamo_get_write(gdrm);
104
105         /* Calculate where we'll end up */
106         new_ring_write = (ring_write + count) % GLAMO_CMDQ_SIZE;
107
108         /* Wait until there is enough space to queue the cmd buffer */
109         if (new_ring_write > ring_write) {
110                 /* Loop while the read pointer is between the old and new
111                  * positions */
112                 do {
113                         ring_read = glamo_get_read(gdrm);
114                 } while (ring_read > ring_write && ring_read < new_ring_write);
115         } else {
116                 /* Same, but kind of inside-out */
117                 do {
118                         ring_read = glamo_get_read(gdrm);
119                 } while (ring_read > ring_write || ring_read < new_ring_write);
120         }
121
122         /* Are we about to wrap around? */
123         if (ring_write >= new_ring_write) {
124
125                 u32 rest_size;
126
127                 /* Wrap around */
128                 rest_size = GLAMO_CMDQ_SIZE - ring_write; /* Space left */
129
130                 /* Write from current position to end */
131                 memcpy_toio(gdrm->cmdq_base+ring_write, addr, rest_size);
132
133                 /* Write from start */
134                 memcpy_toio(gdrm->cmdq_base, addr+(rest_size>>1),
135                             count - rest_size);
136
137                 /* ring_write being 0 will result in a deadlock because the
138                  * cmdq read will never stop. To avoid such an behaviour insert
139                  * an empty instruction. */
140                 if (new_ring_write == 0) {
141                         iowrite16(0x0000, gdrm->cmdq_base);
142                         iowrite16(0x0000, gdrm->cmdq_base + 2);
143                         new_ring_write = 4;
144                 }
145
146                 /* Suppose we just filled the WHOLE ring buffer, and so the
147                  * write position ends up in the same place as it started.
148                  * No change in poginter means no activity from the command
149                  * queue engine.  So, insert a no-op */
150                 if (ring_write == new_ring_write) {
151                         iowrite16(0x0000, gdrm->cmdq_base + new_ring_write);
152                         iowrite16(0x0000, gdrm->cmdq_base + new_ring_write + 2);
153                         new_ring_write += 4;
154                 }
155
156         } else {
157
158                 memcpy_toio(gdrm->cmdq_base+ring_write, addr, count);
159
160         }
161
162         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH,
163                         (new_ring_write >> 16) & 0x7f);
164         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL,
165                         new_ring_write & 0xffff);
166
167         up(&gdrm->add_to_ring);
168
169         return 0;
170 }
171
172
173 /* Return true for a legal sequence of commands, otherwise false */
174 static int glamo_sanitize_buffer(u16 *cmds, unsigned int count)
175 {
176         /* XXX FIXME TODO: Implementation... */
177         return 1;
178 }
179
180
181 /* Substitute the real addresses in VRAM for any required buffer objects */
182 static int glamo_do_relocation(struct glamodrm_handle *gdrm,
183                                drm_glamo_cmd_buffer_t *cbuf, u16 *cmds,
184                                struct drm_device *dev,
185                                struct drm_file *file_priv)
186 {
187         u32 *handles;
188         int *offsets;
189         int nobjs =  cbuf->nobjs;
190         int i;
191
192         if ( nobjs > 32 ) return -EINVAL;       /* Get real... */
193
194         handles = drm_alloc(nobjs*sizeof(u32), DRM_MEM_DRIVER);
195         if ( handles == NULL ) return -1;
196         if ( copy_from_user(handles, cbuf->objs, nobjs*sizeof(u32)) )
197                 return -1;
198
199         offsets = drm_alloc(nobjs*sizeof(int), DRM_MEM_DRIVER);
200         if ( offsets == NULL ) return -1;
201         if ( copy_from_user(offsets, cbuf->obj_pos, nobjs*sizeof(int)) )
202                 return -1;
203
204         for ( i=0; i<nobjs; i++ ) {
205
206                 u32 handle = handles[i];
207                 int offset = offsets[i];
208                 struct drm_gem_object *obj;
209                 struct drm_glamo_gem_object *gobj;
210                 u32 addr;
211                 u16 addr_low, addr_high;
212
213                 if ( offset > cbuf->bufsz ) {
214                         printk(KERN_WARNING "[glamo-drm] Offset out of range"
215                                             " for this relocation!\n");
216                         goto fail;
217                 }
218
219                 obj = drm_gem_object_lookup(dev, file_priv, handle);
220                 if ( obj == NULL ) return -1;
221
222                 /* Unref the object now, or it'll never get freed.
223                  * This should really happen after the GPU has finished
224                  * the commands which are about to be submitted. */
225                 drm_gem_object_unreference(obj);
226
227                 gobj = obj->driver_private;
228                 if ( gobj == NULL ) {
229                         printk(KERN_WARNING "[glamo-drm] This object has no"
230                                              " private data!\n");
231                         goto fail;
232                 }
233
234                 addr = GLAMO_OFFSET_FB + gobj->block->start;
235                 addr_low = addr & 0xffff;
236                 addr_high = (addr >> 16) & 0x7f;
237
238                 /* FIXME: Should really check that the register is a
239                  * valid one for this relocation. */
240
241                 *(cmds+(offset/2)+1) = addr_low;
242                 *(cmds+(offset/2)+3) = addr_high;
243
244         }
245
246         drm_free(handles, 1, DRM_MEM_DRIVER);
247         drm_free(offsets, 1, DRM_MEM_DRIVER);
248         return 0;
249
250 fail:
251         drm_free(handles, 1, DRM_MEM_DRIVER);
252         drm_free(offsets, 1, DRM_MEM_DRIVER);
253         return -1;
254 }
255
256
257 /* This is DRM_IOCTL_GLAMO_CMDBUF */
258 int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data,
259                        struct drm_file *file_priv)
260 {
261         int ret = 0;
262         struct glamodrm_handle *gdrm;
263         unsigned int count;
264         drm_glamo_cmd_buffer_t *cbuf = data;
265         u16 *cmds;
266
267         gdrm = dev->dev_private;
268
269         count = cbuf->bufsz;
270
271         if ( count > PAGE_SIZE ) return -EINVAL;
272
273         cmds = drm_alloc(count, DRM_MEM_DRIVER);
274         if ( cmds == NULL ) return -ENOMEM;
275         if ( copy_from_user(cmds, cbuf->buf, count) )   {
276                 printk(KERN_WARNING "[glamo-drm] copy from user failed\n");
277                 ret = -EINVAL;
278                 goto cleanup;
279         }
280
281         /* Check the buffer isn't going to tell Glamo to enact naughtiness */
282         if ( !glamo_sanitize_buffer(cmds, count) ) {
283                 printk(KERN_WARNING "[glamo-drm] sanitize buffer failed\n");
284                 ret = -EINVAL;
285                 goto cleanup;
286         }
287
288         /* Perform relocation, if necessary */
289         if ( cbuf->nobjs ) {
290                 if ( glamo_do_relocation(gdrm, cbuf, cmds, dev, file_priv) )
291                 {
292                         printk(KERN_WARNING "[glamo-drm] Relocation failed\n");
293                         ret = -EINVAL;
294                         goto cleanup;
295                 }
296         }
297
298         glamo_add_to_ring(gdrm, cmds, count);
299
300
301 cleanup:
302         drm_free(cmds, 1, DRM_MEM_DRIVER);
303
304         return ret;
305 }
306
307
308 int glamo_cmdq_init(struct glamodrm_handle *gdrm)
309 {
310         unsigned int i;
311
312         init_MUTEX(&gdrm->add_to_ring);
313
314         /* Enable 2D and 3D */
315         glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_2D);
316         glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D);
317
318         /* Start by zeroing the command queue memory */
319         for ( i=0; i<GLAMO_CMDQ_SIZE; i+=2 ) {
320                 iowrite16(0x0000, gdrm->cmdq_base+i);
321         }
322
323         glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
324         glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
325
326         /* Set up command queue location */
327         reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRL,
328                                         GLAMO_OFFSET_CMDQ & 0xffff);
329         reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRH,
330                                         (GLAMO_OFFSET_CMDQ >> 16) & 0x7f);
331
332         /* Length of command queue in 1k blocks, minus one */
333         reg_write(gdrm, GLAMO_REG_CMDQ_LEN, (GLAMO_CMDQ_SIZE >> 10)-1);
334         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH, 0);
335         reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL, 0);
336         reg_write(gdrm, GLAMO_REG_CMDQ_CONTROL,
337                                          1 << 12 |   /* Turbo flip (?) */
338                                          5 << 8  |   /* no interrupt */
339                                          8 << 4);    /* HQ threshold */
340
341         return 0;
342 }
343
344
345 int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm)
346 {
347         return 0;
348 }
349
350
351 void glamo_cmdq_suspend(struct glamodrm_handle *gdrm)
352 {
353         /* Placeholder... */
354 }
355
356
357 void glamo_cmdq_resume(struct glamodrm_handle *gdrm)
358 {
359         glamo_cmdq_init(gdrm);
360 }