blob: d1600b404d5d0128556ba03a852c7b68dc27ddb5 [file] [log] [blame]
/*
* drivers/amlogic/input/sensor/cy8c4014.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* 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.
*
*/
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#define DRV_VERSION "1.0.0.0"
#define CY8C4014_DEV_NAME "cy8c4014"
#define CY8C4014_SCHE_DELAY 200
#define CY8C4014_ADDR 0x8
struct cy8c4014_data {
struct delayed_work work;
struct input_dev *input_dev;
struct i2c_client *i2c_client;
int delay;
int enable;
struct mutex mutex;
};
struct cy8c4014_data *cy8c4014_data;
static struct i2c_driver cy8c4014_driver;
static int cy8c4014_reset(struct i2c_client *client)
{
return 0;
}
int cy8c4014_read(struct i2c_client *dev, int add, uint8_t *val)
{
int ret;
uint8_t buf[2] = {};
struct i2c_msg msg[] = {
{
.addr = CY8C4014_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
},
{
.addr = CY8C4014_ADDR,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
}
};
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
ret = i2c_transfer(dev->adapter, msg, 2);
if (ret < 0) {
pr_info("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
static void cy8c4014_schedwork(struct work_struct *work)
{
unsigned long delay = msecs_to_jiffies(cy8c4014_data->delay);
struct input_dev *input_dev = cy8c4014_data->input_dev;
uint8_t x = 0;
/*uint8_t y = 0;*/
cy8c4014_read(cy8c4014_data->i2c_client, 0x00, &x);
/*cy8c4014_read(cy8c4014_data->i2c_client,0x01,&y);*/
if (x != 0xFF)
input_report_abs(input_dev, ABS_X, x);
/*if(y != 0xFF)*/
/*input_report_abs(input_dev, ABS_Y, y);*/
input_sync(input_dev);
schedule_delayed_work(&cy8c4014_data->work, delay);
}
static ssize_t cy8c4014_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret = 0;
ret = sprintf(buf, "cy8c4014 sensor Auto Enable = %d\n",
cy8c4014_data->enable);
return ret;
}
static int cy8c4014_sensor_enable(void)
{
int ret = 0;
unsigned long delay = msecs_to_jiffies(cy8c4014_data->delay);
if (cy8c4014_data->enable == 1)
return ret;
mutex_lock(&cy8c4014_data->mutex);
schedule_delayed_work(&cy8c4014_data->work, delay);
cy8c4014_data->enable = 1;
mutex_unlock(&cy8c4014_data->mutex);
return ret;
}
static int cy8c4014_sensor_disable(void)
{
int ret = 0;
if (cy8c4014_data->enable == 0)
return ret;
mutex_lock(&cy8c4014_data->mutex);
cancel_delayed_work(&cy8c4014_data->work);
cy8c4014_data->enable = 0;
mutex_unlock(&cy8c4014_data->mutex);
return ret;
}
static ssize_t cy8c4014_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ls_auto, ret;
ls_auto = -1;
ret = kstrtoint(buf, 10, &ls_auto);
if (ret == -1)
return -EINVAL;
if (ls_auto != 0 && ls_auto != 1)
return -EINVAL;
if (ls_auto)
cy8c4014_sensor_enable();
else
cy8c4014_sensor_disable();
return count;
}
static ssize_t cy8c4014_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret = 0;
ret = sprintf(buf, "cy8c4014 sensor Poll Delay = %d ms\n",
cy8c4014_data->delay);
return ret;
}
static ssize_t cy8c4014_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int new_delay, ret;
ret = kstrtoint(buf, 10, &new_delay);
if (ret == -1)
return -EINVAL;
pr_info("new delay = %d ms, old delay = %d ms\n", new_delay,
cy8c4014_data->delay);
cy8c4014_data->delay = new_delay;
if (cy8c4014_data->enable) {
cy8c4014_sensor_disable();
cy8c4014_sensor_enable();
}
return count;
}
static struct device_attribute dev_attr_cy8c4014_enable =
__ATTR(enable, 0664, cy8c4014_enable_show,
cy8c4014_enable_store);
static struct device_attribute dev_attr_cy8c4014_delay =
__ATTR(delay, 0664, cy8c4014_poll_delay_show,
cy8c4014_poll_delay_store);
static struct attribute *sensor_sysfs_attrs[] = {
&dev_attr_cy8c4014_enable.attr,
&dev_attr_cy8c4014_delay.attr,
NULL
};
static struct attribute_group cy8c4014_attribute_group = {
.attrs = sensor_sysfs_attrs,
};
static int cy8c4014_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct input_dev *idev;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
pr_info(" start cy8c4014 probe !!\n");
if (!i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)){
ret = -EIO;
return ret;
}
/* data memory allocation */
cy8c4014_data = kzalloc(sizeof(struct cy8c4014_data), GFP_KERNEL);
if (cy8c4014_data == NULL) {
ret = -ENOMEM;
return ret;
}
cy8c4014_data->i2c_client = client;
i2c_set_clientdata(client, cy8c4014_data);
cy8c4014_reset(cy8c4014_data->i2c_client);
INIT_DELAYED_WORK(&cy8c4014_data->work, cy8c4014_schedwork);
cy8c4014_data->delay = CY8C4014_SCHE_DELAY;
idev = input_allocate_device();
if (!idev) {
pr_alert("%s: cy8c4014 allocate input device failed.\n",
__func__);
goto kfree_exit;
}
idev->name = CY8C4014_DEV_NAME;
idev->id.bustype = BUS_I2C;
input_set_capability(idev, EV_ABS, ABS_X);
input_set_capability(idev, EV_ABS, ABS_Y);
input_set_abs_params(idev, ABS_X, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0);
cy8c4014_data->input_dev = idev;
input_set_drvdata(idev, cy8c4014_data);
ret = input_register_device(idev);
if (ret < 0) {
input_free_device(idev);
goto kfree_exit;
}
mutex_init(&cy8c4014_data->mutex);
/* register the attributes */
ret = sysfs_create_group(&idev->dev.kobj, &cy8c4014_attribute_group);
if (ret)
goto unregister_exit;
schedule_delayed_work(&cy8c4014_data->work, CY8C4014_SCHE_DELAY);
cy8c4014_data->enable = 1;
return ret;
unregister_exit:
input_unregister_device(idev);
input_free_device(idev);
kfree_exit:
kfree(cy8c4014_data);
return ret;
}
static int cy8c4014_remove(struct i2c_client *client)
{
i2c_unregister_device(cy8c4014_data->i2c_client);
cancel_delayed_work(&cy8c4014_data->work);
kfree(cy8c4014_data);
return 0;
}
static const struct i2c_device_id cy8c4014_id[] = {
{ CY8C4014_DEV_NAME, 0 },
{ }
};
static struct i2c_driver cy8c4014_driver = {
.driver = {
.name = CY8C4014_DEV_NAME,
.owner = THIS_MODULE,
},
.probe = cy8c4014_probe,
.remove = cy8c4014_remove,
.id_table = cy8c4014_id,
};
static int __init cy8c4014_init(void)
{
i2c_add_driver(&cy8c4014_driver);
return 0;
}
static void __exit cy8c4014_exit(void)
{
i2c_del_driver(&cy8c4014_driver);
}
module_init(cy8c4014_init);
module_exit(cy8c4014_exit);
MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("CYPRESS CY8C4014 driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);