OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [lcddma.c] - Rev 1626

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * Device driver for driving LCD module via DMA.
 * It continously copies a memory area to the LCD.
 * It is up to the user to embed timing control signals in the memory data.
 *
 * Hardware config:
 *  Second timer output connected to DMA1 request (DREQ 0) input
 *
 * Copyright (C) 1999 Rob Scott (rscott@mtrob.fdns.net)
 */
 
#ifndef __KERNEL__
#define __KERNEL__
#endif
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
 
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include <asm/mcfdma.h>
#include <asm/mcftimer.h>
#include <asm/param.h>     /* for HZ */
 
/*
 * From sysdep 2.1:
 */
 
#ifndef LINUX_VERSION_CODE
#  include <linux/version.h>
#endif
 
#ifndef VERSION_CODE
#  define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
#endif
 
/* only allow 2.0.x and 2.1.y */
 
#if LINUX_VERSION_CODE < VERSION_CODE(2,0,0)
#  error "This kernel is too old: not supported by this file"
#endif
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,0)
#  define LINUX_20
#else
#  define LINUX_21
#endif
 
/* changed the prototype of read/write */
 
#if defined(LINUX_21) || defined(__alpha__)
# define count_t unsigned long
# define read_write_t long
#else
# define count_t int
# define read_write_t int
#endif
 
 
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,31)
# define release_t void
#  define release_return(x) return
#else
#  define release_t int
#  define release_return(x) return (x)
#endif
 
/*
 * access to user space: use the 2.1 functions,
 * and implement them as macros for 2.0
 */
 
#ifdef LINUX_20
#  include <asm/segment.h>
#  define access_ok(t,a,sz)           (verify_area((t),(a),(sz)) ? 0 : 1)
#  define verify_area_20              verify_area
#  define copy_to_user(t,f,n)         (memcpy_tofs(t,f,n), 0)
#  define __copy_to_user(t,f,n)       copy_to_user((t),(f),(n))
#  define copy_to_user_ret(t,f,n,r)   copy_to_user((t),(f),(n))
#  define copy_from_user(t,f,n)       (memcpy_fromfs((t),(f),(n)), 0)
#  define __copy_from_user(t,f,n)     copy_from_user((t),(f),(n))
#  define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
#  define PUT_USER(val,add)           (put_user((val),(add)), 0)
#  define __PUT_USER(val,add)         PUT_USER((val),(add))
#  define PUT_USER_RET(val,add,ret)   PUT_USER((val),(add))
#  define GET_USER(dest,add)          ((dest)=get_user((add)), 0)
#  define __GET_USER(dest,add)        GET_USER((dest),(add))
#  define GET_USER_RET(dest,add,ret)  GET_USER((dest),(add))
#else
#  include <asm/uaccess.h>
#  include <asm/io.h>
#  define verify_area_20(t,a,sz) (0) /* == success */
#  define PUT_USER put_user
#  define __PUT_USER __put_user
#  define PUT_USER_RET put_user_ret
#  define GET_USER get_user
#  define __GET_USER __get_user
#  define GET_USER_RET get_user_ret
#endif
 
 
/*
 * Use one of the "local" device numbers
 */
 
#define LCDDMA_MAJOR 120
 
/* Define interrupt vector location */
#define DMA_VEC_LOC 120
 
/* Define which dma channel to use */
#define CHAN 0 
 
/* Define DMA data destination address (I/O device) */
#define DMA_DEST_ADDR 0x40000000
 
/*
 * Driver data structures
 */
 
unsigned int dma_xfer_len = 16;
 
/* Place to store screen data */
unsigned short dma_buf[32*1024];
 
unsigned short twiddle_array[16] = {0x3f00, 0x0600, 0x5b00, 0x4f00,
				    0x6600, 0x6d00, 0x7d00, 0x0700,
				    0x7f00, 0x6700, 0x7700, 0x7c00,
				    0x3900, 0x5e00, 0x7900, 0x7100};
 
 
