Path: blob/master/drivers/gpu/drm/bridge/lontium-lt8713sx.c
170953 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.3*/45#include <linux/crc8.h>6#include <linux/firmware.h>7#include <linux/gpio/consumer.h>8#include <linux/i2c.h>9#include <linux/interrupt.h>10#include <linux/module.h>11#include <linux/mutex.h>12#include <linux/of_graph.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15#include <linux/regulator/consumer.h>16#include <linux/sizes.h>17#include <linux/wait.h>18#include <linux/workqueue.h>1920#include <drm/drm_bridge.h>21#include <drm/drm_of.h>2223#define FW_FILE "lt8713sx_fw.bin"2425#define REG_PAGE_CONTROL 0xff2627#define LT8713SX_PAGE_SIZE 2562829DECLARE_CRC8_TABLE(lt8713sx_crc_table);3031struct lt8713sx {32struct device *dev;33struct drm_bridge bridge;34struct drm_bridge *next_bridge;3536struct regmap *regmap;37/* Protects all accesses to registers by stopping the on-chip MCU */38struct mutex ocm_lock;3940struct gpio_desc *reset_gpio;41struct gpio_desc *enable_gpio;4243struct i2c_client *client;44const struct firmware *fw;4546u8 *fw_buffer;4748u32 main_crc_value;49u32 bank_crc_value[17];5051int bank_num;52};5354static void lt8713sx_reset(struct lt8713sx *lt8713sx);5556static const struct regmap_range lt8713sx_ranges[] = {57{58.range_min = 0x0000,59.range_max = 0xffff60},61};6263static const struct regmap_access_table lt8713sx_table = {64.yes_ranges = lt8713sx_ranges,65.n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges),66};6768static const struct regmap_range_cfg lt8713sx_range_cfg = {69.name = "lt8713sx",70.range_min = 0x0000,71.range_max = 0xffff,72.selector_reg = REG_PAGE_CONTROL,73.selector_mask = 0xff,74.selector_shift = 0,75.window_start = 0,76.window_len = 0x100,77};7879static const struct regmap_config lt8713sx_regmap_config = {80.reg_bits = 8,81.val_bits = 8,82.volatile_table = <8713sx_table,83.ranges = <8713sx_range_cfg,84.num_ranges = 1,85.cache_type = REGCACHE_NONE,86.max_register = 0xffff,87};8889static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx)90{91regmap_write(lt8713sx->regmap, 0xe0ee, 0x01);92}9394static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx)95{96regmap_write(lt8713sx->regmap, 0xe0ee, 0x00);97}9899static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx)100{101int ret = 0;102size_t sz_12k = 12 * SZ_1K;103104ret = request_firmware(<8713sx->fw, FW_FILE, lt8713sx->dev);105if (ret < 0) {106dev_err(lt8713sx->dev, "request firmware failed\n");107return ret;108}109110dev_dbg(lt8713sx->dev, "Firmware size: %zu bytes\n", lt8713sx->fw->size);111112if (lt8713sx->fw->size > SZ_256K - 1) {113dev_err(lt8713sx->dev, "Firmware size exceeds 256KB limit\n");114release_firmware(lt8713sx->fw);115return -EINVAL;116}117118lt8713sx->fw_buffer = kvmalloc(SZ_256K, GFP_KERNEL);119if (!lt8713sx->fw_buffer) {120release_firmware(lt8713sx->fw);121return -ENOMEM;122}123124memset(lt8713sx->fw_buffer, 0xff, SZ_256K);125126/* main firmware */127memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, SZ_64K - 1);128129lt8713sx->fw_buffer[SZ_64K - 1] =130crc8(lt8713sx_crc_table, lt8713sx->fw_buffer, SZ_64K - 1, 0);131lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1];132dev_dbg(lt8713sx->dev,133"Main Firmware Data Crc = 0x%02X\n", lt8713sx->main_crc_value);134135/* bank firmware */136memcpy(lt8713sx->fw_buffer + SZ_64K,137lt8713sx->fw->data + SZ_64K,138lt8713sx->fw->size - SZ_64K);139140lt8713sx->bank_num = (lt8713sx->fw->size - SZ_64K + sz_12k - 1) / sz_12k;141dev_dbg(lt8713sx->dev, "Bank Number Total is %d.\n", lt8713sx->bank_num);142143for (int i = 0; i < lt8713sx->bank_num; i++) {144lt8713sx->bank_crc_value[i] =145crc8(lt8713sx_crc_table, lt8713sx->fw_buffer + SZ_64K + i * sz_12k,146sz_12k, 0);147dev_dbg(lt8713sx->dev, "Bank number:%d; Firmware Data Crc:0x%02X\n",148i, lt8713sx->bank_crc_value[i]);149}150return 0;151}152153static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx)154{155regmap_write(lt8713sx->regmap, 0xe05e, 0xc1);156regmap_write(lt8713sx->regmap, 0xe058, 0x00);157regmap_write(lt8713sx->regmap, 0xe059, 0x50);158regmap_write(lt8713sx->regmap, 0xe05a, 0x10);159regmap_write(lt8713sx->regmap, 0xe05a, 0x00);160regmap_write(lt8713sx->regmap, 0xe058, 0x21);161}162163static void lt8713sx_wren(struct lt8713sx *lt8713sx)164{165regmap_write(lt8713sx->regmap, 0xe103, 0xbf);166regmap_write(lt8713sx->regmap, 0xe103, 0xff);167regmap_write(lt8713sx->regmap, 0xe05a, 0x04);168regmap_write(lt8713sx->regmap, 0xe05a, 0x00);169}170171static void lt8713sx_wrdi(struct lt8713sx *lt8713sx)172{173regmap_write(lt8713sx->regmap, 0xe05a, 0x08);174regmap_write(lt8713sx->regmap, 0xe05a, 0x00);175}176177static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx)178{179regmap_write(lt8713sx->regmap, 0xe103, 0xbf);180regmap_write(lt8713sx->regmap, 0xe103, 0xff);181}182183static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx)184{185regmap_write(lt8713sx->regmap, 0xe055, 0x00);186}187188static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx)189{190regmap_write(lt8713sx->regmap, 0xe05a, 0x30);191regmap_write(lt8713sx->regmap, 0xe05a, 0x00);192}193194static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx)195{196regmap_write(lt8713sx->regmap, 0xe055, 0x80);197regmap_write(lt8713sx->regmap, 0xe05e, 0xc0);198regmap_write(lt8713sx->regmap, 0xe058, 0x21);199}200201static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx)202{203u32 flash_status = 0;204205regmap_write(lt8713sx->regmap, 0xe103, 0x3f);206regmap_write(lt8713sx->regmap, 0xe103, 0xff);207208regmap_write(lt8713sx->regmap, 0xe05e, 0x40);209regmap_write(lt8713sx->regmap, 0xe056, 0x05); /* opcode=read status register */210regmap_write(lt8713sx->regmap, 0xe055, 0x25);211regmap_write(lt8713sx->regmap, 0xe055, 0x01);212regmap_write(lt8713sx->regmap, 0xe058, 0x21);213214regmap_read(lt8713sx->regmap, 0xe05f, &flash_status);215dev_dbg(lt8713sx->dev, "flash_status:%x\n", flash_status);216217return flash_status;218}219220static void lt8713sx_block_erase(struct lt8713sx *lt8713sx)221{222u32 i = 0;223u8 flash_status = 0;224u8 blocknum = 0x00;225u32 flashaddr = 0x00;226227for (blocknum = 0; blocknum < 8; blocknum++) {228flashaddr = blocknum * SZ_32K;229regmap_write(lt8713sx->regmap, 0xe05a, 0x04);230regmap_write(lt8713sx->regmap, 0xe05a, 0x00);231regmap_write(lt8713sx->regmap, 0xe05b, flashaddr >> 16);232regmap_write(lt8713sx->regmap, 0xe05c, flashaddr >> 8);233regmap_write(lt8713sx->regmap, 0xe05d, flashaddr);234regmap_write(lt8713sx->regmap, 0xe05a, 0x01);235regmap_write(lt8713sx->regmap, 0xe05a, 0x00);236msleep(100);237i = 0;238while (1) {239flash_status = lt8713sx_read_flash_status(lt8713sx);240if ((flash_status & 0x01) == 0)241break;242243if (i > 50)244break;245246i++;247msleep(50);248}249}250dev_dbg(lt8713sx->dev, "erase flash done.\n");251}252253static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx)254{255regmap_write(lt8713sx->regmap, 0xe068, 0x00);256regmap_write(lt8713sx->regmap, 0xe069, 0x00);257regmap_write(lt8713sx->regmap, 0xe06a, 0x00);258regmap_write(lt8713sx->regmap, 0xe065, 0x00);259regmap_write(lt8713sx->regmap, 0xe066, 0xff);260regmap_write(lt8713sx->regmap, 0xe067, 0xff);261regmap_write(lt8713sx->regmap, 0xe06b, 0x00);262regmap_write(lt8713sx->regmap, 0xe06c, 0x00);263regmap_write(lt8713sx->regmap, 0xe060, 0x01);264msleep(200);265regmap_write(lt8713sx->regmap, 0xe060, 0x00);266}267268static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr)269{270regmap_write(lt8713sx->regmap, 0xe068, ((addr & 0xff0000) >> 16));271regmap_write(lt8713sx->regmap, 0xe069, ((addr & 0x00ff00) >> 8));272regmap_write(lt8713sx->regmap, 0xe06a, (addr & 0x0000ff));273regmap_write(lt8713sx->regmap, 0xe065, 0x00);274regmap_write(lt8713sx->regmap, 0xe066, 0x30);275regmap_write(lt8713sx->regmap, 0xe067, 0x00);276regmap_write(lt8713sx->regmap, 0xe06b, 0x00);277regmap_write(lt8713sx->regmap, 0xe06c, 0x00);278regmap_write(lt8713sx->regmap, 0xe060, 0x01);279msleep(50);280regmap_write(lt8713sx->regmap, 0xe060, 0x00);281}282283static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize)284{285int page = 0, num = 0, i = 0, val;286287page = (filesize % LT8713SX_PAGE_SIZE) ?288((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE);289290dev_dbg(lt8713sx->dev,291"Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize);292293for (num = 0; num < page; num++) {294dev_dbg(lt8713sx->dev, "page[%d]\n", num);295lt8713sx_i2c_to_sram(lt8713sx);296297for (i = 0; i < LT8713SX_PAGE_SIZE; i++) {298if ((num * LT8713SX_PAGE_SIZE + i) < filesize)299val = *(data + (num * LT8713SX_PAGE_SIZE + i));300else301val = 0xff;302regmap_write(lt8713sx->regmap, 0xe059, val);303}304305lt8713sx_wren(lt8713sx);306lt8713sx_sram_to_flash(lt8713sx);307}308309lt8713sx_wrdi(lt8713sx);310lt8713sx_disable_sram_write(lt8713sx);311312return 0;313}314315static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx)316{317u32 main_crc_result;318319regmap_read(lt8713sx->regmap, 0xe023, &main_crc_result);320321dev_dbg(lt8713sx->dev, "Main CRC HW: 0x%02X\n", main_crc_result);322dev_dbg(lt8713sx->dev, "Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value);323324if (main_crc_result == lt8713sx->main_crc_value)325dev_info(lt8713sx->dev, "Main Firmware Upgrade Success.\n");326else327dev_err(lt8713sx->dev, "Main Firmware Upgrade Failed.\n");328}329330static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum)331{332u32 bank_crc_result;333334regmap_read(lt8713sx->regmap, 0xe023, &bank_crc_result);335336dev_dbg(lt8713sx->dev, "Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result);337338if (bank_crc_result == lt8713sx->bank_crc_value[banknum])339dev_info(lt8713sx->dev, "Bank %d Firmware Upgrade Success.\n", banknum);340else341dev_err(lt8713sx->dev, "Bank %d Firmware Upgrade Failed.\n", banknum);342}343344static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx)345{346int i;347u64 addr = 0x010000;348349for (i = 0; i < lt8713sx->bank_num; i++) {350lt8713sx_load_bank_fw_to_sram(lt8713sx, addr);351lt8713sx_bank_upgrade_result(lt8713sx, i);352addr += 0x3000;353}354}355356static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx)357{358int ret;359360lt8713sx_config_parameters(lt8713sx);361362lt8713sx_block_erase(lt8713sx);363364if (lt8713sx->fw->size < SZ_64K) {365ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, SZ_64K);366if (ret < 0) {367dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret);368return ret;369}370} else {371ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size);372if (ret < 0) {373dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret);374return ret;375}376}377dev_dbg(lt8713sx->dev, "Write Data done.\n");378379return 0;380}381382static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx)383{384int ret = 0;385386guard(mutex)(<8713sx->ocm_lock);387lt8713sx_i2c_enable(lt8713sx);388389ret = lt8713sx_prepare_firmware_data(lt8713sx);390if (ret < 0) {391dev_err(lt8713sx->dev, "Failed to prepare firmware data: %d\n", ret);392goto error;393}394395ret = lt8713sx_firmware_upgrade(lt8713sx);396if (ret < 0) {397dev_err(lt8713sx->dev, "Upgrade failure.\n");398goto error;399}400401/* Validate CRC */402lt8713sx_load_main_fw_to_sram(lt8713sx);403lt8713sx_main_upgrade_result(lt8713sx);404lt8713sx_wrdi(lt8713sx);405lt8713sx_fifo_reset(lt8713sx);406lt8713sx_bank_result_check(lt8713sx);407lt8713sx_wrdi(lt8713sx);408409error:410lt8713sx_i2c_disable(lt8713sx);411if (!ret)412lt8713sx_reset(lt8713sx);413414kvfree(lt8713sx->fw_buffer);415lt8713sx->fw_buffer = NULL;416417if (lt8713sx->fw) {418release_firmware(lt8713sx->fw);419lt8713sx->fw = NULL;420}421422return ret;423}424425static void lt8713sx_reset(struct lt8713sx *lt8713sx)426{427dev_dbg(lt8713sx->dev, "reset bridge.\n");428gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1);429msleep(20);430431gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0);432msleep(20);433434dev_dbg(lt8713sx->dev, "reset done.\n");435}436437static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx)438{439int ret;440441ret = devm_regulator_get_enable(lt8713sx->dev, "vdd");442if (ret < 0)443return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n");444445usleep_range(1000, 10000);446447ret = devm_regulator_get_enable(lt8713sx->dev, "vcc");448if (ret < 0)449return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n");450return 0;451}452453static int lt8713sx_bridge_attach(struct drm_bridge *bridge,454struct drm_encoder *encoder,455enum drm_bridge_attach_flags flags)456{457struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge);458459return drm_bridge_attach(encoder,460lt8713sx->next_bridge,461bridge, flags);462}463464static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx)465{466struct device *dev = lt8713sx->dev;467468lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);469if (IS_ERR(lt8713sx->reset_gpio))470return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio),471"failed to acquire reset gpio\n");472473/* power enable gpio */474lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);475if (IS_ERR(lt8713sx->enable_gpio))476return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio),477"failed to acquire enable gpio\n");478return 0;479}480481static ssize_t lt8713sx_firmware_store(struct device *dev,482struct device_attribute *attr,483const char *buf, size_t len)484{485struct lt8713sx *lt8713sx = dev_get_drvdata(dev);486int ret;487488ret = lt8713sx_firmware_update(lt8713sx);489if (ret < 0)490return ret;491return len;492}493494static DEVICE_ATTR_WO(lt8713sx_firmware);495496static struct attribute *lt8713sx_attrs[] = {497&dev_attr_lt8713sx_firmware.attr,498NULL,499};500501static const struct attribute_group lt8713sx_attr_group = {502.attrs = lt8713sx_attrs,503};504505static const struct attribute_group *lt8713sx_attr_groups[] = {506<8713sx_attr_group,507NULL,508};509510static const struct drm_bridge_funcs lt8713sx_bridge_funcs = {511.attach = lt8713sx_bridge_attach,512};513514static int lt8713sx_probe(struct i2c_client *client)515{516struct lt8713sx *lt8713sx;517struct device *dev = &client->dev;518int ret;519520if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))521return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n");522523lt8713sx = devm_drm_bridge_alloc(dev, struct lt8713sx, bridge, <8713sx_bridge_funcs);524if (IS_ERR(lt8713sx))525return PTR_ERR(lt8713sx);526527lt8713sx->dev = dev;528lt8713sx->client = client;529i2c_set_clientdata(client, lt8713sx);530531ret = devm_mutex_init(lt8713sx->dev, <8713sx->ocm_lock);532if (ret)533return ret;534535lt8713sx->regmap = devm_regmap_init_i2c(client, <8713sx_regmap_config);536if (IS_ERR(lt8713sx->regmap))537return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n");538539ret = drm_of_find_panel_or_bridge(lt8713sx->dev->of_node, 1, -1, NULL,540<8713sx->next_bridge);541if (ret < 0)542return ret;543544ret = lt8713sx_gpio_init(lt8713sx);545if (ret < 0)546return ret;547548ret = lt8713sx_regulator_enable(lt8713sx);549if (ret)550return ret;551552lt8713sx_reset(lt8713sx);553554lt8713sx->bridge.funcs = <8713sx_bridge_funcs;555lt8713sx->bridge.of_node = dev->of_node;556lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;557drm_bridge_add(<8713sx->bridge);558559crc8_populate_msb(lt8713sx_crc_table, 0x31);560561return 0;562}563564static void lt8713sx_remove(struct i2c_client *client)565{566struct lt8713sx *lt8713sx = i2c_get_clientdata(client);567568drm_bridge_remove(<8713sx->bridge);569}570571static struct i2c_device_id lt8713sx_id[] = {572{ "lontium,lt8713sx", 0 },573{ /* sentinel */ }574};575576static const struct of_device_id lt8713sx_match_table[] = {577{ .compatible = "lontium,lt8713sx" },578{ /* sentinel */ }579};580MODULE_DEVICE_TABLE(of, lt8713sx_match_table);581582static struct i2c_driver lt8713sx_driver = {583.driver = {584.name = "lt8713sx",585.of_match_table = lt8713sx_match_table,586.dev_groups = lt8713sx_attr_groups,587},588.probe = lt8713sx_probe,589.remove = lt8713sx_remove,590.id_table = lt8713sx_id,591};592593module_i2c_driver(lt8713sx_driver);594MODULE_LICENSE("GPL");595MODULE_DESCRIPTION("lt8713sx drm bridge driver");596MODULE_AUTHOR("Vishnu Saini <[email protected]>");597MODULE_FIRMWARE(FW_FILE);598599600