[email protected] wrote: > Hello Sirs: > This patch is to add the support of ACPI suspend/resume. Some information > locating in video memory > need to be saved for 3D function. The mesa driver source has been release on > VIA Linux portal > (http://linux.via.com.tw/support/beginDownload.action?eleid=241&fid=463) last > year although it's old. >
Bruce, this patch still have some rough edges. In principle, with the way the VIA drm module is made, it should really be shut down by the X server before suspend / resume. For example, there may be textures, video images and other stuff in VRAM that require saving. As mentioned previously, the X server should ideally handle this by mallocing a big area and saving all the VRAM it has directed for DRM to manage. This may be a little slow, but on the other hand, the memory will be swappable. I didn't quite follow the reasons why you didn't want to use this approach. In the patch below, ioremap() is used to map videoram, and kmalloc() is used to get system memory. Both ioremap() address space and kmalloc() memory of sizes > 4KiB is a scarce resource. So, a) I'd like to see a more thorough explanation why the X server saving approach is not used. b) Limit VIA_MEM_VIDEO_SAVE chunks to 4KiB each. That will make sure kmalloc() is much less likely to fail. c) Put an upper (rather small) limit of the amount of VIA_MEM_VIDEO_SAVE memory to stop it from pinning all system memory, os use up all ioremap() address space. d) Please use the kernel linked list implementation. It makes the code easier to follow. /Thanks. > Signed-off-by Bruce C. Chang <[email protected]> > > 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-10-22 10:13:59.000000000 +0800 > +++ new/drivers/gpu/drm/via/via_dma.c 2009-10-22 10:22:11.000000000 +0800 > @@ -68,6 +68,8 @@ > *vb++ = (w2); \ > dev_priv->dma_low += 8; > > +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); > @@ -155,6 +157,11 @@ > > int via_dma_cleanup(struct drm_device * dev) > { > + struct drm_via_video_save_head *pnode; > + > + 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; > @@ -175,6 +182,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 +203,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; > @@ -737,6 +748,75 @@ > return ret; > } > > +/* For acpi case, when system resume from suspend or hibernate, > + * need to re-initialize dma info into HW > + */ > +int via_drm_resume(struct drm_device *drm_dev) > +{ > + struct pci_dev *pci = drm_dev->pdev; > + drm_via_private_t *dev_priv = > + (drm_via_private_t *) drm_dev->dev_private; > + struct drm_via_video_save_head *pnode = 0; > + > + pci_set_power_state(pci, PCI_D0); > + pci_restore_state(pci); > + if (pci_enable_device(pci)) > + return -1; > + if (!dev_priv->initialize) > + return 0; > + > + /* 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 drm_device *drm_dev, pm_message_t state) > +{ > + struct pci_dev *pci = drm_dev->pdev; > + drm_via_private_t *dev_priv = > + (drm_via_private_t *) drm_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); > + > + if (state.event == PM_EVENT_SUSPEND) { > + /* Shut down the device */ > + pci_disable_device(pci); > + pci_set_power_state(pci, PCI_D3hot); > + } > + 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), > 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-10-22 10:13:59.000000000 +0800 > +++ new/drivers/gpu/drm/via/via_drv.c 2009-10-22 10:22:11.000000000 +0800 > @@ -38,6 +38,8 @@ > DRIVER_IRQ_SHARED, > .load = via_driver_load, > .unload = via_driver_unload, > + .suspend = via_drm_suspend, > + .resume = via_drm_resume, > .context_dtor = via_final_context, > .get_vblank_counter = via_get_vblank_counter, > .enable_vblank = via_enable_vblank, > diff -ruN old/drivers/gpu/drm/via/via_drv.h new/drivers/gpu/drm/via/via_drv.h > --- old/drivers/gpu/drm/via/via_drv.h 2009-10-22 10:13:59.000000000 +0800 > +++ new/drivers/gpu/drm/via/via_drv.h 2009-10-22 10:22:11.000000000 +0800 > @@ -95,6 +95,7 @@ > unsigned long agp_offset; > drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; > uint32_t dma_diff; > + int initialize; > } drm_via_private_t; > > enum via_family { > @@ -154,4 +155,7 @@ > extern void via_dmablit_handler(struct drm_device *dev, int engine, int > from_irq); > extern void via_init_dmablit(struct drm_device *dev); > > +extern int via_drm_resume(struct drm_device *dev); > +extern int via_drm_suspend(struct drm_device *dev, pm_message_t state); > + > #endif > 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-10-22 10:13:59.000000000 +0800 > +++ new/drivers/gpu/drm/via/via_map.c 2009-10-22 10:22:11.000000000 +0800 > @@ -65,12 +65,25 @@ > via_init_dmablit(dev); > > dev->dev_private = (void *)dev_priv; > + > + /* from doing this, we has the stuff in prev data > + * structure to manage agp > + */ > + dev->agp_buffer_map = drm_core_findmap(dev, dev_priv->agpAddr); > + if (!dev->agp_buffer_map) > + DRM_INFO("Dma buffer region not exist!\n"); > + > + /* 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; > } > 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-10-22 10:13:59.000000000 +0800 > +++ new/drivers/gpu/drm/via/via_mm.c 2009-10-22 10:22:11.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) > > @@ -114,6 +115,90 @@ > mutex_unlock(&dev->struct_mutex); > } > > +/* This function is specially for saving some information > + * about video memory allocation. > + * when the video allocated needed to be saved automatically in acpi, > + * we need to call this function to record it for later use. > + */ > +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; > +} > + > +/* please refer to function via_videomem_presave_ok */ > +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 +208,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 +223,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 +261,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); > > diff -ruN old/include/drm/via_drm.h new/include/drm/via_drm.h > --- old/include/drm/via_drm.h 2009-10-22 10:14:45.000000000 +0800 > +++ new/include/drm/via_drm.h 2009-10-22 10:16:21.000000000 +0800 > @@ -114,6 +114,7 @@ > #define VIA_MEM_SYSTEM 2 > #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 struct { > __u32 offset; > @@ -274,4 +275,13 @@ > drm_via_blitsync_t sync; > } drm_via_dmablit_t; > > +struct drm_via_video_save_head { > + void *pvideomem; > + void *psystemmem; > + int size; > + /* token used to identify this video memory */ > + unsigned long token; > + void *next; > +} ; > +extern struct drm_via_video_save_head *via_video_save_head; > #endif /* _VIA_DRM_H_ */ > > 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: [email protected] > ------------------------------------------------------------------------------ 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 [email protected] https://lists.sourceforge.net/lists/listinfo/dri-devel
