blob: 9a6c60521670039c16a97852e3da138350d203ae [file] [log] [blame]
/*
* Himax Android Driver Sample Code for QCT platform
*
* Copyright (C) 2018 Himax Corporation.
*
* 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.
*
*/
#include "himax_platform.h"
#include "himax_common.h"
#include "himax_ic_core.h"
#include "linux/moduleparam.h"
int i2c_error_count;
int irq_enable_count;
active_tp_setup(himax);
int himax_dev_set(struct himax_ts_data *ts)
{
int ret = 0;
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL) {
ret = -ENOMEM;
E("%s: Failed to allocate input device\n", __func__);
return ret;
}
ts->input_dev->name = "himax-touchscreen";
return ret;
}
int himax_input_register_device(struct input_dev *input_dev)
{
return input_register_device(input_dev);
}
#if defined(HX_PLATFOME_DEFINE_KEY)
void himax_platform_key(void)
{
I("Nothing to be done! Plz cancel it!\n");
}
#endif
void himax_vk_parser(struct device_node *dt,
struct himax_i2c_platform_data *pdata)
{
u32 data = 0;
uint8_t cnt = 0, i = 0;
uint32_t coords[4] = {0};
struct device_node *node, *pp = NULL;
struct himax_virtual_key *vk;
node = of_parse_phandle(dt, "virtualkey", 0);
if (node == NULL) {
D(" DT-No vk info in DT");
return;
}
cnt = of_get_child_count(node);
if (!cnt) {
of_node_put(node);
return;
}
vk = kcalloc(cnt, sizeof(*vk), GFP_KERNEL);
if (!vk) {
E(" %s: allocate memory failed!", __func__);
of_node_put(node);
return;
}
for_each_child_of_node(node, pp) {
if (of_property_read_u32(pp, "idx", &data) != 0)
continue;
vk[i].index = data;
if (of_property_read_u32_array(pp, "range", coords, 4) != 0)
continue;
vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1];
vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3];
i++;
}
pdata->virtual_key = vk;
of_node_put(node);
for (i = 0; i < cnt; i++)
I(" vk[%d] idx:%d x_min:%d, y_max:%d", i, pdata->virtual_key[i].index,
pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max);
}
int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata)
{
int rc, coords_size = 0;
uint32_t coords[4] = {0};
struct property *prop;
struct device_node *dt = private_ts->client->dev.of_node;
u32 data = 0;
prop = of_find_property(dt, "himax,panel-coords", NULL);
if (prop) {
coords_size = prop->length / sizeof(u32);
if (coords_size != 4)
D(" %s:Invalid panel coords size %d", __func__, coords_size);
}
if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) {
pdata->abs_x_min = coords[0], pdata->abs_x_max = coords[1];
pdata->abs_y_min = coords[2], pdata->abs_y_max = coords[3];
D(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__,
pdata->abs_x_min, pdata->abs_x_max,
pdata->abs_y_min, pdata->abs_y_max);
}
prop = of_find_property(dt, "himax,display-coords", NULL);
if (prop) {
coords_size = prop->length / sizeof(u32);
if (coords_size != 4)
D(" %s:Invalid display coords size %d", __func__, coords_size);
}
rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size);
if (rc && (rc != -EINVAL)) {
D(" %s:Fail to read display-coords %d\n", __func__, rc);
return rc;
}
pdata->screenWidth = coords[1];
pdata->screenHeight = coords[3];
D(" DT-%s:display-coords = (%d, %d)", __func__, pdata->screenWidth,
pdata->screenHeight);
pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0);
if (!gpio_is_valid(pdata->gpio_irq))
E(" DT:gpio_irq value is not valid\n");
pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0);
if (!gpio_is_valid(pdata->gpio_reset))
E(" DT:gpio_rst value is not valid\n");
pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0);
if (!gpio_is_valid(pdata->gpio_3v3_en))
D(" DT:gpio_3v3_en value is not valid\n");
D(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d",
pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en);
if (of_property_read_u32(dt, "himax,report_type", &data) == 0) {
pdata->protocol_type = data;
I(" DT:protocol_type=%d", pdata->protocol_type);
}
himax_vk_parser(dt, pdata);
return 0;
}
int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry)
{
int retry;
bool reallocate = false;
struct himax_ts_data *ts = private_ts;
uint8_t *buf = ts->i2c_data;
struct i2c_client *client = ts->client;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = HX_CMD_BYTE,
.buf = buf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = buf + HX_CMD_BYTE,
}
};
if (length > FLASH_RW_MAX_LEN) {
W("%s: data length too large %d!\n", __func__, length);
buf = kmalloc(length + HX_CMD_BYTE, GFP_KERNEL);
if (!buf) {
E("%s: failed realloc buf %d\n", __func__,
length + HX_CMD_BYTE);
return -EIO;
}
reallocate = true;
msg[0].buf = buf;
msg[1].buf = buf + HX_CMD_BYTE;
}
mutex_lock(&ts->rw_lock);
buf[0] = command;
for (retry = 0; retry < toRetry; retry++) {
if (i2c_transfer(client->adapter, msg, 2) == 2)
break;
msleep(20);
}
if (retry == toRetry) {
E("%s: i2c_read_block retry over %d\n", __func__, toRetry);
i2c_error_count = toRetry;
mutex_unlock(&ts->rw_lock);
return -EIO;
}
memcpy(data, buf + HX_CMD_BYTE, length);
mutex_unlock(&ts->rw_lock);
if (reallocate)
kfree(buf);
return 0;
}
int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry)
{
int retry;
bool reallocate = false;
struct himax_ts_data *ts = private_ts;
uint8_t *buf = ts->i2c_data;
struct i2c_client *client = ts->client;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = length + HX_CMD_BYTE,
.buf = buf,
}
};
if (length > FLASH_RW_MAX_LEN) {
W("%s: data length too large %d!\n", __func__, length);
buf = kmalloc(length + HX_CMD_BYTE, GFP_KERNEL);
if (!buf) {
E("%s: failed realloc buf %d\n", __func__,
length + HX_CMD_BYTE);
return -EIO;
}
reallocate = true;
msg[0].buf = buf;
}
mutex_lock(&ts->rw_lock);
buf[0] = command;
memcpy(buf + HX_CMD_BYTE, data, length);
for (retry = 0; retry < toRetry; retry++) {
if (i2c_transfer(client->adapter, msg, 1) == 1)
break;
msleep(20);
}
if (retry == toRetry) {
E("%s: i2c_write_block retry over %d\n", __func__, toRetry);
i2c_error_count = toRetry;
mutex_unlock(&ts->rw_lock);
return -EIO;
}
mutex_unlock(&ts->rw_lock);
if (reallocate)
kfree(buf);
return 0;
}
int himax_bus_write_command(uint8_t command, uint8_t toRetry)
{
return himax_bus_write(command, NULL, 0, toRetry);
}
int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry)
{
int retry;
bool reallocate = false;
struct himax_ts_data *ts = private_ts;
uint8_t *buf = ts->i2c_data;
struct i2c_client *client = private_ts->client;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = length,
.buf = buf,
}
};
if (length > FLASH_RW_MAX_LEN) {
W("%s: data length too large %d!\n", __func__, length);
buf = kmalloc(length, GFP_KERNEL);
if (!buf) {
E("%s: failed realloc buf %d\n", __func__, length);
return -EIO;
}
reallocate = true;
msg[0].buf = buf;
}
mutex_lock(&ts->rw_lock);
memcpy(buf, data, length);
for (retry = 0; retry < toRetry; retry++) {
if (i2c_transfer(client->adapter, msg, 1) == 1)
break;
msleep(20);
}
if (retry == toRetry) {
E("%s: i2c_write_block retry over %d\n", __func__, toRetry);
i2c_error_count = toRetry;
mutex_unlock(&ts->rw_lock);
return -EIO;
}
mutex_unlock(&ts->rw_lock);
if (reallocate)
kfree(buf);
return 0;
}
void himax_int_enable(int enable)
{
int irqnum = 0;
irqnum = private_ts->client->irq;
if (enable == 1 && irq_enable_count == 0) {
enable_irq(irqnum);
irq_enable_count++;
private_ts->irq_enabled = 1;
} else if (enable == 0 && irq_enable_count == 1) {
disable_irq_nosync(irqnum);
irq_enable_count--;
private_ts->irq_enabled = 0;
}
D("irq_enable_count = %d", irq_enable_count);
}
#ifdef HX_RST_PIN_FUNC
void himax_rst_gpio_set(int pinnum, uint8_t value)
{
gpio_direction_output(pinnum, value);
}
#endif
uint8_t himax_int_gpio_read(int pinnum)
{
return gpio_get_value(pinnum);
}
#if defined(CONFIG_HMX_DB)
static int himax_regulator_configure(struct himax_i2c_platform_data *pdata)
{
int retval;
struct i2c_client *client = private_ts->client;
pdata->vcc_dig = regulator_get(&client->dev, "vdd");
if (IS_ERR(pdata->vcc_dig)) {
E("%s: Failed to get regulator vdd\n", __func__);
retval = PTR_ERR(pdata->vcc_dig);
return retval;
}
pdata->vcc_ana = regulator_get(&client->dev, "avdd");
if (IS_ERR(pdata->vcc_ana)) {
E("%s: Failed to get regulator avdd\n", __func__);
retval = PTR_ERR(pdata->vcc_ana);
regulator_put(pdata->vcc_ana);
return retval;
}
return 0;
};
static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on)
{
int retval;
if (on) {
retval = regulator_enable(pdata->vcc_dig);
if (retval) {
E("%s: Failed to enable regulator vdd\n", __func__);
return retval;
}
msleep(100);
retval = regulator_enable(pdata->vcc_ana);
if (retval) {
E("%s: Failed to enable regulator avdd\n", __func__);
regulator_disable(pdata->vcc_dig);
return retval;
}
} else {
regulator_disable(pdata->vcc_dig);
regulator_disable(pdata->vcc_ana);
}
return 0;
}
int himax_gpio_power_config(struct himax_i2c_platform_data *pdata)
{
int error;
struct i2c_client *client = private_ts->client;
error = himax_regulator_configure(pdata);
if (error) {
E("Failed to initialize hardware\n");
goto err_regulator_not_on;
}
#ifdef HX_RST_PIN_FUNC
if (gpio_is_valid(pdata->gpio_reset)) {
/* configure touchscreen reset out gpio */
error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio");
if (error) {
E("unable to request gpio [%d]\n", pdata->gpio_reset);
goto err_regulator_on;
}
error = gpio_direction_output(pdata->gpio_reset, 0);
if (error) {
E("unable to set direction for gpio [%d]\n", pdata->gpio_reset);
goto err_gpio_reset_req;
}
}
#endif
error = himax_power_on(pdata, true);
if (error) {
E("Failed to power on hardware\n");
goto err_gpio_reset_req;
}
if (gpio_is_valid(pdata->gpio_irq)) {
/* configure touchscreen irq gpio */
error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq");
if (error) {
E("unable to request gpio [%d]\n", pdata->gpio_irq);
goto err_power_on;
}
error = gpio_direction_input(pdata->gpio_irq);
if (error) {
E("unable to set direction for gpio [%d]\n", pdata->gpio_irq);
goto err_gpio_irq_req;
}
client->irq = gpio_to_irq(pdata->gpio_irq);
} else {
E("irq gpio not provided\n");
goto err_power_on;
}
msleep(20);
#ifdef HX_RST_PIN_FUNC
if (gpio_is_valid(pdata->gpio_reset)) {
error = gpio_direction_output(pdata->gpio_reset, 1);
if (error) {
E("unable to set direction for gpio [%d]\n",
pdata->gpio_reset);
goto err_gpio_irq_req;
}
}
#endif
return 0;
err_gpio_irq_req:
if (gpio_is_valid(pdata->gpio_irq))
gpio_free(pdata->gpio_irq);
err_power_on:
himax_power_on(pdata, false);
err_gpio_reset_req:
#ifdef HX_RST_PIN_FUNC
if (gpio_is_valid(pdata->gpio_reset))
gpio_free(pdata->gpio_reset);
err_regulator_on:
#endif
err_regulator_not_on:
return error;
}
#else
int himax_gpio_power_config(struct himax_i2c_platform_data *pdata)
{
int error = 0;
struct i2c_client *client = private_ts->client;
#ifdef HX_RST_PIN_FUNC
if (pdata->gpio_reset >= 0) {
error = gpio_request(pdata->gpio_reset, "himax-reset");
if (error < 0) {
E("%s: request reset pin failed\n", __func__);
return error;
}
error = gpio_direction_output(pdata->gpio_reset, 0);
if (error) {
E("unable to set direction for gpio [%d]\n", pdata->gpio_reset);
return error;
}
}
#endif
if (pdata->gpio_3v3_en >= 0) {
error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en");
if (error < 0) {
E("%s: request 3v3_en pin failed\n", __func__);
return error;
}
gpio_direction_output(pdata->gpio_3v3_en, 1);
I("3v3_en pin =%d\n", gpio_get_value(pdata->gpio_3v3_en));
}
if (gpio_is_valid(pdata->gpio_irq)) {
/* configure touchscreen irq gpio */
error = gpio_request(pdata->gpio_irq, "himax_gpio_irq");
if (error) {
E("unable to request gpio [%d]\n", pdata->gpio_irq);
return error;
}
error = gpio_direction_input(pdata->gpio_irq);
if (error) {
E("unable to set direction for gpio [%d]\n", pdata->gpio_irq);
return error;
}
client->irq = gpio_to_irq(pdata->gpio_irq);
} else {
E("irq gpio not provided\n");
return error;
}
msleep(20);
#ifdef HX_RST_PIN_FUNC
if (pdata->gpio_reset >= 0) {
error = gpio_direction_output(pdata->gpio_reset, 1);
if (error) {
E("unable to set direction for gpio [%d]\n", pdata->gpio_reset);
return error;
}
}
#endif
return error;
}
#endif
static void himax_ts_isr_func(struct himax_ts_data *ts)
{
himax_ts_work(ts);
}
irqreturn_t himax_ts_thread(int irq, void *ptr)
{
himax_ts_isr_func((struct himax_ts_data *)ptr);
return IRQ_HANDLED;
}
static void himax_ts_work_func(struct work_struct *work)
{
struct himax_ts_data *ts;
ts = container_of(work, struct himax_ts_data, work);
himax_ts_work(ts);
}
int himax_int_register_trigger(void)
{
int ret = 0;
struct himax_ts_data *ts = private_ts;
struct i2c_client *client = private_ts->client;
if (ic_data->HX_INT_IS_EDGE) {
D("%s edge triiger falling\n ", __func__);
ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts);
} else {
D("%s level trigger low\n ", __func__);
ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts);
}
return ret;
}
int himax_int_en_set(void)
{
int ret = NO_ERR;
ret = himax_int_register_trigger();
return ret;
}
int himax_ts_register_interrupt(void)
{
struct himax_ts_data *ts = private_ts;
struct i2c_client *client = private_ts->client;
int ret = 0;
ts->irq_enabled = 0;
/* Work functon */
if (client->irq) {/* INT mode */
ts->use_irq = 1;
ret = himax_int_register_trigger();
if (ret == 0) {
ts->irq_enabled = 1;
irq_enable_count = 1;
D("%s: irq enabled at qpio: %d\n",
__func__, client->irq);
#ifdef HX_SMART_WAKEUP
irq_set_irq_wake(client->irq, 1);
#endif
} else {
ts->use_irq = 0;
E("%s: request_irq failed\n", __func__);
}
} else
I("%s: client->irq is empty, use polling mode.\n", __func__);
/* if use polling mode need to disable HX_ESD_RECOVERY function */
if (!ts->use_irq) {
ts->himax_wq = create_singlethread_workqueue("himax_touch");
INIT_WORK(&ts->work, himax_ts_work_func);
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = himax_ts_timer_func;
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
I("%s: polling mode enabled\n", __func__);
}
return ret;
}
static int himax_common_suspend(struct device *dev)
{
struct himax_ts_data *ts = dev_get_drvdata(dev);
D("%s: enter\n", __func__);
if (!ts->initialized)
return -ECANCELED;
himax_chip_common_suspend(ts);
return 0;
}
static int himax_common_resume(struct device *dev)
{
struct himax_ts_data *ts = dev_get_drvdata(dev);
D("%s: enter\n", __func__);
if (!ts->initialized) {
/*
* wait until device resume for TDDI
* TDDI: Touch and display Driver IC
*/
if (himax_chip_common_init())
return -ECANCELED;
ts->initialized = true;
}
himax_chip_common_resume(ts);
return 0;
}
#if defined(CONFIG_DRM)
int drm_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct msm_drm_notifier *evdata = data;
int *blank;
struct himax_ts_data *ts =
container_of(self, struct himax_ts_data, fb_notif);
if (!evdata || (evdata->id != 0))
return 0;
D("DRM %s\n", __func__);
if (evdata->data && event == MSM_DRM_EARLY_EVENT_BLANK && ts &&
ts->client) {
blank = evdata->data;
switch (*blank) {
case MSM_DRM_BLANK_POWERDOWN:
if (!ts->initialized)
return -ECANCELED;
himax_common_suspend(&ts->client->dev);
break;
}
}
if (evdata->data && event == MSM_DRM_EVENT_BLANK && ts && ts->client) {
blank = evdata->data;
switch (*blank) {
case MSM_DRM_BLANK_UNBLANK:
himax_common_resume(&ts->client->dev);
break;
}
}
return 0;
}
#elif defined(CONFIG_FB)
int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int *blank;
struct himax_ts_data *ts =
container_of(self, struct himax_ts_data, fb_notif);
D("FB %s\n", __func__);
if (evdata && evdata->data && event == FB_EVENT_BLANK && ts &&
ts->client) {
blank = evdata->data;
switch (*blank) {
case FB_BLANK_UNBLANK:
himax_common_resume(&ts->client->dev);
break;
case FB_BLANK_POWERDOWN:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_NORMAL:
himax_common_suspend(&ts->client->dev);
break;
}
}
return 0;
}
#endif
int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct himax_ts_data *ts;
struct device_node *dt = client->dev.of_node;
struct himax_i2c_platform_data *pdata;
D("%s:Enter\n", __func__);
if (himax_check_assigned_tp(dt, "compatible",
"qcom,i2c-touch-active") < 0)
goto err_dt_not_match;
/* Check I2C functionality */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
E("%s: i2c check functionality error\n", __func__);
ret = -ENODEV;
goto err_check_functionality_failed;
}
ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL);
if (ts == NULL) {
E("%s: allocate himax_ts_data failed\n", __func__);
ret = -ENOMEM;
goto err_alloc_data_failed;
}
i2c_set_clientdata(client, ts);
ts->client = client;
ts->dev = &client->dev;
mutex_init(&ts->rw_lock);
private_ts = ts;
ts->i2c_data = kmalloc(FLASH_RW_MAX_LEN + HX_CMD_BYTE, GFP_KERNEL);
if (ts->i2c_data == NULL) {
E("%s: allocate i2c_data failed\n", __func__);
ret = -ENOMEM;
goto err_alloc_i2c_data;
}
/*
* ts chip initialization is deferred till FB_UNBLACK event;
* probe is considered pending till then.
*/
ts->initialized = false;
#if defined(CONFIG_FB) || defined(CONFIG_DRM)
ret = himax_fb_register(ts);
if (ret)
goto err_fb_notify_reg_failed;
#endif
#ifdef HX_AUTO_UPDATE_FW
ts->himax_update_wq =
create_singlethread_workqueue("HMX_update_request");
if (!ts->himax_update_wq) {
E(" allocate syn_update_wq failed\n");
goto err_fb_notify_reg_failed;
}
INIT_DELAYED_WORK(&ts->work_update, himax_update_register);
#endif
D("PDATA START\n");
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL) { /* Allocate Platform data space */
ret = -ENOMEM;
goto err_fb_notify_reg_failed;
}
if (himax_parse_dt(ts, pdata) < 0) {
E(" pdata is NULL for DT\n");
ret = -ECANCELED;
goto err_alloc_dt_pdata_failed;
}
if (pdata->virtual_key)
ts->button = pdata->virtual_key;
ts->pdata = pdata;
#ifdef CONFIG_OF
pdata->cable_config[0] = 0xF0;
pdata->cable_config[1] = 0x00;
ts->pdata->abs_pressure_min = 0;
ts->pdata->abs_pressure_max = 200;
ts->pdata->abs_width_min = 0;
ts->pdata->abs_width_max = 200;
#endif
ts->suspended = false;
#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL)
ts->usb_connected = 0x00;
ts->cable_config = pdata->cable_config;
#endif
#ifdef HX_PROTOCOL_A
ts->protocol_type = PROTOCOL_TYPE_A;
#else
ts->protocol_type = PROTOCOL_TYPE_B;
#endif
D("%s: Use Protocol Type %c\n", __func__,
ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B');
ret = himax_input_register(ts);
if (ret) {
E("%s: Unable to register %s input device\n",
__func__, ts->input_dev->name);
}
return ret;
err_alloc_dt_pdata_failed:
kfree(pdata);
err_fb_notify_reg_failed:
kfree(ts->i2c_data);
err_alloc_i2c_data:
kfree(ts);
err_alloc_data_failed:
err_check_functionality_failed:
err_dt_not_match:
return ret;
}
int himax_chip_common_remove(struct i2c_client *client)
{
himax_chip_common_deinit();
return 0;
}
static const struct i2c_device_id himax_common_ts_id[] = {
{HIMAX_common_NAME, 0 },
{}
};
static const struct dev_pm_ops himax_common_pm_ops = {
#if (!defined(CONFIG_FB) && !defined(CONFIG_DRM))
.suspend = himax_common_suspend,
.resume = himax_common_resume,
#endif
};
#ifdef CONFIG_OF
static const struct of_device_id himax_match_table[] = {
{.compatible = "himax,hxcommon" },
{},
};
#else
#define himax_match_table NULL
#endif
static struct i2c_driver himax_common_driver = {
.id_table = himax_common_ts_id,
.probe = himax_chip_common_probe,
.remove = himax_chip_common_remove,
.driver = {
.name = HIMAX_common_NAME,
.owner = THIS_MODULE,
.of_match_table = himax_match_table,
#ifdef CONFIG_PM
.pm = &himax_common_pm_ops,
#endif
},
};
static int __init himax_common_init(void)
{
D("Himax common touch panel driver init\n");
i2c_add_driver(&himax_common_driver);
return 0;
}
static void __exit himax_common_exit(void)
{
i2c_del_driver(&himax_common_driver);
}
module_init(himax_common_init);
module_exit(himax_common_exit);
MODULE_DESCRIPTION("Himax_common driver");
MODULE_LICENSE("GPL v2");