aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-07-19 12:59:32 -0400
committerRob Clark <robdclark@gmail.com>2013-08-24 14:57:18 -0400
commit7198e6b03155f6dadecadba004eb83b81a6ffe4c (patch)
treeed4ae3e859fd9a722524242a145008d13606a95a /drivers/gpu/drm
parent902e6eb851a78ad9e3db006c1e1df71841f633e2 (diff)
drm/msm: add a3xx gpu support
Add initial support for a3xx 3d core. So far, with hardware that I've seen to date, we can have: + zero, one, or two z180 2d cores + a3xx or a2xx 3d core, which share a common CP (the firmware for the CP seems to implement some different PM4 packet types but the basics of cmdstream submission are the same) Which means that the eventual complete "class" hierarchy, once support for all past and present hw is in place, becomes: + msm_gpu + adreno_gpu + a3xx_gpu + a2xx_gpu + z180_gpu This commit splits out the parts that will eventually be common between a2xx/a3xx into adreno_gpu, and the parts that are even common to z180 into msm_gpu. Note that there is no cmdstream validation required. All memory access from the GPU is via IOMMU/MMU. So as long as you don't map silly things to the GPU, there isn't much damage that the GPU can do. Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/msm/Makefile7
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c501
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.h30
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c350
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h142
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c246
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h44
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c84
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h58
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c412
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c411
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h114
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c61
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.h43
14 files changed, 2487 insertions, 16 deletions
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4068122a9377..439dfb5b417b 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -4,6 +4,8 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
4endif 4endif
5 5
6msm-y := \ 6msm-y := \
7 adreno/adreno_gpu.o \
8 adreno/a3xx_gpu.o \
7 hdmi/hdmi.o \ 9 hdmi/hdmi.o \
8 hdmi/hdmi_connector.o \ 10 hdmi/hdmi_connector.o \
9 hdmi/hdmi_i2c.o \ 11 hdmi/hdmi_i2c.o \
@@ -18,7 +20,10 @@ msm-y := \
18 msm_connector.o \ 20 msm_connector.o \
19 msm_drv.o \ 21 msm_drv.o \
20 msm_fb.o \ 22 msm_fb.o \
21 msm_gem.o 23 msm_gem.o \
24 msm_gem_submit.o \
25 msm_gpu.o \
26 msm_ringbuffer.o
22 27
23msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o 28msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
24 29
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
new file mode 100644
index 000000000000..13d61bbed302
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -0,0 +1,501 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "a3xx_gpu.h"
19
20#define A3XX_INT0_MASK \
21 (A3XX_INT0_RBBM_AHB_ERROR | \
22 A3XX_INT0_RBBM_ATB_BUS_OVERFLOW | \
23 A3XX_INT0_CP_T0_PACKET_IN_IB | \
24 A3XX_INT0_CP_OPCODE_ERROR | \
25 A3XX_INT0_CP_RESERVED_BIT_ERROR | \
26 A3XX_INT0_CP_HW_FAULT | \
27 A3XX_INT0_CP_IB1_INT | \
28 A3XX_INT0_CP_IB2_INT | \
29 A3XX_INT0_CP_RB_INT | \
30 A3XX_INT0_CP_REG_PROTECT_FAULT | \
31 A3XX_INT0_CP_AHB_ERROR_HALT | \
32 A3XX_INT0_UCHE_OOB_ACCESS)
33
34static struct platform_device *a3xx_pdev;
35
36static void a3xx_me_init(struct msm_gpu *gpu)
37{
38 struct msm_ringbuffer *ring = gpu->rb;
39
40 OUT_PKT3(ring, CP_ME_INIT, 17);
41 OUT_RING(ring, 0x000003f7);
42 OUT_RING(ring, 0x00000000);
43 OUT_RING(ring, 0x00000000);
44 OUT_RING(ring, 0x00000000);
45 OUT_RING(ring, 0x00000080);
46 OUT_RING(ring, 0x00000100);
47 OUT_RING(ring, 0x00000180);
48 OUT_RING(ring, 0x00006600);
49 OUT_RING(ring, 0x00000150);
50 OUT_RING(ring, 0x0000014e);
51 OUT_RING(ring, 0x00000154);
52 OUT_RING(ring, 0x00000001);
53 OUT_RING(ring, 0x00000000);
54 OUT_RING(ring, 0x00000000);
55 OUT_RING(ring, 0x00000000);
56 OUT_RING(ring, 0x00000000);
57 OUT_RING(ring, 0x00000000);
58
59 gpu->funcs->flush(gpu);
60 gpu->funcs->idle(gpu);
61}
62
63static int a3xx_hw_init(struct msm_gpu *gpu)
64{
65 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
66 uint32_t *ptr, len;
67 int i, ret;
68
69 DBG("%s", gpu->name);
70
71 if (adreno_is_a305(adreno_gpu)) {
72 /* Set up 16 deep read/write request queues: */
73 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
74 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010);
75 gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010);
76 gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010);
77 gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
78 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010);
79 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010);
80 /* Enable WR-REQ: */
81 gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff);
82 /* Set up round robin arbitration between both AXI ports: */
83 gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
84 /* Set up AOOO: */
85 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c);
86 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c);
87
88 } else if (adreno_is_a320(adreno_gpu)) {
89 /* Set up 16 deep read/write request queues: */
90 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
91 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010);
92 gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010);
93 gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010);
94 gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
95 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010);
96 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010);
97 /* Enable WR-REQ: */
98 gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff);
99 /* Set up round robin arbitration between both AXI ports: */
100 gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
101 /* Set up AOOO: */
102 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c);
103 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c);
104 /* Enable 1K sort: */
105 gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x000000ff);
106 gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4);
107
108 } else if (adreno_is_a330(adreno_gpu)) {
109 /* Set up 16 deep read/write request queues: */
110 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x18181818);
111 gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x18181818);
112 gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x18181818);
113 gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x18181818);
114 gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303);
115 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x18181818);
116 gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x18181818);
117 /* Enable WR-REQ: */
118 gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003f);
119 /* Set up round robin arbitration between both AXI ports: */
120 gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030);
121 /* Set up VBIF_ROUND_ROBIN_QOS_ARB: */
122 gpu_write(gpu, REG_A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0001);
123 /* Set up AOOO: */
124 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000ffff);
125 gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0xffffffff);
126 /* Enable 1K sort: */
127 gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001ffff);
128 gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4);
129 /* Disable VBIF clock gating. This is to enable AXI running
130 * higher frequency than GPU:
131 */
132 gpu_write(gpu, REG_A3XX_VBIF_CLKON, 0x00000001);
133
134 } else {
135 BUG();
136 }
137
138 /* Make all blocks contribute to the GPU BUSY perf counter: */
139 gpu_write(gpu, REG_A3XX_RBBM_GPU_BUSY_MASKED, 0xffffffff);
140
141 /* Tune the hystersis counters for SP and CP idle detection: */
142 gpu_write(gpu, REG_A3XX_RBBM_SP_HYST_CNT, 0x10);
143 gpu_write(gpu, REG_A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10);
144
145 /* Enable the RBBM error reporting bits. This lets us get
146 * useful information on failure:
147 */
148 gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL0, 0x00000001);
149
150 /* Enable AHB error reporting: */
151 gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL1, 0xa6ffffff);
152
153 /* Turn on the power counters: */
154 gpu_write(gpu, REG_A3XX_RBBM_RBBM_CTL, 0x00030000);
155
156 /* Turn on hang detection - this spews a lot of useful information
157 * into the RBBM registers on a hang:
158 */
159 gpu_write(gpu, REG_A3XX_RBBM_INTERFACE_HANG_INT_CTL, 0x00010fff);
160
161 /* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0): */
162 gpu_write(gpu, REG_A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
163
164 /* Enable Clock gating: */
165 gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbfffffff);
166
167 /* Set the OCMEM base address for A330 */
168//TODO:
169// if (adreno_is_a330(adreno_gpu)) {
170// gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR,
171// (unsigned int)(a3xx_gpu->ocmem_base >> 14));
172// }
173
174 /* Turn on performance counters: */
175 gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
176
177 /* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
178 * we will use this to augment our hang detection:
179 */
180 gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT,
181 SP_FS_FULL_ALU_INSTRUCTIONS);
182
183 gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
184
185 ret = adreno_hw_init(gpu);
186 if (ret)
187 return ret;
188
189 /* setup access protection: */
190 gpu_write(gpu, REG_A3XX_CP_PROTECT_CTRL, 0x00000007);
191
192 /* RBBM registers */
193 gpu_write(gpu, REG_A3XX_CP_PROTECT(0), 0x63000040);
194 gpu_write(gpu, REG_A3XX_CP_PROTECT(1), 0x62000080);
195 gpu_write(gpu, REG_A3XX_CP_PROTECT(2), 0x600000cc);
196 gpu_write(gpu, REG_A3XX_CP_PROTECT(3), 0x60000108);
197 gpu_write(gpu, REG_A3XX_CP_PROTECT(4), 0x64000140);
198 gpu_write(gpu, REG_A3XX_CP_PROTECT(5), 0x66000400);
199
200 /* CP registers */
201 gpu_write(gpu, REG_A3XX_CP_PROTECT(6), 0x65000700);
202 gpu_write(gpu, REG_A3XX_CP_PROTECT(7), 0x610007d8);
203 gpu_write(gpu, REG_A3XX_CP_PROTECT(8), 0x620007e0);
204 gpu_write(gpu, REG_A3XX_CP_PROTECT(9), 0x61001178);
205 gpu_write(gpu, REG_A3XX_CP_PROTECT(10), 0x64001180);
206
207 /* RB registers */
208 gpu_write(gpu, REG_A3XX_CP_PROTECT(11), 0x60003300);
209
210 /* VBIF registers */
211 gpu_write(gpu, REG_A3XX_CP_PROTECT(12), 0x6b00c000);
212
213 /* NOTE: PM4/micro-engine firmware registers look to be the same
214 * for a2xx and a3xx.. we could possibly push that part down to
215 * adreno_gpu base class. Or push both PM4 and PFP but
216 * parameterize the pfp ucode addr/data registers..
217 */
218
219 /* Load PM4: */
220 ptr = (uint32_t *)(adreno_gpu->pm4->data);
221 len = adreno_gpu->pm4->size / 4;
222 DBG("loading PM4 ucode version: %u", ptr[0]);
223
224 gpu_write(gpu, REG_AXXX_CP_DEBUG,
225 AXXX_CP_DEBUG_DYNAMIC_CLK_DISABLE |
226 AXXX_CP_DEBUG_MIU_128BIT_WRITE_ENABLE);
227 gpu_write(gpu, REG_AXXX_CP_ME_RAM_WADDR, 0);
228 for (i = 1; i < len; i++)
229 gpu_write(gpu, REG_AXXX_CP_ME_RAM_DATA, ptr[i]);
230
231 /* Load PFP: */
232 ptr = (uint32_t *)(adreno_gpu->pfp->data);
233 len = adreno_gpu->pfp->size / 4;
234 DBG("loading PFP ucode version: %u", ptr[0]);
235
236 gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_ADDR, 0);
237 for (i = 1; i < len; i++)
238 gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_DATA, ptr[i]);
239
240 /* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
241 if (adreno_is_a305(adreno_gpu) || adreno_is_a320(adreno_gpu))
242 gpu_write(gpu, REG_AXXX_CP_QUEUE_THRESHOLDS,
243 AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START(2) |
244 AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START(6) |
245 AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(14));
246
247
248 /* clear ME_HALT to start micro engine */
249 gpu_write(gpu, REG_AXXX_CP_ME_CNTL, 0);
250
251 a3xx_me_init(gpu);
252
253 return 0;
254}
255
256static void a3xx_destroy(struct msm_gpu *gpu)
257{
258 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
259 struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(adreno_gpu);
260
261 DBG("%s", gpu->name);
262
263 adreno_gpu_cleanup(adreno_gpu);
264 put_device(&a3xx_gpu->pdev->dev);
265 kfree(a3xx_gpu);
266}
267
268static void a3xx_idle(struct msm_gpu *gpu)
269{
270 unsigned long t;
271
272 /* wait for ringbuffer to drain: */
273 adreno_idle(gpu);
274
275 t = jiffies + ADRENO_IDLE_TIMEOUT;
276
277 /* then wait for GPU to finish: */
278 do {
279 uint32_t rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
280 if (!(rbbm_status & A3XX_RBBM_STATUS_GPU_BUSY))
281 return;
282 } while(time_before(jiffies, t));
283
284 DRM_ERROR("timeout waiting for %s to idle!\n", gpu->name);
285
286 /* TODO maybe we need to reset GPU here to recover from hang? */
287}
288
289static irqreturn_t a3xx_irq(struct msm_gpu *gpu)
290{
291 uint32_t status;
292
293 status = gpu_read(gpu, REG_A3XX_RBBM_INT_0_STATUS);
294 DBG("%s: %08x", gpu->name, status);
295
296 // TODO
297
298 gpu_write(gpu, REG_A3XX_RBBM_INT_CLEAR_CMD, status);
299
300 msm_gpu_retire(gpu);
301
302 return IRQ_HANDLED;
303}
304
305#ifdef CONFIG_DEBUG_FS
306static const unsigned int a3xx_registers[] = {
307 0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027,
308 0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c,
309 0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5,
310 0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1,
311 0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd,
312 0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff,
313 0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f,
314 0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f,
315 0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e,
316 0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f,
317 0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7,
318 0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05,
319 0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65,
320 0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7,
321 0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09,
322 0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069,
323 0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075,
324 0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109,
325 0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115,
326 0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0,
327 0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e,
328 0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8,
329 0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7,
330 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356,
331 0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d,
332 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472,
333 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef,
334 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511,
335 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed,
336 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a,
337 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce,
338 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec,
339 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749,
340 0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d,
341 0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036,
342 0x303c, 0x303c, 0x305e, 0x305f,
343};
344
345static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
346{
347 int i;
348
349 adreno_show(gpu, m);
350 seq_printf(m, "status: %08x\n",
351 gpu_read(gpu, REG_A3XX_RBBM_STATUS));
352
353 /* dump these out in a form that can be parsed by demsm: */
354 seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
355 for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) {
356 uint32_t start = a3xx_registers[i];
357 uint32_t end = a3xx_registers[i+1];
358 uint32_t addr;
359
360 for (addr = start; addr <= end; addr++) {
361 uint32_t val = gpu_read(gpu, addr);
362 seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
363 }
364 }
365}
366#endif
367
368static const struct adreno_gpu_funcs funcs = {
369 .base = {
370 .get_param = adreno_get_param,
371 .hw_init = a3xx_hw_init,
372 .pm_suspend = msm_gpu_pm_suspend,
373 .pm_resume = msm_gpu_pm_resume,
374 .last_fence = adreno_last_fence,
375 .submit = adreno_submit,
376 .flush = adreno_flush,
377 .idle = a3xx_idle,
378 .irq = a3xx_irq,
379 .destroy = a3xx_destroy,
380#ifdef CONFIG_DEBUG_FS
381 .show = a3xx_show,
382#endif
383 },
384};
385
386struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
387{
388 struct a3xx_gpu *a3xx_gpu = NULL;
389 struct msm_gpu *gpu;
390 struct platform_device *pdev = a3xx_pdev;
391 struct adreno_platform_config *config;
392 int ret;
393
394 if (!pdev) {
395 dev_err(dev->dev, "no a3xx device\n");
396 ret = -ENXIO;
397 goto fail;
398 }
399
400 config = pdev->dev.platform_data;
401
402 a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL);
403 if (!a3xx_gpu) {
404 ret = -ENOMEM;
405 goto fail;
406 }
407
408 gpu = &a3xx_gpu->base.base;
409
410 get_device(&pdev->dev);
411 a3xx_gpu->pdev = pdev;
412
413 gpu->fast_rate = config->fast_rate;
414 gpu->slow_rate = config->slow_rate;
415 gpu->bus_freq = config->bus_freq;
416
417 DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
418 gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
419
420 ret = adreno_gpu_init(dev, pdev, &a3xx_gpu->base,
421 &funcs, config->rev);
422 if (ret)
423 goto fail;
424
425 return &a3xx_gpu->base.base;
426
427fail:
428 if (a3xx_gpu)
429 a3xx_destroy(&a3xx_gpu->base.base);
430
431 return ERR_PTR(ret);
432}
433
434/*
435 * The a3xx device:
436 */
437
438static int a3xx_probe(struct platform_device *pdev)
439{
440 static struct adreno_platform_config config = {};
441#ifdef CONFIG_OF
442 /* TODO */
443#else
444 uint32_t version = socinfo_get_version();
445 if (cpu_is_apq8064ab()) {
446 config.fast_rate = 450000000;
447 config.slow_rate = 27000000;
448 config.bus_freq = 4;
449 config.rev = ADRENO_REV(3, 2, 1, 0);
450 } else if (cpu_is_apq8064() || cpu_is_msm8960ab()) {
451 config.fast_rate = 400000000;
452 config.slow_rate = 27000000;
453 config.bus_freq = 4;
454
455 if (SOCINFO_VERSION_MAJOR(version) == 2)
456 config.rev = ADRENO_REV(3, 2, 0, 2);
457 else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
458 (SOCINFO_VERSION_MINOR(version) == 1))
459 config.rev = ADRENO_REV(3, 2, 0, 1);
460 else
461 config.rev = ADRENO_REV(3, 2, 0, 0);
462
463 } else if (cpu_is_msm8930()) {
464 config.fast_rate = 400000000;
465 config.slow_rate = 27000000;
466 config.bus_freq = 3;
467
468 if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
469 (SOCINFO_VERSION_MINOR(version) == 2))
470 config.rev = ADRENO_REV(3, 0, 5, 2);
471 else
472 config.rev = ADRENO_REV(3, 0, 5, 0);
473
474 }
475#endif
476 pdev->dev.platform_data = &config;
477 a3xx_pdev = pdev;
478 return 0;
479}
480
481static int a3xx_remove(struct platform_device *pdev)
482{
483 a3xx_pdev = NULL;
484 return 0;
485}
486
487static struct platform_driver a3xx_driver = {
488 .probe = a3xx_probe,
489 .remove = a3xx_remove,
490 .driver.name = "kgsl-3d0",
491};
492
493void __init a3xx_register(void)
494{
495 platform_driver_register(&a3xx_driver);
496}
497
498void __exit a3xx_unregister(void)
499{
500 platform_driver_unregister(&a3xx_driver);
501}
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
new file mode 100644
index 000000000000..32c398c2d00a
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __A3XX_GPU_H__
19#define __A3XX_GPU_H__
20
21#include "adreno_gpu.h"
22#include "a3xx.xml.h"
23
24struct a3xx_gpu {
25 struct adreno_gpu base;
26 struct platform_device *pdev;
27};
28#define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base)
29
30#endif /* __A3XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
new file mode 100644
index 000000000000..282163ee3fa5
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -0,0 +1,350 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "adreno_gpu.h"
19#include "msm_gem.h"
20
21struct adreno_info {
22 struct adreno_rev rev;
23 uint32_t revn;
24 const char *name;
25 const char *pm4fw, *pfpfw;
26 uint32_t gmem;
27};
28
29#define ANY_ID 0xff
30
31static const struct adreno_info gpulist[] = {
32 {
33 .rev = ADRENO_REV(3, 0, 5, ANY_ID),
34 .revn = 305,
35 .name = "A305",
36 .pm4fw = "a300_pm4.fw",
37 .pfpfw = "a300_pfp.fw",
38 .gmem = SZ_256K,
39 }, {
40 .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID),
41 .revn = 320,
42 .name = "A320",
43 .pm4fw = "a300_pm4.fw",
44 .pfpfw = "a300_pfp.fw",
45 .gmem = SZ_512K,
46 }, {
47 .rev = ADRENO_REV(3, 3, 0, 0),
48 .revn = 330,
49 .name = "A330",
50 .pm4fw = "a330_pm4.fw",
51 .pfpfw = "a330_pfp.fw",
52 .gmem = SZ_1M,
53 },
54};
55
56#define RB_SIZE SZ_32K
57#define RB_BLKSIZE 16
58
59int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
60{
61 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
62
63 switch (param) {
64 case MSM_PARAM_GPU_ID:
65 *value = adreno_gpu->info->revn;
66 return 0;
67 case MSM_PARAM_GMEM_SIZE:
68 *value = adreno_gpu->info->gmem;
69 return 0;
70 default:
71 DBG("%s: invalid param: %u", gpu->name, param);
72 return -EINVAL;
73 }
74}
75
76#define rbmemptr(adreno_gpu, member) \
77 ((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member))
78
79int adreno_hw_init(struct msm_gpu *gpu)
80{
81 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
82
83 DBG("%s", gpu->name);
84
85 /* Setup REG_CP_RB_CNTL: */
86 gpu_write(gpu, REG_AXXX_CP_RB_CNTL,
87 /* size is log2(quad-words): */
88 AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) |
89 AXXX_CP_RB_CNTL_BLKSZ(RB_BLKSIZE));
90
91 /* Setup ringbuffer address: */
92 gpu_write(gpu, REG_AXXX_CP_RB_BASE, gpu->rb_iova);
93 gpu_write(gpu, REG_AXXX_CP_RB_RPTR_ADDR, rbmemptr(adreno_gpu, rptr));
94
95 /* Setup scratch/timestamp: */
96 gpu_write(gpu, REG_AXXX_SCRATCH_ADDR, rbmemptr(adreno_gpu, fence));
97
98 gpu_write(gpu, REG_AXXX_SCRATCH_UMSK, 0x1);
99
100 return 0;
101}
102
103static uint32_t get_wptr(struct msm_ringbuffer *ring)
104{
105 return ring->cur - ring->start;
106}
107
108uint32_t adreno_last_fence(struct msm_gpu *gpu)
109{
110 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
111 return adreno_gpu->memptrs->fence;
112}
113
114int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
115 struct msm_file_private *ctx)
116{
117 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
118 struct msm_drm_private *priv = gpu->dev->dev_private;
119 struct msm_ringbuffer *ring = gpu->rb;
120 unsigned i, ibs = 0;
121
122 adreno_gpu->last_fence = submit->fence;
123
124 for (i = 0; i < submit->nr_cmds; i++) {
125 switch (submit->cmd[i].type) {
126 case MSM_SUBMIT_CMD_IB_TARGET_BUF:
127 /* ignore IB-targets */
128 break;
129 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
130 /* ignore if there has not been a ctx switch: */
131 if (priv->lastctx == ctx)
132 break;
133 case MSM_SUBMIT_CMD_BUF:
134 OUT_PKT3(ring, CP_INDIRECT_BUFFER_PFD, 2);
135 OUT_RING(ring, submit->cmd[i].iova);
136 OUT_RING(ring, submit->cmd[i].size);
137 ibs++;
138 break;
139 }
140 }
141
142 /* on a320, at least, we seem to need to pad things out to an
143 * even number of qwords to avoid issue w/ CP hanging on wrap-
144 * around:
145 */
146 if (ibs % 2)
147 OUT_PKT2(ring);
148
149 OUT_PKT0(ring, REG_AXXX_CP_SCRATCH_REG2, 1);
150 OUT_RING(ring, submit->fence);
151
152 if (adreno_is_a3xx(adreno_gpu)) {
153 /* Flush HLSQ lazy updates to make sure there is nothing
154 * pending for indirect loads after the timestamp has
155 * passed:
156 */
157 OUT_PKT3(ring, CP_EVENT_WRITE, 1);
158 OUT_RING(ring, HLSQ_FLUSH);
159
160 OUT_PKT3(ring, CP_WAIT_FOR_IDLE, 1);
161 OUT_RING(ring, 0x00000000);
162 }
163
164 OUT_PKT3(ring, CP_EVENT_WRITE, 3);
165 OUT_RING(ring, CACHE_FLUSH_TS);
166 OUT_RING(ring, rbmemptr(adreno_gpu, fence));
167 OUT_RING(ring, submit->fence);
168
169 /* we could maybe be clever and only CP_COND_EXEC the interrupt: */
170 OUT_PKT3(ring, CP_INTERRUPT, 1);
171 OUT_RING(ring, 0x80000000);
172
173#if 0
174 if (adreno_is_a3xx(adreno_gpu)) {
175 /* Dummy set-constant to trigger context rollover */
176 OUT_PKT3(ring, CP_SET_CONSTANT, 2);
177 OUT_RING(ring, CP_REG(REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG));
178 OUT_RING(ring, 0x00000000);
179 }
180#endif
181
182 gpu->funcs->flush(gpu);
183
184 return 0;
185}
186
187void adreno_flush(struct msm_gpu *gpu)
188{
189 uint32_t wptr = get_wptr(gpu->rb);
190
191 /* ensure writes to ringbuffer have hit system memory: */
192 mb();
193
194 gpu_write(gpu, REG_AXXX_CP_RB_WPTR, wptr);
195}
196
197void adreno_idle(struct msm_gpu *gpu)
198{
199 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
200 uint32_t rptr, wptr = get_wptr(gpu->rb);
201 unsigned long t;
202
203 t = jiffies + ADRENO_IDLE_TIMEOUT;
204
205 /* then wait for CP to drain ringbuffer: */
206 do {
207 rptr = adreno_gpu->memptrs->rptr;
208 if (rptr == wptr)
209 return;
210 } while(time_before(jiffies, t));
211
212 DRM_ERROR("timeout waiting for %s to drain ringbuffer!\n", gpu->name);
213
214 /* TODO maybe we need to reset GPU here to recover from hang? */
215}
216
217#ifdef CONFIG_DEBUG_FS
218void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
219{
220 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
221
222 seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
223 adreno_gpu->info->revn, adreno_gpu->rev.core,
224 adreno_gpu->rev.major, adreno_gpu->rev.minor,
225 adreno_gpu->rev.patchid);
226
227 seq_printf(m, "fence: %d/%d\n", adreno_gpu->memptrs->fence,
228 adreno_gpu->last_fence);
229 seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr);
230 seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr);
231 seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
232}
233#endif
234
235void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
236{
237 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
238 uint32_t freedwords;
239 do {
240 uint32_t size = gpu->rb->size / 4;
241 uint32_t wptr = get_wptr(gpu->rb);
242 uint32_t rptr = adreno_gpu->memptrs->rptr;
243 freedwords = (rptr + (size - 1) - wptr) % size;
244 } while(freedwords < ndwords);
245}
246
247static const char *iommu_ports[] = {
248 "gfx3d_user", "gfx3d_priv",
249 "gfx3d1_user", "gfx3d1_priv",
250};
251
252static inline bool _rev_match(uint8_t entry, uint8_t id)
253{
254 return (entry == ANY_ID) || (entry == id);
255}
256
257int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
258 struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
259 struct adreno_rev rev)
260{
261 int i, ret;
262
263 /* identify gpu: */
264 for (i = 0; i < ARRAY_SIZE(gpulist); i++) {
265 const struct adreno_info *info = &gpulist[i];
266 if (_rev_match(info->rev.core, rev.core) &&
267 _rev_match(info->rev.major, rev.major) &&
268 _rev_match(info->rev.minor, rev.minor) &&
269 _rev_match(info->rev.patchid, rev.patchid)) {
270 gpu->info = info;
271 gpu->revn = info->revn;
272 break;
273 }
274 }
275
276 if (i == ARRAY_SIZE(gpulist)) {
277 dev_err(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n",
278 rev.core, rev.major, rev.minor, rev.patchid);
279 return -ENXIO;
280 }
281
282 DBG("Found GPU: %s (%u.%u.%u.%u)", gpu->info->name,
283 rev.core, rev.major, rev.minor, rev.patchid);
284
285 gpu->funcs = funcs;
286 gpu->rev = rev;
287
288 ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev);
289 if (ret) {
290 dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n",
291 gpu->info->pm4fw, ret);
292 return ret;
293 }
294
295 ret = request_firmware(&gpu->pfp, gpu->info->pfpfw, drm->dev);
296 if (ret) {
297 dev_err(drm->dev, "failed to load %s PFP firmware: %d\n",
298 gpu->info->pfpfw, ret);
299 return ret;
300 }
301
302 ret = msm_gpu_init(drm, pdev, &gpu->base, &funcs->base,
303 gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq",
304 RB_SIZE);
305 if (ret)
306 return ret;
307
308 ret = msm_iommu_attach(drm, gpu->base.iommu,
309 iommu_ports, ARRAY_SIZE(iommu_ports));
310 if (ret)
311 return ret;
312
313 gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),
314 MSM_BO_UNCACHED);
315 if (IS_ERR(gpu->memptrs_bo)) {
316 ret = PTR_ERR(gpu->memptrs_bo);
317 gpu->memptrs_bo = NULL;
318 dev_err(drm->dev, "could not allocate memptrs: %d\n", ret);
319 return ret;
320 }
321
322 gpu->memptrs = msm_gem_vaddr_locked(gpu->memptrs_bo);
323 if (!gpu->memptrs) {
324 dev_err(drm->dev, "could not vmap memptrs\n");
325 return -ENOMEM;
326 }
327
328 ret = msm_gem_get_iova_locked(gpu->memptrs_bo, gpu->base.id,
329 &gpu->memptrs_iova);
330 if (ret) {
331 dev_err(drm->dev, "could not map memptrs: %d\n", ret);
332 return ret;
333 }
334
335 return 0;
336}
337
338void adreno_gpu_cleanup(struct adreno_gpu *gpu)
339{
340 if (gpu->memptrs_bo) {
341 if (gpu->memptrs_iova)
342 msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id);
343 drm_gem_object_unreference(gpu->memptrs_bo);
344 }
345 if (gpu->pm4)
346 release_firmware(gpu->pm4);
347 if (gpu->pfp)
348 release_firmware(gpu->pfp);
349 msm_gpu_cleanup(&gpu->base);
350}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
new file mode 100644
index 000000000000..6b49c4f27fec
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -0,0 +1,142 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __ADRENO_GPU_H__
19#define __ADRENO_GPU_H__
20
21#include <linux/firmware.h>
22
23#include "msm_gpu.h"
24
25#include "adreno_common.xml.h"
26#include "adreno_pm4.xml.h"
27
28struct adreno_rev {
29 uint8_t core;
30 uint8_t major;
31 uint8_t minor;
32 uint8_t patchid;
33};
34
35#define ADRENO_REV(core, major, minor, patchid) \
36 ((struct adreno_rev){ core, major, minor, patchid })
37
38struct adreno_gpu_funcs {
39 struct msm_gpu_funcs base;
40};
41
42struct adreno_info;
43
44struct adreno_rbmemptrs {
45 volatile uint32_t rptr;
46 volatile uint32_t wptr;
47 volatile uint32_t fence;
48};
49
50struct adreno_gpu {
51 struct msm_gpu base;
52 struct adreno_rev rev;
53 const struct adreno_info *info;
54 uint32_t revn; /* numeric revision name */
55 const struct adreno_gpu_funcs *funcs;
56
57 uint32_t last_fence;
58
59 /* firmware: */
60 const struct firmware *pm4, *pfp;
61
62 /* ringbuffer rptr/wptr: */
63 // TODO should this be in msm_ringbuffer? I think it would be
64 // different for z180..
65 struct adreno_rbmemptrs *memptrs;
66 struct drm_gem_object *memptrs_bo;
67 uint32_t memptrs_iova;
68};
69#define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base)
70
71/* platform config data (ie. from DT, or pdata) */
72struct adreno_platform_config {
73 struct adreno_rev rev;
74 uint32_t fast_rate, slow_rate, bus_freq;
75};
76
77#define ADRENO_IDLE_TIMEOUT (20 * 1000)
78
79static inline bool adreno_is_a3xx(struct adreno_gpu *gpu)
80{
81 return (gpu->revn >= 300) && (gpu->revn < 400);
82}
83
84static inline bool adreno_is_a305(struct adreno_gpu *gpu)
85{
86 return gpu->revn == 305;
87}
88
89static inline bool adreno_is_a320(struct adreno_gpu *gpu)
90{
91 return gpu->revn == 320;
92}
93
94static inline bool adreno_is_a330(struct adreno_gpu *gpu)
95{
96 return gpu->revn == 330;
97}
98
99int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
100int adreno_hw_init(struct msm_gpu *gpu);
101uint32_t adreno_last_fence(struct msm_gpu *gpu);
102int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
103 struct msm_file_private *ctx);
104void adreno_flush(struct msm_gpu *gpu);
105void adreno_idle(struct msm_gpu *gpu);
106#ifdef CONFIG_DEBUG_FS
107void adreno_show(struct msm_gpu *gpu, struct seq_file *m);
108#endif
109void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);
110
111int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
112 struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
113 struct adreno_rev rev);
114void adreno_gpu_cleanup(struct adreno_gpu *gpu);
115
116
117/* ringbuffer helpers (the parts that are adreno specific) */
118
119static inline void
120OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
121{
122 adreno_wait_ring(ring->gpu, cnt+1);
123 OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF));
124}
125
126/* no-op packet: */
127static inline void
128OUT_PKT2(struct msm_ringbuffer *ring)
129{
130 adreno_wait_ring(ring->gpu, 1);
131 OUT_RING(ring, CP_TYPE2_PKT);
132}
133
134static inline void
135OUT_PKT3(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
136{
137 adreno_wait_ring(ring->gpu, cnt+1);
138 OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8));
139}
140
141
142#endif /* __ADRENO_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index b5ae0dbe1eb8..864c9773636b 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18#include "msm_drv.h" 18#include "msm_drv.h"
19#include "msm_gpu.h"
19 20
20#include <mach/iommu.h> 21#include <mach/iommu.h>
21 22
@@ -135,6 +136,7 @@ static int msm_unload(struct drm_device *dev)
135{ 136{
136 struct msm_drm_private *priv = dev->dev_private; 137 struct msm_drm_private *priv = dev->dev_private;
137 struct msm_kms *kms = priv->kms; 138 struct msm_kms *kms = priv->kms;
139 struct msm_gpu *gpu = priv->gpu;
138 140
139 drm_kms_helper_poll_fini(dev); 141 drm_kms_helper_poll_fini(dev);
140 drm_mode_config_cleanup(dev); 142 drm_mode_config_cleanup(dev);
@@ -152,6 +154,12 @@ static int msm_unload(struct drm_device *dev)
152 kms->funcs->destroy(kms); 154 kms->funcs->destroy(kms);
153 } 155 }
154 156
157 if (gpu) {
158 mutex_lock(&dev->struct_mutex);
159 gpu->funcs->pm_suspend(gpu);
160 gpu->funcs->destroy(gpu);
161 mutex_unlock(&dev->struct_mutex);
162 }
155 163
156 dev->dev_private = NULL; 164 dev->dev_private = NULL;
157 165
@@ -176,6 +184,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
176 dev->dev_private = priv; 184 dev->dev_private = priv;
177 185
178 priv->wq = alloc_ordered_workqueue("msm", 0); 186 priv->wq = alloc_ordered_workqueue("msm", 0);
187 init_waitqueue_head(&priv->fence_event);
179 188
180 INIT_LIST_HEAD(&priv->inactive_list); 189 INIT_LIST_HEAD(&priv->inactive_list);
181 190
@@ -240,12 +249,70 @@ fail:
240 return ret; 249 return ret;
241} 250}
242 251
252static void load_gpu(struct drm_device *dev)
253{
254 struct msm_drm_private *priv = dev->dev_private;
255 struct msm_gpu *gpu;
256
257 if (priv->gpu)
258 return;
259
260 mutex_lock(&dev->struct_mutex);
261 gpu = a3xx_gpu_init(dev);
262 if (IS_ERR(gpu)) {
263 dev_warn(dev->dev, "failed to load a3xx gpu\n");
264 gpu = NULL;
265 /* not fatal */
266 }
267 mutex_unlock(&dev->struct_mutex);
268
269 if (gpu) {
270 int ret;
271 gpu->funcs->pm_resume(gpu);
272 ret = gpu->funcs->hw_init(gpu);
273 if (ret) {
274 dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
275 gpu->funcs->destroy(gpu);
276 gpu = NULL;
277 }
278 }
279
280 priv->gpu = gpu;
281}
282
283static int msm_open(struct drm_device *dev, struct drm_file *file)
284{
285 struct msm_file_private *ctx;
286
287 /* For now, load gpu on open.. to avoid the requirement of having
288 * firmware in the initrd.
289 */
290 load_gpu(dev);
291
292 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
293 if (!ctx)
294 return -ENOMEM;
295
296 file->driver_priv = ctx;
297
298 return 0;
299}
300
243static void msm_preclose(struct drm_device *dev, struct drm_file *file) 301static void msm_preclose(struct drm_device *dev, struct drm_file *file)
244{ 302{
245 struct msm_drm_private *priv = dev->dev_private; 303 struct msm_drm_private *priv = dev->dev_private;
304 struct msm_file_private *ctx = file->driver_priv;
246 struct msm_kms *kms = priv->kms; 305 struct msm_kms *kms = priv->kms;
306
247 if (kms) 307 if (kms)
248 kms->funcs->preclose(kms, file); 308 kms->funcs->preclose(kms, file);
309
310 mutex_lock(&dev->struct_mutex);
311 if (ctx == priv->lastctx)
312 priv->lastctx = NULL;
313 mutex_unlock(&dev->struct_mutex);
314
315 kfree(ctx);
249} 316}
250 317
251static void msm_lastclose(struct drm_device *dev) 318static void msm_lastclose(struct drm_device *dev)
@@ -316,11 +383,30 @@ static void msm_disable_vblank(struct drm_device *dev, int crtc_id)
316 */ 383 */
317 384
318#ifdef CONFIG_DEBUG_FS 385#ifdef CONFIG_DEBUG_FS
386static int msm_gpu_show(struct drm_device *dev, struct seq_file *m)
387{
388 struct msm_drm_private *priv = dev->dev_private;
389 struct msm_gpu *gpu = priv->gpu;
390
391 if (gpu) {
392 seq_printf(m, "%s Status:\n", gpu->name);
393 gpu->funcs->show(gpu, m);
394 }
395
396 return 0;
397}
398
319static int msm_gem_show(struct drm_device *dev, struct seq_file *m) 399static int msm_gem_show(struct drm_device *dev, struct seq_file *m)
320{ 400{
321 struct msm_drm_private *priv = dev->dev_private; 401 struct msm_drm_private *priv = dev->dev_private;
402 struct msm_gpu *gpu = priv->gpu;
403
404 if (gpu) {
405 seq_printf(m, "Active Objects (%s):\n", gpu->name);
406 msm_gem_describe_objects(&gpu->active_list, m);
407 }
322 408
323 seq_printf(m, "All Objects:\n"); 409 seq_printf(m, "Inactive Objects:\n");
324 msm_gem_describe_objects(&priv->inactive_list, m); 410 msm_gem_describe_objects(&priv->inactive_list, m);
325 411
326 return 0; 412 return 0;
@@ -375,6 +461,7 @@ static int show_locked(struct seq_file *m, void *arg)
375} 461}
376 462
377static struct drm_info_list msm_debugfs_list[] = { 463static struct drm_info_list msm_debugfs_list[] = {
464 {"gpu", show_locked, 0, msm_gpu_show},
378 {"gem", show_locked, 0, msm_gem_show}, 465 {"gem", show_locked, 0, msm_gem_show},
379 { "mm", show_locked, 0, msm_mm_show }, 466 { "mm", show_locked, 0, msm_mm_show },
380 { "fb", show_locked, 0, msm_fb_show }, 467 { "fb", show_locked, 0, msm_fb_show },
@@ -404,6 +491,158 @@ static void msm_debugfs_cleanup(struct drm_minor *minor)
404} 491}
405#endif 492#endif
406 493
494/*
495 * Fences:
496 */
497
498int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
499 struct timespec *timeout)
500{
501 struct msm_drm_private *priv = dev->dev_private;
502 unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
503 unsigned long start_jiffies = jiffies;
504 unsigned long remaining_jiffies;
505 int ret;
506
507 if (time_after(start_jiffies, timeout_jiffies))
508 remaining_jiffies = 0;
509 else
510 remaining_jiffies = timeout_jiffies - start_jiffies;
511
512 ret = wait_event_interruptible_timeout(priv->fence_event,
513 priv->completed_fence >= fence,
514 remaining_jiffies);
515 if (ret == 0) {
516 DBG("timeout waiting for fence: %u (completed: %u)",
517 fence, priv->completed_fence);
518 ret = -ETIMEDOUT;
519 } else if (ret != -ERESTARTSYS) {
520 ret = 0;
521 }
522
523 return ret;
524}
525
526/* call under struct_mutex */
527void msm_update_fence(struct drm_device *dev, uint32_t fence)
528{
529 struct msm_drm_private *priv = dev->dev_private;
530
531 if (fence > priv->completed_fence) {
532 priv->completed_fence = fence;
533 wake_up_all(&priv->fence_event);
534 }
535}
536
537/*
538 * DRM ioctls:
539 */
540
541static int msm_ioctl_get_param(struct drm_device *dev, void *data,
542 struct drm_file *file)
543{
544 struct msm_drm_private *priv = dev->dev_private;
545 struct drm_msm_param *args = data;
546 struct msm_gpu *gpu;
547
548 /* for now, we just have 3d pipe.. eventually this would need to
549 * be more clever to dispatch to appropriate gpu module:
550 */
551 if (args->pipe != MSM_PIPE_3D0)
552 return -EINVAL;
553
554 gpu = priv->gpu;
555
556 if (!gpu)
557 return -ENXIO;
558
559 return gpu->funcs->get_param(gpu, args->param, &args->value);
560}
561
562static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
563 struct drm_file *file)
564{
565 struct drm_msm_gem_new *args = data;
566 return msm_gem_new_handle(dev, file, args->size,
567 args->flags, &args->handle);
568}
569
570#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec })
571
572static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
573 struct drm_file *file)
574{
575 struct drm_msm_gem_cpu_prep *args = data;
576 struct drm_gem_object *obj;
577 int ret;
578
579 obj = drm_gem_object_lookup(dev, file, args->handle);
580 if (!obj)
581 return -ENOENT;
582
583 ret = msm_gem_cpu_prep(obj, args->op, &TS(args->timeout));
584
585 drm_gem_object_unreference_unlocked(obj);
586
587 return ret;
588}
589
590static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
591 struct drm_file *file)
592{
593 struct drm_msm_gem_cpu_fini *args = data;
594 struct drm_gem_object *obj;
595 int ret;
596
597 obj = drm_gem_object_lookup(dev, file, args->handle);
598 if (!obj)
599 return -ENOENT;
600
601 ret = msm_gem_cpu_fini(obj);
602
603 drm_gem_object_unreference_unlocked(obj);
604
605 return ret;
606}
607
608static int msm_ioctl_gem_info(struct drm_device *dev, void *data,
609 struct drm_file *file)
610{
611 struct drm_msm_gem_info *args = data;
612 struct drm_gem_object *obj;
613 int ret = 0;
614
615 if (args->pad)
616 return -EINVAL;
617
618 obj = drm_gem_object_lookup(dev, file, args->handle);
619 if (!obj)
620 return -ENOENT;
621
622 args->offset = msm_gem_mmap_offset(obj);
623
624 drm_gem_object_unreference_unlocked(obj);
625
626 return ret;
627}
628
629static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
630 struct drm_file *file)
631{
632 struct drm_msm_wait_fence *args = data;
633 return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout));
634}
635
636static const struct drm_ioctl_desc msm_ioctls[] = {
637 DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
638 DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
639 DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
640 DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
641 DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
642 DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH),
643 DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH),
644};
645
407static const struct vm_operations_struct vm_ops = { 646static const struct vm_operations_struct vm_ops = {
408 .fault = msm_gem_fault, 647 .fault = msm_gem_fault,
409 .open = drm_gem_vm_open, 648 .open = drm_gem_vm_open,
@@ -428,6 +667,7 @@ static struct drm_driver msm_driver = {
428 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, 667 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
429 .load = msm_load, 668 .load = msm_load,
430 .unload = msm_unload, 669 .unload = msm_unload,
670 .open = msm_open,
431 .preclose = msm_preclose, 671 .preclose = msm_preclose,
432 .lastclose = msm_lastclose, 672 .lastclose = msm_lastclose,
433 .irq_handler = msm_irq, 673 .irq_handler = msm_irq,
@@ -446,6 +686,8 @@ static struct drm_driver msm_driver = {
446 .debugfs_init = msm_debugfs_init, 686 .debugfs_init = msm_debugfs_init,
447 .debugfs_cleanup = msm_debugfs_cleanup, 687 .debugfs_cleanup = msm_debugfs_cleanup,
448#endif 688#endif
689 .ioctls = msm_ioctls,
690 .num_ioctls = DRM_MSM_NUM_IOCTLS,
449 .fops = &fops, 691 .fops = &fops,
450 .name = "msm", 692 .name = "msm",
451 .desc = "MSM Snapdragon DRM", 693 .desc = "MSM Snapdragon DRM",
@@ -514,6 +756,7 @@ static int __init msm_drm_register(void)
514{ 756{
515 DBG("init"); 757 DBG("init");
516 hdmi_register(); 758 hdmi_register();
759 a3xx_register();
517 return platform_driver_register(&msm_platform_driver); 760 return platform_driver_register(&msm_platform_driver);
518} 761}
519 762
@@ -522,6 +765,7 @@ static void __exit msm_drm_unregister(void)
522 DBG("fini"); 765 DBG("fini");
523 platform_driver_unregister(&msm_platform_driver); 766 platform_driver_unregister(&msm_platform_driver);
524 hdmi_unregister(); 767 hdmi_unregister();
768 a3xx_unregister();
525} 769}
526 770
527module_init(msm_drm_register); 771module_init(msm_drm_register);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 36f8ba2f5c84..34c36b2911d9 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -40,17 +40,34 @@
40#include <drm/drmP.h> 40#include <drm/drmP.h>
41#include <drm/drm_crtc_helper.h> 41#include <drm/drm_crtc_helper.h>
42#include <drm/drm_fb_helper.h> 42#include <drm/drm_fb_helper.h>
43#include <drm/msm_drm.h>
43 44
44struct msm_kms; 45struct msm_kms;
46struct msm_gpu;
45 47
46#define NUM_DOMAINS 1 /* one for KMS, then one per gpu core (?) */ 48#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
49
50struct msm_file_private {
51 /* currently we don't do anything useful with this.. but when
52 * per-context address spaces are supported we'd keep track of
53 * the context's page-tables here.
54 */
55 int dummy;
56};
47 57
48struct msm_drm_private { 58struct msm_drm_private {
49 59
50 struct msm_kms *kms; 60 struct msm_kms *kms;
51 61
62 /* when we have more than one 'msm_gpu' these need to be an array: */
63 struct msm_gpu *gpu;
64 struct msm_file_private *lastctx;
65
52 struct drm_fb_helper *fbdev; 66 struct drm_fb_helper *fbdev;
53 67
68 uint32_t next_fence, completed_fence;
69 wait_queue_head_t fence_event;
70
54 /* list of GEM objects: */ 71 /* list of GEM objects: */
55 struct list_head inactive_list; 72 struct list_head inactive_list;
56 73
@@ -108,6 +125,13 @@ int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu);
108int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu, 125int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
109 const char **names, int cnt); 126 const char **names, int cnt);
110 127
128int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
129 struct timespec *timeout);
130void msm_update_fence(struct drm_device *dev, uint32_t fence);
131
132int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
133 struct drm_file *file);
134
111int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); 135int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
112int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); 136int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
113uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); 137uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
@@ -125,6 +149,12 @@ void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
125void *msm_gem_vaddr(struct drm_gem_object *obj); 149void *msm_gem_vaddr(struct drm_gem_object *obj);
126int msm_gem_queue_inactive_work(struct drm_gem_object *obj, 150int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
127 struct work_struct *work); 151 struct work_struct *work);
152void msm_gem_move_to_active(struct drm_gem_object *obj,
153 struct msm_gpu *gpu, uint32_t fence);
154void msm_gem_move_to_inactive(struct drm_gem_object *obj);
155int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
156 struct timespec *timeout);
157int msm_gem_cpu_fini(struct drm_gem_object *obj);
128void msm_gem_free_object(struct drm_gem_object *obj); 158void msm_gem_free_object(struct drm_gem_object *obj);
129int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, 159int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
130 uint32_t size, uint32_t flags, uint32_t *handle); 160 uint32_t size, uint32_t flags, uint32_t *handle);
@@ -168,20 +198,14 @@ static inline int align_pitch(int width, int bpp)
168 198
169/* for the generated headers: */ 199/* for the generated headers: */
170#define INVALID_IDX(idx) ({BUG(); 0;}) 200#define INVALID_IDX(idx) ({BUG(); 0;})
201#define fui(x) ({BUG(); 0;})
202#define util_float_to_half(x) ({BUG(); 0;})
203
171 204
172#define FIELD(val, name) (((val) & name ## __MASK) >> name ## __SHIFT) 205#define FIELD(val, name) (((val) & name ## __MASK) >> name ## __SHIFT)
173 206
174/* for conditionally setting boolean flag(s): */ 207/* for conditionally setting boolean flag(s): */
175#define COND(bool, val) ((bool) ? (val) : 0) 208#define COND(bool, val) ((bool) ? (val) : 0)
176 209
177/* just put these here until we start adding driver private ioctls: */
178// TODO might shuffle these around.. just need something for now..
179#define MSM_BO_CACHE_MASK 0x0000000f
180#define MSM_BO_SCANOUT 0x00010000 /* scanout capable */
181
182#define MSM_BO_CACHED 0x00000001 /* default */
183#define MSM_BO_WC 0x0000002
184#define MSM_BO_UNCACHED 0x00000004
185
186 210
187#endif /* __MSM_DRV_H__ */ 211#endif /* __MSM_DRV_H__ */
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index a52e6cca8403..6b5a6c8c7658 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -20,6 +20,7 @@
20 20
21#include "msm_drv.h" 21#include "msm_drv.h"
22#include "msm_gem.h" 22#include "msm_gem.h"
23#include "msm_gpu.h"
23 24
24 25
25/* called with dev->struct_mutex held */ 26/* called with dev->struct_mutex held */
@@ -375,10 +376,74 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
375{ 376{
376 struct drm_device *dev = obj->dev; 377 struct drm_device *dev = obj->dev;
377 struct msm_drm_private *priv = dev->dev_private; 378 struct msm_drm_private *priv = dev->dev_private;
379 struct msm_gem_object *msm_obj = to_msm_bo(obj);
380 int ret = 0;
381
382 mutex_lock(&dev->struct_mutex);
383 if (!list_empty(&work->entry)) {
384 ret = -EINVAL;
385 } else if (is_active(msm_obj)) {
386 list_add_tail(&work->entry, &msm_obj->inactive_work);
387 } else {
388 queue_work(priv->wq, work);
389 }
390 mutex_unlock(&dev->struct_mutex);
391
392 return ret;
393}
394
395void msm_gem_move_to_active(struct drm_gem_object *obj,
396 struct msm_gpu *gpu, uint32_t fence)
397{
398 struct msm_gem_object *msm_obj = to_msm_bo(obj);
399 msm_obj->gpu = gpu;
400 msm_obj->fence = fence;
401 list_del_init(&msm_obj->mm_list);
402 list_add_tail(&msm_obj->mm_list, &gpu->active_list);
403}
404
405void msm_gem_move_to_inactive(struct drm_gem_object *obj)
406{
407 struct drm_device *dev = obj->dev;
408 struct msm_drm_private *priv = dev->dev_private;
409 struct msm_gem_object *msm_obj = to_msm_bo(obj);
410
411 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
412
413 msm_obj->gpu = NULL;
414 msm_obj->fence = 0;
415 list_del_init(&msm_obj->mm_list);
416 list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
417
418 while (!list_empty(&msm_obj->inactive_work)) {
419 struct work_struct *work;
420
421 work = list_first_entry(&msm_obj->inactive_work,
422 struct work_struct, entry);
423
424 list_del_init(&work->entry);
425 queue_work(priv->wq, work);
426 }
427}
428
429int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
430 struct timespec *timeout)
431{
432 struct drm_device *dev = obj->dev;
433 struct msm_gem_object *msm_obj = to_msm_bo(obj);
434 int ret = 0;
435
436 if (is_active(msm_obj) && !(op & MSM_PREP_NOSYNC))
437 ret = msm_wait_fence_interruptable(dev, msm_obj->fence, timeout);
438
439 /* TODO cache maintenance */
378 440
379 /* just a place-holder until we have gpu.. */ 441 return ret;
380 queue_work(priv->wq, work); 442}
381 443
444int msm_gem_cpu_fini(struct drm_gem_object *obj)
445{
446 /* TODO cache maintenance */
382 return 0; 447 return 0;
383} 448}
384 449
@@ -390,8 +455,9 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
390 uint64_t off = drm_vma_node_start(&obj->vma_node); 455 uint64_t off = drm_vma_node_start(&obj->vma_node);
391 456
392 WARN_ON(!mutex_is_locked(&dev->struct_mutex)); 457 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
393 seq_printf(m, "%08x: %2d (%2d) %08llx %p %d\n", 458 seq_printf(m, "%08x: %c(%d) %2d (%2d) %08llx %p %d\n",
394 msm_obj->flags, obj->name, obj->refcount.refcount.counter, 459 msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
460 msm_obj->fence, obj->name, obj->refcount.refcount.counter,
395 off, msm_obj->vaddr, obj->size); 461 off, msm_obj->vaddr, obj->size);
396} 462}
397 463
@@ -421,6 +487,9 @@ void msm_gem_free_object(struct drm_gem_object *obj)
421 487
422 WARN_ON(!mutex_is_locked(&dev->struct_mutex)); 488 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
423 489
490 /* object should not be on active list: */
491 WARN_ON(is_active(msm_obj));
492
424 list_del(&msm_obj->mm_list); 493 list_del(&msm_obj->mm_list);
425 494
426 for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { 495 for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) {
@@ -439,6 +508,9 @@ void msm_gem_free_object(struct drm_gem_object *obj)
439 508
440 put_pages(obj); 509 put_pages(obj);
441 510
511 if (msm_obj->resv == &msm_obj->_resv)
512 reservation_object_fini(msm_obj->resv);
513
442 drm_gem_object_release(obj); 514 drm_gem_object_release(obj);
443 515
444 kfree(msm_obj); 516 kfree(msm_obj);
@@ -508,7 +580,11 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
508 580
509 msm_obj->flags = flags; 581 msm_obj->flags = flags;
510 582
583 msm_obj->resv = &msm_obj->_resv;
584 reservation_object_init(msm_obj->resv);
511 585
586 INIT_LIST_HEAD(&msm_obj->submit_entry);
587 INIT_LIST_HEAD(&msm_obj->inactive_work);
512 list_add_tail(&msm_obj->mm_list, &priv->inactive_list); 588 list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
513 589
514 return obj; 590 return obj;
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index fcafd1965151..d746f13d283c 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -18,6 +18,7 @@
18#ifndef __MSM_GEM_H__ 18#ifndef __MSM_GEM_H__
19#define __MSM_GEM_H__ 19#define __MSM_GEM_H__
20 20
21#include <linux/reservation.h>
21#include "msm_drv.h" 22#include "msm_drv.h"
22 23
23struct msm_gem_object { 24struct msm_gem_object {
@@ -25,7 +26,27 @@ struct msm_gem_object {
25 26
26 uint32_t flags; 27 uint32_t flags;
27 28
29 /* And object is either:
30 * inactive - on priv->inactive_list
31 * active - on one one of the gpu's active_list.. well, at
32 * least for now we don't have (I don't think) hw sync between
33 * 2d and 3d one devices which have both, meaning we need to
34 * block on submit if a bo is already on other ring
35 *
36 */
28 struct list_head mm_list; 37 struct list_head mm_list;
38 struct msm_gpu *gpu; /* non-null if active */
39 uint32_t fence;
40
41 /* Transiently in the process of submit ioctl, objects associated
42 * with the submit are on submit->bo_list.. this only lasts for
43 * the duration of the ioctl, so one bo can never be on multiple
44 * submit lists.
45 */
46 struct list_head submit_entry;
47
48 /* work defered until bo is inactive: */
49 struct list_head inactive_work;
29 50
30 struct page **pages; 51 struct page **pages;
31 struct sg_table *sgt; 52 struct sg_table *sgt;
@@ -35,7 +56,44 @@ struct msm_gem_object {
35 // XXX 56 // XXX
36 uint32_t iova; 57 uint32_t iova;
37 } domain[NUM_DOMAINS]; 58 } domain[NUM_DOMAINS];
59
60 /* normally (resv == &_resv) except for imported bo's */
61 struct reservation_object *resv;
62 struct reservation_object _resv;
38}; 63};
39#define to_msm_bo(x) container_of(x, struct msm_gem_object, base) 64#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
40 65
66static inline bool is_active(struct msm_gem_object *msm_obj)
67{
68 return msm_obj->gpu != NULL;
69}
70
71#define MAX_CMDS 4
72
73/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
74 * associated with the cmdstream submission for synchronization (and
75 * make it easier to unwind when things go wrong, etc). This only
76 * lasts for the duration of the submit-ioctl.
77 */
78struct msm_gem_submit {
79 struct drm_device *dev;
80 struct msm_gpu *gpu;
81 struct list_head bo_list;
82 struct ww_acquire_ctx ticket;
83 uint32_t fence;
84 bool valid;
85 unsigned int nr_cmds;
86 unsigned int nr_bos;
87 struct {
88 uint32_t type;
89 uint32_t size; /* in dwords */
90 uint32_t iova;
91 } cmd[MAX_CMDS];
92 struct {
93 uint32_t flags;
94 struct msm_gem_object *obj;
95 uint32_t iova;
96 } bos[0];
97};
98
41#endif /* __MSM_GEM_H__ */ 99#endif /* __MSM_GEM_H__ */
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
new file mode 100644
index 000000000000..3e1ef3a00f60
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -0,0 +1,412 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19#include "msm_gpu.h"
20#include "msm_gem.h"
21
22/*
23 * Cmdstream submission:
24 */
25
26#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
27/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
28#define BO_VALID 0x8000
29#define BO_LOCKED 0x4000
30#define BO_PINNED 0x2000
31
32static inline void __user *to_user_ptr(u64 address)
33{
34 return (void __user *)(uintptr_t)address;
35}
36
37static struct msm_gem_submit *submit_create(struct drm_device *dev,
38 struct msm_gpu *gpu, int nr)
39{
40 struct msm_gem_submit *submit;
41 int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0]));
42
43 submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
44 if (submit) {
45 submit->dev = dev;
46 submit->gpu = gpu;
47
48 /* initially, until copy_from_user() and bo lookup succeeds: */
49 submit->nr_bos = 0;
50 submit->nr_cmds = 0;
51
52 INIT_LIST_HEAD(&submit->bo_list);
53 ww_acquire_init(&submit->ticket, &reservation_ww_class);
54 }
55
56 return submit;
57}
58
59static int submit_lookup_objects(struct msm_gem_submit *submit,
60 struct drm_msm_gem_submit *args, struct drm_file *file)
61{
62 unsigned i;
63 int ret = 0;
64
65 spin_lock(&file->table_lock);
66
67 for (i = 0; i < args->nr_bos; i++) {
68 struct drm_msm_gem_submit_bo submit_bo;
69 struct drm_gem_object *obj;
70 struct msm_gem_object *msm_obj;
71 void __user *userptr =
72 to_user_ptr(args->bos + (i * sizeof(submit_bo)));
73
74 ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
75 if (ret) {
76 ret = -EFAULT;
77 goto out_unlock;
78 }
79
80 if (submit_bo.flags & BO_INVALID_FLAGS) {
81 DBG("invalid flags: %x", submit_bo.flags);
82 ret = -EINVAL;
83 goto out_unlock;
84 }
85
86 submit->bos[i].flags = submit_bo.flags;
87 /* in validate_objects() we figure out if this is true: */
88 submit->bos[i].iova = submit_bo.presumed;
89
90 /* normally use drm_gem_object_lookup(), but for bulk lookup
91 * all under single table_lock just hit object_idr directly:
92 */
93 obj = idr_find(&file->object_idr, submit_bo.handle);
94 if (!obj) {
95 DBG("invalid handle %u at index %u", submit_bo.handle, i);
96 ret = -EINVAL;
97 goto out_unlock;
98 }
99
100 msm_obj = to_msm_bo(obj);
101
102 if (!list_empty(&msm_obj->submit_entry)) {
103 DBG("handle %u at index %u already on submit list",
104 submit_bo.handle, i);
105 ret = -EINVAL;
106 goto out_unlock;
107 }
108
109 drm_gem_object_reference(obj);
110
111 submit->bos[i].obj = msm_obj;
112
113 list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
114 }
115
116out_unlock:
117 submit->nr_bos = i;
118 spin_unlock(&file->table_lock);
119
120 return ret;
121}
122
123static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
124{
125 struct msm_gem_object *msm_obj = submit->bos[i].obj;
126
127 if (submit->bos[i].flags & BO_PINNED)
128 msm_gem_put_iova(&msm_obj->base, submit->gpu->id);
129
130 if (submit->bos[i].flags & BO_LOCKED)
131 ww_mutex_unlock(&msm_obj->resv->lock);
132
133 if (!(submit->bos[i].flags & BO_VALID))
134 submit->bos[i].iova = 0;
135
136 submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
137}
138
139/* This is where we make sure all the bo's are reserved and pin'd: */
140static int submit_validate_objects(struct msm_gem_submit *submit)
141{
142 int contended, slow_locked = -1, i, ret = 0;
143
144retry:
145 submit->valid = true;
146
147 for (i = 0; i < submit->nr_bos; i++) {
148 struct msm_gem_object *msm_obj = submit->bos[i].obj;
149 uint32_t iova;
150
151 if (slow_locked == i)
152 slow_locked = -1;
153
154 contended = i;
155
156 if (!(submit->bos[i].flags & BO_LOCKED)) {
157 ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
158 &submit->ticket);
159 if (ret)
160 goto fail;
161 submit->bos[i].flags |= BO_LOCKED;
162 }
163
164
165 /* if locking succeeded, pin bo: */
166 ret = msm_gem_get_iova(&msm_obj->base,
167 submit->gpu->id, &iova);
168
169 /* this would break the logic in the fail path.. there is no
170 * reason for this to happen, but just to be on the safe side
171 * let's notice if this starts happening in the future:
172 */
173 WARN_ON(ret == -EDEADLK);
174
175 if (ret)
176 goto fail;
177
178 submit->bos[i].flags |= BO_PINNED;
179
180 if (iova == submit->bos[i].iova) {
181 submit->bos[i].flags |= BO_VALID;
182 } else {
183 submit->bos[i].iova = iova;
184 submit->bos[i].flags &= ~BO_VALID;
185 submit->valid = false;
186 }
187 }
188
189 ww_acquire_done(&submit->ticket);
190
191 return 0;
192
193fail:
194 for (; i >= 0; i--)
195 submit_unlock_unpin_bo(submit, i);
196
197 if (slow_locked > 0)
198 submit_unlock_unpin_bo(submit, slow_locked);
199
200 if (ret == -EDEADLK) {
201 struct msm_gem_object *msm_obj = submit->bos[contended].obj;
202 /* we lost out in a seqno race, lock and retry.. */
203 ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
204 &submit->ticket);
205 if (!ret) {
206 submit->bos[contended].flags |= BO_LOCKED;
207 slow_locked = contended;
208 goto retry;
209 }
210 }
211
212 return ret;
213}
214
215static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
216 struct msm_gem_object **obj, uint32_t *iova, bool *valid)
217{
218 if (idx >= submit->nr_bos) {
219 DBG("invalid buffer index: %u (out of %u)", idx, submit->nr_bos);
220 return EINVAL;
221 }
222
223 if (obj)
224 *obj = submit->bos[idx].obj;
225 if (iova)
226 *iova = submit->bos[idx].iova;
227 if (valid)
228 *valid = !!(submit->bos[idx].flags & BO_VALID);
229
230 return 0;
231}
232
233/* process the reloc's and patch up the cmdstream as needed: */
234static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
235 uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
236{
237 uint32_t i, last_offset = 0;
238 uint32_t *ptr;
239 int ret;
240
241 if (offset % 4) {
242 DBG("non-aligned cmdstream buffer: %u", offset);
243 return -EINVAL;
244 }
245
246 /* For now, just map the entire thing. Eventually we probably
247 * to do it page-by-page, w/ kmap() if not vmap()d..
248 */
249 ptr = msm_gem_vaddr(&obj->base);
250
251 if (IS_ERR(ptr)) {
252 ret = PTR_ERR(ptr);
253 DBG("failed to map: %d", ret);
254 return ret;
255 }
256
257 for (i = 0; i < nr_relocs; i++) {
258 struct drm_msm_gem_submit_reloc submit_reloc;
259 void __user *userptr =
260 to_user_ptr(relocs + (i * sizeof(submit_reloc)));
261 uint32_t iova, off;
262 bool valid;
263
264 ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
265 if (ret)
266 return -EFAULT;
267
268 if (submit_reloc.submit_offset % 4) {
269 DBG("non-aligned reloc offset: %u",
270 submit_reloc.submit_offset);
271 return -EINVAL;
272 }
273
274 /* offset in dwords: */
275 off = submit_reloc.submit_offset / 4;
276
277 if ((off >= (obj->base.size / 4)) ||
278 (off < last_offset)) {
279 DBG("invalid offset %u at reloc %u", off, i);
280 return -EINVAL;
281 }
282
283 ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
284 if (ret)
285 return ret;
286
287 if (valid)
288 continue;
289
290 iova += submit_reloc.reloc_offset;
291
292 if (submit_reloc.shift < 0)
293 iova >>= -submit_reloc.shift;
294 else
295 iova <<= submit_reloc.shift;
296
297 ptr[off] = iova | submit_reloc.or;
298
299 last_offset = off;
300 }
301
302 return 0;
303}
304
305static void submit_cleanup(struct msm_gem_submit *submit, bool fail)
306{
307 unsigned i;
308
309 mutex_lock(&submit->dev->struct_mutex);
310 for (i = 0; i < submit->nr_bos; i++) {
311 struct msm_gem_object *msm_obj = submit->bos[i].obj;
312 submit_unlock_unpin_bo(submit, i);
313 list_del_init(&msm_obj->submit_entry);
314 drm_gem_object_unreference(&msm_obj->base);
315 }
316 mutex_unlock(&submit->dev->struct_mutex);
317
318 ww_acquire_fini(&submit->ticket);
319 kfree(submit);
320}
321
322int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
323 struct drm_file *file)
324{
325 struct msm_drm_private *priv = dev->dev_private;
326 struct drm_msm_gem_submit *args = data;
327 struct msm_file_private *ctx = file->driver_priv;
328 struct msm_gem_submit *submit;
329 struct msm_gpu *gpu;
330 unsigned i;
331 int ret;
332
333 /* for now, we just have 3d pipe.. eventually this would need to
334 * be more clever to dispatch to appropriate gpu module:
335 */
336 if (args->pipe != MSM_PIPE_3D0)
337 return -EINVAL;
338
339 gpu = priv->gpu;
340
341 if (args->nr_cmds > MAX_CMDS)
342 return -EINVAL;
343
344 submit = submit_create(dev, gpu, args->nr_bos);
345 if (!submit) {
346 ret = -ENOMEM;
347 goto out;
348 }
349
350 ret = submit_lookup_objects(submit, args, file);
351 if (ret)
352 goto out;
353
354 ret = submit_validate_objects(submit);
355 if (ret)
356 goto out;
357
358 for (i = 0; i < args->nr_cmds; i++) {
359 struct drm_msm_gem_submit_cmd submit_cmd;
360 void __user *userptr =
361 to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
362 struct msm_gem_object *msm_obj;
363 uint32_t iova;
364
365 ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
366 if (ret) {
367 ret = -EFAULT;
368 goto out;
369 }
370
371 ret = submit_bo(submit, submit_cmd.submit_idx,
372 &msm_obj, &iova, NULL);
373 if (ret)
374 goto out;
375
376 if (submit_cmd.size % 4) {
377 DBG("non-aligned cmdstream buffer size: %u",
378 submit_cmd.size);
379 ret = -EINVAL;
380 goto out;
381 }
382
383 if (submit_cmd.size >= msm_obj->base.size) {
384 DBG("invalid cmdstream size: %u", submit_cmd.size);
385 ret = -EINVAL;
386 goto out;
387 }
388
389 submit->cmd[i].type = submit_cmd.type;
390 submit->cmd[i].size = submit_cmd.size / 4;
391 submit->cmd[i].iova = iova + submit_cmd.submit_offset;
392
393 if (submit->valid)
394 continue;
395
396 ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
397 submit_cmd.nr_relocs, submit_cmd.relocs);
398 if (ret)
399 goto out;
400 }
401
402 submit->nr_cmds = i;
403
404 ret = msm_gpu_submit(gpu, submit, ctx);
405
406 args->fence = submit->fence;
407
408out:
409 if (submit)
410 submit_cleanup(submit, !!ret);
411 return ret;
412}
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
new file mode 100644
index 000000000000..7c6541e4a7ec
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -0,0 +1,411 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_gpu.h"
19#include "msm_gem.h"
20
21
22/*
23 * Power Management:
24 */
25
26#ifdef CONFIG_MSM_BUS_SCALING
27#include <mach/board.h>
28#include <mach/kgsl.h>
29static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev)
30{
31 struct drm_device *dev = gpu->dev;
32 struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
33
34 if (!pdev) {
35 dev_err(dev->dev, "could not find dtv pdata\n");
36 return;
37 }
38
39 if (pdata->bus_scale_table) {
40 gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table);
41 DBG("bus scale client: %08x", gpu->bsc);
42 }
43}
44
45static void bs_fini(struct msm_gpu *gpu)
46{
47 if (gpu->bsc) {
48 msm_bus_scale_unregister_client(gpu->bsc);
49 gpu->bsc = 0;
50 }
51}
52
53static void bs_set(struct msm_gpu *gpu, int idx)
54{
55 if (gpu->bsc) {
56 DBG("set bus scaling: %d", idx);
57 msm_bus_scale_client_update_request(gpu->bsc, idx);
58 }
59}
60#else
61static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) {}
62static void bs_fini(struct msm_gpu *gpu) {}
63static void bs_set(struct msm_gpu *gpu, int idx) {}
64#endif
65
66static int enable_pwrrail(struct msm_gpu *gpu)
67{
68 struct drm_device *dev = gpu->dev;
69 int ret = 0;
70
71 if (gpu->gpu_reg) {
72 ret = regulator_enable(gpu->gpu_reg);
73 if (ret) {
74 dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
75 return ret;
76 }
77 }
78
79 if (gpu->gpu_cx) {
80 ret = regulator_enable(gpu->gpu_cx);
81 if (ret) {
82 dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
83 return ret;
84 }
85 }
86
87 return 0;
88}
89
90static int disable_pwrrail(struct msm_gpu *gpu)
91{
92 if (gpu->gpu_cx)
93 regulator_disable(gpu->gpu_cx);
94 if (gpu->gpu_reg)
95 regulator_disable(gpu->gpu_reg);
96 return 0;
97}
98
99static int enable_clk(struct msm_gpu *gpu)
100{
101 struct clk *rate_clk = NULL;
102 int i;
103
104 /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
105 for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
106 if (gpu->grp_clks[i]) {
107 clk_prepare(gpu->grp_clks[i]);
108 rate_clk = gpu->grp_clks[i];
109 }
110 }
111
112 if (rate_clk && gpu->fast_rate)
113 clk_set_rate(rate_clk, gpu->fast_rate);
114
115 for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
116 if (gpu->grp_clks[i])
117 clk_enable(gpu->grp_clks[i]);
118
119 return 0;
120}
121
122static int disable_clk(struct msm_gpu *gpu)
123{
124 struct clk *rate_clk = NULL;
125 int i;
126
127 /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
128 for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) {
129 if (gpu->grp_clks[i]) {
130 clk_disable(gpu->grp_clks[i]);
131 rate_clk = gpu->grp_clks[i];
132 }
133 }
134
135 if (rate_clk && gpu->slow_rate)
136 clk_set_rate(rate_clk, gpu->slow_rate);
137
138 for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--)
139 if (gpu->grp_clks[i])
140 clk_unprepare(gpu->grp_clks[i]);
141
142 return 0;
143}
144
145static int enable_axi(struct msm_gpu *gpu)
146{
147 if (gpu->ebi1_clk)
148 clk_prepare_enable(gpu->ebi1_clk);
149 if (gpu->bus_freq)
150 bs_set(gpu, gpu->bus_freq);
151 return 0;
152}
153
154static int disable_axi(struct msm_gpu *gpu)
155{
156 if (gpu->ebi1_clk)
157 clk_disable_unprepare(gpu->ebi1_clk);
158 if (gpu->bus_freq)
159 bs_set(gpu, 0);
160 return 0;
161}
162
163int msm_gpu_pm_resume(struct msm_gpu *gpu)
164{
165 int ret;
166
167 DBG("%s", gpu->name);
168
169 ret = enable_pwrrail(gpu);
170 if (ret)
171 return ret;
172
173 ret = enable_clk(gpu);
174 if (ret)
175 return ret;
176
177 ret = enable_axi(gpu);
178 if (ret)
179 return ret;
180
181 return 0;
182}
183
184int msm_gpu_pm_suspend(struct msm_gpu *gpu)
185{
186 int ret;
187
188 DBG("%s", gpu->name);
189
190 ret = disable_axi(gpu);
191 if (ret)
192 return ret;
193
194 ret = disable_clk(gpu);
195 if (ret)
196 return ret;
197
198 ret = disable_pwrrail(gpu);
199 if (ret)
200 return ret;
201
202 return 0;
203}
204
205/*
206 * Cmdstream submission/retirement:
207 */
208
209static void retire_worker(struct work_struct *work)
210{
211 struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work);
212 struct drm_device *dev = gpu->dev;
213 uint32_t fence = gpu->funcs->last_fence(gpu);
214
215 mutex_lock(&dev->struct_mutex);
216
217 while (!list_empty(&gpu->active_list)) {
218 struct msm_gem_object *obj;
219
220 obj = list_first_entry(&gpu->active_list,
221 struct msm_gem_object, mm_list);
222
223 if (obj->fence <= fence) {
224 /* move to inactive: */
225 msm_gem_move_to_inactive(&obj->base);
226 msm_gem_put_iova(&obj->base, gpu->id);
227 drm_gem_object_unreference(&obj->base);
228 } else {
229 break;
230 }
231 }
232
233 msm_update_fence(gpu->dev, fence);
234
235 mutex_unlock(&dev->struct_mutex);
236}
237
238/* call from irq handler to schedule work to retire bo's */
239void msm_gpu_retire(struct msm_gpu *gpu)
240{
241 struct msm_drm_private *priv = gpu->dev->dev_private;
242 queue_work(priv->wq, &gpu->retire_work);
243}
244
245/* add bo's to gpu's ring, and kick gpu: */
246int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
247 struct msm_file_private *ctx)
248{
249 struct drm_device *dev = gpu->dev;
250 struct msm_drm_private *priv = dev->dev_private;
251 int i, ret;
252
253 mutex_lock(&dev->struct_mutex);
254
255 submit->fence = ++priv->next_fence;
256
257 ret = gpu->funcs->submit(gpu, submit, ctx);
258 priv->lastctx = ctx;
259
260 for (i = 0; i < submit->nr_bos; i++) {
261 struct msm_gem_object *msm_obj = submit->bos[i].obj;
262
263 /* can't happen yet.. but when we add 2d support we'll have
264 * to deal w/ cross-ring synchronization:
265 */
266 WARN_ON(is_active(msm_obj) && (msm_obj->gpu != gpu));
267
268 if (!is_active(msm_obj)) {
269 uint32_t iova;
270
271 /* ring takes a reference to the bo and iova: */
272 drm_gem_object_reference(&msm_obj->base);
273 msm_gem_get_iova_locked(&msm_obj->base,
274 submit->gpu->id, &iova);
275 }
276
277 msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence);
278 }
279 mutex_unlock(&dev->struct_mutex);
280
281 return ret;
282}
283
284/*
285 * Init/Cleanup:
286 */
287
288static irqreturn_t irq_handler(int irq, void *data)
289{
290 struct msm_gpu *gpu = data;
291 return gpu->funcs->irq(gpu);
292}
293
294static const char *clk_names[] = {
295 "src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk",
296};
297
298int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
299 struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
300 const char *name, const char *ioname, const char *irqname, int ringsz)
301{
302 int i, ret;
303
304 gpu->dev = drm;
305 gpu->funcs = funcs;
306 gpu->name = name;
307
308 INIT_LIST_HEAD(&gpu->active_list);
309 INIT_WORK(&gpu->retire_work, retire_worker);
310
311 BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
312
313 /* Map registers: */
314 gpu->mmio = msm_ioremap(pdev, ioname, name);
315 if (IS_ERR(gpu->mmio)) {
316 ret = PTR_ERR(gpu->mmio);
317 goto fail;
318 }
319
320 /* Get Interrupt: */
321 gpu->irq = platform_get_irq_byname(pdev, irqname);
322 if (gpu->irq < 0) {
323 ret = gpu->irq;
324 dev_err(drm->dev, "failed to get irq: %d\n", ret);
325 goto fail;
326 }
327
328 ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
329 IRQF_TRIGGER_HIGH, gpu->name, gpu);
330 if (ret) {
331 dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret);
332 goto fail;
333 }
334
335 /* Acquire clocks: */
336 for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
337 gpu->grp_clks[i] = devm_clk_get(&pdev->dev, clk_names[i]);
338 DBG("grp_clks[%s]: %p", clk_names[i], gpu->grp_clks[i]);
339 if (IS_ERR(gpu->grp_clks[i]))
340 gpu->grp_clks[i] = NULL;
341 }
342
343 gpu->ebi1_clk = devm_clk_get(&pdev->dev, "bus_clk");
344 DBG("ebi1_clk: %p", gpu->ebi1_clk);
345 if (IS_ERR(gpu->ebi1_clk))
346 gpu->ebi1_clk = NULL;
347
348 /* Acquire regulators: */
349 gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd");
350 DBG("gpu_reg: %p", gpu->gpu_reg);
351 if (IS_ERR(gpu->gpu_reg))
352 gpu->gpu_reg = NULL;
353
354 gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx");
355 DBG("gpu_cx: %p", gpu->gpu_cx);
356 if (IS_ERR(gpu->gpu_cx))
357 gpu->gpu_cx = NULL;
358
359 /* Setup IOMMU.. eventually we will (I think) do this once per context
360 * and have separate page tables per context. For now, to keep things
361 * simple and to get something working, just use a single address space:
362 */
363 gpu->iommu = iommu_domain_alloc(&platform_bus_type);
364 if (!gpu->iommu) {
365 dev_err(drm->dev, "failed to allocate IOMMU\n");
366 ret = -ENOMEM;
367 goto fail;
368 }
369 gpu->id = msm_register_iommu(drm, gpu->iommu);
370
371 /* Create ringbuffer: */
372 gpu->rb = msm_ringbuffer_new(gpu, ringsz);
373 if (IS_ERR(gpu->rb)) {
374 ret = PTR_ERR(gpu->rb);
375 gpu->rb = NULL;
376 dev_err(drm->dev, "could not create ringbuffer: %d\n", ret);
377 goto fail;
378 }
379
380 ret = msm_gem_get_iova_locked(gpu->rb->bo, gpu->id, &gpu->rb_iova);
381 if (ret) {
382 gpu->rb_iova = 0;
383 dev_err(drm->dev, "could not map ringbuffer: %d\n", ret);
384 goto fail;
385 }
386
387 bs_init(gpu, pdev);
388
389 return 0;
390
391fail:
392 return ret;
393}
394
395void msm_gpu_cleanup(struct msm_gpu *gpu)
396{
397 DBG("%s", gpu->name);
398
399 WARN_ON(!list_empty(&gpu->active_list));
400
401 bs_fini(gpu);
402
403 if (gpu->rb) {
404 if (gpu->rb_iova)
405 msm_gem_put_iova(gpu->rb->bo, gpu->id);
406 msm_ringbuffer_destroy(gpu->rb);
407 }
408
409 if (gpu->iommu)
410 iommu_domain_free(gpu->iommu);
411}
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
new file mode 100644
index 000000000000..8d2cd6c2226b
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -0,0 +1,114 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MSM_GPU_H__
19#define __MSM_GPU_H__
20
21#include <linux/clk.h>
22#include <linux/regulator/consumer.h>
23
24#include "msm_drv.h"
25#include "msm_ringbuffer.h"
26
27struct msm_gem_submit;
28
29/* So far, with hardware that I've seen to date, we can have:
30 * + zero, one, or two z180 2d cores
31 * + a3xx or a2xx 3d core, which share a common CP (the firmware
32 * for the CP seems to implement some different PM4 packet types
33 * but the basics of cmdstream submission are the same)
34 *
35 * Which means that the eventual complete "class" hierarchy, once
36 * support for all past and present hw is in place, becomes:
37 * + msm_gpu
38 * + adreno_gpu
39 * + a3xx_gpu
40 * + a2xx_gpu
41 * + z180_gpu
42 */
43struct msm_gpu_funcs {
44 int (*get_param)(struct msm_gpu *gpu, uint32_t param, uint64_t *value);
45 int (*hw_init)(struct msm_gpu *gpu);
46 int (*pm_suspend)(struct msm_gpu *gpu);
47 int (*pm_resume)(struct msm_gpu *gpu);
48 int (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit,
49 struct msm_file_private *ctx);
50 void (*flush)(struct msm_gpu *gpu);
51 void (*idle)(struct msm_gpu *gpu);
52 irqreturn_t (*irq)(struct msm_gpu *irq);
53 uint32_t (*last_fence)(struct msm_gpu *gpu);
54 void (*destroy)(struct msm_gpu *gpu);
55#ifdef CONFIG_DEBUG_FS
56 /* show GPU status in debugfs: */
57 void (*show)(struct msm_gpu *gpu, struct seq_file *m);
58#endif
59};
60
61struct msm_gpu {
62 const char *name;
63 struct drm_device *dev;
64 const struct msm_gpu_funcs *funcs;
65
66 struct msm_ringbuffer *rb;
67 uint32_t rb_iova;
68
69 /* list of GEM active objects: */
70 struct list_head active_list;
71
72 /* worker for handling active-list retiring: */
73 struct work_struct retire_work;
74
75 void __iomem *mmio;
76 int irq;
77
78 struct iommu_domain *iommu;
79 int id;
80
81 /* Power Control: */
82 struct regulator *gpu_reg, *gpu_cx;
83 struct clk *ebi1_clk, *grp_clks[5];
84 uint32_t fast_rate, slow_rate, bus_freq;
85 uint32_t bsc;
86};
87
88static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
89{
90 msm_writel(data, gpu->mmio + (reg << 2));
91}
92
93static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
94{
95 return msm_readl(gpu->mmio + (reg << 2));
96}
97
98int msm_gpu_pm_suspend(struct msm_gpu *gpu);
99int msm_gpu_pm_resume(struct msm_gpu *gpu);
100
101void msm_gpu_retire(struct msm_gpu *gpu);
102int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
103 struct msm_file_private *ctx);
104
105int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
106 struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
107 const char *name, const char *ioname, const char *irqname, int ringsz);
108void msm_gpu_cleanup(struct msm_gpu *gpu);
109
110struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
111void __init a3xx_register(void);
112void __exit a3xx_unregister(void);
113
114#endif /* __MSM_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
new file mode 100644
index 000000000000..8171537dd7d1
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -0,0 +1,61 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_ringbuffer.h"
19#include "msm_gpu.h"
20
21struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size)
22{
23 struct msm_ringbuffer *ring;
24 int ret;
25
26 size = ALIGN(size, 4); /* size should be dword aligned */
27
28 ring = kzalloc(sizeof(*ring), GFP_KERNEL);
29 if (!ring) {
30 ret = -ENOMEM;
31 goto fail;
32 }
33
34 ring->gpu = gpu;
35 ring->bo = msm_gem_new(gpu->dev, size, MSM_BO_WC);
36 if (IS_ERR(ring->bo)) {
37 ret = PTR_ERR(ring->bo);
38 ring->bo = NULL;
39 goto fail;
40 }
41
42 ring->start = msm_gem_vaddr_locked(ring->bo);
43 ring->end = ring->start + (size / 4);
44 ring->cur = ring->start;
45
46 ring->size = size;
47
48 return ring;
49
50fail:
51 if (ring)
52 msm_ringbuffer_destroy(ring);
53 return ERR_PTR(ret);
54}
55
56void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
57{
58 if (ring->bo)
59 drm_gem_object_unreference(ring->bo);
60 kfree(ring);
61}
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h
new file mode 100644
index 000000000000..6e0e1049fa4f
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.h
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __MSM_RINGBUFFER_H__
19#define __MSM_RINGBUFFER_H__
20
21#include "msm_drv.h"
22
23struct msm_ringbuffer {
24 struct msm_gpu *gpu;
25 int size;
26 struct drm_gem_object *bo;
27 uint32_t *start, *end, *cur;
28};
29
30struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size);
31void msm_ringbuffer_destroy(struct msm_ringbuffer *ring);
32
33/* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */
34
35static inline void
36OUT_RING(struct msm_ringbuffer *ring, uint32_t data)
37{
38 if (ring->cur == ring->end)
39 ring->cur = ring->start;
40 *(ring->cur++) = data;
41}
42
43#endif /* __MSM_RINGBUFFER_H__ */