diff options
Diffstat (limited to 'drivers/gpu/drm/ast/ast_main.c')
-rw-r--r-- | drivers/gpu/drm/ast/ast_main.c | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c new file mode 100644 index 000000000000..95ae55b8214b --- /dev/null +++ b/drivers/gpu/drm/ast/ast_main.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Red Hat Inc. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the | ||
6 | * "Software"), to deal in the Software without restriction, including | ||
7 | * without limitation the rights to use, copy, modify, merge, publish, | ||
8 | * distribute, sub license, and/or sell copies of the Software, and to | ||
9 | * permit persons to whom the Software is furnished to do so, subject to | ||
10 | * the following conditions: | ||
11 | * | ||
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
13 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
15 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
16 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
17 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
18 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
19 | * | ||
20 | * The above copyright notice and this permission notice (including the | ||
21 | * next paragraph) shall be included in all copies or substantial portions | ||
22 | * of the Software. | ||
23 | * | ||
24 | */ | ||
25 | /* | ||
26 | * Authors: Dave Airlie <airlied@redhat.com> | ||
27 | */ | ||
28 | #include "drmP.h" | ||
29 | #include "ast_drv.h" | ||
30 | |||
31 | |||
32 | #include "drm_fb_helper.h" | ||
33 | #include "drm_crtc_helper.h" | ||
34 | |||
35 | #include "ast_dram_tables.h" | ||
36 | |||
37 | void ast_set_index_reg_mask(struct ast_private *ast, | ||
38 | uint32_t base, uint8_t index, | ||
39 | uint8_t mask, uint8_t val) | ||
40 | { | ||
41 | u8 tmp; | ||
42 | ast_io_write8(ast, base, index); | ||
43 | tmp = (ast_io_read8(ast, base + 1) & mask) | val; | ||
44 | ast_set_index_reg(ast, base, index, tmp); | ||
45 | } | ||
46 | |||
47 | uint8_t ast_get_index_reg(struct ast_private *ast, | ||
48 | uint32_t base, uint8_t index) | ||
49 | { | ||
50 | uint8_t ret; | ||
51 | ast_io_write8(ast, base, index); | ||
52 | ret = ast_io_read8(ast, base + 1); | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | uint8_t ast_get_index_reg_mask(struct ast_private *ast, | ||
57 | uint32_t base, uint8_t index, uint8_t mask) | ||
58 | { | ||
59 | uint8_t ret; | ||
60 | ast_io_write8(ast, base, index); | ||
61 | ret = ast_io_read8(ast, base + 1) & mask; | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | |||
66 | static int ast_detect_chip(struct drm_device *dev) | ||
67 | { | ||
68 | struct ast_private *ast = dev->dev_private; | ||
69 | |||
70 | if (dev->pdev->device == PCI_CHIP_AST1180) { | ||
71 | ast->chip = AST1100; | ||
72 | DRM_INFO("AST 1180 detected\n"); | ||
73 | } else { | ||
74 | if (dev->pdev->revision >= 0x20) { | ||
75 | ast->chip = AST2300; | ||
76 | DRM_INFO("AST 2300 detected\n"); | ||
77 | } else if (dev->pdev->revision >= 0x10) { | ||
78 | uint32_t data; | ||
79 | ast_write32(ast, 0xf004, 0x1e6e0000); | ||
80 | ast_write32(ast, 0xf000, 0x1); | ||
81 | |||
82 | data = ast_read32(ast, 0x1207c); | ||
83 | switch (data & 0x0300) { | ||
84 | case 0x0200: | ||
85 | ast->chip = AST1100; | ||
86 | DRM_INFO("AST 1100 detected\n"); | ||
87 | break; | ||
88 | case 0x0100: | ||
89 | ast->chip = AST2200; | ||
90 | DRM_INFO("AST 2200 detected\n"); | ||
91 | break; | ||
92 | case 0x0000: | ||
93 | ast->chip = AST2150; | ||
94 | DRM_INFO("AST 2150 detected\n"); | ||
95 | break; | ||
96 | default: | ||
97 | ast->chip = AST2100; | ||
98 | DRM_INFO("AST 2100 detected\n"); | ||
99 | break; | ||
100 | } | ||
101 | ast->vga2_clone = false; | ||
102 | } else { | ||
103 | ast->chip = 2000; | ||
104 | DRM_INFO("AST 2000 detected\n"); | ||
105 | } | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static int ast_get_dram_info(struct drm_device *dev) | ||
111 | { | ||
112 | struct ast_private *ast = dev->dev_private; | ||
113 | uint32_t data, data2; | ||
114 | uint32_t denum, num, div, ref_pll; | ||
115 | |||
116 | ast_write32(ast, 0xf004, 0x1e6e0000); | ||
117 | ast_write32(ast, 0xf000, 0x1); | ||
118 | |||
119 | |||
120 | ast_write32(ast, 0x10000, 0xfc600309); | ||
121 | |||
122 | do { | ||
123 | ; | ||
124 | } while (ast_read32(ast, 0x10000) != 0x01); | ||
125 | data = ast_read32(ast, 0x10004); | ||
126 | |||
127 | if (data & 0x400) | ||
128 | ast->dram_bus_width = 16; | ||
129 | else | ||
130 | ast->dram_bus_width = 32; | ||
131 | |||
132 | if (ast->chip == AST2300) { | ||
133 | switch (data & 0x03) { | ||
134 | case 0: | ||
135 | ast->dram_type = AST_DRAM_512Mx16; | ||
136 | break; | ||
137 | default: | ||
138 | case 1: | ||
139 | ast->dram_type = AST_DRAM_1Gx16; | ||
140 | break; | ||
141 | case 2: | ||
142 | ast->dram_type = AST_DRAM_2Gx16; | ||
143 | break; | ||
144 | case 3: | ||
145 | ast->dram_type = AST_DRAM_4Gx16; | ||
146 | break; | ||
147 | } | ||
148 | } else { | ||
149 | switch (data & 0x0c) { | ||
150 | case 0: | ||
151 | case 4: | ||
152 | ast->dram_type = AST_DRAM_512Mx16; | ||
153 | break; | ||
154 | case 8: | ||
155 | if (data & 0x40) | ||
156 | ast->dram_type = AST_DRAM_1Gx16; | ||
157 | else | ||
158 | ast->dram_type = AST_DRAM_512Mx32; | ||
159 | break; | ||
160 | case 0xc: | ||
161 | ast->dram_type = AST_DRAM_1Gx32; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | data = ast_read32(ast, 0x10120); | ||
167 | data2 = ast_read32(ast, 0x10170); | ||
168 | if (data2 & 0x2000) | ||
169 | ref_pll = 14318; | ||
170 | else | ||
171 | ref_pll = 12000; | ||
172 | |||
173 | denum = data & 0x1f; | ||
174 | num = (data & 0x3fe0) >> 5; | ||
175 | data = (data & 0xc000) >> 14; | ||
176 | switch (data) { | ||
177 | case 3: | ||
178 | div = 0x4; | ||
179 | break; | ||
180 | case 2: | ||
181 | case 1: | ||
182 | div = 0x2; | ||
183 | break; | ||
184 | default: | ||
185 | div = 0x1; | ||
186 | break; | ||
187 | } | ||
188 | ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | uint32_t ast_get_max_dclk(struct drm_device *dev, int bpp) | ||
193 | { | ||
194 | struct ast_private *ast = dev->dev_private; | ||
195 | uint32_t dclk, jreg; | ||
196 | uint32_t dram_bus_width, mclk, dram_bandwidth, actual_dram_bandwidth, dram_efficency = 500; | ||
197 | |||
198 | dram_bus_width = ast->dram_bus_width; | ||
199 | mclk = ast->mclk; | ||
200 | |||
201 | if (ast->chip == AST2100 || | ||
202 | ast->chip == AST1100 || | ||
203 | ast->chip == AST2200 || | ||
204 | ast->chip == AST2150 || | ||
205 | ast->dram_bus_width == 16) | ||
206 | dram_efficency = 600; | ||
207 | else if (ast->chip == AST2300) | ||
208 | dram_efficency = 400; | ||
209 | |||
210 | dram_bandwidth = mclk * dram_bus_width * 2 / 8; | ||
211 | actual_dram_bandwidth = dram_bandwidth * dram_efficency / 1000; | ||
212 | |||
213 | if (ast->chip == AST1180) | ||
214 | dclk = actual_dram_bandwidth / ((bpp + 1) / 8); | ||
215 | else { | ||
216 | jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); | ||
217 | if ((jreg & 0x08) && (ast->chip == AST2000)) | ||
218 | dclk = actual_dram_bandwidth / ((bpp + 1 + 16) / 8); | ||
219 | else if ((jreg & 0x08) && (bpp == 8)) | ||
220 | dclk = actual_dram_bandwidth / ((bpp + 1 + 24) / 8); | ||
221 | else | ||
222 | dclk = actual_dram_bandwidth / ((bpp + 1) / 8); | ||
223 | } | ||
224 | |||
225 | if (ast->chip == AST2100 || | ||
226 | ast->chip == AST2200 || | ||
227 | ast->chip == AST2300 || | ||
228 | ast->chip == AST1180) { | ||
229 | if (dclk > 200) | ||
230 | dclk = 200; | ||
231 | } else { | ||
232 | if (dclk > 165) | ||
233 | dclk = 165; | ||
234 | } | ||
235 | |||
236 | return dclk; | ||
237 | } | ||
238 | |||
239 | static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) | ||
240 | { | ||
241 | struct ast_framebuffer *ast_fb = to_ast_framebuffer(fb); | ||
242 | if (ast_fb->obj) | ||
243 | drm_gem_object_unreference_unlocked(ast_fb->obj); | ||
244 | |||
245 | drm_framebuffer_cleanup(fb); | ||
246 | kfree(fb); | ||
247 | } | ||
248 | |||
249 | static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, | ||
250 | struct drm_file *file, | ||
251 | unsigned int *handle) | ||
252 | { | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | static const struct drm_framebuffer_funcs ast_fb_funcs = { | ||
257 | .destroy = ast_user_framebuffer_destroy, | ||
258 | .create_handle = ast_user_framebuffer_create_handle, | ||
259 | }; | ||
260 | |||
261 | |||
262 | int ast_framebuffer_init(struct drm_device *dev, | ||
263 | struct ast_framebuffer *ast_fb, | ||
264 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
265 | struct drm_gem_object *obj) | ||
266 | { | ||
267 | int ret; | ||
268 | |||
269 | ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); | ||
270 | if (ret) { | ||
271 | DRM_ERROR("framebuffer init failed %d\n", ret); | ||
272 | return ret; | ||
273 | } | ||
274 | drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); | ||
275 | ast_fb->obj = obj; | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static struct drm_framebuffer * | ||
280 | ast_user_framebuffer_create(struct drm_device *dev, | ||
281 | struct drm_file *filp, | ||
282 | struct drm_mode_fb_cmd2 *mode_cmd) | ||
283 | { | ||
284 | struct drm_gem_object *obj; | ||
285 | struct ast_framebuffer *ast_fb; | ||
286 | int ret; | ||
287 | |||
288 | obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); | ||
289 | if (obj == NULL) | ||
290 | return ERR_PTR(-ENOENT); | ||
291 | |||
292 | ast_fb = kzalloc(sizeof(*ast_fb), GFP_KERNEL); | ||
293 | if (!ast_fb) { | ||
294 | drm_gem_object_unreference_unlocked(obj); | ||
295 | return ERR_PTR(-ENOMEM); | ||
296 | } | ||
297 | |||
298 | ret = ast_framebuffer_init(dev, ast_fb, mode_cmd, obj); | ||
299 | if (ret) { | ||
300 | drm_gem_object_unreference_unlocked(obj); | ||
301 | kfree(ast_fb); | ||
302 | return ERR_PTR(ret); | ||
303 | } | ||
304 | return &ast_fb->base; | ||
305 | } | ||
306 | |||
307 | static const struct drm_mode_config_funcs ast_mode_funcs = { | ||
308 | .fb_create = ast_user_framebuffer_create, | ||
309 | }; | ||
310 | |||
311 | static u32 ast_get_vram_info(struct drm_device *dev) | ||
312 | { | ||
313 | struct ast_private *ast = dev->dev_private; | ||
314 | u8 jreg; | ||
315 | |||
316 | ast_open_key(ast); | ||
317 | |||
318 | jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); | ||
319 | switch (jreg & 3) { | ||
320 | case 0: return AST_VIDMEM_SIZE_8M; | ||
321 | case 1: return AST_VIDMEM_SIZE_16M; | ||
322 | case 2: return AST_VIDMEM_SIZE_32M; | ||
323 | case 3: return AST_VIDMEM_SIZE_64M; | ||
324 | } | ||
325 | return AST_VIDMEM_DEFAULT_SIZE; | ||
326 | } | ||
327 | |||
328 | int ast_driver_load(struct drm_device *dev, unsigned long flags) | ||
329 | { | ||
330 | struct ast_private *ast; | ||
331 | int ret = 0; | ||
332 | |||
333 | ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL); | ||
334 | if (!ast) | ||
335 | return -ENOMEM; | ||
336 | |||
337 | dev->dev_private = ast; | ||
338 | ast->dev = dev; | ||
339 | |||
340 | ast->regs = pci_iomap(dev->pdev, 1, 0); | ||
341 | if (!ast->regs) { | ||
342 | ret = -EIO; | ||
343 | goto out_free; | ||
344 | } | ||
345 | ast->ioregs = pci_iomap(dev->pdev, 2, 0); | ||
346 | if (!ast->ioregs) { | ||
347 | ret = -EIO; | ||
348 | goto out_free; | ||
349 | } | ||
350 | |||
351 | ast_detect_chip(dev); | ||
352 | |||
353 | if (ast->chip != AST1180) { | ||
354 | ast_get_dram_info(dev); | ||
355 | ast->vram_size = ast_get_vram_info(dev); | ||
356 | DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); | ||
357 | } | ||
358 | |||
359 | ret = ast_mm_init(ast); | ||
360 | if (ret) | ||
361 | goto out_free; | ||
362 | |||
363 | drm_mode_config_init(dev); | ||
364 | |||
365 | dev->mode_config.funcs = (void *)&ast_mode_funcs; | ||
366 | dev->mode_config.min_width = 0; | ||
367 | dev->mode_config.min_height = 0; | ||
368 | dev->mode_config.preferred_depth = 24; | ||
369 | dev->mode_config.prefer_shadow = 1; | ||
370 | |||
371 | if (ast->chip == AST2100 || | ||
372 | ast->chip == AST2200 || | ||
373 | ast->chip == AST2300 || | ||
374 | ast->chip == AST1180) { | ||
375 | dev->mode_config.max_width = 1920; | ||
376 | dev->mode_config.max_height = 2048; | ||
377 | } else { | ||
378 | dev->mode_config.max_width = 1600; | ||
379 | dev->mode_config.max_height = 1200; | ||
380 | } | ||
381 | |||
382 | ret = ast_mode_init(dev); | ||
383 | if (ret) | ||
384 | goto out_free; | ||
385 | |||
386 | ret = ast_fbdev_init(dev); | ||
387 | if (ret) | ||
388 | goto out_free; | ||
389 | |||
390 | return 0; | ||
391 | out_free: | ||
392 | kfree(ast); | ||
393 | dev->dev_private = NULL; | ||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | int ast_driver_unload(struct drm_device *dev) | ||
398 | { | ||
399 | struct ast_private *ast = dev->dev_private; | ||
400 | |||
401 | ast_mode_fini(dev); | ||
402 | ast_fbdev_fini(dev); | ||
403 | drm_mode_config_cleanup(dev); | ||
404 | |||
405 | ast_mm_fini(ast); | ||
406 | pci_iounmap(dev->pdev, ast->ioregs); | ||
407 | pci_iounmap(dev->pdev, ast->regs); | ||
408 | kfree(ast); | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | int ast_gem_create(struct drm_device *dev, | ||
413 | u32 size, bool iskernel, | ||
414 | struct drm_gem_object **obj) | ||
415 | { | ||
416 | struct ast_bo *astbo; | ||
417 | int ret; | ||
418 | |||
419 | *obj = NULL; | ||
420 | |||
421 | size = roundup(size, PAGE_SIZE); | ||
422 | if (size == 0) | ||
423 | return -EINVAL; | ||
424 | |||
425 | ret = ast_bo_create(dev, size, 0, 0, &astbo); | ||
426 | if (ret) { | ||
427 | if (ret != -ERESTARTSYS) | ||
428 | DRM_ERROR("failed to allocate GEM object\n"); | ||
429 | return ret; | ||
430 | } | ||
431 | *obj = &astbo->gem; | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | int ast_dumb_create(struct drm_file *file, | ||
436 | struct drm_device *dev, | ||
437 | struct drm_mode_create_dumb *args) | ||
438 | { | ||
439 | int ret; | ||
440 | struct drm_gem_object *gobj; | ||
441 | u32 handle; | ||
442 | |||
443 | args->pitch = args->width * ((args->bpp + 7) / 8); | ||
444 | args->size = args->pitch * args->height; | ||
445 | |||
446 | ret = ast_gem_create(dev, args->size, false, | ||
447 | &gobj); | ||
448 | if (ret) | ||
449 | return ret; | ||
450 | |||
451 | ret = drm_gem_handle_create(file, gobj, &handle); | ||
452 | drm_gem_object_unreference_unlocked(gobj); | ||
453 | if (ret) | ||
454 | return ret; | ||
455 | |||
456 | args->handle = handle; | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | int ast_dumb_destroy(struct drm_file *file, | ||
461 | struct drm_device *dev, | ||
462 | uint32_t handle) | ||
463 | { | ||
464 | return drm_gem_handle_delete(file, handle); | ||
465 | } | ||
466 | |||
467 | int ast_gem_init_object(struct drm_gem_object *obj) | ||
468 | { | ||
469 | BUG(); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | void ast_bo_unref(struct ast_bo **bo) | ||
474 | { | ||
475 | struct ttm_buffer_object *tbo; | ||
476 | |||
477 | if ((*bo) == NULL) | ||
478 | return; | ||
479 | |||
480 | tbo = &((*bo)->bo); | ||
481 | ttm_bo_unref(&tbo); | ||
482 | if (tbo == NULL) | ||
483 | *bo = NULL; | ||
484 | |||
485 | } | ||
486 | void ast_gem_free_object(struct drm_gem_object *obj) | ||
487 | { | ||
488 | struct ast_bo *ast_bo = gem_to_ast_bo(obj); | ||
489 | |||
490 | if (!ast_bo) | ||
491 | return; | ||
492 | ast_bo_unref(&ast_bo); | ||
493 | } | ||
494 | |||
495 | |||
496 | static inline u64 ast_bo_mmap_offset(struct ast_bo *bo) | ||
497 | { | ||
498 | return bo->bo.addr_space_offset; | ||
499 | } | ||
500 | int | ||
501 | ast_dumb_mmap_offset(struct drm_file *file, | ||
502 | struct drm_device *dev, | ||
503 | uint32_t handle, | ||
504 | uint64_t *offset) | ||
505 | { | ||
506 | struct drm_gem_object *obj; | ||
507 | int ret; | ||
508 | struct ast_bo *bo; | ||
509 | |||
510 | mutex_lock(&dev->struct_mutex); | ||
511 | obj = drm_gem_object_lookup(dev, file, handle); | ||
512 | if (obj == NULL) { | ||
513 | ret = -ENOENT; | ||
514 | goto out_unlock; | ||
515 | } | ||
516 | |||
517 | bo = gem_to_ast_bo(obj); | ||
518 | *offset = ast_bo_mmap_offset(bo); | ||
519 | |||
520 | drm_gem_object_unreference(obj); | ||
521 | ret = 0; | ||
522 | out_unlock: | ||
523 | mutex_unlock(&dev->struct_mutex); | ||
524 | return ret; | ||
525 | |||
526 | } | ||
527 | |||