diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Kbuild b/drivers/input/touchscreen/focaltech_touch_v3/Kbuild index 2ed67ed7ddbe..c03284477988 100644 --- a/drivers/input/touchscreen/focaltech_touch_v3/Kbuild +++ b/drivers/input/touchscreen/focaltech_touch_v3/Kbuild @@ -18,6 +18,7 @@ focaltech_v3-objs += focaltech_spi.o focaltech_v3-objs += focaltech_test/focaltech_test.o focaltech_v3-objs += focaltech_test/focaltech_test_ini.o focaltech_v3-objs += focaltech_test/supported_ic/focaltech_test_ft3681.o +focaltech_v3-objs += focaltech_ts_config.o ifneq ($(filter ft3681,$(CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME)),) EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME_FT3681 @@ -71,5 +72,15 @@ ifneq ($(filter m y,$(CONFIG_FTS_LAST_TIME)),) EXTRA_CFLAGS += -DCONFIG_FTS_LAST_TIME endif +ifneq ($(findstring _factory,$(TARGET_PRODUCT)),) +USE_TARGET_PRODUCT := $(strip $(subst _factory,, $(TARGET_PRODUCT))) +else +USE_TARGET_PRODUCT := $(TARGET_PRODUCT) +endif + +ifneq ($(filter bangkk%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_BANGKK +endif + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c index 4cdb9b9c57c4..b75d40dfcac8 100644 --- a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c @@ -1922,6 +1922,21 @@ static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); #endif + pdata->edge_ctrl = of_property_read_bool(np, + "focaltech,edge-ctrl"); + if (pdata->edge_ctrl) + FTS_INFO("support focaltech edge mode"); + + pdata->interpolation_ctrl = of_property_read_bool(np, + "focaltech,interpolation-ctrl"); + if (pdata->interpolation_ctrl) + FTS_INFO("support focaltech interpolation mode"); + + pdata->report_rate_ctrl = of_property_read_bool(np, + "focaltech,report_rate-ctrl"); + if (pdata->report_rate_ctrl) + FTS_INFO("support focaltech report rate switch mode"); + FTS_FUNC_EXIT(); return 0; } diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h index ffff53ae435c..ae5c7c5f4042 100644 --- a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h @@ -157,6 +157,9 @@ struct fts_ts_platform_data { u32 y_min; u32 max_touch_number; bool pocket_mode_ctrl; + bool edge_ctrl; + bool interpolation_ctrl; + bool report_rate_ctrl; }; struct ts_event { @@ -170,6 +173,9 @@ struct ts_event { struct fts_mode_info { int pocket_mode; + int interpolation; + int report_rate_mode; + int edge_mode[2]; }; #ifdef FOCALTECH_PALM_SENSOR_EN @@ -220,6 +226,7 @@ struct fts_ts_data { int log_level; int fw_is_running; /* confirm fw is running when using spi:default 0 */ int dummy_byte; + int refresh_rate; #if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM struct completion pm_completion; bool pm_suspend; diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c new file mode 100644 index 000000000000..23e2a29673c2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include "focaltech_ts_config.h" + +struct focaltech_ic_report_rate_config report_rate_config_info = { +#if defined(PRODUCT_BANGKK) + .rate_config_count = 2, + .refresh_rate_ctrl = 0, + .interpolation_ctrl = 1, + { + { + .interpolation_flag = 0, + .report_rate = 240, + .command = 0, + }, + { + .interpolation_flag = 1, + .report_rate = 360, + .command = 1, + }, + } +#endif +}; + +int fts_mmi_get_report_rate(struct fts_ts_data *ts_data) +{ + int refresh_rate_ctrl = 0; + int interpolation_ctrl = 0; + int interpolation_flag = 0; + int refresh_rate = 0; + int i = 0; + + refresh_rate_ctrl = ts_data->pdata->report_rate_ctrl; + interpolation_ctrl = ts_data->pdata->interpolation_ctrl; + + interpolation_flag = ts_data->get_mode.interpolation; + refresh_rate = ts_data->refresh_rate; + + FTS_DEBUG("refresh_rate_ctrl: %d, interpolation_ctrl: %d, interpolation_flag: %d, refresh_rate: %d", + refresh_rate_ctrl, interpolation_ctrl, interpolation_flag, refresh_rate); + + if (refresh_rate_ctrl == 0 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if (interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) && + ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1]))) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 0) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1])) { + break; + } + } + } else { + //refresh_rate_ctrl = 0, interpolation_ctrl = 0 + i = 0; + } + + if (i == report_rate_config_info.rate_config_count) { + FTS_ERROR("Get config report rate fail"); + return -1; + } else { + FTS_DEBUG("Get config report rate %dHZ, command : 0x%02x ", + report_rate_config_info.report_rate_info[i].report_rate, + report_rate_config_info.report_rate_info[i].command); + return report_rate_config_info.report_rate_info[i].command; + } +} + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h new file mode 100644 index 000000000000..95b7a154a945 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef __FOCALTECH_TS_CONFIG_H__ +#define __FOCALTECH_TS_CONFIG_H__ + +#include +#include "focaltech_core.h" + +#define MAX_REPORT_RATE_CONFIG 12 + +#define REPORT_RATE_CMD_240HZ 0 +#define REPORT_RATE_CMD_360HZ 1 + + +struct report_rate_config { + bool interpolation_flag; //if enable interpolation report rate + u16 refresh_rate[2]; //display refresh rate + u16 report_rate; //touch report rate + u16 command; //report rate switch command +}; + +struct focaltech_ic_report_rate_config { + u8 rate_config_count; //the count of report rate combination + bool refresh_rate_ctrl; //if support report rate change according to refresh rate + bool interpolation_ctrl; //if support interpolation report rate + struct report_rate_config report_rate_info[MAX_REPORT_RATE_CONFIG]; +}; + +int fts_mmi_get_report_rate(struct fts_ts_data *ts_data); + +#endif diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c index d904d4c4317c..da3226140e35 100644 --- a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c @@ -15,7 +15,34 @@ #include #include #include "focaltech_core.h" +#include "focaltech_ts_config.h" +#define EDGE_SWITCH_CMD 0x17 +#define ROTATE_DEFAULT_0 0x00 +#define ROTATE_LEFT_90 0X40 +#define ROTATE_RIGHT_90 0x80 +#define DEFAULT_EDGE 0x00 +#define SMALL_EDGE 0x40 +#define BIG_EDGE 0x80 + +/* hal settings */ +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define DEFAULT_MODE 0 + +#define NORMAL_DEFAULT_MODE 10 +#define NORMAL_SMALL_MODE 11 +#define NORMAL_BIG_MODE 12 + +#define NORMAL_DEFAULT_EDGE 0x08 +#define NORMAL_SMALL_EDGE 0x48 +#define NORMAL_BIG_EDGE 0x88 + +#define FTS_CMD_REPORT_RATE_ADDR 0x8E #ifdef CONFIG_INPUT_TOUCHSCREEN_MMI //extern int ts_mmi_dev_register(struct device *parent, struct ts_mmi_methods *mdata); //extern void ts_mmi_dev_unregister(struct device *parent); @@ -30,6 +57,19 @@ } #define MAX_ATTRS_ENTRIES 10 +static ssize_t fts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_edge_show, fts_edge_store); +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_interpolation_show, fts_interpolation_store); #define ADD_ATTR(name) { \ if (idx < MAX_ATTRS_ENTRIES) { \ @@ -46,6 +86,245 @@ static struct attribute_group ext_attr_group = { .attrs = ext_attributes, }; +int fts_set_edge_mode(struct fts_mode_info mode) +{ + u8 reg_value_8c = 0; + u8 reg_value_8d = 0; + int ret = 0; + + switch (mode.edge_mode[0]) { + case ROTATE_DEFAULT_0: + reg_value_8c = 0; + break; + case ROTATE_LEFT_90: + reg_value_8c = 1; + break; + case ROTATE_RIGHT_90: + reg_value_8c = 2; + break; + default: + FTS_ERROR("Invalid edge mode!"); + return -EINVAL; + } + + switch (mode.edge_mode[1]) { + case DEFAULT_EDGE: + reg_value_8d = 0; + break; + case SMALL_EDGE: + reg_value_8d = 1; + break; + case BIG_EDGE: + reg_value_8d = 2; + break; + case NORMAL_DEFAULT_EDGE: + reg_value_8d = 3; + break; + case NORMAL_SMALL_EDGE: + reg_value_8d = 4; + break; + case NORMAL_BIG_EDGE: + reg_value_8d = 5; + break; + default: + FTS_ERROR("Invalid edge mode!"); + return -EINVAL; + } + + //In landscape mode and not in normal edge suppression + if((reg_value_8c > 0) && (reg_value_8d < 3)) + reg_value_8d++; + + ret = fts_write_reg(0x8C, reg_value_8c); + if (ret < 0) { + FTS_ERROR("set reg 0x8C fail, ret=%d", ret); + return -EINVAL; + } + + ret = fts_write_reg(0x8D, reg_value_8d); + if (ret < 0) { + FTS_ERROR("set reg 0x8D fail, ret=%d", ret); + return -EINVAL; + } + + FTS_INFO("Write edge value (%02x %02x)", + reg_value_8c, reg_value_8d); + return 0; +} + +static ssize_t fts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + int edge_cmd[2] = { 0 }; + unsigned int args[2] = { 0 }; + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + + switch (args[0]) { + case DEFAULT_MODE: + edge_cmd[1] = DEFAULT_EDGE; + break; + case SMALL_MODE: + edge_cmd[1] = SMALL_EDGE; + break; + case BIG_MODE: + edge_cmd[1] = BIG_EDGE; + break; + case NORMAL_DEFAULT_MODE: + edge_cmd[1] = NORMAL_DEFAULT_EDGE; + break; + case NORMAL_SMALL_MODE: + edge_cmd[1] = NORMAL_SMALL_EDGE; + break; + case NORMAL_BIG_MODE: + edge_cmd[1] = NORMAL_BIG_EDGE; + break; + default: + FTS_ERROR("Invalid edge mode: %d!", args[0]); + return -EINVAL; + } + + if (ROTATE_0 == args[1]) { + edge_cmd[0] = ROTATE_DEFAULT_0; + } else if (ROTATE_90 == args[1]) { + edge_cmd[0] = ROTATE_RIGHT_90; + } else if (ROTATE_270 == args[1]) { + edge_cmd[0] = ROTATE_LEFT_90; + } else { + FTS_ERROR("Invalid rotation mode: %d!", args[1]); + return -EINVAL; + } + + mutex_lock(&ts_data->mode_lock); + memcpy(ts_data->get_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + if (!memcmp(ts_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd))) { + FTS_INFO("The value (%02x %02x) is same,so not write.", + edge_cmd[0], edge_cmd[1]); + ret = size; + goto exit; + } + + if (ts_data->power_disabled) { + FTS_ERROR("The touch is in sleep state, restore the value when resume"); + ret = size; + goto exit; + } + + ret = fts_set_edge_mode(ts_data->get_mode); + if (ret < 0) { + FTS_ERROR("failed to send edge switch cmd"); + goto exit; + } + + memcpy(ts_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + msleep(20); + ret = size; + FTS_INFO("Success to set edge = %02x, rotation = %02x", edge_cmd[1], edge_cmd[0]); + +exit: + mutex_unlock(&ts_data->mode_lock); + return ret; +} + +static ssize_t fts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_INFO("edge area = %02x, rotation = %02x\n", + ts_data->set_mode.edge_mode[1], ts_data->set_mode.edge_mode[0]); + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x", + ts_data->set_mode.edge_mode[1], ts_data->set_mode.edge_mode[0]); +} + +static int fts_mmi_set_report_rate(struct fts_ts_data *ts_data) +{ + int ret = 0; + int mode = 0; + + mode = fts_mmi_get_report_rate(ts_data); + if (mode == -1) { + return -EINVAL; + } + + ts_data->get_mode.report_rate_mode = mode; + if (ts_data->set_mode.report_rate_mode == mode) { + FTS_DEBUG("The value = %d is same, so not to write", mode); + return 0; + } + + if (ts_data->power_disabled) { + FTS_DEBUG("The touch is in sleep state, restore the value when resume\n"); + return 0; + } + + ret = fts_write_reg(FTS_CMD_REPORT_RATE_ADDR, mode); + if (ret < 0) { + FTS_ERROR("failed to set report rate, mode = %d", mode); + return -EINVAL; + } + msleep(20); + + ts_data->set_mode.report_rate_mode = mode; + + FTS_INFO("Success to set %s\n", mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + "Unsupported")); + + return ret; +} + +static ssize_t fts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + FTS_ERROR("Failed to convert value."); + return -EINVAL; + } + + mutex_lock(&ts_data->mode_lock); + ts_data->get_mode.interpolation = mode; + ret = fts_mmi_set_report_rate(ts_data); + if (ret < 0) + goto exit; + + ret = size; + ts_data->set_mode.interpolation = mode; +exit: + mutex_unlock(&ts_data->mode_lock); + return ret; +} + +static ssize_t fts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_INFO("interpolation = %d.", ts_data->set_mode.interpolation); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts_data->set_mode.interpolation); +} + static int fts_mmi_methods_get_vendor(struct device *dev, void *cdata) { return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "focaltech"); @@ -356,11 +635,12 @@ static int fts_mmi_pre_resume(struct device *dev) static int fts_mmi_post_resume(struct device *dev) { struct fts_ts_data *ts_data; + struct fts_ts_platform_data *pdata; int ret = 0; uint8_t read_data = 0; GET_TS_DATA(dev); - + pdata = ts_data->pdata; #ifdef FOCALTECH_SENSOR_EN ts_data->fod_suspended = false; #endif @@ -368,8 +648,8 @@ static int fts_mmi_post_resume(struct device *dev) #ifdef FOCALTECH_SENSOR_EN if(!fts_is_fod_resume(ts_data)) { #endif - FTS_INFO("Reset IC in resume"); - fts_reset_proc(200); + FTS_INFO("Reset IC in post resume"); + fts_reset_proc(200); #ifdef FOCALTECH_SENSOR_EN } #endif @@ -422,6 +702,30 @@ exit: } } + if (pdata->interpolation_ctrl && ts_data->get_mode.interpolation) { + ret = fts_write_reg(FTS_CMD_REPORT_RATE_ADDR, ts_data->get_mode.report_rate_mode); + if (ret >= 0) { + ts_data->set_mode.interpolation = ts_data->get_mode.interpolation; + ts_data->set_mode.report_rate_mode = ts_data->get_mode.report_rate_mode; + + FTS_INFO("Success to %s interpolation mode\n", + ts_data->get_mode.report_rate_mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (ts_data->get_mode.report_rate_mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + "Unsupported")); + } + } + + if (pdata->edge_ctrl) { + ret = fts_set_edge_mode(ts_data->get_mode); + if (!ret) { + memcpy(ts_data->set_mode.edge_mode, ts_data->get_mode.edge_mode, + sizeof(ts_data->get_mode.edge_mode)); + msleep(20); + FTS_INFO("Success to set edge area = %02x, rotation = %02x", + ts_data->get_mode.edge_mode[1], ts_data->get_mode.edge_mode[0]); + } + } + mutex_unlock(&ts_data->mode_lock); FTS_FUNC_EXIT(); @@ -602,6 +906,12 @@ static int fts_mmi_extend_attribute_group(struct device *dev, struct attribute_g GET_TS_DATA(dev); pdata = ts_data->pdata; + if (pdata->edge_ctrl) + ADD_ATTR(edge); + + if (pdata->interpolation_ctrl) + ADD_ATTR(interpolation); + #ifdef CONFIG_FTS_LAST_TIME ADD_ATTR(timestamp); #endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild index b8178fde323d..00e3540b6281 100644 --- a/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild @@ -110,6 +110,8 @@ else ifneq ($(filter zeekr%, $(USE_TARGET_PRODUCT)), ) EXTRA_CFLAGS += -DPRODUCT_ZEEKR else ifneq ($(filter lynkco%, $(USE_TARGET_PRODUCT)), ) EXTRA_CFLAGS += -DPRODUCT_LYNKCO +else ifneq ($(filter bangkk%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_BANGKK endif $(warning "william EXTRA_CFLAGS = $(EXTRA_CFLAGS)") diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c index ff3964bf2cbf..5bde88f8c6e0 100644 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c @@ -15,7 +15,7 @@ struct goodix_ic_report_rate_config report_rate_config_info = { #if defined(PRODUCT_LI) || defined(PRODUCT_ONELI) || defined(PRODUCT_TUNDRA) \ ||defined(PRODUCT_EQS) || defined(PRODUCT_RTWO) || defined(PRODUCT_BRONCO) \ - || defined(PRODUCT_ZEEKR) || defined(PRODUCT_LYNKCO) + || defined(PRODUCT_ZEEKR) || defined(PRODUCT_LYNKCO) || defined(PRODUCT_BANGKK) .rate_config_count = 2, .refresh_rate_ctrl = 0, .interpolation_ctrl = 1,