Files
android_kernel_samsung_sm86…/pt/pt_pen.c
Parade-Github b0e4b1c194 touch : Add files via upload
Removed example files and move source to /pt/
Git-commit: e2e4bb9eacc2406cb0351d15e478e68249d5f625
Git-repo: https://github.com/Parade-Github/TTDL/tree/main

Change-Id: I07f3464718058d5bba76b7d5be486a79934db354
Signed-off-by: Surya Teja Kudiri <quic_skudiri@quicinc.com>
2022-09-08 22:27:01 -07:00

573 lines
15 KiB
C

#ifndef TTDL_KERNEL_SUBMISSION
/*
* pt_pen.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-2021 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 <ttdrivers@paradetech.com>
*/
#include "pt_regs.h"
/*******************************************************************************
* FUNCTION: pt_pen_lift_all
*
* SUMMARY: Reports pen liftoff action
*
* PARAMETERS:
* *pend - pointer to pen data structure
******************************************************************************/
static void pt_pen_lift_all(struct pt_pen_data *pend)
{
input_report_key(pend->input, BTN_STYLUS, 0);
input_report_key(pend->input, BTN_STYLUS2, 0);
input_report_key(pend->input, BTN_TOUCH, 0);
input_report_key(pend->input, BTN_TOOL_PEN, 0);
input_sync(pend->input);
}
/*******************************************************************************
* FUNCTION: pt_get_pen_data
*
* SUMMARY: Gets axis of pen report
*
* PARAMETERS:
* *pend - pointer to pen data structure
* *xy_data - pointer to touch data
******************************************************************************/
static void pt_get_pen(struct pt_pen_data *pend,
struct pt_pen *pen, u8 *xy_data)
{
struct device *dev = pend->dev;
struct pt_sysinfo *si = pend->si;
enum pt_pen_abs abs;
for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) {
if (!si->pen_abs[abs].report)
continue;
pt_get_touch_field(dev, &pen->abs[abs],
si->pen_abs[abs].size,
si->pen_abs[abs].max,
xy_data + si->tch_abs[abs].ofs,
si->pen_abs[abs].bofs);
pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
__func__, pt_pen_abs_string[abs],
pen->abs[abs], pen->abs[abs]);
}
}
/*******************************************************************************
* FUNCTION: pt_xy_worker
*
* SUMMARY: Read xy_data for current pen touch
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *pend - pointer to pen data structure
******************************************************************************/
static int pt_xy_worker(struct pt_pen_data *pend)
{
struct pt_sysinfo *si = pend->si;
struct pt_pen pen;
bool tool;
pt_get_pen(pend, &pen, si->xy_data + 3);
tool = pen.abs[PT_PEN_IV] ?
BTN_TOOL_RUBBER : BTN_TOOL_PEN;
input_report_abs(pend->input, ABS_X, pen.abs[PT_PEN_X]);
input_report_abs(pend->input, ABS_Y, pen.abs[PT_PEN_Y]);
input_report_abs(pend->input, ABS_PRESSURE, pen.abs[PT_PEN_P]);
input_report_key(pend->input, BTN_STYLUS, pen.abs[PT_PEN_BS]);
if (si->pen_abs[PT_PEN_2ND_BS].report)
input_report_key(pend->input, BTN_STYLUS2,
pen.abs[PT_PEN_2ND_BS]);
input_report_key(pend->input, BTN_TOUCH, pen.abs[PT_PEN_TS]);
input_report_key(pend->input, tool, pen.abs[PT_PEN_IR]);
if (si->pen_abs[PT_PEN_X_TILT].report)
input_report_abs(pend->input, ABS_TILT_X,
pen.abs[PT_PEN_X_TILT]);
if (si->pen_abs[PT_PEN_Y_TILT].report)
input_report_abs(pend->input, ABS_TILT_Y,
pen.abs[PT_PEN_Y_TILT]);
input_sync(pend->input);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pen_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_pen_attention(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
int rc;
if (pend->si->xy_mode[2] != pend->si->desc.pen_report_id)
return 0;
/* core handles handshake */
mutex_lock(&pend->pen_lock);
rc = pt_xy_worker(pend);
mutex_unlock(&pend->pen_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_pen_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_pen_data *pend = &cd->pend;
mutex_lock(&pend->pen_lock);
pt_pen_lift_all(pend);
mutex_unlock(&pend->pen_lock);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pen_suspend_attention
*
* SUMMARY: Function for pen to enter suspend state that as following steps:
* 1) Lift pen touch
* 2) Set flag with suspend state
* 3) Decrese pm system count
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int pt_pen_suspend_attention(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
mutex_lock(&pend->pen_lock);
pt_pen_lift_all(pend);
pend->is_suspended = true;
mutex_unlock(&pend->pen_lock);
pm_runtime_put(dev);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pen_resume_attention
*
* SUMMARY: Function for pen 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_pen_resume_attention(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
pm_runtime_get(dev);
mutex_lock(&pend->pen_lock);
pend->is_suspended = false;
mutex_unlock(&pend->pen_lock);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pen_open
*
* SUMMARY: Open method for input device(pen) that sets up call back
* functions to TTDL attention list
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *input - pointer to input_dev structure
******************************************************************************/
static int pt_pen_open(struct input_dev *input)
{
struct device *dev = input->dev.parent;
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
pm_runtime_get_sync(dev);
mutex_lock(&pend->pen_lock);
pend->is_suspended = false;
mutex_unlock(&pend->pen_lock);
pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
/* set up touch call back */
_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
pt_pen_attention, PT_MODE_OPERATIONAL);
/* set up startup call back */
_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
pt_startup_attention, 0);
/* set up suspend call back */
_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
pt_pen_suspend_attention, 0);
/* set up resume call back */
_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
pt_pen_resume_attention, 0);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pen_close
*
* SUMMARY: Close method for input device(pen) that clears call back
* functions from TTDL attention list.
*
* PARAMETERS:
* *input - pointer to input_dev structure
******************************************************************************/
static void pt_pen_close(struct input_dev *input)
{
struct device *dev = input->dev.parent;
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
pt_pen_attention, PT_MODE_OPERATIONAL);
_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
pt_startup_attention, 0);
_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
pt_pen_suspend_attention, 0);
_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
pt_pen_resume_attention, 0);
mutex_lock(&pend->pen_lock);
if (!pend->is_suspended) {
pm_runtime_put(dev);
pend->is_suspended = true;
}
mutex_unlock(&pend->pen_lock);
}
/*******************************************************************************
* FUNCTION: pt_setup_input_device
*
* SUMMARY: Set up resolution, event signal capabilities and register input
* device for pen.
*
* 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_pen_data *pend = &cd->pend;
int i;
int rc;
u32 usage;
int max_x, max_y, max_p;
pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
__func__);
__set_bit(EV_ABS, pend->input->evbit);
__set_bit(EV_KEY, pend->input->evbit);
for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) {
usage = pt_pen_abs_field_map[i];
switch (usage) {
case HID_GD_X:
max_x = pend->si->sensing_conf_data.res_x;
input_set_abs_params(pend->input,
ABS_X, 0, max_x, 0, 0);
break;
case HID_GD_Y:
max_y = pend->si->sensing_conf_data.res_y;
input_set_abs_params(pend->input,
ABS_Y, 0, max_y, 0, 0);
break;
case HID_DG_TIPPRESSURE:
max_p = pend->si->sensing_conf_data.max_z;
input_set_abs_params(pend->input,
ABS_PRESSURE, 0, max_p, 0, 0);
break;
case HID_DG_INRANGE:
input_set_capability(pend->input,
EV_KEY, BTN_TOOL_PEN);
break;
case HID_DG_INVERT:
input_set_capability(pend->input,
EV_KEY, BTN_TOOL_RUBBER);
break;
case HID_DG_TILT_X:
max_x = pend->si->pen_abs[i].max;
input_set_abs_params(pend->input,
ABS_TILT_X, 0, max_x, 0, 0);
break;
case HID_DG_TILT_Y:
max_x = pend->si->pen_abs[i].max;
input_set_abs_params(pend->input,
ABS_TILT_Y, 0, max_x, 0, 0);
break;
case HID_DG_ERASER:
case HID_DG_TIPSWITCH:
input_set_capability(pend->input,
EV_KEY, BTN_TOUCH);
break;
case HID_DG_BARRELSWITCH:
input_set_capability(pend->input,
EV_KEY, BTN_STYLUS);
break;
case HID_DG_BARRELSWITCH2:
input_set_capability(pend->input,
EV_KEY, BTN_STYLUS2);
break;
}
}
rc = input_register_device(pend->input);
if (rc < 0)
pt_debug(dev, DL_ERROR,
"%s: Error, failed register input device r=%d\n",
__func__, rc);
else
pend->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_pen_data *pend = &cd->pend;
int rc;
pend->si = _pt_request_sysinfo(dev);
if (!pend->si)
return -1;
rc = pt_setup_input_device(dev);
_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
pt_setup_input_attention, 0);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pen_probe
*
* SUMMARY: The probe function for pen input device
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
int pt_pen_probe(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pen_data *pend = &cd->pend;
struct pt_platform_data *pdata = dev_get_platdata(dev);
struct pt_pen_platform_data *pen_pdata;
int rc = 0;
if (!pdata || !pdata->pen_pdata) {
pt_debug(dev, DL_ERROR,
"%s: Missing platform data\n", __func__);
rc = -ENODEV;
goto error_no_pdata;
}
pen_pdata = pdata->pen_pdata;
mutex_init(&pend->pen_lock);
pend->dev = dev;
pend->pdata = pen_pdata;
/* Create the input device and register it. */
pt_debug(dev, DL_INFO,
"%s: Create the input device and register it\n", __func__);
pend->input = input_allocate_device();
if (!pend->input) {
pt_debug(dev, DL_ERROR,
"%s: Error, failed to allocate input device\n",
__func__);
rc = -ENODEV;
goto error_alloc_failed;
} else
pend->input_device_allocated = true;
if (pend->pdata->inp_dev_name)
pend->input->name = pend->pdata->inp_dev_name;
else
pend->input->name = PT_PEN_NAME;
scnprintf(pend->phys, sizeof(pend->phys), "%s/input%d", dev_name(dev),
cd->phys_num++);
pend->input->phys = pend->phys;
pend->input->dev.parent = pend->dev;
pend->input->open = pt_pen_open;
pend->input->close = pt_pen_close;
input_set_drvdata(pend->input, pend);
/* get sysinfo */
pend->si = _pt_request_sysinfo(dev);
if (pend->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__, pend->si);
_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
PT_PEN_NAME, pt_setup_input_attention, 0);
}
return 0;
error_init_input:
input_free_device(pend->input);
pend->input_device_allocated = false;
error_alloc_failed:
error_no_pdata:
pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pen_release
*
* SUMMARY: The release function for pen input device
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
int pt_pen_release(struct device *dev)
{
struct pt_core_data *cd;
struct pt_pen_data *pend;
/* Ensure valid pointers before de-referencing them */
if (dev) {
cd = dev_get_drvdata(dev);
if (cd)
pend = &cd->pend;
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 (pend && pend->input_device_registered) {
pend->input_device_registered = false;
input_unregister_device(pend->input);
/* Unregistering device will free the device too */
pend->input_device_allocated = false;
} else if (pend && pend->input_device_allocated) {
pend->input_device_allocated = false;
input_free_device(pend->input);
_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
PT_PEN_NAME, pt_setup_input_attention, 0);
}
return 0;
}
#endif /*!TTDL_KERNEL_SUBMISSION */