Hello Sirs:
    The following patch is based on 2.6.31 mainline kernel for the system hang 
issue caused by 3D scaling + ACPI. The modified c files includes 
1. via_dmablit.c
2. via_dma.c
3. via_drv.c
4. via_irq.c
5. via_map.c
6. via_mm.c
7. via_verifier.c

Signed-off-by: Bruce Chang<brucech...@via.com.tw>
diff -ruN old/drivers/gpu/drm/via/via_dmablit.c 
new/drivers/gpu/drm/via/via_dmablit.c
--- old/drivers/gpu/drm/via/via_dmablit.c       2009-07-14 16:42:05.000000000 
+0800
+++ new/drivers/gpu/drm/via/via_dmablit.c       2009-07-14 16:45:28.000000000 
+0800
@@ -195,8 +195,10 @@
        default:
                vsg->state = dr_via_sg_init;
        }
-       vfree(vsg->bounce_buffer);
-       vsg->bounce_buffer = NULL;
+       if (vsg->bounce_buffer) {
+               vfree(vsg->bounce_buffer);
+               vsg->bounce_buffer = NULL;
+       }
        vsg->free_on_sequence = 0;
 }
diff -ruN old/drivers/gpu/drm/via/via_dma.c new/drivers/gpu/drm/via/via_dma.c
--- old/drivers/gpu/drm/via/via_dma.c   2009-07-14 16:42:05.000000000 +0800
+++ new/drivers/gpu/drm/via/via_dma.c   2009-07-14 16:45:28.000000000 +0800
@@ -68,6 +68,15 @@
        *vb++ = (w2);                           \
        dev_priv->dma_low += 8;
 
+#define VIA_OUT_VIDEO_AGP_BUFFER(cmd1, cmd2)    \
+       do {                                    \
+               *cur_virtual++ = cmd1;          \
+               *cur_virtual++ = cmd2;          \
+               cmdbuf_info.cmd_size += 8;      \
+       } while (0);
+
+static void via_cmdbuf_flush(struct drm_via_private *dev_priv,
+       uint32_t cmd_type);
 static void via_cmdbuf_start(drm_via_private_t * dev_priv);
 static void via_cmdbuf_pause(drm_via_private_t * dev_priv);
 static void via_cmdbuf_reset(drm_via_private_t * dev_priv);
@@ -75,6 +84,7 @@
 static int via_wait_idle(drm_via_private_t * dev_priv);
 static void via_pad_cache(drm_via_private_t * dev_priv, int qwords);
 
+
 /*
  * Free space in command buffer.
  */
@@ -155,17 +165,34 @@
 
 int via_dma_cleanup(struct drm_device * dev)
 {
+       struct drm_via_video_save_head *pnode;
+       int i;
+
+       for (pnode = via_video_save_head; pnode; pnode =
+               (struct drm_via_video_save_head *)pnode->next)
+               memcpy(pnode->psystemmem, pnode->pvideomem, pnode->size);
        if (dev->dev_private) {
                drm_via_private_t *dev_priv =
                    (drm_via_private_t *) dev->dev_private;
 
                if (dev_priv->ring.virtual_start) {
-                       via_cmdbuf_reset(dev_priv);
+                       if (dev_priv->cr_status == CR_FOR_RINGBUFFER)
+                               via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP);
+
+                       via_wait_idle(dev_priv);
 
                        drm_core_ioremapfree(&dev_priv->ring.map, dev);
                        dev_priv->ring.virtual_start = NULL;
                }
 
+               for (i = 0; i < 3; i++) {
+                       if (dev_priv->video_agp_address_map[i].handle &&
+                        dev_priv->video_agp_address_map[i].size)
+                               drm_core_ioremapfree(dev_priv->
+                               video_agp_address_map+i, dev);
+                       /*Fix for suspend reuse video buf*/
+                       dev_priv->video_agp_address_map[i].handle = NULL;
+               }
        }
 
        return 0;
@@ -175,6 +202,7 @@
                          drm_via_private_t * dev_priv,
                          drm_via_dma_init_t * init)
 {
+       struct drm_via_video_save_head *pnode;
        if (!dev_priv || !dev_priv->mmio) {
                DRM_ERROR("via_dma_init called before via_map_init\n");
                return -EFAULT;
@@ -195,6 +223,9 @@
                return -EINVAL;
        }
 
+       for (pnode = via_video_save_head; pnode; pnode = pnode->next)
+               memcpy(pnode->pvideomem, pnode->psystemmem, pnode->size);
+
        dev_priv->ring.map.offset = dev->agp->base + init->offset;
        dev_priv->ring.map.size = init->size;
        dev_priv->ring.map.type = 0;
@@ -224,6 +255,7 @@
 
        via_cmdbuf_start(dev_priv);
 
+       dev_priv->cr_status = CR_FOR_RINGBUFFER;
        return 0;
 }
 
