1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263 |
- /*
- * Goodix Touchscreen Driver
- * Copyright (C) 2020 - 2021 Goodix, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- */
- #include <linux/version.h>
- #include <linux/fs.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/uaccess.h>
- #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
- #include <linux/input/mt.h>
- #define INPUT_TYPE_B_PROTOCOL
- #endif
- #include "goodix_ts_core.h"
- #define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg"
- #define GOOIDX_INPUT_PHYS "goodix_ts/input0"
- struct goodix_module goodix_modules;
- int core_module_prob_sate = CORE_MODULE_UNPROBED;
- static int goodix_send_ic_config(struct goodix_ts_core *cd, int type);
- /**
- * __do_register_ext_module - register external module
- * to register into touch core modules structure
- * return 0 on success, otherwise return < 0
- */
- static int __do_register_ext_module(struct goodix_ext_module *module)
- {
- struct goodix_ext_module *ext_module, *next;
- struct list_head *insert_point = &goodix_modules.head;
- /* prority level *must* be set */
- if (module->priority == EXTMOD_PRIO_RESERVED) {
- ts_err("Priority of module [%s] needs to be set",
- module->name);
- return -EINVAL;
- }
- mutex_lock(&goodix_modules.mutex);
- /* find insert point for the specified priority */
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (ext_module == module) {
- ts_info("Module [%s] already exists",
- module->name);
- mutex_unlock(&goodix_modules.mutex);
- return 0;
- }
- }
- /* smaller priority value with higher priority level */
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (ext_module->priority >= module->priority) {
- insert_point = &ext_module->list;
- break;
- }
- }
- }
- if (module->funcs && module->funcs->init) {
- if (module->funcs->init(goodix_modules.core_data,
- module) < 0) {
- ts_err("Module [%s] init error",
- module->name ? module->name : " ");
- mutex_unlock(&goodix_modules.mutex);
- return -EFAULT;
- }
- }
- list_add(&module->list, insert_point->prev);
- mutex_unlock(&goodix_modules.mutex);
- ts_info("Module [%s] registered,priority:%u", module->name,
- module->priority);
- return 0;
- }
- static void goodix_register_ext_module_work(struct work_struct *work) {
- struct goodix_ext_module *module =
- container_of(work, struct goodix_ext_module, work);
- ts_info("module register work IN");
- /* driver probe failed */
- if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) {
- ts_err("Can't register ext_module core error");
- return;
- }
- if (__do_register_ext_module(module))
- ts_err("failed register module: %s", module->name);
- else
- ts_info("success register module: %s", module->name);
- }
- static void goodix_core_module_init(void)
- {
- if (goodix_modules.initilized)
- return;
- goodix_modules.initilized = true;
- INIT_LIST_HEAD(&goodix_modules.head);
- mutex_init(&goodix_modules.mutex);
- }
- /**
- * goodix_register_ext_module - interface for register external module
- * to the core. This will create a workqueue to finish the real register
- * work and return immediately. The user need to check the final result
- * to make sure registe is success or fail.
- *
- * @module: pointer to external module to be register
- * return: 0 ok, <0 failed
- */
- int goodix_register_ext_module(struct goodix_ext_module *module)
- {
- if (!module)
- return -EINVAL;
- ts_info("goodix_register_ext_module IN");
- goodix_core_module_init();
- INIT_WORK(&module->work, goodix_register_ext_module_work);
- schedule_work(&module->work);
- ts_info("goodix_register_ext_module OUT");
- return 0;
- }
- /**
- * goodix_register_ext_module_no_wait
- * return: 0 ok, <0 failed
- */
- int goodix_register_ext_module_no_wait(struct goodix_ext_module *module)
- {
- if (!module)
- return -EINVAL;
- ts_info("goodix_register_ext_module_no_wait IN");
- goodix_core_module_init();
- /* driver probe failed */
- if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) {
- ts_err("Can't register ext_module core error");
- return -EINVAL;
- }
- return __do_register_ext_module(module);
- }
- /**
- * goodix_unregister_ext_module - interface for external module
- * to unregister external modules
- *
- * @module: pointer to external module
- * return: 0 ok, <0 failed
- */
- int goodix_unregister_ext_module(struct goodix_ext_module *module)
- {
- struct goodix_ext_module *ext_module, *next;
- bool found = false;
- if (!module)
- return -EINVAL;
- if (!goodix_modules.initilized)
- return -EINVAL;
- if (!goodix_modules.core_data)
- return -ENODEV;
- mutex_lock(&goodix_modules.mutex);
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (ext_module == module) {
- found = true;
- break;
- }
- }
- } else {
- mutex_unlock(&goodix_modules.mutex);
- return 0;
- }
- if (!found) {
- ts_debug("Module [%s] never registed",
- module->name);
- mutex_unlock(&goodix_modules.mutex);
- return 0;
- }
- list_del(&module->list);
- mutex_unlock(&goodix_modules.mutex);
- if (module->funcs && module->funcs->exit)
- module->funcs->exit(goodix_modules.core_data, module);
- ts_info("Moudle [%s] unregistered",
- module->name ? module->name : " ");
- return 0;
- }
- static void goodix_ext_sysfs_release(struct kobject *kobj)
- {
- ts_info("Kobject released!");
- }
- #define to_ext_module(kobj) container_of(kobj,\
- struct goodix_ext_module, kobj)
- #define to_ext_attr(attr) container_of(attr,\
- struct goodix_ext_attribute, attr)
- static ssize_t goodix_ext_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
- {
- struct goodix_ext_module *module = to_ext_module(kobj);
- struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
- if (ext_attr->show)
- return ext_attr->show(module, buf);
- return -EIO;
- }
- static ssize_t goodix_ext_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
- {
- struct goodix_ext_module *module = to_ext_module(kobj);
- struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
- if (ext_attr->store)
- return ext_attr->store(module, buf, count);
- return -EIO;
- }
- static const struct sysfs_ops goodix_ext_ops = {
- .show = goodix_ext_sysfs_show,
- .store = goodix_ext_sysfs_store
- };
- static struct kobj_type goodix_ext_ktype = {
- .release = goodix_ext_sysfs_release,
- .sysfs_ops = &goodix_ext_ops,
- };
- struct kobj_type *goodix_get_default_ktype(void)
- {
- return &goodix_ext_ktype;
- }
- struct kobject *goodix_get_default_kobj(void)
- {
- struct kobject *kobj = NULL;
- if (goodix_modules.core_data &&
- goodix_modules.core_data->pdev)
- kobj = &goodix_modules.core_data->pdev->dev.kobj;
- return kobj;
- }
- /* show driver infomation */
- static ssize_t goodix_ts_driver_info_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "DriverVersion:%s\n",
- GOODIX_DRIVER_VERSION);
- }
- /* show chip infoamtion */
- static ssize_t goodix_ts_chip_info_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- struct goodix_fw_version chip_ver;
- u8 temp_pid[8] = {0};
- int ret;
- int cnt = -EINVAL;
- if (hw_ops->read_version) {
- ret = hw_ops->read_version(core_data, &chip_ver);
- if (!ret) {
- memcpy(temp_pid, chip_ver.rom_pid, sizeof(chip_ver.rom_pid));
- cnt = snprintf(&buf[0], PAGE_SIZE,
- "rom_pid:%s\nrom_vid:%02x%02x%02x\n",
- temp_pid, chip_ver.rom_vid[0],
- chip_ver.rom_vid[1], chip_ver.rom_vid[2]);
- cnt += snprintf(&buf[cnt], PAGE_SIZE,
- "patch_pid:%s\npatch_vid:%02x%02x%02x%02x\n",
- chip_ver.patch_pid, chip_ver.patch_vid[0],
- chip_ver.patch_vid[1], chip_ver.patch_vid[2],
- chip_ver.patch_vid[3]);
- cnt += snprintf(&buf[cnt], PAGE_SIZE,
- "sensorid:%d\n", chip_ver.sensor_id);
- }
- }
- if (hw_ops->get_ic_info) {
- ret = hw_ops->get_ic_info(core_data, &core_data->ic_info);
- if (!ret) {
- cnt += snprintf(&buf[cnt], PAGE_SIZE,
- "config_id:%x\n", core_data->ic_info.version.config_id);
- cnt += snprintf(&buf[cnt], PAGE_SIZE,
- "config_version:%x\n", core_data->ic_info.version.config_version);
- }
- }
- return cnt;
- }
- /* reset chip */
- static ssize_t goodix_ts_reset_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- if (!buf || count <= 0)
- return -EINVAL;
- if (buf[0] != '0')
- hw_ops->reset(core_data, GOODIX_NORMAL_RESET_DELAY_MS);
- return count;
- }
- /* read config */
- static ssize_t goodix_ts_read_cfg_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- int ret;
- int i;
- int offset;
- char *cfg_buf = NULL;
- cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!cfg_buf)
- return -ENOMEM;
- if (hw_ops->read_config)
- ret = hw_ops->read_config(core_data, cfg_buf, PAGE_SIZE);
- else
- ret = -EINVAL;
- if (ret > 0) {
- offset = 0;
- for (i = 0; i < 200; i++) { // only print 200 bytes
- offset += snprintf(&buf[offset], PAGE_SIZE - offset,
- "%02x,", cfg_buf[i]);
- if ((i + 1) % 20 == 0)
- buf[offset++] = '\n';
- }
- }
- kfree(cfg_buf);
- if (ret <= 0)
- return ret;
- return offset;
- }
- static u8 ascii2hex(u8 a)
- {
- s8 value = 0;
- if (a >= '0' && a <= '9')
- value = a - '0';
- else if (a >= 'A' && a <= 'F')
- value = a - 'A' + 0x0A;
- else if (a >= 'a' && a <= 'f')
- value = a - 'a' + 0x0A;
- else
- value = 0xff;
- return value;
- }
- static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size,
- u8 *out_buf, int *out_buf_len)
- {
- int i, m_size = 0;
- int temp_index = 0;
- u8 high, low;
- for (i = 0; i < buf_size; i++) {
- if (buf[i] == 'x' || buf[i] == 'X')
- m_size++;
- }
- if (m_size <= 1) {
- ts_err("cfg file ERROR, valid data count:%d", m_size);
- return -EINVAL;
- }
- *out_buf_len = m_size;
- for (i = 0; i < buf_size; i++) {
- if (buf[i] != 'x' && buf[i] != 'X')
- continue;
- if (temp_index >= m_size) {
- ts_err("exchange cfg data error, overflow,"
- "temp_index:%d,m_size:%d",
- temp_index, m_size);
- return -EINVAL;
- }
- high = ascii2hex(buf[i + 1]);
- low = ascii2hex(buf[i + 2]);
- if (high == 0xff || low == 0xff) {
- ts_err("failed convert: 0x%x, 0x%x",
- buf[i + 1], buf[i + 2]);
- return -EINVAL;
- }
- out_buf[temp_index++] = (high << 4) + low;
- }
- return 0;
- }
- /* send config */
- static ssize_t goodix_ts_send_cfg_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- struct goodix_ic_config *config = NULL;
- const struct firmware *cfg_img = NULL;
- int en;
- int ret;
- if (sscanf(buf, "%d", &en) != 1)
- return -EINVAL;
- if (en != 1)
- return -EINVAL;
- hw_ops->irq_enable(core_data, false);
- ret = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev);
- if (ret < 0) {
- ts_err("cfg file [%s] not available,errno:%d",
- GOODIX_DEFAULT_CFG_NAME, ret);
- goto exit;
- } else {
- ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME);
- }
- config = kzalloc(sizeof(*config), GFP_KERNEL);
- if (!config)
- goto exit;
- if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size,
- config->data, &config->len)) {
- ts_err("convert config data FAILED");
- goto exit;
- }
- if (hw_ops->send_config) {
- ret = hw_ops->send_config(core_data, config->data, config->len);
- if (ret < 0)
- ts_err("send config failed");
- }
-
- exit:
- hw_ops->irq_enable(core_data, true);
- kfree(config);
- if (cfg_img)
- release_firmware(cfg_img);
- return count;
- }
- /* reg read/write */
- static u32 rw_addr;
- static u32 rw_len;
- static u8 rw_flag;
- static u8 store_buf[32];
- static u8 show_buf[PAGE_SIZE];
- static ssize_t goodix_ts_reg_rw_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- int ret;
- if (!rw_addr || !rw_len) {
- ts_err("address(0x%x) and length(%d) can't be null",
- rw_addr, rw_len);
- return -EINVAL;
- }
- if (rw_flag != 1) {
- ts_err("invalid rw flag %d, only support [1/2]", rw_flag);
- return -EINVAL;
- }
- ret = hw_ops->read(core_data, rw_addr, show_buf, rw_len);
- if (ret < 0) {
- ts_err("failed read addr(%x) length(%d)", rw_addr, rw_len);
- return snprintf(buf, PAGE_SIZE, "failed read addr(%x), len(%d)\n",
- rw_addr, rw_len);
- }
- return snprintf(buf, PAGE_SIZE, "0x%x,%d {%*ph}\n",
- rw_addr, rw_len, rw_len, show_buf);
- }
- static ssize_t goodix_ts_reg_rw_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- char *pos = NULL;
- char *token = NULL;
- long result = 0;
- int ret;
- int i;
- if (!buf || !count) {
- ts_err("invalid parame");
- goto err_out;
- }
- if (buf[0] == 'r') {
- rw_flag = 1;
- } else if (buf[0] == 'w') {
- rw_flag = 2;
- } else {
- ts_err("string must start with 'r/w'");
- goto err_out;
- }
- /* get addr */
- pos = (char *)buf;
- pos += 2;
- token = strsep(&pos, ":");
- if (!token) {
- ts_err("invalid address info");
- goto err_out;
- } else {
- if (kstrtol(token, 16, &result)) {
- ts_err("failed get addr info");
- goto err_out;
- }
- rw_addr = (u32)result;
- ts_info("rw addr is 0x%x", rw_addr);
- }
- /* get length */
- token = strsep(&pos, ":");
- if (!token) {
- ts_err("invalid length info");
- goto err_out;
- } else {
- if (kstrtol(token, 0, &result)) {
- ts_err("failed get length info");
- goto err_out;
- }
- rw_len = (u32)result;
- ts_info("rw length info is %d", rw_len);
- if (rw_len > sizeof(store_buf)) {
- ts_err("data len > %lu", sizeof(store_buf));
- goto err_out;
- }
- }
- if (rw_flag == 1)
- return count;
- for (i = 0; i < rw_len; i++) {
- token = strsep(&pos, ":");
- if (!token) {
- ts_err("invalid data info");
- goto err_out;
- } else {
- if (kstrtol(token, 16, &result)) {
- ts_err("failed get data[%d] info", i);
- goto err_out;
- }
- store_buf[i] = (u8)result;
- ts_info("get data[%d]=0x%x", i, store_buf[i]);
- }
- }
- ret = hw_ops->write(core_data, rw_addr, store_buf, rw_len);
- if (ret < 0) {
- ts_err("failed write addr(%x) data %*ph", rw_addr,
- rw_len, store_buf);
- goto err_out;
- }
- ts_info("%s write to addr (%x) with data %*ph",
- "success", rw_addr, rw_len, store_buf);
- return count;
- err_out:
- snprintf(show_buf, PAGE_SIZE, "%s\n",
- "invalid params, format{r/w:4100:length:[41:21:31]}");
- return -EINVAL;
- }
- /* show irq infomation */
- static ssize_t goodix_ts_irq_info_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct irq_desc *desc;
- size_t offset = 0;
- int r;
- r = snprintf(&buf[offset], PAGE_SIZE, "irq:%u\n", core_data->irq);
- if (r < 0)
- return -EINVAL;
- offset += r;
- r = snprintf(&buf[offset], PAGE_SIZE - offset, "state:%s\n",
- atomic_read(&core_data->irq_enabled) ?
- "enabled" : "disabled");
- if (r < 0)
- return -EINVAL;
- desc = irq_to_desc(core_data->irq);
- offset += r;
- r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n",
- desc->depth);
- if (r < 0)
- return -EINVAL;
- offset += r;
- r = snprintf(&buf[offset], PAGE_SIZE - offset, "trigger-count:%zu\n",
- core_data->irq_trig_cnt);
- if (r < 0)
- return -EINVAL;
- offset += r;
- r = snprintf(&buf[offset], PAGE_SIZE - offset,
- "echo 0/1 > irq_info to disable/enable irq\n");
- if (r < 0)
- return -EINVAL;
- offset += r;
- return offset;
- }
- /* enable/disable irq */
- static ssize_t goodix_ts_irq_info_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- if (!buf || count <= 0)
- return -EINVAL;
- if (buf[0] != '0')
- hw_ops->irq_enable(core_data, true);
- else
- hw_ops->irq_enable(core_data, false);
- return count;
- }
- /* show esd status */
- static ssize_t goodix_ts_esd_info_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct goodix_ts_core *core_data = dev_get_drvdata(dev);
- struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
- int r = 0;
- r = snprintf(buf, PAGE_SIZE, "state:%s\n",
- atomic_read(&ts_esd->esd_on) ?
- "enabled" : "disabled");
- return r;
- }
- /* enable/disable esd */
- static ssize_t goodix_ts_esd_info_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- if (!buf || count <= 0)
- return -EINVAL;
- if (buf[0] != '0')
- goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
- else
- goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
- return count;
- }
- /* debug level show */
- static ssize_t goodix_ts_debug_log_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- int r = 0;
- r = snprintf(buf, PAGE_SIZE, "state:%s\n",
- debug_log_flag ?
- "enabled" : "disabled");
- return r;
- }
- /* debug level store */
- static ssize_t goodix_ts_debug_log_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- if (!buf || count <= 0)
- return -EINVAL;
- if (buf[0] != '0')
- debug_log_flag = true;
- else
- debug_log_flag = false;
- return count;
- }
- static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL);
- static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL);
- static DEVICE_ATTR(reset, 0220, NULL, goodix_ts_reset_store);
- static DEVICE_ATTR(send_cfg, 0220, NULL, goodix_ts_send_cfg_store);
- static DEVICE_ATTR(read_cfg, 0444, goodix_ts_read_cfg_show, NULL);
- static DEVICE_ATTR(reg_rw, 0664, goodix_ts_reg_rw_show, goodix_ts_reg_rw_store);
- static DEVICE_ATTR(irq_info, 0664, goodix_ts_irq_info_show, goodix_ts_irq_info_store);
- static DEVICE_ATTR(esd_info, 0664, goodix_ts_esd_info_show, goodix_ts_esd_info_store);
- static DEVICE_ATTR(debug_log, 0664, goodix_ts_debug_log_show, goodix_ts_debug_log_store);
- static struct attribute *sysfs_attrs[] = {
- &dev_attr_driver_info.attr,
- &dev_attr_chip_info.attr,
- &dev_attr_reset.attr,
- &dev_attr_send_cfg.attr,
- &dev_attr_read_cfg.attr,
- &dev_attr_reg_rw.attr,
- &dev_attr_irq_info.attr,
- &dev_attr_esd_info.attr,
- &dev_attr_debug_log.attr,
- NULL,
- };
- static const struct attribute_group sysfs_group = {
- .attrs = sysfs_attrs,
- };
- static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data)
- {
- int ret;
- ret = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group);
- if (ret) {
- ts_err("failed create core sysfs group");
- return ret;
- }
- return ret;
- }
- static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data)
- {
- sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group);
- }
- /* prosfs create */
- static int rawdata_proc_show(struct seq_file *m, void *v)
- {
- struct ts_rawdata_info *info;
- struct goodix_ts_core *cd = m->private;
- int tx;
- int rx;
- int ret;
- int i;
- int index;
- if (!m || !v || !cd) {
- ts_err("rawdata_proc_show, input null ptr");
- return -EIO;
- }
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- ts_err("Failed to alloc rawdata info memory");
- return -ENOMEM;
- }
- ret = cd->hw_ops->get_capacitance_data(cd, info);
- if (ret < 0) {
- ts_err("failed to get_capacitance_data, exit!");
- goto exit;
- }
- rx = info->buff[0];
- tx = info->buff[1];
- seq_printf(m, "TX:%d RX:%d\n", tx, rx);
- seq_printf(m, "mutual_rawdata:\n");
- index = 2;
- for (i = 0; i < tx * rx; i++) {
- seq_printf(m, "%5d,", info->buff[index + i]);
- if ((i + 1) % tx == 0)
- seq_printf(m, "\n");
- }
- seq_printf(m, "mutual_diffdata:\n");
- index += tx * rx;
- for (i = 0; i < tx * rx; i++) {
- seq_printf(m, "%3d,", info->buff[index + i]);
- if ((i + 1) % tx == 0)
- seq_printf(m, "\n");
- }
- exit:
- kfree(info);
- return ret;
- }
- static int rawdata_proc_open(struct inode *inode, struct file *file)
- {
- return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10);
- }
- static const struct file_operations rawdata_proc_fops = {
- .open = rawdata_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static void goodix_ts_procfs_init(struct goodix_ts_core *core_data)
- {
- if (!proc_mkdir("goodix_ts", NULL))
- return;
- proc_create_data("goodix_ts/tp_capacitance_data",
- 0666, NULL, &rawdata_proc_fops, core_data);
- }
- static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data)
- {
- remove_proc_entry("goodix_ts/tp_capacitance_data", NULL);
- remove_proc_entry("goodix_ts", NULL);
- }
- /* event notifier */
- static BLOCKING_NOTIFIER_HEAD(ts_notifier_list);
- /**
- * goodix_ts_register_client - register a client notifier
- * @nb: notifier block to callback on events
- * see enum ts_notify_event in goodix_ts_core.h
- */
- int goodix_ts_register_notifier(struct notifier_block *nb)
- {
- return blocking_notifier_chain_register(&ts_notifier_list, nb);
- }
- /**
- * goodix_ts_unregister_client - unregister a client notifier
- * @nb: notifier block to callback on events
- * see enum ts_notify_event in goodix_ts_core.h
- */
- int goodix_ts_unregister_notifier(struct notifier_block *nb)
- {
- return blocking_notifier_chain_unregister(&ts_notifier_list, nb);
- }
- /**
- * fb_notifier_call_chain - notify clients of fb_events
- * see enum ts_notify_event in goodix_ts_core.h
- */
- int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v)
- {
- int ret;
- ret = blocking_notifier_call_chain(&ts_notifier_list,
- (unsigned long)evt, v);
- return ret;
- }
- #ifdef CONFIG_OF
- /**
- * goodix_parse_dt_resolution - parse resolution from dt
- * @node: devicetree node
- * @board_data: pointer to board data structure
- * return: 0 - no error, <0 error
- */
- static int goodix_parse_dt_resolution(struct device_node *node,
- struct goodix_ts_board_data *board_data)
- {
- int ret;
- ret = of_property_read_u32(node, "goodix,panel-max-x",
- &board_data->panel_max_x);
- if (ret) {
- ts_err("failed get panel-max-x");
- return ret;
- }
- ret = of_property_read_u32(node, "goodix,panel-max-y",
- &board_data->panel_max_y);
- if (ret) {
- ts_err("failed get panel-max-y");
- return ret;
- }
- ret = of_property_read_u32(node, "goodix,panel-max-w",
- &board_data->panel_max_w);
- if (ret) {
- ts_err("failed get panel-max-w");
- return ret;
- }
- ret = of_property_read_u32(node, "goodix,panel-max-p",
- &board_data->panel_max_p);
- if (ret) {
- ts_err("failed get panel-max-p, use default");
- board_data->panel_max_p = GOODIX_PEN_MAX_PRESSURE;
- }
- return 0;
- }
- /**
- * goodix_parse_dt - parse board data from dt
- * @dev: pointer to device
- * @board_data: pointer to board data structure
- * return: 0 - no error, <0 error
- */
- static int goodix_parse_dt(struct device_node *node,
- struct goodix_ts_board_data *board_data)
- {
- const char *name_tmp;
- int r;
- if (!board_data) {
- ts_err("invalid board data");
- return -EINVAL;
- }
- r = of_get_named_gpio(node, "goodix,avdd-gpio", 0);
- if (r < 0) {
- ts_info("can't find avdd-gpio, use other power supply");
- board_data->avdd_gpio = 0;
- } else {
- ts_info("get avdd-gpio[%d] from dt", r);
- board_data->avdd_gpio = r;
- }
- r = of_get_named_gpio(node, "goodix,iovdd-gpio", 0);
- if (r < 0) {
- ts_info("can't find iovdd-gpio, use other power supply");
- board_data->iovdd_gpio = 0;
- } else {
- ts_info("get iovdd-gpio[%d] from dt", r);
- board_data->iovdd_gpio = r;
- }
- r = of_get_named_gpio(node, "goodix,reset-gpio", 0);
- if (r < 0) {
- ts_err("invalid reset-gpio in dt: %d", r);
- return -EINVAL;
- }
- ts_info("get reset-gpio[%d] from dt", r);
- board_data->reset_gpio = r;
- r = of_get_named_gpio(node, "goodix,irq-gpio", 0);
- if (r < 0) {
- ts_err("invalid irq-gpio in dt: %d", r);
- return -EINVAL;
- }
- ts_info("get irq-gpio[%d] from dt", r);
- board_data->irq_gpio = r;
- r = of_property_read_u32(node, "goodix,irq-flags",
- &board_data->irq_flags);
- if (r) {
- ts_err("invalid irq-flags");
- return -EINVAL;
- }
- memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name));
- r = of_property_read_string(node, "goodix,avdd-name", &name_tmp);
- if (!r) {
- ts_info("avdd name from dt: %s", name_tmp);
- if (strlen(name_tmp) < sizeof(board_data->avdd_name))
- strncpy(board_data->avdd_name,
- name_tmp, sizeof(board_data->avdd_name));
- else
- ts_info("invalied avdd name length: %ld > %ld",
- strlen(name_tmp),
- sizeof(board_data->avdd_name));
- }
- memset(board_data->iovdd_name, 0, sizeof(board_data->iovdd_name));
- r = of_property_read_string(node, "goodix,iovdd-name", &name_tmp);
- if (!r) {
- ts_info("iovdd name from dt: %s", name_tmp);
- if (strlen(name_tmp) < sizeof(board_data->iovdd_name))
- strncpy(board_data->iovdd_name,
- name_tmp, sizeof(board_data->iovdd_name));
- else
- ts_info("invalied iovdd name length: %ld > %ld",
- strlen(name_tmp),
- sizeof(board_data->iovdd_name));
- }
- /* get firmware file name */
- r = of_property_read_string(node, "goodix,firmware-name", &name_tmp);
- if (!r) {
- ts_info("firmware name from dt: %s", name_tmp);
- strncpy(board_data->fw_name, name_tmp, sizeof(board_data->fw_name));
- } else {
- ts_info("can't find firmware name, use default: %s", TS_DEFAULT_FIRMWARE);
- strncpy(board_data->fw_name, TS_DEFAULT_FIRMWARE, sizeof(board_data->fw_name));
- }
- /* get config file name */
- r = of_property_read_string(node, "goodix,config-name", &name_tmp);
- if (!r) {
- ts_info("config name from dt: %s", name_tmp);
- strncpy(board_data->cfg_bin_name, name_tmp, sizeof(board_data->cfg_bin_name));
- } else {
- ts_info("can't find config name, use default: %s", TS_DEFAULT_CFG_BIN);
- strncpy(board_data->cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(board_data->cfg_bin_name));
- }
- /* get xyz resolutions */
- r = goodix_parse_dt_resolution(node, board_data);
- if (r) {
- ts_err("Failed to parse resolutions:%d", r);
- return r;
- }
- /*get pen-enable switch and pen keys, must after "key map"*/
- board_data->pen_enable = of_property_read_bool(node,
- "goodix,pen-enable");
- if (board_data->pen_enable)
- ts_info("goodix pen enabled");
- ts_debug("[DT]x:%d, y:%d, w:%d, p:%d", board_data->panel_max_x,
- board_data->panel_max_y, board_data->panel_max_w,
- board_data->panel_max_p);
- return 0;
- }
- #endif
- static void goodix_ts_report_pen(struct input_dev *dev,
- struct goodix_pen_data *pen_data)
- {
- int i;
- mutex_lock(&dev->mutex);
- if (pen_data->coords.status == TS_TOUCH) {
- input_report_key(dev, BTN_TOUCH, 1);
- input_report_key(dev, pen_data->coords.tool_type, 1);
- input_report_abs(dev, ABS_X, pen_data->coords.x);
- input_report_abs(dev, ABS_Y, pen_data->coords.y);
- input_report_abs(dev, ABS_PRESSURE, pen_data->coords.p);
- input_report_abs(dev, ABS_TILT_X, pen_data->coords.tilt_x);
- input_report_abs(dev, ABS_TILT_Y, pen_data->coords.tilt_y);
- ts_debug("pen_data:x %d, y %d, p %d, tilt_x %d tilt_y %d key[%d %d]",
- pen_data->coords.x, pen_data->coords.y,
- pen_data->coords.p, pen_data->coords.tilt_x,
- pen_data->coords.tilt_y, pen_data->keys[0].status == TS_TOUCH ? 1 : 0,
- pen_data->keys[1].status == TS_TOUCH ? 1 : 0);
- } else {
- input_report_key(dev, BTN_TOUCH, 0);
- input_report_key(dev, pen_data->coords.tool_type, 0);
- }
- /* report pen button */
- for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) {
- if (pen_data->keys[i].status == TS_TOUCH)
- input_report_key(dev, pen_data->keys[i].code, 1);
- else
- input_report_key(dev, pen_data->keys[i].code, 0);
- }
- input_sync(dev);
- mutex_unlock(&dev->mutex);
- }
- static void goodix_ts_report_finger(struct input_dev *dev,
- struct goodix_touch_data *touch_data)
- {
- unsigned int touch_num = touch_data->touch_num;
- int i;
- mutex_lock(&dev->mutex);
- for (i = 0; i < GOODIX_MAX_TOUCH; i++) {
- if (touch_data->coords[i].status == TS_TOUCH) {
- ts_debug("report: id %d, x %d, y %d, w %d", i,
- touch_data->coords[i].x, touch_data->coords[i].y,
- touch_data->coords[i].w);
- input_mt_slot(dev, i);
- input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
- input_report_abs(dev, ABS_MT_POSITION_X,
- touch_data->coords[i].x);
- input_report_abs(dev, ABS_MT_POSITION_Y,
- touch_data->coords[i].y);
- input_report_abs(dev, ABS_MT_TOUCH_MAJOR,
- touch_data->coords[i].w);
- } else {
- input_mt_slot(dev, i);
- input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
- }
- }
- input_report_key(dev, BTN_TOUCH, touch_num > 0 ? 1 : 0);
- input_sync(dev);
- mutex_unlock(&dev->mutex);
- }
- static int goodix_ts_request_handle(struct goodix_ts_core *cd,
- struct goodix_ts_event *ts_event)
- {
- struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
- int ret = -1;
- if (ts_event->request_code == REQUEST_TYPE_CONFIG)
- ret = goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL);
- else if (ts_event->request_code == REQUEST_TYPE_RESET)
- ret = hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS);
- else
- ts_info("can not handle request type 0x%x",
- ts_event->request_code);
- if (ret)
- ts_err("failed handle request 0x%x",
- ts_event->request_code);
- else
- ts_info("success handle ic request 0x%x",
- ts_event->request_code);
- return ret;
- }
- /**
- * goodix_ts_threadirq_func - Bottom half of interrupt
- * This functions is excuted in thread context,
- * sleep in this function is permit.
- *
- * @data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- static irqreturn_t goodix_ts_threadirq_func(int irq, void *data)
- {
- struct goodix_ts_core *core_data = data;
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- struct goodix_ext_module *ext_module, *next;
- struct goodix_ts_event *ts_event = &core_data->ts_event;
- struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
- int ret;
- ts_esd->irq_status = true;
- core_data->irq_trig_cnt++;
- /* inform external module */
- mutex_lock(&goodix_modules.mutex);
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (!ext_module->funcs->irq_event)
- continue;
- ret = ext_module->funcs->irq_event(core_data, ext_module);
- if (ret == EVT_CANCEL_IRQEVT) {
- mutex_unlock(&goodix_modules.mutex);
- return IRQ_HANDLED;
- }
- }
- mutex_unlock(&goodix_modules.mutex);
- /* read touch data from touch device */
- ret = hw_ops->event_handler(core_data, ts_event);
- if (likely(!ret)) {
- if (ts_event->event_type == EVENT_TOUCH) {
- /* report touch */
- goodix_ts_report_finger(core_data->input_dev,
- &ts_event->touch_data);
- }
- if (core_data->board_data.pen_enable &&
- ts_event->event_type == EVENT_PEN) {
- goodix_ts_report_pen(core_data->pen_dev,
- &ts_event->pen_data);
- }
- if (ts_event->event_type == EVENT_REQUEST) {
- goodix_ts_request_handle(core_data, ts_event);
- }
- }
- if (!core_data->tools_ctrl_sync && !ts_event->retry)
- hw_ops->after_event_handler(core_data);
- ts_event->retry = 0;
- return IRQ_HANDLED;
- }
- /**
- * goodix_ts_init_irq - Requset interrput line from system
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- static int goodix_ts_irq_setup(struct goodix_ts_core *core_data)
- {
- const struct goodix_ts_board_data *ts_bdata = board_data(core_data);
- int ret;
- /* if ts_bdata-> irq is invalid */
- core_data->irq = gpio_to_irq(ts_bdata->irq_gpio);
- if (core_data->irq < 0) {
- ts_err("failed get irq num %d", core_data->irq);
- return -EINVAL;
- }
- ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags);
- ret = devm_request_threaded_irq(&core_data->pdev->dev,
- core_data->irq, NULL,
- goodix_ts_threadirq_func,
- ts_bdata->irq_flags | IRQF_ONESHOT,
- GOODIX_CORE_DRIVER_NAME,
- core_data);
- if (ret < 0)
- ts_err("Failed to requeset threaded irq:%d", ret);
- else
- atomic_set(&core_data->irq_enabled, 1);
- return ret;
- }
- /**
- * goodix_ts_power_init - Get regulator for touch device
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- static int goodix_ts_power_init(struct goodix_ts_core *core_data)
- {
- struct goodix_ts_board_data *ts_bdata = board_data(core_data);
- struct device *dev = core_data->bus->dev;
- int ret = 0;
- ts_info("Power init");
- if (strlen(ts_bdata->avdd_name)) {
- core_data->avdd = devm_regulator_get(dev,
- ts_bdata->avdd_name);
- if (IS_ERR_OR_NULL(core_data->avdd)) {
- ret = PTR_ERR(core_data->avdd);
- ts_err("Failed to get regulator avdd:%d", ret);
- core_data->avdd = NULL;
- return ret;
- }
- } else {
- ts_info("Avdd name is NULL");
- }
- if (strlen(ts_bdata->iovdd_name)) {
- core_data->iovdd = devm_regulator_get(dev,
- ts_bdata->iovdd_name);
- if (IS_ERR_OR_NULL(core_data->iovdd)) {
- ret = PTR_ERR(core_data->iovdd);
- ts_err("Failed to get regulator iovdd:%d", ret);
- core_data->iovdd = NULL;
- }
- } else {
- ts_info("iovdd name is NULL");
- }
- return ret;
- }
- /**
- * goodix_ts_power_on - Turn on power to the touch device
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- int goodix_ts_power_on(struct goodix_ts_core *cd)
- {
- int ret = 0;
- ts_info("Device power on");
- if (cd->power_on)
- return 0;
- ret = cd->hw_ops->power_on(cd, true);
- if (!ret)
- cd->power_on = 1;
- else
- ts_err("failed power on, %d", ret);
- return ret;
- }
- /**
- * goodix_ts_power_off - Turn off power to the touch device
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- int goodix_ts_power_off(struct goodix_ts_core *cd)
- {
- int ret;
- ts_info("Device power off");
- if (!cd->power_on)
- return 0;
- ret = cd->hw_ops->power_on(cd, false);
- if (!ret)
- cd->power_on = 0;
- else
- ts_err("failed power off, %d", ret);
- return ret;
- }
- /**
- * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data)
- {
- struct goodix_ts_board_data *ts_bdata = board_data(core_data);
- int r = 0;
- ts_info("GPIO setup,reset-gpio:%d, irq-gpio:%d",
- ts_bdata->reset_gpio, ts_bdata->irq_gpio);
- /*
- * after kenerl3.13, gpio_ api is deprecated, new
- * driver should use gpiod_ api.
- */
- r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->reset_gpio,
- GPIOF_OUT_INIT_LOW, "ts_reset_gpio");
- if (r < 0) {
- ts_err("Failed to request reset gpio, r:%d", r);
- return r;
- }
- r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->irq_gpio,
- GPIOF_IN, "ts_irq_gpio");
- if (r < 0) {
- ts_err("Failed to request irq gpio, r:%d", r);
- return r;
- }
- if (ts_bdata->avdd_gpio > 0) {
- r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->avdd_gpio,
- GPIOF_OUT_INIT_LOW, "ts_avdd_gpio");
- if (r < 0) {
- ts_err("Failed to request avdd-gpio, r:%d", r);
- return r;
- }
- }
- if (ts_bdata->iovdd_gpio > 0) {
- r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->iovdd_gpio,
- GPIOF_OUT_INIT_LOW, "ts_iovdd_gpio");
- if (r < 0) {
- ts_err("Failed to request iovdd-gpio, r:%d", r);
- return r;
- }
- }
- return 0;
- }
- /**
- * goodix_ts_input_dev_config - Requset and config a input device
- * then register it to input sybsystem.
- * @core_data: pointer to touch core data
- * return: 0 ok, <0 failed
- */
- static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data)
- {
- struct goodix_ts_board_data *ts_bdata = board_data(core_data);
- struct input_dev *input_dev = NULL;
- int r;
- input_dev = input_allocate_device();
- if (!input_dev) {
- ts_err("Failed to allocated input device");
- return -ENOMEM;
- }
- core_data->input_dev = input_dev;
- input_set_drvdata(input_dev, core_data);
- input_dev->name = GOODIX_CORE_DRIVER_NAME;
- input_dev->phys = GOOIDX_INPUT_PHYS;
- input_dev->id.product = 0xDEAD;
- input_dev->id.vendor = 0xBEEF;
- input_dev->id.version = 10427;
- __set_bit(EV_SYN, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- #ifdef INPUT_PROP_DIRECT
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
- #endif
- /* set input parameters */
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, ts_bdata->panel_max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, ts_bdata->panel_max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, ts_bdata->panel_max_w, 0, 0);
- #ifdef INPUT_TYPE_B_PROTOCOL
- #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
- input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH,
- INPUT_MT_DIRECT);
- #else
- input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH);
- #endif
- #endif
- input_set_capability(input_dev, EV_KEY, KEY_POWER);
- r = input_register_device(input_dev);
- if (r < 0) {
- ts_err("Unable to register input device");
- input_free_device(input_dev);
- return r;
- }
- return 0;
- }
- static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data)
- {
- struct goodix_ts_board_data *ts_bdata = board_data(core_data);
- struct input_dev *pen_dev = NULL;
- int r;
- pen_dev = input_allocate_device();
- if (!pen_dev) {
- ts_err("Failed to allocated pen device");
- return -ENOMEM;
- }
- core_data->pen_dev = pen_dev;
- input_set_drvdata(pen_dev, core_data);
- pen_dev->name = GOODIX_PEN_DRIVER_NAME;
- pen_dev->id.product = 0xDEAD;
- pen_dev->id.vendor = 0xBEEF;
- pen_dev->id.version = 10427;
- pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- __set_bit(ABS_X, pen_dev->absbit);
- __set_bit(ABS_Y, pen_dev->absbit);
- __set_bit(ABS_TILT_X, pen_dev->absbit);
- __set_bit(ABS_TILT_Y, pen_dev->absbit);
- __set_bit(BTN_STYLUS, pen_dev->keybit);
- __set_bit(BTN_STYLUS2, pen_dev->keybit);
- __set_bit(BTN_TOUCH, pen_dev->keybit);
- __set_bit(BTN_TOOL_PEN, pen_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit);
- input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0);
- input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0);
- input_set_abs_params(pen_dev, ABS_PRESSURE, 0,
- ts_bdata->panel_max_p, 0, 0);
- input_set_abs_params(pen_dev, ABS_TILT_X,
- -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0);
- input_set_abs_params(pen_dev, ABS_TILT_Y,
- -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0);
- r = input_register_device(pen_dev);
- if (r < 0) {
- ts_err("Unable to register pen device");
- input_free_device(pen_dev);
- return r;
- }
- return 0;
- }
- void goodix_ts_input_dev_remove(struct goodix_ts_core *core_data)
- {
- if (!core_data->input_dev)
- return;
- input_unregister_device(core_data->input_dev);
- input_free_device(core_data->input_dev);
- core_data->input_dev = NULL;
- }
- void goodix_ts_pen_dev_remove(struct goodix_ts_core *core_data)
- {
- if (!core_data->pen_dev)
- return;
- input_unregister_device(core_data->pen_dev);
- input_free_device(core_data->pen_dev);
- core_data->pen_dev = NULL;
- }
- /**
- * goodix_ts_esd_work - check hardware status and recovery
- * the hardware if needed.
- */
- static void goodix_ts_esd_work(struct work_struct *work)
- {
- struct delayed_work *dwork = to_delayed_work(work);
- struct goodix_ts_esd *ts_esd = container_of(dwork,
- struct goodix_ts_esd, esd_work);
- struct goodix_ts_core *cd = container_of(ts_esd,
- struct goodix_ts_core, ts_esd);
- const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
- int ret = 0;
- if (ts_esd->irq_status)
- goto exit;
- if (!atomic_read(&ts_esd->esd_on))
- return;
- if (!hw_ops->esd_check)
- return;
- ret = hw_ops->esd_check(cd);
- if (ret) {
- ts_err("esd check failed");
- goodix_ts_power_off(cd);
- usleep_range(5000, 5100);
- goodix_ts_power_on(cd);
- }
- exit:
- ts_esd->irq_status = false;
- if (atomic_read(&ts_esd->esd_on))
- schedule_delayed_work(&ts_esd->esd_work, 2 * HZ);
- }
- /**
- * goodix_ts_esd_on - turn on esd protection
- */
- static void goodix_ts_esd_on(struct goodix_ts_core *cd)
- {
- struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
- struct goodix_ts_esd *ts_esd = &cd->ts_esd;
- if (!misc->esd_addr)
- return;
- if (atomic_read(&ts_esd->esd_on))
- return;
- atomic_set(&ts_esd->esd_on, 1);
- if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ)) {
- ts_info("esd work already in workqueue");
- }
- ts_info("esd on");
- }
- /**
- * goodix_ts_esd_off - turn off esd protection
- */
- static void goodix_ts_esd_off(struct goodix_ts_core *cd)
- {
- struct goodix_ts_esd *ts_esd = &cd->ts_esd;
- int ret;
- if (!atomic_read(&ts_esd->esd_on))
- return;
- atomic_set(&ts_esd->esd_on, 0);
- ret = cancel_delayed_work_sync(&ts_esd->esd_work);
- ts_info("Esd off, esd work state %d", ret);
- }
- /**
- * goodix_esd_notifier_callback - notification callback
- * under certain condition, we need to turn off/on the esd
- * protector, we use kernel notify call chain to achieve this.
- *
- * for example: before firmware update we need to turn off the
- * esd protector and after firmware update finished, we should
- * turn on the esd protector.
- */
- static int goodix_esd_notifier_callback(struct notifier_block *nb,
- unsigned long action, void *data)
- {
- struct goodix_ts_esd *ts_esd = container_of(nb,
- struct goodix_ts_esd, esd_notifier);
- switch (action) {
- case NOTIFY_FWUPDATE_START:
- case NOTIFY_SUSPEND:
- case NOTIFY_ESD_OFF:
- goodix_ts_esd_off(ts_esd->ts_core);
- break;
- case NOTIFY_FWUPDATE_FAILED:
- case NOTIFY_FWUPDATE_SUCCESS:
- case NOTIFY_RESUME:
- case NOTIFY_ESD_ON:
- goodix_ts_esd_on(ts_esd->ts_core);
- break;
- default:
- break;
- }
- return 0;
- }
- /**
- * goodix_ts_esd_init - initialize esd protection
- */
- int goodix_ts_esd_init(struct goodix_ts_core *cd)
- {
- struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
- struct goodix_ts_esd *ts_esd = &cd->ts_esd;
- if (!cd->hw_ops->esd_check || !misc->esd_addr) {
- ts_info("missing key info for esd check");
- return 0;
- }
- INIT_DELAYED_WORK(&ts_esd->esd_work, goodix_ts_esd_work);
- ts_esd->ts_core = cd;
- atomic_set(&ts_esd->esd_on, 0);
- ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback;
- goodix_ts_register_notifier(&ts_esd->esd_notifier);
- goodix_ts_esd_on(cd);
- return 0;
- }
- static void goodix_ts_release_connects(struct goodix_ts_core *core_data)
- {
- struct input_dev *input_dev = core_data->input_dev;
- int i;
- mutex_lock(&input_dev->mutex);
- for (i = 0; i < GOODIX_MAX_TOUCH; i++) {
- input_mt_slot(input_dev, i);
- input_mt_report_slot_state(input_dev,
- MT_TOOL_FINGER,
- false);
- }
- input_report_key(input_dev, BTN_TOUCH, 0);
- input_mt_sync_frame(input_dev);
- input_sync(input_dev);
- mutex_unlock(&input_dev->mutex);
- }
- /**
- * goodix_ts_suspend - Touchscreen suspend function
- * Called by PM/FB/EARLYSUSPEN module to put the device to sleep
- */
- static int goodix_ts_suspend(struct goodix_ts_core *core_data)
- {
- struct goodix_ext_module *ext_module, *next;
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- int ret;
- if (core_data->init_stage < CORE_INIT_STAGE2 ||
- atomic_read(&core_data->suspended))
- return 0;
- ts_info("Suspend start");
- atomic_set(&core_data->suspended, 1);
- /* disable irq */
- hw_ops->irq_enable(core_data, false);
- /*
- * notify suspend event, inform the esd protector
- * and charger detector to turn off the work
- */
- goodix_ts_blocking_notify(NOTIFY_SUSPEND, NULL);
- /* inform external module */
- mutex_lock(&goodix_modules.mutex);
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (!ext_module->funcs->before_suspend)
- continue;
- ret = ext_module->funcs->before_suspend(core_data,
- ext_module);
- if (ret == EVT_CANCEL_SUSPEND) {
- mutex_unlock(&goodix_modules.mutex);
- ts_info("Canceled by module:%s",
- ext_module->name);
- goto out;
- }
- }
- }
- mutex_unlock(&goodix_modules.mutex);
- /* enter sleep mode or power off */
- if (hw_ops->suspend)
- hw_ops->suspend(core_data);
- /* inform exteranl modules */
- mutex_lock(&goodix_modules.mutex);
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (!ext_module->funcs->after_suspend)
- continue;
- ret = ext_module->funcs->after_suspend(core_data,
- ext_module);
- if (ret == EVT_CANCEL_SUSPEND) {
- mutex_unlock(&goodix_modules.mutex);
- ts_info("Canceled by module:%s",
- ext_module->name);
- goto out;
- }
- }
- }
- mutex_unlock(&goodix_modules.mutex);
- out:
- goodix_ts_release_connects(core_data);
- ts_info("Suspend end");
- return 0;
- }
- /**
- * goodix_ts_resume - Touchscreen resume function
- * Called by PM/FB/EARLYSUSPEN module to wakeup device
- */
- static int goodix_ts_resume(struct goodix_ts_core *core_data)
- {
- struct goodix_ext_module *ext_module, *next;
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- int ret;
- if (core_data->init_stage < CORE_INIT_STAGE2 ||
- !atomic_read(&core_data->suspended))
- return 0;
- ts_info("Resume start");
- atomic_set(&core_data->suspended, 0);
- mutex_lock(&goodix_modules.mutex);
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (!ext_module->funcs->before_resume)
- continue;
- ret = ext_module->funcs->before_resume(core_data,
- ext_module);
- if (ret == EVT_CANCEL_RESUME) {
- mutex_unlock(&goodix_modules.mutex);
- ts_info("Canceled by module:%s",
- ext_module->name);
- goto out;
- }
- }
- }
- mutex_unlock(&goodix_modules.mutex);
- /* reset device or power on*/
- if (hw_ops->resume)
- hw_ops->resume(core_data);
- mutex_lock(&goodix_modules.mutex);
- if (!list_empty(&goodix_modules.head)) {
- list_for_each_entry_safe(ext_module, next,
- &goodix_modules.head, list) {
- if (!ext_module->funcs->after_resume)
- continue;
- ret = ext_module->funcs->after_resume(core_data,
- ext_module);
- if (ret == EVT_CANCEL_RESUME) {
- mutex_unlock(&goodix_modules.mutex);
- ts_info("Canceled by module:%s",
- ext_module->name);
- goto out;
- }
- }
- }
- mutex_unlock(&goodix_modules.mutex);
- out:
- /* enable irq */
- hw_ops->irq_enable(core_data, true);
- /* open esd */
- goodix_ts_blocking_notify(NOTIFY_RESUME, NULL);
- ts_info("Resume end");
- return 0;
- }
- #ifdef CONFIG_FB
- /**
- * goodix_ts_fb_notifier_callback - Framebuffer notifier callback
- * Called by kernel during framebuffer blanck/unblank phrase
- */
- int goodix_ts_fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data)
- {
- struct goodix_ts_core *core_data =
- container_of(self, struct goodix_ts_core, fb_notifier);
- struct fb_event *fb_event = data;
- if (fb_event && fb_event->data && core_data) {
- if (event == FB_EARLY_EVENT_BLANK) {
- /* before fb blank */
- } else if (event == FB_EVENT_BLANK) {
- int *blank = fb_event->data;
- if (*blank == FB_BLANK_UNBLANK)
- goodix_ts_resume(core_data);
- else if (*blank == FB_BLANK_POWERDOWN)
- goodix_ts_suspend(core_data);
- }
- }
- return 0;
- }
- #endif
- #ifdef CONFIG_PM
- #if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)
- /**
- * goodix_ts_pm_suspend - PM suspend function
- * Called by kernel during system suspend phrase
- */
- static int goodix_ts_pm_suspend(struct device *dev)
- {
- struct goodix_ts_core *core_data =
- dev_get_drvdata(dev);
- return goodix_ts_suspend(core_data);
- }
- /**
- * goodix_ts_pm_resume - PM resume function
- * Called by kernel during system wakeup
- */
- static int goodix_ts_pm_resume(struct device *dev)
- {
- struct goodix_ts_core *core_data =
- dev_get_drvdata(dev);
- return goodix_ts_resume(core_data);
- }
- #endif
- #endif
- /**
- * goodix_generic_noti_callback - generic notifier callback
- * for goodix touch notification event.
- */
- static int goodix_generic_noti_callback(struct notifier_block *self,
- unsigned long action, void *data)
- {
- struct goodix_ts_core *cd = container_of(self,
- struct goodix_ts_core, ts_notifier);
- const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
- if (cd->init_stage < CORE_INIT_STAGE2)
- return 0;
- ts_info("notify event type 0x%x", (unsigned int)action);
- switch (action) {
- case NOTIFY_FWUPDATE_START:
- hw_ops->irq_enable(cd, 0);
- break;
- case NOTIFY_FWUPDATE_SUCCESS:
- case NOTIFY_FWUPDATE_FAILED:
- if (hw_ops->read_version(cd, &cd->fw_version))
- ts_info("failed read fw version info[ignore]");
- hw_ops->irq_enable(cd, 1);
- break;
- default:
- break;
- }
- return 0;
- }
- int goodix_ts_stage2_init(struct goodix_ts_core *cd)
- {
- int ret;
- /* alloc/config/register input device */
- ret = goodix_ts_input_dev_config(cd);
- if (ret < 0) {
- ts_err("failed set input device");
- return ret;
- }
- if (cd->board_data.pen_enable) {
- ret = goodix_ts_pen_dev_config(cd);
- if (ret < 0) {
- ts_err("failed set pen device");
- goto err_finger;
- }
- }
- /* request irq line */
- ret = goodix_ts_irq_setup(cd);
- if (ret < 0) {
- ts_info("failed set irq");
- goto exit;
- }
- ts_info("success register irq");
- #ifdef CONFIG_FB
- cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback;
- if (fb_register_client(&cd->fb_notifier))
- ts_err("Failed to register fb notifier client:%d", ret);
- #endif
- /* create sysfs files */
- goodix_ts_sysfs_init(cd);
- /* create procfs files */
- goodix_ts_procfs_init(cd);
- /* esd protector */
- goodix_ts_esd_init(cd);
- /* gesture init */
- gesture_module_init();
- /* inspect init */
- inspect_module_init();
- return 0;
- exit:
- goodix_ts_pen_dev_remove(cd);
- err_finger:
- goodix_ts_input_dev_remove(cd);
- return ret;
- }
- /* try send the config specified with type */
- static int goodix_send_ic_config(struct goodix_ts_core *cd, int type)
- {
- u32 config_id;
- struct goodix_ic_config *cfg;
- if (type >= GOODIX_MAX_CONFIG_GROUP) {
- ts_err("unsupproted config type %d", type);
- return -EINVAL;
- }
- cfg = cd->ic_configs[type];
- if (!cfg || cfg->len <= 0) {
- ts_info("no valid normal config found");
- return -EINVAL;
- }
- config_id = goodix_get_file_config_id(cfg->data);
- if (cd->ic_info.version.config_id == config_id) {
- ts_info("config id is equal 0x%x, skiped", config_id);
- return 0;
- }
- ts_info("try send config, id=0x%x", config_id);
- return cd->hw_ops->send_config(cd, cfg->data, cfg->len);
- }
- /**
- * goodix_later_init_thread - init IC fw and config
- * @data: point to goodix_ts_core
- *
- * This function respond for get fw version and try upgrade fw and config.
- * Note: when init encounter error, need release all resource allocated here.
- */
- static int goodix_later_init_thread(void *data)
- {
- int ret, i;
- int update_flag = UPDATE_MODE_BLOCK | UPDATE_MODE_SRC_REQUEST;
- struct goodix_ts_core *cd = data;
- struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
- /* step 1: read version */
- ret = cd->hw_ops->read_version(cd, &cd->fw_version);
- if (ret < 0) {
- ts_err("failed to get version info, try to upgrade");
- update_flag |= UPDATE_MODE_FORCE;
- goto upgrade;
- }
- /* step 2: get config data from config bin */
- ret = goodix_get_config_proc(cd);
- if (ret)
- ts_info("no valid ic config found");
- else
- ts_info("success get valid ic config");
- upgrade:
- /* step 3: init fw struct add try do fw upgrade */
- ret = goodix_fw_update_init(cd);
- if (ret) {
- ts_err("failed init fw update module");
- goto err_out;
- }
- ts_info("update flag: 0x%X", update_flag);
- ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL], update_flag);
- if (ret)
- ts_err("failed do fw update");
- /* step 4: get fw version and ic_info
- * at this step we believe that the ic is in normal mode,
- * if the version info is invalid there must have some
- * problem we cann't cover so exit init directly.
- */
- ret = hw_ops->read_version(cd, &cd->fw_version);
- if (ret) {
- ts_err("invalid fw version, abort");
- goto uninit_fw;
- }
- ret = hw_ops->get_ic_info(cd, &cd->ic_info);
- if (ret) {
- ts_err("invalid ic info, abort");
- goto uninit_fw;
- }
- /* the recomend way to update ic config is throuth ISP,
- * if not we will send config with interactive mode
- */
- goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL);
- /* init other resources */
- ret = goodix_ts_stage2_init(cd);
- if (ret) {
- ts_err("stage2 init failed");
- goto uninit_fw;
- }
- cd->init_stage = CORE_INIT_STAGE2;
- return 0;
- uninit_fw:
- goodix_fw_update_uninit();
- err_out:
- ts_err("stage2 init failed");
- cd->init_stage = CORE_INIT_FAIL;
- for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) {
- if (cd->ic_configs[i])
- kfree(cd->ic_configs[i]);
- cd->ic_configs[i] = NULL;
- }
- return ret;
- }
- static int goodix_start_later_init(struct goodix_ts_core *ts_core)
- {
- struct task_struct *init_thrd;
- /* create and run update thread */
- init_thrd = kthread_run(goodix_later_init_thread,
- ts_core, "goodix_init_thread");
- if (IS_ERR_OR_NULL(init_thrd)) {
- ts_err("Failed to create update thread:%ld",
- PTR_ERR(init_thrd));
- return -EFAULT;
- }
- return 0;
- }
- /**
- * goodix_ts_probe - called by kernel when Goodix touch
- * platform driver is added.
- */
- static int goodix_ts_probe(struct platform_device *pdev)
- {
- struct goodix_ts_core *core_data = NULL;
- struct goodix_bus_interface *bus_interface;
- int ret;
- ts_info("goodix_ts_probe IN");
- bus_interface = pdev->dev.platform_data;
- if (!bus_interface) {
- ts_err("Invalid touch device");
- core_module_prob_sate = CORE_MODULE_PROB_FAILED;
- return -ENODEV;
- }
- core_data = devm_kzalloc(&pdev->dev,
- sizeof(struct goodix_ts_core), GFP_KERNEL);
- if (!core_data) {
- ts_err("Failed to allocate memory for core data");
- core_module_prob_sate = CORE_MODULE_PROB_FAILED;
- return -ENOMEM;
- }
- if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) {
- /* parse devicetree property */
- ret = goodix_parse_dt(bus_interface->dev->of_node,
- &core_data->board_data);
- if (ret) {
- ts_err("failed parse device info form dts, %d", ret);
- return -EINVAL;
- }
- } else {
- ts_err("no valid device tree node found");
- return -ENODEV;
- }
- core_data->hw_ops = goodix_get_hw_ops();
- if (!core_data->hw_ops) {
- ts_err("hw ops is NULL");
- core_module_prob_sate = CORE_MODULE_PROB_FAILED;
- return -EINVAL;
- }
- goodix_core_module_init();
- /* touch core layer is a platform driver */
- core_data->pdev = pdev;
- core_data->bus = bus_interface;
- platform_set_drvdata(pdev, core_data);
- /* get GPIO resource */
- ret = goodix_ts_gpio_setup(core_data);
- if (ret) {
- ts_err("failed init gpio");
- goto err_out;
- }
- ret = goodix_ts_power_init(core_data);
- if (ret) {
- ts_err("failed init power");
- goto err_out;
- }
- ret = goodix_ts_power_on(core_data);
- if (ret) {
- ts_err("failed power on");
- goto err_out;
- }
- /* generic notifier callback */
- core_data->ts_notifier.notifier_call = goodix_generic_noti_callback;
- goodix_ts_register_notifier(&core_data->ts_notifier);
- /* debug node init */
- goodix_tools_init();
- core_data->init_stage = CORE_INIT_STAGE1;
- goodix_modules.core_data = core_data;
- core_module_prob_sate = CORE_MODULE_PROB_SUCCESS;
- /* Try start a thread to get config-bin info */
- goodix_start_later_init(core_data);
- ts_info("goodix_ts_core probe success");
- return 0;
- err_out:
- core_data->init_stage = CORE_INIT_FAIL;
- core_module_prob_sate = CORE_MODULE_PROB_FAILED;
- ts_err("goodix_ts_core failed, ret:%d", ret);
- return ret;
- }
- static int goodix_ts_remove(struct platform_device *pdev)
- {
- struct goodix_ts_core *core_data = platform_get_drvdata(pdev);
- struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
- struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
- goodix_ts_unregister_notifier(&core_data->ts_notifier);
- goodix_tools_exit();
- if (core_data->init_stage >= CORE_INIT_STAGE2) {
- gesture_module_exit();
- inspect_module_exit();
- hw_ops->irq_enable(core_data, false);
- #ifdef CONFIG_FB
- fb_unregister_client(&core_data->fb_notifier);
- #endif
- core_module_prob_sate = CORE_MODULE_REMOVED;
- if (atomic_read(&core_data->ts_esd.esd_on))
- goodix_ts_esd_off(core_data);
- goodix_ts_unregister_notifier(&ts_esd->esd_notifier);
- goodix_fw_update_uninit();
- goodix_ts_input_dev_remove(core_data);
- goodix_ts_pen_dev_remove(core_data);
- goodix_ts_sysfs_exit(core_data);
- goodix_ts_procfs_exit(core_data);
- goodix_ts_power_off(core_data);
- }
- return 0;
- }
- #ifdef CONFIG_PM
- static const struct dev_pm_ops dev_pm_ops = {
- #if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)
- .suspend = goodix_ts_pm_suspend,
- .resume = goodix_ts_pm_resume,
- #endif
- };
- #endif
- static const struct platform_device_id ts_core_ids[] = {
- {.name = GOODIX_CORE_DRIVER_NAME},
- {}
- };
- MODULE_DEVICE_TABLE(platform, ts_core_ids);
- static struct platform_driver goodix_ts_driver = {
- .driver = {
- .name = GOODIX_CORE_DRIVER_NAME,
- .owner = THIS_MODULE,
- #ifdef CONFIG_PM
- .pm = &dev_pm_ops,
- #endif
- },
- .probe = goodix_ts_probe,
- .remove = goodix_ts_remove,
- .id_table = ts_core_ids,
- };
- static int __init goodix_ts_core_init(void)
- {
- int ret;
- ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION);
- #ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI
- ret = goodix_spi_bus_init();
- #else
- ret = goodix_i2c_bus_init();
- #endif
- if (ret) {
- ts_err("failed add bus driver");
- return ret;
- }
- return platform_driver_register(&goodix_ts_driver);
- }
- static void __exit goodix_ts_core_exit(void)
- {
- ts_info("Core layer exit");
- platform_driver_unregister(&goodix_ts_driver);
- #ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI
- goodix_spi_bus_exit();
- #else
- goodix_i2c_bus_exit();
- #endif
- }
- late_initcall(goodix_ts_core_init);
- module_exit(goodix_ts_core_exit);
- MODULE_DESCRIPTION("Goodix Touchscreen Core Module");
- MODULE_AUTHOR("Goodix, Inc.");
- MODULE_LICENSE("GPL v2");
|