/*
 * Define return values
 */
static int lcddma_open(struct inode *inode, struct file *filp);
static release_t lcddma_release (struct inode *inode, struct file *filp);
static read_write_t lcddma_read (struct inode *inode, struct file *filp,
				 char *buf, count_t count);
 
static read_write_t lcddma_write (struct inode *inode, struct file *filp,
				  const char *buf, count_t count);
 
/* structure defined in fs.h */
struct file_operations lcddma_fops = {
  NULL,          /* lseek */
  lcddma_read,
  lcddma_write,
  NULL,          /* readdir */
  NULL,          /* poll */
  NULL,          /* ioctl */
  NULL,          /* mmap */
  lcddma_open,
  lcddma_release,
  NULL,          /* fsync */
  NULL,          /* fasync */
  NULL,          /* check_media_change */
  NULL           /* revalidate */
};
 
//#define LCDDMA_DEBUG
 
#ifdef LCDDMA_DEBUG
void lcddma_dump_regs(void) {
  volatile unsigned long	*dmap;
  volatile unsigned short	*dmapw;
  volatile unsigned char	*dmapb;
 
  dmap = (unsigned long *)   (MCF_MBAR + MCFDMA_BASE0);
  dmapw = (unsigned short *) (MCF_MBAR + MCFDMA_BASE0);
  dmapb = (unsigned char *)  (MCF_MBAR + MCFDMA_BASE0);
 
  /* Dump regs */
  printk("Src:  %08x\n", dmap[MCFDMA_SAR]);
  printk("Dest: %08x\n", dmap[MCFDMA_DAR]);
  printk("Byte: %08x\n", dmapw[MCFDMA_BCR]);
  printk("Ctl:  %08x\n", dmapw[MCFDMA_DCR]);
  printk("stat: %08x\n", dmapb[MCFDMA_DSR]);
  printk("vec:  %08x\n", dmapb[MCFDMA_DIVR]);
}
#endif
 
 
/*
 * lcddma_open - allocate memory buffer, start DMA
 */
 
static int lcddma_open(struct inode *inode, struct file *filp) {
 
  /* Clear DMA interrupt */
  disable_dma(CHAN);
 
  /* Do DMA write to i/o operation */
  set_dma_mode(CHAN, DMA_MODE_WRITE_WORD);
  set_dma_device_addr(CHAN, DMA_DEST_ADDR);
  set_dma_addr(CHAN, (unsigned int)dma_buf);
  set_dma_count(CHAN, dma_xfer_len);
 
#ifdef LCDDMA_DEBUG
  lcddma_dump_regs();
#endif
 
  /* Fire it off! */
  enable_dma(CHAN);
 
  return(0);
}
 
/*
 * lcddma_read - return device bitmap address
 */
 
static read_write_t lcddma_read (struct inode *inode, struct file *filp,
			  char *buf, count_t count)
{
  unsigned long temp;
  unsigned long *tempP;
 
  tempP = &(temp);
  temp = (unsigned int)(dma_buf);
 
  copy_to_user(buf, tempP, 4);
 
  /* Return number of bytes read */
  return (4);
}
 
 
/*
 * lcddma_write - set dma xfer length
 */
 
static read_write_t lcddma_write (struct inode *inode, struct file *filp,
			   const char *buf, count_t count)
{
  int i;
  unsigned int temp;
 
  temp = 0;
  for (i = 0; i < 4; i++) {
    temp = (temp << 8) | (unsigned char)buf[i];
  }
 
  dma_xfer_len = temp & 0xffff;
 
  return(4);
}
 
/*
 * lcddma_release - deallocate DMA buffer, stop DMAs...
 */
 