@@ -270,7 +302,6 @@
                DRM_ERROR("called without initializing AGP ring buffer.\n");
                return -EFAULT;
        }
-
        if (cmd->size > VIA_PCI_BUF_SIZE) {
                return -ENOMEM;
        }
@@ -289,14 +320,14 @@
                                       cmd->size, dev, 1))) {
                return ret;
        }
-
        vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size);
        if (vb == NULL) {
                return -EAGAIN;
        }
-
        memcpy(vb, dev_priv->pci_buf, cmd->size);
-
+       ret = DRM_COPY_FROM_USER(vb, cmd->buf, cmd->size);
+       if (ret)
+               return -EFAULT;
        dev_priv->dma_low += cmd->size;
 
        /*
@@ -332,12 +363,42 @@
 static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file 
*file_priv)
 {
        drm_via_cmdbuffer_t *cmdbuf = data;
-       int ret;
+       drm_via_private_t *dev_priv = dev->dev_private;
+       int ret = 0, count;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
        DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size);
 
+       if (dev_priv->cr_status == CR_FOR_VIDEO) {
+               /* Because our driver will hook CR stop cmd behind video cmd,
+               * all we need to do here is to wait for CR idle,
+               * and initialize ring buffer.
+               */
+               count = 10000000;
+               while (count-- && (VIA_READ(VIA_REG_STATUS) &
+                       VIA_CMD_RGTR_BUSY))
+                       cpu_relax();
+               /* Seldom happen */
+               if (count < 0) {
+                       DRM_INFO("The CR can't be idle from video agp cmd \
+                               dispatch when it is needed by ring buffer \n");
+                       return -1;
+               }
+               /* CR has been idle so that we need to initialize ring buffer */
+               dev_priv->dma_ptr = dev_priv->ring.virtual_start;
+               dev_priv->dma_low = 0;
+               dev_priv->dma_high = 0x1000000;
+               dev_priv->dma_wrap = 0x1000000;
+               dev_priv->dma_offset = 0x0;
+               dev_priv->last_pause_ptr = NULL;
+               dev_priv->hw_addr_ptr = dev_priv->mmio->handle + 0x418;
+
+               via_cmdbuf_start(dev_priv);
+
+               dev_priv->cr_status = CR_FOR_RINGBUFFER;
+
+       }
        ret = via_dispatch_cmdbuffer(dev, cmdbuf);
        if (ret) {
                return ret;
@@ -346,6 +407,134 @@
        return 0;
 }
 
