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

drm/bridge: ti-sn65dsi83: Add ti,lvds-vod-swing optional properties

Add a optional properties to change LVDS output voltage. This should not
be static as this depends mainly on the connected display voltage
requirement. We have three properties:
- "ti,lvds-termination-ohms", which sets near end termination,
- "ti,lvds-vod-swing-data-microvolt" and
- "ti,lvds-vod-swing-clock-microvolt" which both set LVDS differential
output voltage for data and clock lanes. They are defined as an array
with min and max values. The appropriate bitfield will be set if
selected constraints can be met.

If "ti,lvds-termination-ohms" is not defined the default of 200 Ohm near
end termination will be used. Selecting only one:
"ti,lvds-vod-swing-data-microvolt" or
"ti,lvds-vod-swing-clock-microvolt" can be done, but the output voltage
constraint for only data/clock lanes will be met. Setting both is
recommended.

Signed-off-by: Andrej Picej <andrej.picej@norik.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://lore.kernel.org/r/20241216085410.1968634-3-andrej.picej@norik.com
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20241216085410.1968634-3-andrej.picej@norik.com

authored by

Andrej Picej and committed by
Neil Armstrong
d2b8c6d5 63f4e7df

+142 -3
+142 -3
drivers/gpu/drm/bridge/ti-sn65dsi83.c
··· 132 132 #define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2) 133 133 #define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0) 134 134 135 + enum sn65dsi83_channel { 136 + CHANNEL_A, 137 + CHANNEL_B 138 + }; 139 + 140 + enum sn65dsi83_lvds_term { 141 + OHM_100, 142 + OHM_200 143 + }; 144 + 135 145 enum sn65dsi83_model { 136 146 MODEL_SN65DSI83, 137 147 MODEL_SN65DSI84, ··· 157 147 struct regulator *vcc; 158 148 bool lvds_dual_link; 159 149 bool lvds_dual_link_even_odd_swap; 150 + int lvds_vod_swing_conf[2]; 151 + int lvds_term_conf[2]; 160 152 }; 161 153 162 154 static const struct regmap_range sn65dsi83_readable_ranges[] = { ··· 247 235 .volatile_table = &sn65dsi83_volatile_table, 248 236 .cache_type = REGCACHE_MAPLE, 249 237 .max_register = REG_IRQ_STAT, 238 + }; 239 + 240 + static const int lvds_vod_swing_data_table[2][4][2] = { 241 + { /* 100 Ohm */ 242 + { 180000, 313000 }, 243 + { 215000, 372000 }, 244 + { 250000, 430000 }, 245 + { 290000, 488000 }, 246 + }, 247 + { /* 200 Ohm */ 248 + { 150000, 261000 }, 249 + { 200000, 346000 }, 250 + { 250000, 428000 }, 251 + { 300000, 511000 }, 252 + }, 253 + }; 254 + 255 + static const int lvds_vod_swing_clock_table[2][4][2] = { 256 + { /* 100 Ohm */ 257 + { 140000, 244000 }, 258 + { 168000, 290000 }, 259 + { 195000, 335000 }, 260 + { 226000, 381000 }, 261 + }, 262 + { /* 200 Ohm */ 263 + { 117000, 204000 }, 264 + { 156000, 270000 }, 265 + { 195000, 334000 }, 266 + { 234000, 399000 }, 267 + }, 250 268 }; 251 269 252 270 static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge) ··· 477 435 val |= REG_LVDS_FMT_LVDS_LINK_CFG; 478 436 479 437 regmap_write(ctx->regmap, REG_LVDS_FMT, val); 480 - regmap_write(ctx->regmap, REG_LVDS_VCOM, 0x05); 438 + regmap_write(ctx->regmap, REG_LVDS_VCOM, 439 + REG_LVDS_VCOM_CHA_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_A]) | 440 + REG_LVDS_VCOM_CHB_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_B])); 481 441 regmap_write(ctx->regmap, REG_LVDS_LANE, 482 442 (ctx->lvds_dual_link_even_odd_swap ? 483 443 REG_LVDS_LANE_EVEN_ODD_SWAP : 0) | 484 - REG_LVDS_LANE_CHA_LVDS_TERM | 485 - REG_LVDS_LANE_CHB_LVDS_TERM); 444 + (ctx->lvds_term_conf[CHANNEL_A] ? 445 + REG_LVDS_LANE_CHA_LVDS_TERM : 0) | 446 + (ctx->lvds_term_conf[CHANNEL_B] ? 447 + REG_LVDS_LANE_CHB_LVDS_TERM : 0)); 486 448 regmap_write(ctx->regmap, REG_LVDS_CM, 0x00); 487 449 488 450 le16val = cpu_to_le16(mode->hdisplay); ··· 622 576 .atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts, 623 577 }; 624 578 579 + static int sn65dsi83_select_lvds_vod_swing(struct device *dev, 580 + u32 lvds_vod_swing_data[2], u32 lvds_vod_swing_clk[2], u8 lvds_term) 581 + { 582 + int i; 583 + 584 + for (i = 0; i <= 3; i++) { 585 + if (lvds_vod_swing_data_table[lvds_term][i][0] >= lvds_vod_swing_data[0] && 586 + lvds_vod_swing_data_table[lvds_term][i][1] <= lvds_vod_swing_data[1] && 587 + lvds_vod_swing_clock_table[lvds_term][i][0] >= lvds_vod_swing_clk[0] && 588 + lvds_vod_swing_clock_table[lvds_term][i][1] <= lvds_vod_swing_clk[1]) 589 + return i; 590 + } 591 + 592 + dev_err(dev, "failed to find appropriate LVDS_VOD_SWING configuration\n"); 593 + return -EINVAL; 594 + } 595 + 596 + static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel) 597 + { 598 + struct device *dev = ctx->dev; 599 + struct device_node *endpoint; 600 + int endpoint_reg; 601 + /* Set so the property can be freely selected if not defined */ 602 + u32 lvds_vod_swing_data[2] = { 0, 1000000 }; 603 + u32 lvds_vod_swing_clk[2] = { 0, 1000000 }; 604 + /* Set default near end terminataion to 200 Ohm */ 605 + u32 lvds_term = 200; 606 + int lvds_vod_swing_conf; 607 + int ret = 0; 608 + int ret_data; 609 + int ret_clock; 610 + 611 + if (channel == CHANNEL_A) 612 + endpoint_reg = 2; 613 + else 614 + endpoint_reg = 3; 615 + 616 + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1); 617 + 618 + of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term); 619 + if (lvds_term == 100) 620 + ctx->lvds_term_conf[channel] = OHM_100; 621 + else if (lvds_term == 200) 622 + ctx->lvds_term_conf[channel] = OHM_200; 623 + else { 624 + ret = -EINVAL; 625 + goto exit; 626 + } 627 + 628 + ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt", 629 + lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data)); 630 + if (ret_data != 0 && ret_data != -EINVAL) { 631 + ret = ret_data; 632 + goto exit; 633 + } 634 + 635 + ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt", 636 + lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk)); 637 + if (ret_clock != 0 && ret_clock != -EINVAL) { 638 + ret = ret_clock; 639 + goto exit; 640 + } 641 + 642 + /* Use default value if both properties are NOT defined. */ 643 + if (ret_data == -EINVAL && ret_clock == -EINVAL) 644 + lvds_vod_swing_conf = 0x1; 645 + 646 + /* Use lookup table if any of the two properties is defined. */ 647 + if (!ret_data || !ret_clock) { 648 + lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data, 649 + lvds_vod_swing_clk, ctx->lvds_term_conf[channel]); 650 + if (lvds_vod_swing_conf < 0) { 651 + ret = lvds_vod_swing_conf; 652 + goto exit; 653 + } 654 + } 655 + 656 + ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf; 657 + ret = 0; 658 + exit: 659 + of_node_put(endpoint); 660 + return ret; 661 + } 662 + 625 663 static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) 626 664 { 627 665 struct drm_bridge *panel_bridge; 628 666 struct device *dev = ctx->dev; 667 + int ret; 668 + 669 + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_A); 670 + if (ret < 0) 671 + return ret; 672 + 673 + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_B); 674 + if (ret < 0) 675 + return ret; 629 676 630 677 ctx->lvds_dual_link = false; 631 678 ctx->lvds_dual_link_even_odd_swap = false;