diff options
-rw-r--r-- | drivers/gpu/drm/msm/Makefile | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 501 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/adreno/a3xx_gpu.h | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/adreno/adreno_gpu.c | 350 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/adreno/adreno_gpu.h | 142 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 246 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.c | 84 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem.h | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gem_submit.c | 412 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.c | 411 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.h | 114 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_ringbuffer.c | 61 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_ringbuffer.h | 43 | ||||
-rw-r--r-- | include/uapi/drm/Kbuild | 1 | ||||
-rw-r--r-- | include/uapi/drm/msm_drm.h | 207 |
16 files changed, 2695 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))) | |||
4 | endif | 4 | endif |
5 | 5 | ||
6 | msm-y := \ | 6 | msm-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 | ||
23 | msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o | 28 | msm-$(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 | |||
34 | static struct platform_device *a3xx_pdev; | ||
35 | |||
36 | static 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 | |||
63 | static 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 | |||
256 | static 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 | |||
268 | static 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 | |||
289 | static 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 | ||
306 | static 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 | |||
345 | static 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 | |||
368 | static 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 | |||
386 | struct 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 | |||
427 | fail: | ||
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 | |||
438 | static 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 | |||
481 | static int a3xx_remove(struct platform_device *pdev) | ||
482 | { | ||
483 | a3xx_pdev = NULL; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static struct platform_driver a3xx_driver = { | ||
488 | .probe = a3xx_probe, | ||
489 | .remove = a3xx_remove, | ||
490 | .driver.name = "kgsl-3d0", | ||
491 | }; | ||
492 | |||
493 | void __init a3xx_register(void) | ||
494 | { | ||
495 | platform_driver_register(&a3xx_driver); | ||
496 | } | ||
497 | |||
498 | void __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 | |||
24 | struct 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 | |||
21 | struct 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 | |||
31 | static 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 | |||
59 | int 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 | |||
79 | int 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 | |||
103 | static uint32_t get_wptr(struct msm_ringbuffer *ring) | ||
104 | { | ||
105 | return ring->cur - ring->start; | ||
106 | } | ||
107 | |||
108 | uint32_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 | |||
114 | int 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 | |||
187 | void 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 | |||
197 | void 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 | ||
218 | void 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 | |||
235 | void 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 | |||
247 | static const char *iommu_ports[] = { | ||
248 | "gfx3d_user", "gfx3d_priv", | ||
249 | "gfx3d1_user", "gfx3d1_priv", | ||
250 | }; | ||
251 | |||
252 | static inline bool _rev_match(uint8_t entry, uint8_t id) | ||
253 | { | ||
254 | return (entry == ANY_ID) || (entry == id); | ||
255 | } | ||
256 | |||
257 | int 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 | |||
338 | void 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 | |||
28 | struct 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 | |||
38 | struct adreno_gpu_funcs { | ||
39 | struct msm_gpu_funcs base; | ||
40 | }; | ||
41 | |||
42 | struct adreno_info; | ||
43 | |||
44 | struct adreno_rbmemptrs { | ||
45 | volatile uint32_t rptr; | ||
46 | volatile uint32_t wptr; | ||
47 | volatile uint32_t fence; | ||
48 | }; | ||
49 | |||
50 | struct 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) */ | ||
72 | struct 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 | |||
79 | static inline bool adreno_is_a3xx(struct adreno_gpu *gpu) | ||
80 | { | ||
81 | return (gpu->revn >= 300) && (gpu->revn < 400); | ||
82 | } | ||
83 | |||
84 | static inline bool adreno_is_a305(struct adreno_gpu *gpu) | ||
85 | { | ||
86 | return gpu->revn == 305; | ||
87 | } | ||
88 | |||
89 | static inline bool adreno_is_a320(struct adreno_gpu *gpu) | ||
90 | { | ||
91 | return gpu->revn == 320; | ||
92 | } | ||
93 | |||
94 | static inline bool adreno_is_a330(struct adreno_gpu *gpu) | ||
95 | { | ||
96 | return gpu->revn == 330; | ||
97 | } | ||
98 | |||
99 | int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value); | ||
100 | int adreno_hw_init(struct msm_gpu *gpu); | ||
101 | uint32_t adreno_last_fence(struct msm_gpu *gpu); | ||
102 | int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, | ||
103 | struct msm_file_private *ctx); | ||
104 | void adreno_flush(struct msm_gpu *gpu); | ||
105 | void adreno_idle(struct msm_gpu *gpu); | ||
106 | #ifdef CONFIG_DEBUG_FS | ||
107 | void adreno_show(struct msm_gpu *gpu, struct seq_file *m); | ||
108 | #endif | ||
109 | void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords); | ||
110 | |||
111 | int 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); | ||
114 | void adreno_gpu_cleanup(struct adreno_gpu *gpu); | ||
115 | |||
116 | |||
117 | /* ringbuffer helpers (the parts that are adreno specific) */ | ||
118 | |||
119 | static inline void | ||
120 | OUT_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: */ | ||
127 | static inline void | ||
128 | OUT_PKT2(struct msm_ringbuffer *ring) | ||
129 | { | ||
130 | adreno_wait_ring(ring->gpu, 1); | ||
131 | OUT_RING(ring, CP_TYPE2_PKT); | ||
132 | } | ||
133 | |||
134 | static inline void | ||
135 | OUT_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 | ||
252 | static 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 | |||
283 | static 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 | |||
243 | static void msm_preclose(struct drm_device *dev, struct drm_file *file) | 301 | static 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 | ||
251 | static void msm_lastclose(struct drm_device *dev) | 318 | static 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 |
386 | static 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 | |||
319 | static int msm_gem_show(struct drm_device *dev, struct seq_file *m) | 399 | static 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 | ||
377 | static struct drm_info_list msm_debugfs_list[] = { | 463 | static 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 | |||
498 | int 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 */ | ||
527 | void 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 | |||
541 | static 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 | |||
562 | static 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 | |||
572 | static 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 | |||
590 | static 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 | |||
608 | static 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 | |||
629 | static 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 | |||
636 | static 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 | |||
407 | static const struct vm_operations_struct vm_ops = { | 646 | static 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 | ||
527 | module_init(msm_drm_register); | 771 | module_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 | ||
44 | struct msm_kms; | 45 | struct msm_kms; |
46 | struct 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 | |||
50 | struct 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 | ||
48 | struct msm_drm_private { | 58 | struct 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); | |||
108 | int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu, | 125 | int 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 | ||
128 | int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, | ||
129 | struct timespec *timeout); | ||
130 | void msm_update_fence(struct drm_device *dev, uint32_t fence); | ||
131 | |||
132 | int msm_ioctl_gem_submit(struct drm_device *dev, void *data, | ||
133 | struct drm_file *file); | ||
134 | |||
111 | int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | 135 | int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); |
112 | int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); | 136 | int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); |
113 | uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); | 137 | uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); |
@@ -125,6 +149,12 @@ void *msm_gem_vaddr_locked(struct drm_gem_object *obj); | |||
125 | void *msm_gem_vaddr(struct drm_gem_object *obj); | 149 | void *msm_gem_vaddr(struct drm_gem_object *obj); |
126 | int msm_gem_queue_inactive_work(struct drm_gem_object *obj, | 150 | int msm_gem_queue_inactive_work(struct drm_gem_object *obj, |
127 | struct work_struct *work); | 151 | struct work_struct *work); |
152 | void msm_gem_move_to_active(struct drm_gem_object *obj, | ||
153 | struct msm_gpu *gpu, uint32_t fence); | ||
154 | void msm_gem_move_to_inactive(struct drm_gem_object *obj); | ||
155 | int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, | ||
156 | struct timespec *timeout); | ||
157 | int msm_gem_cpu_fini(struct drm_gem_object *obj); | ||
128 | void msm_gem_free_object(struct drm_gem_object *obj); | 158 | void msm_gem_free_object(struct drm_gem_object *obj); |
129 | int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, | 159 | int 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 | |||
395 | void 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 | |||
405 | void 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 | |||
429 | int 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 | ||
444 | int 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 | ||
23 | struct msm_gem_object { | 24 | struct 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 | ||
66 | static 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 | */ | ||
78 | struct 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 | |||
32 | static inline void __user *to_user_ptr(u64 address) | ||
33 | { | ||
34 | return (void __user *)(uintptr_t)address; | ||
35 | } | ||
36 | |||
37 | static 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 | |||
59 | static 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 | |||
116 | out_unlock: | ||
117 | submit->nr_bos = i; | ||
118 | spin_unlock(&file->table_lock); | ||
119 | |||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static 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: */ | ||
140 | static int submit_validate_objects(struct msm_gem_submit *submit) | ||
141 | { | ||
142 | int contended, slow_locked = -1, i, ret = 0; | ||
143 | |||
144 | retry: | ||
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 | |||
193 | fail: | ||
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 | |||
215 | static 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: */ | ||
234 | static 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 | |||
305 | static 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 | |||
322 | int 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 | |||
408 | out: | ||
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> | ||
29 | static 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 | |||
45 | static 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 | |||
53 | static 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 | ||
61 | static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) {} | ||
62 | static void bs_fini(struct msm_gpu *gpu) {} | ||
63 | static void bs_set(struct msm_gpu *gpu, int idx) {} | ||
64 | #endif | ||
65 | |||
66 | static 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 | |||
90 | static 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 | |||
99 | static 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 | |||
122 | static 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 | |||
145 | static 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 | |||
154 | static 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 | |||
163 | int 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 | |||
184 | int 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 | |||
209 | static 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 */ | ||
239 | void 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: */ | ||
246 | int 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 | |||
288 | static irqreturn_t irq_handler(int irq, void *data) | ||
289 | { | ||
290 | struct msm_gpu *gpu = data; | ||
291 | return gpu->funcs->irq(gpu); | ||
292 | } | ||
293 | |||
294 | static const char *clk_names[] = { | ||
295 | "src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk", | ||
296 | }; | ||
297 | |||
298 | int 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 | |||
391 | fail: | ||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | void 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 | |||
27 | struct 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 | */ | ||
43 | struct 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 | |||
61 | struct 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 | |||
88 | static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) | ||
89 | { | ||
90 | msm_writel(data, gpu->mmio + (reg << 2)); | ||
91 | } | ||
92 | |||
93 | static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg) | ||
94 | { | ||
95 | return msm_readl(gpu->mmio + (reg << 2)); | ||
96 | } | ||
97 | |||
98 | int msm_gpu_pm_suspend(struct msm_gpu *gpu); | ||
99 | int msm_gpu_pm_resume(struct msm_gpu *gpu); | ||
100 | |||
101 | void msm_gpu_retire(struct msm_gpu *gpu); | ||
102 | int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, | ||
103 | struct msm_file_private *ctx); | ||
104 | |||
105 | int 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); | ||
108 | void msm_gpu_cleanup(struct msm_gpu *gpu); | ||
109 | |||
110 | struct msm_gpu *a3xx_gpu_init(struct drm_device *dev); | ||
111 | void __init a3xx_register(void); | ||
112 | void __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 | |||
21 | struct 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 | |||
50 | fail: | ||
51 | if (ring) | ||
52 | msm_ringbuffer_destroy(ring); | ||
53 | return ERR_PTR(ret); | ||
54 | } | ||
55 | |||
56 | void 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 | |||
23 | struct msm_ringbuffer { | ||
24 | struct msm_gpu *gpu; | ||
25 | int size; | ||
26 | struct drm_gem_object *bo; | ||
27 | uint32_t *start, *end, *cur; | ||
28 | }; | ||
29 | |||
30 | struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size); | ||
31 | void msm_ringbuffer_destroy(struct msm_ringbuffer *ring); | ||
32 | |||
33 | /* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */ | ||
34 | |||
35 | static inline void | ||
36 | OUT_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__ */ | ||
diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild index 119487e05e65..2d9a25daab05 100644 --- a/include/uapi/drm/Kbuild +++ b/include/uapi/drm/Kbuild | |||
@@ -16,3 +16,4 @@ header-y += sis_drm.h | |||
16 | header-y += tegra_drm.h | 16 | header-y += tegra_drm.h |
17 | header-y += via_drm.h | 17 | header-y += via_drm.h |
18 | header-y += vmwgfx_drm.h | 18 | header-y += vmwgfx_drm.h |
19 | header-y += msm_drm.h | ||
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h new file mode 100644 index 000000000000..d3c62074016d --- /dev/null +++ b/include/uapi/drm/msm_drm.h | |||
@@ -0,0 +1,207 @@ | |||
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_DRM_H__ | ||
19 | #define __MSM_DRM_H__ | ||
20 | |||
21 | #include <stddef.h> | ||
22 | #include <drm/drm.h> | ||
23 | |||
24 | /* Please note that modifications to all structs defined here are | ||
25 | * subject to backwards-compatibility constraints: | ||
26 | * 1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit | ||
27 | * user/kernel compatibility | ||
28 | * 2) Keep fields aligned to their size | ||
29 | * 3) Because of how drm_ioctl() works, we can add new fields at | ||
30 | * the end of an ioctl if some care is taken: drm_ioctl() will | ||
31 | * zero out the new fields at the tail of the ioctl, so a zero | ||
32 | * value should have a backwards compatible meaning. And for | ||
33 | * output params, userspace won't see the newly added output | ||
34 | * fields.. so that has to be somehow ok. | ||
35 | */ | ||
36 | |||
37 | #define MSM_PIPE_NONE 0x00 | ||
38 | #define MSM_PIPE_2D0 0x01 | ||
39 | #define MSM_PIPE_2D1 0x02 | ||
40 | #define MSM_PIPE_3D0 0x10 | ||
41 | |||
42 | /* timeouts are specified in clock-monotonic absolute times (to simplify | ||
43 | * restarting interrupted ioctls). The following struct is logically the | ||
44 | * same as 'struct timespec' but 32/64b ABI safe. | ||
45 | */ | ||
46 | struct drm_msm_timespec { | ||
47 | int64_t tv_sec; /* seconds */ | ||
48 | int64_t tv_nsec; /* nanoseconds */ | ||
49 | }; | ||
50 | |||
51 | #define MSM_PARAM_GPU_ID 0x01 | ||
52 | #define MSM_PARAM_GMEM_SIZE 0x02 | ||
53 | |||
54 | struct drm_msm_param { | ||
55 | uint32_t pipe; /* in, MSM_PIPE_x */ | ||
56 | uint32_t param; /* in, MSM_PARAM_x */ | ||
57 | uint64_t value; /* out (get_param) or in (set_param) */ | ||
58 | }; | ||
59 | |||
60 | /* | ||
61 | * GEM buffers: | ||
62 | */ | ||
63 | |||
64 | #define MSM_BO_SCANOUT 0x00000001 /* scanout capable */ | ||
65 | #define MSM_BO_GPU_READONLY 0x00000002 | ||
66 | #define MSM_BO_CACHE_MASK 0x000f0000 | ||
67 | /* cache modes */ | ||
68 | #define MSM_BO_CACHED 0x00010000 | ||
69 | #define MSM_BO_WC 0x00020000 | ||
70 | #define MSM_BO_UNCACHED 0x00040000 | ||
71 | |||
72 | struct drm_msm_gem_new { | ||
73 | uint64_t size; /* in */ | ||
74 | uint32_t flags; /* in, mask of MSM_BO_x */ | ||
75 | uint32_t handle; /* out */ | ||
76 | }; | ||
77 | |||
78 | struct drm_msm_gem_info { | ||
79 | uint32_t handle; /* in */ | ||
80 | uint32_t pad; | ||
81 | uint64_t offset; /* out, offset to pass to mmap() */ | ||
82 | }; | ||
83 | |||
84 | #define MSM_PREP_READ 0x01 | ||
85 | #define MSM_PREP_WRITE 0x02 | ||
86 | #define MSM_PREP_NOSYNC 0x04 | ||
87 | |||
88 | struct drm_msm_gem_cpu_prep { | ||
89 | uint32_t handle; /* in */ | ||
90 | uint32_t op; /* in, mask of MSM_PREP_x */ | ||
91 | struct drm_msm_timespec timeout; /* in */ | ||
92 | }; | ||
93 | |||
94 | struct drm_msm_gem_cpu_fini { | ||
95 | uint32_t handle; /* in */ | ||
96 | }; | ||
97 | |||
98 | /* | ||
99 | * Cmdstream Submission: | ||
100 | */ | ||
101 | |||
102 | /* The value written into the cmdstream is logically: | ||
103 | * | ||
104 | * ((relocbuf->gpuaddr + reloc_offset) << shift) | or | ||
105 | * | ||
106 | * When we have GPU's w/ >32bit ptrs, it should be possible to deal | ||
107 | * with this by emit'ing two reloc entries with appropriate shift | ||
108 | * values. Or a new MSM_SUBMIT_CMD_x type would also be an option. | ||
109 | * | ||
110 | * NOTE that reloc's must be sorted by order of increasing submit_offset, | ||
111 | * otherwise EINVAL. | ||
112 | */ | ||
113 | struct drm_msm_gem_submit_reloc { | ||
114 | uint32_t submit_offset; /* in, offset from submit_bo */ | ||
115 | uint32_t or; /* in, value OR'd with result */ | ||
116 | int32_t shift; /* in, amount of left shift (can be negative) */ | ||
117 | uint32_t reloc_idx; /* in, index of reloc_bo buffer */ | ||
118 | uint64_t reloc_offset; /* in, offset from start of reloc_bo */ | ||
119 | }; | ||
120 | |||
121 | /* submit-types: | ||
122 | * BUF - this cmd buffer is executed normally. | ||
123 | * IB_TARGET_BUF - this cmd buffer is an IB target. Reloc's are | ||
124 | * processed normally, but the kernel does not setup an IB to | ||
125 | * this buffer in the first-level ringbuffer | ||
126 | * CTX_RESTORE_BUF - only executed if there has been a GPU context | ||
127 | * switch since the last SUBMIT ioctl | ||
128 | */ | ||
129 | #define MSM_SUBMIT_CMD_BUF 0x0001 | ||
130 | #define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002 | ||
131 | #define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003 | ||
132 | struct drm_msm_gem_submit_cmd { | ||
133 | uint32_t type; /* in, one of MSM_SUBMIT_CMD_x */ | ||
134 | uint32_t submit_idx; /* in, index of submit_bo cmdstream buffer */ | ||
135 | uint32_t submit_offset; /* in, offset into submit_bo */ | ||
136 | uint32_t size; /* in, cmdstream size */ | ||
137 | uint32_t pad; | ||
138 | uint32_t nr_relocs; /* in, number of submit_reloc's */ | ||
139 | uint64_t __user relocs; /* in, ptr to array of submit_reloc's */ | ||
140 | }; | ||
141 | |||
142 | /* Each buffer referenced elsewhere in the cmdstream submit (ie. the | ||
143 | * cmdstream buffer(s) themselves or reloc entries) has one (and only | ||
144 | * one) entry in the submit->bos[] table. | ||
145 | * | ||
146 | * As a optimization, the current buffer (gpu virtual address) can be | ||
147 | * passed back through the 'presumed' field. If on a subsequent reloc, | ||
148 | * userspace passes back a 'presumed' address that is still valid, | ||
149 | * then patching the cmdstream for this entry is skipped. This can | ||
150 | * avoid kernel needing to map/access the cmdstream bo in the common | ||
151 | * case. | ||
152 | */ | ||
153 | #define MSM_SUBMIT_BO_READ 0x0001 | ||
154 | #define MSM_SUBMIT_BO_WRITE 0x0002 | ||
155 | struct drm_msm_gem_submit_bo { | ||
156 | uint32_t flags; /* in, mask of MSM_SUBMIT_BO_x */ | ||
157 | uint32_t handle; /* in, GEM handle */ | ||
158 | uint64_t presumed; /* in/out, presumed buffer address */ | ||
159 | }; | ||
160 | |||
161 | /* Each cmdstream submit consists of a table of buffers involved, and | ||
162 | * one or more cmdstream buffers. This allows for conditional execution | ||
163 | * (context-restore), and IB buffers needed for per tile/bin draw cmds. | ||
164 | */ | ||
165 | struct drm_msm_gem_submit { | ||
166 | uint32_t pipe; /* in, MSM_PIPE_x */ | ||
167 | uint32_t fence; /* out */ | ||
168 | uint32_t nr_bos; /* in, number of submit_bo's */ | ||
169 | uint32_t nr_cmds; /* in, number of submit_cmd's */ | ||
170 | uint64_t __user bos; /* in, ptr to array of submit_bo's */ | ||
171 | uint64_t __user cmds; /* in, ptr to array of submit_cmd's */ | ||
172 | }; | ||
173 | |||
174 | /* The normal way to synchronize with the GPU is just to CPU_PREP on | ||
175 | * a buffer if you need to access it from the CPU (other cmdstream | ||
176 | * submission from same or other contexts, PAGE_FLIP ioctl, etc, all | ||
177 | * handle the required synchronization under the hood). This ioctl | ||
178 | * mainly just exists as a way to implement the gallium pipe_fence | ||
179 | * APIs without requiring a dummy bo to synchronize on. | ||
180 | */ | ||
181 | struct drm_msm_wait_fence { | ||
182 | uint32_t fence; /* in */ | ||
183 | uint32_t pad; | ||
184 | struct drm_msm_timespec timeout; /* in */ | ||
185 | }; | ||
186 | |||
187 | #define DRM_MSM_GET_PARAM 0x00 | ||
188 | /* placeholder: | ||
189 | #define DRM_MSM_SET_PARAM 0x01 | ||
190 | */ | ||
191 | #define DRM_MSM_GEM_NEW 0x02 | ||
192 | #define DRM_MSM_GEM_INFO 0x03 | ||
193 | #define DRM_MSM_GEM_CPU_PREP 0x04 | ||
194 | #define DRM_MSM_GEM_CPU_FINI 0x05 | ||
195 | #define DRM_MSM_GEM_SUBMIT 0x06 | ||
196 | #define DRM_MSM_WAIT_FENCE 0x07 | ||
197 | #define DRM_MSM_NUM_IOCTLS 0x08 | ||
198 | |||
199 | #define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) | ||
200 | #define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) | ||
201 | #define DRM_IOCTL_MSM_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_INFO, struct drm_msm_gem_info) | ||
202 | #define DRM_IOCTL_MSM_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP, struct drm_msm_gem_cpu_prep) | ||
203 | #define DRM_IOCTL_MSM_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini) | ||
204 | #define DRM_IOCTL_MSM_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit) | ||
205 | #define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence) | ||
206 | |||
207 | #endif /* __MSM_DRM_H__ */ | ||