423 lines
9.6 KiB
C
423 lines
9.6 KiB
C
|
|
/*
|
|
* linux/drivers/video/s3c_g3d.c
|
|
*
|
|
* Revision 1.0
|
|
*
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file COPYING in the main directory of this archive for
|
|
* more details.
|
|
*
|
|
* S3C PostProcessor driver
|
|
*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/types.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/errno.h> /* error codes */
|
|
#include <asm/div64.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/tty.h>
|
|
#include <asm/io.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/arch/map.h>
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/version.h>
|
|
//#include <linux/config.h>
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/mach/dma.h>
|
|
#include <asm/plat-s3c24xx/dma.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
typedef struct{
|
|
unsigned int pool_buffer_addr;
|
|
unsigned int pool_buffer_size;
|
|
unsigned int dma_buffer_addr;
|
|
unsigned int dma_buffer_size;
|
|
}G3D_CONFIG_STRUCT;
|
|
|
|
typedef struct{
|
|
unsigned int offset; // should be word aligned
|
|
unsigned int size; // byte size, should be word aligned
|
|
}DMA_BLOCK_STRUCT;
|
|
|
|
typedef struct {
|
|
ulong src;
|
|
ulong dst;
|
|
int len;
|
|
} s3c_3d_dma_info;
|
|
|
|
#define FGGB_PIPESTATE 0x00
|
|
#define FGGB_CACHECTL 0x04
|
|
#define FGGB_RST 0x8
|
|
#define FGGB_VERSION 0x10
|
|
#define FGGB_INTPENDING 0x40
|
|
#define FGGB_INTMASK 0x44
|
|
#define FGGB_PIPEMASK 0x48
|
|
#define FGGB_HOSTINTERFACE 0xc000
|
|
|
|
G3D_CONFIG_STRUCT g3d_config={
|
|
0x56000000, // pool buffer addr
|
|
0x1800000, //pool buffer size,24Mb
|
|
0x57800000, //dma buffer addr
|
|
0x800000 //dma buffer size
|
|
};
|
|
|
|
#define G3D_IOCTL_MAGIC 'S'
|
|
#define WAIT_FOR_FLUSH _IO(G3D_IOCTL_MAGIC, 100)
|
|
#define GET_CONFIG _IO(G3D_IOCTL_MAGIC, 101)
|
|
#define START_DMA_BLOCK _IO(G3D_IOCTL_MAGIC, 102)
|
|
|
|
#define PFX "s3c_g3d"
|
|
#define G3D_MINOR 249
|
|
|
|
static wait_queue_head_t waitq;
|
|
static struct resource *s3c_g3d_mem;
|
|
static void __iomem *s3c_g3d_base;
|
|
static int s3c_g3d_irq;
|
|
static struct clk *g3d_clock;
|
|
static struct clk *h_clk;
|
|
|
|
#define DMACH_3D_IN 34
|
|
void *dma_3d_done;
|
|
static struct s3c2410_dma_client s3c6410_3d_dma_client = {
|
|
.name = "s3c6410-3d-dma",
|
|
};
|
|
|
|
|
|
|
|
int interrupt_already_recevied;
|
|
|
|
unsigned int s3c_g3d_base_physical;
|
|
|
|
irqreturn_t s3c_g3d_isr(int irq, void *dev_id,
|
|
struct pt_regs *regs)
|
|
{
|
|
u32 mode;
|
|
u32 pending;
|
|
|
|
//printk("s3c_g3d interrupt ~~~~\n");
|
|
//printk("s3c_g3d_isr interrupt !!!!!!!!\n");
|
|
|
|
// mode = __raw_readl(s3c_g3d_base + S3C_VG3D_MODE);
|
|
// mode &= ~(1 << 6); /* Clear Source in POST Processor */
|
|
// __raw_writel(mode, s3c_g3d_base + S3C_VG3D_MODE);
|
|
|
|
pending = __raw_readl(s3c_g3d_base + FGGB_INTPENDING);
|
|
__raw_writel(mode, s3c_g3d_base + FGGB_INTPENDING);
|
|
|
|
if(pending>0){
|
|
|
|
}
|
|
|
|
interrupt_already_recevied = 1;
|
|
wake_up_interruptible(&waitq);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
void s3c_g3d_dma_finish(struct s3c2410_dma_chan *dma_ch, void *buf_id,
|
|
int size, enum s3c2410_dma_buffresult result){
|
|
// printk("3d dma transfer completed.\n");
|
|
complete(dma_3d_done);
|
|
}
|
|
|
|
int s3c_g3d_open(struct inode *inode, struct file *file)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s3c_g3d_read(struct file *file, char *buf, size_t count,
|
|
loff_t *f_pos)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int s3c_g3d_write(struct file *file, const char *buf, size_t
|
|
count, loff_t *f_pos)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int s3c_g3d_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
|
|
u32 val;
|
|
DMA_BLOCK_STRUCT dma_block;
|
|
s3c_3d_dma_info dma_info;
|
|
DECLARE_COMPLETION_ONSTACK(complete);
|
|
|
|
switch(cmd) {
|
|
case WAIT_FOR_FLUSH:
|
|
|
|
//if fifo has already been flushed, return;
|
|
val = __raw_readl(s3c_g3d_base+FGGB_PIPESTATE);
|
|
//printk("read pipestate = 0x%x\n",val);
|
|
if((val & arg) ==0)break;
|
|
|
|
// enable interrupt
|
|
interrupt_already_recevied = 0;
|
|
__raw_writel(0x0001171f,s3c_g3d_base+FGGB_PIPEMASK);
|
|
__raw_writel(1,s3c_g3d_base+FGGB_INTMASK);
|
|
|
|
//printk("wait for flush (arg=0x%lx)\n",arg);
|
|
|
|
|
|
while(1){
|
|
wait_event_interruptible(waitq, (interrupt_already_recevied>0));
|
|
__raw_writel(0,s3c_g3d_base+FGGB_INTMASK);
|
|
interrupt_already_recevied = 0;
|
|
//if(interrupt_already_recevied==0)interruptible_sleep_on(&waitq);
|
|
val = __raw_readl(s3c_g3d_base+FGGB_PIPESTATE);
|
|
//printk("in while read pipestate = 0x%x\n",val);
|
|
if(val & arg){}
|
|
else{
|
|
break;
|
|
}
|
|
__raw_writel(1,s3c_g3d_base+FGGB_INTMASK);
|
|
}
|
|
break;
|
|
|
|
case GET_CONFIG:
|
|
copy_to_user((void *)arg,&g3d_config,sizeof(G3D_CONFIG_STRUCT));
|
|
break;
|
|
|
|
case START_DMA_BLOCK:
|
|
copy_from_user(&dma_block,(void *)arg,sizeof(DMA_BLOCK_STRUCT));
|
|
if(dma_block.offset%4!=0)
|
|
{
|
|
printk("G3D: dma offset is not aligned by word\n");
|
|
return -EINVAL;
|
|
}
|
|
if(dma_block.size%4!=0)
|
|
{
|
|
printk("G3D: dma size is not aligned by word\n");
|
|
return -EINVAL;
|
|
}
|
|
if(dma_block.offset+dma_block.size >g3d_config.dma_buffer_size)
|
|
{
|
|
printk("G3D: offset+size exceeds dam buffer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dma_info.src = g3d_config.dma_buffer_addr+dma_block.offset;
|
|
dma_info.len = dma_block.size;
|
|
dma_info.dst = s3c_g3d_base_physical+FGGB_HOSTINTERFACE;
|
|
|
|
// printk(" dma src=0x%x\n",dma_info.src);
|
|
// printk(" dma len =%u\n",dma_info.len);
|
|
// printk(" dma dst = 0x%x\n",dma_info.dst);
|
|
|
|
dma_3d_done = &complete;
|
|
|
|
if (s3c2410_dma_request(DMACH_3D_IN, &s3c6410_3d_dma_client, NULL))
|
|
{
|
|
printk(KERN_WARNING "Unable to get DMA channel.\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
s3c2410_dma_set_buffdone_fn(DMACH_3D_IN, s3c_g3d_dma_finish);
|
|
s3c2410_dma_devconfig(DMACH_3D_IN, S3C_DMA_MEM2G3D, 1, (u_long) dma_info.src);
|
|
s3c2410_dma_config(DMACH_3D_IN, 4, 4);
|
|
s3c2410_dma_setflags(DMACH_3D_IN, S3C2410_DMAF_AUTOSTART);
|
|
//consistent_sync((void *) dma_info.dst, dma_info.len, DMA_FROM_DEVICE);
|
|
// s3c2410_dma_enqueue(DMACH_3D_IN, NULL, (dma_addr_t) virt_to_dma(NULL, dma_info.dst), dma_info.len);
|
|
s3c2410_dma_enqueue(DMACH_3D_IN, NULL, (dma_addr_t) dma_info.dst, dma_info.len);
|
|
|
|
// printk("wait for end of dma operation\n");
|
|
wait_for_completion(&complete);
|
|
// printk("dma operation is performed\n");
|
|
|
|
s3c2410_dma_free(DMACH_3D_IN, &s3c6410_3d_dma_client);
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct file_operations s3c_g3d_fops = {
|
|
.owner = THIS_MODULE,
|
|
.ioctl = s3c_g3d_ioctl,
|
|
.open = s3c_g3d_open,
|
|
.read = s3c_g3d_read,
|
|
.write = s3c_g3d_write,
|
|
};
|
|
|
|
|
|
static struct miscdevice s3c_g3d_dev = {
|
|
.minor = G3D_MINOR,
|
|
.name = "s3c-g3d",
|
|
.fops = &s3c_g3d_fops,
|
|
};
|
|
|
|
static int s3c_g3d_remove(struct platform_device *dev)
|
|
{
|
|
//clk_disable(g3d_clock);
|
|
|
|
free_irq(s3c_g3d_irq, NULL);
|
|
if (s3c_g3d_mem != NULL) {
|
|
pr_debug("s3c_g3d: releasing s3c_post_mem\n");
|
|
iounmap(s3c_g3d_base);
|
|
release_resource(s3c_g3d_mem);
|
|
kfree(s3c_g3d_mem);
|
|
}
|
|
misc_deregister(&s3c_g3d_dev);
|
|
return 0;
|
|
}
|
|
|
|
int s3c_g3d_probe(struct platform_device *pdev)
|
|
{
|
|
|
|
struct resource *res;
|
|
|
|
int ret;
|
|
int i;
|
|
|
|
printk("s3c_g3d probe\n");
|
|
|
|
s3c_g3d_irq = platform_get_irq(pdev, 0);
|
|
|
|
|
|
if(s3c_g3d_irq <= 0) {
|
|
printk(KERN_ERR PFX "failed to get irq resouce\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* get the memory region for the post processor driver */
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if(res == NULL) {
|
|
printk(KERN_ERR PFX "failed to get memory region resouce\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
s3c_g3d_base_physical = (unsigned int)res->start;
|
|
|
|
s3c_g3d_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name);
|
|
if(s3c_g3d_mem == NULL) {
|
|
printk(KERN_ERR PFX "failed to reserve memory region\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
|
|
s3c_g3d_base = ioremap(s3c_g3d_mem->start, s3c_g3d_mem->end - res->start + 1);
|
|
if(s3c_g3d_base == NULL) {
|
|
printk(KERN_ERR PFX "failed ioremap\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
g3d_clock = clk_get(&pdev->dev, "post");
|
|
if(g3d_clock == NULL) {
|
|
printk(KERN_ERR PFX "failed to find post clock source\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
clk_enable(g3d_clock);
|
|
|
|
h_clk = clk_get(&pdev->dev, "hclk");
|
|
if(h_clk == NULL) {
|
|
printk(KERN_ERR PFX "failed to find h_clk clock source\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
init_waitqueue_head(&waitq);
|
|
|
|
ret = misc_register(&s3c_g3d_dev);
|
|
if (ret) {
|
|
printk (KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
|
|
G3D_MINOR, ret);
|
|
return ret;
|
|
}
|
|
|
|
// device reset
|
|
__raw_writel(1,s3c_g3d_base+FGGB_RST);
|
|
for(i=0;i<1000;i++);
|
|
__raw_writel(0,s3c_g3d_base+FGGB_RST);
|
|
for(i=0;i<1000;i++);
|
|
|
|
ret = request_irq(s3c_g3d_irq, s3c_g3d_isr, SA_INTERRUPT,
|
|
pdev->name, NULL);
|
|
if (ret) {
|
|
printk("request_irq(PP) failed.\n");
|
|
return ret;
|
|
}
|
|
|
|
printk("s3c_g3d version : 0x%x\n",__raw_readl(s3c_g3d_base + FGGB_VERSION));
|
|
|
|
/* check to see if everything is setup correctly */
|
|
return 0;
|
|
}
|
|
|
|
static int s3c_g3d_suspend(struct platform_device *dev, pm_message_t state)
|
|
{
|
|
//clk_disable(g3d_clock);
|
|
return 0;
|
|
}
|
|
static int s3c_g3d_resume(struct platform_device *pdev)
|
|
{
|
|
//clk_enable(g3d_clock);
|
|
return 0;
|
|
}
|
|
static struct platform_driver s3c_g3d_driver = {
|
|
.probe = s3c_g3d_probe,
|
|
.remove = s3c_g3d_remove,
|
|
.suspend = s3c_g3d_suspend,
|
|
.resume = s3c_g3d_resume,
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "s3c-g3d",
|
|
},
|
|
};
|
|
|
|
static char banner[] __initdata = KERN_INFO "S3C G3D Driver, (c) 2007 Samsung Electronics\n";
|
|
|
|
int __init s3c_g3d_init(void)
|
|
{
|
|
|
|
printk(banner);
|
|
if(platform_driver_register(&s3c_g3d_driver)!=0)
|
|
{
|
|
printk("platform device register Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void s3c_g3d_exit(void)
|
|
{
|
|
platform_driver_unregister(&s3c_g3d_driver);
|
|
printk("S3C G3D module exit\n");
|
|
}
|
|
module_init(s3c_g3d_init);
|
|
module_exit(s3c_g3d_exit);
|
|
|
|
|
|
MODULE_AUTHOR("highvolt.lee@samsung.com");
|
|
MODULE_DESCRIPTION("S3C G3D Device Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|