static release_t lcddma_release (struct inode *inode, struct file *filp) {
  unsigned long flags;
 
  /* Save current interrupt status: enabled or disabled */
  save_flags(flags);
 
  /* Disable interrupts */
  cli();
 
  /* Turn off DMA interrupts and stop any DMA in progress */
  disable_dma(CHAN);
 
  /* Restore interrupt status */
  restore_flags(flags);
 
  MOD_DEC_USE_COUNT;
 
  release_return(0);
}
 
/*
 * Send out the same buffer upon completion of DMA
 */
 
void lcddma_isr(int irq, void *dev_id, struct pt_regs *regs) {
 
  /* Clear DMA interrupt */
  disable_dma(CHAN);
 
  /* Do DMA write to i/o operation */
  set_dma_mode(CHAN, DMA_MODE_WRITE_WORD);
  set_dma_device_addr(CHAN, DMA_DEST_ADDR);
  set_dma_addr(CHAN, (unsigned int)dma_buf);
  set_dma_count(CHAN, dma_xfer_len);
 
  /* Fire it off! */
  enable_dma(CHAN);
 
}
 
 
/*
 * Init the interrupt vector location, and setup timer 2
 */
 
 
void lcddma_init(void) {
  volatile unsigned char *regp;
  volatile unsigned short *shortp;
  int i;
  int result;
 
  /* Init dma buffer */
  for (i= 0; i < 16; i++) {
    dma_buf[i] = twiddle_array[i] | i;
  }
 
  /* Register lcddma as character device */
  result = register_chrdev(LCDDMA_MAJOR, "lcddma", &lcddma_fops);
  if (result < 0) {
    printk(KERN_WARNING "LCDDMA: can't get major %d\n",LCDDMA_MAJOR);
    return;
  }
 
  /* Set interrupt level and priority */
  /* Note: this is hardwired for channel 0 */
  regp = (volatile unsigned char *) (MCF_MBAR);
  regp[MCFSIM_DMA1ICR] = MCFSIM_ICR_LEVEL6 | MCFSIM_ICR_PRI3;
  mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_DMA1);
 
  /* Set interrupt vector location */
  /* Note: this is hardwired for channel 0 */
  regp[MCFDMA_BASE0 + MCFDMA_DIVR] = DMA_VEC_LOC;
 
  /* Install ISR (interrupt service routine) */
  printk ("lcddma: requesting IRQ %d\n", DMA_VEC_LOC);
  result = request_irq(DMA_VEC_LOC, lcddma_isr, SA_INTERRUPT, "lcddma", NULL);
  if (result) {
    printk ("lcddma: IRQ %d already in use\n", DMA_VEC_LOC);
    return;
  }
 
  /* Request DMA channel */
  printk ("lcddma: requesting DMA channel %d\n", CHAN);
  result = request_dma(CHAN, "lcddma");
  if (result) {
    printk ("lcddma: dma channel %d already in use\n", CHAN);
    return;
  }
 
  /* Setup timer 2 as LCD pixel clock */
  shortp = (volatile unsigned short *) (MCF_MBAR + MCFTIMER_BASE2);
 
  shortp[MCFTIMER_TMR] = MCFTIMER_TMR_DISABLE;
  shortp[MCFTIMER_TER] = MCFTIMER_TER_CAP | MCFTIMER_TER_REF;
  shortp[MCFTIMER_TRR] = (unsigned short) ((MCF_CLK/10000) / HZ );
  shortp[MCFTIMER_TMR] = (0 << 8) | /* set prescaler to divide by 1 */
                        MCFTIMER_TMR_DISCE | MCFTIMER_TMR_DISOM |
                        MCFTIMER_TMR_ENORI | MCFTIMER_TMR_RESTART |
                        MCFTIMER_TMR_CLK1  | MCFTIMER_TMR_ENABLE;
 
  /* Enable external DMA requests on TIN0/DREQ0 pin */
#define MCFSIM_PAR_PAR8 (1 << 8)
  shortp = (volatile unsigned short *)(MCF_MBAR + MCFSIM_PAR);
  shortp[0] |= MCFSIM_PAR_PAR8;
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.