+int via_cmdbuffer_video_agp(struct drm_device *dev, void *data,
+       struct drm_file *file_priv)
+{
+       drm_via_private_t *dev_priv = dev->dev_private;
+       struct drm_via_video_agp_cmd cmdbuf_info;
+       int count;
+       u32 start_addr, start_addr_lo;
+       u32 end_addr, end_addr_lo;
+       u32 pause_addr, pause_addr_hi, pause_addr_lo;
+       u32 *cur_virtual;
+       u32 command;
+       int i = 0;
+       struct drm_local_map map;
+
+       LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+       /* Check whether CR services for ring buffer or for video engine. */
+       if (dev_priv->cr_status == CR_FOR_RINGBUFFER) {
+               /* Here we need to hook stop cmd in tail of ringbuffer
+                * in order to stop CR, for we will reset start/end/pause
+                * address for fetch cmd from video AGP buffer
+                */
+                via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP);
+       }
+
+       /* Set CR status here to avoid ring buffer crush in case we
+       * can't initialize CR for video properly
+       */
+       dev_priv->cr_status = CR_FOR_VIDEO;
+
+       /* Wait idle since we will reset CR relevant registers. */
+       count = 10000000;
+       while (count-- && (VIA_READ(VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY))
+               cpu_relax();
+
+       /* Seldom happen */
+       if (count < 0) {
+               DRM_INFO("The CR can't be idle from video agp cmd dispatch \
+                       when it is needed by ring buffer \n");
+               return -1;
+       }
+
+       /* Till here, the CR has been idle, all need to here is to initialize
+       * CR START/END/PAUSE address registers according to video AGP buffer
+       * location and size. BE LUCKY!!!
+       */
+       cmdbuf_info = *(struct drm_via_video_agp_cmd *)data;
+
+       start_addr = cmdbuf_info.offset + dev->agp->base;
+       end_addr = cmdbuf_info.buffer_size + start_addr;
+
+       if ((cmdbuf_info.buffer_size & 0xFF) ||
+       (start_addr + 2 * 0xFF > end_addr) ||
+       start_addr & 0xFF) {
+               DRM_INFO("The video cmd is too large or you didn't set the \
+                       video cmd 2 DWORD alignment. \n");
+               return -1;
+       }
+
+       map.offset = start_addr;
+       map.size = cmdbuf_info.buffer_size;
+       map.type = map.flags = map.mtrr = 0;
+       map.handle = 0;
+
+       for (i = 0; i < 3; i++) {
+               if ((dev_priv->video_agp_address_map[i].offset == map.offset) &&
+               (dev_priv->video_agp_address_map[i].size == map.size) &&
+               dev_priv->video_agp_address_map[i].handle) {
+                       map.handle = dev_priv->video_agp_address_map[i].handle;
+                       break;
+               }
+               if (!dev_priv->video_agp_address_map[i].handle)
+                       break;
+       }
+
+       /* Check whether this agp cmd buffer has already been remaped before */
+       /* case: Never be remaped before */
+       if (!map.handle) {
+               drm_core_ioremap(&map, dev);
+               if (!map.handle)
+                       return -1;
+               /* there is a free hole for filling in this address map */
+               if (i < 3)
+                       dev_priv->video_agp_address_map[i] = map;
+               else {
+                       drm_core_ioremapfree(dev_priv->video_agp_address_map,
+                               dev);
+                       dev_priv->video_agp_address_map[0] = map;
+               }
+       }
+
+       cur_virtual = map.handle + cmdbuf_info.cmd_size;
+
+       VIA_OUT_VIDEO_AGP_BUFFER(HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) |
+                       (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16);
+
+       /* pause register need 0xFF alignment */
+       do {
+               VIA_OUT_VIDEO_AGP_BUFFER(HC_DUMMY, HC_DUMMY);
+       } while (cmdbuf_info.cmd_size & 0xFF);
+       pause_addr = cmdbuf_info.cmd_size + start_addr - 8;
+
+       pause_addr_lo = ((HC_SubA_HAGPBpL << 24) | HC_HAGPBpID_STOP |
+       (pause_addr & HC_HAGPBpL_MASK));
+       pause_addr_hi = ((HC_SubA_HAGPBpH << 24) | (pause_addr >> 24));
+       start_addr_lo = ((HC_SubA_HAGPBstL << 24) | (start_addr & 0xFFFFFF));
+       end_addr_lo = ((HC_SubA_HAGPBendL << 24) | (end_addr & 0xFFFFFF));
+       command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) |
+                  ((end_addr & 0xff000000) >> 16));
+       *(cur_virtual-2) = pause_addr_hi;
+       *(cur_virtual-1) = pause_addr_lo;
+
+       via_flush_write_combine();
+
+       VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16));
+       VIA_WRITE(VIA_REG_TRANSPACE, command);
+       VIA_WRITE(VIA_REG_TRANSPACE, start_addr_lo);
+       VIA_WRITE(VIA_REG_TRANSPACE, end_addr_lo);
+
+       VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi);
+       VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo);
+       DRM_WRITEMEMORYBARRIER();
+       /* fire */
+       VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK);
+
+       return 0;
+}
+
 static int via_dispatch_pci_cmdbuffer(struct drm_device * dev,
                                      drm_via_cmdbuffer_t * cmd)
 {
@@ -357,7 +546,6 @@
        }
        if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size))
                return -EFAULT;
