xref: /linux/drivers/gpu/drm/bridge/lontium-lt8713sx.c (revision 4a57e0913e8c7fff407e97909f4ae48caa84d612)
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 = &lt8713sx_table,
844037c6adSVishnu Saini 	.ranges = &lt8713sx_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(&lt8713sx->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)(&lt8713sx->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 	&lt8713sx_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, &lt8713sx_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, &lt8713sx->ocm_lock);
5334037c6adSVishnu Saini 	if (ret)
5344037c6adSVishnu Saini 		return ret;
5354037c6adSVishnu Saini 
5364037c6adSVishnu Saini 	lt8713sx->regmap = devm_regmap_init_i2c(client, &lt8713sx_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 					  &lt8713sx->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 = &lt8713sx_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(&lt8713sx->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(&lt8713sx->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