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