From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- .../gfxdrivers/sh772x/kernel-module/sh7723.c | 566 +++++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100755 Source/DirectFB/gfxdrivers/sh772x/kernel-module/sh7723.c (limited to 'Source/DirectFB/gfxdrivers/sh772x/kernel-module/sh7723.c') diff --git a/Source/DirectFB/gfxdrivers/sh772x/kernel-module/sh7723.c b/Source/DirectFB/gfxdrivers/sh772x/kernel-module/sh7723.c new file mode 100755 index 0000000..ea64cf4 --- /dev/null +++ b/Source/DirectFB/gfxdrivers/sh772x/kernel-module/sh7723.c @@ -0,0 +1,566 @@ +/* + * SH7723 Graphics Device + * + * Copyright (C) 2006-2008 IGEL Co.,Ltd + * + * Written by Janine Kropp , + * Denis Oliver Kropp + * + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License v2 + * as published by the Free Software Foundation. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) +#include +#endif + +#include + + +//#define SH7723GFX_DEBUG_2DG +//#define SH7723GFX_IRQ_POLLER + + +/**********************************************************************************************************************/ + +#ifndef SH7723_BEU_IRQ +#define SH7723_BEU_IRQ 53 +#endif + +#ifndef SH7723_TDG_IRQ +#define SH7723_TDG_IRQ 44 +#endif + +/**********************************************************************************************************************/ + +#define ENGINE_REG_TOP 0xA4680000 +#define SH7723_BEU_BASE 0xFE930000 + +#define M2DG_REG(x) (*(volatile u32*)((x)+ENGINE_REG_TOP)) +#define BEU_REG(x) (*(volatile u32*)((x)+SH7723_BEU_BASE)) + +#define M2DG_SCLR M2DG_REG(0x000) +#define M2DG_DLSAR M2DG_REG(0x048) + + +#define M2DG_STATUS M2DG_REG(0x004) +#define M2DG_STATUS_CLEAR M2DG_REG(0x008) +#define M2DG_INT_ENABLE M2DG_REG(0x00c) + +#define M2DG_SCLR_START 0x00000001 +#define M2DG_SCLR_RESET 0x80000000 + +#define M2DG_INT_TRAP 0x0001 +#define M2DG_INT_INTERRUPT 0x0002 +#define M2DG_INT_ERROR 0x0004 +#define M2DG_INT_ANY 0x0007 + +#define BEVTR BEU_REG(0x0018C) + +/**********************************************************************************************************************/ + +#ifdef SH7723GFX_DEBUG_2DG +#define QPRINT(x...) do { \ + char buf[128]; \ + struct timeval tv; \ + do_gettimeofday( &tv ); \ + snprintf( buf, sizeof(buf), x ); \ + printk( KERN_DEBUG "%ld.%03ld.%03ld - %-17s: %s\n", \ + tv.tv_sec - base_time.tv_sec, \ + tv.tv_usec / 1000, tv.tv_usec % 1000, __FUNCTION__, buf ); \ +} while (0) +#else +#define QPRINT(x...) do {} while (0) +#endif + +#define QDUMP(msg) QPRINT( "%-12s (%s, hw %5d-%5d, next %5d-%5d, %svalid, " \ + "STATUS 0x%07x)", msg, \ + shared->hw_running ? "running" : " idle", \ + shared->hw_start, \ + shared->hw_end, \ + shared->next_start, \ + shared->next_end, \ + shared->next_valid ? " " : "in", \ + M2DG_STATUS & M2DG_INT_ANY ); + +/**********************************************************************************************************************/ + +static DECLARE_WAIT_QUEUE_HEAD( wait_idle ); +static DECLARE_WAIT_QUEUE_HEAD( wait_next ); + +static SH772xGfxSharedArea *shared; + +static struct timeval base_time; + +#ifndef SHARED_AREA_PHYS +static struct page *shared_page; +static unsigned int shared_order; +#endif + +#ifdef SH7723GFX_IRQ_POLLER +static int stop_poller; +#endif + +/**********************************************************************************************************************/ + +static int +sh7723_reset( SH772xGfxSharedArea *shared ) +{ + do_gettimeofday( &base_time ); + + QPRINT( "Resetting hardware..." ); + + M2DG_SCLR = M2DG_SCLR_RESET; + udelay( 5 ); + M2DG_SCLR = 0; + + QPRINT( "Initializing shared area..." ); + + memset( (void*) shared, 0, sizeof(SH772xGfxSharedArea) ); + + shared->buffer_phys = virt_to_phys(&shared->buffer[0]); + shared->magic = SH7723GFX_SHARED_MAGIC; + + + QPRINT( "Clearing interrupts..." ); + + M2DG_STATUS_CLEAR = M2DG_INT_ANY; + + M2DG_INT_ENABLE = M2DG_INT_ANY; + + QDUMP( "Ready" ); + + return 0; +} + +/* copied from board-ap325rxa.c */ +#define PORT_PSCR 0xA405011E +#define PORT_PSDR 0xA405013E +#define FPGA_LCDREG 0xB4100180 +#define FPGA_BKLREG 0xB4100212 + +static int +sh7723_power_display( void ) +{ + msleep(100); + + /* ASD AP-320/325 LCD ON */ + ctrl_outw(0x0018, FPGA_LCDREG); + + /* backlight */ + ctrl_outw((ctrl_inw(PORT_PSCR) & ~0x00C0) | 0x40, PORT_PSCR); + ctrl_outb(ctrl_inb(PORT_PSDR) & ~0x08, PORT_PSDR); + ctrl_outw(0x100, FPGA_BKLREG); + + return 0; +} + +static int +sh7723_wait_idle( SH772xGfxSharedArea *shared ) +{ + int ret; + + QDUMP( "Waiting....." ); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + shared->num_wait_idle++; + + ret = wait_event_interruptible_timeout( wait_idle, !shared->hw_running, 42*HZ ); + if (!ret) { + printk( KERN_ERR "%s: TIMEOUT! (%srunning, hw %d-%d, next %d-%d - %svalid, " + "STATUS 0x%08x)\n", + __FUNCTION__, + shared->hw_running ? "" : "not ", + shared->hw_start, + shared->hw_end, + shared->next_start, + shared->next_end, + shared->next_valid ? "" : "not ", + M2DG_STATUS & M2DG_INT_ANY ); + } + + QDUMP( "........done" ); + + return (ret > 0) ? 0 : (ret < 0) ? ret : -ETIMEDOUT; +} + +static int +sh7723_wait_next( SH772xGfxSharedArea *shared ) +{ + int ret; + + QDUMP( "Waiting....." ); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + shared->num_wait_next++; + + ret = wait_event_interruptible_timeout( wait_next, !shared->hw_running || + shared->next_start == shared->next_end, 42*HZ ); + if (!ret) { + printk( KERN_ERR "%s: TIMEOUT! (%srunning, hw %d-%d, next %d-%d - %svalid, " + "STATUS 0x%08x)\n", + __FUNCTION__, + shared->hw_running ? "" : "not ", + shared->hw_start, + shared->hw_end, + shared->next_start, + shared->next_end, + shared->next_valid ? "" : "not ", + M2DG_STATUS & M2DG_INT_ANY ); + } + + QDUMP( "........done" ); + + return (ret > 0) ? 0 : (ret < 0) ? ret : -ETIMEDOUT; +} + +/**********************************************************************************************************************/ + +static irqreturn_t +sh7723_beu_irq( int irq, void *ctx ) +{ + BEVTR = 0; + + /* Nothing here so far. But Vsync could be added. */ + + return IRQ_HANDLED; +} + +static irqreturn_t +sh7723_tdg_irq( int irq, void *ctx ) +{ + SH772xGfxSharedArea *shared = ctx; + u32 status = M2DG_STATUS & M2DG_INT_ANY; + + if (! (status & M2DG_INT_ANY)) { +#ifndef SH7723GFX_IRQ_POLLER + printk( KERN_WARNING "%s: bogus interrupt, STATUS 0x%08x!\n", __FUNCTION__, status ); +#endif + return IRQ_NONE; + } + +// if (status & ~0x100) + QDUMP( "-Interrupt" ); + + if (status & M2DG_INT_ERROR) + printk( KERN_ERR "%s: error! STATUS 0x%08x!\n", __FUNCTION__, status ); + + shared->num_interrupts++; + + /* Clear the interrupt. */ + M2DG_STATUS_CLEAR = status; + + if (status & (M2DG_INT_TRAP | M2DG_INT_ERROR)) { + if (!shared->hw_running) + printk( KERN_WARNING "%s: huh, hw running? STATUS 0x%08x!\n", __FUNCTION__, status ); + + if (status & M2DG_INT_ERROR) { + printk( KERN_ERR "%s: ERROR! (%srunning, hw %d-%d, next %d-%d - %svalid, " + "STATUS 0x%08x)\n", + __FUNCTION__, + shared->hw_running ? "" : "not ", + shared->hw_start, + shared->hw_end, + shared->next_start, + shared->next_end, + shared->next_valid ? "" : "not ", + status ); + + M2DG_SCLR = M2DG_SCLR_RESET; + } + + /* Next valid means user space is not in the process of extending the buffer. */ + if (shared->next_valid && shared->next_start != shared->next_end) { + shared->hw_start = shared->next_start; + shared->hw_end = shared->next_end; + + shared->next_start = shared->next_end = (shared->hw_end + 1 + 3) & ~3; + shared->next_valid = 0; + + shared->num_words += shared->hw_end - shared->hw_start; + + shared->num_starts++; + + QDUMP( " '-> Start!" ); + + M2DG_DLSAR = shared->buffer_phys + shared->hw_start*4; + M2DG_SCLR = M2DG_SCLR_START; + + wake_up_all( &wait_next ); + } + else { + shared->num_idle++; + + QDUMP( " '-> Idle." ); + +//check if needed +// BEM_PE_CACHE = 1; + + shared->hw_running = 0; + + wake_up_all( &wait_next ); + wake_up_all( &wait_idle ); + } + + shared->num_done++; + } + + return IRQ_HANDLED; +} + +#ifdef SH7723GFX_IRQ_POLLER +static int +sh7723_tdg_irq_poller( void *arg ) +{ + daemonize( "%s", __FUNCTION__ ); + + sigfillset( ¤t->blocked ); + + while (!stop_poller) { + set_current_state( TASK_UNINTERRUPTIBLE ); + schedule_timeout( 1 ); + + sh7723_tdg_irq( SH7723_TDG_IRQ, (void*) arg ); + } + + stop_poller = 0; + + return 0; +} +#endif + +/**********************************************************************************************************************/ + +static int +sh7723gfx_ioctl( struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg ) +{ + SH772xRegister reg; + + switch (cmd) { + case SH772xGFX_IOCTL_RESET: + return sh7723_reset( shared ); + + case SH772xGFX_IOCTL_WAIT_IDLE: + return sh7723_wait_idle( shared ); + + case SH772xGFX_IOCTL_WAIT_NEXT: + return sh7723_wait_next( shared ); + + case SH772xGFX_IOCTL_SETREG32: + if (copy_from_user( ®, (void*)arg, sizeof(SH772xRegister) )) + return -EFAULT; + + /* BEU, LCDC, VOU, 2DG */ + if ((reg.address < 0xFE930000 || reg.address > 0xFEA102D0) && + (reg.address < 0xA4680000 || reg.address > 0xA468FFFF)) + return -EACCES; + + *(volatile __u32 *) reg.address = reg.value; + + return 0; + + case SH772xGFX_IOCTL_GETREG32: + if (copy_from_user( ®, (void*)arg, sizeof(SH772xRegister) )) + return -EFAULT; + + /* BEU, LCDC, VOU */ + if ((reg.address < 0xFE930000 || reg.address > 0xFEA102D0) && + (reg.address < 0xA4680000 || reg.address > 0xA468FFFF)) + return -EACCES; + + reg.value = *(volatile __u32 *) reg.address; + + if (copy_to_user( (void*)arg, ®, sizeof(SH772xRegister) )) + return -EFAULT; + + return 0; + + case SH772xGFX_IOCTL_POWER_DISPLAY: + return sh7723_power_display( ); + } + + return -ENOSYS; +} + +static int +sh7723gfx_mmap( struct file *file, + struct vm_area_struct *vma ) +{ + unsigned int size; + + /* Just allow mapping at offset 0. */ + if (vma->vm_pgoff) + return -EINVAL; + + /* Check size of requested mapping. */ + size = vma->vm_end - vma->vm_start; + if (size != PAGE_ALIGN(sizeof(SH772xGfxSharedArea))) + return -EINVAL; + + /* Set reserved and I/O flag for the area. */ + vma->vm_flags |= VM_RESERVED | VM_IO; + + /* Select uncached access. */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) + return remap_pfn_range( vma, vma->vm_start, + virt_to_phys((void*)shared) >> PAGE_SHIFT, + size, vma->vm_page_prot ); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + return remap_page_range( vma, vma->vm_start, + virt_to_phys((void*)shared), + size, vma->vm_page_prot ); +#else + return io_remap_page_range( vma->vm_start, + virt_to_phys((void*)shared), + size, vma->vm_page_prot ); +#endif +} + +/**********************************************************************************************************************/ + +static struct file_operations sh7723gfx_fops = { + ioctl: sh7723gfx_ioctl, + mmap: sh7723gfx_mmap +}; + +static struct miscdevice sh7723gfx_miscdev = { + minor: 196, // 7*7*2*2 + name: "sh772x_gfx", + fops: &sh7723gfx_fops +}; + +/**********************************************************************************************************************/ + +int +sh7723_init( void ) +{ +#ifndef SHARED_AREA_PHYS + int i; +#endif + int ret; + + /* Register the SH7723 graphics device. */ + ret = misc_register( &sh7723gfx_miscdev ); + if (ret < 0) { + printk( KERN_ERR "%s: misc_register() for minor %d failed! (error %d)\n", + __FUNCTION__, sh7723gfx_miscdev.minor, ret ); + return ret; + } + + /* Allocate and initialize the shared area. */ +#ifdef SHARED_AREA_PHYS +#if SHARED_AREA_SIZE < PAGE_ALIGN(sizeof(SH772xGfxSharedArea)) +#error SHARED_AREA_SIZE < PAGE_ALIGN(sizeof(SH772xGfxSharedArea))! +#endif + shared = ioremap( SHARED_AREA_PHYS, PAGE_ALIGN(sizeof(SH772xGfxSharedArea)) ); +#else + shared_order = get_order(sizeof(SH772xGfxSharedArea)); + shared_page = alloc_pages( GFP_DMA | GFP_KERNEL, shared_order ); + shared = ioremap( virt_to_phys( page_address(shared_page) ), + PAGE_ALIGN(sizeof(SH772xGfxSharedArea)) ); + + for (i=0; i<1<