/* 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");