@@ -7819,7 +7819,11 @@ enum {
#define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12)
#define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12)
#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8)
+#define TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7) #define
+TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
#define TRANS_DDI_BFI_ENABLE (1<<4)
+#define TRANS_DDI_HIGH_TMDS_CHAR_RATE (1<<4)
+#define TRANS_DDI_HDMI_SCRAMBLING (1<<0)
/* DisplayPort Transport Control */
#define _DP_TP_CTL_A 0x64040
@@ -1278,6 +1278,11 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
temp |= TRANS_DDI_MODE_SELECT_HDMI;
else
temp |= TRANS_DDI_MODE_SELECT_DVI;
+
+ if (IS_GEMINILAKE(dev_priv))
+ temp = intel_hdmi_handle_source_scrambling(
+ intel_encoder,
+ &intel_crtc->config->base.adjusted_mode, temp);
} else if (type == INTEL_OUTPUT_ANALOG) {
temp |= TRANS_DDI_MODE_SELECT_FDI;
temp |= (intel_crtc->config->fdi_lanes - 1) << 1; @@ -1843,6 +1848,21 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder,
struct intel_digital_port *intel_dig_port =
enc_to_dig_port(encoder);
+ if (IS_GEMINILAKE(dev_priv)) {
+ struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
+ /*
+ * GLK sports a native HDMI 2.0 controller. If required
+ * clock rate is > 340 Mhz && scrambling is supported
+ * by sink, enable scrambling before enabling the
+ * HDMI 2.0 port. The sink can choose to disable the
+ * scrambling if it doesn't detect a scrambled within
+ * 100 ms.
+ */
+ intel_hdmi_handle_sink_scrambling(intel_encoder,
+ conn_state->connector,
+ crtc->config, true);
+ }
+
/* In HDMI/DVI mode, the port width, and swing/emphasis values
* are ignored so nothing special needs to be done besides
* enabling the port.
@@ -1875,6 +1895,14 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder,
if (old_crtc_state->has_audio)
intel_audio_codec_disable(intel_encoder);
+ if (type == INTEL_OUTPUT_HDMI) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+
+ intel_hdmi_handle_sink_scrambling(intel_encoder,
+ old_conn_state->connector,
+ intel_crtc->config, false);
+ }
+
if (type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
@@ -691,6 +691,12 @@ struct intel_crtc_state {
/* Gamma mode programmed on the pipe */
uint32_t gamma_mode;
+
+ /* HDMI scrambling status (sink) */
+ bool scrambling;
+
+ /* HDMI High TMDS char rate ratio (sink) */
+ bool high_tmds_clock_ratio;
};
struct vlv_wm_state {
@@ -1609,6 +1615,14 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, bool intel_hdmi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state);
+uint32_t
+intel_hdmi_handle_source_scrambling(struct intel_encoder *intel_encoder,
+ struct drm_display_mode *mode,
+ uint32_t config);
+void intel_hdmi_handle_sink_scrambling(struct intel_encoder *intel_encoder,
+ struct drm_connector *connector,
+ struct intel_crtc_state *config,
+ bool enable);
void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable);
@@ -34,6 +34,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_scdc_helper.h>
#include "intel_drv.h"
#include <drm/i915_drm.h>
#include <drm/intel_lpe_audio.h>
@@ -1795,6 +1796,111 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; }
+void intel_hdmi_handle_sink_scrambling(struct intel_encoder *intel_encoder,
+ struct drm_connector *connector,
+ struct intel_crtc_state *config,
+ bool enable)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
+ struct drm_scdc *scdc = &connector->display_info.hdmi.scdc;
+ struct drm_scrambling *scrambling = &scdc->scrambling;
+ struct drm_display_mode *mode = &config->base.adjusted_mode;
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct i2c_adapter *adptr = intel_gmbus_get_adapter(dev_priv,
+ intel_hdmi->ddc_bus);
+
+ if (!scrambling->supported)
+ return;
+
+ DRM_DEBUG_KMS("Setting sink scrambling for enc:%s connector:%s\n",
+ intel_encoder->base.name, connector->name);
+
+ if (enable) {
+
+ if (mode->clock > 340000) {
+ /* Set TMDS bit clock ratio to 1/40 */
+ config->high_tmds_clock_ratio =
+ drm_scdc_set_high_tmds_clock_ratio(adptr);
+ if (!config->high_tmds_clock_ratio) {
+ DRM_ERROR("Set high TMDS ratio failed\n");
+ return;
+ }
+
+ /* Enable sink scrambling */
+ config->scrambling = drm_scdc_enable_scrambling(adptr);
+ if (!config->scrambling) {
+ DRM_ERROR("Can't enable sink scrambling\n");
+ return;
+ }
+ }
+
+ /* Few sinks support scrambling at clocks <=340 MHz too */
+ if (!config->scrambling && scrambling->low_rates) {
+ config->scrambling = drm_scdc_enable_scrambling(adptr);
+ if (!config->scrambling)
+ DRM_ERROR("Can't enable sink scrambling\n");
+ }
+
+ return;
+ }
+
+ if (config->high_tmds_clock_ratio) {
+ /* Set TMDS bit clock ratio back to 1/10 */
+ config->high_tmds_clock_ratio =
+ !(drm_scdc_clear_high_tmds_clock_ratio(adptr));
+ if (config->high_tmds_clock_ratio)
+ DRM_ERROR("Reset high TMDS ratio failed\n");
+ }
+
+ if (config->scrambling) {
+ /* Disable sink scrambling */
+ config->scrambling = !(drm_scdc_disable_scrambling(adptr));
+ if (config->scrambling)
+ DRM_ERROR("Disable sink scrambling failed\n");
+ }
+}
+
+static inline uint32_t _intel_hdmi_set_source_scrambling(uint32_t
+hdmi_config) {
+ return hdmi_config |= (TRANS_DDI_HDMI_SCRAMBLING |
+ TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ |
+ TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE);
+}
+
+uint32_t
+intel_hdmi_handle_source_scrambling(struct intel_encoder *intel_encoder,
+ struct drm_display_mode *mode, uint32_t hdmi_config) {
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
+ struct drm_connector *connector = &intel_hdmi->attached_connector->base;
+ struct drm_hdmi_info *hdmi_info = &connector->display_info.hdmi;
+ struct drm_scrambling *scrambling = &hdmi_info->scdc.scrambling;
+
+ DRM_DEBUG_KMS("Setting scrambling for enc:%s connector:%s\n",
+ intel_encoder->base.name, connector->name);
+
+ hdmi_config &= ~(TRANS_DDI_HDMI_SCRAMBLING |
+ TRANS_DDI_HIGH_TMDS_CHAR_RATE |
+ TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ |
+ TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE);
+
+ if (mode->clock <= 340000) {
+ /* Few sinks support scrambling at rate < 340 MHz too */
+ if (scrambling->low_rates)
+ hdmi_config =
+ _intel_hdmi_set_source_scrambling(hdmi_config);
+ return hdmi_config;
+ }
+
+ /* Scrambling or not, if clock > 340 MHz, set high char rate */
+ hdmi_config |= TRANS_DDI_HIGH_TMDS_CHAR_RATE;
+
+ if (scrambling->supported)
+ hdmi_config = _intel_hdmi_set_source_scrambling(hdmi_config);
+
+ return hdmi_config;
+}
+
static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv,
enum port port)
{
--
1.9.1