diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 211e5b0..e5bb922 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -25,7 +25,7 @@ ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o \ via_video.o via_dmablit.o -mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o +mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o mach64_dmablit.o nv-objs := nv_drv.o ifeq ($(CONFIG_COMPAT),y) diff --git a/linux-core/mach64_dmablit.c b/linux-core/mach64_dmablit.c new file mode 100644 index 0000000..15cbe78 --- /dev/null +++ b/linux-core/mach64_dmablit.c @@ -0,0 +1,328 @@ + +#include "drmP.h" +#include "drm.h" +#include "mach64_drm.h" +#include "mach64_drv.h" + + +#define MACH64_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) +#define MACH64_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) +#define MACH64_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) + + +typedef struct _drm_mach64_sg_info { + enum dma_data_direction direction; + + struct page **pages; + unsigned long num_pages; + + dma_addr_t *dma_addrs; + + unsigned long num_desc; + + enum { + dr_mach64_device_mapped, + dr_mach64_pages_locked, + dr_mach64_pages_alloc, + dr_mach64_sg_init + } state; +} drm_mach64_sg_info_t; + + +static int mach64_lock_dma_pages(drm_mach64_sysblit_t * xfer, + drm_mach64_sg_info_t * vsg) +{ + int ret; + unsigned long first_pfn; + unsigned long last_pfn; + + first_pfn = MACH64_PFN(xfer->mem_addr); + last_pfn = MACH64_PFN(xfer->mem_addr + xfer->h * xfer->mem_pitch -1); + + vsg->num_pages = last_pfn - first_pfn + 1; + + vsg->pages = vmalloc(sizeof(struct page *) * vsg->num_pages); + if (vsg->pages == NULL) + return DRM_ERR(ENOMEM); + memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages); + + vsg->state = dr_mach64_pages_alloc; + + down_read(¤t->mm->mmap_sem); + ret = get_user_pages(current, current->mm, (unsigned long) xfer->mem_addr, + vsg->num_pages, (vsg->direction == DMA_FROM_DEVICE), + 0, vsg->pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (ret != vsg->num_pages) { + if (ret < 0) + return ret; + vsg->state = dr_mach64_pages_locked; + return DRM_ERR(EINVAL); + } + + vsg->state = dr_mach64_pages_locked; + + return 0; +} + + +static void mach64_release_dma_pages(drm_mach64_sg_info_t * vsg) +{ + int i; + + for (i = 0; i < vsg->num_pages; ++i) { + struct page *page = vsg->pages[i]; + if (page == NULL) + continue; + + if (! PageReserved(page) && (DMA_FROM_DEVICE == vsg->direction)) + SetPageDirty(page); + page_cache_release(page); + } +} + + +static void mach64_count_desc(const drm_mach64_sysblit_t * xfer, + drm_mach64_sg_info_t * vsg) +{ + unsigned char *mem_addr = xfer->mem_addr; + unsigned char *cur_mem; + uint32_t fb_addr = xfer->fb_addr; + uint32_t cur_fb; + unsigned long line_len; + unsigned remaining_len; + int num_desc = 0; + int cur_line; + + for (cur_line = 0; cur_line < xfer->h; ++cur_line) { + line_len = xfer->w_bytes; + cur_mem = mem_addr; + cur_fb = fb_addr; + + while (line_len > 0) { + remaining_len = min(PAGE_SIZE - MACH64_PGOFF(cur_mem), line_len); + line_len -= remaining_len; + + num_desc++; + cur_mem += remaining_len; + cur_fb += remaining_len; + } + + mem_addr += xfer->mem_pitch; + fb_addr += xfer->fb_pitch; + } + + vsg->num_desc = num_desc; + + DRM_DEBUG("(%4u,%4u) num_desc=%4lu num_pages=%4lu\n", + xfer->w_bytes, xfer->h, vsg->num_desc, vsg->num_pages); +} + + +static void mach64_fire_dmablit(drm_device_t * dev, + const drm_mach64_sysblit_t * xfer, + drm_mach64_sg_info_t * vsg) +{ + drm_mach64_private_t *dev_priv = dev->dev_private; + + unsigned char *mem_addr = xfer->mem_addr; + unsigned char *cur_mem; + uint32_t fb_addr = xfer->fb_addr; + uint32_t cur_fb; + unsigned long line_len; + unsigned remaining_len, eol; + int num_desc = 0; + int cur_line; + +#if 1 + unsigned long mem_pfn = MACH64_PFN(xfer->mem_addr); + unsigned long cur_pfn; + dma_addr_t cur_dma; + + uint32_t *table = (uint32_t *) dev_priv->ring.start; + uint32_t direction; +#endif + + for (cur_line = 0; cur_line < xfer->h; ++cur_line) { + line_len = xfer->w_bytes; + cur_mem = mem_addr; + cur_fb = fb_addr; + + while (line_len > 0) { + remaining_len = min(PAGE_SIZE - MACH64_PGOFF(cur_mem), line_len); + line_len -= remaining_len; + +#if 1 + cur_pfn = MACH64_PFN(cur_mem); + cur_dma = vsg->dma_addrs[cur_pfn - mem_pfn] + MACH64_PGOFF(cur_mem); + + eol = (num_desc == vsg->num_desc - 1) ? MACH64_DMA_EOL : 0x0; + + table[MACH64_DMA_FRAME_BUF_OFFSET] = cpu_to_le32(cur_fb); + table[MACH64_DMA_SYS_MEM_ADDR] = cpu_to_le32(cur_dma); + table[MACH64_DMA_COMMAND] = cpu_to_le32(remaining_len | eol); + table[MACH64_DMA_RESERVED] = 0; + + table += 4; +#endif + + num_desc++; + cur_mem += remaining_len; + cur_fb += remaining_len; + } + + mem_addr += xfer->mem_pitch; + fb_addr += xfer->fb_pitch; + } + +#if 1 + direction = (vsg->direction == DMA_TO_DEVICE) ? 0x0 : 0x1; + + MACH64_WRITE(MACH64_BM_SYSTEM_TABLE, + dev_priv->ring.start_addr | direction); + + wait_for_completion(&dev_priv->bm_eol_completion); +#endif +} + + +static int mach64_map_blit_for_device(struct pci_dev * pdev, + const drm_mach64_sysblit_t * xfer, + drm_mach64_sg_info_t * vsg) +{ + int i; + + /* Allocate page to DMA address translation table */ + vsg->dma_addrs = vmalloc(sizeof(dma_addr_t) * vsg->num_pages); + if (vsg->dma_addrs == NULL) + return DRM_ERR(ENOMEM); + memset(vsg->dma_addrs, 0, sizeof(dma_addr_t) * vsg->num_pages); + + /* Map whole pages, see LDD3, ch. 15, p. 450 */ + for (i = 0; i < vsg->num_pages; ++i) { + vsg->dma_addrs[i] = dma_map_page(&pdev->dev, vsg->pages[i], + 0, PAGE_SIZE, vsg->direction); + } + + vsg->state = dr_mach64_device_mapped; + + DRM_DEBUG("%08x %p\n", vsg->dma_addrs[0], page_address(vsg->pages[0])); + DRM_DEBUG("%08x %p\n", vsg->dma_addrs[vsg->num_pages-1], page_address(vsg->pages[vsg->num_pages-1])); + + return 0; +} + + +static void mach64_unmap_blit_from_device(struct pci_dev * pdev, + drm_mach64_sg_info_t * vsg) +{ + int i; + + for (i = 0; i < vsg->num_pages; ++i) { + dma_unmap_page(&pdev->dev, vsg->dma_addrs[i], + PAGE_SIZE, vsg->direction); + } + + vfree(vsg->dma_addrs); +} + + +static void mach64_free_sg_info(struct pci_dev * pdev, + drm_mach64_sg_info_t * vsg) +{ + switch(vsg->state) { + case dr_mach64_device_mapped: + mach64_unmap_blit_from_device(pdev, vsg); + case dr_mach64_pages_locked: + mach64_release_dma_pages(vsg); + case dr_mach64_pages_alloc: + vfree(vsg->pages); + default: + vsg->state = dr_mach64_sg_init; + } +} + + +static int mach64_build_sg_info(drm_device_t * dev, drm_mach64_sg_info_t * vsg, + drm_mach64_sysblit_t * xfer) +{ + int ret; + + vsg->state = dr_mach64_sg_init; + + vsg->direction = (xfer->to_fb) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + mach64_count_desc(xfer, vsg); + if (vsg->num_desc > 2048) { + DRM_ERROR("Too many DRM descriptors required.\n"); + mach64_free_sg_info(dev->pdev, vsg); + return DRM_ERR(EINVAL); + } + + ret = mach64_lock_dma_pages(xfer, vsg); + if (ret < 0) { + DRM_ERROR("Could not lock DMA pages.\n"); + mach64_free_sg_info(dev->pdev, vsg); + return ret; + } + + ret = mach64_map_blit_for_device(dev->pdev, xfer, vsg); + if (ret < 0) { + DRM_ERROR("Could not allocate DMA addresses array.\n"); + mach64_free_sg_info(dev->pdev, vsg); + return ret; + } + + mach64_fire_dmablit(dev, xfer, vsg); + + return 0; +} + + +static int mach64_dma_dispatch_sysblit(drm_device_t * dev, + drm_mach64_sysblit_t * xfer) +{ + drm_mach64_sg_info_t *vsg; + int ret; + + vsg = drm_alloc(sizeof(*vsg), DRM_MEM_DRIVER); + if (vsg == NULL) + return DRM_ERR(ENOMEM); + + ret = mach64_build_sg_info(dev, vsg, xfer); + if (ret < 0) { + drm_free(vsg, sizeof(*vsg), DRM_MEM_DRIVER); + return ret; + } + + mach64_free_sg_info(dev->pdev, vsg); + drm_free(vsg, sizeof(*vsg), DRM_MEM_DRIVER); + + return 0; +} + + +int mach64_dma_sysblit(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_mach64_private_t *dev_priv = dev->dev_private; + drm_mach64_sysblit_t xfer; + int ret; + + LOCK_TEST_WITH_RETURN(dev, filp); + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(xfer, (drm_mach64_sysblit_t *) data, sizeof(xfer)); + + DRM_DEBUG("%s: pid=%d\n", + __FUNCTION__, DRM_CURRENTPID); + + ret = mach64_dma_dispatch_sysblit(dev, &xfer); + + return ret; +} diff --git a/shared-core/mach64_dma.c b/shared-core/mach64_dma.c index 4c8edea..5b5297a 100644 --- a/shared-core/mach64_dma.c +++ b/shared-core/mach64_dma.c @@ -893,6 +893,7 @@ static int mach64_do_dma_init(drm_device /* allocate descriptor memory from pci pool */ DRM_DEBUG("Allocating dma descriptor ring\n"); dev_priv->ring.size = 0x4000; /* 16KB */ + dev_priv->ring.size = 0x8000; /* hijack for sysblit */ if (dev_priv->is_pci) { dev_priv->ring.dmah = drm_pci_alloc(dev, dev_priv->ring.size, @@ -978,6 +979,9 @@ static int mach64_do_dma_init(drm_device return ret; } + /* init bus master end of transfer completion */ + init_completion(&dev_priv->bm_eol_completion); + return 0; } diff --git a/shared-core/mach64_drm.h b/shared-core/mach64_drm.h index 1fd8c00..d2cb920 100644 --- a/shared-core/mach64_drm.h +++ b/shared-core/mach64_drm.h @@ -162,6 +162,7 @@ #define DRM_MACH64_VERTEX 0x05 #define DRM_MACH64_BLIT 0x06 #define DRM_MACH64_FLUSH 0x07 #define DRM_MACH64_GETPARAM 0x08 +#define DRM_MACH64_SYSBLIT 0x09 #define DRM_IOCTL_MACH64_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_INIT, drm_mach64_init_t) #define DRM_IOCTL_MACH64_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_IDLE ) @@ -171,7 +172,8 @@ #define DRM_IOCTL_MACH64_CLEAR #define DRM_IOCTL_MACH64_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_VERTEX, drm_mach64_vertex_t) #define DRM_IOCTL_MACH64_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_BLIT, drm_mach64_blit_t) #define DRM_IOCTL_MACH64_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_FLUSH ) -#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) +#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) +#define DRM_IOCTL_MACH64_SYSBLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_SYSBLIT, drm_mach64_sysblit_t) /* Buffer flags for clears */ @@ -253,4 +255,17 @@ typedef struct drm_mach64_getparam { void *value; } drm_mach64_getparam_t; +typedef struct drm_mach64_sysblit { + unsigned int h; + unsigned int w_bytes; + + unsigned int fb_addr; + unsigned int fb_pitch; + + char *mem_addr; + unsigned int mem_pitch; + + int to_fb; +} drm_mach64_sysblit_t; + #endif diff --git a/shared-core/mach64_drv.h b/shared-core/mach64_drv.h index 2a5f2ff..5bce9c2 100644 --- a/shared-core/mach64_drv.h +++ b/shared-core/mach64_drv.h @@ -107,6 +107,8 @@ typedef struct drm_mach64_private { drm_local_map_t *ring_map; drm_local_map_t *dev_buffers; /* this is a pointer to a structure in dev */ drm_local_map_t *agp_textures; + + struct completion bm_eol_completion; } drm_mach64_private_t; extern drm_ioctl_desc_t mach64_ioctls[]; @@ -144,6 +146,7 @@ extern int mach64_dma_swap(DRM_IOCTL_ARG extern int mach64_dma_vertex(DRM_IOCTL_ARGS); extern int mach64_dma_blit(DRM_IOCTL_ARGS); extern int mach64_get_param(DRM_IOCTL_ARGS); +extern int mach64_dma_sysblit(DRM_IOCTL_ARGS); extern int mach64_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence); diff --git a/shared-core/mach64_irq.c b/shared-core/mach64_irq.c index 663642d..010ddba 100644 --- a/shared-core/mach64_irq.c +++ b/shared-core/mach64_irq.c @@ -67,6 +67,17 @@ irqreturn_t mach64_driver_irq_handler(DR drm_vbl_send_signals(dev); return IRQ_HANDLED; } + + /* BM_EOL (bus master end of transfer) interrupt */ + if (status & MACH64_CRTC_BUSMASTER_EOL_INT) { + MACH64_WRITE(MACH64_CRTC_INT_CNTL, + (status & ~MACH64_CRTC_INT_ACKS) + | MACH64_CRTC_BUSMASTER_EOL_INT); + + complete(&dev_priv->bm_eol_completion); + return IRQ_HANDLED; + } + return IRQ_NONE; } @@ -102,6 +113,10 @@ void mach64_driver_irq_preinstall(drm_de /* Disable and clear VBLANK interrupt */ MACH64_WRITE(MACH64_CRTC_INT_CNTL, (status & ~MACH64_CRTC_VBLANK_INT_EN) | MACH64_CRTC_VBLANK_INT); + + /* Disable and clear BM_EOL interrupt */ + MACH64_WRITE(MACH64_CRTC_INT_CNTL, (status & ~MACH64_CRTC_BUSMASTER_EOL_INT_EN) + | MACH64_CRTC_BUSMASTER_EOL_INT); } void mach64_driver_irq_postinstall(drm_device_t * dev) @@ -113,6 +128,10 @@ void mach64_driver_irq_postinstall(drm_d MACH64_WRITE(MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) | MACH64_CRTC_VBLANK_INT_EN); + /* Turn on BM_EOL interrupt */ + MACH64_WRITE(MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) + | MACH64_CRTC_BUSMASTER_EOL_INT_EN); + DRM_DEBUG("after install CRTC_INT_CTNL: 0x%08x\n", MACH64_READ(MACH64_CRTC_INT_CNTL)); @@ -131,6 +150,12 @@ void mach64_driver_irq_uninstall(drm_dev ~MACH64_CRTC_VBLANK_INT_EN) | MACH64_CRTC_VBLANK_INT); + /* Disable and clear BM_EOL interrupt */ + MACH64_WRITE(MACH64_CRTC_INT_CNTL, + (MACH64_READ(MACH64_CRTC_INT_CNTL) & + ~MACH64_CRTC_BUSMASTER_EOL_INT_EN) + | MACH64_CRTC_BUSMASTER_EOL_INT); + DRM_DEBUG("after uninstall CRTC_INT_CTNL: 0x%08x\n", MACH64_READ(MACH64_CRTC_INT_CNTL)); } diff --git a/shared-core/mach64_state.c b/shared-core/mach64_state.c index ce44f0d..2f89b97 100644 --- a/shared-core/mach64_state.c +++ b/shared-core/mach64_state.c @@ -50,6 +50,7 @@ drm_ioctl_desc_t mach64_ioctls[] = { [DRM_IOCTL_NR(DRM_MACH64_BLIT)] = {mach64_dma_blit, DRM_AUTH}, [DRM_IOCTL_NR(DRM_MACH64_FLUSH)] = {mach64_dma_flush, DRM_AUTH}, [DRM_IOCTL_NR(DRM_MACH64_GETPARAM)] = {mach64_get_param, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_MACH64_SYSBLIT)] = {mach64_dma_sysblit, DRM_AUTH}, }; int mach64_max_ioctl = DRM_ARRAY_SIZE(mach64_ioctls);