-
        if ((ret =
             via_verify_command_stream((uint32_t *) dev_priv->pci_buf,
                                       cmd->size, dev, 0))) {
@@ -481,13 +669,13 @@
 {
        int count = 10000000;
 
-       while (!(VIA_READ(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && --count)
+       while (!(VIA_READ(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && count--)
                ;
 
-       while (count && (VIA_READ(VIA_REG_STATUS) &
+       while (count-- && (VIA_READ(VIA_REG_STATUS) &
                           (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY |
                            VIA_3D_ENG_BUSY)))
-               --count;
+               ;
        return count;
 }
 
@@ -707,7 +895,7 @@
        switch (d_siz->func) {
        case VIA_CMDBUF_SPACE:
                while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz->size)
-                      && --count) {
+                      && count--) {
                        if (!d_siz->wait) {
                                break;
                        }
@@ -719,7 +907,7 @@
                break;
        case VIA_CMDBUF_LAG:
                while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz->size)
-                      && --count) {
+                      && count--) {
                        if (!d_siz->wait) {
                                break;
                        }
@@ -737,6 +925,149 @@
        return ret;
 }
 
+/*The following functions are for ACPI*/
+
+static void initialize3Dengine(drm_via_private_t *dev_priv)
+{
+       int i = 0;
+
+       VIA_WRITE(0x43C, 0x00010000);
+
+       for (i = 0; i <= 0x7D; i++)
+               VIA_WRITE(0x440, (unsigned long) i << 24);
+
+       VIA_WRITE(0x43C, 0x00020000);
+
+       for (i = 0; i <= 0x94; i++)
+               VIA_WRITE(0x440, (unsigned long) i << 24);
+
+       VIA_WRITE(0x440, 0x82400000);
+       VIA_WRITE(0x43C, 0x01020000);
+
+       for (i = 0; i <= 0x94; i++)
+               VIA_WRITE(0x440, (unsigned long) i << 24);
+
+       VIA_WRITE(0x440, 0x82400000);
+       VIA_WRITE(0x43C, 0xfe020000);
+
+       for (i = 0; i <= 0x03; i++)
+               VIA_WRITE(0x440, (unsigned long) i << 24);
+
+       VIA_WRITE(0x43C, 0x00030000);
+
+       for (i = 0; i <= 0xff; i++)
+               VIA_WRITE(0x440, 0);
+
+       VIA_WRITE(0x43C, 0x00100000);
+       VIA_WRITE(0x440, 0x00333004);
+       VIA_WRITE(0x440, 0x10000002);
+       VIA_WRITE(0x440, 0x60000000);
+       VIA_WRITE(0x440, 0x61000000);
+       VIA_WRITE(0x440, 0x62000000);
+       VIA_WRITE(0x440, 0x63000000);
+       VIA_WRITE(0x440, 0x64000000);
+
+       VIA_WRITE(0x43C, 0x00fe0000);
+       VIA_WRITE(0x440, 0x40008c0f);
+       VIA_WRITE(0x440, 0x44000000);
+       VIA_WRITE(0x440, 0x45080C04);
+       VIA_WRITE(0x440, 0x46800408);
+       VIA_WRITE(0x440, 0x50000000);
+       VIA_WRITE(0x440, 0x51000000);
+       VIA_WRITE(0x440, 0x52000000);
+       VIA_WRITE(0x440, 0x53000000);
+
+
+       VIA_WRITE(0x43C, 0x00fe0000);
+       VIA_WRITE(0x440, 0x08000001);
+       VIA_WRITE(0x440, 0x0A000183);
+       VIA_WRITE(0x440, 0x0B00019F);
+       VIA_WRITE(0x440, 0x0C00018B);
+       VIA_WRITE(0x440, 0x0D00019B);
+       VIA_WRITE(0x440, 0x0E000000);
+       VIA_WRITE(0x440, 0x0F000000);
+       VIA_WRITE(0x440, 0x10000000);
+       VIA_WRITE(0x440, 0x11000000);
+       VIA_WRITE(0x440, 0x20000000);
+}
+/* For acpi case, when system resume from suspend or hibernate,
+ * need to re-initialize dma info into HW
+ */
+int via_drm_resume(struct pci_dev *pci)
+{
+       struct drm_device *dev = (struct drm_device *)pci_get_drvdata(pci);
+       drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+       struct drm_via_video_save_head *pnode = 0;
+
+       pci_restore_state(pci);
+
+       if (!dev_priv->initialize)
+               return 0;
+       /* when resume, initialize 3d registers */
+       initialize3Dengine(dev_priv);
+
+       /* here we need to restore some video memory content */
+       for (pnode = via_video_save_head; pnode; pnode = pnode->next)
+               memcpy(pnode->pvideomem, pnode->psystemmem, pnode->size);
+
+       /* if pci path, return */
+       if (!dev_priv->ring.virtual_start)
+               return 0;
+
+       dev_priv->dma_ptr = dev_priv->ring.virtual_start;
+       dev_priv->dma_low = 0;
+       dev_priv->dma_high = 0x1000000;
+       dev_priv->dma_wrap = 0x1000000;
+       dev_priv->dma_offset = 0x0;
+       dev_priv->last_pause_ptr = NULL;
+       dev_priv->hw_addr_ptr = dev_priv->mmio->handle + 0x418;
+
+       via_cmdbuf_start(dev_priv);
+
+       return 0;
+}
+
+int via_drm_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct drm_device *dev = (struct drm_device *)pci_get_drvdata(pci);
+       drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+
+       struct drm_via_video_save_head *pnode = 0;
+
+       pci_save_state(pci);
+
+       if (!dev_priv->initialize)
+               return 0;
+       /*here we need to save some video mem information into system memory,
+       to keep the system consistent between suspend *before* and *after*
+       1.save only necessary */
+       for (pnode = via_video_save_head; pnode;
+               pnode = (struct drm_via_video_save_head *)pnode->next)
+               memcpy(pnode->psystemmem, pnode->pvideomem, pnode->size);
+
+       /* Only agp path need to flush the cmd */
+       if (dev_priv->ring.virtual_start)
+               via_cmdbuf_reset(dev_priv);
+
+       return 0;
+}
+int via_drm_authmagic(struct drm_device *dev, void *data,
+       struct drm_file *file_priv)
+{
+       return 0;
+}
+
+int via_drm_init_judge(struct drm_device *dev, void *data,
+       struct drm_file *file_priv)
+{
+       struct drm_via_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->initialize)
+               *(int *)data = 1;
+       else
+               *(int *)data = -1;
+       return 0;
+}
 struct drm_ioctl_desc via_ioctls[] = {
        DRM_IOCTL_DEF(DRM_VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_FREEMEM, via_mem_free, DRM_AUTH),
@@ -744,6 +1075,7 @@
        DRM_IOCTL_DEF(DRM_VIA_FB_INIT, via_fb_init, DRM_AUTH|DRM_MASTER),
        DRM_IOCTL_DEF(DRM_VIA_MAP_INIT, via_map_init, DRM_AUTH|DRM_MASTER),
        DRM_IOCTL_DEF(DRM_VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_GET_INFO, via_get_drm_info, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_DMA_INIT, via_dma_init, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_CMDBUFFER, via_cmdbuffer, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_FLUSH, via_flush_ioctl, DRM_AUTH),
@@ -751,7 +1083,10 @@
        DRM_IOCTL_DEF(DRM_VIA_CMDBUF_SIZE, via_cmdbuf_size, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_WAIT_IRQ, via_wait_irq, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_VIA_DMA_BLIT, via_dma_blit, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH)
+       DRM_IOCTL_DEF(DRM_VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_AUTH_MAGIC, via_drm_authmagic, 0),
+       DRM_IOCTL_DEF(DRM_VIA_FLUSH_VIDEO, via_cmdbuffer_video_agp, 0),
+       DRM_IOCTL_DEF(DRM_VIA_INIT_JUDGE, via_drm_init_judge, 0)
 };
 
 int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls);
 
diff -ruN old/drivers/gpu/drm/via/via_drv.c new/drivers/gpu/drm/via/via_drv.c
--- old/drivers/gpu/drm/via/via_drv.c   2009-07-14 16:42:05.000000000 +0800
+++ new/drivers/gpu/drm/via/via_drv.c   2009-07-14 16:45:28.000000000 +0800
@@ -32,10 +32,16 @@
        viadrv_PCI_IDS
 };
 
+int  via_driver_open(struct drm_device *dev, struct drm_file *priv)
+{
+    priv->authenticated = 1;
+    return 0;
+}
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ |
            DRIVER_IRQ_SHARED,
+       .open = via_driver_open,
        .load = via_driver_load,
        .unload = via_driver_unload,
        .context_dtor = via_final_context,
@@ -66,6 +72,8 @@
        .pci_driver = {
                .name = DRIVER_NAME,
                .id_table = pciidlist,
+               .suspend = via_drm_suspend,
+               .resume = via_drm_resume,
        },
 
        .name = DRIVER_NAME,
@@ -80,6 +88,7 @@
 {
        driver.num_ioctls = via_max_ioctl;
        via_init_command_verifier();
+       DRM_INFO("via verify function enabled.\n");
        return drm_init(&driver);
 }

diff -ruN old/drivers/gpu/drm/via/via_irq.c new/drivers/gpu/drm/via/via_irq.c
--- old/drivers/gpu/drm/via/via_irq.c   2009-07-14 16:42:05.000000000 +0800
+++ new/drivers/gpu/drm/via/via_irq.c   2009-07-14 16:45:28.000000000 +0800
@@ -357,6 +357,8 @@
        drm_via_irq_t *cur_irq = dev_priv->via_irqs;
        int force_sequence;
 
+       if (!dev->pdev->irq)
+               return -EINVAL;
        if (irqwait->request.irq >= dev_priv->num_irqs) {
                DRM_ERROR("Trying to wait on unknown irq %d\n",
                          irqwait->request.irq);
diff -ruN old/drivers/gpu/drm/via/via_map.c new/drivers/gpu/drm/via/via_map.c
--- old/drivers/gpu/drm/via/via_map.c   2009-07-14 16:42:05.000000000 +0800
+++ new/drivers/gpu/drm/via/via_map.c   2009-07-14 17:17:34.000000000 +0800
@@ -25,6 +25,7 @@
 #include "via_drm.h"
 #include "via_drv.h"
 
+static int associate;
 static int via_do_init_map(struct drm_device * dev, drm_via_init_t * init)
 {
        drm_via_private_t *dev_priv = dev->dev_private;
@@ -65,12 +66,35 @@
        via_init_dmablit(dev);
 
        dev->dev_private = (void *)dev_priv;
+
+       /* from doing this, we has the stuff in prev data
+        * structure to manage agp
+        */
+       if (init->agp_type != DISABLED) {
+               dev->agp_buffer_map = drm_core_findmap(dev, init->agp_offset);
+               if (!dev->agp_buffer_map) {
+                       DRM_ERROR("failed to find dma buffer region!\n");
+                       return -EINVAL;
+               }
+               if (init->agp_type == AGP_DOUBLE_BUFFER)
+                       dev_priv->agptype = AGP_DOUBLE_BUFFER;
+               if (init->agp_type == AGP_RING_BUFFER)
+                       dev_priv->agptype = AGP_RING_BUFFER;
+       } else {
+               dev_priv->agptype = DISABLED;
+               dev->agp_buffer_map = 0;
+       }
+       /* end */
+       dev_priv->initialize = 1;
+
        return 0;
 }
 
 int via_do_cleanup_map(struct drm_device * dev)
 {
+       drm_via_private_t *dev_priv = dev->dev_private;
        via_dma_cleanup(dev);
+       dev_priv->initialize = 0;
 
        return 0;
 }
@@ -95,6 +119,11 @@
 {
        drm_via_private_t *dev_priv;
        int ret = 0;
+       if (!associate) {
+               pci_set_drvdata(dev->pdev, dev);
+               dev->pdev->driver = &dev->driver->pci_driver;
+               associate = 1;
+       }
 
        dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL);
        if (dev_priv == NULL)
@@ -106,14 +135,14 @@
 
        ret = drm_sman_init(&dev_priv->sman, 2, 12, 8);
        if (ret) {
-               kfree(dev_priv);
+               dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL);
                return ret;
        }
 
        ret = drm_vblank_init(dev, 1);
        if (ret) {
                drm_sman_takedown(&dev_priv->sman);
-               kfree(dev_priv);
+               dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL);
                return ret;
        }
 
