/*++
linux/drivers/char/wmt-pwm.c
Copyright (c) 2008 WonderMedia Technologies, Inc.
This program is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software Foundation,
either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#define WMT_PWM_C
/*--- wmt-pwm.c ---------------------------------------------------------------
* Copyright (C) 2009 WMT Tech. Inc.
*
* MODULE : wmt-pwm.c
* AUTHOR : Sam Shen
* DATE : 2009/8/12
* DESCRIPTION :
*-----------------------------------------------------------------------------*/
/*--- History -------------------------------------------------------------------
*Version 0.01 , Sam Shen, 2009/8/12
* First version
*
*------------------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "wmt-pwm.h"
// #define DEBUG
#ifdef __KERNEL__
#define DPRINT printk
#else
#define DPRINT printf
#endif
#ifdef DEBUG
#define DBG(fmt, args...) DPRINT("[BTM] %s: " fmt, __FUNCTION__ , ## args)
#define DBG_DETAIL(fmt, args...) DPRINT("[BTM] %s: " fmt, __FUNCTION__ , ## args)
#else
#define DBG(fmt, args...)
#define DBG_DETAIL(fmt, args...)
#endif
#define DEVICE_NAME "WMT-PWM"
#define PWM_NUM 2 /* WM3429 has 2 pwm outputs */
struct pwm_dev_s {
/* module parameters */
/* char dev struct */
struct cdev cdev;
};
#define PWM_MAJOR 239
static int pwm_dev_major = PWM_MAJOR;
static int pwm_dev_minor = 0;
static int pwm_dev_nr = 1;
static int pwm_dev_ref = 0; /* is device open */
static struct pwm_dev_s pwm_dev;
unsigned int pwm_level[2] = { 0, 0 };
unsigned int pwm_freq[2] = {16000, 16000 }; //modified by howayhuo. org: {1000, 1000 }
static unsigned int pwm_enable[2] = {0,0};
DEFINE_SEMAPHORE(pwm_lock);
#define ENV_DISPLAY_PWM "wmt.display.pwm"
#define ENV_LCD_POWER "wmt.gpo.lcd"
#define GPIO_PHY_BASE_ADDR (GPIO_BASE_ADDR-WMT_MMAP_OFFSET)
struct gpio_operation_t {
unsigned int id;
unsigned int act;
unsigned int bitmap;
unsigned int ctl;
unsigned int oc;
unsigned int od;
};
static struct gpio_operation_t g_lcd_pw_pin;
static int g_pwm_invert;
static int g_pwm_param;
static unsigned int g_pwm_power;
extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
extern void lcd_set_enable(int enable);
void pwm_get_env(void)
{
unsigned char buf[100];
int varlen = 100;
g_pwm_invert = 0;
if( wmt_getsyspara(ENV_DISPLAY_PWM,buf,&varlen) == 0) {
//printk(" pwm_get_env : %s = %s \n",ENV_DISPLAY_PWM,buf);
sscanf(buf,"%x",&g_pwm_invert);
//sscanf(buf,"%d",&g_pwm_invert);
g_pwm_invert = (g_pwm_invert&0xFF);
g_pwm_invert = g_pwm_invert >> 4;
}
memset(&g_lcd_pw_pin, 0, sizeof(struct gpio_operation_t));
if( wmt_getsyspara(ENV_LCD_POWER,buf,&varlen) == 0) {
//printk(" pwm_get_env : %s = %s \n",ENV_LCD_POWER,buf);
sscanf(buf,"%x:%d:%x:%x:%x:%x",&g_lcd_pw_pin.id, &g_lcd_pw_pin.act, &g_lcd_pw_pin.bitmap, &g_lcd_pw_pin.ctl, \
&g_lcd_pw_pin.oc, &g_lcd_pw_pin.od);
if ((g_lcd_pw_pin.ctl&0xffff0000) == GPIO_PHY_BASE_ADDR)
g_lcd_pw_pin.ctl += WMT_MMAP_OFFSET;
if ((g_lcd_pw_pin.oc&0xffff0000) == GPIO_PHY_BASE_ADDR)
g_lcd_pw_pin.oc += WMT_MMAP_OFFSET;
if ((g_lcd_pw_pin.od&0xffff0000) == GPIO_PHY_BASE_ADDR)
g_lcd_pw_pin.od += WMT_MMAP_OFFSET;
}
g_pwm_param = 1;
}
#define pwm_write_reg(addr,val,wait) \
REG32_VAL(addr) = val; \
while(REG32_VAL(PWM_STS_REG_ADDR)&=wait);
void pwm_set_control(int no,unsigned int ctrl)
{
unsigned int addr;
addr = PWM_CTRL_REG_ADDR + (0x10 * no);
pwm_write_reg(addr,ctrl,PWM_CTRL_UPDATE << (8*no));
} /* End of pwm_proc */
void pwm_set_scalar(int no,unsigned int scalar)
{
unsigned int addr;
addr = PWM_SCALAR_REG_ADDR + (0x10 * no);
pwm_write_reg(addr,scalar,PWM_SCALAR_UPDATE << (8*no));
}
void pwm_set_period(int no,unsigned int period)
{
unsigned int addr;
addr = PWM_PERIOD_REG_ADDR + (0x10 * no);
pwm_write_reg(addr,period,PWM_PERIOD_UPDATE << (8*no));
}
void pwm_set_duty(int no,unsigned int duty)
{
unsigned int addr;
addr = PWM_DUTY_REG_ADDR + (0x10 * no);
pwm_write_reg(addr,duty,PWM_DUTY_UPDATE << (8*no));
}
unsigned int pwm_get_period(int no)
{
unsigned int addr;
addr = PWM_PERIOD_REG_ADDR + (0x10 * no);
return (REG32_VAL(addr) & 0xFFF);
}
unsigned int pwm_get_duty(int no)
{
unsigned int addr;
addr = PWM_DUTY_REG_ADDR + (0x10 * no);
return (REG32_VAL(addr) & 0xFFF);
}
unsigned int get_lcd_power(void)
{
return g_pwm_power;
/*
unsigned int val, reg;
if ((g_lcd_pw_pin.ctl == 0) || (g_lcd_pw_pin.oc == 0) || (g_lcd_pw_pin.od == 0)) {
printk("lcd power ping not define\n");
return 0;
}
val = 1< /proc/sys/vpp
.mode = 0555,
.child = pwm_table
},
{ }
};
static struct ctl_table_header *pwm_table_header;
#endif
/*!*************************************************************************
* pwm_open()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static int pwm_open
(
struct inode *inode, /*!<; //[IN] a pointer point to struct inode */
struct file *filp /*!<; //[IN] a pointer point to struct file */
)
{
struct pwm_dev_s *dev;
int minor_no;
DBG("Enter pwm_open\n");
if(pwm_dev_ref)
return -EBUSY;
pwm_dev_ref++;
try_module_get(THIS_MODULE);
dev = container_of(inode->i_cdev,struct pwm_dev_s,cdev);
filp->private_data = dev;
minor_no = iminor(inode); /* get */
/* TODO */
/* Step 1: check hardware resource */
/* Step 2: if first then initial hardware */
/* Step 3: update f_op pointer */
/* Step 4: allocate and assign provate_data structure */
/* filp->private_data */
return 0;
} /* End of pwm_open() */
/*!*************************************************************************
* pwm_release()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static int pwm_release
(
struct inode *inode, /*!<; //[IN] a pointer point to struct inode */
struct file *filp /*!<; //[IN] a pointer point to struct file */
)
{
struct pwm_dev_s *dev;
DBG("Enter pwm_release\n");
dev = container_of(inode->i_cdev,struct pwm_dev_s,cdev);
pwm_dev_ref--;
module_put(THIS_MODULE);
/* TODO */
/* Step 1: free private data resource */
/* Step 2: if last then shutdown hardware */
return 0;
} /* End of pwm_release() */
/*!*************************************************************************
* pwm_ioctl()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static long pwm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
pwm_ctrl_t parm;
/* check type and number, if fail return ENOTTY */
if( _IOC_TYPE(cmd) != PWM_IOC_MAGIC ) return -ENOTTY;
if( _IOC_NR(cmd) >= PWM_IOC_MAXNR ) return -ENOTTY;
/* check argument area */
if( _IOC_DIR(cmd) & _IOC_READ )
err = !access_ok( VERIFY_WRITE, (void __user *) arg, _IOC_SIZE(cmd));
else if ( _IOC_DIR(cmd) & _IOC_WRITE )
err = !access_ok( VERIFY_READ, (void __user *) arg, _IOC_SIZE(cmd));
if( err ) return -EFAULT;
down(&pwm_lock);
switch(cmd){
case PWMIOSET_ENABLE:
copy_from_user( (void *) &parm, (const void *)arg, sizeof(pwm_ctrl_t));
pwm_set_enable(parm.no,parm.value);
break;
case PWMIOSET_FREQ:
copy_from_user( (void *) &parm, (const void *)arg, sizeof(pwm_ctrl_t));
pwm_freq[parm.no] = parm.value;
pwm_set_freq(parm.no,parm.value);
break;
case PWMIOGET_FREQ:
copy_from_user( (void *) &parm, (const void *)arg, sizeof(pwm_ctrl_t));
parm.value = pwm_freq[parm.no];
copy_to_user((void *) arg,(void *) &parm, sizeof(pwm_ctrl_t));
break;
case PWMIOSET_LEVEL:
copy_from_user( (void *) &parm, (const void *)arg, sizeof(pwm_ctrl_t));
pwm_level[parm.no] = parm.value;
pwm_set_level(parm.no,parm.value);
break;
case PWMIOGET_LEVEL:
copy_from_user( (void *) &parm, (const void *)arg, sizeof(pwm_ctrl_t));
parm.value = pwm_level[parm.no];
copy_to_user((void *) arg,(void *) &parm, sizeof(pwm_ctrl_t));
break;
default:
retval = -ENOTTY;
break;
}
up(&pwm_lock);
return retval;
} /* End of pwm_ioctl() */
/*!*************************************************************************
driver file operations struct define
****************************************************************************/
struct file_operations pwm_fops = {
.owner = THIS_MODULE,
.open = pwm_open,
.unlocked_ioctl = pwm_ioctl,
.release = pwm_release,
};
/*!*************************************************************************
* pwm_probe()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static int pwm_probe
(
struct platform_device *pdev /*!<; // please add parameters description her*/
)
{
int ret = 0;
dev_t dev_no;
struct cdev *cdev;
dev_no = MKDEV(pwm_dev_major,pwm_dev_minor);
/* register char device */
// cdev = cdev_alloc();
cdev = &pwm_dev.cdev;
cdev_init(cdev,&pwm_fops);
ret = cdev_add(cdev,dev_no,1);
if( ret ){
printk(KERN_ALERT "*E* register char dev \n");
return ret;
}
// printk( KERN_ALERT "/dev/%s major number %d, minor number %d\n", DEVICE_NAME, pwm_dev_major,pwm_dev_minor);
#ifdef CONFIG_PROC_FS
pwm_table_header = register_sysctl_table(pwm_root_table);
#endif
return ret;
} /* End of pwm_probe() */
/*!*************************************************************************
* pwm_remove()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static int pwm_remove
(
struct platform_device *dev /*!<; // please add parameters description her*/
)
{
struct cdev *cdev;
cdev = &pwm_dev.cdev;
cdev_del(cdev);
printk( KERN_ALERT "Enter pwm_remove \n");
return 0;
} /* End of pwm_remove() */
/*!*************************************************************************
* pwm_suspend()
*
* Private Function by Sam Shen, 2009/8/12
*/
/*!
* \brief
*
* \retval 0 if success
*/
static int pwm_suspend
(
struct platform_device *pdev, /*!<; // a pointer point to struct device */
pm_message_t state /*!<; // suspend state */
)
{
#ifndef CONFIG_ANDROID
int i;
for (i=0;i