Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/bridge: add support for lontium lt8713sx bridge driver

The lt8713sx is a Type-C/DP1.4 to DP1.4/HDMI2.0 converter,
with three configurable DP1.4/HDMI2.0/DP++ output interfaces and
audio output interface.

Driver is required for firmware upgrade and enabling the bridge chip.

Co-developed-by: Prahlad Valluru <vvalluru@qti.qualcomm.com>
Signed-off-by: Prahlad Valluru <vvalluru@qti.qualcomm.com>
Signed-off-by: Vishnu Saini <vishnu.saini@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Cc: Simon Zhu <xmzhu@lontium.corp-partner.google.com>
Link: https://patch.msgid.link/20260303-lt8713sx-bridge-driver-v5-2-6cc2a855aafa@oss.qualcomm.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Vishnu Saini and committed by
Dmitry Baryshkov
4037c6ad 130daea1

+609
+10
drivers/gpu/drm/bridge/Kconfig
··· 191 191 HDMI signals 192 192 Please say Y if you have such hardware. 193 193 194 + config DRM_LONTIUM_LT8713SX 195 + tristate "Lontium LT8713SX DP MST bridge" 196 + depends on OF 197 + select REGMAP_I2C 198 + help 199 + Driver for Lontium LT8713SX DP MST bridge 200 + chip firmware upgrade, which converts Type-C/DP1.4 201 + to 3 configurable Type-C/DP1.4/HDMI2.0 outputs 202 + Please say Y if you have such hardware. 203 + 194 204 config DRM_ITE_IT66121 195 205 tristate "ITE IT66121 HDMI bridge" 196 206 depends on OF
+1
drivers/gpu/drm/bridge/Makefile
··· 17 17 obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o 18 18 obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o 19 19 obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o 20 + obj-$(CONFIG_DRM_LONTIUM_LT8713SX) += lontium-lt8713sx.o 20 21 obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o 21 22 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o 22 23 obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o
+598
drivers/gpu/drm/bridge/lontium-lt8713sx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 + */ 5 + 6 + #include <linux/crc8.h> 7 + #include <linux/firmware.h> 8 + #include <linux/gpio/consumer.h> 9 + #include <linux/i2c.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/module.h> 12 + #include <linux/mutex.h> 13 + #include <linux/of_graph.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/regmap.h> 16 + #include <linux/regulator/consumer.h> 17 + #include <linux/sizes.h> 18 + #include <linux/wait.h> 19 + #include <linux/workqueue.h> 20 + 21 + #include <drm/drm_bridge.h> 22 + #include <drm/drm_of.h> 23 + 24 + #define FW_FILE "lt8713sx_fw.bin" 25 + 26 + #define REG_PAGE_CONTROL 0xff 27 + 28 + #define LT8713SX_PAGE_SIZE 256 29 + 30 + DECLARE_CRC8_TABLE(lt8713sx_crc_table); 31 + 32 + struct lt8713sx { 33 + struct device *dev; 34 + struct drm_bridge bridge; 35 + struct drm_bridge *next_bridge; 36 + 37 + struct regmap *regmap; 38 + /* Protects all accesses to registers by stopping the on-chip MCU */ 39 + struct mutex ocm_lock; 40 + 41 + struct gpio_desc *reset_gpio; 42 + struct gpio_desc *enable_gpio; 43 + 44 + struct i2c_client *client; 45 + const struct firmware *fw; 46 + 47 + u8 *fw_buffer; 48 + 49 + u32 main_crc_value; 50 + u32 bank_crc_value[17]; 51 + 52 + int bank_num; 53 + }; 54 + 55 + static void lt8713sx_reset(struct lt8713sx *lt8713sx); 56 + 57 + static const struct regmap_range lt8713sx_ranges[] = { 58 + { 59 + .range_min = 0x0000, 60 + .range_max = 0xffff 61 + }, 62 + }; 63 + 64 + static const struct regmap_access_table lt8713sx_table = { 65 + .yes_ranges = lt8713sx_ranges, 66 + .n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges), 67 + }; 68 + 69 + static const struct regmap_range_cfg lt8713sx_range_cfg = { 70 + .name = "lt8713sx", 71 + .range_min = 0x0000, 72 + .range_max = 0xffff, 73 + .selector_reg = REG_PAGE_CONTROL, 74 + .selector_mask = 0xff, 75 + .selector_shift = 0, 76 + .window_start = 0, 77 + .window_len = 0x100, 78 + }; 79 + 80 + static const struct regmap_config lt8713sx_regmap_config = { 81 + .reg_bits = 8, 82 + .val_bits = 8, 83 + .volatile_table = &lt8713sx_table, 84 + .ranges = &lt8713sx_range_cfg, 85 + .num_ranges = 1, 86 + .cache_type = REGCACHE_NONE, 87 + .max_register = 0xffff, 88 + }; 89 + 90 + static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx) 91 + { 92 + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); 93 + } 94 + 95 + static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx) 96 + { 97 + regmap_write(lt8713sx->regmap, 0xe0ee, 0x00); 98 + } 99 + 100 + static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx) 101 + { 102 + int ret = 0; 103 + u64 sz_12k = 12 * SZ_1K; 104 + 105 + ret = request_firmware(&lt8713sx->fw, FW_FILE, lt8713sx->dev); 106 + if (ret < 0) { 107 + dev_err(lt8713sx->dev, "request firmware failed\n"); 108 + return ret; 109 + } 110 + 111 + dev_dbg(lt8713sx->dev, "Firmware size: %zu bytes\n", lt8713sx->fw->size); 112 + 113 + if (lt8713sx->fw->size > SZ_256K - 1) { 114 + dev_err(lt8713sx->dev, "Firmware size exceeds 256KB limit\n"); 115 + release_firmware(lt8713sx->fw); 116 + return -EINVAL; 117 + } 118 + 119 + lt8713sx->fw_buffer = kvmalloc(SZ_256K, GFP_KERNEL); 120 + if (!lt8713sx->fw_buffer) { 121 + release_firmware(lt8713sx->fw); 122 + return -ENOMEM; 123 + } 124 + 125 + memset(lt8713sx->fw_buffer, 0xff, SZ_256K); 126 + 127 + /* main firmware */ 128 + memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, SZ_64K - 1); 129 + 130 + lt8713sx->fw_buffer[SZ_64K - 1] = 131 + crc8(lt8713sx_crc_table, lt8713sx->fw_buffer, SZ_64K - 1, 0); 132 + lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1]; 133 + dev_dbg(lt8713sx->dev, 134 + "Main Firmware Data Crc = 0x%02X\n", lt8713sx->main_crc_value); 135 + 136 + /* bank firmware */ 137 + memcpy(lt8713sx->fw_buffer + SZ_64K, 138 + lt8713sx->fw->data + SZ_64K, 139 + lt8713sx->fw->size - SZ_64K); 140 + 141 + lt8713sx->bank_num = (lt8713sx->fw->size - SZ_64K + sz_12k - 1) / sz_12k; 142 + dev_dbg(lt8713sx->dev, "Bank Number Total is %d.\n", lt8713sx->bank_num); 143 + 144 + for (int i = 0; i < lt8713sx->bank_num; i++) { 145 + lt8713sx->bank_crc_value[i] = 146 + crc8(lt8713sx_crc_table, lt8713sx->fw_buffer + SZ_64K + i * sz_12k, 147 + sz_12k, 0); 148 + dev_dbg(lt8713sx->dev, "Bank number:%d; Firmware Data Crc:0x%02X\n", 149 + i, lt8713sx->bank_crc_value[i]); 150 + } 151 + return 0; 152 + } 153 + 154 + static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx) 155 + { 156 + regmap_write(lt8713sx->regmap, 0xe05e, 0xc1); 157 + regmap_write(lt8713sx->regmap, 0xe058, 0x00); 158 + regmap_write(lt8713sx->regmap, 0xe059, 0x50); 159 + regmap_write(lt8713sx->regmap, 0xe05a, 0x10); 160 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 161 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 162 + } 163 + 164 + static void lt8713sx_wren(struct lt8713sx *lt8713sx) 165 + { 166 + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 167 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 168 + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 169 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 170 + } 171 + 172 + static void lt8713sx_wrdi(struct lt8713sx *lt8713sx) 173 + { 174 + regmap_write(lt8713sx->regmap, 0xe05a, 0x08); 175 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 176 + } 177 + 178 + static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx) 179 + { 180 + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 181 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 182 + } 183 + 184 + static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx) 185 + { 186 + regmap_write(lt8713sx->regmap, 0xe055, 0x00); 187 + } 188 + 189 + static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx) 190 + { 191 + regmap_write(lt8713sx->regmap, 0xe05a, 0x30); 192 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 193 + } 194 + 195 + static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx) 196 + { 197 + regmap_write(lt8713sx->regmap, 0xe055, 0x80); 198 + regmap_write(lt8713sx->regmap, 0xe05e, 0xc0); 199 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 200 + } 201 + 202 + static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx) 203 + { 204 + u32 flash_status = 0; 205 + 206 + regmap_write(lt8713sx->regmap, 0xe103, 0x3f); 207 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 208 + 209 + regmap_write(lt8713sx->regmap, 0xe05e, 0x40); 210 + regmap_write(lt8713sx->regmap, 0xe056, 0x05); /* opcode=read status register */ 211 + regmap_write(lt8713sx->regmap, 0xe055, 0x25); 212 + regmap_write(lt8713sx->regmap, 0xe055, 0x01); 213 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 214 + 215 + regmap_read(lt8713sx->regmap, 0xe05f, &flash_status); 216 + dev_dbg(lt8713sx->dev, "flash_status:%x\n", flash_status); 217 + 218 + return flash_status; 219 + } 220 + 221 + static void lt8713sx_block_erase(struct lt8713sx *lt8713sx) 222 + { 223 + u32 i = 0; 224 + u8 flash_status = 0; 225 + u8 blocknum = 0x00; 226 + u32 flashaddr = 0x00; 227 + 228 + for (blocknum = 0; blocknum < 8; blocknum++) { 229 + flashaddr = blocknum * SZ_32K; 230 + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 231 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 232 + regmap_write(lt8713sx->regmap, 0xe05b, flashaddr >> 16); 233 + regmap_write(lt8713sx->regmap, 0xe05c, flashaddr >> 8); 234 + regmap_write(lt8713sx->regmap, 0xe05d, flashaddr); 235 + regmap_write(lt8713sx->regmap, 0xe05a, 0x01); 236 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 237 + msleep(100); 238 + i = 0; 239 + while (1) { 240 + flash_status = lt8713sx_read_flash_status(lt8713sx); 241 + if ((flash_status & 0x01) == 0) 242 + break; 243 + 244 + if (i > 50) 245 + break; 246 + 247 + i++; 248 + msleep(50); 249 + } 250 + } 251 + dev_dbg(lt8713sx->dev, "erase flash done.\n"); 252 + } 253 + 254 + static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx) 255 + { 256 + regmap_write(lt8713sx->regmap, 0xe068, 0x00); 257 + regmap_write(lt8713sx->regmap, 0xe069, 0x00); 258 + regmap_write(lt8713sx->regmap, 0xe06a, 0x00); 259 + regmap_write(lt8713sx->regmap, 0xe065, 0x00); 260 + regmap_write(lt8713sx->regmap, 0xe066, 0xff); 261 + regmap_write(lt8713sx->regmap, 0xe067, 0xff); 262 + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 263 + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 264 + regmap_write(lt8713sx->regmap, 0xe060, 0x01); 265 + msleep(200); 266 + regmap_write(lt8713sx->regmap, 0xe060, 0x00); 267 + } 268 + 269 + static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr) 270 + { 271 + regmap_write(lt8713sx->regmap, 0xe068, ((addr & 0xff0000) >> 16)); 272 + regmap_write(lt8713sx->regmap, 0xe069, ((addr & 0x00ff00) >> 8)); 273 + regmap_write(lt8713sx->regmap, 0xe06a, (addr & 0x0000ff)); 274 + regmap_write(lt8713sx->regmap, 0xe065, 0x00); 275 + regmap_write(lt8713sx->regmap, 0xe066, 0x30); 276 + regmap_write(lt8713sx->regmap, 0xe067, 0x00); 277 + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 278 + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 279 + regmap_write(lt8713sx->regmap, 0xe060, 0x01); 280 + msleep(50); 281 + regmap_write(lt8713sx->regmap, 0xe060, 0x00); 282 + } 283 + 284 + static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize) 285 + { 286 + int page = 0, num = 0, i = 0, val; 287 + 288 + page = (filesize % LT8713SX_PAGE_SIZE) ? 289 + ((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE); 290 + 291 + dev_dbg(lt8713sx->dev, 292 + "Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize); 293 + 294 + for (num = 0; num < page; num++) { 295 + dev_dbg(lt8713sx->dev, "page[%d]\n", num); 296 + lt8713sx_i2c_to_sram(lt8713sx); 297 + 298 + for (i = 0; i < LT8713SX_PAGE_SIZE; i++) { 299 + if ((num * LT8713SX_PAGE_SIZE + i) < filesize) 300 + val = *(data + (num * LT8713SX_PAGE_SIZE + i)); 301 + else 302 + val = 0xff; 303 + regmap_write(lt8713sx->regmap, 0xe059, val); 304 + } 305 + 306 + lt8713sx_wren(lt8713sx); 307 + lt8713sx_sram_to_flash(lt8713sx); 308 + } 309 + 310 + lt8713sx_wrdi(lt8713sx); 311 + lt8713sx_disable_sram_write(lt8713sx); 312 + 313 + return 0; 314 + } 315 + 316 + static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx) 317 + { 318 + u32 main_crc_result; 319 + 320 + regmap_read(lt8713sx->regmap, 0xe023, &main_crc_result); 321 + 322 + dev_dbg(lt8713sx->dev, "Main CRC HW: 0x%02X\n", main_crc_result); 323 + dev_dbg(lt8713sx->dev, "Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value); 324 + 325 + if (main_crc_result == lt8713sx->main_crc_value) 326 + dev_info(lt8713sx->dev, "Main Firmware Upgrade Success.\n"); 327 + else 328 + dev_err(lt8713sx->dev, "Main Firmware Upgrade Failed.\n"); 329 + } 330 + 331 + static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum) 332 + { 333 + u32 bank_crc_result; 334 + 335 + regmap_read(lt8713sx->regmap, 0xe023, &bank_crc_result); 336 + 337 + dev_dbg(lt8713sx->dev, "Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result); 338 + 339 + if (bank_crc_result == lt8713sx->bank_crc_value[banknum]) 340 + dev_info(lt8713sx->dev, "Bank %d Firmware Upgrade Success.\n", banknum); 341 + else 342 + dev_err(lt8713sx->dev, "Bank %d Firmware Upgrade Failed.\n", banknum); 343 + } 344 + 345 + static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx) 346 + { 347 + int i; 348 + u64 addr = 0x010000; 349 + 350 + for (i = 0; i < lt8713sx->bank_num; i++) { 351 + lt8713sx_load_bank_fw_to_sram(lt8713sx, addr); 352 + lt8713sx_bank_upgrade_result(lt8713sx, i); 353 + addr += 0x3000; 354 + } 355 + } 356 + 357 + static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx) 358 + { 359 + int ret; 360 + 361 + lt8713sx_config_parameters(lt8713sx); 362 + 363 + lt8713sx_block_erase(lt8713sx); 364 + 365 + if (lt8713sx->fw->size < SZ_64K) { 366 + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, SZ_64K); 367 + if (ret < 0) { 368 + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 369 + return ret; 370 + } 371 + } else { 372 + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size); 373 + if (ret < 0) { 374 + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 375 + return ret; 376 + } 377 + } 378 + dev_dbg(lt8713sx->dev, "Write Data done.\n"); 379 + 380 + return 0; 381 + } 382 + 383 + static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx) 384 + { 385 + int ret = 0; 386 + 387 + guard(mutex)(&lt8713sx->ocm_lock); 388 + lt8713sx_i2c_enable(lt8713sx); 389 + 390 + ret = lt8713sx_prepare_firmware_data(lt8713sx); 391 + if (ret < 0) { 392 + dev_err(lt8713sx->dev, "Failed to prepare firmware data: %d\n", ret); 393 + goto error; 394 + } 395 + 396 + ret = lt8713sx_firmware_upgrade(lt8713sx); 397 + if (ret < 0) { 398 + dev_err(lt8713sx->dev, "Upgrade failure.\n"); 399 + goto error; 400 + } 401 + 402 + /* Validate CRC */ 403 + lt8713sx_load_main_fw_to_sram(lt8713sx); 404 + lt8713sx_main_upgrade_result(lt8713sx); 405 + lt8713sx_wrdi(lt8713sx); 406 + lt8713sx_fifo_reset(lt8713sx); 407 + lt8713sx_bank_result_check(lt8713sx); 408 + lt8713sx_wrdi(lt8713sx); 409 + 410 + error: 411 + lt8713sx_i2c_disable(lt8713sx); 412 + if (!ret) 413 + lt8713sx_reset(lt8713sx); 414 + 415 + kvfree(lt8713sx->fw_buffer); 416 + lt8713sx->fw_buffer = NULL; 417 + 418 + if (lt8713sx->fw) { 419 + release_firmware(lt8713sx->fw); 420 + lt8713sx->fw = NULL; 421 + } 422 + 423 + return ret; 424 + } 425 + 426 + static void lt8713sx_reset(struct lt8713sx *lt8713sx) 427 + { 428 + dev_dbg(lt8713sx->dev, "reset bridge.\n"); 429 + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); 430 + msleep(20); 431 + 432 + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0); 433 + msleep(20); 434 + 435 + dev_dbg(lt8713sx->dev, "reset done.\n"); 436 + } 437 + 438 + static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx) 439 + { 440 + int ret; 441 + 442 + ret = devm_regulator_get_enable(lt8713sx->dev, "vdd"); 443 + if (ret < 0) 444 + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n"); 445 + 446 + usleep_range(1000, 10000); 447 + 448 + ret = devm_regulator_get_enable(lt8713sx->dev, "vcc"); 449 + if (ret < 0) 450 + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n"); 451 + return 0; 452 + } 453 + 454 + static int lt8713sx_bridge_attach(struct drm_bridge *bridge, 455 + struct drm_encoder *encoder, 456 + enum drm_bridge_attach_flags flags) 457 + { 458 + struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge); 459 + 460 + return drm_bridge_attach(encoder, 461 + lt8713sx->next_bridge, 462 + bridge, flags); 463 + } 464 + 465 + static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx) 466 + { 467 + struct device *dev = lt8713sx->dev; 468 + 469 + lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 470 + if (IS_ERR(lt8713sx->reset_gpio)) 471 + return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio), 472 + "failed to acquire reset gpio\n"); 473 + 474 + /* power enable gpio */ 475 + lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); 476 + if (IS_ERR(lt8713sx->enable_gpio)) 477 + return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio), 478 + "failed to acquire enable gpio\n"); 479 + return 0; 480 + } 481 + 482 + static ssize_t lt8713sx_firmware_store(struct device *dev, 483 + struct device_attribute *attr, 484 + const char *buf, size_t len) 485 + { 486 + struct lt8713sx *lt8713sx = dev_get_drvdata(dev); 487 + int ret; 488 + 489 + ret = lt8713sx_firmware_update(lt8713sx); 490 + if (ret < 0) 491 + return ret; 492 + return len; 493 + } 494 + 495 + static DEVICE_ATTR_WO(lt8713sx_firmware); 496 + 497 + static struct attribute *lt8713sx_attrs[] = { 498 + &dev_attr_lt8713sx_firmware.attr, 499 + NULL, 500 + }; 501 + 502 + static const struct attribute_group lt8713sx_attr_group = { 503 + .attrs = lt8713sx_attrs, 504 + }; 505 + 506 + static const struct attribute_group *lt8713sx_attr_groups[] = { 507 + &lt8713sx_attr_group, 508 + NULL, 509 + }; 510 + 511 + static const struct drm_bridge_funcs lt8713sx_bridge_funcs = { 512 + .attach = lt8713sx_bridge_attach, 513 + }; 514 + 515 + static int lt8713sx_probe(struct i2c_client *client) 516 + { 517 + struct lt8713sx *lt8713sx; 518 + struct device *dev = &client->dev; 519 + int ret; 520 + 521 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 522 + return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n"); 523 + 524 + lt8713sx = devm_drm_bridge_alloc(dev, struct lt8713sx, bridge, &lt8713sx_bridge_funcs); 525 + if (IS_ERR(lt8713sx)) 526 + return PTR_ERR(lt8713sx); 527 + 528 + lt8713sx->dev = dev; 529 + lt8713sx->client = client; 530 + i2c_set_clientdata(client, lt8713sx); 531 + 532 + ret = devm_mutex_init(lt8713sx->dev, &lt8713sx->ocm_lock); 533 + if (ret) 534 + return ret; 535 + 536 + lt8713sx->regmap = devm_regmap_init_i2c(client, &lt8713sx_regmap_config); 537 + if (IS_ERR(lt8713sx->regmap)) 538 + return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n"); 539 + 540 + ret = drm_of_find_panel_or_bridge(lt8713sx->dev->of_node, 1, -1, NULL, 541 + &lt8713sx->next_bridge); 542 + if (ret < 0) 543 + return ret; 544 + 545 + ret = lt8713sx_gpio_init(lt8713sx); 546 + if (ret < 0) 547 + return ret; 548 + 549 + ret = lt8713sx_regulator_enable(lt8713sx); 550 + if (ret) 551 + return ret; 552 + 553 + lt8713sx_reset(lt8713sx); 554 + 555 + lt8713sx->bridge.funcs = &lt8713sx_bridge_funcs; 556 + lt8713sx->bridge.of_node = dev->of_node; 557 + lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; 558 + drm_bridge_add(&lt8713sx->bridge); 559 + 560 + crc8_populate_msb(lt8713sx_crc_table, 0x31); 561 + 562 + return 0; 563 + } 564 + 565 + static void lt8713sx_remove(struct i2c_client *client) 566 + { 567 + struct lt8713sx *lt8713sx = i2c_get_clientdata(client); 568 + 569 + drm_bridge_remove(&lt8713sx->bridge); 570 + } 571 + 572 + static struct i2c_device_id lt8713sx_id[] = { 573 + { "lontium,lt8713sx", 0 }, 574 + { /* sentinel */ } 575 + }; 576 + 577 + static const struct of_device_id lt8713sx_match_table[] = { 578 + { .compatible = "lontium,lt8713sx" }, 579 + { /* sentinel */ } 580 + }; 581 + MODULE_DEVICE_TABLE(of, lt8713sx_match_table); 582 + 583 + static struct i2c_driver lt8713sx_driver = { 584 + .driver = { 585 + .name = "lt8713sx", 586 + .of_match_table = lt8713sx_match_table, 587 + .dev_groups = lt8713sx_attr_groups, 588 + }, 589 + .probe = lt8713sx_probe, 590 + .remove = lt8713sx_remove, 591 + .id_table = lt8713sx_id, 592 + }; 593 + 594 + module_i2c_driver(lt8713sx_driver); 595 + MODULE_LICENSE("GPL"); 596 + MODULE_DESCRIPTION("lt8713sx drm bridge driver"); 597 + MODULE_AUTHOR("Vishnu Saini <vishnu.saini@oss.qualcomm.com>"); 598 + MODULE_FIRMWARE(FW_FILE);