drm/tegra: Add DisplayPort support
cover drm/tegra: Add DisplayPort support
From: Thierry Reding <[email protected]>
This set of patches build on top of the existing eDP support that exists for Tegra124 and extends it with full DP support on Tegra210, Tegra186 and Tegra194. After the series, the eDP code is unified with the DP code and only parameterized where necessary.
Thierry
Thierry Reding (32):
drm/tegra: Add missing kerneldoc for struct drm_dp_link drm/tegra: dp: Add drm_dp_link_reset() implementation drm/tegra: dp: Track link capabilities alongside settings drm/tegra: dp: Turn link capabilities into booleans drm/tegra: dp: Probe link using existing parsing helpers drm/tegra: dp: Read fast training capability from link drm/tegra: dp: Read TPS3 capability from sink drm/tegra: dp: Read channel coding capability from sink drm/tegra: dp: Read alternate scrambler reset capability from sink drm/tegra: dp: Read eDP version from DPCD
drm/tegra: dp: Read AUX read interval from DPCD drm/tegra: dp: Set channel coding on link configuration drm/tegra: dp: Enable alternate scrambler reset when supported drm/tegra: dp: Add drm_dp_link_choose() helper drm/tegra: dp: Add support for eDP link rates drm/tegra: dp: Add DisplayPort link training helper drm/tegra: sor: Use DP link training helpers
drm/tegra: sor: Hook up I2C-over-AUX to output drm/tegra: sor: Stabilize eDP
drm/tegra: sor: Filter eDP rates
drm/tegra: sor: Add DisplayPort support
drm/tegra: sor: Remove tegra186-sor1 support
drm/tegra: sor: Use correct SOR index on Tegra210 drm/tegra: sor: Implement pad clock for all SOR instances drm/tegra: sor: Deduplicate connector type detection code drm/tegra: sor: Support DisplayPort on Tegra194 drm/tegra: sor: Unify clock setup for eDP, HDMI and DP drm/tegra: sor: Use correct I/O pad for DP
drm/tegra: sor: Unify eDP and DP support
drm/tegra: sor: Avoid timeouts on unplug events drm/tegra: sor: Extract common audio enabling code drm/tegra: sor: Introduce audio enable/disable callbacks
drivers/gpu/drm/tegra/dp.c | 765 +++++++++- drivers/gpu/drm/tegra/dp.h | 157 +- drivers/gpu/drm/tegra/dpaux.c | 69 - drivers/gpu/drm/tegra/drm.h | 5 - drivers/gpu/drm/tegra/sor.c | 2571 +++++++++++++++++++-------------- drivers/gpu/drm/tegra/sor.h | 3 + 6 files changed, 2395 insertions(+), 1175 deletions(-)
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 1/32 drm/tegra: Add missing kerneldoc for struct drm_dp_link
From: Thierry Reding <[email protected]>
The drm_dp_link structure tracks capabilities on the DP link. Add some kerneldoc to explain what each of its fields means.
--- drivers/gpu/drm/tegra/dp.h | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/gpu/drm/tegra/dp.h +7 -0
@@ -11,6 +11,13 @@ struct drm_dp_aux;#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) +/** +* struct drm_dp_link - DP link capabilities +* @revision: DP specification revision supported on the link +* @rate: maximum clock rate supported on the link +* @num_lanes: maximum number of lanes supported on the link +* @capabilities: bitmask of capabilities supported on the link +*/ struct drm_dp_link {unsigned char revision;unsigned int rate;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 2/32 drm/tegra: dp: Add drm_dp_link_reset() implementation
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Subsequent patches will add non-volatile fields to struct drm_dp_link, so introduce a function to zero out only the volatile fields.
--- drivers/gpu/drm/tegra/dp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
drivers/gpu/drm/tegra/dp.c +12 -1
@@ -8,6 +8,17 @@#include "dp.h" +static void drm_dp_link_reset (struct drm_dp_link *link) +{ +if (!link) +return ; + + link->revision =0 ; + link->rate =0 ; + link->num_lanes =0 ; + link->capabilities =0 ; +} +/** * drm_dp_link_probe() - probe a DisplayPort link for capabilities * @aux: DisplayPort AUX channel
@@ -24,7 +35,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)u8 values[3 ];int err; -memset (link,0 ,sizeof (*link)); +drm_dp_link_reset (link); err =drm_dp_dpcd_read (aux,DP_DPCD_REV , values,sizeof (values));if (err <0 )
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 3/32 drm/tegra: dp: Track link capabilities alongside settings
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Store capabilities in max_* fields and add separate fields for the currently selected settings.
--- drivers/gpu/drm/tegra/dp.c | 16 +++++++++++----- drivers/gpu/drm/tegra/dp.h | 15 ++++++++++----- drivers/gpu/drm/tegra/dpaux.c | 8 ++++---- drivers/gpu/drm/tegra/sor.c | 28 ++++++++++++++-------------- 4 files changed, 39 insertions(+), 28 deletions(-)
drivers/gpu/drm/tegra/dp.c +11 -5
@@ -14,9 +14,12 @@ static void drm_dp_link_reset(struct drm_dp_link *link)return ; link->revision =0 ; - link->rate =0 ; - link->num_lanes =0 ; + link->max_rate =0 ; + link->max_lanes =0 ; link->capabilities =0 ; + + link->rate =0 ; + link->lanes =0 ; }/**
@@ -42,12 +45,15 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)return err; link->revision = values[0 ]; - link->rate =drm_dp_bw_code_to_link_rate (values[1 ]); - link->num_lanes = values[2 ] &DP_MAX_LANE_COUNT_MASK ; + link->max_rate =drm_dp_bw_code_to_link_rate (values[1 ]); + link->max_lanes = values[2 ] &DP_MAX_LANE_COUNT_MASK ;if (values[2 ] &DP_ENHANCED_FRAME_CAP ) link->capabilities |=DP_LINK_CAP_ENHANCED_FRAMING ; + link->rate = link->max_rate; + link->lanes = link->max_lanes; +return 0 ; }
@@ -131,7 +137,7 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)int err; values[0 ] =drm_dp_link_rate_to_bw_code (link->rate); - values[1 ] = link->num_lanes; + values[1 ] = link->lanes;if (link->capabilities &DP_LINK_CAP_ENHANCED_FRAMING ) values[1 ] |=DP_LANE_COUNT_ENHANCED_FRAME_EN ;
drivers/gpu/drm/tegra/dp.h +10 -5
@@ -12,17 +12,22 @@ struct drm_dp_aux;#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)/** -* struct drm_dp_link - DP link capabilities +* struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link -* @rate: maximum clock rate supported on the link -* @num_lanes: maximum number of lanes supported on the link +* @max_rate: maximum clock rate supported on the link +* @max_lanes: maximum number of lanes supported on the link * @capabilities: bitmask of capabilities supported on the link +* @rate: currently configured link rate +* @lanes: currently configured number of lanes */ struct drm_dp_link {unsigned char revision; -unsigned int rate; -unsigned int num_lanes; +unsigned int max_rate; +unsigned int max_lanes;unsigned long capabilities; + +unsigned int rate; +unsigned int lanes; };int drm_dp_link_probe (struct drm_dp_aux *aux,struct drm_dp_link *link);
drivers/gpu/drm/tegra/dpaux.c +4 -4
@@ -849,14 +849,14 @@ int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,if (tp ==DP_TRAINING_PATTERN_DISABLE )return 0 ; -for (i =0 ; i < link->num_lanes; i++) +for (i =0 ; i < link->lanes; i++) values[i] =DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |DP_TRAIN_PRE_EMPH_LEVEL_0 |DP_TRAIN_MAX_SWING_REACHED |DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ; err =drm_dp_dpcd_write (aux,DP_TRAINING_LANE0_SET , values, - link->num_lanes); + link->lanes);if (err <0 )return err;
@@ -868,13 +868,13 @@ int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,switch (tp) {case DP_TRAINING_PATTERN_1 : -if (!drm_dp_clock_recovery_ok (status, link->num_lanes)) +if (!drm_dp_clock_recovery_ok (status, link->lanes))return -EAGAIN ;break ;case DP_TRAINING_PATTERN_2 : -if (!drm_dp_channel_eq_ok (status, link->num_lanes)) +if (!drm_dp_channel_eq_ok (status, link->lanes))return -EAGAIN ;break ;
drivers/gpu/drm/tegra/sor.c +14 -14
@@ -650,7 +650,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor,if (err <0 )return err; -for (i =0 , value =0 ; i < link->num_lanes; i++) { +for (i =0 , value =0 ; i < link->lanes; i++) {unsigned long lane =SOR_DP_TPG_CHANNEL_CODING |SOR_DP_TPG_SCRAMBLER_NONE |SOR_DP_TPG_PATTERN_TRAIN1 ;
@@ -671,7 +671,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, value |=SOR_DP_SPARE_MACRO_SOR_CLK ;tegra_sor_writel (sor, value,SOR_DP_SPARE0 ); -for (i =0 , value =0 ; i < link->num_lanes; i++) { +for (i =0 , value =0 ; i < link->lanes; i++) {unsigned long lane =SOR_DP_TPG_CHANNEL_CODING |SOR_DP_TPG_SCRAMBLER_NONE |SOR_DP_TPG_PATTERN_TRAIN2 ;
@@ -686,7 +686,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor,if (err <0 )return err; -for (i =0 , value =0 ; i < link->num_lanes; i++) { +for (i =0 , value =0 ; i < link->lanes; i++) {unsigned long lane =SOR_DP_TPG_CHANNEL_CODING |SOR_DP_TPG_SCRAMBLER_GALIOS |SOR_DP_TPG_PATTERN_NONE ;
@@ -913,11 +913,11 @@ static int tegra_sor_compute_config(struct tegra_sor *sor,u32 num_syms_per_line;unsigned int i; -if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) +if (!link_rate || !link->lanes || !pclk || !config->bits_per_pixel)return -EINVAL ; - output = link_rate *8 * link->num_lanes; input = pclk * config->bits_per_pixel; + output = link_rate *8 * link->lanes;if (input >= output)return -ERANGE ;
@@ -960,7 +960,7 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, watermark =div_u64 (watermark + params.error, f); config->watermark = watermark + (config->bits_per_pixel /8 ) +2 ; num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * - (link->num_lanes *8 ); + (link->lanes *8 );if (config->watermark >30 ) { config->watermark =30 ;
@@ -980,12 +980,12 @@ static int tegra_sor_compute_config(struct tegra_sor *sor,if (link->capabilities &DP_LINK_CAP_ENHANCED_FRAMING ) config->hblank_symbols -=3 ; - config->hblank_symbols -=12 / link->num_lanes; + config->hblank_symbols -=12 / link->lanes;/* compute the number of symbols per vertical blanking interval */ num = (mode->hdisplay -25 ) * link_rate; config->vblank_symbols =div_u64 (num, pclk); - config->vblank_symbols -=36 / link->num_lanes +4 ; + config->vblank_symbols -=36 / link->lanes +4 ;dev_dbg (sor->dev,"blank symbols: H:%u V:%u\n" , config->hblank_symbols, config->vblank_symbols);
@@ -1831,17 +1831,17 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)/* power DP lanes */ value =tegra_sor_readl (sor, sor->soc->regs->dp_padctl0); -if (link.num_lanes <=2 ) +if (link.lanes <=2 ) value &= ~(SOR_DP_PADCTL_PD_TXD_3 |SOR_DP_PADCTL_PD_TXD_2 );else value |=SOR_DP_PADCTL_PD_TXD_3 |SOR_DP_PADCTL_PD_TXD_2 ; -if (link.num_lanes <=1 ) +if (link.lanes <=1 ) value &= ~SOR_DP_PADCTL_PD_TXD_1 ;else value |=SOR_DP_PADCTL_PD_TXD_1 ; -if (link.num_lanes ==0 ) +if (link.lanes ==0 ) value &= ~SOR_DP_PADCTL_PD_TXD_0 ;else value |=SOR_DP_PADCTL_PD_TXD_0 ;
@@ -1850,7 +1850,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value =tegra_sor_readl (sor,SOR_DP_LINKCTL0 ); value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK ; - value |=SOR_DP_LINKCTL_LANE_COUNT (link.num_lanes); + value |=SOR_DP_LINKCTL_LANE_COUNT (link.lanes);tegra_sor_writel (sor, value,SOR_DP_LINKCTL0 );/* start lane sequencer */
@@ -1907,7 +1907,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)dev_err (sor->dev,"failed to configure eDP link: %d\n" , err); rate =drm_dp_link_rate_to_bw_code (link.rate); - lanes = link.num_lanes; + lanes = link.lanes; value =tegra_sor_readl (sor,SOR_CLK_CNTRL ); value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK ;
@@ -1925,7 +1925,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)/* disable training pattern generator */ -for (i =0 ; i < link.num_lanes; i++) { +for (i =0 ; i < link.lanes; i++) {unsigned long lane =SOR_DP_TPG_CHANNEL_CODING |SOR_DP_TPG_SCRAMBLER_GALIOS |SOR_DP_TPG_PATTERN_NONE ;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 4/32 drm/tegra: dp: Turn link capabilities into booleans
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Rather than storing capabilities as flags in an integer, use a separate boolean per capability. This simplifies the code that checks for these capabilities.
--- drivers/gpu/drm/tegra/dp.c | 18 +++++++++++++++--- drivers/gpu/drm/tegra/dp.h | 22 +++++++++++++++++++--- drivers/gpu/drm/tegra/sor.c | 4 ++-- 3 files changed, 36 insertions(+), 8 deletions(-)
drivers/gpu/drm/tegra/dp.c +15 -3
@@ -8,6 +8,17 @@#include "dp.h" +static void drm_dp_link_caps_reset (struct drm_dp_link_caps *caps) +{ + caps->enhanced_framing = false; +} + +void drm_dp_link_caps_copy (struct drm_dp_link_caps *dest, +const struct drm_dp_link_caps *src) +{ + dest->enhanced_framing = src->enhanced_framing; +} +static void drm_dp_link_reset (struct drm_dp_link *link) {if (!link)
@@ -16,7 +27,8 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->revision =0 ; link->max_rate =0 ; link->max_lanes =0 ; - link->capabilities =0 ; + +drm_dp_link_caps_reset (&link->caps); link->rate =0 ; link->lanes =0 ;
@@ -49,7 +61,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes = values[2 ] &DP_MAX_LANE_COUNT_MASK ;if (values[2 ] &DP_ENHANCED_FRAME_CAP ) - link->capabilities |=DP_LINK_CAP_ENHANCED_FRAMING ; + link->caps.enhanced_framing = true; link->rate = link->max_rate; link->lanes = link->max_lanes;
@@ -139,7 +151,7 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) values[0 ] =drm_dp_link_rate_to_bw_code (link->rate); values[1 ] = link->lanes; -if (link->capabilities &DP_LINK_CAP_ENHANCED_FRAMING ) +if (link->caps.enhanced_framing) values[1 ] |=DP_LANE_COUNT_ENHANCED_FRAME_EN ; err =drm_dp_dpcd_write (aux,DP_LINK_BW_SET , values,sizeof (values));
drivers/gpu/drm/tegra/dp.h +19 -3
@@ -7,16 +7,31 @@#ifndef DRM_TEGRA_DP_H #define DRM_TEGRA_DP_H 1 +#include <linux/types.h> +struct drm_dp_aux ; -#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) +/** +* struct drm_dp_link_caps - DP link capabilities +*/ +struct drm_dp_link_caps { +/** +* @enhanced_framing: +* +* enhanced framing capability (mandatory as of DP 1.2) +*/ +bool enhanced_framing; +}; + +void drm_dp_link_caps_copy (struct drm_dp_link_caps *dest, +const struct drm_dp_link_caps *src);/** * struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link * @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link -* @capabilities: bitmask of capabilities supported on the link +* @caps: capabilities supported on the link (see &drm_dp_link_caps) * @rate: currently configured link rate * @lanes: currently configured number of lanes */
@@ -24,7 +39,8 @@ struct drm_dp_link {unsigned char revision;unsigned int max_rate;unsigned int max_lanes; -unsigned long capabilities; + +struct drm_dp_link_caps caps;unsigned int rate;unsigned int lanes;
drivers/gpu/drm/tegra/sor.c +2 -2
@@ -977,7 +977,7 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, num = ((mode->htotal - mode->hdisplay) -7 ) * link_rate; config->hblank_symbols =div_u64 (num, pclk); -if (link->capabilities &DP_LINK_CAP_ENHANCED_FRAMING ) +if (link->caps.enhanced_framing) config->hblank_symbols -=3 ; config->hblank_symbols -=12 / link->lanes;
@@ -1918,7 +1918,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK ; value |=SOR_DP_LINKCTL_LANE_COUNT (lanes); -if (link.capabilities &DP_LINK_CAP_ENHANCED_FRAMING ) +if (link.caps.enhanced_framing) value |=SOR_DP_LINKCTL_ENHANCED_FRAME ;tegra_sor_writel (sor, value,SOR_DP_LINKCTL0 );
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 5/32 drm/tegra: dp: Probe link using existing parsing helpers
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Use existing parsing helpers to probe a DisplayPort link.
--- drivers/gpu/drm/tegra/dp.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
drivers/gpu/drm/tegra/dp.c +6 -7
@@ -47,21 +47,20 @@ static void drm_dp_link_reset(struct drm_dp_link *link)*/ int drm_dp_link_probe (struct drm_dp_aux *aux,struct drm_dp_link *link) { -u8 values[3 ]; +u8 dpcd[DP_RECEIVER_CAP_SIZE ];int err;drm_dp_link_reset (link); - err =drm_dp_dpcd_read (aux,DP_DPCD_REV , values,sizeof (values)); + err =drm_dp_dpcd_read (aux,DP_DPCD_REV , dpcd,sizeof (dpcd));if (err <0 )return err; - link->revision = values[0 ]; - link->max_rate =drm_dp_bw_code_to_link_rate (values[1 ]); - link->max_lanes = values[2 ] &DP_MAX_LANE_COUNT_MASK ; + link->revision = dpcd[DP_DPCD_REV ]; + link->max_rate =drm_dp_max_link_rate (dpcd); + link->max_lanes =drm_dp_max_lane_count (dpcd); -if (values[2 ] &DP_ENHANCED_FRAME_CAP ) - link->caps.enhanced_framing = true; + link->caps.enhanced_framing =drm_dp_enhanced_frame_cap (dpcd); link->rate = link->max_rate; link->lanes = link->max_lanes;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 6/32 drm/tegra: dp: Read fast training capability from link
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
While probing the DisplayPort link, query the fast training capability. If supported, drivers can use the fast link training sequence instead of the more involved full link training sequence.
--- drivers/gpu/drm/tegra/dp.c | 3 +++ drivers/gpu/drm/tegra/dp.h | 7 +++++++ 2 files changed, 10 insertions(+)
drivers/gpu/drm/tegra/dp.c +3 -0
@@ -11,12 +11,14 @@static void drm_dp_link_caps_reset (struct drm_dp_link_caps *caps) { caps->enhanced_framing = false; + caps->fast_training = false; }void drm_dp_link_caps_copy (struct drm_dp_link_caps *dest,const struct drm_dp_link_caps *src) { dest->enhanced_framing = src->enhanced_framing; + dest->fast_training = src->fast_training; }static void drm_dp_link_reset (struct drm_dp_link *link)
@@ -61,6 +63,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes =drm_dp_max_lane_count (dpcd); link->caps.enhanced_framing =drm_dp_enhanced_frame_cap (dpcd); + link->caps.fast_training =drm_dp_fast_training_cap (dpcd); link->rate = link->max_rate; link->lanes = link->max_lanes;
drivers/gpu/drm/tegra/dp.h +7 -0
@@ -21,6 +21,13 @@ struct drm_dp_link_caps {* enhanced framing capability (mandatory as of DP 1.2) */ bool enhanced_framing; + +/** +* @fast_training: +* +* AUX CH handshake not required for link training +*/ +bool fast_training; }; void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 7/32 drm/tegra: dp: Read TPS3 capability from sink
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The TPS3 capability can be exposed by DP 1.2 and later sinks if they support the alternative training pattern for channel equalization.
--- drivers/gpu/drm/tegra/dp.c | 3 +++ drivers/gpu/drm/tegra/dp.h | 7 +++++++ 2 files changed, 10 insertions(+)
drivers/gpu/drm/tegra/dp.c +3 -0
@@ -11,6 +11,7 @@static void drm_dp_link_caps_reset (struct drm_dp_link_caps *caps) { caps->enhanced_framing = false; + caps->tps3_supported = false; caps->fast_training = false; }
@@ -18,6 +19,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, const struct drm_dp_link_caps *src) { dest->enhanced_framing = src->enhanced_framing; + dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; }
@@ -63,6 +65,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes =drm_dp_max_lane_count (dpcd); link->caps.enhanced_framing =drm_dp_enhanced_frame_cap (dpcd); + link->caps.tps3_supported =drm_dp_tps3_supported (dpcd); link->caps.fast_training =drm_dp_fast_training_cap (dpcd); link->rate = link->max_rate;
drivers/gpu/drm/tegra/dp.h +7 -0
@@ -22,6 +22,13 @@ struct drm_dp_link_caps {*/ bool enhanced_framing; +/** +* tps3_supported: +* +* training pattern sequence 3 supported for equalization +*/ +bool tps3_supported; +/** * @fast_training: *
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 8/32 drm/tegra: dp: Read channel coding capability from sink
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Parse from the sink capabilities whether or not it supports ANSI 8B/10B channel coding as specified in ANSI X3.230-1994, clause 11.
--- drivers/gpu/drm/tegra/dp.c | 3 +++ drivers/gpu/drm/tegra/dp.h | 7 +++++++ 2 files changed, 10 insertions(+)
drivers/gpu/drm/tegra/dp.c +3 -0
@@ -13,6 +13,7 @@ static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) caps->enhanced_framing = false; caps->tps3_supported = false; caps->fast_training = false; + caps->channel_coding = false; } void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
@@ -21,6 +22,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, dest->enhanced_framing = src->enhanced_framing; dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; + dest->channel_coding = src->channel_coding; }static void drm_dp_link_reset (struct drm_dp_link *link)
@@ -67,6 +69,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.enhanced_framing =drm_dp_enhanced_frame_cap (dpcd); link->caps.tps3_supported =drm_dp_tps3_supported (dpcd); link->caps.fast_training =drm_dp_fast_training_cap (dpcd); + link->caps.channel_coding =drm_dp_channel_coding_supported (dpcd); link->rate = link->max_rate; link->lanes = link->max_lanes;
drivers/gpu/drm/tegra/dp.h +7 -0
@@ -35,6 +35,13 @@ struct drm_dp_link_caps {* AUX CH handshake not required for link training */ bool fast_training; + +/** +* @channel_coding: +* +* ANSI 8B/10B channel coding capability +*/ +bool channel_coding; }; void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 9/32 drm/tegra: dp: Read alternate scrambler reset capability from sink
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Parse from the sink capabilities whether or not the eDP alternate scrambler reset value of 0xfffe is supported.
--- drivers/gpu/drm/tegra/dp.c | 5 +++++ drivers/gpu/drm/tegra/dp.h | 7 +++++++ 2 files changed, 12 insertions(+)
drivers/gpu/drm/tegra/dp.c +5 -0
@@ -14,6 +14,7 @@ static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) caps->tps3_supported = false; caps->fast_training = false; caps->channel_coding = false; + caps->alternate_scrambler_reset = false; } void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
@@ -23,6 +24,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; dest->channel_coding = src->channel_coding; + dest->alternate_scrambler_reset = src->alternate_scrambler_reset; }static void drm_dp_link_reset (struct drm_dp_link *link)
@@ -71,6 +73,9 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.fast_training =drm_dp_fast_training_cap (dpcd); link->caps.channel_coding =drm_dp_channel_coding_supported (dpcd); +if (drm_dp_alternate_scrambler_reset_cap (dpcd)) + link->caps.alternate_scrambler_reset = true; + link->rate = link->max_rate; link->lanes = link->max_lanes;
drivers/gpu/drm/tegra/dp.h +7 -0
@@ -42,6 +42,13 @@ struct drm_dp_link_caps {* ANSI 8B/10B channel coding capability */ bool channel_coding; + +/** +* @alternate_scrambler_reset: +* +* eDP alternate scrambler reset capability +*/ +bool alternate_scrambler_reset; }; void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 10/32 drm/tegra: dp: Read eDP version from DPCD
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
If the sink supports eDP, read the eDP revision from it's DPCD.
--- drivers/gpu/drm/tegra/dp.c | 18 ++++++++++++++++-- drivers/gpu/drm/tegra/dp.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-)
drivers/gpu/drm/tegra/dp.c +16 -2
@@ -5,9 +5,12 @@*/ #include <drm/drm_dp_helper.h> +#include <drm/drm_print.h> #include "dp.h" +static const u8 drm_dp_edp_revisions[] = {0x11 ,0x12 ,0x13 ,0x14 }; +static void drm_dp_link_caps_reset (struct drm_dp_link_caps *caps) { caps->enhanced_framing = false;
@@ -37,6 +40,7 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->max_lanes =0 ;drm_dp_link_caps_reset (&link->caps); + link->edp =0 ; link->rate =0 ; link->lanes =0 ;
@@ -55,7 +59,7 @@ static void drm_dp_link_reset(struct drm_dp_link *link)*/ int drm_dp_link_probe (struct drm_dp_aux *aux,struct drm_dp_link *link) { -u8 dpcd[DP_RECEIVER_CAP_SIZE ]; +u8 dpcd[DP_RECEIVER_CAP_SIZE ], value;int err;drm_dp_link_reset (link);
@@ -73,9 +77,19 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.fast_training =drm_dp_fast_training_cap (dpcd); link->caps.channel_coding =drm_dp_channel_coding_supported (dpcd); -if (drm_dp_alternate_scrambler_reset_cap (dpcd)) +if (drm_dp_alternate_scrambler_reset_cap (dpcd)) { link->caps.alternate_scrambler_reset = true; + err =drm_dp_dpcd_readb (aux,DP_EDP_DPCD_REV , &value); +if (err <0 ) +return err; + +if (value >=ARRAY_SIZE (drm_dp_edp_revisions)) +DRM_ERROR ("unsupported eDP version: %02x\n" , value); +else + link->edp = drm_dp_edp_revisions[value]; + } + link->rate = link->max_rate; link->lanes = link->max_lanes;
drivers/gpu/drm/tegra/dp.h +2 -0
@@ -60,6 +60,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,* @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link * @caps: capabilities supported on the link (see &drm_dp_link_caps) +* @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes */
@@ -69,6 +70,7 @@ struct drm_dp_link {unsigned int max_lanes;struct drm_dp_link_caps caps; +unsigned char edp;unsigned int rate;unsigned int lanes;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 11/32 drm/tegra: dp: Read AUX read interval from DPCD
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Store the AUX read interval from DPCD, so that it can be used to wait for the durations given in the specification during link training.
--- drivers/gpu/drm/tegra/dp.c | 31 +++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/dp.h | 11 +++++++++++ 2 files changed, 42 insertions(+)
drivers/gpu/drm/tegra/dp.c +31 -0
@@ -40,6 +40,8 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->max_lanes =0 ;drm_dp_link_caps_reset (&link->caps); + link->aux_rd_interval.cr =0 ; + link->aux_rd_interval.ce =0 ; link->edp =0 ; link->rate =0 ;
@@ -60,6 +62,7 @@ static void drm_dp_link_reset(struct drm_dp_link *link)int drm_dp_link_probe (struct drm_dp_aux *aux,struct drm_dp_link *link) {u8 dpcd[DP_RECEIVER_CAP_SIZE ], value; +unsigned int rd_interval;int err;drm_dp_link_reset (link);
@@ -90,6 +93,34 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->edp = drm_dp_edp_revisions[value]; } +/* +* The DPCD stores the AUX read interval in units of 4 ms. There are +* two special cases: +* +* 1) if the TRAINING_AUX_RD_INTERVAL field is 0, the clock recovery +* and channel equalization should use 100 us or 400 us AUX read +* intervals, respectively +* +* 2) for DP v1.4 and above, clock recovery should always use 100 us +* AUX read intervals +*/ + rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL ] & +DP_TRAINING_AUX_RD_MASK ; + +if (rd_interval >4 ) { +DRM_DEBUG_KMS ("AUX interval %u out of range (max. 4)\n" , + rd_interval); + rd_interval =4 ; + } + + rd_interval *=4 *USEC_PER_MSEC ; + +if (rd_interval ==0 || link->revision >=DP_DPCD_REV_14 ) + link->aux_rd_interval.cr =100 ; + +if (rd_interval ==0 ) + link->aux_rd_interval.ce =400 ; + link->rate = link->max_rate; link->lanes = link->max_lanes;
drivers/gpu/drm/tegra/dp.h +11 -0
@@ -60,6 +60,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,* @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link * @caps: capabilities supported on the link (see &drm_dp_link_caps) +* @aux_rd_interval: AUX read interval to use for training (in microseconds) * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes
@@ -70,6 +71,16 @@ struct drm_dp_link {unsigned int max_lanes;struct drm_dp_link_caps caps; + +/** +* @cr: clock recovery read interval +* @ce: channel equalization read interval +*/ +struct { +unsigned int cr; +unsigned int ce; + } aux_rd_interval; +unsigned char edp;unsigned int rate;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 12/32 drm/tegra: dp: Set channel coding on link configuration
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Make use of ANSI 8B/10B channel coding if the DisplayPort sink supports it.
--- drivers/gpu/drm/tegra/dp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/gpu/drm/tegra/dp.c +10 -1
@@ -203,7 +203,7 @@ int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)*/ int drm_dp_link_configure (struct drm_dp_aux *aux,struct drm_dp_link *link) { -u8 values[2 ]; +u8 values[2 ], value;int err; values[0 ] =drm_dp_link_rate_to_bw_code (link->rate);
@@ -216,5 +216,14 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)if (err <0 )return err; +if (link->caps.channel_coding) + value =DP_SET_ANSI_8B10B ; +else + value =0 ; + + err =drm_dp_dpcd_writeb (aux,DP_MAIN_LINK_CHANNEL_CODING_SET , value); +if (err <0 ) +return err; +return 0 ; }
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 13/32 drm/tegra: dp: Enable alternate scrambler reset when supported
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
If the sink is eDP and supports the alternate scrambler reset, enable it.
--- drivers/gpu/drm/tegra/dp.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/gpu/drm/tegra/dp.c +7 -0
@@ -225,5 +225,12 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)if (err <0 )return err; +if (link->caps.alternate_scrambler_reset) { + err =drm_dp_dpcd_writeb (aux,DP_EDP_CONFIGURATION_SET , +DP_ALTERNATE_SCRAMBLER_RESET_ENABLE ); +if (err <0 ) +return err; + } +return 0 ; }
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 14/32 drm/tegra: dp: Add drm_dp_link_choose() helper
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
This helper chooses an appropriate configuration, according to the bitrate requirements of the video mode and the capabilities of the DisplayPort sink.
--- drivers/gpu/drm/tegra/dp.c | 55 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/dp.h | 5 ++++ 2 files changed, 60 insertions(+)
drivers/gpu/drm/tegra/dp.c +55 -0
@@ -4,6 +4,7 @@* Copyright (C) 2015 Rob Clark */ +#include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h> #include <drm/drm_print.h>
@@ -234,3 +235,57 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)return 0 ; } + +/** +* drm_dp_link_choose() - choose the lowest possible configuration for a mode +* @link: DRM DP link object +* @mode: DRM display mode +* @info: DRM display information +* +* According to the eDP specification, a source should select a configuration +* with the lowest number of lanes and the lowest possible link rate that can +* match the bitrate requirements of a video mode. However it must ensure not +* to exceed the capabilities of the sink. +* +* Returns: 0 on success or a negative error code on failure. +*/ +int drm_dp_link_choose (struct drm_dp_link *link, +const struct drm_display_mode *mode, +const struct drm_display_info *info) +{ +/* available link symbol clock rates */ +static const unsigned int rates[3 ] = {162000 ,270000 ,540000 }; +/* available number of lanes */ +static const unsigned int lanes[3 ] = {1 ,2 ,4 }; +unsigned long requirement, capacity; +unsigned int rate = link->max_rate; +unsigned int i, j; + +/* bandwidth requirement */ + requirement = mode->clock * info->bpc *3 ; + +for (i =0 ; i <ARRAY_SIZE (lanes) && lanes[i] <= link->max_lanes; i++) { +for (j =0 ; j <ARRAY_SIZE (rates) && rates[j] <= rate; j++) { +/* +* Capacity for this combination of lanes and rate, +* factoring in the ANSI 8B/10B encoding. +* +* Link rates in the DRM DP helpers are really link +* symbol frequencies, so a tenth of the actual rate +* of the link. +*/ + capacity = lanes[i] * (rates[j] *10 ) *8 /10 ; + +if (capacity >= requirement) { +DRM_DEBUG_KMS ("using %u lanes at %u kHz (%lu/%lu kbps)\n" , + lanes[i], rates[j], requirement, + capacity); + link->lanes = lanes[i]; + link->rate = rates[j]; +return 0 ; + } + } + } + +return -ERANGE ; +}
drivers/gpu/drm/tegra/dp.h +5 -0
@@ -9,6 +9,8 @@#include <linux/types.h> +struct drm_display_info ; +struct drm_display_mode ;struct drm_dp_aux ;/**
@@ -91,5 +93,8 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);int drm_dp_link_power_up (struct drm_dp_aux *aux,struct drm_dp_link *link);int drm_dp_link_power_down (struct drm_dp_aux *aux,struct drm_dp_link *link);int drm_dp_link_configure (struct drm_dp_aux *aux,struct drm_dp_link *link); +int drm_dp_link_choose (struct drm_dp_link *link, +const struct drm_display_mode *mode, +const struct drm_display_info *info);#endif
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 15/32 drm/tegra: dp: Add support for eDP link rates
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Parses additional link rates from DPCD if the sink supports eDP 1.4.
--- drivers/gpu/drm/tegra/dp.c | 127 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/dp.h | 9 +++ 2 files changed, 136 insertions(+)
drivers/gpu/drm/tegra/dp.c +127 -0
@@ -33,6 +33,8 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,static void drm_dp_link_reset (struct drm_dp_link *link) { +unsigned int i; +if (!link)return ;
@@ -47,6 +49,111 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->rate =0 ; link->lanes =0 ; + +for (i =0 ; i <DP_MAX_SUPPORTED_RATES ; i++) + link->rates[i] =0 ; + + link->num_rates =0 ; +} + +/** +* drm_dp_link_add_rate() - add a rate to the list of supported rates +* @link: the link to add the rate to +* @rate: the rate to add +* +* Add a link rate to the list of supported link rates. +* +* Returns: +* 0 on success or one of the following negative error codes on failure: +* - ENOSPC if the maximum number of supported rates has been reached +* - EEXISTS if the link already supports this rate +* +* See also: +* drm_dp_link_remove_rate() +*/ +int drm_dp_link_add_rate (struct drm_dp_link *link,unsigned long rate) +{ +unsigned int i, pivot; + +if (link->num_rates ==DP_MAX_SUPPORTED_RATES ) +return -ENOSPC ; + +for (pivot =0 ; pivot < link->num_rates; pivot++) +if (rate <= link->rates[pivot]) +break ; + +if (pivot != link->num_rates && rate == link->rates[pivot]) +return -EEXIST ; + +for (i = link->num_rates; i > pivot; i--) + link->rates[i] = link->rates[i -1 ]; + + link->rates[pivot] = rate; + link->num_rates++; + +return 0 ; +} + +/** +* drm_dp_link_remove_rate() - remove a rate from the list of supported rates +* @link: the link from which to remove the rate +* @rate: the rate to remove +* +* Removes a link rate from the list of supported link rates. +* +* Returns: +* 0 on success or one of the following negative error codes on failure: +* - EINVAL if the specified rate is not among the supported rates +* +* See also: +* drm_dp_link_add_rate() +*/ +int drm_dp_link_remove_rate (struct drm_dp_link *link,unsigned long rate) +{ +unsigned int i; + +for (i =0 ; i < link->num_rates; i++) +if (rate == link->rates[i]) +break ; + +if (i == link->num_rates) +return -EINVAL ; + + link->num_rates--; + +while (i < link->num_rates) { + link->rates[i] = link->rates[i +1 ]; + i++; + } + +return 0 ; +} + +/** +* drm_dp_link_update_rates() - normalize the supported link rates array +* @link: the link for which to normalize the supported link rates +* +* Users should call this function after they've manually modified the array +* of supported link rates. This function removes any stale entries, compacts +* the array and updates the supported link rate count. Note that calling the +* drm_dp_link_remove_rate() function already does this janitorial work. +* +* See also: +* drm_dp_link_add_rate(), drm_dp_link_remove_rate() +*/ +void drm_dp_link_update_rates (struct drm_dp_link *link) +{ +unsigned int i, count =0 ; + +for (i =0 ; i < link->num_rates; i++) { +if (link->rates[i] !=0 ) + link->rates[count++] = link->rates[i]; + } + +for (i = count; i < link->num_rates; i++) + link->rates[i] =0 ; + + link->num_rates = count; }/**
@@ -125,6 +232,26 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->rate = link->max_rate; link->lanes = link->max_lanes; +/* Parse SUPPORTED_LINK_RATES from eDP 1.4 */ +if (link->edp >=0x14 ) { +u8 supported_rates[DP_MAX_SUPPORTED_RATES *2 ]; +unsigned int i; +u16 rate; + + err =drm_dp_dpcd_read (aux,DP_SUPPORTED_LINK_RATES , + supported_rates, +sizeof (supported_rates)); +if (err <0 ) +return err; + +for (i =0 ; i <DP_MAX_SUPPORTED_RATES ; i++) { + rate = supported_rates[i *2 +1 ] <<8 | + supported_rates[i *2 +0 ]; + +drm_dp_link_add_rate (link, rate *200 ); + } + } +return 0 ; }
drivers/gpu/drm/tegra/dp.h +9 -0
@@ -66,6 +66,8 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,* @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes +* @rates: additional supported link rates in kHz (eDP 1.4) +* @num_rates: number of additional supported link rates (eDP 1.4) */ struct drm_dp_link {unsigned char revision;
@@ -87,8 +89,15 @@ struct drm_dp_link {unsigned int rate;unsigned int lanes; + +unsigned long rates[DP_MAX_SUPPORTED_RATES ]; +unsigned int num_rates; }; +int drm_dp_link_add_rate (struct drm_dp_link *link,unsigned long rate); +int drm_dp_link_remove_rate (struct drm_dp_link *link,unsigned long rate); +void drm_dp_link_update_rates (struct drm_dp_link *link); +int drm_dp_link_probe (struct drm_dp_aux *aux,struct drm_dp_link *link);int drm_dp_link_power_up (struct drm_dp_aux *aux,struct drm_dp_link *link);int drm_dp_link_power_down (struct drm_dp_aux *aux,struct drm_dp_link *link);
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 16/32 drm/tegra: dp: Add DisplayPort link training helper
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Add a helper that will perform link training as described in the DisplayPort specification.
--- drivers/gpu/drm/tegra/dp.c | 456 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/dp.h | 68 ++++++ 2 files changed, 524 insertions(+)
drivers/gpu/drm/tegra/dp.c +456 -0
@@ -334,6 +334,14 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)u8 values[2 ], value;int err; +if (link->ops && link->ops->configure) { + err = link->ops->configure (link); +if (err <0 ) { +DRM_ERROR ("failed to configure DP link: %d\n" , err); +return err; + } + } + values[0 ] =drm_dp_link_rate_to_bw_code (link->rate); values[1 ] = link->lanes;
@@ -416,3 +424,451 @@ int drm_dp_link_choose(struct drm_dp_link *link,return -ERANGE ; } + +/** +* DOC: Link training +* +* These functions contain common logic and helpers to implement DisplayPort +* link training. +*/ + +/** +* drm_dp_link_train_init() - initialize DisplayPort link training state +* @train: DisplayPort link training state +*/ +void drm_dp_link_train_init (struct drm_dp_link_train *train) +{ +struct drm_dp_link_train_set *request = &train->request; +struct drm_dp_link_train_set *adjust = &train->adjust; +unsigned int i; + +for (i =0 ; i <4 ; i++) { + request->voltage_swing[i] =0 ; + adjust->voltage_swing[i] =0 ; + + request->pre_emphasis[i] =0 ; + adjust->pre_emphasis[i] =0 ; + + request->post_cursor[i] =0 ; + adjust->post_cursor[i] =0 ; + } + + train->pattern =DP_TRAINING_PATTERN_DISABLE ; + train->clock_recovered = false; + train->channel_equalized = false; +} + +static bool drm_dp_link_train_valid (const struct drm_dp_link_train *train) +{ +return train->clock_recovered && train->channel_equalized; +} + +static int drm_dp_link_apply_training (struct drm_dp_link *link) +{ +struct drm_dp_link_train_set *request = &link->train.request; +unsigned int lanes = link->lanes, *vs, *pe, *pc, i; +struct drm_dp_aux *aux = link->aux; +u8 values[4 ], pattern =0 ; +int err; + + err = link->ops->apply_training (link); +if (err <0 ) { +DRM_ERROR ("failed to apply link training: %d\n" , err); +return err; + } + + vs = request->voltage_swing; + pe = request->pre_emphasis; + pc = request->post_cursor; + +/* write currently selected voltage-swing and pre-emphasis levels */ +for (i =0 ; i < lanes; i++) + values[i] =DP_TRAIN_VOLTAGE_SWING_LEVEL (vs[i]) | +DP_TRAIN_PRE_EMPHASIS_LEVEL (pe[i]); + + err =drm_dp_dpcd_write (aux,DP_TRAINING_LANE0_SET , values, lanes); +if (err <0 ) { +DRM_ERROR ("failed to set training parameters: %d\n" , err); +return err; + } + +/* write currently selected post-cursor level (if supported) */ +if (link->revision >=0x12 && link->rate ==540000 ) { + values[0 ] = values[1 ] =0 ; + +for (i =0 ; i < lanes; i++) + values[i /2 ] |=DP_LANE_POST_CURSOR (i, pc[i]); + + err =drm_dp_dpcd_write (aux,DP_TRAINING_LANE0_1_SET2 , values, +DIV_ROUND_UP (lanes,2 )); +if (err <0 ) { +DRM_ERROR ("failed to set post-cursor: %d\n" , err); +return err; + } + } + +/* write link pattern */ +if (link->train.pattern !=DP_TRAINING_PATTERN_DISABLE ) + pattern |=DP_LINK_SCRAMBLING_DISABLE ; + + pattern |= link->train.pattern; + + err =drm_dp_dpcd_writeb (aux,DP_TRAINING_PATTERN_SET , pattern); +if (err <0 ) { +DRM_ERROR ("failed to set training pattern: %d\n" , err); +return err; + } + +return 0 ; +} + +static void drm_dp_link_train_wait (struct drm_dp_link *link) +{ +unsigned long min =0 ; + +switch (link->train.pattern) { +case DP_TRAINING_PATTERN_1 : + min = link->aux_rd_interval.cr; +break ; + +case DP_TRAINING_PATTERN_2 : +case DP_TRAINING_PATTERN_3 : + min = link->aux_rd_interval.ce; +break ; + +default : +break ; + } + +if (min >0 ) +usleep_range (min,2 * min); +} + +static void drm_dp_link_get_adjustments (struct drm_dp_link *link, +u8 status[DP_LINK_STATUS_SIZE ]) +{ +struct drm_dp_link_train_set *adjust = &link->train.adjust; +unsigned int i; + +for (i =0 ; i < link->lanes; i++) { + adjust->voltage_swing[i] = +drm_dp_get_adjust_request_voltage (status, i) >> +DP_TRAIN_VOLTAGE_SWING_SHIFT ; + + adjust->pre_emphasis[i] = +drm_dp_get_adjust_request_pre_emphasis (status, i) >> +DP_TRAIN_PRE_EMPHASIS_SHIFT ; + + adjust->post_cursor[i] = +drm_dp_get_adjust_request_post_cursor (status, i); + } +} + +static void drm_dp_link_train_adjust (struct drm_dp_link_train *train) +{ +struct drm_dp_link_train_set *request = &train->request; +struct drm_dp_link_train_set *adjust = &train->adjust; +unsigned int i; + +for (i =0 ; i <4 ; i++) +if (request->voltage_swing[i] != adjust->voltage_swing[i]) + request->voltage_swing[i] = adjust->voltage_swing[i]; + +for (i =0 ; i <4 ; i++) +if (request->pre_emphasis[i] != adjust->pre_emphasis[i]) + request->pre_emphasis[i] = adjust->pre_emphasis[i]; + +for (i =0 ; i <4 ; i++) +if (request->post_cursor[i] != adjust->post_cursor[i]) + request->post_cursor[i] = adjust->post_cursor[i]; +} + +static int drm_dp_link_recover_clock (struct drm_dp_link *link) +{ +u8 status[DP_LINK_STATUS_SIZE ]; +int err; + + err =drm_dp_link_apply_training (link); +if (err <0 ) +return err; + +drm_dp_link_train_wait (link); + + err =drm_dp_dpcd_read_link_status (link->aux, status); +if (err <0 ) { +DRM_ERROR ("failed to read link status: %d\n" , err); +return err; + } + +if (!drm_dp_clock_recovery_ok (status, link->lanes)) +drm_dp_link_get_adjustments (link, status); +else + link->train.clock_recovered = true; + +return 0 ; +} + +static int drm_dp_link_clock_recovery (struct drm_dp_link *link) +{ +unsigned int repeat; +int err; + +/* start clock recovery using training pattern 1 */ + link->train.pattern =DP_TRAINING_PATTERN_1 ; + +for (repeat =1 ; repeat <5 ; repeat++) { + err =drm_dp_link_recover_clock (link); +if (err <0 ) { +DRM_ERROR ("failed to recover clock: %d\n" , err); +return err; + } + +drm_dp_link_train_adjust (&link->train); + +if (link->train.clock_recovered) +break ; + } + +return 0 ; +} + +static int drm_dp_link_equalize_channel (struct drm_dp_link *link) +{ +struct drm_dp_aux *aux = link->aux; +u8 status[DP_LINK_STATUS_SIZE ]; +int err; + + err =drm_dp_link_apply_training (link); +if (err <0 ) +return err; + +drm_dp_link_train_wait (link); + + err =drm_dp_dpcd_read_link_status (aux, status); +if (err <0 ) { +DRM_ERROR ("failed to read link status: %d\n" , err); +return err; + } + +if (!drm_dp_clock_recovery_ok (status, link->lanes)) { +DRM_ERROR ("clock recovery lost while equalizing channel\n" ); + link->train.clock_recovered = false; +return 0 ; + } + +if (!drm_dp_channel_eq_ok (status, link->lanes)) +drm_dp_link_get_adjustments (link, status); +else + link->train.channel_equalized = true; + +return 0 ; +} + +static int drm_dp_link_channel_equalization (struct drm_dp_link *link) +{ +unsigned int repeat; +int err; + +/* start channel equalization using pattern 2 or 3 */ +if (link->caps.tps3_supported) + link->train.pattern =DP_TRAINING_PATTERN_3 ; +else + link->train.pattern =DP_TRAINING_PATTERN_2 ; + +for (repeat =1 ; repeat <5 ; repeat++) { + err =drm_dp_link_equalize_channel (link); +if (err <0 ) { +DRM_ERROR ("failed to equalize channel: %d\n" , err); +return err; + } + +drm_dp_link_train_adjust (&link->train); + +if (link->train.channel_equalized) +break ; + } + +return 0 ; +} + +static int drm_dp_link_downgrade (struct drm_dp_link *link) +{ +switch (link->rate) { +case 162000 : +return -EINVAL ; + +case 270000 : + link->rate =162000 ; +break ; + +case 540000 : + link->rate =270000 ; +return 0 ; + } + +return 0 ; +} + +static void drm_dp_link_train_disable (struct drm_dp_link *link) +{ +int err; + + link->train.pattern =DP_TRAINING_PATTERN_DISABLE ; + + err =drm_dp_link_apply_training (link); +if (err <0 ) +DRM_ERROR ("failed to disable link training: %d\n" , err); +} + +static int drm_dp_link_train_full (struct drm_dp_link *link) +{ +int err; + +retry: +DRM_DEBUG_KMS ("full-training link: %u lane%s at %u MHz\n" , + link->lanes, (link->lanes >1 ) ?"s" :"" , + link->rate /100 ); + + err =drm_dp_link_configure (link->aux, link); +if (err <0 ) { +DRM_ERROR ("failed to configure DP link: %d\n" , err); +return err; + } + + err =drm_dp_link_clock_recovery (link); +if (err <0 ) { +DRM_ERROR ("clock recovery failed: %d\n" , err); + goto out; + } + +if (!link->train.clock_recovered) { +DRM_ERROR ("clock recovery failed, downgrading link\n" ); + + err =drm_dp_link_downgrade (link); +if (err <0 ) + goto out; + + goto retry; + } + +DRM_DEBUG_KMS ("clock recovery succeeded\n" ); + + err =drm_dp_link_channel_equalization (link); +if (err <0 ) { +DRM_ERROR ("channel equalization failed: %d\n" , err); + goto out; + } + +if (!link->train.channel_equalized) { +DRM_ERROR ("channel equalization failed, downgrading link\n" ); + + err =drm_dp_link_downgrade (link); +if (err <0 ) + goto out; + + goto retry; + } + +DRM_DEBUG_KMS ("channel equalization succeeded\n" ); + +out: +drm_dp_link_train_disable (link); +return err; +} + +static int drm_dp_link_train_fast (struct drm_dp_link *link) +{ +u8 status[DP_LINK_STATUS_SIZE ]; +int err; + +DRM_DEBUG_KMS ("fast-training link: %u lane%s at %u MHz\n" , + link->lanes, (link->lanes >1 ) ?"s" :"" , + link->rate /100 ); + + err =drm_dp_link_configure (link->aux, link); +if (err <0 ) { +DRM_ERROR ("failed to configure DP link: %d\n" , err); +return err; + } + +/* transmit training pattern 1 for 500 microseconds */ + link->train.pattern =DP_TRAINING_PATTERN_1 ; + + err =drm_dp_link_apply_training (link); +if (err <0 ) + goto out; + +usleep_range (500 ,1000 ); + +/* transmit training pattern 2 or 3 for 500 microseconds */ +if (link->caps.tps3_supported) + link->train.pattern =DP_TRAINING_PATTERN_3 ; +else + link->train.pattern =DP_TRAINING_PATTERN_2 ; + + err =drm_dp_link_apply_training (link); +if (err <0 ) + goto out; + +usleep_range (500 ,1000 ); + + err =drm_dp_dpcd_read_link_status (link->aux, status); +if (err <0 ) { +DRM_ERROR ("failed to read link status: %d\n" , err); + goto out; + } + +if (!drm_dp_clock_recovery_ok (status, link->lanes)) { +DRM_ERROR ("clock recovery failed\n" ); + err = -EIO ; + } + +if (!drm_dp_channel_eq_ok (status, link->lanes)) { +DRM_ERROR ("channel equalization failed\n" ); + err = -EIO ; + } + +out: +drm_dp_link_train_disable (link); +return err; +} + +/** +* drm_dp_link_train() - perform DisplayPort link training +* @link: a DP link object +* +* Uses the context stored in the DP link object to perform link training. It +* is expected that drivers will call drm_dp_link_probe() to obtain the link +* capabilities before performing link training. +* +* If the sink supports fast link training (no AUX CH handshake) and valid +* training settings are available, this function will try to perform fast +* link training and fall back to full link training on failure. +* +* Returns: 0 on success or a negative error code on failure. +*/ +int drm_dp_link_train (struct drm_dp_link *link) +{ +int err; + +if (link->caps.fast_training) { +if (drm_dp_link_train_valid (&link->train)) { + err =drm_dp_link_train_fast (link); +if (err <0 ) +DRM_ERROR ("fast link training failed: %d\n" , + err); +else +return 0 ; + }else { +DRM_DEBUG_KMS ("training parameters not available\n" ); + } + }else { +DRM_DEBUG_KMS ("fast link training not supported\n" ); + } + + err =drm_dp_link_train_full (link); +if (err <0 ) +DRM_ERROR ("full link training failed: %d\n" , err); + +return err; +}
drivers/gpu/drm/tegra/dp.h +68 -0
90 lines not on this page · raw message
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 17/32 drm/tegra: sor: Use DP link training helpers
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Make use of the DP link training helpers to implement full and fast link training. While at it, refactor some of the code and remove various code sequences that are not necessary.
--- drivers/gpu/drm/tegra/dpaux.c | 69 ---- drivers/gpu/drm/tegra/drm.h | 5 - drivers/gpu/drm/tegra/sor.c | 684 +++++++++++++++++++++++----------- drivers/gpu/drm/tegra/sor.h | 2 + 4 files changed, 470 insertions(+), 290 deletions(-)
drivers/gpu/drm/tegra/dpaux.c +0 -69
@@ -821,72 +821,3 @@ int drm_dp_aux_disable(struct drm_dp_aux *aux)return 0 ; } - -int drm_dp_aux_prepare (struct drm_dp_aux *aux,u8 encoding) -{ -int err; - - err =drm_dp_dpcd_writeb (aux,DP_MAIN_LINK_CHANNEL_CODING_SET , - encoding); -if (err <0 ) -return err; - -return 0 ; -} - -int drm_dp_aux_train (struct drm_dp_aux *aux,struct drm_dp_link *link, -u8 pattern) -{ -u8 tp = pattern &DP_TRAINING_PATTERN_MASK ; -u8 status[DP_LINK_STATUS_SIZE ], values[4 ]; -unsigned int i; -int err; - - err =drm_dp_dpcd_writeb (aux,DP_TRAINING_PATTERN_SET , pattern); -if (err <0 ) -return err; - -if (tp ==DP_TRAINING_PATTERN_DISABLE ) -return 0 ; - -for (i =0 ; i < link->lanes; i++) - values[i] =DP_TRAIN_MAX_PRE_EMPHASIS_REACHED | -DP_TRAIN_PRE_EMPH_LEVEL_0 | -DP_TRAIN_MAX_SWING_REACHED | -DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ; - - err =drm_dp_dpcd_write (aux,DP_TRAINING_LANE0_SET , values, - link->lanes); -if (err <0 ) -return err; - -usleep_range (500 ,1000 ); - - err =drm_dp_dpcd_read_link_status (aux, status); -if (err <0 ) -return err; - -switch (tp) { -case DP_TRAINING_PATTERN_1 : -if (!drm_dp_clock_recovery_ok (status, link->lanes)) -return -EAGAIN ; - -break ; - -case DP_TRAINING_PATTERN_2 : -if (!drm_dp_channel_eq_ok (status, link->lanes)) -return -EAGAIN ; - -break ; - -default : -dev_err (aux->dev,"unsupported training pattern %u\n" , tp); -return -EINVAL ; - } - - err =drm_dp_dpcd_writeb (aux,DP_EDP_CONFIGURATION_SET ,0 ); -if (err <0 ) -return err; - -return 0 ; -}
drivers/gpu/drm/tegra/drm.h +0 -5
@@ -153,17 +153,12 @@ void tegra_output_connector_destroy(struct drm_connector *connector);void tegra_output_encoder_destroy (struct drm_encoder *encoder);/* from dpaux.c */ -struct drm_dp_link ; -struct drm_dp_aux *drm_dp_aux_find_by_of_node (struct device_node *np);enum drm_connector_status drm_dp_aux_detect (struct drm_dp_aux *aux);int drm_dp_aux_attach (struct drm_dp_aux *aux,struct tegra_output *output);int drm_dp_aux_detach (struct drm_dp_aux *aux);int drm_dp_aux_enable (struct drm_dp_aux *aux);int drm_dp_aux_disable (struct drm_dp_aux *aux); -int drm_dp_aux_prepare (struct drm_dp_aux *aux,u8 encoding); -int drm_dp_aux_train (struct drm_dp_aux *aux,struct drm_dp_link *link, -u8 pattern);/* from fb.c */ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
drivers/gpu/drm/tegra/sor.c +468 -216
850 lines not on this page · raw message
drivers/gpu/drm/tegra/sor.h +2 -0
@@ -283,10 +283,12 @@#define SOR_DP_PADCTL_CM_TXD_2 (1 << 6)#define SOR_DP_PADCTL_CM_TXD_1 (1 << 5)#define SOR_DP_PADCTL_CM_TXD_0 (1 << 4) +#define SOR_DP_PADCTL_CM_TXD (x) (1 << (4 + (x)))#define SOR_DP_PADCTL_PD_TXD_3 (1 << 3)#define SOR_DP_PADCTL_PD_TXD_0 (1 << 2)#define SOR_DP_PADCTL_PD_TXD_1 (1 << 1)#define SOR_DP_PADCTL_PD_TXD_2 (1 << 0) +#define SOR_DP_PADCTL_PD_TXD (x) (1 << (0 + (x)))#define SOR_DP_PADCTL1 0x5d
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 18/32 drm/tegra: sor: Hook up I2C-over-AUX to output
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
This is necessary for the output abstraction to retrieve a list of valid modes from the EDID of a connected panel/monitor. This will be useful in conjunction with DisplayPort support that will be added in a subsequent patch, so that the driver can read EDID via the AUX channel.
--- drivers/gpu/drm/tegra/sor.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/tegra/sor.c +2 -0
@@ -3524,6 +3524,8 @@ static int tegra_sor_probe(struct platform_device *pdev)if (!sor->aux)return -EPROBE_DEFER ; + + sor->output.ddc = &sor->aux->ddc; }if (!sor->aux) {
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 19/32 drm/tegra: sor: Stabilize eDP
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Rework eDP code to correspond more closely to what's documented. This also improves the reliability of modesets.
--- drivers/gpu/drm/tegra/sor.c | 138 +++++++++++++----------------------- 1 file changed, 49 insertions(+), 89 deletions(-)
drivers/gpu/drm/tegra/sor.c +49 -89
@@ -1878,119 +1878,80 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)pm_runtime_get_sync (sor->dev); -if (output->panel) -drm_panel_prepare (output->panel); +/* switch to safe parent clock */ + err =tegra_sor_set_parent_clock (sor, sor->clk_safe); +if (err <0 ) +dev_err (sor->dev,"failed to set safe parent clock: %d\n" , err); + + err =tegra_io_rail_power_on (TEGRA_IO_RAIL_LVDS ); +if (err <0 ) +dev_err (sor->dev,"failed to power on LVDS rail: %d\n" , err); + +usleep_range (20 ,100 ); err =drm_dp_aux_enable (sor->aux);if (err <0 ) -dev_err (sor->dev,"failed to enable DP: %d\n" , err); +dev_err (sor->dev,"failed to enable DPAUX: %d\n" , err); err =drm_dp_link_probe (sor->aux, &sor->link); -if (err <0 ) { +if (err <0 )dev_err (sor->dev,"failed to probe eDP link: %d\n" , err); -return ; - } -/* switch to safe parent clock */ - err =tegra_sor_set_parent_clock (sor, sor->clk_safe); + err =drm_dp_link_choose (&sor->link, mode, info);if (err <0 ) -dev_err (sor->dev,"failed to set safe parent clock: %d\n" , err); +dev_err (sor->dev,"failed to choose link: %d\n" , err); - value =tegra_sor_readl (sor,SOR_CLK_CNTRL ); - value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK ; - value |=SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK ; -tegra_sor_writel (sor, value,SOR_CLK_CNTRL ); +if (output->panel) +drm_panel_prepare (output->panel); value =tegra_sor_readl (sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN ;tegra_sor_writel (sor, value, sor->soc->regs->pll2); -usleep_range (20 ,100 ); + +usleep_range (20 ,40 ); value =tegra_sor_readl (sor, sor->soc->regs->pll3); value |=SOR_PLL3_PLL_VDD_MODE_3V3 ;tegra_sor_writel (sor, value, sor->soc->regs->pll3); - value =SOR_PLL0_ICHPMP (0xf ) |SOR_PLL0_VCOCAP_RST | -SOR_PLL0_PLLREG_LEVEL_V45 |SOR_PLL0_RESISTOR_EXT ; + value =tegra_sor_readl (sor, sor->soc->regs->pll0); + value &= ~(SOR_PLL0_VCOPD |SOR_PLL0_PWR );tegra_sor_writel (sor, value, sor->soc->regs->pll0); value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value |=SOR_PLL2_SEQ_PLLCAPPD ; value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE ; - value |=SOR_PLL2_LVDS_ENABLE ; + value |=SOR_PLL2_SEQ_PLLCAPPD ;tegra_sor_writel (sor, value, sor->soc->regs->pll2); - value =SOR_PLL1_TERM_COMPOUT |SOR_PLL1_TMDS_TERM ; -tegra_sor_writel (sor, value, sor->soc->regs->pll1); - -while (true) { - value =tegra_sor_readl (sor, sor->soc->regs->pll2); -if ((value &SOR_PLL2_SEQ_PLLCAPPD_ENFORCE ) ==0 ) -break ; - -usleep_range (250 ,1000 ); - } +usleep_range (200 ,400 ); value =tegra_sor_readl (sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_POWERDOWN_OVERRIDE ; value &= ~SOR_PLL2_PORT_POWERDOWN ;tegra_sor_writel (sor, value, sor->soc->regs->pll2); -/* -* power up -*/ - -/* set safe link bandwidth (1.62 Gbps) */ value =tegra_sor_readl (sor,SOR_CLK_CNTRL ); - value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK ; - value |=SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 ; + value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK ; + value |=SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK ;tegra_sor_writel (sor, value,SOR_CLK_CNTRL ); -/* step 1 */ - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value |=SOR_PLL2_SEQ_PLLCAPPD_ENFORCE |SOR_PLL2_PORT_POWERDOWN | -SOR_PLL2_BANDGAP_POWERDOWN ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); - - value =tegra_sor_readl (sor, sor->soc->regs->pll0); - value |=SOR_PLL0_VCOPD |SOR_PLL0_PWR ; -tegra_sor_writel (sor, value, sor->soc->regs->pll0); - - value =tegra_sor_readl (sor, sor->soc->regs->dp_padctl0); - value &= ~SOR_DP_PADCTL_PAD_CAL_PD ; -tegra_sor_writel (sor, value, sor->soc->regs->dp_padctl0); - -/* step 2 */ - err =tegra_io_pad_power_enable (sor->pad); -if (err <0 ) -dev_err (sor->dev,"failed to power on I/O pad: %d\n" , err); - -usleep_range (5 ,100 ); - -/* step 3 */ - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_BANDGAP_POWERDOWN ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); + value =tegra_sor_readl (sor,SOR_DP_SPARE0 ); +/* XXX not in TRM */ + value |=SOR_DP_SPARE_PANEL_INTERNAL ; + value |=SOR_DP_SPARE_SEQ_ENABLE ; +tegra_sor_writel (sor, value,SOR_DP_SPARE0 ); -usleep_range (20 ,100 ); +/* XXX not in TRM */ +tegra_sor_writel (sor,0 ,SOR_LVDS ); -/* step 4 */ value =tegra_sor_readl (sor, sor->soc->regs->pll0); - value &= ~SOR_PLL0_VCOPD ; - value &= ~SOR_PLL0_PWR ; + value &= ~SOR_PLL0_ICHPMP_MASK ; + value &= ~SOR_PLL0_VCOCAP_MASK ; + value |=SOR_PLL0_ICHPMP (0x1 ); + value |=SOR_PLL0_VCOCAP (0x3 ); + value |=SOR_PLL0_RESISTOR_EXT ;tegra_sor_writel (sor, value, sor->soc->regs->pll0); - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); - -usleep_range (200 ,1000 ); - -/* step 5 */ - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_PORT_POWERDOWN ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); -/* XXX not in TRM */ for (value =0 , i =0 ; i <5 ; i++) value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
@@ -2015,7 +1976,6 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value |=SOR_DP_LINKCTL_ENABLE ;tegra_sor_writel (sor, value,SOR_DP_LINKCTL0 ); -/* calibrate termination resistance (XXX do this only on HPD) */ tegra_sor_dp_term_calibrate (sor); err =drm_dp_link_train (&sor->link);
@@ -2025,21 +1985,16 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)dev_dbg (sor->dev,"link training succeeded\n" ); err =drm_dp_link_power_up (sor->aux, &sor->link); -if (err <0 ) { -dev_err (sor->dev,"failed to power up eDP link: %d\n" , - err); -return ; - } +if (err <0 ) +dev_err (sor->dev,"failed to power up eDP link: %d\n" , err);/* compute configuration */ memset (&config,0 ,sizeof (config)); config.bits_per_pixel = state->bpc *3 ; err =tegra_sor_compute_config (sor, mode, &config, &sor->link); -if (err <0 ) { +if (err <0 )dev_err (sor->dev,"failed to compute configuration: %d\n" , err); -return ; - }tegra_sor_apply_config (sor, &config);
@@ -2067,19 +2022,24 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)tegra_sor_update (sor); + err =tegra_sor_power_up (sor,250 ); +if (err <0 ) +dev_err (sor->dev,"failed to power up SOR: %d\n" , err); + +/* attach and wake up */ + err =tegra_sor_attach (sor); +if (err <0 ) +dev_err (sor->dev,"failed to attach SOR: %d\n" , err); + value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS ); value |=SOR_ENABLE (0 );tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS );tegra_dc_commit (dc); - err =tegra_sor_attach (sor); -if (err <0 ) -dev_err (sor->dev,"failed to attach SOR: %d\n" , err); - err =tegra_sor_wakeup (sor);if (err <0 ) -dev_err (sor->dev,"failed to enable DC: %d\n" , err); +dev_err (sor->dev,"failed to wakeup SOR: %d\n" , err);if (output->panel)drm_panel_enable (output->panel);
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 20/32 drm/tegra: sor: Filter eDP rates
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The SOR found on Tegra SoCs does not support all the rates potentially advertised by eDP 1.4. Make sure that the rates that are not supported are filtered out.
--- drivers/gpu/drm/tegra/sor.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
drivers/gpu/drm/tegra/sor.c +26 -0
@@ -605,6 +605,30 @@ static struct clk *tegra_clk_sor_pad_register(struct tegra_sor *sor,return clk; } +static void tegra_sor_filter_rates (struct tegra_sor *sor) +{ +struct drm_dp_link *link = &sor->link; +unsigned int i; + +/* Tegra only supports RBR, HBR and HBR2 */ +for (i =0 ; i < link->num_rates; i++) { +switch (link->rates[i]) { +case 1620000 : +case 2700000 : +case 5400000 : +break ; + +default : +DRM_DEBUG_KMS ("link rate %lu kHz not supported\n" , + link->rates[i]); + link->rates[i] =0 ; +break ; + } + } + +drm_dp_link_update_rates (link); +} +static int tegra_sor_power_up_lanes (struct tegra_sor *sor,unsigned int lanes) {unsigned long timeout;
@@ -1897,6 +1921,8 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)if (err <0 )dev_err (sor->dev,"failed to probe eDP link: %d\n" , err); +tegra_sor_filter_rates (sor); + err =drm_dp_link_choose (&sor->link, mode, info);if (err <0 )dev_err (sor->dev,"failed to choose link: %d\n" , err);
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 21/32 drm/tegra: sor: Add DisplayPort support
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Add support for regular DisplayPort on Tegra210 and Tegra186.
--- drivers/gpu/drm/tegra/dp.c | 10 +- drivers/gpu/drm/tegra/sor.c | 343 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/tegra/sor.h | 1 + 3 files changed, 348 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/dp.c +6 -4
@@ -623,10 +623,10 @@ static int drm_dp_link_clock_recovery(struct drm_dp_link *link)return err; } -drm_dp_link_train_adjust (&link->train); -if (link->train.clock_recovered)break ; + +drm_dp_link_train_adjust (&link->train); }return 0 ;
@@ -682,10 +682,10 @@ static int drm_dp_link_channel_equalization(struct drm_dp_link *link)return err; } -drm_dp_link_train_adjust (&link->train); -if (link->train.channel_equalized)break ; + +drm_dp_link_train_adjust (&link->train); }return 0 ;
@@ -851,6 +851,8 @@ int drm_dp_link_train(struct drm_dp_link *link) {int err; +drm_dp_link_train_init (&link->train); +if (link->caps.fast_training) {if (drm_dp_link_train_valid (&link->train)) { err =drm_dp_link_train_fast (link);
drivers/gpu/drm/tegra/sor.c +341 -2
403 lines not on this page · raw message
drivers/gpu/drm/tegra/sor.h +1 -0
@@ -39,6 +39,7 @@#define SOR_STATE_ASY_CRC_MODE_NON_ACTIVE (0x2 << 6)#define SOR_STATE_ASY_CRC_MODE_COMPLETE (0x1 << 6)#define SOR_STATE_ASY_CRC_MODE_ACTIVE (0x0 << 6) +#define SOR_STATE_ASY_SUBOWNER_MASK (0x3 << 4)#define SOR_STATE_ASY_OWNER_MASK 0xf#define SOR_STATE_ASY_OWNER (x) (((x) & 0xf) << 0)
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 22/32 drm/tegra: sor: Remove tegra186-sor1 support
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
It turns out that SOR1 is just another instance of the same block as the SOR0, so there is no need to distinguish them.
--- drivers/gpu/drm/tegra/sor.c | 18 ------------------ 1 file changed, 18 deletions(-)
drivers/gpu/drm/tegra/sor.c +0 -18
@@ -3680,23 +3680,6 @@ static const u8 tegra186_sor_pre_emphasis[4][4][4] = { };static const struct tegra_sor_soc tegra186_sor = { - .supports_edp = false, - .supports_lvds = false, - .supports_hdmi = false, - .supports_dp = true, - - .regs = &tegra186_sor_regs, - .has_nvdisplay = true, - - .xbar_cfg = tegra124_sor_xbar_cfg, - .lane_map = tegra124_sor_lane_map, - .voltage_swing = tegra186_sor_voltage_swing, - .pre_emphasis = tegra186_sor_pre_emphasis, - .post_cursor = tegra124_sor_post_cursor, - .tx_pu = tegra124_sor_tx_pu, -}; - -static const struct tegra_sor_soc tegra186_sor1 = { .supports_edp = false, .supports_lvds = false, .supports_hdmi = true,
@@ -3747,7 +3730,6 @@ static const struct tegra_sor_soc tegra194_sor = {static const struct of_device_id tegra_sor_of_match[] = { { .compatible ="nvidia,tegra194-sor" , .data = &tegra194_sor }, - { .compatible ="nvidia,tegra186-sor1" , .data = &tegra186_sor1 }, { .compatible ="nvidia,tegra186-sor" , .data = &tegra186_sor }, { .compatible ="nvidia,tegra210-sor1" , .data = &tegra210_sor1 }, { .compatible ="nvidia,tegra210-sor" , .data = &tegra210_sor },
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 23/32 drm/tegra: sor: Use correct SOR index on Tegra210
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The device tree bindings for the Tegra210 SOR don't require the controller instance to be defined, since the instance can be derived from the compatible string. The index is never used on Tegra210, so we got away with it not getting set. However, subsequent patches will change that, so make sure the proper index is used.
--- drivers/gpu/drm/tegra/sor.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/tegra/sor.c +5 -0
@@ -3759,6 +3759,11 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)* earlier */ sor->pad =TEGRA_IO_PAD_HDMI_DP0 + sor->index; + }else { +if (sor->soc->supports_edp) + sor->index =0 ; +else + sor->index =1 ; } err =of_property_read_u32_array (np,"nvidia,xbar-cfg" , xbar_cfg,5 );
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 24/32 drm/tegra: sor: Implement pad clock for all SOR instances
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
So far the pad clock was only needed on the second SOR instance. The clock does exist for all SOR instances, though, so make sure it is always implemented. This prepares for further unification of the code in subsequent patches.
--- drivers/gpu/drm/tegra/sor.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/sor.c +14 -6
@@ -522,8 +522,9 @@ static inline struct tegra_clk_sor_pad *to_pad(struct clk_hw *hw) return container_of(hw, struct tegra_clk_sor_pad, hw); } -static const char *const tegra_clk_sor_pad_parents[] = { -"pll_d2_out0" ,"pll_dp" +static const char *const tegra_clk_sor_pad_parents[2 ][2 ] = { + {"pll_d_out0" ,"pll_dp" }, + {"pll_d2_out0" ,"pll_dp" }, };static int tegra_clk_sor_pad_set_parent (struct clk_hw *hw,u8 index)
@@ -594,8 +595,8 @@ static struct clk *tegra_clk_sor_pad_register(struct tegra_sor *sor, init.name = name; init.flags =0 ; - init.parent_names = tegra_clk_sor_pad_parents; - init.num_parents =ARRAY_SIZE (tegra_clk_sor_pad_parents); + init.parent_names = tegra_clk_sor_pad_parents[sor->index]; + init.num_parents =ARRAY_SIZE (tegra_clk_sor_pad_parents[sor->index]); init.ops = &tegra_clk_sor_pad_ops; pad->hw.init = &init;
@@ -4016,6 +4017,8 @@ static int tegra_sor_probe(struct platform_device *pdev)* pad output clock. */ if (!sor->clk_pad) { +char *name; + err =pm_runtime_get_sync (&pdev->dev);if (err <0 ) { dev_err(&pdev->dev, "failed to get runtime PM: %d\n",
@@ -4023,8 +4026,13 @@ static int tegra_sor_probe(struct platform_device *pdev) goto remove; } - sor->clk_pad =tegra_clk_sor_pad_register (sor, -"sor1_pad_clkout" ); + name =devm_kasprintf (&pdev->dev,GFP_KERNEL ,"sor%u_pad_clkout" , sor->index); +if (!name) { + err = -ENOMEM ; + goto remove; + } + + sor->clk_pad =tegra_clk_sor_pad_register (sor, name);pm_runtime_put (&pdev->dev); }
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 25/32 drm/tegra: sor: Deduplicate connector type detection code
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The connector type detection code is duplicated in two places. Keeping both places in sync is an extra maintenance burden that can be avoided by comparing the connector type operations that are set upon the first detection.
--- drivers/gpu/drm/tegra/sor.c | 218 ++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 109 deletions(-)
drivers/gpu/drm/tegra/sor.c +109 -109
@@ -3135,6 +3135,112 @@ static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = { .atomic_check = tegra_sor_encoder_atomic_check, }; +static const struct tegra_sor_ops tegra_sor_edp_ops = { + .name ="eDP" , +}; + +static int tegra_sor_hdmi_probe (struct tegra_sor *sor) +{ +int err; + + sor->avdd_io_supply =devm_regulator_get (sor->dev,"avdd-io" ); +if (IS_ERR (sor->avdd_io_supply)) { +dev_err (sor->dev,"cannot get AVDD I/O supply: %ld\n" , +PTR_ERR (sor->avdd_io_supply)); +return PTR_ERR (sor->avdd_io_supply); + } + + err =regulator_enable (sor->avdd_io_supply); +if (err <0 ) { +dev_err (sor->dev,"failed to enable AVDD I/O supply: %d\n" , + err); +return err; + } + + sor->vdd_pll_supply =devm_regulator_get (sor->dev,"vdd-pll" ); +if (IS_ERR (sor->vdd_pll_supply)) { +dev_err (sor->dev,"cannot get VDD PLL supply: %ld\n" , +PTR_ERR (sor->vdd_pll_supply)); +return PTR_ERR (sor->vdd_pll_supply); + } + + err =regulator_enable (sor->vdd_pll_supply); +if (err <0 ) { +dev_err (sor->dev,"failed to enable VDD PLL supply: %d\n" , + err); +return err; + } + + sor->hdmi_supply =devm_regulator_get (sor->dev,"hdmi" ); +if (IS_ERR (sor->hdmi_supply)) { +dev_err (sor->dev,"cannot get HDMI supply: %ld\n" , +PTR_ERR (sor->hdmi_supply)); +return PTR_ERR (sor->hdmi_supply); + } + + err =regulator_enable (sor->hdmi_supply); +if (err <0 ) { +dev_err (sor->dev,"failed to enable HDMI supply: %d\n" , err); +return err; + } + +INIT_DELAYED_WORK (&sor->scdc, tegra_sor_hdmi_scdc_work); + +return 0 ; +} + +static int tegra_sor_hdmi_remove (struct tegra_sor *sor) +{ +regulator_disable (sor->hdmi_supply); +regulator_disable (sor->vdd_pll_supply); +regulator_disable (sor->avdd_io_supply); + +return 0 ; +} + +static const struct tegra_sor_ops tegra_sor_hdmi_ops = { + .name ="HDMI" , + .probe = tegra_sor_hdmi_probe, + .remove = tegra_sor_hdmi_remove, +}; + +static int tegra_sor_dp_probe (struct tegra_sor *sor) +{ +int err; + + sor->avdd_io_supply =devm_regulator_get (sor->dev,"avdd-io-hdmi-dp" ); +if (IS_ERR (sor->avdd_io_supply)) +return PTR_ERR (sor->avdd_io_supply); + + err =regulator_enable (sor->avdd_io_supply); +if (err <0 ) +return err; + + sor->vdd_pll_supply =devm_regulator_get (sor->dev,"vdd-hdmi-dp-pll" ); +if (IS_ERR (sor->vdd_pll_supply)) +return PTR_ERR (sor->vdd_pll_supply); + + err =regulator_enable (sor->vdd_pll_supply); +if (err <0 ) +return err; + +return 0 ; +} + +static int tegra_sor_dp_remove (struct tegra_sor *sor) +{ +regulator_disable (sor->vdd_pll_supply); +regulator_disable (sor->avdd_io_supply); + +return 0 ; +} + +static const struct tegra_sor_ops tegra_sor_dp_ops = { + .name ="DP" , + .probe = tegra_sor_dp_probe, + .remove = tegra_sor_dp_remove, +}; +static int tegra_sor_init (struct host1x_client *client) {struct drm_device *drm =dev_get_drvdata (client->parent);
@@ -3145,7 +3251,7 @@ static int tegra_sor_init(struct host1x_client *client)int err;if (!sor->aux) { -if (sor->soc->supports_hdmi) { +if (sor->ops == &tegra_sor_hdmi_ops) { connector =DRM_MODE_CONNECTOR_HDMIA ; encoder =DRM_MODE_ENCODER_TMDS ; helpers = &tegra_sor_hdmi_helpers;
@@ -3154,11 +3260,11 @@ static int tegra_sor_init(struct host1x_client *client) encoder =DRM_MODE_ENCODER_LVDS ; } }else { -if (sor->soc->supports_edp) { +if (sor->ops == &tegra_sor_edp_ops) { connector = DRM_MODE_CONNECTOR_eDP; encoder =DRM_MODE_ENCODER_TMDS ; helpers = &tegra_sor_edp_helpers; - }else if (sor->soc->supports_dp) { + }else { connector = DRM_MODE_CONNECTOR_DisplayPort; encoder =DRM_MODE_ENCODER_TMDS ; helpers = &tegra_sor_dp_helpers;
@@ -3278,112 +3384,6 @@ static const struct host1x_client_ops sor_client_ops = { .exit = tegra_sor_exit, }; -static const struct tegra_sor_ops tegra_sor_edp_ops = { - .name ="eDP" , -}; - -static int tegra_sor_hdmi_probe (struct tegra_sor *sor) -{ -int err; - - sor->avdd_io_supply =devm_regulator_get (sor->dev,"avdd-io" ); -if (IS_ERR (sor->avdd_io_supply)) { -dev_err (sor->dev,"cannot get AVDD I/O supply: %ld\n" , -PTR_ERR (sor->avdd_io_supply)); -return PTR_ERR (sor->avdd_io_supply); - } - - err =regulator_enable (sor->avdd_io_supply); -if (err <0 ) { -dev_err (sor->dev,"failed to enable AVDD I/O supply: %d\n" , - err); -return err; - } - - sor->vdd_pll_supply =devm_regulator_get (sor->dev,"vdd-pll" ); -if (IS_ERR (sor->vdd_pll_supply)) { -dev_err (sor->dev,"cannot get VDD PLL supply: %ld\n" , -PTR_ERR (sor->vdd_pll_supply)); -return PTR_ERR (sor->vdd_pll_supply); - } - - err =regulator_enable (sor->vdd_pll_supply); -if (err <0 ) { -dev_err (sor->dev,"failed to enable VDD PLL supply: %d\n" , - err); -return err; - } - - sor->hdmi_supply =devm_regulator_get (sor->dev,"hdmi" ); -if (IS_ERR (sor->hdmi_supply)) { -dev_err (sor->dev,"cannot get HDMI supply: %ld\n" , -PTR_ERR (sor->hdmi_supply)); -return PTR_ERR (sor->hdmi_supply); - } - - err =regulator_enable (sor->hdmi_supply); -if (err <0 ) { -dev_err (sor->dev,"failed to enable HDMI supply: %d\n" , err); -return err; - } - -INIT_DELAYED_WORK (&sor->scdc, tegra_sor_hdmi_scdc_work); - -return 0 ; -} - -static int tegra_sor_hdmi_remove (struct tegra_sor *sor) -{ -regulator_disable (sor->hdmi_supply); -regulator_disable (sor->vdd_pll_supply); -regulator_disable (sor->avdd_io_supply); - -return 0 ; -} - -static const struct tegra_sor_ops tegra_sor_hdmi_ops = { - .name ="HDMI" , - .probe = tegra_sor_hdmi_probe, - .remove = tegra_sor_hdmi_remove, -}; - -static int tegra_sor_dp_probe (struct tegra_sor *sor) -{ -int err; - - sor->avdd_io_supply =devm_regulator_get (sor->dev,"avdd-io-hdmi-dp" ); -if (IS_ERR (sor->avdd_io_supply)) -return PTR_ERR (sor->avdd_io_supply); - - err =regulator_enable (sor->avdd_io_supply); -if (err <0 ) -return err; - - sor->vdd_pll_supply =devm_regulator_get (sor->dev,"vdd-hdmi-dp-pll" ); -if (IS_ERR (sor->vdd_pll_supply)) -return PTR_ERR (sor->vdd_pll_supply); - - err =regulator_enable (sor->vdd_pll_supply); -if (err <0 ) -return err; - -return 0 ; -} - -static int tegra_sor_dp_remove (struct tegra_sor *sor) -{ -regulator_disable (sor->vdd_pll_supply); -regulator_disable (sor->avdd_io_supply); - -return 0 ; -} - -static const struct tegra_sor_ops tegra_sor_dp_ops = { - .name ="DP" , - .probe = tegra_sor_dp_probe, - .remove = tegra_sor_dp_remove, -}; -static const u8 tegra124_sor_xbar_cfg[5 ] = {0 ,1 ,2 ,3 ,4 };
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 26/32 drm/tegra: sor: Support DisplayPort on Tegra194
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
Reuse parameters from earlier generations to support DisplayPort on Tegra194.
--- drivers/gpu/drm/tegra/sor.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/tegra/sor.c +5 -0
@@ -3727,6 +3727,11 @@ static const struct tegra_sor_soc tegra194_sor = { .settings = tegra194_sor_hdmi_defaults, .xbar_cfg = tegra210_sor_xbar_cfg, + .lane_map = tegra124_sor_lane_map, + .voltage_swing = tegra186_sor_voltage_swing, + .pre_emphasis = tegra186_sor_pre_emphasis, + .post_cursor = tegra124_sor_post_cursor, + .tx_pu = tegra124_sor_tx_pu, };static const struct of_device_id tegra_sor_of_match[] = {
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 27/32 drm/tegra: sor: Unify clock setup for eDP, HDMI and DP
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
With the clocks modelled consistently across SoC generations, the clock setup for eDP, HDMI and DP can now be unified.
--- drivers/gpu/drm/tegra/sor.c | 92 ++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 11 deletions(-)
drivers/gpu/drm/tegra/sor.c +81 -11
@@ -527,6 +527,14 @@ static const char * const tegra_clk_sor_pad_parents[2][2] = { { "pll_d2_out0", "pll_dp" }, }; +/* +* Implementing ->set_parent() here isn't really required because the parent +* will be explicitly selected in the driver code via the DP_CLK_SEL mux in +* the SOR_CLK_CNTRL register. This is primarily for compatibility with the +* Tegra186 and later SoC generations where the BPMP implements this clock +* and doesn't expose the mux via the common clock framework. +*/ +static int tegra_clk_sor_pad_set_parent (struct clk_hw *hw,u8 index) {struct tegra_clk_sor_pad *pad =to_pad (hw);
@@ -1987,10 +1995,36 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)tegra_sor_writel (sor,0x00000000 ,SOR_XBAR_POL );tegra_sor_writel (sor, value,SOR_XBAR_CTRL ); -/* switch to DP parent clock */ - err =tegra_sor_set_parent_clock (sor, sor->clk_dp); -if (err <0 ) -dev_err (sor->dev,"failed to set parent clock: %d\n" , err); +/* +* Switch the pad clock to the DP clock. Note that we cannot actually +* do this because Tegra186 and later don't support clk_set_parent() +* on the sorX_pad_clkout clocks. We already do the equivalent above +* using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register. +*/ +#if 0 + err =clk_set_parent (sor->clk_pad, sor->clk_dp); +if (err <0 ) { +dev_err (sor->dev,"failed to select pad parent clock: %d\n" , + err); +return ; + } +#endif + +/* switch the SOR clock to the pad clock */ + err =tegra_sor_set_parent_clock (sor, sor->clk_pad); +if (err <0 ) { +dev_err (sor->dev,"failed to select SOR parent clock: %d\n" , + err); +return ; + } + +/* switch the output clock to the parent pixel clock */ + err =clk_set_parent (sor->clk, sor->clk_parent); +if (err <0 ) { +dev_err (sor->dev,"failed to select output parent clock: %d\n" , + err); +return ; + }/* use DP-A protocol */ value =tegra_sor_readl (sor,SOR_STATE1 );
@@ -2661,16 +2695,34 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)tegra_sor_writel (sor,0x00000000 ,SOR_XBAR_POL );tegra_sor_writel (sor, value,SOR_XBAR_CTRL ); -/* switch to parent clock */ - err =clk_set_parent (sor->clk, sor->clk_parent); +/* +* Switch the pad clock to the DP clock. Note that we cannot actually +* do this because Tegra186 and later don't support clk_set_parent() +* on the sorX_pad_clkout clocks. We already do the equivalent above +* using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register. +*/ +#if 0 + err =clk_set_parent (sor->clk_pad, sor->clk_dp);if (err <0 ) { -dev_err (sor->dev,"failed to set parent clock: %d\n" , err); +dev_err (sor->dev,"failed to select pad parent clock: %d\n" , + err);return ; } +#endif +/* switch the SOR clock to the pad clock */ err =tegra_sor_set_parent_clock (sor, sor->clk_pad);if (err <0 ) { -dev_err (sor->dev,"failed to set pad clock: %d\n" , err); +dev_err (sor->dev,"failed to select SOR parent clock: %d\n" , + err); +return ; + } + +/* switch the output clock to the parent pixel clock */ + err =clk_set_parent (sor->clk, sor->clk_parent); +if (err <0 ) { +dev_err (sor->dev,"failed to select output parent clock: %d\n" , + err);return ; }
@@ -3061,16 +3113,34 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)tegra_sor_writel (sor,0x00000000 ,SOR_XBAR_POL );tegra_sor_writel (sor, value,SOR_XBAR_CTRL ); -/* switch to DP parent clock */ +/* +* Switch the pad clock to the DP clock. Note that we cannot actually +* do this because Tegra186 and later don't support clk_set_parent() +* on the sorX_pad_clkout clocks. We already do the equivalent above +* using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register. +*/ +#if 0 + err =clk_set_parent (sor->clk_pad, sor->clk_parent); +if (err <0 ) { +dev_err (sor->dev,"failed to select pad parent clock: %d\n" , + err); +return ; + } +#endif + +/* switch the SOR clock to the pad clock */ err =tegra_sor_set_parent_clock (sor, sor->clk_pad);if (err <0 ) { -dev_err (sor->dev,"failed to switch to pad clock: %d\n" , err); +dev_err (sor->dev,"failed to select SOR parent clock: %d\n" , + err);return ; } +/* switch the output clock to the parent pixel clock */ err =clk_set_parent (sor->clk, sor->clk_parent);if (err <0 ) { -dev_err (sor->dev,"failed to switch to parent clock: %d\n" , err); +dev_err (sor->dev,"failed to select output parent clock: %d\n" , + err);return ; }
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 28/32 drm/tegra: sor: Use correct I/O pad for DP
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The correct I/O pad needs to be powered up before DP can be used. Make sure the correct default is set for Tegra generations where the I/O pad cannot be derived from the SOR instance.
--- drivers/gpu/drm/tegra/sor.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/tegra/sor.c +1 -0
@@ -3934,6 +3934,7 @@ static int tegra_sor_probe(struct platform_device *pdev) sor->pad =TEGRA_IO_PAD_LVDS ; }else if (sor->soc->supports_dp) { sor->ops = &tegra_sor_dp_ops; + sor->pad =TEGRA_IO_PAD_LVDS ; }else {dev_err (&pdev->dev,"unknown (DP) support\n" );return -ENODEV ;
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 29/32 drm/tegra: sor: Unify eDP and DP support
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The SOR0 on Tegra210 does, contrary to what was previously assumed, in fact support DisplayPort. The difference between SOR0 and SOR1 is that the latter supports audio and HDCP over DP, whereas the former doesn't.
The code for eDP and DP is now almost identical and the differences can easily be parameterized based on the presence of a panel. There is no need any longer to duplicate the code.
--- drivers/gpu/drm/tegra/sor.c | 421 +++++++----------------------------- 1 file changed, 74 insertions(+), 347 deletions(-)
drivers/gpu/drm/tegra/sor.c +74 -347
@@ -371,10 +371,11 @@ struct tegra_sor_regs { };struct tegra_sor_soc { -bool supports_edp;bool supports_lvds;bool supports_hdmi;bool supports_dp; +bool supports_audio; +bool supports_hdcp;const struct tegra_sor_regs *regs;bool has_nvdisplay;
@@ -1806,306 +1807,6 @@ static const struct drm_encoder_funcs tegra_sor_encoder_funcs = { .destroy = tegra_output_encoder_destroy, }; -static void tegra_sor_edp_disable (struct drm_encoder *encoder) -{ -struct tegra_output *output =encoder_to_output (encoder); -struct tegra_dc *dc =to_tegra_dc (encoder->crtc); -struct tegra_sor *sor =to_sor (output); -u32 value; -int err; - -if (output->panel) -drm_panel_disable (output->panel); - - err =tegra_sor_detach (sor); -if (err <0 ) -dev_err (sor->dev,"failed to detach SOR: %d\n" , err); - -tegra_sor_writel (sor,0 ,SOR_STATE1 ); -tegra_sor_update (sor); - - value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS ); - value &= ~SOR_ENABLE (0 ); -tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS ); - -tegra_dc_commit (dc); - - err =tegra_sor_power_down (sor); -if (err <0 ) -dev_err (sor->dev,"failed to power down SOR: %d\n" , err); - -if (sor->aux) { - err =drm_dp_aux_disable (sor->aux); -if (err <0 ) -dev_err (sor->dev,"failed to disable DP: %d\n" , err); - } - - err =tegra_io_pad_power_disable (sor->pad); -if (err <0 ) -dev_err (sor->dev,"failed to power off I/O pad: %d\n" , err); - -if (output->panel) -drm_panel_unprepare (output->panel); - -pm_runtime_put (sor->dev); -} - -#if 0 -static int calc_h_ref_to_sync (const struct drm_display_mode *mode, -unsigned int *value) -{ -unsigned int hfp, hsw, hbp, a =0 , b; - - hfp = mode->hsync_start - mode->hdisplay; - hsw = mode->hsync_end - mode->hsync_start; - hbp = mode->htotal - mode->hsync_end; - -pr_info ("hfp: %u, hsw: %u, hbp: %u\n" , hfp, hsw, hbp); - - b = hfp -1 ; - -pr_info ("a: %u, b: %u\n" , a, b); -pr_info ("a + hsw + hbp = %u\n" , a + hsw + hbp); - -if (a + hsw + hbp <=11 ) { - a =1 +11 - hsw - hbp; -pr_info ("a: %u\n" , a); - } - -if (a > b) -return -EINVAL ; - -if (hsw <1 ) -return -EINVAL ; - -if (mode->hdisplay <16 ) -return -EINVAL ; - -if (value) { -if (b > a && a %2 ) - *value = a +1 ; -else - *value = a; - } - -return 0 ; -} -#endif - -static void tegra_sor_edp_enable (struct drm_encoder *encoder) -{ -struct tegra_output *output =encoder_to_output (encoder); -struct tegra_dc *dc =to_tegra_dc (encoder->crtc); -struct tegra_sor *sor =to_sor (output); -struct tegra_sor_config config; -struct tegra_sor_state *state; -struct drm_display_mode *mode; -struct drm_display_info *info; -unsigned int i; -u32 value; -int err; - - state =to_sor_state (output->connector.state); - mode = &encoder->crtc->state->adjusted_mode; - info = &output->connector.display_info; - -pm_runtime_get_sync (sor->dev); - -/* switch to safe parent clock */ - err =tegra_sor_set_parent_clock (sor, sor->clk_safe); -if (err <0 ) -dev_err (sor->dev,"failed to set safe parent clock: %d\n" , err); - - err =tegra_io_rail_power_on (TEGRA_IO_RAIL_LVDS ); -if (err <0 ) -dev_err (sor->dev,"failed to power on LVDS rail: %d\n" , err); - -usleep_range (20 ,100 ); - - err =drm_dp_aux_enable (sor->aux); -if (err <0 ) -dev_err (sor->dev,"failed to enable DPAUX: %d\n" , err); - - err =drm_dp_link_probe (sor->aux, &sor->link); -if (err <0 ) -dev_err (sor->dev,"failed to probe eDP link: %d\n" , err); - -tegra_sor_filter_rates (sor); - - err =drm_dp_link_choose (&sor->link, mode, info); -if (err <0 ) -dev_err (sor->dev,"failed to choose link: %d\n" , err); - -if (output->panel) -drm_panel_prepare (output->panel); - - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_BANDGAP_POWERDOWN ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); - -usleep_range (20 ,40 ); - - value =tegra_sor_readl (sor, sor->soc->regs->pll3); - value |=SOR_PLL3_PLL_VDD_MODE_3V3 ; -tegra_sor_writel (sor, value, sor->soc->regs->pll3); - - value =tegra_sor_readl (sor, sor->soc->regs->pll0); - value &= ~(SOR_PLL0_VCOPD |SOR_PLL0_PWR ); -tegra_sor_writel (sor, value, sor->soc->regs->pll0); - - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE ; - value |=SOR_PLL2_SEQ_PLLCAPPD ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); - -usleep_range (200 ,400 ); - - value =tegra_sor_readl (sor, sor->soc->regs->pll2); - value &= ~SOR_PLL2_POWERDOWN_OVERRIDE ; - value &= ~SOR_PLL2_PORT_POWERDOWN ; -tegra_sor_writel (sor, value, sor->soc->regs->pll2); - - value =tegra_sor_readl (sor,SOR_CLK_CNTRL ); - value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK ; - value |=SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK ; -tegra_sor_writel (sor, value,SOR_CLK_CNTRL ); - - value =tegra_sor_readl (sor,SOR_DP_SPARE0 ); -/* XXX not in TRM */ - value |=SOR_DP_SPARE_PANEL_INTERNAL ; - value |=SOR_DP_SPARE_SEQ_ENABLE ; -tegra_sor_writel (sor, value,SOR_DP_SPARE0 ); - -/* XXX not in TRM */ -tegra_sor_writel (sor,0 ,SOR_LVDS ); - - value =tegra_sor_readl (sor, sor->soc->regs->pll0); - value &= ~SOR_PLL0_ICHPMP_MASK ; - value &= ~SOR_PLL0_VCOCAP_MASK ; - value |=SOR_PLL0_ICHPMP (0x1 ); - value |=SOR_PLL0_VCOCAP (0x3 ); - value |=SOR_PLL0_RESISTOR_EXT ; -tegra_sor_writel (sor, value, sor->soc->regs->pll0); - -/* XXX not in TRM */ -for (value =0 , i =0 ; i <5 ; i++) - value |=SOR_XBAR_CTRL_LINK0_XSEL (i, sor->xbar_cfg[i]) | -SOR_XBAR_CTRL_LINK1_XSEL (i, i); - -tegra_sor_writel (sor,0x00000000 ,SOR_XBAR_POL ); -tegra_sor_writel (sor, value,SOR_XBAR_CTRL ); - -/* -* Switch the pad clock to the DP clock. Note that we cannot actually -* do this because Tegra186 and later don't support clk_set_parent() -* on the sorX_pad_clkout clocks. We already do the equivalent above -* using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register. -*/ -#if 0 - err =clk_set_parent (sor->clk_pad, sor->clk_dp); -if (err <0 ) { -dev_err (sor->dev,"failed to select pad parent clock: %d\n" , - err); -return ; - } -#endif - -/* switch the SOR clock to the pad clock */ - err =tegra_sor_set_parent_clock (sor, sor->clk_pad); -if (err <0 ) { -dev_err (sor->dev,"failed to select SOR parent clock: %d\n" , - err); -return ; - } - -/* switch the output clock to the parent pixel clock */ - err =clk_set_parent (sor->clk, sor->clk_parent); -if (err <0 ) { -dev_err (sor->dev,"failed to select output parent clock: %d\n" , - err); -return ; - } - -/* use DP-A protocol */ - value =tegra_sor_readl (sor,SOR_STATE1 ); - value &= ~SOR_STATE_ASY_PROTOCOL_MASK ; - value |=SOR_STATE_ASY_PROTOCOL_DP_A ; -tegra_sor_writel (sor, value,SOR_STATE1 ); - -/* enable port */ - value =tegra_sor_readl (sor,SOR_DP_LINKCTL0 ); - value |=SOR_DP_LINKCTL_ENABLE ; -tegra_sor_writel (sor, value,SOR_DP_LINKCTL0 ); - -tegra_sor_dp_term_calibrate (sor); - - err =drm_dp_link_train (&sor->link); -if (err <0 ) -dev_err (sor->dev,"link training failed: %d\n" , err); -else -dev_dbg (sor->dev,"link training succeeded\n" ); - - err =drm_dp_link_power_up (sor->aux, &sor->link); -if (err <0 ) -dev_err (sor->dev,"failed to power up eDP link: %d\n" , err); - -/* compute configuration */ -memset (&config,0 ,sizeof (config)); - config.bits_per_pixel = state->bpc *3 ; - - err =tegra_sor_compute_config (sor, mode, &config, &sor->link); -if (err <0 ) -dev_err (sor->dev,"failed to compute configuration: %d\n" , err); - -tegra_sor_apply_config (sor, &config); - - err =tegra_sor_power_up (sor,250 ); -if (err <0 ) -dev_err (sor->dev,"failed to power up SOR: %d\n" , err); - -/* CSTM (LVDS, link A/B, upper) */ - value =SOR_CSTM_LVDS |SOR_CSTM_LINK_ACT_A |SOR_CSTM_LINK_ACT_B | -SOR_CSTM_UPPER ; -tegra_sor_writel (sor, value,SOR_CSTM ); - -/* use DP-A protocol */ - value =tegra_sor_readl (sor,SOR_STATE1 ); - value &= ~SOR_STATE_ASY_PROTOCOL_MASK ; - value |=SOR_STATE_ASY_PROTOCOL_DP_A ; -tegra_sor_writel (sor, value,SOR_STATE1 ); - -tegra_sor_mode_set (sor, mode, state); - -/* PWM setup */ - err =tegra_sor_setup_pwm (sor,250 ); -if (err <0 ) -dev_err (sor->dev,"failed to setup PWM: %d\n" , err); - -tegra_sor_update (sor); - - err =tegra_sor_power_up (sor,250 ); -if (err <0 ) -dev_err (sor->dev,"failed to power up SOR: %d\n" , err); - -/* attach and wake up */ - err =tegra_sor_attach (sor); -if (err <0 ) -dev_err (sor->dev,"failed to attach SOR: %d\n" , err); - - value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS ); - value |=SOR_ENABLE (0 ); -tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS ); - -tegra_dc_commit (dc); - - err =tegra_sor_wakeup (sor); -if (err <0 ) -dev_err (sor->dev,"failed to wakeup SOR: %d\n" , err); - -if (output->panel) -drm_panel_enable (output->panel); -} - static int tegra_sor_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state,
@@ -2155,12 +1856,6 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,return 0 ; } -static const struct drm_encoder_helper_funcs tegra_sor_edp_helpers = { - .disable = tegra_sor_edp_disable, - .enable = tegra_sor_edp_enable, - .atomic_check = tegra_sor_encoder_atomic_check, -}; -static inline u32 tegra_sor_hdmi_subpack (const u8 *ptr,size_t size) {u32 value =0 ;
@@ -2535,9 +2230,9 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS );if (!sor->soc->has_nvdisplay) - value &= ~(SOR1_TIMING_CYA |SOR_ENABLE (1 )); -else - value &= ~SOR_ENABLE (sor->index); + value &= ~SOR1_TIMING_CYA ; + + value &= ~SOR_ENABLE (sor->index);tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS );
@@ -2928,9 +2623,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS );if (!sor->soc->has_nvdisplay) - value |=SOR_ENABLE (1 ) |SOR1_TIMING_CYA ; -else - value |=SOR_ENABLE (sor->index); + value |=SOR1_TIMING_CYA ; + + value |=SOR_ENABLE (sor->index);tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS );
@@ -2965,6 +2660,9 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)u32 value;int err; +if (output->panel) +drm_panel_disable (output->panel); + err =drm_dp_link_power_down (sor->aux, &sor->link);if (err <0 )dev_err (sor->dev,"failed to power down link: %d\n" , err);
@@ -2977,12 +2675,7 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)tegra_sor_update (sor); value =tegra_dc_readl (dc,DC_DISP_DISP_WIN_OPTIONS ); - -if (!sor->soc->has_nvdisplay) - value &= ~(SOR1_TIMING_CYA |SOR_ENABLE (1 )); -else - value &= ~SOR_ENABLE (sor->index); - + value &= ~SOR_ENABLE (sor->index);tegra_dc_writel (dc, value,DC_DISP_DISP_WIN_OPTIONS );tegra_dc_commit (dc);
@@ -3010,6 +2703,9 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)if (err <0 )dev_err (sor->dev,"failed disable DPAUX: %d\n" , err); +if (output->panel) +drm_panel_unprepare (output->panel); +pm_runtime_put (sor->dev); }
@@ -3051,10 +2747,15 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)if (err <0 )dev_err (sor->dev,"failed to probe DP link: %d\n" , err); +tegra_sor_filter_rates (sor); + err =drm_dp_link_choose (&sor->link, mode, info);if (err <0 )dev_err (sor->dev,"failed to choose link: %d\n" , err); +if (output->panel) +drm_panel_prepare (output->panel); + value =tegra_sor_readl (sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN ;tegra_sor_writel (sor, value, sor->soc->regs->pll2);
@@ -3083,14 +2784,23 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder) value =tegra_sor_readl (sor,SOR_CLK_CNTRL ); value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK ; - value |=SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK ; + +if (output->panel) + value |=SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK ; +else + value |=SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK ; +tegra_sor_writel (sor, value,SOR_CLK_CNTRL );usleep_range (200 ,400 ); value =tegra_sor_readl (sor,SOR_DP_SPARE0 );/* XXX not in TRM */ - value &= ~SOR_DP_SPARE_PANEL_INTERNAL ; +if (output->panel) + value |=SOR_DP_SPARE_PANEL_INTERNAL ; +else + value &= ~SOR_DP_SPARE_PANEL_INTERNAL ; + value |=SOR_DP_SPARE_SEQ_ENABLE ;tegra_sor_writel (sor, value,SOR_DP_SPARE0 );
@@ -3177,6 +2887,19 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)tegra_sor_apply_config (sor, &config);tegra_sor_mode_set (sor, mode, state); + +if (output->panel) { +/* CSTM (LVDS, link A/B, upper) */ + value =SOR_CSTM_LVDS |SOR_CSTM_LINK_ACT_A |SOR_CSTM_LINK_ACT_B | +SOR_CSTM_UPPER ; +tegra_sor_writel (sor, value,SOR_CSTM ); + +/* PWM setup */ + err =tegra_sor_setup_pwm (sor,250 ); +if (err <0 ) +dev_err (sor->dev,"failed to setup PWM: %d\n" , err); + } +tegra_sor_update (sor); err =tegra_sor_power_up (sor,250 );
@@ -3197,6 +2920,9 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder) err =tegra_sor_wakeup (sor);if (err <0 )dev_err (sor->dev,"failed to wakeup SOR: %d\n" , err); + +if (output->panel) +drm_panel_enable (output->panel); }static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
@@ -3205,10 +2931,6 @@ static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = { .atomic_check = tegra_sor_encoder_atomic_check, }; -static const struct tegra_sor_ops tegra_sor_edp_ops = { - .name ="eDP" , -}; -static int tegra_sor_hdmi_probe (struct tegra_sor *sor) {int err;
@@ -3330,10 +3052,10 @@ static int tegra_sor_init(struct host1x_client *client) encoder =DRM_MODE_ENCODER_LVDS ; } }else { -if (sor->ops == &tegra_sor_edp_ops) { +if (sor->output.panel) { connector = DRM_MODE_CONNECTOR_eDP; encoder =DRM_MODE_ENCODER_TMDS ; - helpers = &tegra_sor_edp_helpers; + helpers = &tegra_sor_dp_helpers; }else { connector = DRM_MODE_CONNECTOR_DisplayPort; encoder =DRM_MODE_ENCODER_TMDS ;
@@ -3575,10 +3297,11 @@ static const u8 tegra124_sor_tx_pu[4][4][4] = { };static const struct tegra_sor_soc tegra124_sor = { - .supports_edp = true, .supports_lvds = true, .supports_hdmi = false, - .supports_dp = false, + .supports_dp = true, + .supports_audio = false, + .supports_hdcp = false, .regs = &tegra124_sor_regs, .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg,
@@ -3614,10 +3337,11 @@ static const u8 tegra132_sor_pre_emphasis[4][4][4] = { };static const struct tegra_sor_soc tegra132_sor = { - .supports_edp = true, .supports_lvds = true, .supports_hdmi = false, - .supports_dp = false, + .supports_dp = true, + .supports_audio = false, + .supports_hdcp = false, .regs = &tegra124_sor_regs, .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg,
@@ -3652,10 +3376,11 @@ static const u8 tegra210_sor_lane_map[4] = { };static const struct tegra_sor_soc tegra210_sor = { - .supports_edp = true, .supports_lvds = false, .supports_hdmi = false, - .supports_dp = false, + .supports_dp = true, + .supports_audio = false, + .supports_hdcp = false, .regs = &tegra210_sor_regs, .has_nvdisplay = false,
@@ -3669,10 +3394,11 @@ static const struct tegra_sor_soc tegra210_sor = { };static const struct tegra_sor_soc tegra210_sor1 = { - .supports_edp = false, .supports_lvds = false, .supports_hdmi = true, .supports_dp = true, + .supports_audio = true, + .supports_hdcp = true, .regs = &tegra210_sor_regs, .has_nvdisplay = false,
@@ -3751,10 +3477,11 @@ static const u8 tegra186_sor_pre_emphasis[4][4][4] = { };static const struct tegra_sor_soc tegra186_sor = { - .supports_edp = false, .supports_lvds = false, .supports_hdmi = true, .supports_dp = true, + .supports_audio = true, + .supports_hdcp = true, .regs = &tegra186_sor_regs, .has_nvdisplay = true,
@@ -3785,10 +3512,11 @@ static const struct tegra_sor_regs tegra194_sor_regs = { };static const struct tegra_sor_soc tegra194_sor = { - .supports_edp = true, .supports_lvds = false, .supports_hdmi = true, .supports_dp = true, + .supports_audio = true, + .supports_hdcp = true, .regs = &tegra194_sor_regs, .has_nvdisplay = true,
@@ -3836,7 +3564,7 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)*/ sor->pad =TEGRA_IO_PAD_HDMI_DP0 + sor->index; }else { -if (sor->soc->supports_edp) +if (!sor->soc->supports_audio) sor->index =0 ;else sor->index =1 ;
@@ -3929,16 +3657,15 @@ static int tegra_sor_probe(struct platform_device *pdev)return -ENODEV ; } }else { -if (sor->soc->supports_edp) { - sor->ops = &tegra_sor_edp_ops; - sor->pad =TEGRA_IO_PAD_LVDS ; - }else if (sor->soc->supports_dp) { - sor->ops = &tegra_sor_dp_ops; - sor->pad =TEGRA_IO_PAD_LVDS ; - }else { -dev_err (&pdev->dev,"unknown (DP) support\n" ); -return -ENODEV ; - } + np =of_parse_phandle (pdev->dev.of_node,"nvidia,panel" ,0 ); +/* +* No need to keep this around since we only use it as a check +* to see if a panel is connected (eDP) or not (DP). +*/ +of_node_put (np); + + sor->ops = &tegra_sor_dp_ops; + sor->pad =TEGRA_IO_PAD_LVDS ; } err =tegra_sor_parse_dt (sor);
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 30/32 drm/tegra: sor: Avoid timeouts on unplug events
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
When the SOR is disabled in DP mode as part of an unplug event, do not attempt to power the DP link down. Powering down the link requires the DPAUX to transmit AUX messages which only works if there's a connected sink.
--- drivers/gpu/drm/tegra/sor.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/gpu/drm/tegra/sor.c +10 -3
@@ -2663,9 +2663,16 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)if (output->panel)drm_panel_disable (output->panel); - err =drm_dp_link_power_down (sor->aux, &sor->link); -if (err <0 ) -dev_err (sor->dev,"failed to power down link: %d\n" , err); +/* +* Do not attempt to power down a DP link if we're not connected since +* the AUX transactions would just be timing out. +*/ +if (output->connector.status != connector_status_disconnected) { + err =drm_dp_link_power_down (sor->aux, &sor->link); +if (err <0 ) +dev_err (sor->dev,"failed to power down link: %d\n" , + err); + } err =tegra_sor_detach (sor);if (err <0 )
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 31/32 drm/tegra: sor: Extract common audio enabling code
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
The code to enable audio support is split into two parts, one being generic for the SOR and another part that is specific whether the SOR is in HDMI mode or in DP mode. Split out the common part in preparation for reusing the code in DP mode.
--- drivers/gpu/drm/tegra/sor.c | 43 +++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-)
drivers/gpu/drm/tegra/sor.c +25 -18
@@ -2002,6 +2002,30 @@ static void tegra_sor_audio_unprepare(struct tegra_sor *sor)tegra_sor_writel (sor,0 ,SOR_INT_ENABLE ); } +static void tegra_sor_audio_enable (struct tegra_sor *sor) +{ +u32 value; + + value =tegra_sor_readl (sor,SOR_AUDIO_CNTRL ); + +/* select HDA audio input */ + value &= ~SOR_AUDIO_CNTRL_SOURCE_SELECT (SOURCE_SELECT_MASK ); + value |=SOR_AUDIO_CNTRL_SOURCE_SELECT (SOURCE_SELECT_HDA ); + +/* inject null samples */ +if (sor->format.channels !=2 ) + value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL ; +else + value |=SOR_AUDIO_CNTRL_INJECT_NULLSMPL ; + + value |=SOR_AUDIO_CNTRL_AFIFO_FLUSH ; + +tegra_sor_writel (sor, value,SOR_AUDIO_CNTRL ); + +/* enable advertising HBR capability */ +tegra_sor_writel (sor,SOR_AUDIO_SPARE_HBR_ENABLE ,SOR_AUDIO_SPARE ); +} +static int tegra_sor_hdmi_enable_audio_infoframe (struct tegra_sor *sor) {u8 buffer[HDMI_INFOFRAME_SIZE (AUDIO )];
@@ -2037,24 +2061,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor) {u32 value; - value =tegra_sor_readl (sor,SOR_AUDIO_CNTRL ); - -/* select HDA audio input */ - value &= ~SOR_AUDIO_CNTRL_SOURCE_SELECT (SOURCE_SELECT_MASK ); - value |=SOR_AUDIO_CNTRL_SOURCE_SELECT (SOURCE_SELECT_HDA ); - -/* inject null samples */ -if (sor->format.channels !=2 ) - value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL ; -else - value |=SOR_AUDIO_CNTRL_INJECT_NULLSMPL ; - - value |=SOR_AUDIO_CNTRL_AFIFO_FLUSH ; - -tegra_sor_writel (sor, value,SOR_AUDIO_CNTRL ); - -/* enable advertising HBR capability */ -tegra_sor_writel (sor,SOR_AUDIO_SPARE_HBR_ENABLE ,SOR_AUDIO_SPARE ); +tegra_sor_audio_enable (sor);tegra_sor_writel (sor,0 ,SOR_HDMI_ACR_CTRL );
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
PATCH 32/32 drm/tegra: sor: Introduce audio enable/disable callbacks
In reply to Thierry RedingFrom: Thierry Reding <[email protected]>
In order to support different modes (DP in addition to HDMI), split out the audio setup/teardown into callbacks.
--- drivers/gpu/drm/tegra/sor.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/gpu/drm/tegra/sor.c +8 -2
@@ -398,6 +398,8 @@ struct tegra_sor_ops {const char *name;int (*probe)(struct tegra_sor *sor);int (*remove)(struct tegra_sor *sor); +void (*audio_enable)(struct tegra_sor *sor); +void (*audio_disable)(struct tegra_sor *sor); };struct tegra_sor {
@@ -3008,6 +3010,8 @@ static const struct tegra_sor_ops tegra_sor_hdmi_ops = { .name = "HDMI", .probe = tegra_sor_hdmi_probe, .remove = tegra_sor_hdmi_remove, + .audio_enable = tegra_sor_hdmi_audio_enable, + .audio_disable = tegra_sor_hdmi_audio_disable, };static int tegra_sor_dp_probe (struct tegra_sor *sor)
@@ -3616,9 +3620,11 @@ static irqreturn_t tegra_sor_irq(int irq, void *data)tegra_hda_parse_format (format, &sor->format); -tegra_sor_hdmi_audio_enable (sor); +if (sor->ops->audio_enable) + sor->ops->audio_enable (sor); }else { -tegra_sor_hdmi_audio_disable (sor); +if (sor->ops->audio_disable) + sor->ops->audio_disable (sor); } }
Signature · 6 lines
2.23.0 _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel