android_kernel_cmhtcleo/arch/arm/mach-msm/board-htcleo-ls.c

393 lines
7.7 KiB
C

/* board-htcleo-ls.c
*
* Copyright (C) 2010 Cotulla
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
/*
Light sensor support for HTC LEO
base code copied from microp
because we have not interrupts, we use polling. polling time for now is 1 sec.
user may be able to adjust time in future
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/jiffies.h>
#include <linux/lightsensor.h>
#include <linux/earlysuspend.h>
#include <mach/board-htcleo-microp.h>
#include "board-htcleo.h"
struct early_suspend early_suspend;
//#define CFG_LSENSOR_TYPE
//#define LSENSOR_ABLK_PLUS_ADC 1
//#define LSENSOR_ABLK_ONLY 2
//#define LSENSOR_ABLK_ONLY 2
#define LSENSOR_POLL_PROMESHUTOK 1000
#define D(x...) pr_debug(x)
// pr_info(x)
static uint16_t lsensor_adc_table[10] =
{
0, 5, 20, 70, 150, 240, 330, 425, 515, 590
};
static struct lsensor_data
{
struct input_dev *input_dev;
struct platform_device *dev;
struct delayed_work work;
uint32_t old_level;
int enabled;
int opened;
} the_data;
int microp_read_lightsensor_value(uint32_t *val);
static DEFINE_MUTEX(api_lock);
// range: adc: 0..1023, value: 0..9
static void map_adc_to_level(uint32_t adc, uint32_t *value)
{
int i;
if (adc > 1024)
{
pr_err("%s: adc > 1024\n", __func__);
*value = 5; // set some good value at error
return;
}
for (i = 9; i >= 0; i--)
{
if (adc >= lsensor_adc_table[i])
{
D("%s: %d -> %d\n", __func__, adc, i);
*value = i;
return;
}
}
// should not happen
*value = 5; // set some good value at error
}
int lightsensor_read_value(uint32_t *val)
{
int ret;
uint8_t data[2];
if (!val)
return -EIO;
ret = microp_i2c_read(MICROP_I2C_RCMD_LSENSOR, data, 2);
if (ret < 0)
{
pr_err("%s: read ls fail\n", __func__);
return -EIO;
}
*val = data[1] | (data[0] << 8);
D("lsensor adc = %u\n", *val); /* val is unsigned */
return 0;
}
static void lightsensor_poll_function(struct work_struct *work)
{
struct lsensor_data* p = &the_data;
uint32_t adc = 0, level = 0;
D("%s\n", __func__);
if (!p->enabled)
{
D(" disable\n");
return;
}
lightsensor_read_value(&adc);
map_adc_to_level(adc, &level);
//if (level != the_data.old_level)
{
input_report_abs(the_data.input_dev, ABS_MISC, (int)level);
input_sync(the_data.input_dev);
the_data.old_level = level;
}
schedule_delayed_work(&the_data.work, msecs_to_jiffies(LSENSOR_POLL_PROMESHUTOK));
}
static int lightsensor_enable(void)
{
struct lsensor_data* p = &the_data;
int rc = -EIO;
D("%s\n", __func__);
if (p->enabled)
{
pr_err("lsensor: already enabled\n");
return 0;
}
rc = capella_cm3602_power(LS_PWR_ON, 1);
if (rc < 0)
return -EIO;
the_data.old_level = -1;
p->enabled = 1;
schedule_delayed_work(&the_data.work, msecs_to_jiffies(LSENSOR_POLL_PROMESHUTOK));
return 0;
}
static int lightsensor_disable(void)
{
struct lsensor_data* p = &the_data;
int rc = -EIO;
D("%s\n", __func__);
if (!p->enabled)
{
pr_err("lsensor: nothing to disable\n");
return 0;
}
rc = capella_cm3602_power(LS_PWR_ON, 0);
if (rc < 0)
return -EIO;
p->enabled = 0;
cancel_delayed_work_sync(&the_data.work);
return 0;
}
static int lightsensor_open(struct inode *inode, struct file *file)
{
int rc = 0;
D("%s\n", __func__);
mutex_lock(&api_lock);
if (the_data.opened)
{
pr_err("%s: already opened\n", __func__);
rc = -EBUSY;
}
the_data.opened = 1;
mutex_unlock(&api_lock);
return rc;
}
static int lightsensor_release(struct inode *inode, struct file *file)
{
D("%s\n", __func__);
mutex_lock(&api_lock);
the_data.opened = 0;
mutex_unlock(&api_lock);
return 0;
}
static long lightsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc, val;
struct lsensor_data* p = &the_data;
mutex_lock(&api_lock);
D("%s cmd %d\n", __func__, _IOC_NR(cmd));
if (!the_data.opened)
{
return -EIO;
}
switch (cmd)
{
case LIGHTSENSOR_IOCTL_ENABLE:
if (get_user(val, (unsigned long __user *)arg))
{
rc = -EFAULT;
break;
}
D("%s ls set to: %d\n", __func__, val);
rc = val ? lightsensor_enable() : lightsensor_disable();
break;
case LIGHTSENSOR_IOCTL_GET_ENABLED:
val = p->enabled;
D("%s enabled %d\n", __func__, val);
rc = put_user(val, (unsigned long __user *)arg);
break;
default:
pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
rc = -EINVAL;
}
mutex_unlock(&api_lock);
return rc;
}
static struct file_operations lightsensor_fops =
{
.owner = THIS_MODULE,
.open = lightsensor_open,
.release = lightsensor_release,
.unlocked_ioctl = lightsensor_ioctl
};
struct miscdevice lightsensor_misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "lightsensor",
.fops = &lightsensor_fops
};
#ifdef CONFIG_HAS_EARLYSUSPEND
static void lightsensor_suspend(struct early_suspend *h)
{
lightsensor_disable();
}
static void lightsensor_resume(struct early_suspend *h)
{
lightsensor_enable();
}
#endif
//////////////////////////////////////////////////////////////////////////
static int lsensor_probe(struct platform_device *pdev)
{
int ret = -EIO;
struct input_dev *input_dev;
D("%s: probe\n", __func__);
platform_set_drvdata(pdev, &the_data);
the_data.dev = pdev;
/* Light Sensor */
/*
ret = device_create_file(&the_data->dev, &dev_attr_ls_adc);
ret = device_create_file(&pdev->dev, &dev_attr_ls_auto);
*/
input_dev = input_allocate_device();
if (!input_dev)
{
pr_err("%s: could not allocate input device\n", __func__);
ret = -ENOMEM;
goto done;
}
the_data.input_dev = input_dev;
input_set_drvdata(input_dev, &the_data);
input_dev->name = "lightsensor-level";
set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_MISC, 0, 9, 0, 0);
D("%s: registering input device\n", __func__);
ret = input_register_device(input_dev);
if (ret < 0)
{
pr_err("%s: can not register input device\n", __func__);
goto err_free_input_device;
}
D("%s: registering misc device\n", __func__);
ret = misc_register(&lightsensor_misc);
if (ret < 0)
{
pr_err("%s: can not register misc device\n", __func__);
goto err_unregister_input_device;
}
the_data.old_level = -1;
the_data.enabled=0;
the_data.opened=0;
INIT_DELAYED_WORK(&the_data.work, lightsensor_poll_function);
lightsensor_enable();
#ifdef CONFIG_HAS_EARLYSUSPEND
early_suspend.level =
EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
early_suspend.suspend = lightsensor_suspend;
early_suspend.resume = lightsensor_resume;
register_early_suspend(&early_suspend);
#endif
if (!ret)
goto done;
misc_deregister(&lightsensor_misc);
err_unregister_input_device:
input_unregister_device(input_dev);
goto done;
err_free_input_device:
input_free_device(input_dev);
done:
return ret;
// device_remove_file(&client->dev, &dev_attr_ls_adc);
// device_remove_file(&pdev->dev, &dev_attr_ls_auto);
}
static struct platform_driver lsensor_driver =
{
.probe = lsensor_probe,
.driver =
{
.name = "htcleo-lsensor",
.owner = THIS_MODULE
},
};
static int __init lsensor_init(void)
{
return platform_driver_register(&lsensor_driver);
}
//device_initcall(lsensor_init);
late_initcall(lsensor_init);
MODULE_AUTHOR("Cotulla");
MODULE_DESCRIPTION("LEO LSensor driver");
MODULE_LICENSE("GPL");
// END OF FILE