diff options
Diffstat (limited to 'drivers/misc/fm_2351/sr2351_fm_ctrl.c')
| -rw-r--r-- | drivers/misc/fm_2351/sr2351_fm_ctrl.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/drivers/misc/fm_2351/sr2351_fm_ctrl.c b/drivers/misc/fm_2351/sr2351_fm_ctrl.c new file mode 100644 index 00000000..d98c5f03 --- /dev/null +++ b/drivers/misc/fm_2351/sr2351_fm_ctrl.c @@ -0,0 +1,473 @@ +#include <linux/miscdevice.h> +#include <linux/sysfs.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/ioctl.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <asm/io.h> +#include "sr2351_fm_ctrl.h" + +#ifdef CONFIG_OF +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#endif + +#define SR2351_FM_VERSION "v0.9" +static u32 g_volume = 0; + +int sr2351_fm_set_volume(u32 iarg) +{ + SR2351_PRINT("FM set volume : %i.", iarg); + + g_volume = iarg; + if(0 == iarg) + { + sr2351_fm_mute(); + } + else + { + sr2351_fm_unmute(); + } + + return 0; +} + +u32 sr2351_fm_get_volume(void) +{ + SR2351_PRINT("FM get volume."); + return g_volume; +} + + + +int sr2351_fm_open(struct inode *inode, struct file *filep) +{ + int ret = -EINVAL; + int status; + + SR2351_PRINT("start open shark fm module..."); +/* + if (get_suspend_state() != PM_SUSPEND_ON) + { + SR2351_PRINT("The system is suspending!"); + return -2; + } +*/ +/* + ret = nonseekable_open(inode, filep); + if (ret < 0) { + SR2351_PRINT("open misc device failed."); + return ret; + } +*/ + + ret = sr2351_fm_init(); + if (ret < 0) { + SR2351_PRINT("sr2351_fm_init failed!"); + return ret; + } + + ret = sr2351_fm_get_status(&status); + if (ret < 0) { + SR2351_PRINT("sr2351_read_fm_en failed!"); + return ret; + } + if (status) { + SR2351_PRINT("sr2351 fm have been opened."); + } else { + ret = sr2351_fm_en(); + if (ret < 0) { + SR2351_PRINT("sr2351_fm_en failed!"); + return ret; + } + } + + SR2351_PRINT("Open shark fm module success."); + + return 0; +} + +long sr2351_fm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + long ret = 0; + u32 iarg = 0; + u32 buf[4] = { 0 }; + + SR2351_PRINT("FM IOCTL: 0x%x.", cmd); + + switch (cmd) { + case FM_IOCTL_ENABLE: + if (copy_from_user(&iarg, argp, sizeof(iarg)) || iarg > 1) + ret = -EFAULT; + + if (iarg == 1) + ret = sr2351_fm_en(); + else + ret = sr2351_fm_dis(); + + break; + + case FM_IOCTL_GET_ENABLE: + ret = sr2351_fm_get_status(&iarg); + if (copy_to_user(argp, &iarg, sizeof(iarg))) + ret = -EFAULT; + + break; + + case FM_IOCTL_SET_TUNE: + if (copy_from_user(&iarg, argp, sizeof(iarg))) + return -EFAULT; + sr2351_fm_mute(); + ret = sr2351_fm_set_tune(iarg); + sr2351_fm_unmute(); + break; + + case FM_IOCTL_GET_FREQ: + ret = sr2351_fm_get_frequency(&iarg); + if (copy_to_user(argp, &iarg, sizeof(iarg))) + ret = -EFAULT; + break; + + case FM_IOCTL_SEARCH: + if (copy_from_user(buf, argp, sizeof(buf))) + ret = -EFAULT; + sr2351_fm_mute(); + ret = sr2351_fm_seek(buf[0], /* start frequency */ + buf[1], /* seek direction */ + buf[2], /* time out */ + &buf[3]); /* frequency found */ + + if (copy_to_user(argp, buf, sizeof(buf))) + ret = -EFAULT; + + break; + + case FM_IOCTL_STOP_SEARCH: + ret = sr2351_fm_stop_seek(); + break; + + case FM_IOCTL_MUTE: + break; + + case FM_IOCTL_SET_VOLUME: + if (copy_from_user(&iarg, argp, sizeof(iarg))) + ret = -EFAULT; + + ret = sr2351_fm_set_volume(iarg); + break; + + case FM_IOCTL_GET_VOLUME: + iarg = sr2351_fm_get_volume(); + if (copy_to_user(argp, &iarg, sizeof(iarg))) + ret = -EFAULT; + + break; + + case FM_IOCTL_CONFIG: + if (copy_from_user(&iarg, argp, sizeof(iarg))) + ret = -EFAULT; + + SR2351_PRINT("\n\nFM FM_IOCTL_CONFIG set %d\n\n", iarg); + + break; + + case FM_IOCTL_GET_RSSI: + ret = sr2351_fm_get_rssi(&iarg); + if(copy_to_user(argp,&iarg,sizeof(iarg))) + ret = -EFAULT; + break; + + case FM_IOCTL_SET_BAND: + if (copy_from_user(buf, argp, sizeof(buf))) + ret = -EFAULT; + ret = sr2351_fm_set_band(buf[0],//min freq + buf[1]);//max freq + break; + + case FM_IOCTL_CHECK_STATUS: + ret = sr2351_fm_check_status(&iarg); + if(copy_to_user(argp,&iarg,sizeof(iarg))) + ret = -EFAULT; + break; + + default: + SR2351_PRINT("Unknown FM IOCTL!"); + return -EINVAL; + } + + return ret; +} + +int sr2351_fm_release(struct inode *inode, struct file *filep) +{ + SR2351_PRINT("sr2351_fm_misc_release"); + + sr2351_fm_deinit(); + + return 0; +} + +const struct file_operations sr2351_fm_misc_fops = { + .owner = THIS_MODULE, + .open = sr2351_fm_open, + .unlocked_ioctl = sr2351_fm_ioctl, + .release = sr2351_fm_release, +}; + +struct miscdevice sr2351_fm_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = SR2351_FM_DEV_NAME, + .fops = &sr2351_fm_misc_fops, +}; + +#ifdef CONFIG_OF +static struct sr2351_fm_addr +{ + unsigned long fm_base; + unsigned long apb_base; + unsigned long pmu_base; + unsigned long aonckg_base; + unsigned long pin_base; +}sr2351_fm_base; + +unsigned long rf2351_fm_get_fm_base(void) +{ + return sr2351_fm_base.fm_base; +} +unsigned long rf2351_fm_get_apb_base(void) +{ + return sr2351_fm_base.apb_base; +} + +unsigned long rf2351_fm_get_pmu_base(void) +{ + return sr2351_fm_base.pmu_base; +} + +unsigned long rf2351_fm_get_aonckg_base(void) +{ + return sr2351_fm_base.aonckg_base; +} + +unsigned long rf2351_fm_get_pin_base(void) +{ + return sr2351_fm_base.pin_base; +} + +static int sr2351_fm_parse_dts(struct device_node *np) +{ + int ret; + struct resource res; + + //np = of_find_node_by_name(NULL, "sprd_fm"); + if (NULL == np) + { + SR2351_PRINT("Can't get the sprd_fm node!\n"); + return -ENODEV; + } + SR2351_PRINT(" find the fm_sr2351 node!\n"); + + ret = of_address_to_resource(np, 0, &res); + if (ret < 0) + { + SR2351_PRINT("Can't get the fm reg base!\n"); + return -EIO; + } + sr2351_fm_base.fm_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res)); + if(!sr2351_fm_base.fm_base) + { + BUG(); + } + SR2351_PRINT("fm reg base is 0x%x\n", sr2351_fm_base.fm_base); + + ret = of_address_to_resource(np, 1, &res); + if (ret < 0) + { + SR2351_PRINT("Can't get the apb reg base!\n"); + return -EIO; + } + sr2351_fm_base.apb_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res)); + if(!sr2351_fm_base.apb_base) + { + BUG(); + } + SR2351_PRINT("apb reg base is 0x%x\n", sr2351_fm_base.apb_base); + + ret = of_address_to_resource(np, 2, &res); + if (ret < 0) + { + SR2351_PRINT("Can't get the pmu reg base!\n"); + return -EIO; + } + sr2351_fm_base.pmu_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res)); + if(!sr2351_fm_base.pmu_base) + { + BUG(); + } + SR2351_PRINT("pmu reg base is 0x%x\n", sr2351_fm_base.pmu_base); + + ret = of_address_to_resource(np, 3, &res); + if (ret < 0) + { + SR2351_PRINT("Can't get the aonckg reg base!\n"); + return -EIO; + } + sr2351_fm_base.aonckg_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res)); + if(!sr2351_fm_base.aonckg_base) + { + BUG(); + } + SR2351_PRINT("pmu reg base is 0x%x\n", sr2351_fm_base.aonckg_base); + + ret = of_address_to_resource(np, 4, &res); + if (ret < 0) + { + SR2351_PRINT("Can't get the pin reg base!\n"); + return -EIO; + } + sr2351_fm_base.pin_base = (unsigned long)ioremap_nocache(res.start, resource_size(&res)); + if(!sr2351_fm_base.pin_base) + { + BUG(); + } + SR2351_PRINT("pin reg base is 0x%x\n", sr2351_fm_base.pin_base); + + + return 0; +} + +static const struct of_device_id of_match_table_fm[] = { + { .compatible = "sprd,sprd_fm", }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_match_table_fm); +#endif + +static int sr2351_fm_probe(struct platform_device *pdev) +{ + int ret = -EINVAL; + char *ver_str = SR2351_FM_VERSION; + +#ifdef CONFIG_OF + struct device_node *np; + np = pdev->dev.of_node; + + ret = sr2351_fm_parse_dts(np); + if (ret < 0) + { + SR2351_PRINT("sr2351_fm_parse_dts failed!"); + return ret; + } +#endif + + SR2351_PRINT("**********************************************"); + SR2351_PRINT(" SR2351 FM driver "); + SR2351_PRINT(" Version: %s", ver_str); + SR2351_PRINT(" Build date: %s %s", __DATE__, __TIME__); + SR2351_PRINT("**********************************************"); + + ret = misc_register(&sr2351_fm_misc_device); + if (ret < 0) + { + SR2351_PRINT("misc_register failed!"); + return ret; + } + + SR2351_PRINT("sr2351_fm_init success.\n"); + + return 0; +} + +static int sr2351_fm_remove(struct platform_device *pdev) +{ + + SR2351_PRINT("exit_fm_driver!\n"); + misc_deregister(&sr2351_fm_misc_device); + + return 0; +} + +#ifdef CONFIG_PM +static int sr2351_fm_suspend(struct platform_device *dev, pm_message_t state) +{ + sr2351_fm_enter_sleep(); + return 0; +} + +static int sr2351_fm_resume(struct platform_device *dev) +{ + sr2351_fm_exit_sleep(); + return 0; +} +#else +#define sr2351_fmt_suspend NULL +#define sr2351_fm_resume NULL +#endif + + +static struct platform_driver sr2351_fm_driver = { + .driver = { + .name = "sprd_fm", + .owner = THIS_MODULE, + #ifdef CONFIG_OF + .of_match_table = of_match_ptr(of_match_table_fm) , + #endif + }, + .probe = sr2351_fm_probe, + .remove = sr2351_fm_remove, + .suspend = sr2351_fm_suspend, + .resume = sr2351_fm_resume, +}; + +#ifndef CONFIG_OF +struct platform_device sr2351_fm_device = { + .name = "sprd_fm", + .id = -1, +}; +#endif + +int __init init_fm_driver(void) +{ + int ret; +#ifndef CONFIG_OF + ret = platform_device_register(&sr2351_fm_device); + if (ret) + { + SR2351_PRINT("sr2351_fm: platform_device_register failed: %d\n", ret); + return ret; + } +#endif + ret = platform_driver_register(&sr2351_fm_driver); + if (ret) + { + #ifndef CONFIG_OF + platform_device_unregister(&sr2351_fm_device); + #endif + SR2351_PRINT("sr2351_fm: probe failed: %d\n", ret); + } + SR2351_PRINT("sr2351_fm: probe sucess: %d\n", ret); + + return ret; +} + +void __exit exit_fm_driver(void) +{ + platform_driver_unregister(&sr2351_fm_driver); +} + +module_init(init_fm_driver); +module_exit(exit_fm_driver); + +MODULE_DESCRIPTION("SR2351 FM radio driver"); +MODULE_AUTHOR("Spreadtrum Inc."); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SR2351_FM_VERSION); + |