@@ -126,7 +155,23 @@
 
        drm_sman_takedown(&dev_priv->sman);
 
-       kfree(dev_priv);
+       dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL);
+
+       return 0;
+}
+int via_get_drm_info(struct drm_device *dev, void *data,
+       struct drm_file *file_priv)
+{
+       drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private;
+       struct drm_via_info *info = data;
+
+       if (!dev_priv->initialize)
+               return -EINVAL;
+
+       info->RegSize = dev_priv->mmio->size;
+       info->AgpSize = dev->agp_buffer_map->size;
+       info->RegHandle = dev_priv->mmio->offset;
+       info->AgpHandle = dev->agp_buffer_map->offset;
 
        return 0;
 }
diff -ruN old/drivers/gpu/drm/via/via_mm.c new/drivers/gpu/drm/via/via_mm.c
--- old/drivers/gpu/drm/via/via_mm.c    2009-07-14 16:42:05.000000000 +0800
+++ new/drivers/gpu/drm/via/via_mm.c    2009-07-14 16:45:28.000000000 +0800
@@ -30,6 +30,7 @@
 #include "via_drv.h"
 #include "drm_sman.h"
 
+struct drm_via_video_save_head *via_video_save_head;
 #define VIA_MM_ALIGN_SHIFT 4
 #define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1)
 
