blob: d594b74a201308c5653b912b22d138c54bd79200 [file]
// SPDX-License-Identifier: GPL-2.0-only
/*
* pt_platform.c
* Parade TrueTouch(TM) Standard Product Platform 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 <ttdrivers@paradetech.com>
*/
#include "pt_regs.h"
#include "pt_platform.h"
#include <linux/regulator/consumer.h>
#ifdef PT_PTSBC_SUPPORT
#include <linux/init-input.h>
#endif
#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
/* FW for Panel ID = 0x00 */
#include "pt_fw_pid00.h"
static struct pt_touch_firmware pt_firmware_pid00 = {
.img = pt_img_pid00,
.size = ARRAY_SIZE(pt_img_pid00),
.ver = pt_ver_pid00,
.vsize = ARRAY_SIZE(pt_ver_pid00),
.panel_id = 0x00,
};
/* FW for Panel ID = 0x01 */
#include "pt_fw_pid01.h"
static struct pt_touch_firmware pt_firmware_pid01 = {
.img = pt_img_pid01,
.size = ARRAY_SIZE(pt_img_pid01),
.ver = pt_ver_pid01,
.vsize = ARRAY_SIZE(pt_ver_pid01),
.panel_id = 0x01,
};
/* FW for Panel ID not enabled (legacy) */
#include "pt_fw.h"
static struct pt_touch_firmware pt_firmware = {
.img = pt_img,
.size = ARRAY_SIZE(pt_img),
.ver = pt_ver,
.vsize = ARRAY_SIZE(pt_ver),
};
#else
/* FW for Panel ID not enabled (legacy) */
static struct pt_touch_firmware pt_firmware = {
.img = NULL,
.size = 0,
.ver = NULL,
.vsize = 0,
};
#endif
#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
/* TT Config for Panel ID = 0x00 */
#include "pt_params_pid00.h"
static struct touch_settings pt_sett_param_regs_pid00 = {
.data = (uint8_t *)&pt_param_regs_pid00[0],
.size = ARRAY_SIZE(pt_param_regs_pid00),
.tag = 0,
};
static struct touch_settings pt_sett_param_size_pid00 = {
.data = (uint8_t *)&pt_param_size_pid00[0],
.size = ARRAY_SIZE(pt_param_size_pid00),
.tag = 0,
};
static struct pt_touch_config pt_ttconfig_pid00 = {
.param_regs = &pt_sett_param_regs_pid00,
.param_size = &pt_sett_param_size_pid00,
.fw_ver = ttconfig_fw_ver_pid00,
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00),
.panel_id = 0x00,
};
/* TT Config for Panel ID = 0x01 */
#include "pt_params_pid01.h"
static struct touch_settings pt_sett_param_regs_pid01 = {
.data = (uint8_t *)&pt_param_regs_pid01[0],
.size = ARRAY_SIZE(pt_param_regs_pid01),
.tag = 0,
};
static struct touch_settings pt_sett_param_size_pid01 = {
.data = (uint8_t *)&pt_param_size_pid01[0],
.size = ARRAY_SIZE(pt_param_size_pid01),
.tag = 0,
};
static struct pt_touch_config pt_ttconfig_pid01 = {
.param_regs = &pt_sett_param_regs_pid01,
.param_size = &pt_sett_param_size_pid01,
.fw_ver = ttconfig_fw_ver_pid01,
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01),
.panel_id = 0x01,
};
/* TT Config for Panel ID not enabled (legacy)*/
#include "pt_params.h"
static struct touch_settings pt_sett_param_regs = {
.data = (uint8_t *)&pt_param_regs[0],
.size = ARRAY_SIZE(pt_param_regs),
.tag = 0,
};
static struct touch_settings pt_sett_param_size = {
.data = (uint8_t *)&pt_param_size[0],
.size = ARRAY_SIZE(pt_param_size),
.tag = 0,
};
static struct pt_touch_config pt_ttconfig = {
.param_regs = &pt_sett_param_regs,
.param_size = &pt_sett_param_size,
.fw_ver = ttconfig_fw_ver,
.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver),
};
#else
/* TT Config for Panel ID not enabled (legacy)*/
static struct pt_touch_config pt_ttconfig = {
.param_regs = NULL,
.param_size = NULL,
.fw_ver = NULL,
.fw_vsize = 0,
};
#endif
static struct pt_touch_firmware *pt_firmwares[] = {
#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
&pt_firmware_pid00,
&pt_firmware_pid01,
#endif
NULL, /* Last item should always be NULL */
};
static struct pt_touch_config *pt_ttconfigs[] = {
#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
&pt_ttconfig_pid00,
&pt_ttconfig_pid01,
#endif
NULL, /* Last item should always be NULL */
};
struct pt_loader_platform_data _pt_loader_platform_data = {
.fw = &pt_firmware,
.ttconfig = &pt_ttconfig,
.fws = pt_firmwares,
.ttconfigs = pt_ttconfigs,
.flags = PT_LOADER_FLAG_NONE,
};
/*******************************************************************************
* FUNCTION: pt_xres
*
* SUMMARY: Toggles the reset gpio (TP_XRES) to perform a HW reset
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
int pt_xres(struct pt_core_platform_data *pdata,
struct device *dev)
{
int rst_gpio = pdata->rst_gpio;
int ddi_rst_gpio = pdata->ddi_rst_gpio;
pt_debug(dev, DL_WARN, "%s: 20ms HARD RESET on gpio=%d\n",
__func__, pdata->rst_gpio);
/* Toggling only TP_XRES as DDI_XRES resets the entire part */
gpio_set_value(rst_gpio, 1);
if (ddi_rst_gpio)
gpio_set_value(ddi_rst_gpio, 1);
usleep_range(3000, 4000);
gpio_set_value(rst_gpio, 0);
usleep_range(6000, 7000);
gpio_set_value(rst_gpio, 1);
if (ddi_rst_gpio)
gpio_set_value(ddi_rst_gpio, 1);
/* Sleep to allow the DUT to boot */
usleep_range(3000, 4000);
return 0;
}
#ifdef PT_PINCTRL_EN
/*******************************************************************************
* FUNCTION: pt_pinctrl_init
*
* SUMMARY: Pinctrl method to obtain pin state handler for TP_RST, IRQ
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_pinctrl_init(struct pt_core_platform_data *pdata,
struct device *dev)
{
int ret = 0;
pdata->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(pdata->pinctrl)) {
pt_debug(dev, DL_ERROR,
"Failed to get pinctrl, please check dts\n");
ret = PTR_ERR(pdata->pinctrl);
goto err_pinctrl_get;
}
pdata->pins_active =
pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_active");
if (IS_ERR_OR_NULL(pdata->pins_active)) {
pt_debug(dev, DL_ERROR, "pmx_ts_active not found\n");
ret = PTR_ERR(pdata->pins_active);
goto err_pinctrl_lookup;
}
pdata->pins_suspend =
pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_suspend");
if (IS_ERR_OR_NULL(pdata->pins_suspend)) {
pt_debug(dev, DL_ERROR, "pmx_ts_suspend not found\n");
ret = PTR_ERR(pdata->pins_suspend);
goto err_pinctrl_lookup;
}
pdata->pins_release =
pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_release");
if (IS_ERR_OR_NULL(pdata->pins_release)) {
pt_debug(dev, DL_ERROR, "pmx_ts_release not found\n");
ret = PTR_ERR(pdata->pins_release);
goto err_pinctrl_lookup;
}
return 0;
err_pinctrl_lookup:
devm_pinctrl_put(pdata->pinctrl);
err_pinctrl_get:
pdata->pinctrl = NULL;
pdata->pins_release = NULL;
pdata->pins_suspend = NULL;
pdata->pins_active = NULL;
return ret;
}
/*******************************************************************************
* FUNCTION: pt_pinctrl_select_normal
*
* SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - normal
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_pinctrl_select_normal(struct pt_core_platform_data *pdata,
struct device *dev)
{
int ret = 0;
if (pdata->pinctrl && pdata->pins_active) {
ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_active);
if (ret < 0) {
pt_debug(dev, DL_ERROR,
"Set normal pin state error=%d\n", ret);
}
}
return ret;
}
/*******************************************************************************
* FUNCTION: pt_pinctrl_select_suspend
*
* SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - suspend
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_pinctrl_select_suspend(struct pt_core_platform_data *pdata,
struct device *dev)
{
int ret = 0;
if (pdata->pinctrl && pdata->pins_suspend) {
ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_suspend);
if (ret < 0) {
pt_debug(dev, DL_ERROR,
"Set suspend pin state error=%d\n", ret);
}
}
return ret;
}
/*******************************************************************************
* FUNCTION: pt_pinctrl_select_release
*
* SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - release
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_pinctrl_select_release(struct pt_core_platform_data *pdata,
struct device *dev)
{
int ret = 0;
if (pdata->pinctrl) {
if (IS_ERR_OR_NULL(pdata->pins_release)) {
devm_pinctrl_put(pdata->pinctrl);
pdata->pinctrl = NULL;
} else {
ret = pinctrl_select_state(pdata->pinctrl,
pdata->pins_release);
if (ret < 0)
pt_debug(dev, DL_ERROR,
"Set gesture pin state error=%d\n",
ret);
}
}
return ret;
}
#endif /* PT_PINCTRL_EN */
#ifdef PT_REGULATOR_EN
#define PT_VCC_MIN_UV (2800000)
#define PT_VCC_MAX_UV (3300000)
#define PT_VDDI_MIN_UV (1800000)
#define PT_VDDI_MAX_UV (1900000)
/*******************************************************************************
* FUNCTION: pt_regulator_init
*
* SUMMARY: With regulator framework to get regulator handle and setup voltage
* level.
*
* NOTE: The funtion only contains setup for VCC and VDDI since AVDD AVEE is
* usually used by TDDI products while the power is setup on other driver.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_regulator_init(struct pt_core_platform_data *pdata,
struct device *dev)
{
int rc = 0;
pdata->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(pdata->vcc)) {
rc = PTR_ERR(pdata->vcc);
pt_debug(dev, DL_ERROR, "get vcc regulator failed,rc=%d\n", rc);
return rc;
}
if (regulator_count_voltages(pdata->vcc) > 0) {
rc = regulator_set_voltage(pdata->vcc, PT_VCC_MIN_UV,
PT_VCC_MAX_UV);
if (rc) {
pt_debug(dev, DL_ERROR,
"set vcc regulator failed rc=%d\n", rc);
goto error_set_vcc;
}
}
pdata->vddi = devm_regulator_get(dev, "vddi");
if (IS_ERR(pdata->vddi)) {
rc = PTR_ERR(pdata->vddi);
pt_debug(dev, DL_ERROR,
"get vddi regulator failed,rc=%d\n", rc);
goto error_get_vcc;
}
if (regulator_count_voltages(pdata->vddi) > 0) {
rc = regulator_set_voltage(pdata->vddi, PT_VDDI_MIN_UV,
PT_VDDI_MAX_UV);
if (rc) {
pt_debug(dev, DL_ERROR,
"set vddi regulator failed rc=%d\n", rc);
goto error_get_vcc;
}
}
return 0;
error_get_vcc:
/*
* regulator_set_voltage always select minimum legal voltage between
* min_uV and max_uV. To set the minuV to 0 means to restore the default
* value of regulator. Since regulator_set_voltage is the part to
* release regulator source, it's not necessary to check the returned
* value of it.
*/
if (regulator_count_voltages(pdata->vcc) > 0)
regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV);
error_set_vcc:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_setup_power_by_regulator
*
* SUMMARY: With regulator framework to set power on/off.
*
* NOTE: The funtion only contains setup for VCC and VDDI since AVDD AVEE is
* usually used by TDDI products while the power is setup on other driver.
*
* NOTE: The timing sequence is the EXAMPLE ONLY for TT7XXX:
* power up order : VDDI, VCC
* power down order : VCC, VDDI
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
* *dev - pointer to Device structure
* *time - pointer to store how many ms this function will sleep. This is
* used to calculate how long to leave XRES asserted during pwr up
******************************************************************************/
static int pt_setup_power_by_regulator(struct pt_core_platform_data *pdata,
int on, struct device *dev, int *time)
{
int rc = 0;
if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) {
pt_debug(dev, DL_ERROR, "vddi or vcc is not valid\n");
return -EINVAL;
}
if (on == PT_MT_POWER_ON) {
rc = regulator_enable(pdata->vddi);
if (rc) {
pt_debug(dev, DL_ERROR,
"enable vddi regulator failed,rc=%d\n", rc);
}
rc = regulator_enable(pdata->vcc);
if (rc) {
pt_debug(dev, DL_ERROR,
"enable vcc regulator failed,rc=%d\n", rc);
}
/*
* Ensure the power is stable.
* This must be a min of 2ms for Gen6 but as short as
* possible for TT/TC parts.
*/
usleep_range(2000, 3000);
/*
* 2ms after enabling VDDI and VCC, change the direction of the
* RST GPIO to be an output so it will no longer float and then
* force teh DUT into reset
*/
rc = gpio_direction_output(pdata->rst_gpio, 0);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Output Setup ERROR: RST GPIO %d\n",
__func__, rst_gpio);
} else {
/* Force DUT into reset */
gpio_set_value(pdata->rst_gpio, 0);
}
/* Record the time XRES was enabled */
*time = 0;
} else {
rc = regulator_disable(pdata->vcc);
if (rc) {
pt_debug(dev, DL_ERROR,
"disable vcc regulator failed,rc=%d\n", rc);
}
rc = regulator_disable(pdata->vddi);
if (rc) {
pt_debug(dev, DL_ERROR,
"disable vddi regulator failed,rc=%d\n", rc);
}
/* Ensure the power ramp down completely */
usleep_range(10000, 12000);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_regulator_release
*
* SUMMARY: With regulator framework to release regulator resource
*
* NOTE: The regulator MUST be disabled before this call.
* NOTE: The funtion only contains setup for VCC and VDDI since AVDD AVEE is
* usually used by TDDI products while the power is setup on other driver.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
static int pt_regulator_release(struct pt_core_platform_data *pdata,
struct device *dev)
{
if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc))
return -EINVAL;
/*
* regulator_set_voltage always select minimum legal voltage between
* min_uV and max_uV. To set the minuV to 0 means to restore the default
* value of regulator. Since regulator_set_voltage is the part to
* release regulator source, it's not necessary to check the returned
* value of it.
*/
if (regulator_count_voltages(pdata->vddi) > 0)
regulator_set_voltage(pdata->vddi, 0, PT_VDDI_MAX_UV);
if (regulator_count_voltages(pdata->vcc) > 0)
regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV);
return 0;
}
#endif /* PT_REGULATOR_EN */
/*******************************************************************************
* FUNCTION: pt_init
*
* SUMMARY: Set up/free gpios for TP_RST, IRQ, DDI_RST.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* on - flag to set up or free gpios(0:free; !0:set up)
* *dev - pointer to Device structure
******************************************************************************/
int pt_init(struct pt_core_platform_data *pdata,
int on, struct device *dev)
{
int rst_gpio = pdata->rst_gpio;
int irq_gpio = pdata->irq_gpio;
int ddi_rst_gpio = pdata->ddi_rst_gpio;
int rc = 0;
if (on) {
#ifdef PT_PINCTRL_EN
rc = pt_pinctrl_init(pdata, dev);
if (!rc) {
pt_pinctrl_select_normal(pdata, dev);
} else {
pt_debug(dev, DL_ERROR,
"%s: Failed to request pinctrl\n", __func__);
}
#endif
#ifdef PT_REGULATOR_EN
rc = pt_regulator_init(pdata, dev);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed requesting regulator rc=%d\n",
__func__, rc);
}
#endif
}
if (on && rst_gpio) {
/* Configure RST GPIO */
pt_debug(dev, DL_WARN, "%s: Request RST GPIO %d\n",
__func__, rst_gpio);
rc = gpio_request(rst_gpio, NULL);
if (rc < 0) {
gpio_free(rst_gpio);
rc = gpio_request(rst_gpio, NULL);
}
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Failed requesting RST GPIO %d\n",
__func__, rst_gpio);
goto fail_rst_gpio;
} else {
/*
* The RST GPIO is typically an output but during init,
* before power is applied, set it up as an input to
* allow it to float up with VDDI when power is applied.
*/
rc = gpio_direction_input(rst_gpio);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Input Setup ERROR: RST GPIO %d\n",
__func__, rst_gpio);
goto fail_rst_gpio;
}
}
}
if (on && irq_gpio) {
/* Configure IRQ GPIO */
pt_debug(dev, DL_WARN, "%s: Request IRQ GPIO %d\n",
__func__, irq_gpio);
rc = gpio_request(irq_gpio, NULL);
if (rc < 0) {
gpio_free(irq_gpio);
rc = gpio_request(irq_gpio, NULL);
}
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Failed requesting IRQ GPIO %d\n",
__func__, irq_gpio);
goto fail_irq_gpio;
} else {
/* Set the GPIO direction */
rc = gpio_direction_input(irq_gpio);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Input Setup ERROR: IRQ GPIO %d\n",
__func__, irq_gpio);
goto fail_irq_gpio;
}
}
}
if (on && ddi_rst_gpio) {
/* Configure DDI RST GPIO */
pt_debug(dev, DL_WARN, "%s: Request DDI RST GPIO %d\n",
__func__, ddi_rst_gpio);
rc = gpio_request(ddi_rst_gpio, NULL);
if (rc < 0) {
gpio_free(ddi_rst_gpio);
rc = gpio_request(ddi_rst_gpio, NULL);
}
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Failed requesting DDI RST GPIO %d\n",
__func__, ddi_rst_gpio);
goto fail_ddi_rst_gpio;
} else {
/* Set the GPIO direction and the starting level */
rc = gpio_direction_output(ddi_rst_gpio, 0);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Output Setup ERROR: RST GPIO %d\n",
__func__, ddi_rst_gpio);
goto fail_ddi_rst_gpio;
}
}
}
if (!on) {
/* "on" not set, therefore free all gpio's */
if (ddi_rst_gpio)
gpio_free(ddi_rst_gpio);
if (irq_gpio)
gpio_free(irq_gpio);
if (rst_gpio)
gpio_free(rst_gpio);
#ifdef PT_REGULATOR_EN
pt_regulator_release(pdata, dev);
#endif
#ifdef PT_PINCTRL_EN
pt_pinctrl_select_release(pdata, dev);
#endif
}
/* All GPIO's created successfully */
goto success;
fail_ddi_rst_gpio:
pt_debug(dev, DL_ERROR,
"%s: ERROR - GPIO setup Failure, freeing DDI_XRES GPIO %d\n",
__func__, ddi_rst_gpio);
gpio_free(ddi_rst_gpio);
fail_irq_gpio:
pt_debug(dev, DL_ERROR,
"%s: ERROR - GPIO setup Failure, freeing IRQ GPIO %d\n",
__func__, irq_gpio);
gpio_free(irq_gpio);
fail_rst_gpio:
pt_debug(dev, DL_ERROR,
"%s: ERROR - GPIO setup Failure, freeing TP_XRES GPIO %d\n",
__func__, rst_gpio);
gpio_free(rst_gpio);
success:
pt_debug(dev, DL_INFO,
"%s: SUCCESS - Configured DDI_XRES GPIO %d, IRQ GPIO %d, TP_XRES GPIO %d\n",
__func__, ddi_rst_gpio, irq_gpio, rst_gpio);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_irq_stat
*
* SUMMARY: Obtain the level state of IRQ gpio.
*
* RETURN:
* level state of IRQ gpio
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
******************************************************************************/
int pt_irq_stat(struct pt_core_platform_data *pdata,
struct device *dev)
{
return gpio_get_value(pdata->irq_gpio);
}
#ifdef PT_DETECT_HW
/*******************************************************************************
* FUNCTION: pt_detect
*
* SUMMARY: Detect the I2C device by reading one byte(FW sentiel) after the
* reset operation.
*
* RETURN:
* 0 - detected
* !0 - undetected
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
* read - pointer to the function to perform a read operation
******************************************************************************/
int pt_detect(struct pt_core_platform_data *pdata,
struct device *dev, pt_platform_read read)
{
int retry = 3;
int rc;
char buf[1];
while (retry--) {
/* Perform reset, wait for 100 ms and perform read */
pt_debug(dev, DL_WARN, "%s: Performing a reset\n",
__func__);
pdata->xres(pdata, dev);
msleep(100);
rc = read(dev, buf, 1);
if (!rc)
return 0;
pt_debug(dev, DL_ERROR, "%s: Read unsuccessful, try=%d\n",
__func__, 3 - retry);
}
return rc;
}
#endif
#ifndef PT_REGULATOR_EN
/*******************************************************************************
* FUNCTION: pt_setup_power_by_gpio
*
* SUMMARY: With GPIOs to control LDO directly to set power on/off.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
* *dev - pointer to Device structure
* *time - pointer to store how many ms this function will sleep. This is
* used to calculate how long to leave XRES asserted during pwr up
******************************************************************************/
static int pt_setup_power_by_gpio(struct pt_core_platform_data *pdata,
int on, struct device *dev, int *time)
{
int rc = 0;
int en_vcc = pdata->vcc_gpio;
int en_vddi = pdata->vddi_gpio;
int en_avdd = pdata->avdd_gpio;
int en_avee = pdata->avee_gpio;
if (on == PT_MT_POWER_ON) {
/*
* Enable GPIOs to turn on voltage regulators to pwr up DUT
* - TC device power up order: VDDI, VCC, AVDD, AVEE
* - TT device power up order: VDDI, VCC
* NOTE: VDDI must be stable for >10ms before XRES is released
*/
pt_debug(dev, DL_INFO,
"%s: Enable defined pwr: VDDI, VCC, AVDD, AVEE\n", __func__);
/* Turn on VDDI [Digital Interface] (+1.8v) */
if (pdata->vddi_gpio) {
rc = gpio_request(en_vddi, NULL);
if (rc < 0) {
gpio_free(en_vddi);
rc = gpio_request(en_vddi, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting VDDI GPIO %d\n",
__func__, en_vddi);
}
rc = gpio_direction_output(en_vddi, 1);
if (rc)
pr_err("%s: setcfg for VDDI GPIO %d failed\n",
__func__, en_vddi);
gpio_free(en_vddi);
}
/* Turn on VCC */
if (pdata->vcc_gpio) {
rc = gpio_request(en_vcc, NULL);
if (rc < 0) {
gpio_free(en_vcc);
rc = gpio_request(en_vcc, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting VCC GPIO %d\n",
__func__, en_vcc);
}
rc = gpio_direction_output(en_vcc, 1);
if (rc)
pr_err("%s: setcfg for VCC GPIO %d failed\n",
__func__, en_vcc);
gpio_free(en_vcc);
/*
* This must be a min of 2ms for Gen6 but as short as
* possible for TT/TC parts.
*/
usleep_range(2000, 3000);
}
/*
* 2-3ms after enabling VDDI and VCC, change the direction of
* the RST GPIO to be an output so it will no longer float and
* then force teh DUT into reset
*/
rc = gpio_direction_output(pdata->rst_gpio, 0);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Output Setup ERROR: RST GPIO %d\n",
__func__, pdata->rst_gpio);
} else {
/* Force DUT into reset */
gpio_set_value(pdata->rst_gpio, 0);
}
/* Reset ms counter since XRES was enabled */
*time = 0;
/* Turn on AVDD (+5.0v) */
if (pdata->avdd_gpio) {
rc = gpio_request(en_avdd, NULL);
if (rc < 0) {
gpio_free(en_avdd);
rc = gpio_request(en_avdd, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting AVDD GPIO %d\n",
__func__, en_avdd);
}
rc = gpio_direction_output(en_avdd, 1);
if (rc)
pr_err("%s: setcfg for AVDD GPIO %d failed\n",
__func__, en_avdd);
gpio_free(en_avdd);
usleep_range(3000, 4000);
*time += 4;
}
/* Turn on AVEE (-5.0v) */
if (pdata->avee_gpio) {
rc = gpio_request(en_avee, NULL);
if (rc < 0) {
gpio_free(en_avee);
rc = gpio_request(en_avee, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting AVEE GPIO %d\n",
__func__, en_avee);
}
rc = gpio_direction_output(en_avee, 1);
if (rc)
pr_err("%s: setcfg for AVEE GPIO %d failed\n",
__func__, en_avee);
gpio_free(en_avee);
usleep_range(3000, 4000);
*time += 4;
}
} else {
/*
* Disable GPIOs to turn off voltage regulators to pwr down
* TC device The power down order is: AVEE, AVDD, VDDI
* TT device The power down order is: VCC, VDDI
*
* Note:Turn off some of regulators may effect display
* parts for TDDI chip
*/
pt_debug(dev, DL_INFO,
"%s: Turn off defined pwr: VCC, AVEE, AVDD, VDDI\n", __func__);
/* Turn off VCC */
if (pdata->vcc_gpio) {
rc = gpio_request(en_vcc, NULL);
if (rc < 0) {
gpio_free(en_vcc);
rc = gpio_request(en_vcc, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting VCC GPIO %d\n",
__func__, en_vcc);
}
rc = gpio_direction_output(en_vcc, 0);
if (rc)
pr_err("%s: setcfg for VCC GPIO %d failed\n",
__func__, en_vcc);
gpio_free(en_vcc);
}
/* Turn off AVEE (-5.0v) */
if (pdata->avee_gpio) {
rc = gpio_request(en_avee, NULL);
if (rc < 0) {
gpio_free(en_avee);
rc = gpio_request(en_avee, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting AVEE GPIO %d\n",
__func__, en_avee);
}
rc = gpio_direction_output(en_avee, 0);
if (rc)
pr_err("%s: setcfg for AVEE GPIO %d failed\n",
__func__, en_avee);
gpio_free(en_avee);
}
/* Turn off AVDD (+5.0v) */
if (pdata->avdd_gpio) {
rc = gpio_request(en_avdd, NULL);
if (rc < 0) {
gpio_free(en_avdd);
rc = gpio_request(en_avdd, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting AVDD GPIO %d\n",
__func__, en_avdd);
}
rc = gpio_direction_output(en_avdd, 0);
if (rc)
pr_err("%s: setcfg for AVDD GPIO %d failed\n",
__func__, en_avdd);
gpio_free(en_avdd);
}
/* Turn off VDDI [Digital Interface] (+1.8v) */
if (pdata->vddi_gpio) {
rc = gpio_request(en_vddi, NULL);
if (rc < 0) {
gpio_free(en_vddi);
rc = gpio_request(en_vddi, NULL);
}
if (rc < 0) {
pr_err("%s: Failed requesting VDDI GPIO %d\n",
__func__, en_vddi);
}
rc = gpio_direction_output(en_vddi, 0);
if (rc)
pr_err("%s: setcfg for VDDI GPIO %d failed\n",
__func__, en_vddi);
gpio_free(en_vddi);
usleep_range(10000, 12000);
}
}
return rc;
}
#endif /* PT_REGULATOR_EN */
/*******************************************************************************
* FUNCTION: pt_setup_power
*
* SUMMARY: Turn on/turn off voltage regulator
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *pdata - pointer to core platform data
* on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
* *dev - pointer to device
******************************************************************************/
int pt_setup_power(struct pt_core_platform_data *pdata, int on,
struct device *dev)
{
int rc = 0;
int sleep_time = 0;
/*
* For TDDI parts, force part into RESET by holding DDI XRES
* while powering it up
*/
if (pdata->ddi_rst_gpio)
gpio_set_value(pdata->ddi_rst_gpio, 0);
/*
* When turning power on:
* - For Gen6 the RST line must be disabled (set high) when VDDI is
* being applied for a min of 2ms, after which it must be enabled (set
* low) for 10ms.
* - For TT/TC parts it must be low while power is applied but the pwr
* sequence for Gen6 is ok.
*/
if (on && pdata->rst_gpio) {
/*
* Set initial RST GPIO direction to input to allow it to
* float up with VDDI.
*/
rc = gpio_direction_input(pdata->rst_gpio);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Input Setup ERROR: RST GPIO %d\n",
__func__, pdata->rst_gpio);
/*
* Not ideal but if setting the direction to input
* failed ensure part is not in reset during first 2ms.
*/
gpio_set_value(pdata->rst_gpio, 1);
}
}
#ifdef PT_REGULATOR_EN
rc = pt_setup_power_by_regulator(pdata, on, dev, &sleep_time);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed setup power by regulator rc=%d\n",
__func__, rc);
}
#else /* PT_REGULATOR_EN */
rc = pt_setup_power_by_gpio(pdata, on, dev, &sleep_time);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed setup power by gpio rc=%d\n",
__func__, rc);
}
#endif /* PT_REGULATOR_EN */
/*
* TP_XRES must be held low for a min of 10ms, however, TDDI panels
* will have already slept for 8ms so only add 2ms, whereas TT7K/Gen6
* panels will not have slept yet so do full 10ms sleep.
*/
if (sleep_time < 8)
usleep_range(10000, 11000);
else
usleep_range(2000, 3000);
/* Force part out of RESET by releasing XRES#(TP_XRES) */
if (pdata->rst_gpio)
gpio_set_value(pdata->rst_gpio, 1);
/* Force part out of RESET by releasing DDI XRES */
if (pdata->ddi_rst_gpio)
gpio_set_value(pdata->ddi_rst_gpio, 1);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_wakeup
*
* SUMMARY: Resume power for "power on/off" sleep strategy which against to
* "deepsleep" strategy.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
* *ignore_irq - pointer to atomic structure to allow the host ignoring false
* IRQ during power up
******************************************************************************/
static int pt_wakeup(struct pt_core_platform_data *pdata,
struct device *dev, atomic_t *ignore_irq)
{
int rc = 0;
#ifdef PT_PINCTRL_EN
pt_pinctrl_select_normal(pdata, dev);
#endif
rc = pt_setup_power(pdata, PT_MT_POWER_ON, dev);
if (rc)
pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_sleep
*
* SUMMARY: Suspend power for "power on/off" sleep strategy which against to
* "deepsleep" strategy.
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* *dev - pointer to Device structure
* *ignore_irq - pointer to atomic structure to allow the host ignoring false
* IRQ during power down
******************************************************************************/
static int pt_sleep(struct pt_core_platform_data *pdata,
struct device *dev, atomic_t *ignore_irq)
{
int rc = 0;
rc = pt_setup_power(pdata, PT_MT_POWER_OFF, dev);
if (rc)
pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__);
#ifdef PT_PINCTRL_EN
pt_pinctrl_select_suspend(pdata, dev);
#endif
return rc;
}
/*******************************************************************************
* FUNCTION: pt_power
*
* SUMMARY: Wrapper function to resume/suspend power with function
* pt_wakeup()/pt_sleep().
*
* RETURN:
* 0 = success
* !0 = fail
*
* PARAMETERS:
* *pdata - pointer to the platform data structure
* on - flag to remsume/suspend power(0:resume; 1:suspend)
* *dev - pointer to Device structure
* *ignore_irq - pointer to atomic structure to allow the host ignoring false
* IRQ during power up/down
******************************************************************************/
int pt_power(struct pt_core_platform_data *pdata,
int on, struct device *dev, atomic_t *ignore_irq)
{
if (on)
return pt_wakeup(pdata, dev, ignore_irq);
return pt_sleep(pdata, dev, ignore_irq);
}
#ifdef PT_PTSBC_SUPPORT
static struct workqueue_struct *parade_wq;
static u32 int_handle;
/*******************************************************************************
* FUNCTION: pt_irq_work_function
*
* SUMMARY: Work function for queued IRQ activity
*
* RETURN: Void
*
* PARAMETERS:
* *work - pointer to work structure
******************************************************************************/
static void pt_irq_work_function(struct work_struct *work)
{
struct pt_core_data *cd = container_of(work,
struct pt_core_data, irq_work);
pt_irq(cd->irq, (void *)cd);
}
/*******************************************************************************
* FUNCTION: pt_irq_wrapper
*
* SUMMARY: Wrapper function for IRQ to queue the irq_work function
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *handle - void pointer to contain the core_data pointer
******************************************************************************/
peint_handle *pt_irq_wrapper(void *handle)
{
struct pt_core_data *cd = (struct pt_core_data *)handle;
queue_work(parade_wq, &cd->irq_work);
return 0;
}
#endif /* PT_PTSBC_SUPPORT */
/*******************************************************************************
* FUNCTION: pt_setup_irq
*
* SUMMARY: Configure the IRQ GPIO used by the TT DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *pdata - pointer to core platform data
* on - flag to setup interrupt process work(PT_MT_IRQ_FREE/)
* *dev - pointer to device
******************************************************************************/
int pt_setup_irq(struct pt_core_platform_data *pdata, int on,
struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
unsigned long irq_flags;
int rc = 0;
if (on == PT_MT_IRQ_REG) {
/*
* When TTDL has direct access to the GPIO the irq_stat function
* will be defined and the gpio_to_irq conversion must be
* performed. e.g. For CHROMEOS this is not the case, the irq is
* passed in directly.
*/
if (pdata->irq_stat) {
/* Initialize IRQ */
dev_vdbg(dev, "%s: Value Passed to gpio_to_irq =%d\n",
__func__, pdata->irq_gpio);
cd->irq = gpio_to_irq(pdata->irq_gpio);
dev_vdbg(dev,
"%s: Value Returned from gpio_to_irq =%d\n",
__func__, cd->irq);
}
if (cd->irq < 0)
return -EINVAL;
cd->irq_enabled = true;
pt_debug(dev, DL_INFO, "%s: initialize threaded irq=%d\n",
__func__, cd->irq);
if (pdata->level_irq_udelay > 0)
#ifdef PT_PTSBC_SUPPORT
/* use level triggered interrupts */
irq_flags = TRIG_LEVL_LOW;
else
/* use edge triggered interrupts */
irq_flags = TRIG_EDGE_NEGATIVE;
#else
/* use level triggered interrupts */
irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
else
/* use edge triggered interrupts */
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
#endif /* PT_PTSBC_SUPPORT */
#ifdef PT_PTSBC_SUPPORT
/* Adding new work queue to cd struct */
INIT_WORK(&cd->irq_work, pt_irq_work_function);
parade_wq = create_singlethread_workqueue("parade_wq");
if (!parade_wq)
pt_debug(dev, DL_ERROR, "%s Create workqueue failed.\n",
__func__);
int_handle = sw_gpio_irq_request(pdata->irq_gpio, irq_flags,
(peint_handle)pt_irq_wrapper, cd);
if (!int_handle) {
pt_debug(dev, DL_ERROR,
"%s: PARADE could not request irq\n", __func__);
rc = -1;
} else {
rc = 0;
/* clk=0: 32Khz; clk=1: 24Mhz*/
ctp_set_int_port_rate(pdata->irq_gpio, 1);
/*
* Debounce INT Line by clock divider: 2^n. E.g. The
* para:0x03 means the period of interrupt controller is
* 0.33 us = (2^3)/24. It has ability to measure the
* high/low width of the pulse bigger than 1 us.
*/
ctp_set_int_port_deb(pdata->irq_gpio, 0x03);
pt_debug(cd->dev, DL_INFO,
"%s: Parade sw_gpio_irq_request SUCCESS\n",
__func__);
}
#else
rc = request_threaded_irq(cd->irq, pt_top_irq, pt_bottom_irq, irq_flags,
dev_name(dev), cd);
if (rc < 0)
pt_debug(dev, DL_ERROR,
"%s: Error, could not request irq\n", __func__);
#endif /* PT_PTSBC_SUPPORT */
} else {
disable_irq_nosync(cd->irq);
#ifndef PT_PTSBC_SUPPORT
free_irq(cd->irq, cd);
#else
sw_gpio_irq_free(int_handle);
cancel_work_sync(&cd->irq_work);
destroy_workqueue(parade_wq);
#endif /* PT_PTSBC_SUPPORT */
}
return rc;
}