The polling every second for backlight is much for the bus, so it's better to use the internal autobl functionality. The 5s is enough for other lightsensor applications.
371 lines
7.2 KiB
C
371 lines
7.2 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"
|
|
|
|
|
|
//#define CFG_LSENSOR_TYPE
|
|
//#define LSENSOR_ABLK_PLUS_ADC 1
|
|
//#define LSENSOR_ABLK_ONLY 2
|
|
//#define LSENSOR_ABLK_ONLY 2
|
|
|
|
|
|
#define LSENSOR_POLL_PROMESHUTOK 5000
|
|
|
|
#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 = %d\n", *val);
|
|
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
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
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);
|
|
|
|
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
|