@@ -56,6 +57,8 @@
        DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size);
        return 0;
 }
+static void *global_dev;
+static void *global_file_priv;
 
 int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
@@ -78,6 +81,8 @@
 
        mutex_unlock(&dev->struct_mutex);
        DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size);
+       global_dev = dev;
+       global_file_priv = file_priv;
 
        return 0;
 
@@ -86,6 +91,17 @@
 int via_final_context(struct drm_device *dev, int context)
 {
        drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+       struct drm_ctx_list *pos, *n;
+       bool context_valid = 0;
+
+       list_for_each_entry_safe(pos, n, &dev->ctxlist, head)
+               if (pos->handle == context) {
+                       context_valid = 1;
+                       break;
+               }
+
+       if (!context_valid)
+               return 0;
 
        via_release_futex(dev_priv, context);
 
@@ -93,7 +109,8 @@
        /* Last context, perform cleanup */
        if (dev->ctx_count == 1 && dev->dev_private) {
                DRM_DEBUG("Last Context\n");
-               drm_irq_uninstall(dev);
+               if (dev->pdev->irq)
+                       drm_irq_uninstall(dev);
                via_cleanup_futex(dev_priv);
                via_do_cleanup_map(dev);
        }
@@ -114,6 +131,84 @@
        mutex_unlock(&dev->struct_mutex);
 }
 
