/*++ 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