linuxOS_AP06/kernel/drivers/input/touchscreen/hxchipset/himax_platform.c
2025-06-03 12:28:32 +08:00

1198 lines
30 KiB
C

/* Himax Android Driver Sample Code for QCT platform
*
* Copyright (C) 2021 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.h"
#include "himax_platform.h"
#include "himax_common.h"
#include "himax_ic_core.h"
#if IS_ENABLED(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192)
DECLARE_HIMAX_CHIP(hx83192_chip, hx83192_chip_detect);
#endif
/* FIXME */
static int mmi_refcnt;
extern void notify_tp_procfile(const int exsited_flag);
static ssize_t himax_i2c_trc_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, loff_t off,
size_t bytes);
static BIN_ATTR(i2c_trace, 0444, himax_i2c_trc_read, NULL, sizeof_field(struct trc_lines, lines));
/*******************************************************************************
* Provides the data for the SysFS 'i2c_trace' binary file. This is the data
* sent and received via i2c. If a transfer fails, we cannot say here at
* which byte the failure occurred so this is not a replacement for a logic
* analyser.
*
* Returns number of bytes stored.
******************************************************************************/
static ssize_t himax_i2c_trc_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf, loff_t off,
size_t bytes)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct himax_ts_data *ts = dev_get_drvdata(dev);
struct trc_line *line;
int offset = 0;
/* mutex_lock(&ts->trc_lines.mutex);*/
if (ts->trc_lines.fill_status >= TRC_NUM_LINES) {
/* Some debug lines have been lost so say how many */
offset = scnprintf(buf, bytes, "Discarded %d lines\n",
ts->trc_lines.fill_status - TRC_NUM_LINES);
/* Now we've said how many lines have been lost, we only need
* to know the buffer is now completely full. */
ts->trc_lines.fill_status = TRC_NUM_LINES;
}
while ((ts->trc_lines.fill_status > 0) && (offset != bytes)) {
line = &ts->trc_lines.lines[ts->trc_lines.read_idx];
/* line_start = line->text + line->read_offset; */
/* Copy as much of the line as possible into the buffer
* (excluding terminating null.
*/
while ((offset != bytes) && (line->text[line->read_offset] != '\0')) {
buf[offset++] = line->text[line->read_offset++];
}
if (offset != bytes) {
/* There's still space in the buffer so move
* to the next line in the ring buffer */
ts->trc_lines.read_idx++;
ts->trc_lines.read_idx %= TRC_NUM_LINES;
ts->trc_lines.fill_status--;
}
}
/* mutex_unlock(&ts->trc_lines.mutex);*/
return offset;
}
/*******************************************************************************
* Converts binary data into ASCII hex. This is a helper function for
* himax_dbg_add.
*
* buf - the buffer to hold the ASCII hex.
* avail - the number of characters available in buf
* data - pointer to the binary data
* len - length of the binary data
* consumed - pointer (may be NULL) to a variable to hold the number of bytes of
* binary data that were consumed.
*
* Returns the number of ASCII characters written (not including terminating \0)
******************************************************************************/
static int himax_dbg_write_hex(char *buf, size_t avail, const u8 *data, size_t len, int *consumed)
{
int offset = 0;
int min_length = 0;
char buffer[5];
if (consumed)
*consumed = 0;
while ((len > 0) && (avail >= min_length)) {
min_length = scnprintf(buffer, avail, "%02x%s", *data++, --len > 0 ? " " : "");
/* Make sure there is enough space to store a full ASCII hex
* representation of the byte */
if (avail >= min_length) {
memcpy(buf + offset, buffer, min_length);
offset += min_length;
avail -= min_length;
if (consumed)
(*consumed)++;
}
}
return offset;
}
/*******************************************************************************
* Add a log message to the debug log.
*
* msg - A text message to store.
* ptr - pointer (may be NULL) to the binary data to convert to hex.
* data_len - length of the binary data
******************************************************************************/
static void himax_dbg_add(struct himax_ts_data *ts, const char *msg, const u8 *ptr, int data_len)
{
struct trc_line *line;
int length;
int consumed;
int offset;
int idx;
/* struct timespec ts_nsec;
getnstimeofday(&ts_nsec);*/
/* if (!ts->trc_lines.enabled)
return;*/
/* mutex_lock(&ts->trc_lines.mutex);*/
do {
offset = 0;
idx = ts->trc_lines.write_idx;
line = &ts->trc_lines.lines[idx];
line->read_offset = 0;
/*
offset = scnprintf(line->text,
TRC_TEXT_LENGTH,
"[%5llu.%06llu] ",
ts_nsec.tv_nsec / 1000000000,
(ts_nsec.tv_nsec % 1000000000) / 1000);
*/
length = scnprintf(line->text + offset, TRC_TEXT_LENGTH - offset, "%s", msg);
offset += length;
if (offset == TRC_TEXT_LENGTH)
/* We didn't copy all of the message */
msg += length;
else
/* Message was completely copied but prepare for having
* too much binary data for one line */
msg = "|->";
if (ptr) {
length = himax_dbg_write_hex(line->text + offset, TRC_TEXT_LENGTH - offset,
ptr, data_len, &consumed);
offset += length;
ptr += consumed;
data_len -= consumed;
}
/* Add the end-of-line string.
* We don't need to check for space because we've used
* TRC_TEXT_LENGTH so far which leaves space for the end-of-line
* string and terminating NUL.
*/
scnprintf(&line->text[offset], TRC_EOL_LENGTH, TRC_EOL);
ts->trc_lines.write_idx = (++idx) % TRC_NUM_LINES;
/* If we've filled the ring buffer and have wrapped round,
* move the read index forward so that it always points to the
* oldest unread entry that still exists. */
if (++ts->trc_lines.fill_status > TRC_NUM_LINES) {
ts->trc_lines.read_idx++;
ts->trc_lines.read_idx %= TRC_NUM_LINES;
}
} while (data_len > 0);
/* mutex_unlock(&ts->trc_lines.mutex);*/
}
/*******************************************************************************
* Creates the SysFS file entries.
******************************************************************************/
int himax_sysfs_init(struct himax_ts_data *ts)
{
struct i2c_client *client = ts->client;
int error;
/*
error = sysfs_create_group(&client->dev.kobj, &himax_attr_group);
if (error) {
dev_err(&client->dev,
"Failed to create sysfs sysfs group (%d)\n",
error);
return error;
}
*/
error = sysfs_create_bin_file(&client->dev.kobj, &bin_attr_i2c_trace);
/* This is only for debugging purposes so failing to create the
* file for whatever reason is not a critical error. */
if (error)
dev_warn(&client->dev, "Failed to create %s (%d)\n", bin_attr_i2c_trace.attr.name,
error);
return 0;
}
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;
}
if (ts->location) {
ts->input_dev->name = ts->location;
/* NOTE: To identify each display. */
ts->input_dev->phys =
devm_kasprintf(ts->dev, GFP_KERNEL, "himax-%s/input0", dev_name(ts->dev));
} else {
ts->input_dev->name = "himax-touchscreen";
}
return ret;
}
int himax_input_register_device(struct input_dev *input_dev)
{
return input_register_device(input_dev);
}
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 = ts->client->dev.of_node;
u32 data = 0;
int ret = 0;
const char *name;
struct pinctrl *p;
struct pinctrl_state *default_state;
of_property_read_string(dt, "himax,location", &ts->location);
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");
pdata->fail_det = of_get_named_gpio(dt, "himax,fail-det", 0);
if (!gpio_is_valid(pdata->fail_det))
I(" DT:fail_det value is not valid\n");
pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0);
if (!gpio_is_valid(pdata->gpio_reset))
I(" DT:gpio_rst value is not valid\n");
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");
pdata->lcm_rst = of_get_named_gpio(dt, "himax,lcm-rst", 0);
if (!gpio_is_valid(pdata->lcm_rst))
I(" DT:tp-rst value is not valid\n");
I(" DT:pdata->gpio_pon=%d, pdata->lcm_rst=%d\n", pdata->gpio_pon, pdata->lcm_rst);
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);
I(" DT:fail_det=%d", pdata->fail_det);
if (of_property_read_u32(dt, "report_type", &data) == 0) {
pdata->protocol_type = data;
I(" DT:protocol_type=%d\n", pdata->protocol_type);
}
if (of_property_read_string(dt, "himax,tp-fw-bin", &name) < 0) {
I("DT:fail to get himax tp fw bin\n");
} else {
pdata->fw_name = name;
I("DT:himax tp fw bin:%s\n", pdata->fw_name);
}
if (of_property_read_string(dt, "himax,hx-criteria", &name) < 0) {
I("DT:fail to get himax,hx-criteria file\n");
} else {
pdata->criteria_file_name = name;
I("DT:himax,hx-criteria:%s\n", pdata->criteria_file_name);
}
p = devm_pinctrl_get(&ts->client->dev);
default_state = pinctrl_lookup_state(p, "default");
pinctrl_select_state(p, default_state);
I("%s: active pinctrl!\n", __func__);
return 0;
}
EXPORT_SYMBOL(himax_parse_dt);
int himax_bus_read(struct himax_ts_data *ts, uint8_t command, uint8_t *data, uint32_t length,
uint8_t toRetry)
{
int retry;
char buf[50];
int i;
int ret = 0;
struct i2c_client *client = 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 = ts->rw_buf,
} };
mutex_lock(&ts->rw_lock);
for (retry = 0; retry < toRetry; retry++) {
ret = i2c_transfer(client->adapter, msg, 2);
if (ret == 2) {
memcpy(data, ts->rw_buf, length);
break;
}
msleep(20);
}
for (i = 0; i < ret; i++) {
himax_dbg_add(ts, msg[i].flags & I2C_M_RD ? "[R]" : "[W]", (u8 *)msg[i].buf,
msg[i].len);
}
scnprintf(buf, sizeof(buf), "Messages sent: %d/%d", ret, 2);
buf[sizeof(buf) - 1] = '\0';
himax_dbg_add(ts, buf, 0, 0);
if (retry == toRetry) {
E("%s: i2c_read_block retry over %d\n", __func__, toRetry);
mutex_unlock(&ts->rw_lock);
return -EIO;
}
mutex_unlock(&ts->rw_lock);
return 0;
}
EXPORT_SYMBOL(himax_bus_read);
int himax_bus_write(struct himax_ts_data *ts, uint8_t command, uint8_t *data, uint32_t length,
uint8_t toRetry)
{
int retry;
char buf[50];
int i;
int ret = 0;
struct i2c_client *client = ts->client;
struct i2c_msg msg[] = { {
.addr = client->addr,
.flags = 0,
.len = length + 1,
.buf = ts->rw_buf,
} };
mutex_lock(&ts->rw_lock);
ts->rw_buf[0] = command;
if (data != NULL)
memcpy(ts->rw_buf + 1, data, length);
for (retry = 0; retry < toRetry; retry++) {
ret = i2c_transfer(client->adapter, msg, 1);
if (ret == 1)
break;
msleep(20);
}
for (i = 0; i < ret; i++) {
himax_dbg_add(ts, msg[i].flags & I2C_M_RD ? "[R]" : "[W]", (u8 *)msg[i].buf,
msg[i].len);
}
scnprintf(buf, sizeof(buf), "Messages sent: %d/%d", ret, 1);
buf[sizeof(buf) - 1] = '\0';
himax_dbg_add(ts, buf, 0, 0);
if (retry == toRetry) {
E("%s: i2c_write_block retry over %d\n", __func__, toRetry);
mutex_unlock(&ts->rw_lock);
return -EIO;
}
mutex_unlock(&ts->rw_lock);
return 0;
}
EXPORT_SYMBOL(himax_bus_write);
void himax_int_enable(struct himax_ts_data *ts, int enable)
{
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);
ts->irq_enabled = 1;
} else if (enable == 0 && atomic_read(&ts->irq_state) == 1) {
atomic_set(&ts->irq_state, 0);
disable_irq_nosync(irqnum);
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_gpio_set(int pinnum, uint8_t value)
{
gpio_direction_output(pinnum, value);
}
EXPORT_SYMBOL(himax_gpio_set);
#endif
uint8_t himax_int_gpio_read(int pinnum)
{
return gpio_get_value(pinnum);
}
int himax_gpio_power_config(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata)
{
int error = 0;
struct i2c_client *client = 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 (pdata->lcm_rst >= 0) {
error = gpio_request(pdata->lcm_rst, "lcm-reset");
if (error < 0) {
E("%s: request lcm-reset pin failed\n", __func__);
goto err_lcm_rst_req;
}
error = gpio_direction_output(pdata->lcm_rst, 0);
if (error) {
E("unable to set direction for lcm_rst [%d]\n", pdata->lcm_rst);
goto err_lcm_rst_dir;
}
}
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;
}
}
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);
ts->hx_irq = client->irq;
} else {
E("irq gpio not provided\n");
goto err_gpio_irq_req;
}
if (gpio_is_valid(pdata->fail_det)) {
/* configure touchscreen fail_det gpio */
error = gpio_request(pdata->fail_det, "himax_fail_det");
if (error)
E("unable to request gpio [%d]\n", pdata->fail_det);
error = gpio_direction_input(pdata->fail_det);
if (error)
E("unable to set direction for gpio [%d]\n", pdata->fail_det);
ts->hx_fail_det = gpio_to_irq(pdata->fail_det);
} else {
I("fail_det not provided\n");
}
msleep(10);
if (pdata->lcm_rst >= 0) {
error = gpio_direction_output(pdata->lcm_rst, 1);
if (error) {
E("lcm_rst unable to set direction for gpio [%d]\n", pdata->lcm_rst);
goto err_lcm_reset_set_high;
}
}
msleep(10);
#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
msleep(100);
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;
}
}
return error;
err_gpio_pon_set_high:
#if defined(HX_RST_PIN_FUNC)
err_gpio_reset_set_high:
#endif
err_lcm_reset_set_high:
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_pon_dir:
if (gpio_is_valid(pdata->gpio_pon))
gpio_free(pdata->gpio_pon);
err_gpio_pon_req:
err_lcm_rst_dir:
if (gpio_is_valid(pdata->lcm_rst))
gpio_free(pdata->lcm_rst);
err_lcm_rst_req:
#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;
}
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 (pdata->gpio_3v3_en >= 0)
gpio_free(pdata->gpio_3v3_en);
if (gpio_is_valid(pdata->gpio_pon))
gpio_free(pdata->gpio_pon);
}
static void himax_ts_isr_func(struct himax_ts_data *ts)
{
himax_ts_work(ts);
}
static 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);
}
static irqreturn_t himax_fail_det_thread(int irq, void *ptr)
{
struct himax_ts_data *ts = (struct himax_ts_data *)ptr;
himax_fail_det_work(ts);
return IRQ_HANDLED;
}
static int himax_int_register_trigger(struct himax_ts_data *ts)
{
int ret = 0;
struct i2c_client *client = ts->client;
if (ts->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;
}
static int himax_fail_det_int_register_trigger(struct himax_ts_data *ts)
{
int ret = 0;
char *hx_fail_det_name = "hx_fail_det";
I("%s level trigger high\n ", __func__);
ret = request_threaded_irq(ts->hx_fail_det, NULL, himax_fail_det_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT, hx_fail_det_name,
(void *)ts);
return ret;
}
int himax_int_en_set(struct himax_ts_data *ts)
{
int ret = NO_ERR;
ret = himax_int_register_trigger(ts);
return ret;
}
int himax_fail_det_register_interrupt(struct himax_ts_data *ts)
{
int ret = 0;
if (ts->hx_fail_det) { /*INT mode*/
ret = himax_fail_det_int_register_trigger(ts);
if (ret == 0)
I("%s: irq enabled at gpio: %d\n", __func__, ts->hx_fail_det);
else
E("%s: request_irq failed\n", __func__);
} else {
I("%s: hx_fail_det is empty.\n", __func__);
}
return ret;
}
int himax_ts_register_interrupt(struct himax_ts_data *ts)
{
struct i2c_client *client = ts->client;
int ret = 0;
ts->irq_enabled = 0;
/* Work functon */
if (client->irq && ts->hx_irq) { /*INT mode*/
ts->use_irq = 1;
ret = himax_int_register_trigger(ts);
if (ret == 0) {
ts->irq_enabled = 1;
atomic_set(&ts->irq_state, 1);
I("%s: irq enabled at gpio: %d\n", __func__, client->irq);
} 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(struct himax_ts_data *ts)
{
int ret = 0;
I("%s: entered.\n", __func__);
/* Work functon */
if (ts->hx_irq && ts->use_irq) { /*INT mode*/
free_irq(ts->hx_irq, ts);
I("%s: irq disabled at qpio: %d\n", __func__, 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);
struct pinctrl *p;
struct pinctrl_state *suspend_state;
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);
if (gpio_is_valid(ts->pdata->gpio_reset))
gpio_direction_output(ts->pdata->gpio_reset, 0);
I("%s: set tp_rst 0\n", __func__);
p = devm_pinctrl_get(&ts->client->dev);
suspend_state = pinctrl_lookup_state(p, "sleep");
pinctrl_select_state(p, suspend_state);
I("%s: suspend mode pinctrl!\n", __func__);
return 0;
}
static int himax_common_resume(struct device *dev)
{
struct himax_ts_data *ts = dev_get_drvdata(dev);
struct pinctrl *p;
struct pinctrl_state *default_state;
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(ts))
return -ECANCELED;
#endif
himax_chip_common_resume(ts);
p = devm_pinctrl_get(&ts->client->dev);
default_state = pinctrl_lookup_state(p, "default");
pinctrl_select_state(p, default_state);
I("%s: active pinctrl!\n", __func__);
return 0;
}
#if defined(HX_CONFIG_FB)
int himax_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->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 himax_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->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->client) {
blank = evdata->data;
switch (*blank) {
case MSM_DRM_BLANK_UNBLANK:
himax_common_resume(&ts->client->dev);
ts->touch_num = 50;
break;
}
}
return 0;
}
#endif
static int himax_2nd_probe(struct himax_ts_data *ts)
{
struct i2c_client *client = ts->client;
int i, ret = 0;
if (ts->probe_flag) {
pr_err("[HiMax_Touch][ERROR] have initialed already!!\n\n");
return 0;
}
for (i = 0; i < 3; i++) {
if (i2c_smbus_read_byte_data(client, 0x40) < 0) {
E("can't read ic data!\n");
msleep(100);
} else {
break;
}
}
ret = himax_chip_common_init(ts);
if (ret < 0) {
E("%s: ret=%d\n", __func__, ret);
return ret;
}
ts->probe_flag = true;
return ret;
}
/*static void himax_probe_work(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data, probe_work);
himax_2nd_probe(ts);
}
static void himax_connected_work(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
connected_work);
himax_2nd_probe(ts);
if (ts->probe_flag)
himax_common_resume(&ts->client->dev);
ts->touch_num = 50;
}
static void himax_disconnected_work(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
disconnected_work);
if (ts->probe_flag)
himax_common_suspend(&ts->client->dev);
}
int himax_hotplug_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct himax_ts_data *ts = container_of(nb, struct himax_ts_data,
cable_hotplug_nb);
I("%s:Enter\n", __func__);
if (extcon_get_state(ts->edev, EXTCON_JACK_VIDEO_OUT)) {
cancel_work_sync(&ts->connected_work);
schedule_work(&ts->connected_work);
} else {
cancel_work_sync(&ts->disconnected_work);
schedule_work(&ts->disconnected_work);
}
return 0;
}
int tp_diag_himax(void)
{
int flag = 1;
// int flag = 0;
// if(tp_client) {
// if( i2c_smbus_read_byte_data(tp_client, 0x10) < 0) {
// E("[DIAG] himax i2c bus connection error\n");
// } else {
// flag = 1;
// I("[DIAG]PASS!\n");
// }
// return flag;
// } else {
// E("[DIAG]himax probe error\n");
// }
return flag;
}
EXPORT_SYMBOL(tp_diag_himax);
*/
static int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
//struct device_node *node = client->dev.of_node;
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);
/* FIXME */
INIT_LIST_HEAD(&ts->chips);
#if IS_ENABLED(CONFIG_TOUCHSCREEN_HIMAX_IC_HX83192)
list_add_tail(&hx83192_chip.list, &ts->chips);
#endif
ts->rw_buf = kcalloc(BUS_RW_MAX_LEN, sizeof(uint8_t), GFP_KERNEL);
if (!ts->rw_buf) {
E("Allocate I2C RW Buffer failed\n");
ret = -ENODEV;
goto err_alloc_rw_buf_failed;
}
ts->initialized = false;
ts->touch_num = 50;
himax_2nd_probe(ts);
/*ts->edev = extcon_find_edev_by_node(node->parent->parent);
if (IS_ERR(ts->edev)) {
if (PTR_ERR(ts->edev) != -EPROBE_DEFER)
dev_err(ts->dev, "Invalid or missing extcon\n");
return PTR_ERR(ts->edev);
}
INIT_WORK(&ts->connected_work, himax_connected_work);
INIT_WORK(&ts->disconnected_work, himax_disconnected_work);
ts->cable_hotplug_nb.notifier_call = himax_hotplug_notifier;
ret = extcon_register_notifier(ts->edev, EXTCON_JACK_VIDEO_OUT,
&ts->cable_hotplug_nb);
if (ret < 0) {
dev_err(ts->dev, "failed to register notifier for SDP\n");
return ret;
}
ts->probe_flag = false;
ret = extcon_get_state(ts->edev, EXTCON_JACK_VIDEO_OUT);
if (ret) {
I("[HiMax_Touch]%s:touch is connected\n",__func__);
INIT_WORK(&ts->probe_work, himax_probe_work);
schedule_work(&ts->probe_work);
}*/
// tp_client = client;
return 0;
err_alloc_rw_buf_failed:
kfree(ts);
err_alloc_data_failed:
return ret;
}
static int himax_chip_common_remove(struct i2c_client *client)
{
struct himax_ts_data *ts = i2c_get_clientdata(client);
if (ts->hx_chip_inited)
himax_chip_common_deinit(ts);
/* FIXME */
// himax_hx83192_remove();
kfree(ts->rw_buf);
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 (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);
}
late_initcall(himax_common_init);
module_exit(himax_common_exit);
MODULE_DESCRIPTION("Himax_common driver");
MODULE_LICENSE("GPL");