+static int via_videomem_presave_ok(drm_via_private_t *dev_priv,
+       drm_via_mem_t *mem)
+{
+       void *pvideomem = 0, *psystemmem = 0;
+       struct drm_via_video_save_head *pnode = 0;
+
+       if (!mem || !mem->size || (mem->type != VIA_MEM_VIDEO_SAVE))
+               return 0;
+
+       /* here the mem->offset is the absolute address,
+        * not the offset within videomem
+        */
+       pvideomem = (void *)ioremap(dev_priv->fb->offset + mem->offset,
+               mem->size);
+       if (!pvideomem)
+               return 0;
+
+       psystemmem = kmalloc(mem->size, GFP_KERNEL);
+       if (!psystemmem) {
+               iounmap(pvideomem);
+               return 0;
+       }
+
+       /* map success, then save this information into
+        * a data structure for later saving usage
+        */
+       pnode = kmalloc(
+               sizeof(struct drm_via_video_save_head), GFP_KERNEL);
+       if (!pnode) {
+               iounmap(pvideomem);
+               kfree(psystemmem);
+               return 0;
+       }
+
+       pnode->next = 0;
+       pnode->psystemmem = psystemmem;
+       pnode->pvideomem = pvideomem;
+       pnode->size = mem->size;
+       pnode->token = mem->offset;
+
+       /* insert this node into list */
+       if (!via_video_save_head) {
+               via_video_save_head = pnode;
+       } else {
+               pnode->next = via_video_save_head;
+               via_video_save_head = pnode;
+       }
+
+       return 1;
+}
+
+static int via_videomem_housekeep_ok(drm_via_mem_t *mem)
+{
+       struct drm_via_video_save_head **ppnode = 0;
+       struct drm_via_video_save_head *tmpnode = 0;
+       /* if this mem's token match with one node of the list */
+       for (ppnode = &via_video_save_head; *ppnode;
+       ppnode = (struct drm_via_video_save_head **)(&((*ppnode)->next))) {
+               if ((*ppnode)->token == mem->offset)
+                       break;
+       }
+
+       if (*ppnode == 0) {
+               /* not found, the user may specify the wrong mem node to free */
+               return 0;
+       }
+
+       /* delete this node from the list and then
+       *free all the mem to avoid memory leak
+       */
+       tmpnode = *ppnode;
+       *ppnode = (*ppnode)->next;
+       iounmap(tmpnode->pvideomem);
+       kfree(tmpnode->psystemmem);
+       kfree(tmpnode);
+
+       return 1;
+}
 int via_mem_alloc(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
@@ -123,12 +218,13 @@
        drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
        unsigned long tmpSize;
 
-       if (mem->type > VIA_MEM_AGP) {
+       if (mem->type > VIA_MEM_VIDEO_SAVE) {
                DRM_ERROR("Unknown memory type allocation\n");
                return -EINVAL;
        }
        mutex_lock(&dev->struct_mutex);
-       if (0 == ((mem->type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized :
+       if (0 == ((mem->type == VIA_MEM_VIDEO ||
+               mem->type == VIA_MEM_VIDEO_SAVE) ? dev_priv->vram_initialized :
                      dev_priv->agp_initialized)) {
                DRM_ERROR
                    ("Attempt to allocate from uninitialized memory 
manager.\n");
@@ -137,15 +233,25 @@
        }
 
        tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT;
-       item = drm_sman_alloc(&dev_priv->sman, mem->type, tmpSize, 0,
-                             (unsigned long)file_priv);
+       item = drm_sman_alloc(&dev_priv->sman,
+               (mem->type == VIA_MEM_VIDEO_SAVE ? VIA_MEM_VIDEO : mem->type),
+                       tmpSize, 0, (unsigned long)file_priv);
        mutex_unlock(&dev->struct_mutex);
        if (item) {
-               mem->offset = ((mem->type == VIA_MEM_VIDEO) ?
-                             dev_priv->vram_offset : dev_priv->agp_offset) +
-                   (item->mm->
-                    offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT);
+               mem->offset = ((mem->type == VIA_MEM_VIDEO ||
+                       mem->type == VIA_MEM_VIDEO_SAVE) ?
+                       dev_priv->vram_offset : dev_priv->agp_offset) +
+                       (item->mm->offset(item->mm, item->mm_info) <<
+                       VIA_MM_ALIGN_SHIFT);
                mem->index = item->user_hash.key;
+               if (mem->type == VIA_MEM_VIDEO_SAVE) {
+                       if (!via_videomem_presave_ok(dev_priv, mem)) {
+                               mutex_lock(&dev->struct_mutex);
+                               drm_sman_free_key(&dev_priv->sman, mem->index);
+                               mutex_unlock(&dev->struct_mutex);
+                               retval = -ENOMEM;
+                       }
+               }
        } else {
                mem->offset = 0;
                mem->size = 0;
@@ -165,6 +271,10 @@
 
        mutex_lock(&dev->struct_mutex);
        ret = drm_sman_free_key(&dev_priv->sman, mem->index);
+       if (mem->type == VIA_MEM_VIDEO_SAVE) {
+               if (!via_videomem_housekeep_ok(mem))
+                       ret = -EINVAL;
+       }
        mutex_unlock(&dev->struct_mutex);
        DRM_DEBUG("free = 0x%lx\n", mem->index);
 
@@ -191,3 +301,26 @@
        mutex_unlock(&dev->struct_mutex);
        return;
 }
+static int via_fb_alloc(drm_via_mem_t *mem)
+{
+       struct drm_device *dev = global_dev;
+       struct drm_file *file_priv = global_file_priv;
+
+       if (dev && file_priv)
+               return via_mem_alloc(dev, mem, file_priv);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL(via_fb_alloc);
+
+static int via_fb_free(drm_via_mem_t *mem)
+{
+       struct drm_device *dev = global_dev;
+       struct drm_file *file_priv = global_file_priv;
+
+       if (dev && file_priv)
+               return via_mem_free(dev, mem, file_priv);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL(via_fb_free);
diff -ruN old/drivers/gpu/drm/via/via_verifier.c 
new/drivers/gpu/drm/via/via_verifier.c
--- old/drivers/gpu/drm/via/via_verifier.c      2009-07-14 16:42:05.000000000 
+0800
+++ new/drivers/gpu/drm/via/via_verifier.c      2009-07-14 16:45:28.000000000 
+0800
@@ -41,6 +41,8 @@
        state_header1,
        state_vheader5,
        state_vheader6,
+       state_header3,
+       state_header4,
        state_error
 } verifier_state_t;
 
@@ -227,7 +229,10 @@
        {0xf2, check_for_header2_err},
        {0xf0, check_for_header1_err},
        {0xcc, check_for_dummy},
-       {0x00, check_number_texunits}
+       {0x00, check_number_texunits},
+       {0x01, no_check},
+       {0x02, no_check},
+       {0x03, no_check}
 };
 
 static hazard_t table1[256];
@@ -768,11 +773,15 @@
                DRM_ERROR("Invalid VIDEO DMA command. "
                          "Attempt to access 3D- or command burst area.\n");
                return 1;
+    } else if ((0x1260 == address) || (0x126c == address)) {
+       /*For CR sync cmd, adjust for XV*/
+       return 0;
        } else if ((address > 0xCFF) && (address < 0x1300)) {
                DRM_ERROR("Invalid VIDEO DMA command. "
                          "Attempt to access PCI DMA area.\n");
                return 1;
-       } else if (address > 0x13FF) {
+       } else if ((address > 0x13FF) && (address < 0x2000)) {
+               /*Adjust for XV, VQ path*/
                DRM_ERROR("Invalid VIDEO DMA command. "
                          "Attempt to access VGA registers.\n");
                return 1;
@@ -792,8 +801,13 @@
        }
        while (dwords--) {
                if (*buf++) {
+#if 0
                        DRM_ERROR("Illegal video command tail.\n");
                        return 1;
+#else
+                       if (is_agp_header(*buf))
+                               break;
+#endif
                }
        }
        *buffer = buf;
@@ -1001,6 +1015,14 @@
                case state_vheader6:
                        state = via_check_vheader6(&buf, buf_end);
                        break;
+               case state_header3:
+               case state_header4:
+                       buf++;
+                       while (buf < buf_end && !is_agp_header(*buf))
+                               buf++;
+                       state = state_command;
+                       break;
+
                case state_command:
                        if ((HALCYON_HEADER2 == (cmd = *buf)) &&
                            supported_3d)
@@ -1016,7 +1038,12 @@
                        else if ((cmd == HALCYON_HEADER2) && !supported_3d) {
                                DRM_ERROR("Accelerated 3D is not supported on 
this chipset yet.\n");
                                state = state_error;
-                       } else {
+                       } else if ((cmd & HALCYON_HEADER_MASK) ==
+                                       HALCYON_HEADER3)
+                               state = state_header3;
+                       else if ((cmd & HALCYON_HEADER_MASK) == HALCYON_HEADER4)
+                               state = state_header4;
+                       else {
                                DRM_ERROR
                                    ("Invalid / Unimplemented DMA HEADER 
command. 0x%x\n",
                                     cmd);

Thanks and Best Regards 
=================================================
Bruce C. Chang(張祖明)
VIA Technologies, Inc. 
Address: 1F, 531, Chung-Cheng Road, Hsin-Tien, 231 Taipei
Tel: +886-2-22185452 Ext 7323
Mobile: +886-968343824
Fax: +886-2-22186282
Skype: Bruce.C.Chang
Email: brucech...@via.com.tw
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to