Skip to content

Commit d2b8c6d

Browse files
andrejpicejsuperna9999
authored andcommitted
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 <[email protected]> Reviewed-by: Dmitry Baryshkov <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Neil Armstrong <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 63f4e7d commit d2b8c6d

File tree

1 file changed

+142
-3
lines changed

1 file changed

+142
-3
lines changed

drivers/gpu/drm/bridge/ti-sn65dsi83.c

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@
132132
#define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2)
133133
#define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0)
134134

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+
135145
enum sn65dsi83_model {
136146
MODEL_SN65DSI83,
137147
MODEL_SN65DSI84,
@@ -147,6 +157,8 @@ struct sn65dsi83 {
147157
struct regulator *vcc;
148158
bool lvds_dual_link;
149159
bool lvds_dual_link_even_odd_swap;
160+
int lvds_vod_swing_conf[2];
161+
int lvds_term_conf[2];
150162
};
151163

152164
static const struct regmap_range sn65dsi83_readable_ranges[] = {
@@ -237,6 +249,36 @@ static const struct regmap_config sn65dsi83_regmap_config = {
237249
.max_register = REG_IRQ_STAT,
238250
};
239251

252+
static const int lvds_vod_swing_data_table[2][4][2] = {
253+
{ /* 100 Ohm */
254+
{ 180000, 313000 },
255+
{ 215000, 372000 },
256+
{ 250000, 430000 },
257+
{ 290000, 488000 },
258+
},
259+
{ /* 200 Ohm */
260+
{ 150000, 261000 },
261+
{ 200000, 346000 },
262+
{ 250000, 428000 },
263+
{ 300000, 511000 },
264+
},
265+
};
266+
267+
static const int lvds_vod_swing_clock_table[2][4][2] = {
268+
{ /* 100 Ohm */
269+
{ 140000, 244000 },
270+
{ 168000, 290000 },
271+
{ 195000, 335000 },
272+
{ 226000, 381000 },
273+
},
274+
{ /* 200 Ohm */
275+
{ 117000, 204000 },
276+
{ 156000, 270000 },
277+
{ 195000, 334000 },
278+
{ 234000, 399000 },
279+
},
280+
};
281+
240282
static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge)
241283
{
242284
return container_of(bridge, struct sn65dsi83, bridge);
@@ -435,12 +477,16 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
435477
val |= REG_LVDS_FMT_LVDS_LINK_CFG;
436478

437479
regmap_write(ctx->regmap, REG_LVDS_FMT, val);
438-
regmap_write(ctx->regmap, REG_LVDS_VCOM, 0x05);
480+
regmap_write(ctx->regmap, REG_LVDS_VCOM,
481+
REG_LVDS_VCOM_CHA_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_A]) |
482+
REG_LVDS_VCOM_CHB_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_B]));
439483
regmap_write(ctx->regmap, REG_LVDS_LANE,
440484
(ctx->lvds_dual_link_even_odd_swap ?
441485
REG_LVDS_LANE_EVEN_ODD_SWAP : 0) |
442-
REG_LVDS_LANE_CHA_LVDS_TERM |
443-
REG_LVDS_LANE_CHB_LVDS_TERM);
486+
(ctx->lvds_term_conf[CHANNEL_A] ?
487+
REG_LVDS_LANE_CHA_LVDS_TERM : 0) |
488+
(ctx->lvds_term_conf[CHANNEL_B] ?
489+
REG_LVDS_LANE_CHB_LVDS_TERM : 0));
444490
regmap_write(ctx->regmap, REG_LVDS_CM, 0x00);
445491

446492
le16val = cpu_to_le16(mode->hdisplay);
@@ -576,10 +622,103 @@ static const struct drm_bridge_funcs sn65dsi83_funcs = {
576622
.atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts,
577623
};
578624

625+
static int sn65dsi83_select_lvds_vod_swing(struct device *dev,
626+
u32 lvds_vod_swing_data[2], u32 lvds_vod_swing_clk[2], u8 lvds_term)
627+
{
628+
int i;
629+
630+
for (i = 0; i <= 3; i++) {
631+
if (lvds_vod_swing_data_table[lvds_term][i][0] >= lvds_vod_swing_data[0] &&
632+
lvds_vod_swing_data_table[lvds_term][i][1] <= lvds_vod_swing_data[1] &&
633+
lvds_vod_swing_clock_table[lvds_term][i][0] >= lvds_vod_swing_clk[0] &&
634+
lvds_vod_swing_clock_table[lvds_term][i][1] <= lvds_vod_swing_clk[1])
635+
return i;
636+
}
637+
638+
dev_err(dev, "failed to find appropriate LVDS_VOD_SWING configuration\n");
639+
return -EINVAL;
640+
}
641+
642+
static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel)
643+
{
644+
struct device *dev = ctx->dev;
645+
struct device_node *endpoint;
646+
int endpoint_reg;
647+
/* Set so the property can be freely selected if not defined */
648+
u32 lvds_vod_swing_data[2] = { 0, 1000000 };
649+
u32 lvds_vod_swing_clk[2] = { 0, 1000000 };
650+
/* Set default near end terminataion to 200 Ohm */
651+
u32 lvds_term = 200;
652+
int lvds_vod_swing_conf;
653+
int ret = 0;
654+
int ret_data;
655+
int ret_clock;
656+
657+
if (channel == CHANNEL_A)
658+
endpoint_reg = 2;
659+
else
660+
endpoint_reg = 3;
661+
662+
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1);
663+
664+
of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term);
665+
if (lvds_term == 100)
666+
ctx->lvds_term_conf[channel] = OHM_100;
667+
else if (lvds_term == 200)
668+
ctx->lvds_term_conf[channel] = OHM_200;
669+
else {
670+
ret = -EINVAL;
671+
goto exit;
672+
}
673+
674+
ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt",
675+
lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data));
676+
if (ret_data != 0 && ret_data != -EINVAL) {
677+
ret = ret_data;
678+
goto exit;
679+
}
680+
681+
ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt",
682+
lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk));
683+
if (ret_clock != 0 && ret_clock != -EINVAL) {
684+
ret = ret_clock;
685+
goto exit;
686+
}
687+
688+
/* Use default value if both properties are NOT defined. */
689+
if (ret_data == -EINVAL && ret_clock == -EINVAL)
690+
lvds_vod_swing_conf = 0x1;
691+
692+
/* Use lookup table if any of the two properties is defined. */
693+
if (!ret_data || !ret_clock) {
694+
lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data,
695+
lvds_vod_swing_clk, ctx->lvds_term_conf[channel]);
696+
if (lvds_vod_swing_conf < 0) {
697+
ret = lvds_vod_swing_conf;
698+
goto exit;
699+
}
700+
}
701+
702+
ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf;
703+
ret = 0;
704+
exit:
705+
of_node_put(endpoint);
706+
return ret;
707+
}
708+
579709
static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
580710
{
581711
struct drm_bridge *panel_bridge;
582712
struct device *dev = ctx->dev;
713+
int ret;
714+
715+
ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_A);
716+
if (ret < 0)
717+
return ret;
718+
719+
ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_B);
720+
if (ret < 0)
721+
return ret;
583722

584723
ctx->lvds_dual_link = false;
585724
ctx->lvds_dual_link_even_odd_swap = false;

0 commit comments

Comments
 (0)