/* * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef __SAVAGE_DRM_H__ #define __SAVAGE_DRM_H__ #ifndef __SAVAGE_SAREA_DEFINES__ #define __SAVAGE_SAREA_DEFINES__ #define DRM_SAVAGE_MEM_PAGE (1UL<<12) #define DRM_SAVAGE_MEM_WORK 32 #define DRM_SAVAGE_MEM_LOCATION_PCI 1 #define DRM_SAVAGE_MEM_LOCATION_AGP 2 #define DRM_SAVAGE_DMA_AGP_SIZE (16*1024*1024) typedef struct drm_savage_alloc_cont_mem { size_t size; /*size of buffer*/ unsigned long type; /*4k page or word*/ unsigned long alignment; unsigned long location; /*agp or pci*/ unsigned long phyaddress; unsigned long linear; } drm_savage_alloc_cont_mem_t; typedef struct drm_savage_get_physcis_address { unsigned long v_address; unsigned long p_address; }drm_savage_get_physcis_address_t; /*ioctl number*/ #define DRM_IOCTL_SAVAGE_ALLOC_CONTINUOUS_MEM \ DRM_IOWR(0x40,drm_savage_alloc_cont_mem_t) #define DRM_IOCTL_SAVAGE_GET_PHYSICS_ADDRESS \ DRM_IOWR(0x41, drm_savage_get_physcis_address_t) #define DRM_IOCTL_SAVAGE_FREE_CONTINUOUS_MEM \ DRM_IOWR(0x42, drm_savage_alloc_cont_mem_t) #define SAVAGE_FRONT 0x1 #define SAVAGE_BACK 0x2 #define SAVAGE_DEPTH 0x4 #define SAVAGE_STENCIL 0x8 /* What needs to be changed for the current vertex dma buffer? */ #define SAVAGE_UPLOAD_CTX 0x1 #define SAVAGE_UPLOAD_TEX0 0x2 #define SAVAGE_UPLOAD_TEX1 0x4 #define SAVAGE_UPLOAD_PIPE 0x8 /* <- seems should be removed, Jiayo Hsu */ #define SAVAGE_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ #define SAVAGE_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ #define SAVAGE_UPLOAD_2D 0x40 #define SAVAGE_WAIT_AGE 0x80 /* handled client-side */ #define SAVAGE_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ /*frank:add Buffer state 2001/11/15*/ #define SAVAGE_UPLOAD_BUFFERS 0x200 /* original marked off in MGA drivers , Jiayo Hsu Oct.23,2001 */ /* Keep these small for testing. */ #define SAVAGE_NR_SAREA_CLIPRECTS 8 /* 2 heaps (1 for card, 1 for agp), each divided into upto 128 * regions, subject to a minimum region size of (1<<16) == 64k. * * Clients may subdivide regions internally, but when sharing between * clients, the region size is the minimum granularity. */ #define SAVAGE_CARD_HEAP 0 #define SAVAGE_AGP_HEAP 1 #define SAVAGE_NR_TEX_HEAPS 2 #define SAVAGE_NR_TEX_REGIONS 16 /* num. of global texture manage list element*/ #define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16 /* each region 64K, Jiayo Hsu */ #endif /* __SAVAGE_SAREA_DEFINES__ */ /* drm_tex_region_t define in drm.h */ typedef drm_tex_region_t drm_savage_tex_region_t; /* Setup registers for 2D, X server */ typedef struct { unsigned int pitch; } drm_savage_server_regs_t; typedef struct _drm_savage_sarea { /* The channel for communication of state information to the kernel * on firing a vertex dma buffer. */ unsigned int setup[28]; /* 3D context registers */ drm_savage_server_regs_t server_state; unsigned int dirty; unsigned int vertsize; /* vertext size */ /* The current cliprects, or a subset thereof. */ drm_clip_rect_t boxes[SAVAGE_NR_SAREA_CLIPRECTS]; unsigned int nbox; /* Information about the most recently used 3d drawable. The * client fills in the req_* fields, the server fills in the * exported_ fields and puts the cliprects into boxes, above. * * The client clears the exported_drawable field before * clobbering the boxes data. */ unsigned int req_drawable; /* the X drawable id */ unsigned int req_draw_buffer; /* SAVAGE_FRONT or SAVAGE_BACK */ unsigned int exported_drawable; unsigned int exported_index; unsigned int exported_stamp; unsigned int exported_buffers; unsigned int exported_nfront; unsigned int exported_nback; int exported_back_x, exported_front_x, exported_w; int exported_back_y, exported_front_y, exported_h; drm_clip_rect_t exported_boxes[SAVAGE_NR_SAREA_CLIPRECTS]; /* Counters for aging textures and for client-side throttling. */ unsigned int status[4]; /* LRU lists for texture memory in agp space and on the card. */ drm_tex_region_t texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS+1]; unsigned int texAge[SAVAGE_NR_TEX_HEAPS]; /* Mechanism to validate card state. */ int ctxOwner; unsigned long shadow_status[64];/*too big?*/ /*agp offset*/ unsigned long agp_offset; } drm_savage_sarea_t,*drm_savage_sarea_ptr; typedef struct drm_savage_init { unsigned long sarea_priv_offset; int chipset; int sgram; unsigned int maccess; unsigned int fb_cpp; unsigned int front_offset, front_pitch; unsigned int back_offset, back_pitch; unsigned int depth_cpp; unsigned int depth_offset, depth_pitch; unsigned int texture_offset[SAVAGE_NR_TEX_HEAPS]; unsigned int texture_size[SAVAGE_NR_TEX_HEAPS]; unsigned long fb_offset; unsigned long mmio_offset; unsigned long status_offset; } drm_savage_init_t; typedef struct drm_savage_fullscreen { enum { SAVAGE_INIT_FULLSCREEN = 0x01, SAVAGE_CLEANUP_FULLSCREEN = 0x02 } func; } drm_savage_fullscreen_t; typedef struct drm_savage_clear { unsigned int flags; unsigned int clear_color; unsigned int clear_depth; unsigned int color_mask; unsigned int depth_mask; } drm_savage_clear_t; typedef struct drm_savage_vertex { int idx; /* buffer to queue */ int used; /* bytes in use */ int discard; /* client finished with buffer? */ } drm_savage_vertex_t; typedef struct drm_savage_indices { int idx; /* buffer to queue */ unsigned int start; unsigned int end; int discard; /* client finished with buffer? */ } drm_savage_indices_t; typedef struct drm_savage_iload { int idx; unsigned int dstorg; unsigned int length; } drm_savage_iload_t; typedef struct _drm_savage_blit { unsigned int planemask; unsigned int srcorg; unsigned int dstorg; int src_pitch, dst_pitch; int delta_sx, delta_sy; int delta_dx, delta_dy; int height, ydir; /* flip image vertically */ int source_pitch, dest_pitch; } drm_savage_blit_t; #endif n133' href='#n133'>133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
*/
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#define IS_I965G(dev) (dev->pci_device == 0x2972 || \
dev->pci_device == 0x2982 || \
dev->pci_device == 0x2992 || \
dev->pci_device == 0x29A2 || \
dev->pci_device == 0x2A02 || \
dev->pci_device == 0x2A12)
#define IS_G33(dev) (dev->pci_device == 0x29C2 || \
dev->pci_device == 0x29B2 || \
dev->pci_device == 0x29D2)
/* Really want an OS-independent resettable timer. Would like to have
* this loop run for (eg) 3 sec, but have the timer reset every time
* the head pointer changes, so that EBUSY only happens if the ring
* actually stalls for (eg) 3 seconds.
*/
int i915_wait_ring(drm_device_t * dev, int n, const char *caller)
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
int i;
for (i = 0; i < 10000; i++) {
ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->Size;
if (ring->space >= n)
return 0;
dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
if (ring->head != last_head)
i = 0;
last_head = ring->head;
DRM_UDELAY(1);
}
return DRM_ERR(EBUSY);
}
void i915_kernel_lost_context(drm_device_t * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->Size;
if (ring->head == ring->tail)
dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}
static int i915_dma_cleanup(drm_device_t * dev)
{
/* Make sure interrupts are disabled here because the uninstall ioctl
* may not have been called from userspace and after dev_private
* is freed, it's too late.
*/
if (dev->irq)
drm_irq_uninstall(dev);
if (dev->dev_private) {
drm_i915_private_t *dev_priv =
(drm_i915_private_t *) dev->dev_private;
if (dev_priv->ring.virtual_start) {
drm_core_ioremapfree(&dev_priv->ring.map, dev);
}
if (dev_priv->status_page_dmah) {
drm_pci_free(dev, dev_priv->status_page_dmah);
/* Need to rewrite hardware status page */
I915_WRITE(0x02080, 0x1ffff000);
}
if (dev_priv->status_gfx_addr) {
dev_priv->status_gfx_addr = 0;
drm_core_ioremapfree(&dev_priv->hws_map, dev);
I915_WRITE(0x02080, 0x1ffff000);
}
drm_free(dev->dev_private, sizeof(drm_i915_private_t),
DRM_MEM_DRIVER);
dev->dev_private = NULL;
}
return 0;
}
static int i915_initialize(drm_device_t * dev,
drm_i915_private_t * dev_priv,
drm_i915_init_t * init)
{
memset(dev_priv, 0, sizeof(drm_i915_private_t));
dev_priv->sarea = drm_getsarea(dev);
if (!dev_priv->sarea) {
DRM_ERROR("can not find sarea!\n");
dev->dev_private = (void *)dev_priv;
i915_dma_cleanup(dev);
return DRM_ERR(EINVAL);
}
dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);
if (!dev_priv->mmio_map) {
dev->dev_private = (void *)dev_priv;
i915_dma_cleanup(dev);
DRM_ERROR("can not find mmio map!\n");
return DRM_ERR(EINVAL);
}
dev_priv->sarea_priv = (drm_i915_sarea_t *)
((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);
dev_priv->ring.Start = init->ring_start;
dev_priv->ring.End = init->ring_end;
dev_priv->ring.Size = init->ring_size;
dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
dev_priv->ring.map.offset = init->ring_start;
dev_priv->ring.map.size = init->ring_size;
dev_priv->ring.map.type = 0;
dev_priv->ring.map.flags = 0;
dev_priv->ring.map.mtrr = 0;
drm_core_ioremap(&dev_priv->ring.map, dev);
if (dev_priv->ring.map.handle == NULL) {
dev->dev_private = (void *)dev_priv;
i915_dma_cleanup(dev);
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
return DRM_ERR(ENOMEM);
}
dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
dev_priv->cpp = init->cpp;
dev_priv->sarea_priv->pf_current_page = 0;
/* We are using separate values as placeholders for mechanisms for
* private backbuffer/depthbuffer usage.
*/
dev_priv->use_mi_batchbuffer_start = 0;
/* Allow hardware batchbuffers unless told otherwise.
*/
dev_priv->allow_batchbuffer = 1;
/* Enable vblank on pipe A for older X servers
*/
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
/* Program Hardware Status Page */
if (!IS_G33(dev)) {
dev_priv->status_page_dmah =
drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff);
if (!dev_priv->status_page_dmah) {
dev->dev_private = (void *)dev_priv;
i915_dma_cleanup(dev);
DRM_ERROR("Can not allocate hardware status page\n");
return DRM_ERR(ENOMEM);
}
dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
I915_WRITE(0x02080, dev_priv->dma_status_page);
}
DRM_DEBUG("Enabled hardware status page\n");
dev->dev_private = (void *)dev_priv;
return 0;
}
static int i915_dma_resume(drm_device_t * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
DRM_DEBUG("%s\n", __FUNCTION__);
if (!dev_priv->sarea) {
DRM_ERROR("can not find sarea!\n");
return DRM_ERR(EINVAL);
}
if (!dev_priv->mmio_map) {
DRM_ERROR("can not find mmio map!\n");
return DRM_ERR(EINVAL);
}
if (dev_priv->ring.map.handle == NULL) {
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
return DRM_ERR(ENOMEM);
}
/* Program Hardware Status Page */
if (!dev_priv->hw_status_page) {
DRM_ERROR("Can not find hardware status page\n");
return DRM_ERR(EINVAL);
}
DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
if (dev_priv->status_gfx_addr != 0)
I915_WRITE(0x02080, dev_priv->status_gfx_addr);
else
I915_WRITE(0x02080, dev_priv->dma_status_page);
DRM_DEBUG("Enabled hardware status page\n");
return 0;
}
static int i915_dma_init(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
drm_i915_private_t *dev_priv;
drm_i915_init_t init;
int retcode = 0;
DRM_COPY_FROM_USER_IOCTL(init, (drm_i915_init_t __user *) data,
sizeof(init));
switch (init.func) {
case I915_INIT_DMA:
dev_priv = drm_alloc(sizeof(drm_i915_private_t),
DRM_MEM_DRIVER);
if (dev_priv == NULL)
return DRM_ERR(ENOMEM);
retcode = i915_initialize(dev, dev_priv, &init);
break;
case I915_CLEANUP_DMA:
retcode = i915_dma_cleanup(dev);
break;
case I915_RESUME_DMA:
retcode = i915_dma_resume(dev);
break;
default:
retcode = DRM_ERR(EINVAL);
break;
}
return retcode;
}
/* Implement basically the same security restrictions as hardware does
* for MI_BATCH_NON_SECURE. These can be made stricter at any time.
*
* Most of the calculations below involve calculating the size of a
* particular instruction. It's important to get the size right as
* that tells us where the next instruction to check is. Any illegal
* instruction detected will be given a size of zero, which is a
* signal to abort the rest of the buffer.
*/
static int do_validate_cmd(int cmd)
{
switch (((cmd >> 29) & 0x7)) {
case 0x0:
switch ((cmd >> 23) & 0x3f) {
case 0x0:
return 1; /* MI_NOOP */
case 0x4:
return 1; /* MI_FLUSH */
default:
return 0; /* disallow everything else */
}
break;
case 0x1:
return 0; /* reserved */
case 0x2:
return (cmd & 0xff) + 2; /* 2d commands */
case 0x3:
if (((cmd >> 24) & 0x1f) <= 0x18)
return 1;
switch ((cmd >> 24) & 0x1f) {
case 0x1c:
return 1;
case 0x1d:
switch ((cmd >> 16) & 0xff) {
case 0x3:
return (cmd & 0x1f) + 2;
case 0x4:
return (cmd & 0xf) + 2;
default:
return (cmd & 0xffff) + 2;
}
case 0x1e:
if (cmd & (1 << 23))
return (cmd & 0xffff) + 1;
else
return 1;
case 0x1f:
if ((cmd & (1 << 23)) == 0) /* inline vertices */
return (cmd & 0x1ffff) + 2;
else if (cmd & (1 << 17)) /* indirect random */
if ((cmd & 0xffff) == 0)
return 0; /* unknown length, too hard */
else
return (((cmd & 0xffff) + 1) / 2) + 1;
else
return 2; /* indirect sequential */
default:
return 0;
}
default:
return 0;
}
return 0;
}
static int validate_cmd(int cmd)
{
int ret = do_validate_cmd(cmd);
/* printk("validate_cmd( %x ): %d\n", cmd, ret); */
return ret;
}
static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i;
RING_LOCALS;
if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)
return DRM_ERR(EINVAL);
BEGIN_LP_RING((dwords+1)&~1);
for (i = 0; i < dwords;) {
int cmd, sz;
if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))
return DRM_ERR(EINVAL);
if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
return DRM_ERR(EINVAL);
OUT_RING(cmd);
while (++i, --sz) {
if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
sizeof(cmd))) {
return DRM_ERR(EINVAL);
}
OUT_RING(cmd);
}
}
if (dwords & 1)
OUT_RING(0);
ADVANCE_LP_RING();
return 0;
}
static int i915_emit_box(drm_device_t * dev,
drm_clip_rect_t __user * boxes,
int i, int DR1, int DR4)
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_clip_rect_t box;
RING_LOCALS;
if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
return DRM_ERR(EFAULT);
}
if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {
DRM_ERROR("Bad box %d,%d..%d,%d\n",
box.x1, box.y1, box.x2, box.y2);
return DRM_ERR(EINVAL);
}
if (IS_I965G(dev)) {
BEGIN_LP_RING(4);
OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
OUT_RING(DR4);
ADVANCE_LP_RING();
} else {
BEGIN_LP_RING(6);
OUT_RING(GFX_OP_DRAWRECT_INFO);
OUT_RING(DR1);
OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
OUT_RING(DR4);
OUT_RING(0);
ADVANCE_LP_RING();
}
return 0;
}
/* XXX: Emitting the counter should really be moved to part of the IRQ
* emit. For now, do it in both places:
*/
void i915_emit_breadcrumb(drm_device_t *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
RING_LOCALS;
dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
if (dev_priv->counter > 0x7FFFFFFFUL)
dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
BEGIN_LP_RING(4);
OUT_RING(CMD_STORE_DWORD_IDX);
OUT_RING(20);
OUT_RING(dev_priv->counter);
OUT_RING(0);
ADVANCE_LP_RING();
}
int i915_emit_mi_flush(drm_device_t *dev, uint32_t flush)
{
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t flush_cmd = CMD_MI_FLUSH;
RING_LOCALS;
flush_cmd |= flush;
i915_kernel_lost_context(dev);
BEGIN_LP_RING(4);
OUT_RING(flush_cmd);
OUT_RING(0);
OUT_RING(0);
OUT_RING(0);
ADVANCE_LP_RING();
return 0;
}
static int i915_dispatch_cmdbuffer(drm_device_t * dev,
drm_i915_cmdbuffer_t * cmd)
{
#ifdef I915_HAVE_FENCE
drm_i915_private_t *dev_priv = dev->dev_private;
#endif
int nbox = cmd->num_cliprects;
int i = 0, count, ret;
if (cmd->sz & 0x3) {
DRM_ERROR("alignment");
return DRM_ERR(EINVAL);
}
i915_kernel_lost_context(dev);
count = nbox ? nbox : 1;
for (i = 0; i < count; i++) {
if (i < nbox) {
ret = i915_emit_box(dev, cmd->cliprects, i,
cmd->DR1, cmd->DR4);
if (ret)
return ret;
}
ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4);
if (ret)
return ret;
}
i915_emit_breadcrumb( dev );
#ifdef I915_HAVE_FENCE
drm_fence_flush_old(dev, 0, dev_priv->counter);
#endif
return 0;
}
static int i915_dispatch_batchbuffer(drm_device_t * dev,
drm_i915_batchbuffer_t * batch)
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_clip_rect_t __user *boxes = batch->cliprects;
int nbox = batch->num_cliprects;
int i = 0, count;
RING_LOCALS;
if ((batch->start | batch->used) & 0x7) {
DRM_ERROR("alignment");
return DRM_ERR(EINVAL);
}
i915_kernel_lost_context(dev);
count = nbox ? nbox : 1;
for (i = 0; i < count; i++) {
if (i < nbox) {
int ret = i915_emit_box(dev, boxes, i,
batch->DR1, batch->DR4);
if (ret)
return ret;
}
if (dev_priv->use_mi_batchbuffer_start) {
BEGIN_LP_RING(2);
OUT_RING(MI_BATCH_BUFFER_START | (2 << 6));
OUT_RING(batch->start | MI_BATCH_NON_SECURE);
ADVANCE_LP_RING();
} else {
BEGIN_LP_RING(4);
OUT_RING(MI_BATCH_BUFFER);
OUT_RING(batch->start | MI_BATCH_NON_SECURE);
OUT_RING(batch->start + batch->used - 4);
OUT_RING(0);
ADVANCE_LP_RING();
}
}
i915_emit_breadcrumb( dev );
#ifdef I915_HAVE_FENCE
drm_fence_flush_old(dev, 0, dev_priv->counter);
#endif
return 0;
}
static void i915_do_dispatch_flip(drm_device_t * dev, int pipe, int sync)
{
drm_i915_private_t *dev_priv = dev->dev_private;
u32 num_pages, current_page, next_page, dspbase;
int shift = 2 * pipe, x, y;
RING_LOCALS;
/* Calculate display base offset */
num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;
current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3;
next_page = (current_page + 1) % num_pages;
switch (next_page) {
default:
case 0:
dspbase = dev_priv->sarea_priv->front_offset;
break;
case 1:
dspbase = dev_priv->sarea_priv->back_offset;
break;
case 2:
dspbase = dev_priv->sarea_priv->third_offset;
break;
}
if (pipe == 0) {
x = dev_priv->sarea_priv->pipeA_x;
y = dev_priv->sarea_priv->pipeA_y;
} else {
x = dev_priv->sarea_priv->pipeB_x;
y = dev_priv->sarea_priv->pipeB_y;
}
dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp;
DRM_DEBUG("pipe=%d current_page=%d dspbase=0x%x\n", pipe, current_page,
dspbase);
BEGIN_LP_RING(4);
OUT_RING(sync ? 0 :
(MI_WAIT_FOR_EVENT | (pipe ? MI_WAIT_FOR_PLANE_B_FLIP :
MI_WAIT_FOR_PLANE_A_FLIP)));
OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) |
(pipe ? DISPLAY_PLANE_B : DISPLAY_PLANE_A));
OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp);
OUT_RING(dspbase);
ADVANCE_LP_RING();
dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift);
dev_priv->sarea_priv->pf_current_page |= next_page << shift;
}
void i915_dispatch_flip(drm_device_t * dev, int pipes, int sync)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i;
DRM_DEBUG("%s: pipes=0x%x pfCurrentPage=%d\n",
__FUNCTION__,
pipes, dev_priv->sarea_priv->pf_current_page);
i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH);
for (i = 0; i < 2; i++)
if (pipes & (1 << i))
i915_do_dispatch_flip(dev, i, sync);
i915_emit_breadcrumb(dev);
#ifdef I915_HAVE_FENCE
if (!sync)
drm_fence_flush_old(dev, 0, dev_priv->counter);
#endif
}
static int i915_quiescent(drm_device_t * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
i915_kernel_lost_context(dev);
return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__);
}
static int i915_flush_ioctl(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
LOCK_TEST_WITH_RETURN(dev, filp);
return i915_quiescent(dev);
}
static int i915_batchbuffer(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
dev_priv->sarea_priv;
drm_i915_batchbuffer_t batch;
int ret;
if (!dev_priv->allow_batchbuffer) {
DRM_ERROR("Batchbuffer ioctl disabled\n");
return DRM_ERR(EINVAL);
}
DRM_COPY_FROM_USER_IOCTL(batch, (drm_i915_batchbuffer_t __user *) data,
sizeof(batch));
DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
batch.start, batch.used, batch.num_cliprects);
LOCK_TEST_WITH_RETURN(dev, filp);
if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects,
batch.num_cliprects *
sizeof(drm_clip_rect_t)))
return DRM_ERR(EFAULT);
ret = i915_dispatch_batchbuffer(dev, &batch);
sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return ret;
}
static int i915_cmdbuffer(DRM_IOCTL_ARGS)
{
DRM_DEVICE;