--- linux-2.6.26/drivers/char/drm/via_dmablit.c.ark 2008-07-16 14:06:58.802068086 +0200 +++ linux-2.6.26/drivers/char/drm/via_dmablit.c 2008-07-16 14:10:29.601067453 +0200 @@ -606,7 +606,7 @@ via_build_sg_info(struct drm_device *dev * (Not a big limitation anyway.) */ - if ((xfer->mem_stride - xfer->line_length) > 2*PAGE_SIZE) { + if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) { DRM_ERROR("Too large system memory stride. Stride: %d, " "Length: %d\n", xfer->mem_stride, xfer->line_length); return -EINVAL; --- linux-2.6.26/drivers/char/drm/via_dma.c.ark 2008-07-16 14:10:32.098193265 +0200 +++ linux-2.6.26/drivers/char/drm/via_dma.c 2008-07-16 14:18:33.894972856 +0200 @@ -261,31 +261,13 @@ static int via_dispatch_cmdbuffer(struct return -EFAULT; } - if (cmd->size > VIA_PCI_BUF_SIZE) { - return -ENOMEM; - } - - if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size)) - return -EFAULT; - - /* - * Running this function on AGP memory is dead slow. Therefore - * we run it on a temporary cacheable system memory buffer and - * copy it to AGP memory when ready. - */ - - if ((ret = - via_verify_command_stream((uint32_t *)dev_priv->pci_buf, - 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); + if (DRM_COPY_FROM_USER(vb, cmd->buf, cmd->size)) + return -EFAULT; dev_priv->dma_low += cmd->size; @@ -424,34 +406,14 @@ static int via_hook_segment(drm_via_priv dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; - /* - * If there is a possibility that the command reader will - * miss the new pause address and pause on the old one, - * In that case we need to program the new start address - * using PCI. - */ - - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; - count = 10000000; - while(diff == 0 && count--) { - paused = (VIA_READ(0x41c) & 0x80000000); - if (paused) - break; - reader = *(dev_priv->hw_addr_ptr); - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + if ((ptr - reader) <= dev_priv->dma_diff) { + count = 10000000; + while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--); } - paused = VIA_READ(0x41c) & 0x80000000; - if (paused && !no_pci_fire) { reader = *(dev_priv->hw_addr_ptr); - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; - diff &= (dev_priv->dma_high - 1); - if (diff != 0 && diff < (dev_priv->dma_high >> 1)) { - DRM_ERROR("Paused at incorrect address. " - "0x%08x, 0x%08x 0x%08x\n", - ptr, reader, dev_priv->dma_diff); - } else if (diff == 0) { + if ((ptr - reader) == dev_priv->dma_diff) { /* * There is a concern that these writes may stall the PCI bus * if the GPU is not idle. However, idling the GPU first @@ -596,7 +558,6 @@ static void via_cmdbuf_jump(drm_via_priv uint32_t pause_addr_lo, pause_addr_hi; uint32_t jump_addr_lo, jump_addr_hi; volatile uint32_t *last_pause_ptr; - uint32_t dma_low_save1, dma_low_save2; agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, @@ -623,29 +584,7 @@ static void via_cmdbuf_jump(drm_via_priv &pause_addr_lo, 0); *last_pause_ptr = pause_addr_lo; - dma_low_save1 = dev_priv->dma_low; - - /* - * Now, set a trap that will pause the regulator if it tries to rerun the old - * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause - * and reissues the jump command over PCI, while the regulator has already taken the jump - * and actually paused at the current buffer end). - * There appears to be no other way to detect this condition, since the hw_addr_pointer - * does not seem to get updated immediately when a jump occurs. - */ - - last_pause_ptr = - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0) - 1; - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0); - *last_pause_ptr = pause_addr_lo; - - dma_low_save2 = dev_priv->dma_low; - dev_priv->dma_low = dma_low_save1; via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); - dev_priv->dma_low = dma_low_save2; - via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); } @@ -730,6 +669,142 @@ static int via_cmdbuf_size(struct drm_de return ret; } +/*The following functions are for ACPI*/ + +static void initialize3Dengine(struct drm_via_private *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); + struct drm_via_private *dev_priv = + (struct drm_via_private *) dev->dev_private; + struct drm_via_video_save_head *pnode = 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); + struct drm_via_private *dev_priv = + (struct drm_via_private *) dev->dev_private; + + struct drm_via_video_save_head *pnode = 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; +} struct drm_ioctl_desc via_ioctls[] = { DRM_IOCTL_DEF(DRM_VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH), @@ -738,6 +813,7 @@ struct drm_ioctl_desc via_ioctls[] = { 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), @@ -745,7 +821,8 @@ struct drm_ioctl_desc via_ioctls[] = { 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) }; int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls); --- linux-2.6.26/drivers/char/drm/via_drm.h.ark 2008-07-16 14:18:37.735193093 +0200 +++ linux-2.6.26/drivers/char/drm/via_drm.h 2008-07-16 14:27:27.629942779 +0200 @@ -62,6 +62,13 @@ #endif +typedef struct drm_via_info { + unsigned long AgpHandle; + unsigned long AgpSize; + unsigned long RegHandle; + unsigned long RegSize; +} drm_via_info_t; + #define DRM_VIA_FENCE_TYPE_ACCEL 0x00000002 /* VIA specific ioctls */ @@ -71,7 +78,7 @@ #define DRM_VIA_FB_INIT 0x03 #define DRM_VIA_MAP_INIT 0x04 #define DRM_VIA_DEC_FUTEX 0x05 -#define NOT_USED +#define DRM_VIA_GET_INFO 0x06 #define DRM_VIA_DMA_INIT 0x07 #define DRM_VIA_CMDBUFFER 0x08 #define DRM_VIA_FLUSH 0x09 @@ -81,6 +88,7 @@ #define DRM_VIA_WAIT_IRQ 0x0d #define DRM_VIA_DMA_BLIT 0x0e #define DRM_VIA_BLIT_SYNC 0x0f +#define DRM_VIA_AUTH_MAGIC 0x11 #define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) #define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) @@ -88,6 +96,7 @@ #define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) #define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) #define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_GET_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_GET_INFO, drm_via_info_t) #define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) #define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) #define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) @@ -97,6 +106,7 @@ #define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) #define DRM_IOCTL_VIA_DMA_BLIT DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_DMA_BLIT, drm_via_dmablit_t) #define DRM_IOCTL_VIA_BLIT_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_BLIT_SYNC, drm_via_blitsync_t) +#define DRM_IOCTL_VIA_AUTH_MAGIC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_AUTH_MAGIC, drm_auth_t) /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -118,6 +128,14 @@ #define VIA_MEM_MIXED 3 #define VIA_MEM_UNKNOWN 4 +#define VIA_MEM_VIDEO_SAVE 2 /*For video memory need to be saved in ACPI */ + +typedef enum { + AGP_RING_BUFFER, + AGP_DOUBLE_BUFFER, + DISABLED +} drm_agp_type_t; + typedef struct { uint32_t offset; uint32_t size; @@ -146,6 +164,8 @@ typedef struct _drm_via_init { unsigned long fb_offset; unsigned long mmio_offset; unsigned long agpAddr; + unsigned long agp_offset; + drm_agp_type_t agp_type; } drm_via_init_t; typedef struct _drm_via_futex { @@ -278,5 +298,15 @@ typedef struct drm_via_dmablit { drm_via_blitsync_t sync; } drm_via_dmablit_t; +typedef struct drm_via_video_save_head { + void *pvideomem; + void *psystemmem; + int size; + /* token used to identify this video memory */ + unsigned long token; + void *next; +} drm_via_video_save_head_t; + +extern drm_via_video_save_head_t *via_video_save_head; #endif /* _VIA_DRM_H_ */ --- linux-2.6.26/drivers/char/drm/via_drv.c.ark 2008-07-16 14:27:32.431317920 +0200 +++ linux-2.6.26/drivers/char/drm/via_drv.c 2008-07-16 14:29:10.419072933 +0200 @@ -70,10 +70,18 @@ static struct drm_bo_driver via_bo_drive }; static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); + +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, @@ -107,6 +115,8 @@ static struct drm_driver driver = { .id_table = pciidlist, .probe = probe, .remove = __devexit_p(drm_cleanup_pci), + .suspend = via_drm_suspend, + .resume = via_drm_resume, }, .fence_driver = &via_fence_driver, .bo_driver = &via_bo_driver, --- linux-2.6.26/drivers/char/drm/via_drv.h.ark 2008-07-16 14:29:11.856954781 +0200 +++ linux-2.6.26/drivers/char/drm/via_drv.h 2008-07-16 14:32:20.467945447 +0200 @@ -87,6 +87,10 @@ typedef struct drm_via_private { drm_local_map_t *sarea; drm_local_map_t *fb; drm_local_map_t *mmio; + /* support managing agp in our own prev data structure */ + drm_local_map_t *agp; + drm_agp_type_t agptype; + /* end */ unsigned long agpAddr; wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS]; char *dma_ptr; @@ -150,6 +154,7 @@ extern int via_mem_free(struct drm_devic extern int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_map_init(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int via_get_drm_info(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit_sync( struct drm_device *dev, void *data, struct drm_file *file_priv ); extern int via_dma_blit( struct drm_device *dev, void *data, struct drm_file *file_priv ); @@ -192,4 +197,6 @@ extern uint64_t via_evict_flags(struct d extern int via_move(struct drm_buffer_object *bo, int evict, int no_wait, struct drm_bo_mem_reg *new_mem); +extern int via_drm_resume(struct pci_dev *dev); +extern int via_drm_suspend(struct pci_dev *dev, pm_message_t state); #endif --- linux-2.6.26/drivers/char/drm/via_map.c.ark 2008-07-16 14:32:26.049943134 +0200 +++ linux-2.6.26/drivers/char/drm/via_map.c 2008-07-16 14:37:30.995067752 +0200 @@ -25,6 +25,8 @@ #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; @@ -67,6 +69,28 @@ static int via_do_init_map(struct drm_de dev_priv->have_idlelock = 0; spin_lock_init(&dev_priv->fence_lock); 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_priv->agp = drm_core_findmap(dev, init->agp_offset); + if (!dev_priv->agp) { + 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_priv->agp = 0; + } + /* end */ + ret = drm_bo_driver_init(dev); if (ret) DRM_ERROR("Could not initialize buffer object driver.\n"); @@ -103,6 +127,12 @@ int via_driver_load(struct drm_device *d 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 = drm_calloc(1, sizeof(drm_via_private_t), DRM_MEM_DRIVER); if (dev_priv == NULL) return -ENOMEM; @@ -127,3 +157,19 @@ int via_driver_unload(struct drm_device return 0; } + +int via_get_drm_info(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_via_private *dev_priv = (struct drm_via_private *)dev->dev_private; + struct drm_via_info info; + + if (copy_from_user(&info, data, sizeof(info))) + return -EFAULT; + info.RegSize = dev_priv->mmio->size; + info.AgpSize = dev_priv->agp->size; + info.RegHandle = dev_priv->mmio->offset; + info.AgpHandle = dev_priv->agp->offset; + if (copy_to_user((struct drm_via_info *)data, &info, sizeof(info))) + return -EFAULT; + return 0; +} --- linux-2.6.26/drivers/char/drm/via_mm.c.ark 2008-07-16 14:37:41.323953389 +0200 +++ linux-2.6.26/drivers/char/drm/via_mm.c 2008-07-16 14:53:44.236953104 +0200 @@ -30,6 +30,8 @@ #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) @@ -57,6 +59,11 @@ int via_agp_init(struct drm_device *dev, return 0; } +/*Henry:For via_fb_alloc/free used by video*/ +static void *global_dev; +static void *global_file_priv; +/*Henry:End*/ + int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_via_fb_t *fb = data; @@ -79,6 +86,11 @@ int via_fb_init(struct drm_device *dev, mutex_unlock(&dev->struct_mutex); DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); + /*Henry:For via_fb_alloc/free used by video*/ + global_dev = dev; + global_file_priv = file_priv; + /*Henry:End*/ + return 0; } @@ -115,6 +127,89 @@ void via_lastclose(struct drm_device *de mutex_unlock(&dev->struct_mutex); } +static int via_videomem_presave_ok(struct drm_via_private *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) { @@ -124,12 +219,12 @@ int via_mem_alloc(struct drm_device *dev 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"); @@ -138,15 +233,23 @@ int via_mem_alloc(struct drm_device *dev } tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; - item = drm_sman_alloc(&dev_priv->sman, mem->type, tmpSize, 0, + 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) ? + 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; @@ -166,6 +269,10 @@ int via_mem_free(struct drm_device *dev, 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); @@ -192,3 +299,28 @@ void via_reclaim_buffers_locked(struct d mutex_unlock(&dev->struct_mutex); return; } + +/*Henry:For via_fb_alloc/free used by video*/ +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);