diff --git a/.gitignore b/.gitignore index 0c5aa0916126..2c95bf2e6dab 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ out .*.swp *.deb *.changes +.vscode* \ No newline at end of file diff --git a/arch/arm64/boot/dts/qcom/comma3.dtsi b/arch/arm64/boot/dts/qcom/comma3.dtsi index 710d2b353c39..7a586bd59ccb 100644 --- a/arch/arm64/boot/dts/qcom/comma3.dtsi +++ b/arch/arm64/boot/dts/qcom/comma3.dtsi @@ -73,6 +73,7 @@ interrupt-gpios = <&tlmm 125 0x2002>; reset-gpios = <&tlmm 104 0>; ta-gpios = <&tlmm 128 0>; + vdd-supply = <&tp3v3>; }; }; @@ -151,6 +152,7 @@ gpio = <&pm8998_gpios 10 0>; regulator-boot-on; + regulator-always-on; }; ssd3v3: ssd3v3 { diff --git a/arch/arm64/configs/tici_defconfig b/arch/arm64/configs/tici_defconfig index e9907453e9e2..b47390f8e0b0 100644 --- a/arch/arm64/configs/tici_defconfig +++ b/arch/arm64/configs/tici_defconfig @@ -1232,7 +1232,8 @@ CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y CONFIG_FIRMWARE_IN_KERNEL=y -CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_FIRMWARE="samsung_touch.img" +# CONFIG_EXTRA_FIRMWARE_DIR is not set CONFIG_FW_LOADER_USER_HELPER=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y # CONFIG_FW_CACHE is not set @@ -1317,7 +1318,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_ATA_OVER_ETH is not set # CONFIG_BLK_DEV_RBD is not set # CONFIG_BLK_DEV_RSXX is not set -# CONFIG_BLK_DEV_NVME is not set CONFIG_NVME_CORE=y CONFIG_BLK_DEV_NVME=y # CONFIG_NVME_TARGET is not set diff --git a/drivers/input/touchscreen/samsung_touch.c b/drivers/input/touchscreen/samsung_touch.c index b33b352d14c4..93cbdd87ef9b 100644 --- a/drivers/input/touchscreen/samsung_touch.c +++ b/drivers/input/touchscreen/samsung_touch.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define SS_I2C_NAME "samsung_i2c_touchpanel" @@ -40,6 +41,15 @@ /* Touchscreen registers */ #define SS_ONE_EVENT_CMD 0x60 #define SS_ONE_EVENT_SIZE 15 +#define SS_BOOT_STATUS_CMD 0x55 +#define SS_ENTER_FW_MODE_CMD 0x57 +#define SS_CHIP_ID_CMD 0x52 +#define SS_FLASH_ERASE_CMD 0xD8 +#define SS_FLASH_WRITE_CMD 0xD9 +#define SS_PROTOCOL_ID_CMD 0xDD +#define SS_SW_RESET_APP_CMD 0x12 +#define SS_SW_RESET_BOOT_CMD 0x42 +#define SS_ENABLE_SENSING_CMD 0x10 /* Touchscreen events */ #define SS_EVENT_COORDINATE 0 @@ -53,20 +63,60 @@ #define SS_EVENT_COORDINATE_ACTION_MOVE 2 #define SS_EVENT_COORDINATE_ACTION_UP 3 +/* Touchscreen ACKs */ +#define SS_ACK_BOOT_COMPLETE 0x00 + /* Constants */ #define SS_STOP 0 #define SS_CONTINUE 1 - +#define SS_BOOT_STATUS_BOOT 0x10 +#define SS_BOOT_STATUS_APP 0x20 +#define SS_FIRMWARE "samsung_touch.img" +#define SS_HEADER_SIGNATURE 0x53494654 +#define SS_CHUNK_SIGNATURE 0x53434654 +#define SS_PAGE_SIZE 256 +#define SS_PROTOCOL_APP 0x1 +uint8_t expected_chip_id[3] = {0xBA, 0xB6, 0x61}; struct ss_ts_data { struct i2c_client *client; struct input_dev *input; + struct regulator *vdd; struct gpio_desc *interrupt_gpio; struct gpio_desc *reset_gpio; struct gpio_desc *ta_gpio; + + uint8_t chip_id[3]; }; +/* Firmware header structures */ +typedef struct { + uint32_t signature; + uint32_t version; + uint32_t totalsize; + uint32_t checksum; + uint32_t img_ver; + uint32_t img_date; + uint32_t img_description; + uint32_t fw_ver; + uint32_t fw_date; + uint32_t fw_description; + uint32_t para_ver; + uint32_t para_date; + uint32_t para_description; + uint32_t num_chunk; + uint32_t reserved1; + uint32_t reserved2; +} ss_ts_fw_header; + +typedef struct { + uint32_t signature; + uint32_t addr; + uint32_t size; + uint32_t reserved; +} ss_ts_fw_chunk; + /* Event structures */ struct sec_ts_event_coordinate { uint8_t eid:2; @@ -102,6 +152,33 @@ static int ss_read(struct ss_ts_data *ts, uint8_t command, uint8_t *data, uint8_ return 0; } +static int ss_write(struct ss_ts_data *ts, uint8_t command, uint8_t *data, uint8_t len) +{ + int ret; + ret = i2c_smbus_write_i2c_block_data(ts->client, command, len, data); + if(ret != 0){ + dev_err(&ts->client->dev, "%s: i2c_smbus_write_i2c_block_data: %d\n", __func__, ret); + return ret; + } + return 0; +} + +// This doesn't have the 32 byte limit +static int ss_write_bulk(struct ss_ts_data *ts, uint8_t command, uint8_t *data, uint16_t len) +{ + int ret; + uint8_t buffer[len + 1]; + buffer[0] = command; + memcpy(&buffer[1], data, len); + + ret = i2c_master_send(ts->client, buffer, len + 1); + if(ret != (len + 1)){ + dev_err(&ts->client->dev, "%s: i2c_master_send: %d\n", __func__, ret); + return ret; + } + return 0; +} + static int ss_report_touch(struct ss_ts_data *ts, int touch_id, int action, int x, int y, int z, int type, int major, int minor, int ewx, int ewy) { input_mt_slot(ts->input, touch_id); @@ -117,7 +194,7 @@ static int ss_report_touch(struct ss_ts_data *ts, int touch_id, int action, int break; case SS_EVENT_COORDINATE_ACTION_UP: input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false); - break; + break; } input_mt_report_pointer_emulation(ts->input, true); input_sync(ts->input); @@ -133,7 +210,7 @@ static int ss_handle_coordinate_event(struct ss_ts_data *ts, uint8_t *event) touch_id = (event_coord->tid - 1); if(touch_id < SS_MAX_FINGERS + SS_MAX_HOVER && touch_id >= 0){ - dev_dbg(&ts->client->dev, "%s: coordinate event: ID: %d ACT: %d X: %d, Y: %d Z: %d Type: %d Major: %d Minor: %d WX: %d WY: %d EWX: %d EWY: %d", + dev_dbg(&ts->client->dev, "%s: coordinate event: ID: %d ACT: %d X: %d, Y: %d Z: %d Type: %d Major: %d Minor: %d WX: %d WY: %d EWX: %d EWY: %d\n", __func__, touch_id, event_coord->tchsta, @@ -149,7 +226,7 @@ static int ss_handle_coordinate_event(struct ss_ts_data *ts, uint8_t *event) ret = ss_report_touch( ts, - touch_id, + touch_id, event_coord->tchsta, (event_coord->x_11_4 << 4) | (event_coord->x_3_0), (event_coord->y_11_4 << 4) | (event_coord->y_3_0), @@ -161,7 +238,7 @@ static int ss_handle_coordinate_event(struct ss_ts_data *ts, uint8_t *event) event_coord->ewy ); if(ret < 0){ - dev_err(&ts->client->dev, "%s: report touch failed: %d", __func__, ret); + dev_err(&ts->client->dev, "%s: report touch failed: %d\n", __func__, ret); } // Maybe there's more??? @@ -239,12 +316,294 @@ static void ss_ts_reset(struct ss_ts_data *ts) } } +static int ss_get_boot_status(struct ss_ts_data *ts) +{ + int ret = 0; + uint8_t result[1]; + ret = ss_read(ts, SS_BOOT_STATUS_CMD, result, 1); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: reading boot status: %d\n", __func__, ret); + return ret; + } + + return result[0]; +} + +static int ss_get_protocol_id(struct ss_ts_data *ts) +{ + int ret = 0; + uint8_t result[1]; + ret = ss_read(ts, SS_PROTOCOL_ID_CMD, result, 1); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: reading protocol id: %d\n", __func__, ret); + return ret; + } + + return result[0]; +} + +static int ss_enter_boot_mode(struct ss_ts_data *ts) +{ + int ret = 0; + int boot_status = 0; + uint8_t firmware_password[2] = {0x55, 0xAC}; + boot_status = ss_get_boot_status(ts); + if(boot_status < 0) return boot_status; + + if(boot_status != SS_BOOT_STATUS_BOOT){ + // Enter firmware mode + ret = ss_write(ts, SS_ENTER_FW_MODE_CMD, firmware_password, sizeof(firmware_password)); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: entering fw mode: %d\n", __func__, ret); + return ret; + } + + msleep(200); + + // Check that it worked + boot_status = ss_get_boot_status(ts); + if(boot_status != SS_BOOT_STATUS_BOOT) { + dev_err(&ts->client->dev, "%s: didn't enter fw mode. Boot status: %d\n", __func__, boot_status); + return -1; + } + } + + return 0; +} + +static int ss_wait_for_ack(struct ss_ts_data *ts, uint8_t ack) +{ + int ret = 0; + uint8_t event[8]; + uint32_t i; + + dev_info(&ts->client->dev, "%s: waiting for ack %d\n", __func__, ack); + for(i = 0; i < 100; i++){ + // Read event + ret = ss_read(ts, SS_ONE_EVENT_CMD, event, 8); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: reading event failed: %d\n", __func__, ret); + return ret; + } + + // Check if it's right + if(((event[0] >> 2) & 0xF) == ack){ + dev_info(&ts->client->dev, "%s: ack received\n", __func__); + return 0; + } + + msleep(20); + } + dev_err(&ts->client->dev, "%s: timed out\n", __func__); + return -ETIMEDOUT; +} + +static int ss_exit_boot_mode(struct ss_ts_data *ts) +{ + int ret = 0; + int boot_status = 0, protocol_id; + uint8_t firmware_password[2] = {0x55, 0xAC}; + boot_status = ss_get_boot_status(ts); + if(boot_status < 0) return boot_status; + + if(boot_status != SS_BOOT_STATUS_APP){ + // Software reset + protocol_id = ss_get_protocol_id(ts); + if(IS_ERR(protocol_id)){ + dev_err(&ts->client->dev, "%s: couldn't read protocol ID: %d\n", __func__, protocol_id); + return protocol_id; + } + + ret = ss_write(ts, (protocol_id == SS_PROTOCOL_APP) ? SS_SW_RESET_APP_CMD : SS_SW_RESET_BOOT_CMD, NULL, 0); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: couldn't perform SW reset: %d\n", __func__, ret); + return ret; + } + + msleep(100); + + + // Wait for boot acknowledge + ret = ss_wait_for_ack(ts, SS_ACK_BOOT_COMPLETE); + if(ret != 0){ + dev_err(&ts->client->dev, "%s: SW reset failed: %d\n", __func__, ret); + return ret; + } + + // Check that it worked + boot_status = ss_get_boot_status(ts); + if(boot_status != SS_BOOT_STATUS_APP) { + dev_err(&ts->client->dev, "%s: didn't exit fw mode. Boot status: %d\n", __func__, boot_status); + return -1; + } + } + + return 0; +} + +static int ss_read_chip_id(struct ss_ts_data *ts) +{ + int ret = 0; + ret = ss_read(ts, SS_CHIP_ID_CMD, ts->chip_id, 3); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: reading chip id: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int ss_power_init(struct ss_ts_data *ts) +{ + int ret = 0; + ts->vdd = devm_regulator_get(&ts->client->dev, "vdd"); + if(IS_ERR(ts->vdd)){ + ret = PTR_ERR(ts->vdd); + if(ret == -EPROBE_DEFER){ + dev_info(&ts->client->dev, "%s: regulator vdd not ready. Deferring probe...\n", __func__); + } else { + dev_err(&ts->client->dev, "%s: getting regulator vdd: %d\n", __func__, ret); + } + goto err; + } + + // Enabling crashes the kernel. + // Not sure why, but it's a fixed regulator which is always enabled anyway + //ret = regulator_enable(ts->vdd); + if(ret){ + dev_err(&ts->client->dev, "%s: enabling regulator vdd: %d\n", __func__, ret); + } +err: + return ret; +} + +static int ss_erase_pages(struct ss_ts_data *ts, uint32_t start_page, uint32_t len) +{ + uint8_t data[4]; + data[0] = (uint8_t)((start_page >> 8) & 0xFF); + data[1] = (uint8_t)(start_page & 0xFF); + data[2] = (uint8_t)((len >> 8) & 0xFF); + data[3] = (uint8_t)(len & 0xFF); + + dev_info(&ts->client->dev, "%s: erasing pages: 0x%x - 0x%x\n", __func__, start_page, start_page + len); + return ss_write(ts, SS_FLASH_ERASE_CMD, data, sizeof(data)); +} + +static uint8_t ss_calculate_checksum(uint8_t *data, uint32_t len){ + uint8_t result = 0; + uint32_t i; + for(i = 0; iclient->dev, "%s: non-aligned flashing is not supported!\n", __func__); + return -EINVAL; + } + + ret = ss_erase_pages(ts, start_page, num_pages); + if(IS_ERR(ret)){ + dev_err(&ts->client->dev, "%s: failed to erase pages: %d\n", __func__, ret); + goto err; + } + msleep(100); + + dev_info(&ts->client->dev, "%s: flashing pages: 0x%x - 0x%x\n", __func__, start_page, start_page + num_pages); + for(i = 0; i> 8) & 0xFF); + buf[1] = (uint8_t)(page_index & 0xFF); + + // Append data (zero padded) + memset(&buf[2], 0, SS_PAGE_SIZE); + data_len = min(SS_PAGE_SIZE, (len - (i * SS_PAGE_SIZE))); + memcpy(&buf[2], (data + (i * SS_PAGE_SIZE)), data_len); + + // Append checksum + buf[SS_PAGE_SIZE + 2] = ss_calculate_checksum(buf, (SS_PAGE_SIZE + 2)); + + // Write the page + ret = ss_write_bulk(ts, SS_FLASH_WRITE_CMD, buf, sizeof(buf)); + if(IS_ERR(ret)){ + dev_err(&ts->client->dev, "%s: failed to write page %d: %d\n", __func__, page_index, ret); + goto err; + } + msleep(5); + } + dev_info(&ts->client->dev, "%s: flashed pages: 0x%x - 0x%x\n", __func__, start_page, start_page + num_pages); + +err: + return ret; +} + +static int ss_flash_firmware(struct ss_ts_data *ts, struct firmware *fw) +{ + int ret = 0; + uint32_t i; + uint8_t *data_ptr = (uint8_t *) fw->data; + ss_ts_fw_header *header; + ss_ts_fw_chunk *chunk_header; + + // Parse header + header = (ss_ts_fw_header *) data_ptr; + data_ptr += sizeof(ss_ts_fw_header); + if(header->signature != SS_HEADER_SIGNATURE){ + dev_err(&ts->client->dev, "%s: header signature mismatch\n", __func__); + return -ENODATA; + } + + for (i = 0; inum_chunk; i++){ + // Parse chunk + chunk_header = (ss_ts_fw_chunk *) data_ptr; + data_ptr += sizeof(ss_ts_fw_chunk); + if(chunk_header->signature != SS_CHUNK_SIGNATURE){ + dev_err(&ts->client->dev, "%s: chunk %d signature mismatch\n", __func__, i); + return -ENODATA; + } + + // Flash chunk + ret = ss_flash_memory(ts, chunk_header->addr, data_ptr, chunk_header->size); + if(IS_ERR(ret)){ + return ret; + } + data_ptr += chunk_header->size; + + // TODO: check that flashed pages match + } +} + +static int ss_enable_sensing(struct ss_ts_data *ts) +{ + int ret = 0; + ret = ss_write(ts, SS_ENABLE_SENSING_CMD, NULL, 0); + if(ret < 0){ + dev_err(&ts->client->dev, "%s: enabling sensing failed: %d\n", __func__, ret); + return ret; + } + dev_info(&ts->client->dev, "%s: enabled sensing\n", __func__); + + return 0; +} + static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ss_ts_data *ts; - int error; + struct firmware *fw; + int error, boot_status; - dev_info(&client->dev, "SAMSUNG PANEL probe"); + dev_info(&client->dev, "SAMSUNG PANEL probe\n"); ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); if (!ts) @@ -253,6 +612,12 @@ static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id ts->client = client; i2c_set_clientdata(client, ts); + // Init regulator + error = ss_power_init(ts); + if(IS_ERR(error)) { + return error; + } + ts->interrupt_gpio = devm_gpiod_get(&client->dev, "interrupt", GPIOD_IN); if (IS_ERR(ts->interrupt_gpio)) { error = PTR_ERR(ts->interrupt_gpio); @@ -277,8 +642,56 @@ static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id return error; } + // Reset panel ss_ts_reset(ts); + // Check that the panel is running in app mode, otherwise flash + boot_status = ss_get_boot_status(ts); + if (boot_status != SS_BOOT_STATUS_APP) { + dev_info(&client->dev, "Device not in app mode (current status: 0x%x). Attempting reflash\n", boot_status); + + // Enter boot mode + error = ss_enter_boot_mode(ts); + if(error < 0) { + return error; + } + + // Check that the chip ID matches + error = ss_read_chip_id(ts); + if(error < 0) { + return error; + } else if(memcmp(ts->chip_id, expected_chip_id, sizeof(expected_chip_id))){ + dev_err(&client->dev, "Chip ID does not match expected: 0x%x%x%x\n", ts->chip_id[0], ts->chip_id[1], ts->chip_id[2]); + return -1; + } + + // Load firmware + error = request_firmware(&fw, SS_FIRMWARE, &client->dev); + if(error < 0){ + dev_err(&client->dev, "firmware request failed: %d\n", error); + return error; + } + + // Flash firmware + error = ss_flash_firmware(ts, fw); + if(error < 0){ + dev_err(&client->dev, "firmware flash failed: %d\n", error); + return error; + } + + // Exit boot mode + error = ss_exit_boot_mode(ts); + if(error < 0) { + return error; + } + } + + // Enable sensing + error = ss_enable_sensing(ts); + if(error < 0) { + return error; + } + ts->input = devm_input_allocate_device(&client->dev); if (!ts->input) { dev_err(&client->dev, "failed to allocate input device\n"); @@ -316,12 +729,15 @@ static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id return error; } + // Handle all events currently stored + ss_ts_irq_handler(client->irq, ts); + return 0; } static int ss_ts_remove(struct i2c_client *client) { - dev_info(&client->dev, "SAMSUNG PANEL remove"); + dev_info(&client->dev, "SAMSUNG PANEL remove\n"); return 0; } diff --git a/firmware/samsung_touch.img b/firmware/samsung_touch.img new file mode 100644 index 000000000000..35985d3a7d7e Binary files /dev/null and b/firmware/samsung_touch.img differ