| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * pt_devtree.c |
| * Parade TrueTouch(TM) Standard Product Device Tree Support 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 <linux/device.h> |
| #include <linux/err.h> |
| #include <linux/of_device.h> |
| #include <linux/of_gpio.h> |
| #include <linux/slab.h> |
| #include "pt_regs.h" |
| #include "pt_platform.h" |
| |
| #define MAX_NAME_LENGTH 64 |
| |
| static bool is_create_and_get_pdata; |
| |
| enum pt_device_type { |
| DEVICE_MT, |
| DEVICE_BTN, |
| DEVICE_PEN, |
| DEVICE_PROXIMITY, |
| DEVICE_TYPE_MAX, |
| }; |
| |
| struct pt_device_pdata_func { |
| void * (*create_and_get_pdata)(struct device_node *dn); |
| void (*free_pdata)(void *ptr); |
| }; |
| |
| struct pt_pdata_ptr { |
| void **pdata; |
| }; |
| |
| #ifdef ENABLE_VIRTUAL_KEYS |
| static struct kobject *board_properties_kobj; |
| |
| struct pt_virtual_keys { |
| struct kobj_attribute kobj_attr; |
| u16 *data; |
| int size; |
| }; |
| #endif |
| |
| struct pt_extended_mt_platform_data { |
| struct pt_mt_platform_data pdata; |
| #ifdef ENABLE_VIRTUAL_KEYS |
| struct pt_virtual_keys vkeys; |
| #endif |
| }; |
| |
| /******************************************************************************* |
| * FUNCTION: get_inp_dev_name |
| * |
| * SUMMARY: Get the name of input device from dts. |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = failure |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| * **inp_dev_name - double pointer to the name of input device |
| ******************************************************************************/ |
| static inline int get_inp_dev_name(struct device_node *dev_node, |
| const char **inp_dev_name) |
| { |
| return of_property_read_string(dev_node, "parade,inp_dev_name", |
| inp_dev_name); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_u16_array |
| * |
| * SUMMARY: Create and get u16 array from dts. |
| * |
| * RETURN: |
| * success: the pointer of the created array |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| * *name - name of device node |
| * size - number of u16 array elements |
| ******************************************************************************/ |
| static s16 *create_and_get_u16_array(struct device_node *dev_node, |
| const char *name, int *size) |
| { |
| const __be32 *values; |
| s16 *val_array; |
| int len; |
| int sz; |
| int rc; |
| int i; |
| |
| values = of_get_property(dev_node, name, &len); |
| if (values == NULL) |
| return NULL; |
| |
| sz = len / sizeof(u32); |
| pr_debug("%s: %s size:%d\n", __func__, name, sz); |
| |
| val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL); |
| if (!val_array) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| for (i = 0; i < sz; i++) |
| val_array[i] = (s16)be32_to_cpup(values++); |
| |
| *size = sz; |
| |
| return val_array; |
| |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_touch_framework |
| * |
| * SUMMARY: Create and get touch framework structure from dts. |
| * |
| * RETURN: |
| * success: the pointer of the touch framework data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| ******************************************************************************/ |
| static struct touch_framework *create_and_get_touch_framework( |
| struct device_node *dev_node) |
| { |
| struct touch_framework *frmwrk; |
| s16 *abs; |
| int size; |
| int rc; |
| |
| abs = create_and_get_u16_array(dev_node, "parade,abs", &size); |
| if (IS_ERR_OR_NULL(abs)) |
| return (void *)abs; |
| |
| /* Check for valid abs size */ |
| if (size % PT_NUM_ABS_SET) { |
| rc = -EINVAL; |
| goto fail_free_abs; |
| } |
| |
| frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL); |
| if (!frmwrk) { |
| rc = -ENOMEM; |
| goto fail_free_abs; |
| } |
| |
| frmwrk->abs = abs; |
| frmwrk->size = size; |
| |
| return frmwrk; |
| |
| fail_free_abs: |
| kfree(abs); |
| |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_touch_framework |
| * |
| * SUMMARY: Free all the pointer of touch framework structure. |
| * |
| * PARAMETERS: |
| * *frmwrk - pointer to touch framework structure |
| ******************************************************************************/ |
| static void free_touch_framework(struct touch_framework *frmwrk) |
| { |
| kfree(frmwrk->abs); |
| kfree(frmwrk); |
| } |
| |
| #ifdef ENABLE_VIRTUAL_KEYS |
| #define VIRTUAL_KEY_ELEMENT_SIZE 5 |
| /******************************************************************************* |
| * FUNCTION: virtual_keys_show |
| * |
| * SUMMARY: Show method for the board_properties sysfs node that will show the |
| * information for all virtual keys |
| * |
| * RETURN: size of data written to sysfs node |
| * |
| * PARAMETERS: |
| * *kobj - pointer to kobject structure |
| * *attr - pointer to kobject attributes |
| * *buf - pointer to print output buffer |
| ******************************************************************************/ |
| static ssize_t virtual_keys_show(struct kobject *kobj, |
| struct kobj_attribute *attr, char *buf) |
| { |
| struct pt_virtual_keys *vkeys = container_of(attr, |
| struct pt_virtual_keys, kobj_attr); |
| u16 *data = vkeys->data; |
| int size = vkeys->size; |
| int index; |
| int i; |
| |
| index = 0; |
| for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE) |
| index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, |
| "0x01:%d:%d:%d:%d:%d\n", |
| data[i], data[i+1], data[i+2], data[i+3], data[i+4]); |
| |
| return index; |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: setup_virtual_keys |
| * |
| * SUMMARY: Create virtual key data array from dts and set up dynamic sysfs for |
| * board_properties node. |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = failure |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| * **inp_dev_name - double pointer to the name of input device |
| * *vkeys - pointer to virtual key structure |
| ******************************************************************************/ |
| static int setup_virtual_keys(struct device_node *dev_node, |
| const char *inp_dev_name, struct pt_virtual_keys *vkeys) |
| { |
| char *name; |
| u16 *data; |
| int size; |
| int rc; |
| |
| data = create_and_get_u16_array(dev_node, "parade,virtual_keys", &size); |
| if (data == NULL) |
| return 0; |
| else if (IS_ERR(data)) { |
| rc = PTR_ERR(data); |
| goto fail; |
| } |
| |
| /* Check for valid virtual keys size */ |
| if (size % VIRTUAL_KEY_ELEMENT_SIZE) { |
| rc = -EINVAL; |
| goto fail_free_data; |
| } |
| |
| name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL); |
| if (!name) { |
| rc = -ENOMEM; |
| goto fail_free_data; |
| } |
| |
| snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name); |
| |
| vkeys->data = data; |
| vkeys->size = size; |
| |
| /* TODO: Instantiate in board file and export it */ |
| if (board_properties_kobj == NULL) |
| board_properties_kobj = |
| kobject_create_and_add("board_properties", NULL); |
| if (board_properties_kobj == NULL) { |
| pr_err("%s: Cannot get board_properties kobject!\n", __func__); |
| rc = -EINVAL; |
| goto fail_free_name; |
| } |
| |
| /* Initialize dynamic SysFs attribute */ |
| sysfs_attr_init(&vkeys->kobj_attr.attr); |
| vkeys->kobj_attr.attr.name = name; |
| vkeys->kobj_attr.attr.mode = 0444; |
| vkeys->kobj_attr.show = virtual_keys_show; |
| |
| rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr); |
| if (rc) |
| goto fail_del_kobj; |
| |
| return 0; |
| |
| fail_del_kobj: |
| kobject_del(board_properties_kobj); |
| fail_free_name: |
| kfree(name); |
| vkeys->kobj_attr.attr.name = NULL; |
| fail_free_data: |
| kfree(data); |
| vkeys->data = NULL; |
| fail: |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_virtual_keys |
| * |
| * SUMMARY: Remove board_properties node and free all pointers. |
| * |
| * PARAMETERS: |
| * *vkeys - pointer to virtual key structure |
| ******************************************************************************/ |
| static void free_virtual_keys(struct pt_virtual_keys *vkeys) |
| { |
| if (board_properties_kobj) |
| sysfs_remove_file(board_properties_kobj, |
| &vkeys->kobj_attr.attr); |
| |
| |
| kobject_del(board_properties_kobj); |
| board_properties_kobj = NULL; |
| |
| kfree(vkeys->data); |
| kfree(vkeys->kobj_attr.attr.name); |
| } |
| #endif |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_mt_pdata |
| * |
| * SUMMARY: Create and get touch platform data from dts.Touch framework and |
| * virtual keys are set up in this function. |
| * |
| * RETURN: |
| * success: the pointer of the platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| ******************************************************************************/ |
| static void *create_and_get_mt_pdata(struct device_node *dev_node) |
| { |
| struct pt_extended_mt_platform_data *ext_pdata; |
| struct pt_mt_platform_data *pdata; |
| u32 value; |
| int rc; |
| |
| ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL); |
| if (!ext_pdata) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| pdata = &ext_pdata->pdata; |
| |
| rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); |
| if (rc) |
| goto fail_free_pdata; |
| |
| /* Optional fields */ |
| rc = of_property_read_u32(dev_node, "parade,flags", &value); |
| if (!rc) |
| pdata->flags = value; |
| |
| rc = of_property_read_u32(dev_node, "parade,vkeys_x", &value); |
| if (!rc) |
| pdata->vkeys_x = value; |
| |
| rc = of_property_read_u32(dev_node, "parade,vkeys_y", &value); |
| if (!rc) |
| pdata->vkeys_y = value; |
| |
| /* Required fields */ |
| pdata->frmwrk = create_and_get_touch_framework(dev_node); |
| if (pdata->frmwrk == NULL) { |
| rc = -EINVAL; |
| goto fail_free_pdata; |
| } else if (IS_ERR(pdata->frmwrk)) { |
| rc = PTR_ERR(pdata->frmwrk); |
| goto fail_free_pdata; |
| } |
| #ifdef ENABLE_VIRTUAL_KEYS |
| rc = setup_virtual_keys(dev_node, pdata->inp_dev_name, |
| &ext_pdata->vkeys); |
| if (rc) { |
| pr_err("%s: Cannot setup virtual keys!\n", __func__); |
| goto fail_free_pdata; |
| } |
| #endif |
| return pdata; |
| |
| fail_free_pdata: |
| kfree(ext_pdata); |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_mt_pdata |
| * |
| * SUMMARY: Free all pointers that include touch framework, virtual keys and |
| * touch data. |
| * |
| * PARAMETERS: |
| * *vkeys - pointer to virtual key structure |
| ******************************************************************************/ |
| static void free_mt_pdata(void *pdata) |
| { |
| struct pt_mt_platform_data *mt_pdata = |
| (struct pt_mt_platform_data *)pdata; |
| struct pt_extended_mt_platform_data *ext_mt_pdata = |
| container_of(mt_pdata, |
| struct pt_extended_mt_platform_data, pdata); |
| |
| free_touch_framework(mt_pdata->frmwrk); |
| #ifdef ENABLE_VIRTUAL_KEYS |
| free_virtual_keys(&ext_mt_pdata->vkeys); |
| #endif |
| kfree(ext_mt_pdata); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_btn_pdata |
| * |
| * SUMMARY: Create and get button platform data from dts. |
| * |
| * RETURN: |
| * success: the pointer of the platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| ******************************************************************************/ |
| static void *create_and_get_btn_pdata(struct device_node *dev_node) |
| { |
| struct pt_btn_platform_data *pdata; |
| int rc; |
| |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); |
| if (rc) |
| goto fail_free_pdata; |
| |
| return pdata; |
| |
| fail_free_pdata: |
| kfree(pdata); |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_btn_pdata |
| * |
| * SUMMARY: Free all pointers for button platform data. |
| * |
| * PARAMETERS: |
| * *vkeys - pointer to virtual key structure |
| ******************************************************************************/ |
| static void free_btn_pdata(void *pdata) |
| { |
| struct pt_btn_platform_data *btn_pdata = |
| (struct pt_btn_platform_data *)pdata; |
| |
| kfree(btn_pdata); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_pen_pdata |
| * |
| * SUMMARY: Create and get pen platform data from dts. |
| * |
| * RETURN: |
| * success: the pointer of the platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| ******************************************************************************/ |
| static void *create_and_get_pen_pdata(struct device_node *dev_node) |
| { |
| struct pt_pen_platform_data *pdata; |
| int rc; |
| |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); |
| if (rc) |
| goto fail_free_pdata; |
| |
| return pdata; |
| |
| fail_free_pdata: |
| kfree(pdata); |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_pen_pdata |
| * |
| * SUMMARY: Free all pointers for pen platform data. |
| * |
| * PARAMETERS: |
| * *pdata - pointer to virtual key structure |
| ******************************************************************************/ |
| static void free_pen_pdata(void *pdata) |
| { |
| struct pt_pen_platform_data *pen_pdata = |
| (struct pt_pen_platform_data *)pdata; |
| |
| kfree(pen_pdata); |
| } |
| |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_proximity_pdata |
| * |
| * SUMMARY: Create and get proximity platform data from dts. |
| * |
| * RETURN: |
| * success: the pointer of the proximity platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| ******************************************************************************/ |
| static void *create_and_get_proximity_pdata(struct device_node *dev_node) |
| { |
| struct pt_proximity_platform_data *pdata; |
| int rc; |
| |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); |
| if (rc) |
| goto fail_free_pdata; |
| |
| pdata->frmwrk = create_and_get_touch_framework(dev_node); |
| if (pdata->frmwrk == NULL) { |
| rc = -EINVAL; |
| goto fail_free_pdata; |
| } else if (IS_ERR(pdata->frmwrk)) { |
| rc = PTR_ERR(pdata->frmwrk); |
| goto fail_free_pdata; |
| } |
| |
| return pdata; |
| |
| fail_free_pdata: |
| kfree(pdata); |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_proximity_pdata |
| * |
| * SUMMARY: Free all pointers for proximity platform data. |
| * |
| * PARAMETERS: |
| * *vkeys - pointer to virtual key structure |
| ******************************************************************************/ |
| static void free_proximity_pdata(void *pdata) |
| { |
| struct pt_proximity_platform_data *proximity_pdata = |
| (struct pt_proximity_platform_data *)pdata; |
| |
| free_touch_framework(proximity_pdata->frmwrk); |
| |
| kfree(proximity_pdata); |
| } |
| |
| static struct pt_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = { |
| [DEVICE_MT] = { |
| .create_and_get_pdata = create_and_get_mt_pdata, |
| .free_pdata = free_mt_pdata, |
| }, |
| [DEVICE_BTN] = { |
| .create_and_get_pdata = create_and_get_btn_pdata, |
| .free_pdata = free_btn_pdata, |
| }, |
| [DEVICE_PEN] = { |
| .create_and_get_pdata = create_and_get_pen_pdata, |
| .free_pdata = free_pen_pdata, |
| }, |
| [DEVICE_PROXIMITY] = { |
| .create_and_get_pdata = create_and_get_proximity_pdata, |
| .free_pdata = free_proximity_pdata, |
| }, |
| }; |
| |
| static struct pt_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX]; |
| |
| static const char *device_names[DEVICE_TYPE_MAX] = { |
| [DEVICE_MT] = "parade,mt", |
| [DEVICE_BTN] = "parade,btn", |
| [DEVICE_PEN] = "parade,pen", |
| [DEVICE_PROXIMITY] = "parade,proximity", |
| }; |
| |
| /******************************************************************************* |
| * FUNCTION: set_pdata_ptr |
| * |
| * SUMMARY: Set platform data pointer for touch, button, proximity module. |
| * |
| * PARAMETERS: |
| * *pdata - pointer to platform data structure |
| ******************************************************************************/ |
| static void set_pdata_ptr(struct pt_platform_data *pdata) |
| { |
| pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata; |
| pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata; |
| pdata_ptr[DEVICE_PEN].pdata = (void **)&pdata->pen_pdata; |
| pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata; |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: get_device_type |
| * |
| * SUMMARY: Determine the device type[mt,btn,proximity] from dts. |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = failure |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| * *type - pointer to store the device type |
| ******************************************************************************/ |
| static int get_device_type(struct device_node *dev_node, |
| enum pt_device_type *type) |
| { |
| const char *name; |
| enum pt_device_type t; |
| int rc; |
| |
| rc = of_property_read_string(dev_node, "name", &name); |
| if (rc) |
| return rc; |
| |
| for (t = 0; t < DEVICE_TYPE_MAX; t++) |
| if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) { |
| *type = t; |
| return 0; |
| } |
| |
| return -EINVAL; |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_device_pdata |
| * |
| * SUMMARY: Create platform data for mt, btn, proximity module. |
| * |
| * RETURN: |
| * success: the pointer of the platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *dev_node - pointer to device_node structure |
| * type - determine the device type |
| ******************************************************************************/ |
| static inline void *create_and_get_device_pdata(struct device_node *dev_node, |
| enum pt_device_type type) |
| { |
| return device_pdata_funcs[type].create_and_get_pdata(dev_node); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_device_pdata |
| * |
| * SUMMARY: Free platform data for mt, btn, proximity module. |
| * |
| * PARAMETERS: |
| * type - determine the device type |
| ******************************************************************************/ |
| static inline void free_device_pdata(enum pt_device_type type) |
| { |
| device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_touch_setting |
| * |
| * SUMMARY: Create and get touch settings from dts. |
| * |
| * RETURN: |
| * success: the pointer of touch settings |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *core_node - pointer to device_node structure |
| * name - name of touch setting |
| ******************************************************************************/ |
| static struct touch_settings *create_and_get_touch_setting( |
| struct device_node *core_node, const char *name) |
| { |
| struct touch_settings *setting; |
| char *tag_name; |
| u32 tag_value; |
| u16 *data; |
| int size; |
| int rc; |
| |
| data = create_and_get_u16_array(core_node, name, &size); |
| if (IS_ERR_OR_NULL(data)) |
| return (void *)data; |
| |
| pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size); |
| |
| setting = kzalloc(sizeof(*setting), GFP_KERNEL); |
| if (!setting) { |
| rc = -ENOMEM; |
| goto fail_free_data; |
| } |
| |
| setting->data = (u8 *)data; |
| setting->size = size; |
| |
| tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL); |
| if (!tag_name) { |
| rc = -ENOMEM; |
| goto fail_free_setting; |
| } |
| |
| snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name); |
| |
| rc = of_property_read_u32(core_node, tag_name, &tag_value); |
| if (!rc) |
| setting->tag = tag_value; |
| |
| kfree(tag_name); |
| |
| return setting; |
| |
| fail_free_setting: |
| kfree(setting); |
| fail_free_data: |
| kfree(data); |
| |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_touch_setting |
| * |
| * SUMMARY: Free touch setting data. |
| * |
| * PARAMETERS: |
| * setting - pointer to touch setting |
| ******************************************************************************/ |
| static void free_touch_setting(struct touch_settings *setting) |
| { |
| if (setting) { |
| kfree(setting->data); |
| kfree(setting); |
| } |
| } |
| |
| static char *touch_setting_names[PT_IC_GRPNUM_NUM] = { |
| NULL, /* PT_IC_GRPNUM_RESERVED */ |
| "parade,cmd_regs", /* PT_IC_GRPNUM_CMD_REGS */ |
| "parade,tch_rep", /* PT_IC_GRPNUM_TCH_REP */ |
| "parade,data_rec", /* PT_IC_GRPNUM_DATA_REC */ |
| "parade,test_rec", /* PT_IC_GRPNUM_TEST_REC */ |
| "parade,pcfg_rec", /* PT_IC_GRPNUM_PCFG_REC */ |
| "parade,tch_parm_val", /* PT_IC_GRPNUM_TCH_PARM_VAL */ |
| "parade,tch_parm_size", /* PT_IC_GRPNUM_TCH_PARM_SIZE */ |
| NULL, /* PT_IC_GRPNUM_RESERVED1 */ |
| NULL, /* PT_IC_GRPNUM_RESERVED2 */ |
| "parade,opcfg_rec", /* PT_IC_GRPNUM_OPCFG_REC */ |
| "parade,ddata_rec", /* PT_IC_GRPNUM_DDATA_REC */ |
| "parade,mdata_rec", /* PT_IC_GRPNUM_MDATA_REC */ |
| "parade,test_regs", /* PT_IC_GRPNUM_TEST_REGS */ |
| "parade,btn_keys", /* PT_IC_GRPNUM_BTN_KEYS */ |
| NULL, /* PT_IC_GRPNUM_TTHE_REGS */ |
| }; |
| |
| /******************************************************************************* |
| * FUNCTION: create_and_get_core_pdata |
| * |
| * SUMMARY: Create and get core module platform data from dts. |
| * |
| * RETURN: |
| * success: the pointer of core platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *core_node - pointer to device_node structure |
| ******************************************************************************/ |
| static struct pt_core_platform_data *create_and_get_core_pdata( |
| struct device_node *core_node) |
| { |
| struct pt_core_platform_data *pdata; |
| u32 value = 0; |
| int rc = 0; |
| int i; |
| |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| /* Required fields */ |
| pdata->irq_gpio = of_get_named_gpio(core_node, "parade,irq_gpio", 0); |
| if (pdata->irq_gpio < 0) |
| goto fail_free; |
| |
| rc = of_property_read_u32(core_node, "parade,hid_desc_register", |
| &value); |
| if (rc) |
| goto fail_free; |
| pdata->hid_desc_register = value; |
| |
| /* Optional fields */ |
| /* rst_gpio is optional since a platform may use |
| * power cycling instead of using the XRES pin |
| */ |
| pdata->rst_gpio = of_get_named_gpio(core_node, "parade,rst_gpio", 0); |
| |
| rc = of_property_read_u32(core_node, "parade,ddi_rst_gpio", &value); |
| if (!rc) |
| pdata->ddi_rst_gpio = value; |
| |
| rc = of_property_read_u32(core_node, "parade,vddi_gpio", &value); |
| if (!rc) |
| pdata->vddi_gpio = value; |
| |
| rc = of_property_read_u32(core_node, "parade,vcc_gpio", &value); |
| if (!rc) |
| pdata->vcc_gpio = value; |
| |
| rc = of_property_read_u32(core_node, "parade,avdd_gpio", &value); |
| if (!rc) |
| pdata->avdd_gpio = value; |
| |
| rc = of_property_read_u32(core_node, "parade,avee_gpio", &value); |
| if (!rc) |
| pdata->avee_gpio = value; |
| |
| rc = of_property_read_u32(core_node, "parade,level_irq_udelay", &value); |
| if (!rc) |
| pdata->level_irq_udelay = value; |
| |
| rc = of_property_read_u32(core_node, "parade,vendor_id", &value); |
| if (!rc) |
| pdata->vendor_id = value; |
| |
| rc = of_property_read_u32(core_node, "parade,product_id", &value); |
| if (!rc) |
| pdata->product_id = value; |
| |
| rc = of_property_read_u32(core_node, "parade,flags", &value); |
| if (!rc) |
| pdata->flags = value; |
| |
| rc = of_property_read_u32(core_node, "parade,easy_wakeup_gesture", |
| &value); |
| if (!rc) |
| pdata->easy_wakeup_gesture = (u8)value; |
| |
| rc = of_property_read_u32(core_node, "parade,config_dut_generation", |
| &value); |
| if (!rc) |
| pdata->config_dut_generation = (u8)value; |
| else { |
| pr_err("%s: dut_generation is not configured, set default: DUT_PIP2_CAPABLE!\n", |
| __func__); |
| pdata->config_dut_generation = CONFIG_DUT_PIP2_CAPABLE; |
| } |
| |
| rc = of_property_read_u32(core_node, "parade,watchdog_force_stop", |
| &value); |
| if (!rc) { |
| if (value) |
| pdata->watchdog_force_stop = true; |
| else |
| pdata->watchdog_force_stop = false; |
| } else { |
| pr_err("%s: watchdog_force_stop is not configured, set default: false!\n", |
| __func__); |
| pdata->watchdog_force_stop = false; |
| } |
| |
| rc = of_property_read_u32(core_node, "parade,panel_id_support", |
| &value); |
| if (!rc) { |
| pdata->panel_id_support = (u8)value; |
| } else { |
| pr_err("%s: panel_id_support is not configured, set default: PT_PANEL_ID_DISABLE\n", |
| __func__); |
| pdata->panel_id_support = PT_PANEL_ID_DISABLE; |
| } |
| |
| rc = of_property_read_u32(core_node, "parade,protocol_mode", |
| &value); |
| if (!rc) { |
| pdata->protocol_mode = (u8)value; |
| } else { |
| pr_err("%s: protocol_mode is not configured, set default: PT_PROTOCOL_MODE_PIP\n", |
| __func__); |
| pdata->protocol_mode = PT_PROTOCOL_MODE_UNKNOWN; |
| } |
| |
| for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) { |
| if (touch_setting_names[i] == NULL) |
| continue; |
| |
| pdata->sett[i] = create_and_get_touch_setting(core_node, |
| touch_setting_names[i]); |
| if (IS_ERR(pdata->sett[i])) { |
| rc = PTR_ERR(pdata->sett[i]); |
| goto fail_free_sett; |
| } else if (pdata->sett[i] == NULL) |
| pr_debug("%s: No data for setting '%s'\n", __func__, |
| touch_setting_names[i]); |
| } |
| |
| pr_debug("%s: irq_gpio:%d rst_gpio:%d\n" |
| "hid_desc_reg:%d level_irq_udelay:%d vendor_id:%d prod_id:%d\n" |
| "flags:%d easy_wakeup_gesture:%d\n", |
| __func__, pdata->irq_gpio, pdata->rst_gpio, pdata->hid_desc_register, |
| pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id, pdata->flags, |
| pdata->easy_wakeup_gesture); |
| |
| pdata->xres = pt_xres; |
| pdata->init = pt_init; |
| pdata->power = pt_power; |
| pdata->detect = pt_detect; |
| pdata->irq_stat = pt_irq_stat; |
| pdata->setup_power = pt_setup_power; |
| pdata->setup_irq = pt_setup_irq; |
| |
| return pdata; |
| |
| fail_free_sett: |
| for (i--; i >= 0; i--) |
| free_touch_setting(pdata->sett[i]); |
| fail_free: |
| kfree(pdata); |
| fail: |
| return ERR_PTR(rc); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: free_core_pdata |
| * |
| * SUMMARY: Free the core module platform data and touch settings data. |
| * |
| * RETURN: |
| * success: the pointer of core platform data |
| * fail : error code with type of error pointer |
| * |
| * PARAMETERS: |
| * *core_node - pointer to device_node structure |
| ******************************************************************************/ |
| static void free_core_pdata(void *pdata) |
| { |
| struct pt_core_platform_data *core_pdata = pdata; |
| unsigned int i; |
| |
| for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++) |
| free_touch_setting(core_pdata->sett[i]); |
| kfree(core_pdata); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: pt_check_dsi_panel_dt |
| * |
| * SUMMARY: Get the DSI active panel information from dtsi |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = fail |
| * |
| * PARAMETERS: |
| * np - pointer to device_node structure |
| * active_panel - name of active DSI panel |
| ******************************************************************************/ |
| |
| static int pt_check_dsi_panel_dt(struct device_node *np, struct drm_panel **active_panel) |
| { |
| int i; |
| int count; |
| struct device_node *node; |
| struct drm_panel *panel; |
| |
| count = of_count_phandle_with_args(np, "panel", NULL); |
| pr_info("%s: Active panel count: %d\n", __func__, count); |
| if (count <= 0) { |
| pr_err("%s: no panel prop in pt_devtree!\n", __func__); |
| return 0; |
| } |
| |
| for (i = 0; i < count; i++) { |
| node = of_parse_phandle(np, "panel", i); |
| panel = of_drm_find_panel(node); |
| of_node_put(node); |
| if (!IS_ERR(panel)) { |
| pr_info("%s: Active panel selected !\n", __func__); |
| *active_panel = panel; |
| return 0; |
| } |
| } |
| pr_err("%s: Active panel NOT selected !\n", __func__); |
| return PTR_ERR(panel); |
| } |
| |
| /******************************************************************************* |
| * FUNCTION: pt_devtree_create_and_get_pdata |
| * |
| * SUMMARY: Parse dts and set up platform data for core module, multi-touch(mt) |
| * module, button(btn) module, proximity module.And Assign platform data |
| * pointer for loader module. |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = failure |
| * |
| * PARAMETERS: |
| * *adap_dev - pointer to device structure |
| ******************************************************************************/ |
| int pt_devtree_create_and_get_pdata(struct device *adap_dev) |
| { |
| struct pt_platform_data *pdata; |
| struct device_node *core_node, *dev_node, *dev_node_fail; |
| enum pt_device_type type; |
| int count = 0; |
| int rc = 0; |
| |
| #if defined(CONFIG_DRM_PANEL) && IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) |
| struct drm_panel *panel = NULL; |
| #endif |
| if (is_create_and_get_pdata == true) |
| return 0; |
| |
| if (!adap_dev->of_node) |
| return 0; |
| |
| pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
| if (!pdata) |
| return -ENOMEM; |
| |
| adap_dev->platform_data = pdata; |
| set_pdata_ptr(pdata); |
| |
| #if defined(CONFIG_DRM_PANEL) && IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) |
| rc = pt_check_dsi_panel_dt(adap_dev->of_node, &panel); |
| if (rc) { |
| pr_err("%s: Panel not selected, rc=%d\n", __func__, rc); |
| if (rc == -EPROBE_DEFER) { |
| pr_err("%s: Probe defer selected, rc=%d\n", __func__, rc); |
| kfree(pdata); |
| return rc; |
| } |
| } |
| pdata->panel = panel; |
| #endif |
| /* There should be only one core node */ |
| for_each_child_of_node(adap_dev->of_node, core_node) { |
| const char *name; |
| |
| rc = of_property_read_string(core_node, "name", &name); |
| if (!rc) |
| pr_debug("%s: name:%s\n", __func__, name); |
| |
| pdata->core_pdata = create_and_get_core_pdata(core_node); |
| if (IS_ERR(pdata->core_pdata)) { |
| rc = PTR_ERR(pdata->core_pdata); |
| of_node_put(core_node); |
| break; |
| } |
| |
| for_each_child_of_node(core_node, dev_node) { |
| count++; |
| rc = get_device_type(dev_node, &type); |
| if (rc) { |
| of_node_put(dev_node); |
| break; |
| } |
| *pdata_ptr[type].pdata |
| = create_and_get_device_pdata(dev_node, type); |
| if (IS_ERR(*pdata_ptr[type].pdata)) |
| rc = PTR_ERR(*pdata_ptr[type].pdata); |
| if (rc) { |
| of_node_put(dev_node); |
| break; |
| } |
| } |
| |
| if (rc) { |
| free_core_pdata(pdata->core_pdata); |
| for_each_child_of_node(core_node, dev_node_fail) { |
| if (dev_node == dev_node_fail) { |
| of_node_put(dev_node_fail); |
| break; |
| } |
| rc = get_device_type(dev_node_fail, &type); |
| if (rc) { |
| of_node_put(dev_node_fail); |
| break; |
| } |
| free_device_pdata(type); |
| } |
| of_node_put(core_node); |
| break; |
| } |
| pdata->loader_pdata = &_pt_loader_platform_data; |
| } |
| is_create_and_get_pdata = true; |
| pr_debug("%s: %d child node(s) found\n", __func__, count); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL_GPL(pt_devtree_create_and_get_pdata); |
| |
| /******************************************************************************* |
| * FUNCTION: pt_devtree_clean_pdata |
| * |
| * SUMMARY: Free all platform data for core module, multi-touch(mt) module, |
| * button(btn) module, proximity module. |
| * |
| * RETURN: |
| * 0 = success |
| * !0 = failure |
| * |
| * PARAMETERS: |
| * *adap_dev - pointer to device structure |
| ******************************************************************************/ |
| int pt_devtree_clean_pdata(struct device *adap_dev) |
| { |
| struct pt_platform_data *pdata; |
| struct device_node *core_node, *dev_node; |
| enum pt_device_type type; |
| int rc = 0; |
| |
| if (!is_create_and_get_pdata) |
| return 0; |
| |
| if (!adap_dev->of_node) |
| return 0; |
| |
| pdata = dev_get_platdata(adap_dev); |
| set_pdata_ptr(pdata); |
| free_core_pdata(pdata->core_pdata); |
| for_each_child_of_node(adap_dev->of_node, core_node) { |
| for_each_child_of_node(core_node, dev_node) { |
| rc = get_device_type(dev_node, &type); |
| if (rc) { |
| of_node_put(dev_node); |
| of_node_put(core_node); |
| goto exit; |
| } |
| free_device_pdata(type); |
| } |
| } |
| |
| exit: |
| is_create_and_get_pdata = false; |
| return rc; |
| } |
| EXPORT_SYMBOL_GPL(pt_devtree_clean_pdata); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver"); |
| MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>"); |