1028 lines
23 KiB
C
1028 lines
23 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/* Himax Android Driver Sample Code for QCT platform
|
|
*
|
|
* Copyright (C) 2019 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"
|
|
|
|
int i2c_error_count;
|
|
|
|
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-input_dev\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ts->input_dev->name = "himax-touchscreen";
|
|
|
|
if (!ic_data->HX_PEN_FUNC)
|
|
goto skip_pen_operation;
|
|
|
|
ts->hx_pen_dev = input_allocate_device();
|
|
|
|
if (ts->hx_pen_dev == NULL) {
|
|
ret = -ENOMEM;
|
|
E("%s: Failed to allocate input device-hx_pen_dev\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ts->hx_pen_dev->name = "himax-pen";
|
|
skip_pen_operation:
|
|
|
|
return ret;
|
|
}
|
|
int himax_input_register_device(struct input_dev *input_dev)
|
|
{
|
|
return input_register_device(input_dev);
|
|
}
|
|
|
|
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) {
|
|
I(" DT-No vk info in DT\n");
|
|
} else {
|
|
while ((pp = of_get_next_child(node, pp)))
|
|
cnt++;
|
|
|
|
if (!cnt)
|
|
return;
|
|
|
|
vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL);
|
|
if (vk == NULL) {
|
|
E("%s, Failed to allocate memory\n", __func__);
|
|
return;
|
|
}
|
|
pp = NULL;
|
|
|
|
while ((pp = of_get_next_child(node, pp))) {
|
|
if (of_property_read_u32(pp, "idx", &data) == 0)
|
|
vk[i].index = data;
|
|
|
|
if (of_property_read_u32_array(pp, "range", coords, 4)
|
|
== 0) {
|
|
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];
|
|
} else {
|
|
I(" range faile\n");
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
pdata->virtual_key = vk;
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", 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;
|
|
int ret = 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\n",
|
|
__func__, coords_size);
|
|
}
|
|
|
|
ret = of_property_read_u32_array(dt, "himax,panel-coords",
|
|
coords, coords_size);
|
|
if (ret == 0) {
|
|
pdata->abs_x_min = coords[0];
|
|
pdata->abs_x_max = (coords[1] - 1);
|
|
pdata->abs_y_min = coords[2];
|
|
pdata->abs_y_max = (coords[3] - 1);
|
|
I(" 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\n",
|
|
__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];
|
|
I(" DT-%s:display-coords = (%d, %d)\n", __func__,
|
|
pdata->screenWidth,
|
|
pdata->screenHeight);
|
|
pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0);
|
|
|
|
if (!gpio_is_valid(pdata->gpio_irq))
|
|
I(" DT:gpio_irq value is not valid\n");
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
pdata->gpio_reset = 262;
|
|
#else
|
|
pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0);
|
|
#endif
|
|
|
|
if (!gpio_is_valid(pdata->gpio_reset))
|
|
I(" DT:gpio_rst value is not valid\n");
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
pdata->gpio_pon = of_get_named_gpio(dt, "himax,pon-gpio", 0);
|
|
|
|
if (!gpio_is_valid(pdata->gpio_pon))
|
|
I(" DT:gpio_pon value is not valid\n");
|
|
|
|
|
|
I(" DT:pdata->gpio_pon=%d\n", pdata->gpio_pon);
|
|
|
|
#endif
|
|
|
|
pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0);
|
|
|
|
if (!gpio_is_valid(pdata->gpio_3v3_en))
|
|
I(" DT:gpio_3v3_en value is not valid\n");
|
|
|
|
I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n",
|
|
pdata->gpio_irq,
|
|
pdata->gpio_reset,
|
|
pdata->gpio_3v3_en);
|
|
|
|
if (of_property_read_u32(dt, "report_type", &data) == 0) {
|
|
pdata->protocol_type = data;
|
|
I(" DT:protocol_type=%d\n", pdata->protocol_type);
|
|
}
|
|
|
|
himax_vk_parser(dt, pdata);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(himax_parse_dt);
|
|
|
|
int himax_bus_read(uint8_t command, uint8_t *data,
|
|
uint32_t length, uint8_t toRetry)
|
|
{
|
|
int retry;
|
|
struct i2c_client *client = private_ts->client;
|
|
struct i2c_msg msg[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = 1,
|
|
.buf = &command,
|
|
},
|
|
{
|
|
.addr = client->addr,
|
|
.flags = I2C_M_RD,
|
|
.len = length,
|
|
.buf = data,
|
|
}
|
|
};
|
|
mutex_lock(&private_ts->rw_lock);
|
|
|
|
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(&private_ts->rw_lock);
|
|
return -EIO;
|
|
}
|
|
|
|
mutex_unlock(&private_ts->rw_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(himax_bus_read);
|
|
|
|
int himax_bus_write(uint8_t command, uint8_t *data,
|
|
uint32_t length, uint8_t toRetry)
|
|
{
|
|
int retry/*, loop_i*/;
|
|
uint8_t buf[length + 1];
|
|
struct i2c_client *client = private_ts->client;
|
|
struct i2c_msg msg[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = length + 1,
|
|
.buf = buf,
|
|
}
|
|
};
|
|
|
|
|
|
mutex_lock(&private_ts->rw_lock);
|
|
buf[0] = command;
|
|
memcpy(buf + 1, 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(&private_ts->rw_lock);
|
|
return -EIO;
|
|
}
|
|
|
|
mutex_unlock(&private_ts->rw_lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(himax_bus_write);
|
|
|
|
int himax_bus_write_command(uint8_t command, uint8_t toRetry)
|
|
{
|
|
return himax_bus_write(command, NULL, 0, toRetry);
|
|
}
|
|
|
|
void himax_int_enable(int enable)
|
|
{
|
|
struct himax_ts_data *ts = private_ts;
|
|
unsigned long irqflags = 0;
|
|
int irqnum = ts->client->irq;
|
|
|
|
spin_lock_irqsave(&ts->irq_lock, irqflags);
|
|
I("%s: Entering!\n", __func__);
|
|
if (enable == 1 && atomic_read(&ts->irq_state) == 0) {
|
|
atomic_set(&ts->irq_state, 1);
|
|
enable_irq(irqnum);
|
|
private_ts->irq_enabled = 1;
|
|
} else if (enable == 0 && atomic_read(&ts->irq_state) == 1) {
|
|
atomic_set(&ts->irq_state, 0);
|
|
disable_irq_nosync(irqnum);
|
|
private_ts->irq_enabled = 0;
|
|
}
|
|
|
|
I("enable = %d\n", enable);
|
|
spin_unlock_irqrestore(&ts->irq_lock, irqflags);
|
|
}
|
|
EXPORT_SYMBOL(himax_int_enable);
|
|
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
void himax_rst_gpio_set(int pinnum, uint8_t value)
|
|
{
|
|
gpio_direction_output(pinnum, value);
|
|
}
|
|
EXPORT_SYMBOL(himax_rst_gpio_set);
|
|
#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_dig);
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata)
|
|
{
|
|
I("%s: entered.\n", __func__);
|
|
|
|
if (!IS_ERR(pdata->vcc_ana))
|
|
regulator_put(pdata->vcc_ana);
|
|
|
|
if (!IS_ERR(pdata->vcc_dig))
|
|
regulator_put(pdata->vcc_dig);
|
|
|
|
I("%s: regulator put, completed.\n", __func__);
|
|
};
|
|
|
|
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);*/
|
|
usleep_range(1000, 1001);
|
|
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 intialize hardware\n");
|
|
goto err_regulator_not_on;
|
|
}
|
|
|
|
#if defined(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_power_on;
|
|
}
|
|
|
|
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_req_irq_gpio;
|
|
}
|
|
|
|
error = gpio_direction_input(pdata->gpio_irq);
|
|
|
|
if (error) {
|
|
E("unable to set direction for gpio [%d]\n",
|
|
pdata->gpio_irq);
|
|
goto err_set_gpio_irq;
|
|
}
|
|
|
|
client->irq = gpio_to_irq(pdata->gpio_irq);
|
|
private_ts->hx_irq = client->irq;
|
|
} else {
|
|
E("irq gpio not provided\n");
|
|
goto err_req_irq_gpio;
|
|
}
|
|
|
|
/*msleep(20);*/
|
|
usleep_range(2000, 2001);
|
|
#if defined(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_set_gpio_irq;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
return 0;
|
|
err_set_gpio_irq:
|
|
|
|
if (gpio_is_valid(pdata->gpio_irq))
|
|
gpio_free(pdata->gpio_irq);
|
|
|
|
err_req_irq_gpio:
|
|
himax_power_on(pdata, false);
|
|
err_power_on:
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
err_gpio_reset_req:
|
|
if (gpio_is_valid(pdata->gpio_reset))
|
|
gpio_free(pdata->gpio_reset);
|
|
|
|
err_regulator_on:
|
|
#endif
|
|
himax_regulator_deinit(pdata);
|
|
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;
|
|
#if defined(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__);
|
|
goto err_gpio_reset_req;
|
|
}
|
|
|
|
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_dir;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
if (gpio_is_valid(pdata->gpio_pon)) {
|
|
error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio");
|
|
|
|
if (error) {
|
|
E("unable to request scl gpio [%d]\n", pdata->gpio_pon);
|
|
goto err_gpio_pon_req;
|
|
}
|
|
|
|
error = gpio_direction_output(pdata->gpio_pon, 0);
|
|
|
|
I("gpio_pon LOW [%d]\n", pdata->gpio_pon);
|
|
|
|
if (error) {
|
|
E("unable to set direction for pon gpio [%d]\n",
|
|
pdata->gpio_pon);
|
|
goto err_gpio_pon_dir;
|
|
}
|
|
}
|
|
#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__);
|
|
goto err_gpio_3v3_req;
|
|
}
|
|
|
|
gpio_direction_output(pdata->gpio_3v3_en, 1);
|
|
I("3v3_en set 1 get 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);
|
|
goto err_gpio_irq_req;
|
|
}
|
|
|
|
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_set_input;
|
|
}
|
|
|
|
client->irq = gpio_to_irq(pdata->gpio_irq);
|
|
private_ts->hx_irq = client->irq;
|
|
} else {
|
|
E("irq gpio not provided\n");
|
|
goto err_gpio_irq_req;
|
|
}
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
msleep(20);
|
|
#else
|
|
usleep_range(2000, 2001);
|
|
#endif
|
|
|
|
#if defined(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);
|
|
goto err_gpio_reset_set_high;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
msleep(800);
|
|
|
|
if (gpio_is_valid(pdata->gpio_pon)) {
|
|
|
|
error = gpio_direction_output(pdata->gpio_pon, 1);
|
|
|
|
I("gpio_pon HIGH [%d]\n", pdata->gpio_pon);
|
|
|
|
if (error) {
|
|
E("gpio_pon unable to set direction for gpio [%d]\n",
|
|
pdata->gpio_pon);
|
|
goto err_gpio_pon_set_high;
|
|
}
|
|
}
|
|
#endif
|
|
return error;
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
err_gpio_pon_set_high:
|
|
#endif
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
err_gpio_reset_set_high:
|
|
#endif
|
|
err_gpio_irq_set_input:
|
|
if (gpio_is_valid(pdata->gpio_irq))
|
|
gpio_free(pdata->gpio_irq);
|
|
err_gpio_irq_req:
|
|
if (pdata->gpio_3v3_en >= 0)
|
|
gpio_free(pdata->gpio_3v3_en);
|
|
err_gpio_3v3_req:
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
err_gpio_pon_dir:
|
|
if (gpio_is_valid(pdata->gpio_pon))
|
|
gpio_free(pdata->gpio_pon);
|
|
err_gpio_pon_req:
|
|
#endif
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
err_gpio_reset_dir:
|
|
if (pdata->gpio_reset >= 0)
|
|
gpio_free(pdata->gpio_reset);
|
|
err_gpio_reset_req:
|
|
#endif
|
|
return error;
|
|
}
|
|
|
|
#endif
|
|
|
|
void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata)
|
|
{
|
|
if (gpio_is_valid(pdata->gpio_irq))
|
|
gpio_free(pdata->gpio_irq);
|
|
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
if (gpio_is_valid(pdata->gpio_reset))
|
|
gpio_free(pdata->gpio_reset);
|
|
#endif
|
|
|
|
#if defined(CONFIG_HMX_DB)
|
|
himax_power_on(pdata, false);
|
|
himax_regulator_deinit(pdata);
|
|
#else
|
|
if (pdata->gpio_3v3_en >= 0)
|
|
gpio_free(pdata->gpio_3v3_en);
|
|
|
|
#if defined(HX_PON_PIN_SUPPORT)
|
|
if (gpio_is_valid(pdata->gpio_pon))
|
|
gpio_free(pdata->gpio_pon);
|
|
#endif
|
|
|
|
#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 = 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) {
|
|
I("%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 {
|
|
I("%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 && private_ts->hx_irq) {/*INT mode*/
|
|
ts->use_irq = 1;
|
|
ret = himax_int_register_trigger();
|
|
|
|
if (ret == 0) {
|
|
ts->irq_enabled = 1;
|
|
atomic_set(&ts->irq_state, 1);
|
|
I("%s: irq enabled at gpio: %d\n", __func__,
|
|
client->irq);
|
|
#if defined(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;
|
|
}
|
|
|
|
int himax_ts_unregister_interrupt(void)
|
|
{
|
|
struct himax_ts_data *ts = private_ts;
|
|
int ret = 0;
|
|
|
|
|
|
I("%s: entered.\n", __func__);
|
|
|
|
/* Work functon */
|
|
if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/
|
|
#if defined(HX_SMART_WAKEUP)
|
|
irq_set_irq_wake(ts->hx_irq, 0);
|
|
#endif
|
|
free_irq(ts->hx_irq, ts);
|
|
I("%s: irq disabled at qpio: %d\n", __func__,
|
|
private_ts->hx_irq);
|
|
}
|
|
|
|
/*if use polling mode need to disable HX_ESD_RECOVERY function*/
|
|
if (!ts->use_irq) {
|
|
hrtimer_cancel(&ts->timer);
|
|
cancel_work_sync(&ts->work);
|
|
if (ts->himax_wq != NULL)
|
|
destroy_workqueue(ts->himax_wq);
|
|
I("%s: polling mode destroyed", __func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int himax_common_suspend(struct device *dev)
|
|
{
|
|
struct himax_ts_data *ts = dev_get_drvdata(dev);
|
|
|
|
I("%s: enter\n", __func__);
|
|
#if defined(HX_CONFIG_DRM) && !defined(HX_CONFIG_FB)
|
|
if (!ts->initialized)
|
|
return -ECANCELED;
|
|
#endif
|
|
himax_chip_common_suspend(ts);
|
|
return 0;
|
|
}
|
|
|
|
static int himax_common_resume(struct device *dev)
|
|
{
|
|
struct himax_ts_data *ts = dev_get_drvdata(dev);
|
|
|
|
I("%s: enter\n", __func__);
|
|
#if defined(HX_CONFIG_DRM) && !defined(HX_CONFIG_FB)
|
|
/*
|
|
* wait until device resume for TDDI
|
|
* TDDI: Touch and display Driver IC
|
|
*/
|
|
if (!ts->initialized)
|
|
if (himax_chip_common_init())
|
|
return -ECANCELED;
|
|
#endif
|
|
himax_chip_common_resume(ts);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(HX_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);
|
|
|
|
|
|
I(" %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;
|
|
}
|
|
#elif defined(HX_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;
|
|
}
|
|
#endif
|
|
|
|
int himax_chip_common_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret = 0;
|
|
struct himax_ts_data *ts;
|
|
|
|
I("%s:Enter\n", __func__);
|
|
|
|
/* Check I2C functionality */
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
E("%s: i2c check functionality error\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
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->initialized = false;
|
|
ret = himax_chip_common_init();
|
|
if (ret < 0)
|
|
goto err_common_init_failed;
|
|
|
|
return ret;
|
|
|
|
err_common_init_failed:
|
|
kfree(ts);
|
|
err_alloc_data_failed:
|
|
|
|
return ret;
|
|
}
|
|
|
|
int himax_chip_common_remove(struct i2c_client *client)
|
|
{
|
|
if (g_hx_chip_inited)
|
|
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(HX_CONFIG_FB)) && (!defined(HX_CONFIG_DRM))
|
|
.suspend = himax_common_suspend,
|
|
.resume = himax_common_resume,
|
|
#endif
|
|
};
|
|
|
|
#if defined(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,
|
|
#if defined(CONFIG_PM)
|
|
.pm = &himax_common_pm_ops,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int __init himax_common_init(void)
|
|
{
|
|
I("Himax common touch panel driver init\n");
|
|
D("Himax check double loading\n");
|
|
if (g_mmi_refcnt++ > 0) {
|
|
|
|
I("Himax driver has been loaded! ignoring....\n");
|
|
|
|
return 0;
|
|
}
|
|
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");
|