14037c6adSVishnu Saini // SPDX-License-Identifier: GPL-2.0 24037c6adSVishnu Saini /* 34037c6adSVishnu Saini * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 44037c6adSVishnu Saini */ 54037c6adSVishnu Saini 64037c6adSVishnu Saini #include <linux/crc8.h> 74037c6adSVishnu Saini #include <linux/firmware.h> 84037c6adSVishnu Saini #include <linux/gpio/consumer.h> 94037c6adSVishnu Saini #include <linux/i2c.h> 104037c6adSVishnu Saini #include <linux/interrupt.h> 114037c6adSVishnu Saini #include <linux/module.h> 124037c6adSVishnu Saini #include <linux/mutex.h> 134037c6adSVishnu Saini #include <linux/of_graph.h> 144037c6adSVishnu Saini #include <linux/platform_device.h> 154037c6adSVishnu Saini #include <linux/regmap.h> 164037c6adSVishnu Saini #include <linux/regulator/consumer.h> 174037c6adSVishnu Saini #include <linux/sizes.h> 184037c6adSVishnu Saini #include <linux/wait.h> 194037c6adSVishnu Saini #include <linux/workqueue.h> 204037c6adSVishnu Saini 214037c6adSVishnu Saini #include <drm/drm_bridge.h> 224037c6adSVishnu Saini #include <drm/drm_of.h> 234037c6adSVishnu Saini 244037c6adSVishnu Saini #define FW_FILE "lt8713sx_fw.bin" 254037c6adSVishnu Saini 264037c6adSVishnu Saini #define REG_PAGE_CONTROL 0xff 274037c6adSVishnu Saini 284037c6adSVishnu Saini #define LT8713SX_PAGE_SIZE 256 294037c6adSVishnu Saini 304037c6adSVishnu Saini DECLARE_CRC8_TABLE(lt8713sx_crc_table); 314037c6adSVishnu Saini 324037c6adSVishnu Saini struct lt8713sx { 334037c6adSVishnu Saini struct device *dev; 344037c6adSVishnu Saini struct drm_bridge bridge; 354037c6adSVishnu Saini struct drm_bridge *next_bridge; 364037c6adSVishnu Saini 374037c6adSVishnu Saini struct regmap *regmap; 384037c6adSVishnu Saini /* Protects all accesses to registers by stopping the on-chip MCU */ 394037c6adSVishnu Saini struct mutex ocm_lock; 404037c6adSVishnu Saini 414037c6adSVishnu Saini struct gpio_desc *reset_gpio; 424037c6adSVishnu Saini struct gpio_desc *enable_gpio; 434037c6adSVishnu Saini 444037c6adSVishnu Saini struct i2c_client *client; 454037c6adSVishnu Saini const struct firmware *fw; 464037c6adSVishnu Saini 474037c6adSVishnu Saini u8 *fw_buffer; 484037c6adSVishnu Saini 494037c6adSVishnu Saini u32 main_crc_value; 504037c6adSVishnu Saini u32 bank_crc_value[17]; 514037c6adSVishnu Saini 524037c6adSVishnu Saini int bank_num; 534037c6adSVishnu Saini }; 544037c6adSVishnu Saini 554037c6adSVishnu Saini static void lt8713sx_reset(struct lt8713sx *lt8713sx); 564037c6adSVishnu Saini 574037c6adSVishnu Saini static const struct regmap_range lt8713sx_ranges[] = { 584037c6adSVishnu Saini { 594037c6adSVishnu Saini .range_min = 0x0000, 604037c6adSVishnu Saini .range_max = 0xffff 614037c6adSVishnu Saini }, 624037c6adSVishnu Saini }; 634037c6adSVishnu Saini 644037c6adSVishnu Saini static const struct regmap_access_table lt8713sx_table = { 654037c6adSVishnu Saini .yes_ranges = lt8713sx_ranges, 664037c6adSVishnu Saini .n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges), 674037c6adSVishnu Saini }; 684037c6adSVishnu Saini 694037c6adSVishnu Saini static const struct regmap_range_cfg lt8713sx_range_cfg = { 704037c6adSVishnu Saini .name = "lt8713sx", 714037c6adSVishnu Saini .range_min = 0x0000, 724037c6adSVishnu Saini .range_max = 0xffff, 734037c6adSVishnu Saini .selector_reg = REG_PAGE_CONTROL, 744037c6adSVishnu Saini .selector_mask = 0xff, 754037c6adSVishnu Saini .selector_shift = 0, 764037c6adSVishnu Saini .window_start = 0, 774037c6adSVishnu Saini .window_len = 0x100, 784037c6adSVishnu Saini }; 794037c6adSVishnu Saini 804037c6adSVishnu Saini static const struct regmap_config lt8713sx_regmap_config = { 814037c6adSVishnu Saini .reg_bits = 8, 824037c6adSVishnu Saini .val_bits = 8, 834037c6adSVishnu Saini .volatile_table = <8713sx_table, 844037c6adSVishnu Saini .ranges = <8713sx_range_cfg, 854037c6adSVishnu Saini .num_ranges = 1, 864037c6adSVishnu Saini .cache_type = REGCACHE_NONE, 874037c6adSVishnu Saini .max_register = 0xffff, 884037c6adSVishnu Saini }; 894037c6adSVishnu Saini 904037c6adSVishnu Saini static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx) 914037c6adSVishnu Saini { 924037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); 934037c6adSVishnu Saini } 944037c6adSVishnu Saini 954037c6adSVishnu Saini static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx) 964037c6adSVishnu Saini { 974037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe0ee, 0x00); 984037c6adSVishnu Saini } 994037c6adSVishnu Saini 1004037c6adSVishnu Saini static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx) 1014037c6adSVishnu Saini { 1024037c6adSVishnu Saini int ret = 0; 103*c196276fSArnd Bergmann size_t sz_12k = 12 * SZ_1K; 1044037c6adSVishnu Saini 1054037c6adSVishnu Saini ret = request_firmware(<8713sx->fw, FW_FILE, lt8713sx->dev); 1064037c6adSVishnu Saini if (ret < 0) { 1074037c6adSVishnu Saini dev_err(lt8713sx->dev, "request firmware failed\n"); 1084037c6adSVishnu Saini return ret; 1094037c6adSVishnu Saini } 1104037c6adSVishnu Saini 1114037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Firmware size: %zu bytes\n", lt8713sx->fw->size); 1124037c6adSVishnu Saini 1134037c6adSVishnu Saini if (lt8713sx->fw->size > SZ_256K - 1) { 1144037c6adSVishnu Saini dev_err(lt8713sx->dev, "Firmware size exceeds 256KB limit\n"); 1154037c6adSVishnu Saini release_firmware(lt8713sx->fw); 1164037c6adSVishnu Saini return -EINVAL; 1174037c6adSVishnu Saini } 1184037c6adSVishnu Saini 1194037c6adSVishnu Saini lt8713sx->fw_buffer = kvmalloc(SZ_256K, GFP_KERNEL); 1204037c6adSVishnu Saini if (!lt8713sx->fw_buffer) { 1214037c6adSVishnu Saini release_firmware(lt8713sx->fw); 1224037c6adSVishnu Saini return -ENOMEM; 1234037c6adSVishnu Saini } 1244037c6adSVishnu Saini 1254037c6adSVishnu Saini memset(lt8713sx->fw_buffer, 0xff, SZ_256K); 1264037c6adSVishnu Saini 1274037c6adSVishnu Saini /* main firmware */ 1284037c6adSVishnu Saini memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, SZ_64K - 1); 1294037c6adSVishnu Saini 1304037c6adSVishnu Saini lt8713sx->fw_buffer[SZ_64K - 1] = 1314037c6adSVishnu Saini crc8(lt8713sx_crc_table, lt8713sx->fw_buffer, SZ_64K - 1, 0); 1324037c6adSVishnu Saini lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1]; 1334037c6adSVishnu Saini dev_dbg(lt8713sx->dev, 1344037c6adSVishnu Saini "Main Firmware Data Crc = 0x%02X\n", lt8713sx->main_crc_value); 1354037c6adSVishnu Saini 1364037c6adSVishnu Saini /* bank firmware */ 1374037c6adSVishnu Saini memcpy(lt8713sx->fw_buffer + SZ_64K, 1384037c6adSVishnu Saini lt8713sx->fw->data + SZ_64K, 1394037c6adSVishnu Saini lt8713sx->fw->size - SZ_64K); 1404037c6adSVishnu Saini 1414037c6adSVishnu Saini lt8713sx->bank_num = (lt8713sx->fw->size - SZ_64K + sz_12k - 1) / sz_12k; 1424037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Bank Number Total is %d.\n", lt8713sx->bank_num); 1434037c6adSVishnu Saini 1444037c6adSVishnu Saini for (int i = 0; i < lt8713sx->bank_num; i++) { 1454037c6adSVishnu Saini lt8713sx->bank_crc_value[i] = 1464037c6adSVishnu Saini crc8(lt8713sx_crc_table, lt8713sx->fw_buffer + SZ_64K + i * sz_12k, 1474037c6adSVishnu Saini sz_12k, 0); 1484037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Bank number:%d; Firmware Data Crc:0x%02X\n", 1494037c6adSVishnu Saini i, lt8713sx->bank_crc_value[i]); 1504037c6adSVishnu Saini } 1514037c6adSVishnu Saini return 0; 1524037c6adSVishnu Saini } 1534037c6adSVishnu Saini 1544037c6adSVishnu Saini static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx) 1554037c6adSVishnu Saini { 1564037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05e, 0xc1); 1574037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe058, 0x00); 1584037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe059, 0x50); 1594037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x10); 1604037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 1614037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe058, 0x21); 1624037c6adSVishnu Saini } 1634037c6adSVishnu Saini 1644037c6adSVishnu Saini static void lt8713sx_wren(struct lt8713sx *lt8713sx) 1654037c6adSVishnu Saini { 1664037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 1674037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0xff); 1684037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 1694037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 1704037c6adSVishnu Saini } 1714037c6adSVishnu Saini 1724037c6adSVishnu Saini static void lt8713sx_wrdi(struct lt8713sx *lt8713sx) 1734037c6adSVishnu Saini { 1744037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x08); 1754037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 1764037c6adSVishnu Saini } 1774037c6adSVishnu Saini 1784037c6adSVishnu Saini static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx) 1794037c6adSVishnu Saini { 1804037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 1814037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0xff); 1824037c6adSVishnu Saini } 1834037c6adSVishnu Saini 1844037c6adSVishnu Saini static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx) 1854037c6adSVishnu Saini { 1864037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe055, 0x00); 1874037c6adSVishnu Saini } 1884037c6adSVishnu Saini 1894037c6adSVishnu Saini static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx) 1904037c6adSVishnu Saini { 1914037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x30); 1924037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 1934037c6adSVishnu Saini } 1944037c6adSVishnu Saini 1954037c6adSVishnu Saini static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx) 1964037c6adSVishnu Saini { 1974037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe055, 0x80); 1984037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05e, 0xc0); 1994037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe058, 0x21); 2004037c6adSVishnu Saini } 2014037c6adSVishnu Saini 2024037c6adSVishnu Saini static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx) 2034037c6adSVishnu Saini { 2044037c6adSVishnu Saini u32 flash_status = 0; 2054037c6adSVishnu Saini 2064037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0x3f); 2074037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe103, 0xff); 2084037c6adSVishnu Saini 2094037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05e, 0x40); 2104037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe056, 0x05); /* opcode=read status register */ 2114037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe055, 0x25); 2124037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe055, 0x01); 2134037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe058, 0x21); 2144037c6adSVishnu Saini 2154037c6adSVishnu Saini regmap_read(lt8713sx->regmap, 0xe05f, &flash_status); 2164037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "flash_status:%x\n", flash_status); 2174037c6adSVishnu Saini 2184037c6adSVishnu Saini return flash_status; 2194037c6adSVishnu Saini } 2204037c6adSVishnu Saini 2214037c6adSVishnu Saini static void lt8713sx_block_erase(struct lt8713sx *lt8713sx) 2224037c6adSVishnu Saini { 2234037c6adSVishnu Saini u32 i = 0; 2244037c6adSVishnu Saini u8 flash_status = 0; 2254037c6adSVishnu Saini u8 blocknum = 0x00; 2264037c6adSVishnu Saini u32 flashaddr = 0x00; 2274037c6adSVishnu Saini 2284037c6adSVishnu Saini for (blocknum = 0; blocknum < 8; blocknum++) { 2294037c6adSVishnu Saini flashaddr = blocknum * SZ_32K; 2304037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 2314037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 2324037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05b, flashaddr >> 16); 2334037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05c, flashaddr >> 8); 2344037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05d, flashaddr); 2354037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x01); 2364037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 2374037c6adSVishnu Saini msleep(100); 2384037c6adSVishnu Saini i = 0; 2394037c6adSVishnu Saini while (1) { 2404037c6adSVishnu Saini flash_status = lt8713sx_read_flash_status(lt8713sx); 2414037c6adSVishnu Saini if ((flash_status & 0x01) == 0) 2424037c6adSVishnu Saini break; 2434037c6adSVishnu Saini 2444037c6adSVishnu Saini if (i > 50) 2454037c6adSVishnu Saini break; 2464037c6adSVishnu Saini 2474037c6adSVishnu Saini i++; 2484037c6adSVishnu Saini msleep(50); 2494037c6adSVishnu Saini } 2504037c6adSVishnu Saini } 2514037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "erase flash done.\n"); 2524037c6adSVishnu Saini } 2534037c6adSVishnu Saini 2544037c6adSVishnu Saini static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx) 2554037c6adSVishnu Saini { 2564037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe068, 0x00); 2574037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe069, 0x00); 2584037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06a, 0x00); 2594037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe065, 0x00); 2604037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe066, 0xff); 2614037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe067, 0xff); 2624037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 2634037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 2644037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe060, 0x01); 2654037c6adSVishnu Saini msleep(200); 2664037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe060, 0x00); 2674037c6adSVishnu Saini } 2684037c6adSVishnu Saini 2694037c6adSVishnu Saini static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr) 2704037c6adSVishnu Saini { 2714037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe068, ((addr & 0xff0000) >> 16)); 2724037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe069, ((addr & 0x00ff00) >> 8)); 2734037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06a, (addr & 0x0000ff)); 2744037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe065, 0x00); 2754037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe066, 0x30); 2764037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe067, 0x00); 2774037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 2784037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 2794037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe060, 0x01); 2804037c6adSVishnu Saini msleep(50); 2814037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe060, 0x00); 2824037c6adSVishnu Saini } 2834037c6adSVishnu Saini 2844037c6adSVishnu Saini static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize) 2854037c6adSVishnu Saini { 2864037c6adSVishnu Saini int page = 0, num = 0, i = 0, val; 2874037c6adSVishnu Saini 2884037c6adSVishnu Saini page = (filesize % LT8713SX_PAGE_SIZE) ? 2894037c6adSVishnu Saini ((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE); 2904037c6adSVishnu Saini 2914037c6adSVishnu Saini dev_dbg(lt8713sx->dev, 2924037c6adSVishnu Saini "Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize); 2934037c6adSVishnu Saini 2944037c6adSVishnu Saini for (num = 0; num < page; num++) { 2954037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "page[%d]\n", num); 2964037c6adSVishnu Saini lt8713sx_i2c_to_sram(lt8713sx); 2974037c6adSVishnu Saini 2984037c6adSVishnu Saini for (i = 0; i < LT8713SX_PAGE_SIZE; i++) { 2994037c6adSVishnu Saini if ((num * LT8713SX_PAGE_SIZE + i) < filesize) 3004037c6adSVishnu Saini val = *(data + (num * LT8713SX_PAGE_SIZE + i)); 3014037c6adSVishnu Saini else 3024037c6adSVishnu Saini val = 0xff; 3034037c6adSVishnu Saini regmap_write(lt8713sx->regmap, 0xe059, val); 3044037c6adSVishnu Saini } 3054037c6adSVishnu Saini 3064037c6adSVishnu Saini lt8713sx_wren(lt8713sx); 3074037c6adSVishnu Saini lt8713sx_sram_to_flash(lt8713sx); 3084037c6adSVishnu Saini } 3094037c6adSVishnu Saini 3104037c6adSVishnu Saini lt8713sx_wrdi(lt8713sx); 3114037c6adSVishnu Saini lt8713sx_disable_sram_write(lt8713sx); 3124037c6adSVishnu Saini 3134037c6adSVishnu Saini return 0; 3144037c6adSVishnu Saini } 3154037c6adSVishnu Saini 3164037c6adSVishnu Saini static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx) 3174037c6adSVishnu Saini { 3184037c6adSVishnu Saini u32 main_crc_result; 3194037c6adSVishnu Saini 3204037c6adSVishnu Saini regmap_read(lt8713sx->regmap, 0xe023, &main_crc_result); 3214037c6adSVishnu Saini 3224037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Main CRC HW: 0x%02X\n", main_crc_result); 3234037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value); 3244037c6adSVishnu Saini 3254037c6adSVishnu Saini if (main_crc_result == lt8713sx->main_crc_value) 3264037c6adSVishnu Saini dev_info(lt8713sx->dev, "Main Firmware Upgrade Success.\n"); 3274037c6adSVishnu Saini else 3284037c6adSVishnu Saini dev_err(lt8713sx->dev, "Main Firmware Upgrade Failed.\n"); 3294037c6adSVishnu Saini } 3304037c6adSVishnu Saini 3314037c6adSVishnu Saini static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum) 3324037c6adSVishnu Saini { 3334037c6adSVishnu Saini u32 bank_crc_result; 3344037c6adSVishnu Saini 3354037c6adSVishnu Saini regmap_read(lt8713sx->regmap, 0xe023, &bank_crc_result); 3364037c6adSVishnu Saini 3374037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result); 3384037c6adSVishnu Saini 3394037c6adSVishnu Saini if (bank_crc_result == lt8713sx->bank_crc_value[banknum]) 3404037c6adSVishnu Saini dev_info(lt8713sx->dev, "Bank %d Firmware Upgrade Success.\n", banknum); 3414037c6adSVishnu Saini else 3424037c6adSVishnu Saini dev_err(lt8713sx->dev, "Bank %d Firmware Upgrade Failed.\n", banknum); 3434037c6adSVishnu Saini } 3444037c6adSVishnu Saini 3454037c6adSVishnu Saini static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx) 3464037c6adSVishnu Saini { 3474037c6adSVishnu Saini int i; 3484037c6adSVishnu Saini u64 addr = 0x010000; 3494037c6adSVishnu Saini 3504037c6adSVishnu Saini for (i = 0; i < lt8713sx->bank_num; i++) { 3514037c6adSVishnu Saini lt8713sx_load_bank_fw_to_sram(lt8713sx, addr); 3524037c6adSVishnu Saini lt8713sx_bank_upgrade_result(lt8713sx, i); 3534037c6adSVishnu Saini addr += 0x3000; 3544037c6adSVishnu Saini } 3554037c6adSVishnu Saini } 3564037c6adSVishnu Saini 3574037c6adSVishnu Saini static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx) 3584037c6adSVishnu Saini { 3594037c6adSVishnu Saini int ret; 3604037c6adSVishnu Saini 3614037c6adSVishnu Saini lt8713sx_config_parameters(lt8713sx); 3624037c6adSVishnu Saini 3634037c6adSVishnu Saini lt8713sx_block_erase(lt8713sx); 3644037c6adSVishnu Saini 3654037c6adSVishnu Saini if (lt8713sx->fw->size < SZ_64K) { 3664037c6adSVishnu Saini ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, SZ_64K); 3674037c6adSVishnu Saini if (ret < 0) { 3684037c6adSVishnu Saini dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 3694037c6adSVishnu Saini return ret; 3704037c6adSVishnu Saini } 3714037c6adSVishnu Saini } else { 3724037c6adSVishnu Saini ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size); 3734037c6adSVishnu Saini if (ret < 0) { 3744037c6adSVishnu Saini dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 3754037c6adSVishnu Saini return ret; 3764037c6adSVishnu Saini } 3774037c6adSVishnu Saini } 3784037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "Write Data done.\n"); 3794037c6adSVishnu Saini 3804037c6adSVishnu Saini return 0; 3814037c6adSVishnu Saini } 3824037c6adSVishnu Saini 3834037c6adSVishnu Saini static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx) 3844037c6adSVishnu Saini { 3854037c6adSVishnu Saini int ret = 0; 3864037c6adSVishnu Saini 3874037c6adSVishnu Saini guard(mutex)(<8713sx->ocm_lock); 3884037c6adSVishnu Saini lt8713sx_i2c_enable(lt8713sx); 3894037c6adSVishnu Saini 3904037c6adSVishnu Saini ret = lt8713sx_prepare_firmware_data(lt8713sx); 3914037c6adSVishnu Saini if (ret < 0) { 3924037c6adSVishnu Saini dev_err(lt8713sx->dev, "Failed to prepare firmware data: %d\n", ret); 3934037c6adSVishnu Saini goto error; 3944037c6adSVishnu Saini } 3954037c6adSVishnu Saini 3964037c6adSVishnu Saini ret = lt8713sx_firmware_upgrade(lt8713sx); 3974037c6adSVishnu Saini if (ret < 0) { 3984037c6adSVishnu Saini dev_err(lt8713sx->dev, "Upgrade failure.\n"); 3994037c6adSVishnu Saini goto error; 4004037c6adSVishnu Saini } 4014037c6adSVishnu Saini 4024037c6adSVishnu Saini /* Validate CRC */ 4034037c6adSVishnu Saini lt8713sx_load_main_fw_to_sram(lt8713sx); 4044037c6adSVishnu Saini lt8713sx_main_upgrade_result(lt8713sx); 4054037c6adSVishnu Saini lt8713sx_wrdi(lt8713sx); 4064037c6adSVishnu Saini lt8713sx_fifo_reset(lt8713sx); 4074037c6adSVishnu Saini lt8713sx_bank_result_check(lt8713sx); 4084037c6adSVishnu Saini lt8713sx_wrdi(lt8713sx); 4094037c6adSVishnu Saini 4104037c6adSVishnu Saini error: 4114037c6adSVishnu Saini lt8713sx_i2c_disable(lt8713sx); 4124037c6adSVishnu Saini if (!ret) 4134037c6adSVishnu Saini lt8713sx_reset(lt8713sx); 4144037c6adSVishnu Saini 4154037c6adSVishnu Saini kvfree(lt8713sx->fw_buffer); 4164037c6adSVishnu Saini lt8713sx->fw_buffer = NULL; 4174037c6adSVishnu Saini 4184037c6adSVishnu Saini if (lt8713sx->fw) { 4194037c6adSVishnu Saini release_firmware(lt8713sx->fw); 4204037c6adSVishnu Saini lt8713sx->fw = NULL; 4214037c6adSVishnu Saini } 4224037c6adSVishnu Saini 4234037c6adSVishnu Saini return ret; 4244037c6adSVishnu Saini } 4254037c6adSVishnu Saini 4264037c6adSVishnu Saini static void lt8713sx_reset(struct lt8713sx *lt8713sx) 4274037c6adSVishnu Saini { 4284037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "reset bridge.\n"); 4294037c6adSVishnu Saini gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); 4304037c6adSVishnu Saini msleep(20); 4314037c6adSVishnu Saini 4324037c6adSVishnu Saini gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0); 4334037c6adSVishnu Saini msleep(20); 4344037c6adSVishnu Saini 4354037c6adSVishnu Saini dev_dbg(lt8713sx->dev, "reset done.\n"); 4364037c6adSVishnu Saini } 4374037c6adSVishnu Saini 4384037c6adSVishnu Saini static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx) 4394037c6adSVishnu Saini { 4404037c6adSVishnu Saini int ret; 4414037c6adSVishnu Saini 4424037c6adSVishnu Saini ret = devm_regulator_get_enable(lt8713sx->dev, "vdd"); 4434037c6adSVishnu Saini if (ret < 0) 4444037c6adSVishnu Saini return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n"); 4454037c6adSVishnu Saini 4464037c6adSVishnu Saini usleep_range(1000, 10000); 4474037c6adSVishnu Saini 4484037c6adSVishnu Saini ret = devm_regulator_get_enable(lt8713sx->dev, "vcc"); 4494037c6adSVishnu Saini if (ret < 0) 4504037c6adSVishnu Saini return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n"); 4514037c6adSVishnu Saini return 0; 4524037c6adSVishnu Saini } 4534037c6adSVishnu Saini 4544037c6adSVishnu Saini static int lt8713sx_bridge_attach(struct drm_bridge *bridge, 4554037c6adSVishnu Saini struct drm_encoder *encoder, 4564037c6adSVishnu Saini enum drm_bridge_attach_flags flags) 4574037c6adSVishnu Saini { 4584037c6adSVishnu Saini struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge); 4594037c6adSVishnu Saini 4604037c6adSVishnu Saini return drm_bridge_attach(encoder, 4614037c6adSVishnu Saini lt8713sx->next_bridge, 4624037c6adSVishnu Saini bridge, flags); 4634037c6adSVishnu Saini } 4644037c6adSVishnu Saini 4654037c6adSVishnu Saini static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx) 4664037c6adSVishnu Saini { 4674037c6adSVishnu Saini struct device *dev = lt8713sx->dev; 4684037c6adSVishnu Saini 4694037c6adSVishnu Saini lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 4704037c6adSVishnu Saini if (IS_ERR(lt8713sx->reset_gpio)) 4714037c6adSVishnu Saini return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio), 4724037c6adSVishnu Saini "failed to acquire reset gpio\n"); 4734037c6adSVishnu Saini 4744037c6adSVishnu Saini /* power enable gpio */ 4754037c6adSVishnu Saini lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); 4764037c6adSVishnu Saini if (IS_ERR(lt8713sx->enable_gpio)) 4774037c6adSVishnu Saini return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio), 4784037c6adSVishnu Saini "failed to acquire enable gpio\n"); 4794037c6adSVishnu Saini return 0; 4804037c6adSVishnu Saini } 4814037c6adSVishnu Saini 4824037c6adSVishnu Saini static ssize_t lt8713sx_firmware_store(struct device *dev, 4834037c6adSVishnu Saini struct device_attribute *attr, 4844037c6adSVishnu Saini const char *buf, size_t len) 4854037c6adSVishnu Saini { 4864037c6adSVishnu Saini struct lt8713sx *lt8713sx = dev_get_drvdata(dev); 4874037c6adSVishnu Saini int ret; 4884037c6adSVishnu Saini 4894037c6adSVishnu Saini ret = lt8713sx_firmware_update(lt8713sx); 4904037c6adSVishnu Saini if (ret < 0) 4914037c6adSVishnu Saini return ret; 4924037c6adSVishnu Saini return len; 4934037c6adSVishnu Saini } 4944037c6adSVishnu Saini 4954037c6adSVishnu Saini static DEVICE_ATTR_WO(lt8713sx_firmware); 4964037c6adSVishnu Saini 4974037c6adSVishnu Saini static struct attribute *lt8713sx_attrs[] = { 4984037c6adSVishnu Saini &dev_attr_lt8713sx_firmware.attr, 4994037c6adSVishnu Saini NULL, 5004037c6adSVishnu Saini }; 5014037c6adSVishnu Saini 5024037c6adSVishnu Saini static const struct attribute_group lt8713sx_attr_group = { 5034037c6adSVishnu Saini .attrs = lt8713sx_attrs, 5044037c6adSVishnu Saini }; 5054037c6adSVishnu Saini 5064037c6adSVishnu Saini static const struct attribute_group *lt8713sx_attr_groups[] = { 5074037c6adSVishnu Saini <8713sx_attr_group, 5084037c6adSVishnu Saini NULL, 5094037c6adSVishnu Saini }; 5104037c6adSVishnu Saini 5114037c6adSVishnu Saini static const struct drm_bridge_funcs lt8713sx_bridge_funcs = { 5124037c6adSVishnu Saini .attach = lt8713sx_bridge_attach, 5134037c6adSVishnu Saini }; 5144037c6adSVishnu Saini 5154037c6adSVishnu Saini static int lt8713sx_probe(struct i2c_client *client) 5164037c6adSVishnu Saini { 5174037c6adSVishnu Saini struct lt8713sx *lt8713sx; 5184037c6adSVishnu Saini struct device *dev = &client->dev; 5194037c6adSVishnu Saini int ret; 5204037c6adSVishnu Saini 5214037c6adSVishnu Saini if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 5224037c6adSVishnu Saini return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n"); 5234037c6adSVishnu Saini 5244037c6adSVishnu Saini lt8713sx = devm_drm_bridge_alloc(dev, struct lt8713sx, bridge, <8713sx_bridge_funcs); 5254037c6adSVishnu Saini if (IS_ERR(lt8713sx)) 5264037c6adSVishnu Saini return PTR_ERR(lt8713sx); 5274037c6adSVishnu Saini 5284037c6adSVishnu Saini lt8713sx->dev = dev; 5294037c6adSVishnu Saini lt8713sx->client = client; 5304037c6adSVishnu Saini i2c_set_clientdata(client, lt8713sx); 5314037c6adSVishnu Saini 5324037c6adSVishnu Saini ret = devm_mutex_init(lt8713sx->dev, <8713sx->ocm_lock); 5334037c6adSVishnu Saini if (ret) 5344037c6adSVishnu Saini return ret; 5354037c6adSVishnu Saini 5364037c6adSVishnu Saini lt8713sx->regmap = devm_regmap_init_i2c(client, <8713sx_regmap_config); 5374037c6adSVishnu Saini if (IS_ERR(lt8713sx->regmap)) 5384037c6adSVishnu Saini return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n"); 5394037c6adSVishnu Saini 5404037c6adSVishnu Saini ret = drm_of_find_panel_or_bridge(lt8713sx->dev->of_node, 1, -1, NULL, 5414037c6adSVishnu Saini <8713sx->next_bridge); 5424037c6adSVishnu Saini if (ret < 0) 5434037c6adSVishnu Saini return ret; 5444037c6adSVishnu Saini 5454037c6adSVishnu Saini ret = lt8713sx_gpio_init(lt8713sx); 5464037c6adSVishnu Saini if (ret < 0) 5474037c6adSVishnu Saini return ret; 5484037c6adSVishnu Saini 5494037c6adSVishnu Saini ret = lt8713sx_regulator_enable(lt8713sx); 5504037c6adSVishnu Saini if (ret) 5514037c6adSVishnu Saini return ret; 5524037c6adSVishnu Saini 5534037c6adSVishnu Saini lt8713sx_reset(lt8713sx); 5544037c6adSVishnu Saini 5554037c6adSVishnu Saini lt8713sx->bridge.funcs = <8713sx_bridge_funcs; 5564037c6adSVishnu Saini lt8713sx->bridge.of_node = dev->of_node; 5574037c6adSVishnu Saini lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; 5584037c6adSVishnu Saini drm_bridge_add(<8713sx->bridge); 5594037c6adSVishnu Saini 5604037c6adSVishnu Saini crc8_populate_msb(lt8713sx_crc_table, 0x31); 5614037c6adSVishnu Saini 5624037c6adSVishnu Saini return 0; 5634037c6adSVishnu Saini } 5644037c6adSVishnu Saini 5654037c6adSVishnu Saini static void lt8713sx_remove(struct i2c_client *client) 5664037c6adSVishnu Saini { 5674037c6adSVishnu Saini struct lt8713sx *lt8713sx = i2c_get_clientdata(client); 5684037c6adSVishnu Saini 5694037c6adSVishnu Saini drm_bridge_remove(<8713sx->bridge); 5704037c6adSVishnu Saini } 5714037c6adSVishnu Saini 5724037c6adSVishnu Saini static struct i2c_device_id lt8713sx_id[] = { 5734037c6adSVishnu Saini { "lontium,lt8713sx", 0 }, 5744037c6adSVishnu Saini { /* sentinel */ } 5754037c6adSVishnu Saini }; 5764037c6adSVishnu Saini 5774037c6adSVishnu Saini static const struct of_device_id lt8713sx_match_table[] = { 5784037c6adSVishnu Saini { .compatible = "lontium,lt8713sx" }, 5794037c6adSVishnu Saini { /* sentinel */ } 5804037c6adSVishnu Saini }; 5814037c6adSVishnu Saini MODULE_DEVICE_TABLE(of, lt8713sx_match_table); 5824037c6adSVishnu Saini 5834037c6adSVishnu Saini static struct i2c_driver lt8713sx_driver = { 5844037c6adSVishnu Saini .driver = { 5854037c6adSVishnu Saini .name = "lt8713sx", 5864037c6adSVishnu Saini .of_match_table = lt8713sx_match_table, 5874037c6adSVishnu Saini .dev_groups = lt8713sx_attr_groups, 5884037c6adSVishnu Saini }, 5894037c6adSVishnu Saini .probe = lt8713sx_probe, 5904037c6adSVishnu Saini .remove = lt8713sx_remove, 5914037c6adSVishnu Saini .id_table = lt8713sx_id, 5924037c6adSVishnu Saini }; 5934037c6adSVishnu Saini 5944037c6adSVishnu Saini module_i2c_driver(lt8713sx_driver); 5954037c6adSVishnu Saini MODULE_LICENSE("GPL"); 5964037c6adSVishnu Saini MODULE_DESCRIPTION("lt8713sx drm bridge driver"); 5974037c6adSVishnu Saini MODULE_AUTHOR("Vishnu Saini <vishnu.saini@oss.qualcomm.com>"); 5984037c6adSVishnu Saini MODULE_FIRMWARE(FW_FILE); 599