/* * pt_btn.c * Parade TrueTouch(TM) Standard Product CapSense Reports Module. * For use with Parade touchscreen controllers. * Supported parts include: * TMA5XX * TMA448 * TMA445A * TT21XXX * TT31XXX * TT4XXXX * TT7XXX * TC3XXX * * Copyright (C) 2015-2020 Parade Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2, and only version 2, as published by the * Free Software Foundation. * * 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. * * Contact Parade Technologies at www.paradetech.com */ #include "pt_regs.h" /******************************************************************************* * FUNCTION: pt_btn_key_action * * SUMMARY: Reports key event * * PARAMETERS: * *bd - pointer to button data structure * btn_no - number of button * btn_state - state of button ******************************************************************************/ static inline void pt_btn_key_action(struct pt_btn_data *bd, int btn_no, int btn_state) { struct device *dev = bd->dev; struct pt_sysinfo *si = bd->si; if (!si->btn[btn_no].enabled || si->btn[btn_no].state == btn_state) return; si->btn[btn_no].state = btn_state; input_report_key(bd->input, si->btn[btn_no].key_code, btn_state); input_sync(bd->input); pt_debug(dev, DL_INFO, "%s: btn=%d key_code=%d %s\n", __func__, btn_no, si->btn[btn_no].key_code, btn_state == PT_BTN_PRESSED ? "PRESSED" : "RELEASED"); } /******************************************************************************* * FUNCTION: pt_get_btn_touches * * SUMMARY: Parse and report key event * * PARAMETERS: * *bd - pointer to button data structure ******************************************************************************/ static void pt_get_btn_touches(struct pt_btn_data *bd) { struct pt_sysinfo *si = bd->si; int num_btns = si->num_btns; int cur_btn; int cur_btn_state; for (cur_btn = 0; cur_btn < num_btns; cur_btn++) { /* Get current button state */ cur_btn_state = (si->xy_data[0] >> (cur_btn * PT_BITS_PER_BTN)) & PT_NUM_BTN_EVENT_ID; pt_btn_key_action(bd, cur_btn, cur_btn_state); } } /******************************************************************************* * FUNCTION: pt_btn_lift_all * * SUMMARY: Reports button liftoff action * * PARAMETERS: * *bd - pointer to button data structure ******************************************************************************/ static void pt_btn_lift_all(struct pt_btn_data *bd) { struct pt_sysinfo *si = bd->si; int i; if (!si || si->num_btns == 0) return; for (i = 0; i < si->num_btns; i++) pt_btn_key_action(bd, i, PT_BTN_RELEASED); } /******************************************************************************* * FUNCTION: pt_xy_worker * * SUMMARY: Read xy_data for all current CapSense button touches * * RETURN: * 0 = success * * PARAMETERS: * *bd - pointer to button data structure ******************************************************************************/ static int pt_xy_worker(struct pt_btn_data *bd) { struct pt_sysinfo *si = bd->si; /* extract button press/release touch information */ if (si->num_btns > 0) pt_get_btn_touches(bd); return 0; } /******************************************************************************* * FUNCTION: pt_btn_attention * * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention * list. * * RETURN: * 0 = success * !0 = failure * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_btn_attention(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; int rc; if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id) return 0; /* core handles handshake */ mutex_lock(&bd->btn_lock); rc = pt_xy_worker(bd); mutex_unlock(&bd->btn_lock); if (rc < 0) pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n", __func__, rc); return rc; } /******************************************************************************* * FUNCTION: pt_startup_attention * * SUMMARY: Wrapper function for pt_btn_lift_all() that register to TTDL * attention list. * * RETURN: * 0 = success * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_startup_attention(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; mutex_lock(&bd->btn_lock); pt_btn_lift_all(bd); mutex_unlock(&bd->btn_lock); return 0; } /******************************************************************************* * FUNCTION: pt_btn_suspend_attention * * SUMMARY: Function for button to enter suspend state that as following steps: * 1) Lift all button * 2) Set flag with suspend state * 3) Decrese pm system count * * RETURN: * 0 = success * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_btn_suspend_attention(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; mutex_lock(&bd->btn_lock); pt_btn_lift_all(bd); bd->is_suspended = true; mutex_unlock(&bd->btn_lock); pm_runtime_put(dev); return 0; } /******************************************************************************* * FUNCTION: pt_btn_resume_attention * * SUMMARY: Function for button to leave suspend state that as following steps: * 1) Increse pm system count * 2) Clear suspend state flag * * RETURN: * 0 = success * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_btn_resume_attention(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; pm_runtime_get(dev); mutex_lock(&bd->btn_lock); bd->is_suspended = false; mutex_unlock(&bd->btn_lock); return 0; } /******************************************************************************* * FUNCTION: pt_btn_open * * SUMMARY: Open method for input device(button) that sets up call back * functions to TTDL attention list * * RETURN: * 0 = success * * PARAMETERS: * *input - pointer to input_dev structure ******************************************************************************/ static int pt_btn_open(struct input_dev *input) { struct device *dev = input->dev.parent; struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; pm_runtime_get_sync(dev); mutex_lock(&bd->btn_lock); bd->is_suspended = false; mutex_unlock(&bd->btn_lock); pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__); /* set up touch call back */ _pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME, pt_btn_attention, PT_MODE_OPERATIONAL); /* set up startup call back */ _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, pt_startup_attention, 0); /* set up suspend call back */ _pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME, pt_btn_suspend_attention, 0); /* set up resume call back */ _pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME, pt_btn_resume_attention, 0); return 0; } /******************************************************************************* * FUNCTION: pt_btn_close * * SUMMARY: Close method for input device(button) that clears call back * functions from TTDL attention list. * * PARAMETERS: * *input - pointer to input_dev structure ******************************************************************************/ static void pt_btn_close(struct input_dev *input) { struct device *dev = input->dev.parent; struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; _pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME, pt_btn_attention, PT_MODE_OPERATIONAL); _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, pt_startup_attention, 0); _pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME, pt_btn_suspend_attention, 0); _pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME, pt_btn_resume_attention, 0); mutex_lock(&bd->btn_lock); if (!bd->is_suspended) { pm_runtime_put(dev); bd->is_suspended = true; } mutex_unlock(&bd->btn_lock); } /******************************************************************************* * FUNCTION: pt_setup_input_device * * SUMMARY: Set up resolution, event signal capabilities and register input * device for button. * * RETURN: * 0 = success * !0 = failure * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_setup_input_device(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; int i; int rc; pt_debug(dev, DL_INFO, "%s: Initialize event signals\n", __func__); __set_bit(EV_KEY, bd->input->evbit); pt_debug(dev, DL_INFO, "%s: Number of buttons %d\n", __func__, bd->si->num_btns); for (i = 0; i < bd->si->num_btns; i++) { pt_debug(dev, DL_INFO, "%s: btn:%d keycode:%d\n", __func__, i, bd->si->btn[i].key_code); __set_bit(bd->si->btn[i].key_code, bd->input->keybit); } rc = input_register_device(bd->input); if (rc < 0) pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n", __func__, rc); else bd->input_device_registered = true; return rc; } /******************************************************************************* * FUNCTION: pt_setup_input_attention * * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL * attention list. * * RETURN: * 0 = success * !0 = failure * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ static int pt_setup_input_attention(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; int rc; bd->si = _pt_request_sysinfo(dev); if (!bd->si) return -EPERM; rc = pt_setup_input_device(dev); _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, pt_setup_input_attention, 0); return rc; } /******************************************************************************* * FUNCTION: pt_btn_probe * * SUMMARY: The probe function for button input device * * RETURN: * 0 = success * !0 = failure * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ int pt_btn_probe(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_btn_data *bd = &cd->bd; struct pt_platform_data *pdata = dev_get_platdata(dev); struct pt_btn_platform_data *btn_pdata; int rc = 0; if (!pdata || !pdata->btn_pdata) { pt_debug(dev, DL_ERROR, "%s: Missing platform data\n", __func__); rc = -ENODEV; goto error_no_pdata; } btn_pdata = pdata->btn_pdata; mutex_init(&bd->btn_lock); bd->dev = dev; bd->pdata = btn_pdata; /* Create the input device and register it. */ pt_debug(dev, DL_INFO, "%s: Create the input device and register it\n", __func__); bd->input = input_allocate_device(); if (!bd->input) { pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n", __func__); rc = -ENODEV; goto error_alloc_failed; } else bd->input_device_allocated = true; if (bd->pdata->inp_dev_name) bd->input->name = bd->pdata->inp_dev_name; else bd->input->name = PT_BTN_NAME; scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev), cd->phys_num++); bd->input->phys = bd->phys; bd->input->dev.parent = bd->dev; bd->input->open = pt_btn_open; bd->input->close = pt_btn_close; input_set_drvdata(bd->input, bd); /* get sysinfo */ bd->si = _pt_request_sysinfo(dev); if (bd->si) { rc = pt_setup_input_device(dev); if (rc) goto error_init_input; } else { pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n", __func__, bd->si); _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, pt_setup_input_attention, 0); } return 0; error_init_input: input_free_device(bd->input); bd->input_device_allocated = false; error_alloc_failed: error_no_pdata: pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); return rc; } /******************************************************************************* * FUNCTION: pt_btn_release * * SUMMARY: The release function for button input device * * RETURN: * 0 = success * * PARAMETERS: * *dev - pointer to device structure ******************************************************************************/ int pt_btn_release(struct device *dev) { struct pt_core_data *cd; struct pt_btn_data *bd; /* Ensure valid pointers before de-referencing them */ if (dev) { cd = dev_get_drvdata(dev); if (cd) bd = &cd->bd; else return 0; } else { return 0; } /* * Second call this function may cause kernel panic if probe fail. * Use input_device_registered & input_device_allocated variable to * avoid unregister or free unavailable devive. */ if (bd && bd->input_device_registered) { bd->input_device_registered = false; input_unregister_device(bd->input); /* Unregistering device will free the device too */ bd->input_device_allocated = false; } else if (bd && bd->input_device_allocated) { bd->input_device_allocated = false; input_free_device(bd->input); _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, pt_setup_input_attention, 0); } return 0; }