mirror of
https://github.com/commaai/agnos-kernel-sdm845.git
synced 2026-06-08 11:24:51 +08:00
Touch firmware flasher (#21)
* wip: touch firmware flasher * almost working * working now
This commit is contained in:
committed by
Willem Melching
parent
964407ab97
commit
007cde2c3d
1
.gitignore
vendored
1
.gitignore
vendored
@@ -122,3 +122,4 @@ out
|
|||||||
.*.swp
|
.*.swp
|
||||||
*.deb
|
*.deb
|
||||||
*.changes
|
*.changes
|
||||||
|
.vscode*
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
interrupt-gpios = <&tlmm 125 0x2002>;
|
interrupt-gpios = <&tlmm 125 0x2002>;
|
||||||
reset-gpios = <&tlmm 104 0>;
|
reset-gpios = <&tlmm 104 0>;
|
||||||
ta-gpios = <&tlmm 128 0>;
|
ta-gpios = <&tlmm 128 0>;
|
||||||
|
vdd-supply = <&tp3v3>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,6 +152,7 @@
|
|||||||
|
|
||||||
gpio = <&pm8998_gpios 10 0>;
|
gpio = <&pm8998_gpios 10 0>;
|
||||||
regulator-boot-on;
|
regulator-boot-on;
|
||||||
|
regulator-always-on;
|
||||||
};
|
};
|
||||||
|
|
||||||
ssd3v3: ssd3v3 {
|
ssd3v3: ssd3v3 {
|
||||||
|
|||||||
@@ -1232,7 +1232,8 @@ CONFIG_STANDALONE=y
|
|||||||
CONFIG_PREVENT_FIRMWARE_BUILD=y
|
CONFIG_PREVENT_FIRMWARE_BUILD=y
|
||||||
CONFIG_FW_LOADER=y
|
CONFIG_FW_LOADER=y
|
||||||
CONFIG_FIRMWARE_IN_KERNEL=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=y
|
||||||
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
|
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
|
||||||
# CONFIG_FW_CACHE is not set
|
# CONFIG_FW_CACHE is not set
|
||||||
@@ -1317,7 +1318,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
|
|||||||
# CONFIG_ATA_OVER_ETH is not set
|
# CONFIG_ATA_OVER_ETH is not set
|
||||||
# CONFIG_BLK_DEV_RBD is not set
|
# CONFIG_BLK_DEV_RBD is not set
|
||||||
# CONFIG_BLK_DEV_RSXX is not set
|
# CONFIG_BLK_DEV_RSXX is not set
|
||||||
# CONFIG_BLK_DEV_NVME is not set
|
|
||||||
CONFIG_NVME_CORE=y
|
CONFIG_NVME_CORE=y
|
||||||
CONFIG_BLK_DEV_NVME=y
|
CONFIG_BLK_DEV_NVME=y
|
||||||
# CONFIG_NVME_TARGET is not set
|
# CONFIG_NVME_TARGET is not set
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#define SS_I2C_NAME "samsung_i2c_touchpanel"
|
#define SS_I2C_NAME "samsung_i2c_touchpanel"
|
||||||
@@ -40,6 +41,15 @@
|
|||||||
/* Touchscreen registers */
|
/* Touchscreen registers */
|
||||||
#define SS_ONE_EVENT_CMD 0x60
|
#define SS_ONE_EVENT_CMD 0x60
|
||||||
#define SS_ONE_EVENT_SIZE 15
|
#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 */
|
/* Touchscreen events */
|
||||||
#define SS_EVENT_COORDINATE 0
|
#define SS_EVENT_COORDINATE 0
|
||||||
@@ -53,20 +63,60 @@
|
|||||||
#define SS_EVENT_COORDINATE_ACTION_MOVE 2
|
#define SS_EVENT_COORDINATE_ACTION_MOVE 2
|
||||||
#define SS_EVENT_COORDINATE_ACTION_UP 3
|
#define SS_EVENT_COORDINATE_ACTION_UP 3
|
||||||
|
|
||||||
|
/* Touchscreen ACKs */
|
||||||
|
#define SS_ACK_BOOT_COMPLETE 0x00
|
||||||
|
|
||||||
/* Constants */
|
/* Constants */
|
||||||
#define SS_STOP 0
|
#define SS_STOP 0
|
||||||
#define SS_CONTINUE 1
|
#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 ss_ts_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
struct regulator *vdd;
|
||||||
|
|
||||||
struct gpio_desc *interrupt_gpio;
|
struct gpio_desc *interrupt_gpio;
|
||||||
struct gpio_desc *reset_gpio;
|
struct gpio_desc *reset_gpio;
|
||||||
struct gpio_desc *ta_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 */
|
/* Event structures */
|
||||||
struct sec_ts_event_coordinate {
|
struct sec_ts_event_coordinate {
|
||||||
uint8_t eid:2;
|
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;
|
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)
|
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);
|
input_mt_slot(ts->input, touch_id);
|
||||||
@@ -133,7 +210,7 @@ static int ss_handle_coordinate_event(struct ss_ts_data *ts, uint8_t *event)
|
|||||||
touch_id = (event_coord->tid - 1);
|
touch_id = (event_coord->tid - 1);
|
||||||
|
|
||||||
if(touch_id < SS_MAX_FINGERS + SS_MAX_HOVER && touch_id >= 0){
|
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__,
|
__func__,
|
||||||
touch_id,
|
touch_id,
|
||||||
event_coord->tchsta,
|
event_coord->tchsta,
|
||||||
@@ -161,7 +238,7 @@ static int ss_handle_coordinate_event(struct ss_ts_data *ts, uint8_t *event)
|
|||||||
event_coord->ewy
|
event_coord->ewy
|
||||||
);
|
);
|
||||||
if(ret < 0){
|
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???
|
// 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; i<len; i++){
|
||||||
|
result += data[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ss_flash_memory(struct ss_ts_data *ts, uint32_t addr, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
uint32_t page_index, data_len, i;
|
||||||
|
uint8_t buf[2 + SS_PAGE_SIZE + 1];
|
||||||
|
|
||||||
|
uint32_t num_pages = (len / SS_PAGE_SIZE) + ((len % SS_PAGE_SIZE) != 0);
|
||||||
|
uint32_t start_page = (addr / SS_PAGE_SIZE);
|
||||||
|
if ((start_page * SS_PAGE_SIZE) != addr) {
|
||||||
|
dev_err(&ts->client->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<num_pages; i++){
|
||||||
|
page_index = start_page + i;
|
||||||
|
|
||||||
|
// Set page index
|
||||||
|
buf[0] = (uint8_t)((page_index >> 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; i<header->num_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)
|
static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct ss_ts_data *ts;
|
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);
|
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
|
||||||
if (!ts)
|
if (!ts)
|
||||||
@@ -253,6 +612,12 @@ static int ss_ts_probe(struct i2c_client *client, const struct i2c_device_id *id
|
|||||||
ts->client = client;
|
ts->client = client;
|
||||||
i2c_set_clientdata(client, ts);
|
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);
|
ts->interrupt_gpio = devm_gpiod_get(&client->dev, "interrupt", GPIOD_IN);
|
||||||
if (IS_ERR(ts->interrupt_gpio)) {
|
if (IS_ERR(ts->interrupt_gpio)) {
|
||||||
error = PTR_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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset panel
|
||||||
ss_ts_reset(ts);
|
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);
|
ts->input = devm_input_allocate_device(&client->dev);
|
||||||
if (!ts->input) {
|
if (!ts->input) {
|
||||||
dev_err(&client->dev, "failed to allocate input device\n");
|
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle all events currently stored
|
||||||
|
ss_ts_irq_handler(client->irq, ts);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ss_ts_remove(struct i2c_client *client)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
firmware/samsung_touch.img
Normal file
BIN
firmware/samsung_touch.img
Normal file
Binary file not shown.
Reference in New Issue
Block a user