summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2007-08-19 12:25:39 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-10-09 22:05:46 -0300
commit0989fd2c88a11aa5014b2b348ed51872d14d536d (patch)
treeb01ba5de69e17fcf408ce597af966e4f0949e7c4
parent3869c6a088c2eb165abe476c3372c6a3653649b3 (diff)
V4L/DVB (6058): ivtv: add support for highmem udma
When trying to DMA userspace buffers to the cx23415 you need to check whether the page is in highmem. If this is the case, then bounce buffers have to be used to allow DMA. Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c45
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c9
3 files changed, 47 insertions, 9 deletions
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 93a008409f4..784098d4bf0 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -419,6 +419,8 @@ struct ivtv_user_dma {
struct mutex lock;
int page_count;
struct page *map[IVTV_DMA_SG_OSD_ENT];
+ /* Needed when dealing with highmem userspace buffers */
+ struct page *bouncemap[IVTV_DMA_SG_OSD_ENT];
/* Base Dev SG Array for cx23415/6 */
struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
index bd642e1aafc..5592abbe14e 100644
--- a/drivers/media/video/ivtv/ivtv-udma.c
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -38,23 +38,38 @@ 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;
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 -ENOMEM;
+ 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++;
}
- return map_offset;
+ return 0;
}
void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
@@ -89,7 +104,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 +138,14 @@ 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);
+ err = ivtv_udma_fill_sg_list(dma, &user_dma, 0);
+ if (err) {
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+ return err;
+ }
/* Map SG List */
dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
@@ -166,6 +188,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 +200,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)
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index fa8c76f3d35..2ae7556f5e6 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -83,7 +83,14 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
}
/* Fill & map SG List */
- ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0));
+ if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0))) {
+ IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+ return -ENOMEM;
+ }
dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
/* Fill SG Array with new values */