diff options
Diffstat (limited to 'drivers/media/video/ivtv/ivtv-udma.c')
-rw-r--r-- | drivers/media/video/ivtv/ivtv-udma.c | 46 |
1 files changed, 38 insertions, 8 deletions
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index bd642e1aafc..c4626d1cdf4 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -21,7 +21,6 @@ */ #include "ivtv-driver.h" -#include "ivtv-streams.h" #include "ivtv-udma.h" void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) @@ -38,19 +37,37 @@ void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) { int i, offset; + unsigned long flags; + + if (map_offset < 0) + return map_offset; offset = dma_page->offset; /* Fill SG Array with new values */ for (i = 0; i < dma_page->page_count; i++) { - if (i == dma_page->page_count - 1) { - dma->SGlist[map_offset].length = dma_page->tail; + unsigned int len = (i == dma_page->page_count - 1) ? + dma_page->tail : PAGE_SIZE - offset; + + dma->SGlist[map_offset].length = len; + dma->SGlist[map_offset].offset = offset; + if (PageHighMem(dma->map[map_offset])) { + void *src; + + if (dma->bouncemap[map_offset] == NULL) + dma->bouncemap[map_offset] = alloc_page(GFP_KERNEL); + if (dma->bouncemap[map_offset] == NULL) + return -1; + local_irq_save(flags); + src = kmap_atomic(dma->map[map_offset], KM_BOUNCE_READ) + offset; + memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len); + kunmap_atomic(src, KM_BOUNCE_READ); + local_irq_restore(flags); + dma->SGlist[map_offset].page = dma->bouncemap[map_offset]; } else { - dma->SGlist[map_offset].length = PAGE_SIZE - offset; + dma->SGlist[map_offset].page = dma->map[map_offset]; } - dma->SGlist[map_offset].offset = offset; - dma->SGlist[map_offset].page = dma->map[map_offset]; offset = 0; map_offset++; } @@ -89,7 +106,7 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, { struct ivtv_dma_page_info user_dma; struct ivtv_user_dma *dma = &itv->udma; - int err; + int i, err; IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); @@ -123,7 +140,13 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, dma->page_count = user_dma.page_count; /* Fill SG List with new values */ - ivtv_udma_fill_sg_list(dma, &user_dma, 0); + if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) { + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; + return -ENOMEM; + } /* Map SG List */ dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); @@ -166,6 +189,8 @@ void ivtv_udma_unmap(struct ivtv *itv) void ivtv_udma_free(struct ivtv *itv) { + int i; + /* Unmap SG Array */ if (itv->udma.SG_handle) { pci_unmap_single(itv->dev, itv->udma.SG_handle, @@ -176,6 +201,11 @@ void ivtv_udma_free(struct ivtv *itv) if (itv->udma.SG_length) { pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); } + + for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) { + if (itv->udma.bouncemap[i]) + __free_page(itv->udma.bouncemap[i]); + } } void ivtv_udma_start(struct ivtv *itv) |