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+
135145enum 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
152164static 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+
240282static 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+
579709static 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