1e3adcf8fSFrançois Tigeot /* 2e3adcf8fSFrançois Tigeot * Copyright 2006 Dave Airlie <airlied@linux.ie> 3e3adcf8fSFrançois Tigeot * Copyright © 2006-2009 Intel Corporation 4e3adcf8fSFrançois Tigeot * 5e3adcf8fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 6e3adcf8fSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 7e3adcf8fSFrançois Tigeot * to deal in the Software without restriction, including without limitation 8e3adcf8fSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9e3adcf8fSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 10e3adcf8fSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 11e3adcf8fSFrançois Tigeot * 12e3adcf8fSFrançois Tigeot * The above copyright notice and this permission notice (including the next 13e3adcf8fSFrançois Tigeot * paragraph) shall be included in all copies or substantial portions of the 14e3adcf8fSFrançois Tigeot * Software. 15e3adcf8fSFrançois Tigeot * 16e3adcf8fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17e3adcf8fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18e3adcf8fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19e3adcf8fSFrançois Tigeot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20e3adcf8fSFrançois Tigeot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21e3adcf8fSFrançois Tigeot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22e3adcf8fSFrançois Tigeot * DEALINGS IN THE SOFTWARE. 23e3adcf8fSFrançois Tigeot * 24e3adcf8fSFrançois Tigeot * Authors: 25e3adcf8fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 26e3adcf8fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 27e3adcf8fSFrançois Tigeot */ 28e3adcf8fSFrançois Tigeot 29a2fdbec6SFrançois Tigeot #include <linux/i2c.h> 30a2fdbec6SFrançois Tigeot #include <linux/delay.h> 319edbd4a0SFrançois Tigeot #include <linux/hdmi.h> 3218e26a6dSFrançois Tigeot #include <drm/drmP.h> 3318e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3418e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 3518e26a6dSFrançois Tigeot #include "intel_drv.h" 365c6c6f23SFrançois Tigeot #include <drm/i915_drm.h> 37e3adcf8fSFrançois Tigeot #include "i915_drv.h" 38e3adcf8fSFrançois Tigeot 3919df918dSFrançois Tigeot static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi) 40e3adcf8fSFrançois Tigeot { 4119df918dSFrançois Tigeot return hdmi_to_dig_port(intel_hdmi)->base.base.dev; 4219df918dSFrançois Tigeot } 4319df918dSFrançois Tigeot 4419df918dSFrançois Tigeot static void 4519df918dSFrançois Tigeot assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) 4619df918dSFrançois Tigeot { 4719df918dSFrançois Tigeot struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi); 4819df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 4919df918dSFrançois Tigeot uint32_t enabled_bits; 5019df918dSFrançois Tigeot 51a2fdbec6SFrançois Tigeot enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; 5219df918dSFrançois Tigeot 538e26cdf6SFrançois Tigeot WARN(I915_READ(intel_hdmi->hdmi_reg) & enabled_bits, 5419df918dSFrançois Tigeot "HDMI port enabled, expecting disabled\n"); 5519df918dSFrançois Tigeot } 5619df918dSFrançois Tigeot 5719df918dSFrançois Tigeot struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) 5819df918dSFrançois Tigeot { 5919df918dSFrançois Tigeot struct intel_digital_port *intel_dig_port = 6019df918dSFrançois Tigeot container_of(encoder, struct intel_digital_port, base.base); 6119df918dSFrançois Tigeot return &intel_dig_port->hdmi; 62e3adcf8fSFrançois Tigeot } 63e3adcf8fSFrançois Tigeot 64e3adcf8fSFrançois Tigeot static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) 65e3adcf8fSFrançois Tigeot { 6619df918dSFrançois Tigeot return enc_to_intel_hdmi(&intel_attached_encoder(connector)->base); 67e3adcf8fSFrançois Tigeot } 68e3adcf8fSFrançois Tigeot 699edbd4a0SFrançois Tigeot static u32 g4x_infoframe_index(enum hdmi_infoframe_type type) 70e3adcf8fSFrançois Tigeot { 719edbd4a0SFrançois Tigeot switch (type) { 729edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_AVI: 7319df918dSFrançois Tigeot return VIDEO_DIP_SELECT_AVI; 749edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_SPD: 7519df918dSFrançois Tigeot return VIDEO_DIP_SELECT_SPD; 769edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_VENDOR: 779edbd4a0SFrançois Tigeot return VIDEO_DIP_SELECT_VENDOR; 78e3adcf8fSFrançois Tigeot default: 799edbd4a0SFrançois Tigeot DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); 8019df918dSFrançois Tigeot return 0; 8119df918dSFrançois Tigeot } 82e3adcf8fSFrançois Tigeot } 83e3adcf8fSFrançois Tigeot 849edbd4a0SFrançois Tigeot static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type) 85e3adcf8fSFrançois Tigeot { 869edbd4a0SFrançois Tigeot switch (type) { 879edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_AVI: 8819df918dSFrançois Tigeot return VIDEO_DIP_ENABLE_AVI; 899edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_SPD: 9019df918dSFrançois Tigeot return VIDEO_DIP_ENABLE_SPD; 919edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_VENDOR: 929edbd4a0SFrançois Tigeot return VIDEO_DIP_ENABLE_VENDOR; 93e3adcf8fSFrançois Tigeot default: 949edbd4a0SFrançois Tigeot DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); 9519df918dSFrançois Tigeot return 0; 9619df918dSFrançois Tigeot } 97e3adcf8fSFrançois Tigeot } 98e3adcf8fSFrançois Tigeot 999edbd4a0SFrançois Tigeot static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type) 10019df918dSFrançois Tigeot { 1019edbd4a0SFrançois Tigeot switch (type) { 1029edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_AVI: 10319df918dSFrançois Tigeot return VIDEO_DIP_ENABLE_AVI_HSW; 1049edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_SPD: 10519df918dSFrançois Tigeot return VIDEO_DIP_ENABLE_SPD_HSW; 1069edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_VENDOR: 1079edbd4a0SFrançois Tigeot return VIDEO_DIP_ENABLE_VS_HSW; 10819df918dSFrançois Tigeot default: 1099edbd4a0SFrançois Tigeot DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); 11019df918dSFrançois Tigeot return 0; 11119df918dSFrançois Tigeot } 112e3adcf8fSFrançois Tigeot } 113e3adcf8fSFrançois Tigeot 1149edbd4a0SFrançois Tigeot static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type, 115ba55f2f5SFrançois Tigeot enum transcoder cpu_transcoder, 116ba55f2f5SFrançois Tigeot struct drm_i915_private *dev_priv) 11719df918dSFrançois Tigeot { 1189edbd4a0SFrançois Tigeot switch (type) { 1199edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_AVI: 1208e26cdf6SFrançois Tigeot return HSW_TVIDEO_DIP_AVI_DATA(cpu_transcoder); 1219edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_SPD: 1228e26cdf6SFrançois Tigeot return HSW_TVIDEO_DIP_SPD_DATA(cpu_transcoder); 1239edbd4a0SFrançois Tigeot case HDMI_INFOFRAME_TYPE_VENDOR: 1249edbd4a0SFrançois Tigeot return HSW_TVIDEO_DIP_VS_DATA(cpu_transcoder); 12519df918dSFrançois Tigeot default: 1269edbd4a0SFrançois Tigeot DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); 12719df918dSFrançois Tigeot return 0; 12819df918dSFrançois Tigeot } 12919df918dSFrançois Tigeot } 13019df918dSFrançois Tigeot 13119df918dSFrançois Tigeot static void g4x_write_infoframe(struct drm_encoder *encoder, 1329edbd4a0SFrançois Tigeot enum hdmi_infoframe_type type, 1339edbd4a0SFrançois Tigeot const void *frame, ssize_t len) 134e3adcf8fSFrançois Tigeot { 1359edbd4a0SFrançois Tigeot const uint32_t *data = frame; 136e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 137e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 13819df918dSFrançois Tigeot u32 val = I915_READ(VIDEO_DIP_CTL); 1399edbd4a0SFrançois Tigeot int i; 140e3adcf8fSFrançois Tigeot 14119df918dSFrançois Tigeot WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n"); 142e3adcf8fSFrançois Tigeot 14319df918dSFrançois Tigeot val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ 1449edbd4a0SFrançois Tigeot val |= g4x_infoframe_index(type); 145e3adcf8fSFrançois Tigeot 1469edbd4a0SFrançois Tigeot val &= ~g4x_infoframe_enable(type); 147e3adcf8fSFrançois Tigeot 14819df918dSFrançois Tigeot I915_WRITE(VIDEO_DIP_CTL, val); 149e3adcf8fSFrançois Tigeot 15019df918dSFrançois Tigeot mmiowb(); 151e3adcf8fSFrançois Tigeot for (i = 0; i < len; i += 4) { 152e3adcf8fSFrançois Tigeot I915_WRITE(VIDEO_DIP_DATA, *data); 153e3adcf8fSFrançois Tigeot data++; 154e3adcf8fSFrançois Tigeot } 15519df918dSFrançois Tigeot /* Write every possible data byte to force correct ECC calculation. */ 15619df918dSFrançois Tigeot for (; i < VIDEO_DIP_DATA_SIZE; i += 4) 15719df918dSFrançois Tigeot I915_WRITE(VIDEO_DIP_DATA, 0); 15819df918dSFrançois Tigeot mmiowb(); 159e3adcf8fSFrançois Tigeot 1609edbd4a0SFrançois Tigeot val |= g4x_infoframe_enable(type); 16119df918dSFrançois Tigeot val &= ~VIDEO_DIP_FREQ_MASK; 16219df918dSFrançois Tigeot val |= VIDEO_DIP_FREQ_VSYNC; 163e3adcf8fSFrançois Tigeot 16419df918dSFrançois Tigeot I915_WRITE(VIDEO_DIP_CTL, val); 16519df918dSFrançois Tigeot POSTING_READ(VIDEO_DIP_CTL); 166e3adcf8fSFrançois Tigeot } 167e3adcf8fSFrançois Tigeot 16819df918dSFrançois Tigeot static void ibx_write_infoframe(struct drm_encoder *encoder, 1699edbd4a0SFrançois Tigeot enum hdmi_infoframe_type type, 1709edbd4a0SFrançois Tigeot const void *frame, ssize_t len) 171e3adcf8fSFrançois Tigeot { 1729edbd4a0SFrançois Tigeot const uint32_t *data = frame; 173e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 174e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 17519df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 1769edbd4a0SFrançois Tigeot int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe); 17719df918dSFrançois Tigeot u32 val = I915_READ(reg); 178e3adcf8fSFrançois Tigeot 17919df918dSFrançois Tigeot WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n"); 180e3adcf8fSFrançois Tigeot 181e3adcf8fSFrançois Tigeot val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ 1829edbd4a0SFrançois Tigeot val |= g4x_infoframe_index(type); 183e3adcf8fSFrançois Tigeot 1849edbd4a0SFrançois Tigeot val &= ~g4x_infoframe_enable(type); 185e3adcf8fSFrançois Tigeot 18619df918dSFrançois Tigeot I915_WRITE(reg, val); 18719df918dSFrançois Tigeot 18819df918dSFrançois Tigeot mmiowb(); 189e3adcf8fSFrançois Tigeot for (i = 0; i < len; i += 4) { 190e3adcf8fSFrançois Tigeot I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); 191e3adcf8fSFrançois Tigeot data++; 192e3adcf8fSFrançois Tigeot } 19319df918dSFrançois Tigeot /* Write every possible data byte to force correct ECC calculation. */ 19419df918dSFrançois Tigeot for (; i < VIDEO_DIP_DATA_SIZE; i += 4) 19519df918dSFrançois Tigeot I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0); 19619df918dSFrançois Tigeot mmiowb(); 197e3adcf8fSFrançois Tigeot 1989edbd4a0SFrançois Tigeot val |= g4x_infoframe_enable(type); 19919df918dSFrançois Tigeot val &= ~VIDEO_DIP_FREQ_MASK; 20019df918dSFrançois Tigeot val |= VIDEO_DIP_FREQ_VSYNC; 201e3adcf8fSFrançois Tigeot 20219df918dSFrançois Tigeot I915_WRITE(reg, val); 20319df918dSFrançois Tigeot POSTING_READ(reg); 20419df918dSFrançois Tigeot } 20519df918dSFrançois Tigeot 20619df918dSFrançois Tigeot static void cpt_write_infoframe(struct drm_encoder *encoder, 2079edbd4a0SFrançois Tigeot enum hdmi_infoframe_type type, 2089edbd4a0SFrançois Tigeot const void *frame, ssize_t len) 20919df918dSFrançois Tigeot { 2109edbd4a0SFrançois Tigeot const uint32_t *data = frame; 21119df918dSFrançois Tigeot struct drm_device *dev = encoder->dev; 21219df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 21319df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 2149edbd4a0SFrançois Tigeot int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe); 21519df918dSFrançois Tigeot u32 val = I915_READ(reg); 21619df918dSFrançois Tigeot 21719df918dSFrançois Tigeot WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n"); 21819df918dSFrançois Tigeot 21919df918dSFrançois Tigeot val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ 2209edbd4a0SFrançois Tigeot val |= g4x_infoframe_index(type); 22119df918dSFrançois Tigeot 22219df918dSFrançois Tigeot /* The DIP control register spec says that we need to update the AVI 22319df918dSFrançois Tigeot * infoframe without clearing its enable bit */ 2249edbd4a0SFrançois Tigeot if (type != HDMI_INFOFRAME_TYPE_AVI) 2259edbd4a0SFrançois Tigeot val &= ~g4x_infoframe_enable(type); 22619df918dSFrançois Tigeot 22719df918dSFrançois Tigeot I915_WRITE(reg, val); 22819df918dSFrançois Tigeot 22919df918dSFrançois Tigeot mmiowb(); 23019df918dSFrançois Tigeot for (i = 0; i < len; i += 4) { 23119df918dSFrançois Tigeot I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); 23219df918dSFrançois Tigeot data++; 23319df918dSFrançois Tigeot } 23419df918dSFrançois Tigeot /* Write every possible data byte to force correct ECC calculation. */ 23519df918dSFrançois Tigeot for (; i < VIDEO_DIP_DATA_SIZE; i += 4) 23619df918dSFrançois Tigeot I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0); 23719df918dSFrançois Tigeot mmiowb(); 23819df918dSFrançois Tigeot 2399edbd4a0SFrançois Tigeot val |= g4x_infoframe_enable(type); 24019df918dSFrançois Tigeot val &= ~VIDEO_DIP_FREQ_MASK; 24119df918dSFrançois Tigeot val |= VIDEO_DIP_FREQ_VSYNC; 24219df918dSFrançois Tigeot 24319df918dSFrançois Tigeot I915_WRITE(reg, val); 24419df918dSFrançois Tigeot POSTING_READ(reg); 24519df918dSFrançois Tigeot } 24619df918dSFrançois Tigeot 24719df918dSFrançois Tigeot static void vlv_write_infoframe(struct drm_encoder *encoder, 2489edbd4a0SFrançois Tigeot enum hdmi_infoframe_type type, 2499edbd4a0SFrançois Tigeot const void *frame, ssize_t len) 25019df918dSFrançois Tigeot { 2519edbd4a0SFrançois Tigeot const uint32_t *data = frame; 25219df918dSFrançois Tigeot struct drm_device *dev = encoder->dev; 25319df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 25419df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 2559edbd4a0SFrançois Tigeot int i, reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); 25619df918dSFrançois Tigeot u32 val = I915_READ(reg); 25719df918dSFrançois Tigeot 25819df918dSFrançois Tigeot WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n"); 25919df918dSFrançois Tigeot 26019df918dSFrançois Tigeot val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ 2619edbd4a0SFrançois Tigeot val |= g4x_infoframe_index(type); 26219df918dSFrançois Tigeot 2639edbd4a0SFrançois Tigeot val &= ~g4x_infoframe_enable(type); 26419df918dSFrançois Tigeot 26519df918dSFrançois Tigeot I915_WRITE(reg, val); 26619df918dSFrançois Tigeot 26719df918dSFrançois Tigeot mmiowb(); 26819df918dSFrançois Tigeot for (i = 0; i < len; i += 4) { 26919df918dSFrançois Tigeot I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data); 27019df918dSFrançois Tigeot data++; 27119df918dSFrançois Tigeot } 27219df918dSFrançois Tigeot /* Write every possible data byte to force correct ECC calculation. */ 27319df918dSFrançois Tigeot for (; i < VIDEO_DIP_DATA_SIZE; i += 4) 27419df918dSFrançois Tigeot I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0); 27519df918dSFrançois Tigeot mmiowb(); 27619df918dSFrançois Tigeot 2779edbd4a0SFrançois Tigeot val |= g4x_infoframe_enable(type); 27819df918dSFrançois Tigeot val &= ~VIDEO_DIP_FREQ_MASK; 27919df918dSFrançois Tigeot val |= VIDEO_DIP_FREQ_VSYNC; 28019df918dSFrançois Tigeot 28119df918dSFrançois Tigeot I915_WRITE(reg, val); 28219df918dSFrançois Tigeot POSTING_READ(reg); 28319df918dSFrançois Tigeot } 28419df918dSFrançois Tigeot 28519df918dSFrançois Tigeot static void hsw_write_infoframe(struct drm_encoder *encoder, 2869edbd4a0SFrançois Tigeot enum hdmi_infoframe_type type, 2879edbd4a0SFrançois Tigeot const void *frame, ssize_t len) 28819df918dSFrançois Tigeot { 2899edbd4a0SFrançois Tigeot const uint32_t *data = frame; 29019df918dSFrançois Tigeot struct drm_device *dev = encoder->dev; 29119df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 29219df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 2938e26cdf6SFrançois Tigeot u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder); 2949edbd4a0SFrançois Tigeot u32 data_reg; 2959edbd4a0SFrançois Tigeot int i; 29619df918dSFrançois Tigeot u32 val = I915_READ(ctl_reg); 29719df918dSFrançois Tigeot 2989edbd4a0SFrançois Tigeot data_reg = hsw_infoframe_data_reg(type, 299ba55f2f5SFrançois Tigeot intel_crtc->config.cpu_transcoder, 300ba55f2f5SFrançois Tigeot dev_priv); 30119df918dSFrançois Tigeot if (data_reg == 0) 30219df918dSFrançois Tigeot return; 30319df918dSFrançois Tigeot 3049edbd4a0SFrançois Tigeot val &= ~hsw_infoframe_enable(type); 30519df918dSFrançois Tigeot I915_WRITE(ctl_reg, val); 30619df918dSFrançois Tigeot 30719df918dSFrançois Tigeot mmiowb(); 30819df918dSFrançois Tigeot for (i = 0; i < len; i += 4) { 30919df918dSFrançois Tigeot I915_WRITE(data_reg + i, *data); 31019df918dSFrançois Tigeot data++; 31119df918dSFrançois Tigeot } 31219df918dSFrançois Tigeot /* Write every possible data byte to force correct ECC calculation. */ 31319df918dSFrançois Tigeot for (; i < VIDEO_DIP_DATA_SIZE; i += 4) 31419df918dSFrançois Tigeot I915_WRITE(data_reg + i, 0); 31519df918dSFrançois Tigeot mmiowb(); 31619df918dSFrançois Tigeot 3179edbd4a0SFrançois Tigeot val |= hsw_infoframe_enable(type); 31819df918dSFrançois Tigeot I915_WRITE(ctl_reg, val); 31919df918dSFrançois Tigeot POSTING_READ(ctl_reg); 320e3adcf8fSFrançois Tigeot } 321e3adcf8fSFrançois Tigeot 3229edbd4a0SFrançois Tigeot /* 3239edbd4a0SFrançois Tigeot * The data we write to the DIP data buffer registers is 1 byte bigger than the 3249edbd4a0SFrançois Tigeot * HDMI infoframe size because of an ECC/reserved byte at position 3 (starting 3259edbd4a0SFrançois Tigeot * at 0). It's also a byte used by DisplayPort so the same DIP registers can be 3269edbd4a0SFrançois Tigeot * used for both technologies. 3279edbd4a0SFrançois Tigeot * 3289edbd4a0SFrançois Tigeot * DW0: Reserved/ECC/DP | HB2 | HB1 | HB0 3299edbd4a0SFrançois Tigeot * DW1: DB3 | DB2 | DB1 | DB0 3309edbd4a0SFrançois Tigeot * DW2: DB7 | DB6 | DB5 | DB4 3319edbd4a0SFrançois Tigeot * DW3: ... 3329edbd4a0SFrançois Tigeot * 3339edbd4a0SFrançois Tigeot * (HB is Header Byte, DB is Data Byte) 3349edbd4a0SFrançois Tigeot * 3359edbd4a0SFrançois Tigeot * The hdmi pack() functions don't know about that hardware specific hole so we 3369edbd4a0SFrançois Tigeot * trick them by giving an offset into the buffer and moving back the header 3379edbd4a0SFrançois Tigeot * bytes by one. 3389edbd4a0SFrançois Tigeot */ 3399edbd4a0SFrançois Tigeot static void intel_write_infoframe(struct drm_encoder *encoder, 3409edbd4a0SFrançois Tigeot union hdmi_infoframe *frame) 341e3adcf8fSFrançois Tigeot { 342e3adcf8fSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 3439edbd4a0SFrançois Tigeot uint8_t buffer[VIDEO_DIP_DATA_SIZE]; 3449edbd4a0SFrançois Tigeot ssize_t len; 345e3adcf8fSFrançois Tigeot 3469edbd4a0SFrançois Tigeot /* see comment above for the reason for this offset */ 3479edbd4a0SFrançois Tigeot len = hdmi_infoframe_pack(frame, buffer + 1, sizeof(buffer) - 1); 3489edbd4a0SFrançois Tigeot if (len < 0) 3499edbd4a0SFrançois Tigeot return; 3509edbd4a0SFrançois Tigeot 3519edbd4a0SFrançois Tigeot /* Insert the 'hole' (see big comment above) at position 3 */ 3529edbd4a0SFrançois Tigeot buffer[0] = buffer[1]; 3539edbd4a0SFrançois Tigeot buffer[1] = buffer[2]; 3549edbd4a0SFrançois Tigeot buffer[2] = buffer[3]; 3559edbd4a0SFrançois Tigeot buffer[3] = 0; 3569edbd4a0SFrançois Tigeot len++; 3579edbd4a0SFrançois Tigeot 3589edbd4a0SFrançois Tigeot intel_hdmi->write_infoframe(encoder, frame->any.type, buffer, len); 359e3adcf8fSFrançois Tigeot } 360e3adcf8fSFrançois Tigeot 36119df918dSFrançois Tigeot static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, 36219df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 363e3adcf8fSFrançois Tigeot { 364a2fdbec6SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 3658e26cdf6SFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 3669edbd4a0SFrançois Tigeot union hdmi_infoframe frame; 3679edbd4a0SFrançois Tigeot int ret; 368e3adcf8fSFrançois Tigeot 369*24edb884SFrançois Tigeot /* Set user selected PAR to incoming mode's member */ 370*24edb884SFrançois Tigeot adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio; 371*24edb884SFrançois Tigeot 3729edbd4a0SFrançois Tigeot ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, 3739edbd4a0SFrançois Tigeot adjusted_mode); 3749edbd4a0SFrançois Tigeot if (ret < 0) { 3759edbd4a0SFrançois Tigeot DRM_ERROR("couldn't fill AVI infoframe\n"); 3769edbd4a0SFrançois Tigeot return; 3779edbd4a0SFrançois Tigeot } 37819df918dSFrançois Tigeot 379a2fdbec6SFrançois Tigeot if (intel_hdmi->rgb_quant_range_selectable) { 3808e26cdf6SFrançois Tigeot if (intel_crtc->config.limited_color_range) 3819edbd4a0SFrançois Tigeot frame.avi.quantization_range = 3829edbd4a0SFrançois Tigeot HDMI_QUANTIZATION_RANGE_LIMITED; 383a2fdbec6SFrançois Tigeot else 3849edbd4a0SFrançois Tigeot frame.avi.quantization_range = 3859edbd4a0SFrançois Tigeot HDMI_QUANTIZATION_RANGE_FULL; 386a2fdbec6SFrançois Tigeot } 387a2fdbec6SFrançois Tigeot 3889edbd4a0SFrançois Tigeot intel_write_infoframe(encoder, &frame); 389e3adcf8fSFrançois Tigeot } 390e3adcf8fSFrançois Tigeot 391e3adcf8fSFrançois Tigeot static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) 392e3adcf8fSFrançois Tigeot { 3939edbd4a0SFrançois Tigeot union hdmi_infoframe frame; 3949edbd4a0SFrançois Tigeot int ret; 395e3adcf8fSFrançois Tigeot 3969edbd4a0SFrançois Tigeot ret = hdmi_spd_infoframe_init(&frame.spd, "Intel", "Integrated gfx"); 3979edbd4a0SFrançois Tigeot if (ret < 0) { 3989edbd4a0SFrançois Tigeot DRM_ERROR("couldn't fill SPD infoframe\n"); 3999edbd4a0SFrançois Tigeot return; 4009edbd4a0SFrançois Tigeot } 401e3adcf8fSFrançois Tigeot 4029edbd4a0SFrançois Tigeot frame.spd.sdi = HDMI_SPD_SDI_PC; 4039edbd4a0SFrançois Tigeot 4049edbd4a0SFrançois Tigeot intel_write_infoframe(encoder, &frame); 4059edbd4a0SFrançois Tigeot } 4069edbd4a0SFrançois Tigeot 4079edbd4a0SFrançois Tigeot static void 4089edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder, 4099edbd4a0SFrançois Tigeot struct drm_display_mode *adjusted_mode) 4109edbd4a0SFrançois Tigeot { 4119edbd4a0SFrançois Tigeot union hdmi_infoframe frame; 4129edbd4a0SFrançois Tigeot int ret; 4139edbd4a0SFrançois Tigeot 4149edbd4a0SFrançois Tigeot ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, 4159edbd4a0SFrançois Tigeot adjusted_mode); 4169edbd4a0SFrançois Tigeot if (ret < 0) 4179edbd4a0SFrançois Tigeot return; 4189edbd4a0SFrançois Tigeot 4199edbd4a0SFrançois Tigeot intel_write_infoframe(encoder, &frame); 420e3adcf8fSFrançois Tigeot } 421e3adcf8fSFrançois Tigeot 42219df918dSFrançois Tigeot static void g4x_set_infoframes(struct drm_encoder *encoder, 423ba55f2f5SFrançois Tigeot bool enable, 42419df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 42519df918dSFrançois Tigeot { 42619df918dSFrançois Tigeot struct drm_i915_private *dev_priv = encoder->dev->dev_private; 427a2fdbec6SFrançois Tigeot struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); 428a2fdbec6SFrançois Tigeot struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; 42919df918dSFrançois Tigeot u32 reg = VIDEO_DIP_CTL; 43019df918dSFrançois Tigeot u32 val = I915_READ(reg); 431ba55f2f5SFrançois Tigeot u32 port = VIDEO_DIP_PORT(intel_dig_port->port); 43219df918dSFrançois Tigeot 43319df918dSFrançois Tigeot assert_hdmi_port_disabled(intel_hdmi); 43419df918dSFrançois Tigeot 43519df918dSFrançois Tigeot /* If the registers were not initialized yet, they might be zeroes, 43619df918dSFrançois Tigeot * which means we're selecting the AVI DIP and we're setting its 43719df918dSFrançois Tigeot * frequency to once. This seems to really confuse the HW and make 43819df918dSFrançois Tigeot * things stop working (the register spec says the AVI always needs to 43919df918dSFrançois Tigeot * be sent every VSync). So here we avoid writing to the register more 44019df918dSFrançois Tigeot * than we need and also explicitly select the AVI DIP and explicitly 44119df918dSFrançois Tigeot * set its frequency to every VSync. Avoiding to write it twice seems to 44219df918dSFrançois Tigeot * be enough to solve the problem, but being defensive shouldn't hurt us 44319df918dSFrançois Tigeot * either. */ 44419df918dSFrançois Tigeot val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC; 44519df918dSFrançois Tigeot 446ba55f2f5SFrançois Tigeot if (!enable) { 44719df918dSFrançois Tigeot if (!(val & VIDEO_DIP_ENABLE)) 44819df918dSFrançois Tigeot return; 44919df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 45019df918dSFrançois Tigeot I915_WRITE(reg, val); 45119df918dSFrançois Tigeot POSTING_READ(reg); 45219df918dSFrançois Tigeot return; 45319df918dSFrançois Tigeot } 45419df918dSFrançois Tigeot 45519df918dSFrançois Tigeot if (port != (val & VIDEO_DIP_PORT_MASK)) { 45619df918dSFrançois Tigeot if (val & VIDEO_DIP_ENABLE) { 45719df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 45819df918dSFrançois Tigeot I915_WRITE(reg, val); 45919df918dSFrançois Tigeot POSTING_READ(reg); 46019df918dSFrançois Tigeot } 46119df918dSFrançois Tigeot val &= ~VIDEO_DIP_PORT_MASK; 46219df918dSFrançois Tigeot val |= port; 46319df918dSFrançois Tigeot } 46419df918dSFrançois Tigeot 46519df918dSFrançois Tigeot val |= VIDEO_DIP_ENABLE; 46619df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE_VENDOR; 46719df918dSFrançois Tigeot 46819df918dSFrançois Tigeot I915_WRITE(reg, val); 46919df918dSFrançois Tigeot POSTING_READ(reg); 47019df918dSFrançois Tigeot 47119df918dSFrançois Tigeot intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); 47219df918dSFrançois Tigeot intel_hdmi_set_spd_infoframe(encoder); 4739edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); 47419df918dSFrançois Tigeot } 47519df918dSFrançois Tigeot 47619df918dSFrançois Tigeot static void ibx_set_infoframes(struct drm_encoder *encoder, 477ba55f2f5SFrançois Tigeot bool enable, 47819df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 47919df918dSFrançois Tigeot { 48019df918dSFrançois Tigeot struct drm_i915_private *dev_priv = encoder->dev->dev_private; 48119df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 482a2fdbec6SFrançois Tigeot struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); 483a2fdbec6SFrançois Tigeot struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; 48419df918dSFrançois Tigeot u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe); 48519df918dSFrançois Tigeot u32 val = I915_READ(reg); 486ba55f2f5SFrançois Tigeot u32 port = VIDEO_DIP_PORT(intel_dig_port->port); 48719df918dSFrançois Tigeot 48819df918dSFrançois Tigeot assert_hdmi_port_disabled(intel_hdmi); 48919df918dSFrançois Tigeot 49019df918dSFrançois Tigeot /* See the big comment in g4x_set_infoframes() */ 49119df918dSFrançois Tigeot val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC; 49219df918dSFrançois Tigeot 493ba55f2f5SFrançois Tigeot if (!enable) { 49419df918dSFrançois Tigeot if (!(val & VIDEO_DIP_ENABLE)) 49519df918dSFrançois Tigeot return; 49619df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 49719df918dSFrançois Tigeot I915_WRITE(reg, val); 49819df918dSFrançois Tigeot POSTING_READ(reg); 49919df918dSFrançois Tigeot return; 50019df918dSFrançois Tigeot } 50119df918dSFrançois Tigeot 50219df918dSFrançois Tigeot if (port != (val & VIDEO_DIP_PORT_MASK)) { 50319df918dSFrançois Tigeot if (val & VIDEO_DIP_ENABLE) { 50419df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 50519df918dSFrançois Tigeot I915_WRITE(reg, val); 50619df918dSFrançois Tigeot POSTING_READ(reg); 50719df918dSFrançois Tigeot } 50819df918dSFrançois Tigeot val &= ~VIDEO_DIP_PORT_MASK; 50919df918dSFrançois Tigeot val |= port; 51019df918dSFrançois Tigeot } 51119df918dSFrançois Tigeot 51219df918dSFrançois Tigeot val |= VIDEO_DIP_ENABLE; 51319df918dSFrançois Tigeot val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | 51419df918dSFrançois Tigeot VIDEO_DIP_ENABLE_GCP); 51519df918dSFrançois Tigeot 51619df918dSFrançois Tigeot I915_WRITE(reg, val); 51719df918dSFrançois Tigeot POSTING_READ(reg); 51819df918dSFrançois Tigeot 51919df918dSFrançois Tigeot intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); 52019df918dSFrançois Tigeot intel_hdmi_set_spd_infoframe(encoder); 5219edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); 52219df918dSFrançois Tigeot } 52319df918dSFrançois Tigeot 52419df918dSFrançois Tigeot static void cpt_set_infoframes(struct drm_encoder *encoder, 525ba55f2f5SFrançois Tigeot bool enable, 52619df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 52719df918dSFrançois Tigeot { 52819df918dSFrançois Tigeot struct drm_i915_private *dev_priv = encoder->dev->dev_private; 52919df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 53019df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 53119df918dSFrançois Tigeot u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe); 53219df918dSFrançois Tigeot u32 val = I915_READ(reg); 53319df918dSFrançois Tigeot 53419df918dSFrançois Tigeot assert_hdmi_port_disabled(intel_hdmi); 53519df918dSFrançois Tigeot 53619df918dSFrançois Tigeot /* See the big comment in g4x_set_infoframes() */ 53719df918dSFrançois Tigeot val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC; 53819df918dSFrançois Tigeot 539ba55f2f5SFrançois Tigeot if (!enable) { 54019df918dSFrançois Tigeot if (!(val & VIDEO_DIP_ENABLE)) 54119df918dSFrançois Tigeot return; 54219df918dSFrançois Tigeot val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI); 54319df918dSFrançois Tigeot I915_WRITE(reg, val); 54419df918dSFrançois Tigeot POSTING_READ(reg); 54519df918dSFrançois Tigeot return; 54619df918dSFrançois Tigeot } 54719df918dSFrançois Tigeot 54819df918dSFrançois Tigeot /* Set both together, unset both together: see the spec. */ 54919df918dSFrançois Tigeot val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI; 55019df918dSFrançois Tigeot val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | 55119df918dSFrançois Tigeot VIDEO_DIP_ENABLE_GCP); 55219df918dSFrançois Tigeot 55319df918dSFrançois Tigeot I915_WRITE(reg, val); 55419df918dSFrançois Tigeot POSTING_READ(reg); 55519df918dSFrançois Tigeot 55619df918dSFrançois Tigeot intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); 55719df918dSFrançois Tigeot intel_hdmi_set_spd_infoframe(encoder); 5589edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); 55919df918dSFrançois Tigeot } 56019df918dSFrançois Tigeot 56119df918dSFrançois Tigeot static void vlv_set_infoframes(struct drm_encoder *encoder, 562ba55f2f5SFrançois Tigeot bool enable, 56319df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 56419df918dSFrançois Tigeot { 56519df918dSFrançois Tigeot struct drm_i915_private *dev_priv = encoder->dev->dev_private; 566ba55f2f5SFrançois Tigeot struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); 56719df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 56819df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 56919df918dSFrançois Tigeot u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); 57019df918dSFrançois Tigeot u32 val = I915_READ(reg); 571ba55f2f5SFrançois Tigeot u32 port = VIDEO_DIP_PORT(intel_dig_port->port); 57219df918dSFrançois Tigeot 57319df918dSFrançois Tigeot assert_hdmi_port_disabled(intel_hdmi); 57419df918dSFrançois Tigeot 57519df918dSFrançois Tigeot /* See the big comment in g4x_set_infoframes() */ 57619df918dSFrançois Tigeot val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC; 57719df918dSFrançois Tigeot 578ba55f2f5SFrançois Tigeot if (!enable) { 57919df918dSFrançois Tigeot if (!(val & VIDEO_DIP_ENABLE)) 58019df918dSFrançois Tigeot return; 58119df918dSFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 58219df918dSFrançois Tigeot I915_WRITE(reg, val); 58319df918dSFrançois Tigeot POSTING_READ(reg); 58419df918dSFrançois Tigeot return; 58519df918dSFrançois Tigeot } 58619df918dSFrançois Tigeot 587ba55f2f5SFrançois Tigeot if (port != (val & VIDEO_DIP_PORT_MASK)) { 588ba55f2f5SFrançois Tigeot if (val & VIDEO_DIP_ENABLE) { 589ba55f2f5SFrançois Tigeot val &= ~VIDEO_DIP_ENABLE; 590ba55f2f5SFrançois Tigeot I915_WRITE(reg, val); 591ba55f2f5SFrançois Tigeot POSTING_READ(reg); 592ba55f2f5SFrançois Tigeot } 593ba55f2f5SFrançois Tigeot val &= ~VIDEO_DIP_PORT_MASK; 594ba55f2f5SFrançois Tigeot val |= port; 595ba55f2f5SFrançois Tigeot } 596ba55f2f5SFrançois Tigeot 59719df918dSFrançois Tigeot val |= VIDEO_DIP_ENABLE; 598ba55f2f5SFrançois Tigeot val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR | 599ba55f2f5SFrançois Tigeot VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP); 60019df918dSFrançois Tigeot 60119df918dSFrançois Tigeot I915_WRITE(reg, val); 60219df918dSFrançois Tigeot POSTING_READ(reg); 60319df918dSFrançois Tigeot 60419df918dSFrançois Tigeot intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); 60519df918dSFrançois Tigeot intel_hdmi_set_spd_infoframe(encoder); 6069edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); 60719df918dSFrançois Tigeot } 60819df918dSFrançois Tigeot 60919df918dSFrançois Tigeot static void hsw_set_infoframes(struct drm_encoder *encoder, 610ba55f2f5SFrançois Tigeot bool enable, 61119df918dSFrançois Tigeot struct drm_display_mode *adjusted_mode) 61219df918dSFrançois Tigeot { 61319df918dSFrançois Tigeot struct drm_i915_private *dev_priv = encoder->dev->dev_private; 61419df918dSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 61519df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 6168e26cdf6SFrançois Tigeot u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder); 61719df918dSFrançois Tigeot u32 val = I915_READ(reg); 61819df918dSFrançois Tigeot 61919df918dSFrançois Tigeot assert_hdmi_port_disabled(intel_hdmi); 62019df918dSFrançois Tigeot 621ba55f2f5SFrançois Tigeot if (!enable) { 62219df918dSFrançois Tigeot I915_WRITE(reg, 0); 62319df918dSFrançois Tigeot POSTING_READ(reg); 62419df918dSFrançois Tigeot return; 62519df918dSFrançois Tigeot } 62619df918dSFrançois Tigeot 62719df918dSFrançois Tigeot val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW | 62819df918dSFrançois Tigeot VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW); 62919df918dSFrançois Tigeot 63019df918dSFrançois Tigeot I915_WRITE(reg, val); 63119df918dSFrançois Tigeot POSTING_READ(reg); 63219df918dSFrançois Tigeot 63319df918dSFrançois Tigeot intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); 63419df918dSFrançois Tigeot intel_hdmi_set_spd_infoframe(encoder); 6359edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); 63619df918dSFrançois Tigeot } 63719df918dSFrançois Tigeot 638ba55f2f5SFrançois Tigeot static void intel_hdmi_prepare(struct intel_encoder *encoder) 639e3adcf8fSFrançois Tigeot { 6409edbd4a0SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 641e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 6429edbd4a0SFrançois Tigeot struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); 6439edbd4a0SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 6449edbd4a0SFrançois Tigeot struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode; 6458e26cdf6SFrançois Tigeot u32 hdmi_val; 646e3adcf8fSFrançois Tigeot 6478e26cdf6SFrançois Tigeot hdmi_val = SDVO_ENCODING_HDMI; 6485d0b1887SFrançois Tigeot if (!HAS_PCH_SPLIT(dev)) 6498e26cdf6SFrançois Tigeot hdmi_val |= intel_hdmi->color_range; 650e3adcf8fSFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 6518e26cdf6SFrançois Tigeot hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; 652e3adcf8fSFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 6538e26cdf6SFrançois Tigeot hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH; 654e3adcf8fSFrançois Tigeot 6559edbd4a0SFrançois Tigeot if (crtc->config.pipe_bpp > 24) 6568e26cdf6SFrançois Tigeot hdmi_val |= HDMI_COLOR_FORMAT_12bpc; 657e3adcf8fSFrançois Tigeot else 6588e26cdf6SFrançois Tigeot hdmi_val |= SDVO_COLOR_FORMAT_8bpc; 659e3adcf8fSFrançois Tigeot 660ba55f2f5SFrançois Tigeot if (crtc->config.has_hdmi_sink) 6618e26cdf6SFrançois Tigeot hdmi_val |= HDMI_MODE_SELECT_HDMI; 662e3adcf8fSFrançois Tigeot 663ba55f2f5SFrançois Tigeot if (crtc->config.has_audio) { 664ba55f2f5SFrançois Tigeot WARN_ON(!crtc->config.has_hdmi_sink); 66519df918dSFrançois Tigeot DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", 6669edbd4a0SFrançois Tigeot pipe_name(crtc->pipe)); 6678e26cdf6SFrançois Tigeot hdmi_val |= SDVO_AUDIO_ENABLE; 6689edbd4a0SFrançois Tigeot intel_write_eld(&encoder->base, adjusted_mode); 669e3adcf8fSFrançois Tigeot } 670e3adcf8fSFrançois Tigeot 671e3adcf8fSFrançois Tigeot if (HAS_PCH_CPT(dev)) 6729edbd4a0SFrançois Tigeot hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe); 673ba55f2f5SFrançois Tigeot else if (IS_CHERRYVIEW(dev)) 674ba55f2f5SFrançois Tigeot hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe); 6758e26cdf6SFrançois Tigeot else 6769edbd4a0SFrançois Tigeot hdmi_val |= SDVO_PIPE_SEL(crtc->pipe); 677e3adcf8fSFrançois Tigeot 6788e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val); 6798e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 680e3adcf8fSFrançois Tigeot } 681e3adcf8fSFrançois Tigeot 68219df918dSFrançois Tigeot static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, 68319df918dSFrançois Tigeot enum i915_pipe *pipe) 684e3adcf8fSFrançois Tigeot { 68519df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 686e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 68719df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 688ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 68919df918dSFrançois Tigeot u32 tmp; 69019df918dSFrançois Tigeot 691ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(encoder); 692ba55f2f5SFrançois Tigeot if (!intel_display_power_enabled(dev_priv, power_domain)) 693ba55f2f5SFrançois Tigeot return false; 694ba55f2f5SFrançois Tigeot 6958e26cdf6SFrançois Tigeot tmp = I915_READ(intel_hdmi->hdmi_reg); 69619df918dSFrançois Tigeot 69719df918dSFrançois Tigeot if (!(tmp & SDVO_ENABLE)) 69819df918dSFrançois Tigeot return false; 69919df918dSFrançois Tigeot 70019df918dSFrançois Tigeot if (HAS_PCH_CPT(dev)) 70119df918dSFrançois Tigeot *pipe = PORT_TO_PIPE_CPT(tmp); 702ba55f2f5SFrançois Tigeot else if (IS_CHERRYVIEW(dev)) 703ba55f2f5SFrançois Tigeot *pipe = SDVO_PORT_TO_PIPE_CHV(tmp); 70419df918dSFrançois Tigeot else 70519df918dSFrançois Tigeot *pipe = PORT_TO_PIPE(tmp); 70619df918dSFrançois Tigeot 70719df918dSFrançois Tigeot return true; 70819df918dSFrançois Tigeot } 70919df918dSFrançois Tigeot 7105d0b1887SFrançois Tigeot static void intel_hdmi_get_config(struct intel_encoder *encoder, 7115d0b1887SFrançois Tigeot struct intel_crtc_config *pipe_config) 7125d0b1887SFrançois Tigeot { 7135d0b1887SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 714*24edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 715*24edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 7165d0b1887SFrançois Tigeot u32 tmp, flags = 0; 7179edbd4a0SFrançois Tigeot int dotclock; 7185d0b1887SFrançois Tigeot 7195d0b1887SFrançois Tigeot tmp = I915_READ(intel_hdmi->hdmi_reg); 7205d0b1887SFrançois Tigeot 7215d0b1887SFrançois Tigeot if (tmp & SDVO_HSYNC_ACTIVE_HIGH) 7225d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PHSYNC; 7235d0b1887SFrançois Tigeot else 7245d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NHSYNC; 7255d0b1887SFrançois Tigeot 7265d0b1887SFrançois Tigeot if (tmp & SDVO_VSYNC_ACTIVE_HIGH) 7275d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PVSYNC; 7285d0b1887SFrançois Tigeot else 7295d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NVSYNC; 7305d0b1887SFrançois Tigeot 731ba55f2f5SFrançois Tigeot if (tmp & HDMI_MODE_SELECT_HDMI) 732ba55f2f5SFrançois Tigeot pipe_config->has_hdmi_sink = true; 733ba55f2f5SFrançois Tigeot 734*24edb884SFrançois Tigeot if (tmp & SDVO_AUDIO_ENABLE) 735ba55f2f5SFrançois Tigeot pipe_config->has_audio = true; 736ba55f2f5SFrançois Tigeot 737*24edb884SFrançois Tigeot if (!HAS_PCH_SPLIT(dev) && 738*24edb884SFrançois Tigeot tmp & HDMI_COLOR_RANGE_16_235) 739*24edb884SFrançois Tigeot pipe_config->limited_color_range = true; 740*24edb884SFrançois Tigeot 7415d0b1887SFrançois Tigeot pipe_config->adjusted_mode.flags |= flags; 7429edbd4a0SFrançois Tigeot 7439edbd4a0SFrançois Tigeot if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc) 7449edbd4a0SFrançois Tigeot dotclock = pipe_config->port_clock * 2 / 3; 7459edbd4a0SFrançois Tigeot else 7469edbd4a0SFrançois Tigeot dotclock = pipe_config->port_clock; 7479edbd4a0SFrançois Tigeot 7489edbd4a0SFrançois Tigeot if (HAS_PCH_SPLIT(dev_priv->dev)) 7499edbd4a0SFrançois Tigeot ironlake_check_encoder_dotclock(pipe_config, dotclock); 7509edbd4a0SFrançois Tigeot 7519edbd4a0SFrançois Tigeot pipe_config->adjusted_mode.crtc_clock = dotclock; 7525d0b1887SFrançois Tigeot } 7535d0b1887SFrançois Tigeot 75419df918dSFrançois Tigeot static void intel_enable_hdmi(struct intel_encoder *encoder) 75519df918dSFrançois Tigeot { 75619df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 75719df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 7588e26cdf6SFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); 75919df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 760e3adcf8fSFrançois Tigeot u32 temp; 761e3adcf8fSFrançois Tigeot u32 enable_bits = SDVO_ENABLE; 762e3adcf8fSFrançois Tigeot 763ba55f2f5SFrançois Tigeot if (intel_crtc->config.has_audio) 764e3adcf8fSFrançois Tigeot enable_bits |= SDVO_AUDIO_ENABLE; 765e3adcf8fSFrançois Tigeot 7668e26cdf6SFrançois Tigeot temp = I915_READ(intel_hdmi->hdmi_reg); 767e3adcf8fSFrançois Tigeot 76819df918dSFrançois Tigeot /* HW workaround for IBX, we need to move the port to transcoder A 7698e26cdf6SFrançois Tigeot * before disabling it, so restore the transcoder select bit here. */ 7708e26cdf6SFrançois Tigeot if (HAS_PCH_IBX(dev)) 7718e26cdf6SFrançois Tigeot enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe); 77219df918dSFrançois Tigeot 773e3adcf8fSFrançois Tigeot /* HW workaround, need to toggle enable bit off and on for 12bpc, but 774e3adcf8fSFrançois Tigeot * we do this anyway which shows more stable in testing. 775e3adcf8fSFrançois Tigeot */ 776e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 7778e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); 7788e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 779e3adcf8fSFrançois Tigeot } 780e3adcf8fSFrançois Tigeot 781e3adcf8fSFrançois Tigeot temp |= enable_bits; 78219df918dSFrançois Tigeot 7838e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 7848e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 78519df918dSFrançois Tigeot 78619df918dSFrançois Tigeot /* HW workaround, need to write this twice for issue that may result 78719df918dSFrançois Tigeot * in first write getting masked. 78819df918dSFrançois Tigeot */ 78919df918dSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 7908e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 7918e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 792e3adcf8fSFrançois Tigeot } 7935d0b1887SFrançois Tigeot } 7949edbd4a0SFrançois Tigeot 7959edbd4a0SFrançois Tigeot static void vlv_enable_hdmi(struct intel_encoder *encoder) 7969edbd4a0SFrançois Tigeot { 79719df918dSFrançois Tigeot } 79819df918dSFrançois Tigeot 79919df918dSFrançois Tigeot static void intel_disable_hdmi(struct intel_encoder *encoder) 80019df918dSFrançois Tigeot { 80119df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 80219df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 80319df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 80419df918dSFrançois Tigeot u32 temp; 80519df918dSFrançois Tigeot u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE; 80619df918dSFrançois Tigeot 8078e26cdf6SFrançois Tigeot temp = I915_READ(intel_hdmi->hdmi_reg); 80819df918dSFrançois Tigeot 80919df918dSFrançois Tigeot /* HW workaround for IBX, we need to move the port to transcoder A 81019df918dSFrançois Tigeot * before disabling it. */ 81119df918dSFrançois Tigeot if (HAS_PCH_IBX(dev)) { 81219df918dSFrançois Tigeot struct drm_crtc *crtc = encoder->base.crtc; 81319df918dSFrançois Tigeot int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; 81419df918dSFrançois Tigeot 81519df918dSFrançois Tigeot if (temp & SDVO_PIPE_B_SELECT) { 81619df918dSFrançois Tigeot temp &= ~SDVO_PIPE_B_SELECT; 8178e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 8188e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 81919df918dSFrançois Tigeot 82019df918dSFrançois Tigeot /* Again we need to write this twice. */ 8218e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 8228e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 82319df918dSFrançois Tigeot 82419df918dSFrançois Tigeot /* Transcoder selection bits only update 82519df918dSFrançois Tigeot * effectively on vblank. */ 82619df918dSFrançois Tigeot if (crtc) 82719df918dSFrançois Tigeot intel_wait_for_vblank(dev, pipe); 82819df918dSFrançois Tigeot else 82919df918dSFrançois Tigeot msleep(50); 83019df918dSFrançois Tigeot } 83119df918dSFrançois Tigeot } 83219df918dSFrançois Tigeot 83319df918dSFrançois Tigeot /* HW workaround, need to toggle enable bit off and on for 12bpc, but 83419df918dSFrançois Tigeot * we do this anyway which shows more stable in testing. 83519df918dSFrançois Tigeot */ 83619df918dSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 8378e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); 8388e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 83919df918dSFrançois Tigeot } 84019df918dSFrançois Tigeot 84119df918dSFrançois Tigeot temp &= ~enable_bits; 842e3adcf8fSFrançois Tigeot 8438e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 8448e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 845e3adcf8fSFrançois Tigeot 846e3adcf8fSFrançois Tigeot /* HW workaround, need to write this twice for issue that may result 847e3adcf8fSFrançois Tigeot * in first write getting masked. 848e3adcf8fSFrançois Tigeot */ 849e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 8508e26cdf6SFrançois Tigeot I915_WRITE(intel_hdmi->hdmi_reg, temp); 8518e26cdf6SFrançois Tigeot POSTING_READ(intel_hdmi->hdmi_reg); 852e3adcf8fSFrançois Tigeot } 853e3adcf8fSFrançois Tigeot } 854e3adcf8fSFrançois Tigeot 855ba55f2f5SFrançois Tigeot static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) 8565d0b1887SFrançois Tigeot { 8575d0b1887SFrançois Tigeot struct drm_device *dev = intel_hdmi_to_dev(hdmi); 8585d0b1887SFrançois Tigeot 859ba55f2f5SFrançois Tigeot if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev)) 8605d0b1887SFrançois Tigeot return 165000; 8619edbd4a0SFrançois Tigeot else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) 8625d0b1887SFrançois Tigeot return 300000; 8635d0b1887SFrançois Tigeot else 8645d0b1887SFrançois Tigeot return 225000; 8655d0b1887SFrançois Tigeot } 8665d0b1887SFrançois Tigeot 8679edbd4a0SFrançois Tigeot static enum drm_mode_status 8689edbd4a0SFrançois Tigeot intel_hdmi_mode_valid(struct drm_connector *connector, 869e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 870e3adcf8fSFrançois Tigeot { 871ba55f2f5SFrançois Tigeot if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector), 872ba55f2f5SFrançois Tigeot true)) 873e3adcf8fSFrançois Tigeot return MODE_CLOCK_HIGH; 874e3adcf8fSFrançois Tigeot if (mode->clock < 20000) 875e3adcf8fSFrançois Tigeot return MODE_CLOCK_LOW; 876e3adcf8fSFrançois Tigeot 877e3adcf8fSFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 878e3adcf8fSFrançois Tigeot return MODE_NO_DBLESCAN; 879e3adcf8fSFrançois Tigeot 880e3adcf8fSFrançois Tigeot return MODE_OK; 881e3adcf8fSFrançois Tigeot } 882e3adcf8fSFrançois Tigeot 883ba55f2f5SFrançois Tigeot static bool hdmi_12bpc_possible(struct intel_crtc *crtc) 884ba55f2f5SFrançois Tigeot { 885ba55f2f5SFrançois Tigeot struct drm_device *dev = crtc->base.dev; 886ba55f2f5SFrançois Tigeot struct intel_encoder *encoder; 887ba55f2f5SFrançois Tigeot int count = 0, count_hdmi = 0; 888ba55f2f5SFrançois Tigeot 889*24edb884SFrançois Tigeot if (HAS_GMCH_DISPLAY(dev)) 890ba55f2f5SFrançois Tigeot return false; 891ba55f2f5SFrançois Tigeot 892ba55f2f5SFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { 893ba55f2f5SFrançois Tigeot if (encoder->new_crtc != crtc) 894ba55f2f5SFrançois Tigeot continue; 895ba55f2f5SFrançois Tigeot 896ba55f2f5SFrançois Tigeot count_hdmi += encoder->type == INTEL_OUTPUT_HDMI; 897ba55f2f5SFrançois Tigeot count++; 898ba55f2f5SFrançois Tigeot } 899ba55f2f5SFrançois Tigeot 900ba55f2f5SFrançois Tigeot /* 901ba55f2f5SFrançois Tigeot * HDMI 12bpc affects the clocks, so it's only possible 902ba55f2f5SFrançois Tigeot * when not cloning with other encoder types. 903ba55f2f5SFrançois Tigeot */ 904ba55f2f5SFrançois Tigeot return count_hdmi > 0 && count_hdmi == count; 905ba55f2f5SFrançois Tigeot } 906ba55f2f5SFrançois Tigeot 9078e26cdf6SFrançois Tigeot bool intel_hdmi_compute_config(struct intel_encoder *encoder, 9088e26cdf6SFrançois Tigeot struct intel_crtc_config *pipe_config) 909e3adcf8fSFrançois Tigeot { 9108e26cdf6SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 9118e26cdf6SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 9128e26cdf6SFrançois Tigeot struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; 9139edbd4a0SFrançois Tigeot int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2; 914ba55f2f5SFrançois Tigeot int portclock_limit = hdmi_portclock_limit(intel_hdmi, false); 9155d0b1887SFrançois Tigeot int desired_bpp; 916a2fdbec6SFrançois Tigeot 917ba55f2f5SFrançois Tigeot pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink; 918ba55f2f5SFrançois Tigeot 919a2fdbec6SFrançois Tigeot if (intel_hdmi->color_range_auto) { 920a2fdbec6SFrançois Tigeot /* See CEA-861-E - 5.1 Default Encoding Parameters */ 921ba55f2f5SFrançois Tigeot if (pipe_config->has_hdmi_sink && 922a2fdbec6SFrançois Tigeot drm_match_cea_mode(adjusted_mode) > 1) 9238e26cdf6SFrançois Tigeot intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; 924a2fdbec6SFrançois Tigeot else 925a2fdbec6SFrançois Tigeot intel_hdmi->color_range = 0; 926a2fdbec6SFrançois Tigeot } 927a2fdbec6SFrançois Tigeot 928a2fdbec6SFrançois Tigeot if (intel_hdmi->color_range) 9298e26cdf6SFrançois Tigeot pipe_config->limited_color_range = true; 9308e26cdf6SFrançois Tigeot 9318e26cdf6SFrançois Tigeot if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev)) 9328e26cdf6SFrançois Tigeot pipe_config->has_pch_encoder = true; 9338e26cdf6SFrançois Tigeot 934ba55f2f5SFrançois Tigeot if (pipe_config->has_hdmi_sink && intel_hdmi->has_audio) 935ba55f2f5SFrançois Tigeot pipe_config->has_audio = true; 936ba55f2f5SFrançois Tigeot 9378e26cdf6SFrançois Tigeot /* 9388e26cdf6SFrançois Tigeot * HDMI is either 12 or 8, so if the display lets 10bpc sneak 9398e26cdf6SFrançois Tigeot * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi 9405d0b1887SFrançois Tigeot * outputs. We also need to check that the higher clock still fits 9415d0b1887SFrançois Tigeot * within limits. 9428e26cdf6SFrançois Tigeot */ 943ba55f2f5SFrançois Tigeot if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink && 944ba55f2f5SFrançois Tigeot clock_12bpc <= portclock_limit && 945ba55f2f5SFrançois Tigeot hdmi_12bpc_possible(encoder->new_crtc)) { 9465d0b1887SFrançois Tigeot DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); 9475d0b1887SFrançois Tigeot desired_bpp = 12*3; 9485d0b1887SFrançois Tigeot 9495d0b1887SFrançois Tigeot /* Need to adjust the port link by 1.5x for 12bpc. */ 9505d0b1887SFrançois Tigeot pipe_config->port_clock = clock_12bpc; 9518e26cdf6SFrançois Tigeot } else { 9525d0b1887SFrançois Tigeot DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n"); 9535d0b1887SFrançois Tigeot desired_bpp = 8*3; 9545d0b1887SFrançois Tigeot } 9555d0b1887SFrançois Tigeot 9565d0b1887SFrançois Tigeot if (!pipe_config->bw_constrained) { 9575d0b1887SFrançois Tigeot DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp); 9585d0b1887SFrançois Tigeot pipe_config->pipe_bpp = desired_bpp; 9595d0b1887SFrançois Tigeot } 9605d0b1887SFrançois Tigeot 9619edbd4a0SFrançois Tigeot if (adjusted_mode->crtc_clock > portclock_limit) { 9625d0b1887SFrançois Tigeot DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n"); 9635d0b1887SFrançois Tigeot return false; 9648e26cdf6SFrançois Tigeot } 965a2fdbec6SFrançois Tigeot 966e3adcf8fSFrançois Tigeot return true; 967e3adcf8fSFrançois Tigeot } 968e3adcf8fSFrançois Tigeot 969e3adcf8fSFrançois Tigeot static enum drm_connector_status 970e3adcf8fSFrançois Tigeot intel_hdmi_detect(struct drm_connector *connector, bool force) 971e3adcf8fSFrançois Tigeot { 972a2fdbec6SFrançois Tigeot struct drm_device *dev = connector->dev; 973e3adcf8fSFrançois Tigeot struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); 97419df918dSFrançois Tigeot struct intel_digital_port *intel_dig_port = 97519df918dSFrançois Tigeot hdmi_to_dig_port(intel_hdmi); 97619df918dSFrançois Tigeot struct intel_encoder *intel_encoder = &intel_dig_port->base; 977a2fdbec6SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 978e3adcf8fSFrançois Tigeot struct edid *edid; 979ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 980e3adcf8fSFrançois Tigeot enum drm_connector_status status = connector_status_disconnected; 981e3adcf8fSFrançois Tigeot 9829edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 983b4efbf42Szrj connector->base.id, connector->name); 984ba55f2f5SFrançois Tigeot 985ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 986ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 9879edbd4a0SFrançois Tigeot 988e3adcf8fSFrançois Tigeot intel_hdmi->has_hdmi_sink = false; 989e3adcf8fSFrançois Tigeot intel_hdmi->has_audio = false; 990a2fdbec6SFrançois Tigeot intel_hdmi->rgb_quant_range_selectable = false; 99119df918dSFrançois Tigeot edid = drm_get_edid(connector, 99219df918dSFrançois Tigeot intel_gmbus_get_adapter(dev_priv, 99319df918dSFrançois Tigeot intel_hdmi->ddc_bus)); 994e3adcf8fSFrançois Tigeot 995e3adcf8fSFrançois Tigeot if (edid) { 996e3adcf8fSFrançois Tigeot if (edid->input & DRM_EDID_INPUT_DIGITAL) { 997e3adcf8fSFrançois Tigeot status = connector_status_connected; 998e3adcf8fSFrançois Tigeot if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) 999e3adcf8fSFrançois Tigeot intel_hdmi->has_hdmi_sink = 1000e3adcf8fSFrançois Tigeot drm_detect_hdmi_monitor(edid); 1001e3adcf8fSFrançois Tigeot intel_hdmi->has_audio = drm_detect_monitor_audio(edid); 1002a2fdbec6SFrançois Tigeot intel_hdmi->rgb_quant_range_selectable = 1003a2fdbec6SFrançois Tigeot drm_rgb_quant_range_selectable(edid); 1004e3adcf8fSFrançois Tigeot } 10058e26cdf6SFrançois Tigeot kfree(edid); 1006e3adcf8fSFrançois Tigeot } 1007e3adcf8fSFrançois Tigeot 1008e3adcf8fSFrançois Tigeot if (status == connector_status_connected) { 1009e3adcf8fSFrançois Tigeot if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO) 1010e3adcf8fSFrançois Tigeot intel_hdmi->has_audio = 1011e3adcf8fSFrançois Tigeot (intel_hdmi->force_audio == HDMI_AUDIO_ON); 101219df918dSFrançois Tigeot intel_encoder->type = INTEL_OUTPUT_HDMI; 1013e3adcf8fSFrançois Tigeot } 1014e3adcf8fSFrançois Tigeot 1015ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 1016ba55f2f5SFrançois Tigeot 1017e3adcf8fSFrançois Tigeot return status; 1018e3adcf8fSFrançois Tigeot } 1019e3adcf8fSFrançois Tigeot 1020e3adcf8fSFrançois Tigeot static int intel_hdmi_get_modes(struct drm_connector *connector) 1021e3adcf8fSFrançois Tigeot { 1022ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = intel_attached_encoder(connector); 1023ba55f2f5SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); 1024e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = connector->dev->dev_private; 1025ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 1026ba55f2f5SFrançois Tigeot int ret; 1027e3adcf8fSFrançois Tigeot 1028e3adcf8fSFrançois Tigeot /* We should parse the EDID data and find out if it's an HDMI sink so 1029e3adcf8fSFrançois Tigeot * we can send audio to it. 1030e3adcf8fSFrançois Tigeot */ 1031e3adcf8fSFrançois Tigeot 1032ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 1033ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 1034ba55f2f5SFrançois Tigeot 1035ba55f2f5SFrançois Tigeot ret = intel_ddc_get_modes(connector, 103619df918dSFrançois Tigeot intel_gmbus_get_adapter(dev_priv, 103719df918dSFrançois Tigeot intel_hdmi->ddc_bus)); 1038ba55f2f5SFrançois Tigeot 1039ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 1040ba55f2f5SFrançois Tigeot 1041ba55f2f5SFrançois Tigeot return ret; 1042e3adcf8fSFrançois Tigeot } 1043e3adcf8fSFrançois Tigeot 1044e3adcf8fSFrançois Tigeot static bool 1045e3adcf8fSFrançois Tigeot intel_hdmi_detect_audio(struct drm_connector *connector) 1046e3adcf8fSFrançois Tigeot { 1047ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = intel_attached_encoder(connector); 1048ba55f2f5SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); 1049e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = connector->dev->dev_private; 1050ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 1051e3adcf8fSFrançois Tigeot struct edid *edid; 1052e3adcf8fSFrançois Tigeot bool has_audio = false; 1053e3adcf8fSFrançois Tigeot 1054ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 1055ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 1056ba55f2f5SFrançois Tigeot 105719df918dSFrançois Tigeot edid = drm_get_edid(connector, 105819df918dSFrançois Tigeot intel_gmbus_get_adapter(dev_priv, 105919df918dSFrançois Tigeot intel_hdmi->ddc_bus)); 1060e3adcf8fSFrançois Tigeot if (edid) { 1061e3adcf8fSFrançois Tigeot if (edid->input & DRM_EDID_INPUT_DIGITAL) 1062e3adcf8fSFrançois Tigeot has_audio = drm_detect_monitor_audio(edid); 10638e26cdf6SFrançois Tigeot kfree(edid); 1064e3adcf8fSFrançois Tigeot } 1065e3adcf8fSFrançois Tigeot 1066ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 1067ba55f2f5SFrançois Tigeot 1068e3adcf8fSFrançois Tigeot return has_audio; 1069e3adcf8fSFrançois Tigeot } 1070e3adcf8fSFrançois Tigeot 1071e3adcf8fSFrançois Tigeot static int 1072e3adcf8fSFrançois Tigeot intel_hdmi_set_property(struct drm_connector *connector, 1073e3adcf8fSFrançois Tigeot struct drm_property *property, 1074e3adcf8fSFrançois Tigeot uint64_t val) 1075e3adcf8fSFrançois Tigeot { 1076e3adcf8fSFrançois Tigeot struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); 107719df918dSFrançois Tigeot struct intel_digital_port *intel_dig_port = 107819df918dSFrançois Tigeot hdmi_to_dig_port(intel_hdmi); 1079e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = connector->dev->dev_private; 1080e3adcf8fSFrançois Tigeot int ret; 1081e3adcf8fSFrançois Tigeot 1082b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, property, val); 1083e3adcf8fSFrançois Tigeot if (ret) 1084e3adcf8fSFrançois Tigeot return ret; 1085e3adcf8fSFrançois Tigeot 1086e3adcf8fSFrançois Tigeot if (property == dev_priv->force_audio_property) { 1087e3adcf8fSFrançois Tigeot enum hdmi_force_audio i = val; 1088e3adcf8fSFrançois Tigeot bool has_audio; 1089e3adcf8fSFrançois Tigeot 1090e3adcf8fSFrançois Tigeot if (i == intel_hdmi->force_audio) 1091e3adcf8fSFrançois Tigeot return 0; 1092e3adcf8fSFrançois Tigeot 1093e3adcf8fSFrançois Tigeot intel_hdmi->force_audio = i; 1094e3adcf8fSFrançois Tigeot 1095e3adcf8fSFrançois Tigeot if (i == HDMI_AUDIO_AUTO) 1096e3adcf8fSFrançois Tigeot has_audio = intel_hdmi_detect_audio(connector); 1097e3adcf8fSFrançois Tigeot else 1098e3adcf8fSFrançois Tigeot has_audio = (i == HDMI_AUDIO_ON); 1099e3adcf8fSFrançois Tigeot 1100e3adcf8fSFrançois Tigeot if (i == HDMI_AUDIO_OFF_DVI) 1101e3adcf8fSFrançois Tigeot intel_hdmi->has_hdmi_sink = 0; 1102e3adcf8fSFrançois Tigeot 1103e3adcf8fSFrançois Tigeot intel_hdmi->has_audio = has_audio; 1104e3adcf8fSFrançois Tigeot goto done; 1105e3adcf8fSFrançois Tigeot } 1106e3adcf8fSFrançois Tigeot 1107e3adcf8fSFrançois Tigeot if (property == dev_priv->broadcast_rgb_property) { 11088e26cdf6SFrançois Tigeot bool old_auto = intel_hdmi->color_range_auto; 11098e26cdf6SFrançois Tigeot uint32_t old_range = intel_hdmi->color_range; 11108e26cdf6SFrançois Tigeot 1111a2fdbec6SFrançois Tigeot switch (val) { 1112a2fdbec6SFrançois Tigeot case INTEL_BROADCAST_RGB_AUTO: 1113a2fdbec6SFrançois Tigeot intel_hdmi->color_range_auto = true; 1114a2fdbec6SFrançois Tigeot break; 1115a2fdbec6SFrançois Tigeot case INTEL_BROADCAST_RGB_FULL: 1116a2fdbec6SFrançois Tigeot intel_hdmi->color_range_auto = false; 1117a2fdbec6SFrançois Tigeot intel_hdmi->color_range = 0; 1118a2fdbec6SFrançois Tigeot break; 1119a2fdbec6SFrançois Tigeot case INTEL_BROADCAST_RGB_LIMITED: 1120a2fdbec6SFrançois Tigeot intel_hdmi->color_range_auto = false; 11218e26cdf6SFrançois Tigeot intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; 1122a2fdbec6SFrançois Tigeot break; 1123a2fdbec6SFrançois Tigeot default: 1124a2fdbec6SFrançois Tigeot return -EINVAL; 1125a2fdbec6SFrançois Tigeot } 11268e26cdf6SFrançois Tigeot 11278e26cdf6SFrançois Tigeot if (old_auto == intel_hdmi->color_range_auto && 11288e26cdf6SFrançois Tigeot old_range == intel_hdmi->color_range) 11298e26cdf6SFrançois Tigeot return 0; 11308e26cdf6SFrançois Tigeot 1131e3adcf8fSFrançois Tigeot goto done; 1132e3adcf8fSFrançois Tigeot } 1133e3adcf8fSFrançois Tigeot 1134*24edb884SFrançois Tigeot if (property == connector->dev->mode_config.aspect_ratio_property) { 1135*24edb884SFrançois Tigeot switch (val) { 1136*24edb884SFrançois Tigeot case DRM_MODE_PICTURE_ASPECT_NONE: 1137*24edb884SFrançois Tigeot intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; 1138*24edb884SFrançois Tigeot break; 1139*24edb884SFrançois Tigeot case DRM_MODE_PICTURE_ASPECT_4_3: 1140*24edb884SFrançois Tigeot intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3; 1141*24edb884SFrançois Tigeot break; 1142*24edb884SFrançois Tigeot case DRM_MODE_PICTURE_ASPECT_16_9: 1143*24edb884SFrançois Tigeot intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9; 1144*24edb884SFrançois Tigeot break; 1145*24edb884SFrançois Tigeot default: 1146*24edb884SFrançois Tigeot return -EINVAL; 1147*24edb884SFrançois Tigeot } 1148*24edb884SFrançois Tigeot goto done; 1149*24edb884SFrançois Tigeot } 1150*24edb884SFrançois Tigeot 1151e3adcf8fSFrançois Tigeot return -EINVAL; 1152e3adcf8fSFrançois Tigeot 1153e3adcf8fSFrançois Tigeot done: 1154a2fdbec6SFrançois Tigeot if (intel_dig_port->base.base.crtc) 1155a2fdbec6SFrançois Tigeot intel_crtc_restore_mode(intel_dig_port->base.base.crtc); 1156e3adcf8fSFrançois Tigeot 1157e3adcf8fSFrançois Tigeot return 0; 1158e3adcf8fSFrançois Tigeot } 1159e3adcf8fSFrançois Tigeot 1160ba55f2f5SFrançois Tigeot static void intel_hdmi_pre_enable(struct intel_encoder *encoder) 1161ba55f2f5SFrançois Tigeot { 1162ba55f2f5SFrançois Tigeot struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 1163ba55f2f5SFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); 1164ba55f2f5SFrançois Tigeot struct drm_display_mode *adjusted_mode = 1165ba55f2f5SFrançois Tigeot &intel_crtc->config.adjusted_mode; 1166ba55f2f5SFrançois Tigeot 1167ba55f2f5SFrançois Tigeot intel_hdmi_prepare(encoder); 1168ba55f2f5SFrançois Tigeot 1169ba55f2f5SFrançois Tigeot intel_hdmi->set_infoframes(&encoder->base, 1170ba55f2f5SFrançois Tigeot intel_crtc->config.has_hdmi_sink, 1171ba55f2f5SFrançois Tigeot adjusted_mode); 1172ba55f2f5SFrançois Tigeot } 1173ba55f2f5SFrançois Tigeot 11749edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) 11755d0b1887SFrançois Tigeot { 11765d0b1887SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 1177ba55f2f5SFrançois Tigeot struct intel_hdmi *intel_hdmi = &dport->hdmi; 11785d0b1887SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 11795d0b1887SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 11805d0b1887SFrançois Tigeot struct intel_crtc *intel_crtc = 11815d0b1887SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 1182ba55f2f5SFrançois Tigeot struct drm_display_mode *adjusted_mode = 1183ba55f2f5SFrançois Tigeot &intel_crtc->config.adjusted_mode; 11849edbd4a0SFrançois Tigeot enum dpio_channel port = vlv_dport_to_channel(dport); 11855d0b1887SFrançois Tigeot int pipe = intel_crtc->pipe; 11865d0b1887SFrançois Tigeot u32 val; 11875d0b1887SFrançois Tigeot 11885d0b1887SFrançois Tigeot /* Enable clock channels for this port */ 11899edbd4a0SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 11909edbd4a0SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port)); 11915d0b1887SFrançois Tigeot val = 0; 11925d0b1887SFrançois Tigeot if (pipe) 11935d0b1887SFrançois Tigeot val |= (1<<21); 11945d0b1887SFrançois Tigeot else 11955d0b1887SFrançois Tigeot val &= ~(1<<21); 11965d0b1887SFrançois Tigeot val |= 0x001000c4; 11979edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW8(port), val); 11985d0b1887SFrançois Tigeot 11995d0b1887SFrançois Tigeot /* HDMI 1.0V-2dB */ 12009edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), 0); 12019edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW4(port), 0x2b245f5f); 12029edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(port), 0x5578b83a); 12039edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(port), 0x0c782040); 12049edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX3_DW4(port), 0x2b247878); 12059edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW11(port), 0x00030000); 12069edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000); 12079edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN); 12085d0b1887SFrançois Tigeot 12095d0b1887SFrançois Tigeot /* Program lane clock */ 12109edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018); 12119edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888); 12129edbd4a0SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 12139edbd4a0SFrançois Tigeot 1214ba55f2f5SFrançois Tigeot intel_hdmi->set_infoframes(&encoder->base, 1215ba55f2f5SFrançois Tigeot intel_crtc->config.has_hdmi_sink, 1216ba55f2f5SFrançois Tigeot adjusted_mode); 1217ba55f2f5SFrançois Tigeot 12189edbd4a0SFrançois Tigeot intel_enable_hdmi(encoder); 12199edbd4a0SFrançois Tigeot 12209edbd4a0SFrançois Tigeot vlv_wait_port_ready(dev_priv, dport); 12215d0b1887SFrançois Tigeot } 12225d0b1887SFrançois Tigeot 12239edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) 12245d0b1887SFrançois Tigeot { 12255d0b1887SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 12265d0b1887SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 12275d0b1887SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 12289edbd4a0SFrançois Tigeot struct intel_crtc *intel_crtc = 12299edbd4a0SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 12309edbd4a0SFrançois Tigeot enum dpio_channel port = vlv_dport_to_channel(dport); 12319edbd4a0SFrançois Tigeot int pipe = intel_crtc->pipe; 12325d0b1887SFrançois Tigeot 1233ba55f2f5SFrançois Tigeot intel_hdmi_prepare(encoder); 12345d0b1887SFrançois Tigeot 12355d0b1887SFrançois Tigeot /* Program Tx lane resets to default */ 12369edbd4a0SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 12379edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 12385d0b1887SFrançois Tigeot DPIO_PCS_TX_LANE2_RESET | 12395d0b1887SFrançois Tigeot DPIO_PCS_TX_LANE1_RESET); 12409edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 12415d0b1887SFrançois Tigeot DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | 12425d0b1887SFrançois Tigeot DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | 12435d0b1887SFrançois Tigeot (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) | 12445d0b1887SFrançois Tigeot DPIO_PCS_CLK_SOFT_RESET); 12455d0b1887SFrançois Tigeot 12465d0b1887SFrançois Tigeot /* Fix up inter-pair skew failure */ 12479edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW12(port), 0x00750f00); 12489edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW11(port), 0x00001500); 12499edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW14(port), 0x40400000); 12505d0b1887SFrançois Tigeot 12519edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000); 12529edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN); 12539edbd4a0SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 12545d0b1887SFrançois Tigeot } 12555d0b1887SFrançois Tigeot 1256*24edb884SFrançois Tigeot static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) 1257*24edb884SFrançois Tigeot { 1258*24edb884SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 1259*24edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 1260*24edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1261*24edb884SFrançois Tigeot struct intel_crtc *intel_crtc = 1262*24edb884SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 1263*24edb884SFrançois Tigeot enum dpio_channel ch = vlv_dport_to_channel(dport); 1264*24edb884SFrançois Tigeot enum i915_pipe pipe = intel_crtc->pipe; 1265*24edb884SFrançois Tigeot u32 val; 1266*24edb884SFrançois Tigeot 1267*24edb884SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 1268*24edb884SFrançois Tigeot 1269*24edb884SFrançois Tigeot /* program left/right clock distribution */ 1270*24edb884SFrançois Tigeot if (pipe != PIPE_B) { 1271*24edb884SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); 1272*24edb884SFrançois Tigeot val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); 1273*24edb884SFrançois Tigeot if (ch == DPIO_CH0) 1274*24edb884SFrançois Tigeot val |= CHV_BUFLEFTENA1_FORCE; 1275*24edb884SFrançois Tigeot if (ch == DPIO_CH1) 1276*24edb884SFrançois Tigeot val |= CHV_BUFRIGHTENA1_FORCE; 1277*24edb884SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); 1278*24edb884SFrançois Tigeot } else { 1279*24edb884SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); 1280*24edb884SFrançois Tigeot val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); 1281*24edb884SFrançois Tigeot if (ch == DPIO_CH0) 1282*24edb884SFrançois Tigeot val |= CHV_BUFLEFTENA2_FORCE; 1283*24edb884SFrançois Tigeot if (ch == DPIO_CH1) 1284*24edb884SFrançois Tigeot val |= CHV_BUFRIGHTENA2_FORCE; 1285*24edb884SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); 1286*24edb884SFrançois Tigeot } 1287*24edb884SFrançois Tigeot 1288*24edb884SFrançois Tigeot /* program clock channel usage */ 1289*24edb884SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); 1290*24edb884SFrançois Tigeot val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; 1291*24edb884SFrançois Tigeot if (pipe != PIPE_B) 1292*24edb884SFrançois Tigeot val &= ~CHV_PCS_USEDCLKCHANNEL; 1293*24edb884SFrançois Tigeot else 1294*24edb884SFrançois Tigeot val |= CHV_PCS_USEDCLKCHANNEL; 1295*24edb884SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); 1296*24edb884SFrançois Tigeot 1297*24edb884SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); 1298*24edb884SFrançois Tigeot val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; 1299*24edb884SFrançois Tigeot if (pipe != PIPE_B) 1300*24edb884SFrançois Tigeot val &= ~CHV_PCS_USEDCLKCHANNEL; 1301*24edb884SFrançois Tigeot else 1302*24edb884SFrançois Tigeot val |= CHV_PCS_USEDCLKCHANNEL; 1303*24edb884SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); 1304*24edb884SFrançois Tigeot 1305*24edb884SFrançois Tigeot /* 1306*24edb884SFrançois Tigeot * This a a bit weird since generally CL 1307*24edb884SFrançois Tigeot * matches the pipe, but here we need to 1308*24edb884SFrançois Tigeot * pick the CL based on the port. 1309*24edb884SFrançois Tigeot */ 1310*24edb884SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch)); 1311*24edb884SFrançois Tigeot if (pipe != PIPE_B) 1312*24edb884SFrançois Tigeot val &= ~CHV_CMN_USEDCLKCHANNEL; 1313*24edb884SFrançois Tigeot else 1314*24edb884SFrançois Tigeot val |= CHV_CMN_USEDCLKCHANNEL; 1315*24edb884SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val); 1316*24edb884SFrançois Tigeot 1317*24edb884SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 1318*24edb884SFrançois Tigeot } 1319*24edb884SFrançois Tigeot 13209edbd4a0SFrançois Tigeot static void vlv_hdmi_post_disable(struct intel_encoder *encoder) 13215d0b1887SFrançois Tigeot { 13225d0b1887SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 13235d0b1887SFrançois Tigeot struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; 13249edbd4a0SFrançois Tigeot struct intel_crtc *intel_crtc = 13259edbd4a0SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 13269edbd4a0SFrançois Tigeot enum dpio_channel port = vlv_dport_to_channel(dport); 13279edbd4a0SFrançois Tigeot int pipe = intel_crtc->pipe; 13285d0b1887SFrançois Tigeot 13295d0b1887SFrançois Tigeot /* Reset lanes to avoid HDMI flicker (VLV w/a) */ 13305d0b1887SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 13319edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000); 13329edbd4a0SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060); 13335d0b1887SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 13345d0b1887SFrançois Tigeot } 13355d0b1887SFrançois Tigeot 1336ba55f2f5SFrançois Tigeot static void chv_hdmi_post_disable(struct intel_encoder *encoder) 1337ba55f2f5SFrançois Tigeot { 1338ba55f2f5SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 1339ba55f2f5SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 1340ba55f2f5SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1341ba55f2f5SFrançois Tigeot struct intel_crtc *intel_crtc = 1342ba55f2f5SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 1343ba55f2f5SFrançois Tigeot enum dpio_channel ch = vlv_dport_to_channel(dport); 1344ba55f2f5SFrançois Tigeot enum i915_pipe pipe = intel_crtc->pipe; 1345ba55f2f5SFrançois Tigeot u32 val; 1346ba55f2f5SFrançois Tigeot 1347ba55f2f5SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 1348ba55f2f5SFrançois Tigeot 1349ba55f2f5SFrançois Tigeot /* Propagate soft reset to data lane reset */ 1350ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); 1351ba55f2f5SFrançois Tigeot val |= CHV_PCS_REQ_SOFTRESET_EN; 1352ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); 1353ba55f2f5SFrançois Tigeot 1354ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); 1355ba55f2f5SFrançois Tigeot val |= CHV_PCS_REQ_SOFTRESET_EN; 1356ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); 1357ba55f2f5SFrançois Tigeot 1358ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); 1359ba55f2f5SFrançois Tigeot val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); 1360ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); 1361ba55f2f5SFrançois Tigeot 1362ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); 1363ba55f2f5SFrançois Tigeot val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); 1364ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); 1365ba55f2f5SFrançois Tigeot 1366ba55f2f5SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 1367ba55f2f5SFrançois Tigeot } 1368ba55f2f5SFrançois Tigeot 1369ba55f2f5SFrançois Tigeot static void chv_hdmi_pre_enable(struct intel_encoder *encoder) 1370ba55f2f5SFrançois Tigeot { 1371ba55f2f5SFrançois Tigeot struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); 1372ba55f2f5SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 1373ba55f2f5SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 1374ba55f2f5SFrançois Tigeot struct intel_crtc *intel_crtc = 1375ba55f2f5SFrançois Tigeot to_intel_crtc(encoder->base.crtc); 1376ba55f2f5SFrançois Tigeot enum dpio_channel ch = vlv_dport_to_channel(dport); 1377ba55f2f5SFrançois Tigeot int pipe = intel_crtc->pipe; 1378ba55f2f5SFrançois Tigeot int data, i; 1379ba55f2f5SFrançois Tigeot u32 val; 1380ba55f2f5SFrançois Tigeot 1381ba55f2f5SFrançois Tigeot mutex_lock(&dev_priv->dpio_lock); 1382ba55f2f5SFrançois Tigeot 1383ba55f2f5SFrançois Tigeot /* Deassert soft data lane reset*/ 1384ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); 1385ba55f2f5SFrançois Tigeot val |= CHV_PCS_REQ_SOFTRESET_EN; 1386ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); 1387ba55f2f5SFrançois Tigeot 1388ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); 1389ba55f2f5SFrançois Tigeot val |= CHV_PCS_REQ_SOFTRESET_EN; 1390ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); 1391ba55f2f5SFrançois Tigeot 1392ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); 1393ba55f2f5SFrançois Tigeot val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); 1394ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); 1395ba55f2f5SFrançois Tigeot 1396ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); 1397ba55f2f5SFrançois Tigeot val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); 1398ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); 1399ba55f2f5SFrançois Tigeot 1400ba55f2f5SFrançois Tigeot /* Program Tx latency optimal setting */ 1401ba55f2f5SFrançois Tigeot for (i = 0; i < 4; i++) { 1402ba55f2f5SFrançois Tigeot /* Set the latency optimal bit */ 1403ba55f2f5SFrançois Tigeot data = (i == 1) ? 0x0 : 0x6; 1404ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i), 1405ba55f2f5SFrançois Tigeot data << DPIO_FRC_LATENCY_SHFIT); 1406ba55f2f5SFrançois Tigeot 1407ba55f2f5SFrançois Tigeot /* Set the upar bit */ 1408ba55f2f5SFrançois Tigeot data = (i == 1) ? 0x0 : 0x1; 1409ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i), 1410ba55f2f5SFrançois Tigeot data << DPIO_UPAR_SHIFT); 1411ba55f2f5SFrançois Tigeot } 1412ba55f2f5SFrançois Tigeot 1413ba55f2f5SFrançois Tigeot /* Data lane stagger programming */ 1414ba55f2f5SFrançois Tigeot /* FIXME: Fix up value only after power analysis */ 1415ba55f2f5SFrançois Tigeot 1416ba55f2f5SFrançois Tigeot /* Clear calc init */ 1417ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); 1418ba55f2f5SFrançois Tigeot val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); 1419ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); 1420ba55f2f5SFrançois Tigeot 1421ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); 1422ba55f2f5SFrançois Tigeot val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); 1423ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); 1424ba55f2f5SFrançois Tigeot 1425ba55f2f5SFrançois Tigeot /* FIXME: Program the support xxx V-dB */ 1426ba55f2f5SFrançois Tigeot /* Use 800mV-0dB */ 1427ba55f2f5SFrançois Tigeot for (i = 0; i < 4; i++) { 1428ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i)); 1429ba55f2f5SFrançois Tigeot val &= ~DPIO_SWING_DEEMPH9P5_MASK; 1430ba55f2f5SFrançois Tigeot val |= 128 << DPIO_SWING_DEEMPH9P5_SHIFT; 1431ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val); 1432ba55f2f5SFrançois Tigeot } 1433ba55f2f5SFrançois Tigeot 1434ba55f2f5SFrançois Tigeot for (i = 0; i < 4; i++) { 1435ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); 1436ba55f2f5SFrançois Tigeot val &= ~DPIO_SWING_MARGIN_MASK; 1437ba55f2f5SFrançois Tigeot val |= 102 << DPIO_SWING_MARGIN_SHIFT; 1438ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); 1439ba55f2f5SFrançois Tigeot } 1440ba55f2f5SFrançois Tigeot 1441ba55f2f5SFrançois Tigeot /* Disable unique transition scale */ 1442ba55f2f5SFrançois Tigeot for (i = 0; i < 4; i++) { 1443ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); 1444ba55f2f5SFrançois Tigeot val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN; 1445ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); 1446ba55f2f5SFrançois Tigeot } 1447ba55f2f5SFrançois Tigeot 1448ba55f2f5SFrançois Tigeot /* Additional steps for 1200mV-0dB */ 1449ba55f2f5SFrançois Tigeot #if 0 1450ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch)); 1451ba55f2f5SFrançois Tigeot if (ch) 1452ba55f2f5SFrançois Tigeot val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1; 1453ba55f2f5SFrançois Tigeot else 1454ba55f2f5SFrançois Tigeot val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0; 1455ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val); 1456ba55f2f5SFrançois Tigeot 1457ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch), 1458ba55f2f5SFrançois Tigeot vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) | 1459ba55f2f5SFrançois Tigeot (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT)); 1460ba55f2f5SFrançois Tigeot #endif 1461ba55f2f5SFrançois Tigeot /* Start swing calculation */ 1462ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); 1463ba55f2f5SFrançois Tigeot val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; 1464ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); 1465ba55f2f5SFrançois Tigeot 1466ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); 1467ba55f2f5SFrançois Tigeot val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; 1468ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); 1469ba55f2f5SFrançois Tigeot 1470ba55f2f5SFrançois Tigeot /* LRC Bypass */ 1471ba55f2f5SFrançois Tigeot val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); 1472ba55f2f5SFrançois Tigeot val |= DPIO_LRC_BYPASS; 1473ba55f2f5SFrançois Tigeot vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val); 1474ba55f2f5SFrançois Tigeot 1475ba55f2f5SFrançois Tigeot mutex_unlock(&dev_priv->dpio_lock); 1476ba55f2f5SFrançois Tigeot 1477ba55f2f5SFrançois Tigeot intel_enable_hdmi(encoder); 1478ba55f2f5SFrançois Tigeot 1479ba55f2f5SFrançois Tigeot vlv_wait_port_ready(dev_priv, dport); 1480ba55f2f5SFrançois Tigeot } 1481ba55f2f5SFrançois Tigeot 1482e3adcf8fSFrançois Tigeot static void intel_hdmi_destroy(struct drm_connector *connector) 1483e3adcf8fSFrançois Tigeot { 1484e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 14858e26cdf6SFrançois Tigeot kfree(connector); 1486e3adcf8fSFrançois Tigeot } 1487e3adcf8fSFrançois Tigeot 1488e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_hdmi_connector_funcs = { 148919df918dSFrançois Tigeot .dpms = intel_connector_dpms, 1490e3adcf8fSFrançois Tigeot .detect = intel_hdmi_detect, 1491e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 1492e3adcf8fSFrançois Tigeot .set_property = intel_hdmi_set_property, 1493e3adcf8fSFrançois Tigeot .destroy = intel_hdmi_destroy, 1494e3adcf8fSFrançois Tigeot }; 1495e3adcf8fSFrançois Tigeot 1496e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { 1497e3adcf8fSFrançois Tigeot .get_modes = intel_hdmi_get_modes, 1498e3adcf8fSFrançois Tigeot .mode_valid = intel_hdmi_mode_valid, 1499e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 1500e3adcf8fSFrançois Tigeot }; 1501e3adcf8fSFrançois Tigeot 1502e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { 1503e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 1504e3adcf8fSFrançois Tigeot }; 1505e3adcf8fSFrançois Tigeot 1506e3adcf8fSFrançois Tigeot static void 1507*24edb884SFrançois Tigeot intel_attach_aspect_ratio_property(struct drm_connector *connector) 1508*24edb884SFrançois Tigeot { 1509*24edb884SFrançois Tigeot if (!drm_mode_create_aspect_ratio_property(connector->dev)) 1510*24edb884SFrançois Tigeot drm_object_attach_property(&connector->base, 1511*24edb884SFrançois Tigeot connector->dev->mode_config.aspect_ratio_property, 1512*24edb884SFrançois Tigeot DRM_MODE_PICTURE_ASPECT_NONE); 1513*24edb884SFrançois Tigeot } 1514*24edb884SFrançois Tigeot 1515*24edb884SFrançois Tigeot static void 1516e3adcf8fSFrançois Tigeot intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector) 1517e3adcf8fSFrançois Tigeot { 1518e3adcf8fSFrançois Tigeot intel_attach_force_audio_property(connector); 1519e3adcf8fSFrançois Tigeot intel_attach_broadcast_rgb_property(connector); 1520a2fdbec6SFrançois Tigeot intel_hdmi->color_range_auto = true; 1521*24edb884SFrançois Tigeot intel_attach_aspect_ratio_property(connector); 1522*24edb884SFrançois Tigeot intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; 1523e3adcf8fSFrançois Tigeot } 1524e3adcf8fSFrançois Tigeot 152519df918dSFrançois Tigeot void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, 152619df918dSFrançois Tigeot struct intel_connector *intel_connector) 1527e3adcf8fSFrançois Tigeot { 152819df918dSFrançois Tigeot struct drm_connector *connector = &intel_connector->base; 152919df918dSFrançois Tigeot struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; 153019df918dSFrançois Tigeot struct intel_encoder *intel_encoder = &intel_dig_port->base; 153119df918dSFrançois Tigeot struct drm_device *dev = intel_encoder->base.dev; 1532e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 153319df918dSFrançois Tigeot enum port port = intel_dig_port->port; 1534e3adcf8fSFrançois Tigeot 1535e3adcf8fSFrançois Tigeot drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, 1536e3adcf8fSFrançois Tigeot DRM_MODE_CONNECTOR_HDMIA); 1537e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs); 1538e3adcf8fSFrançois Tigeot 1539e3adcf8fSFrançois Tigeot connector->interlace_allowed = 1; 1540e3adcf8fSFrançois Tigeot connector->doublescan_allowed = 0; 15419edbd4a0SFrançois Tigeot connector->stereo_allowed = 1; 1542e3adcf8fSFrançois Tigeot 154319df918dSFrançois Tigeot switch (port) { 154419df918dSFrançois Tigeot case PORT_B: 1545e3adcf8fSFrançois Tigeot intel_hdmi->ddc_bus = GMBUS_PORT_DPB; 15468e26cdf6SFrançois Tigeot intel_encoder->hpd_pin = HPD_PORT_B; 154719df918dSFrançois Tigeot break; 154819df918dSFrançois Tigeot case PORT_C: 1549e3adcf8fSFrançois Tigeot intel_hdmi->ddc_bus = GMBUS_PORT_DPC; 15508e26cdf6SFrançois Tigeot intel_encoder->hpd_pin = HPD_PORT_C; 155119df918dSFrançois Tigeot break; 155219df918dSFrançois Tigeot case PORT_D: 1553ba55f2f5SFrançois Tigeot if (IS_CHERRYVIEW(dev)) 1554ba55f2f5SFrançois Tigeot intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV; 1555ba55f2f5SFrançois Tigeot else 1556e3adcf8fSFrançois Tigeot intel_hdmi->ddc_bus = GMBUS_PORT_DPD; 15578e26cdf6SFrançois Tigeot intel_encoder->hpd_pin = HPD_PORT_D; 155819df918dSFrançois Tigeot break; 155919df918dSFrançois Tigeot case PORT_A: 15608e26cdf6SFrançois Tigeot intel_encoder->hpd_pin = HPD_PORT_A; 156119df918dSFrançois Tigeot /* Internal port only for eDP. */ 156219df918dSFrançois Tigeot default: 156319df918dSFrançois Tigeot BUG(); 1564e3adcf8fSFrançois Tigeot } 1565e3adcf8fSFrançois Tigeot 15668e26cdf6SFrançois Tigeot if (IS_VALLEYVIEW(dev)) { 156719df918dSFrançois Tigeot intel_hdmi->write_infoframe = vlv_write_infoframe; 156819df918dSFrançois Tigeot intel_hdmi->set_infoframes = vlv_set_infoframes; 1569*24edb884SFrançois Tigeot } else if (IS_G4X(dev)) { 15708e26cdf6SFrançois Tigeot intel_hdmi->write_infoframe = g4x_write_infoframe; 15718e26cdf6SFrançois Tigeot intel_hdmi->set_infoframes = g4x_set_infoframes; 15728e26cdf6SFrançois Tigeot } else if (HAS_DDI(dev)) { 157319df918dSFrançois Tigeot intel_hdmi->write_infoframe = hsw_write_infoframe; 157419df918dSFrançois Tigeot intel_hdmi->set_infoframes = hsw_set_infoframes; 157519df918dSFrançois Tigeot } else if (HAS_PCH_IBX(dev)) { 157619df918dSFrançois Tigeot intel_hdmi->write_infoframe = ibx_write_infoframe; 157719df918dSFrançois Tigeot intel_hdmi->set_infoframes = ibx_set_infoframes; 1578e3adcf8fSFrançois Tigeot } else { 157919df918dSFrançois Tigeot intel_hdmi->write_infoframe = cpt_write_infoframe; 158019df918dSFrançois Tigeot intel_hdmi->set_infoframes = cpt_set_infoframes; 1581e3adcf8fSFrançois Tigeot } 1582e3adcf8fSFrançois Tigeot 1583a2fdbec6SFrançois Tigeot if (HAS_DDI(dev)) 158419df918dSFrançois Tigeot intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; 158519df918dSFrançois Tigeot else 158619df918dSFrançois Tigeot intel_connector->get_hw_state = intel_connector_get_hw_state; 1587ba55f2f5SFrançois Tigeot intel_connector->unregister = intel_connector_unregister; 1588e3adcf8fSFrançois Tigeot 1589e3adcf8fSFrançois Tigeot intel_hdmi_add_properties(intel_hdmi, connector); 1590e3adcf8fSFrançois Tigeot 1591e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, intel_encoder); 1592c6f73aabSFrançois Tigeot drm_connector_register(connector); 1593e3adcf8fSFrançois Tigeot 1594e3adcf8fSFrançois Tigeot /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written 1595e3adcf8fSFrançois Tigeot * 0xd. Failure to do so will result in spurious interrupts being 1596e3adcf8fSFrançois Tigeot * generated on the port when a cable is not attached. 1597e3adcf8fSFrançois Tigeot */ 1598e3adcf8fSFrançois Tigeot if (IS_G4X(dev) && !IS_GM45(dev)) { 1599e3adcf8fSFrançois Tigeot u32 temp = I915_READ(PEG_BAND_GAP_DATA); 1600e3adcf8fSFrançois Tigeot I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); 1601e3adcf8fSFrançois Tigeot } 1602e3adcf8fSFrançois Tigeot } 160319df918dSFrançois Tigeot 16048e26cdf6SFrançois Tigeot void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) 160519df918dSFrançois Tigeot { 160619df918dSFrançois Tigeot struct intel_digital_port *intel_dig_port; 160719df918dSFrançois Tigeot struct intel_encoder *intel_encoder; 160819df918dSFrançois Tigeot struct intel_connector *intel_connector; 160919df918dSFrançois Tigeot 16109edbd4a0SFrançois Tigeot intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL); 161119df918dSFrançois Tigeot if (!intel_dig_port) 161219df918dSFrançois Tigeot return; 161319df918dSFrançois Tigeot 16149edbd4a0SFrançois Tigeot intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); 161519df918dSFrançois Tigeot if (!intel_connector) { 1616158486a6SFrançois Tigeot kfree(intel_dig_port); 161719df918dSFrançois Tigeot return; 161819df918dSFrançois Tigeot } 161919df918dSFrançois Tigeot 162019df918dSFrançois Tigeot intel_encoder = &intel_dig_port->base; 162119df918dSFrançois Tigeot 162219df918dSFrançois Tigeot drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, 162319df918dSFrançois Tigeot DRM_MODE_ENCODER_TMDS); 162419df918dSFrançois Tigeot 16258e26cdf6SFrançois Tigeot intel_encoder->compute_config = intel_hdmi_compute_config; 162619df918dSFrançois Tigeot intel_encoder->disable = intel_disable_hdmi; 162719df918dSFrançois Tigeot intel_encoder->get_hw_state = intel_hdmi_get_hw_state; 16285d0b1887SFrançois Tigeot intel_encoder->get_config = intel_hdmi_get_config; 1629ba55f2f5SFrançois Tigeot if (IS_CHERRYVIEW(dev)) { 1630*24edb884SFrançois Tigeot intel_encoder->pre_pll_enable = chv_hdmi_pre_pll_enable; 1631ba55f2f5SFrançois Tigeot intel_encoder->pre_enable = chv_hdmi_pre_enable; 1632ba55f2f5SFrançois Tigeot intel_encoder->enable = vlv_enable_hdmi; 1633ba55f2f5SFrançois Tigeot intel_encoder->post_disable = chv_hdmi_post_disable; 1634ba55f2f5SFrançois Tigeot } else if (IS_VALLEYVIEW(dev)) { 16359edbd4a0SFrançois Tigeot intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable; 16369edbd4a0SFrançois Tigeot intel_encoder->pre_enable = vlv_hdmi_pre_enable; 16379edbd4a0SFrançois Tigeot intel_encoder->enable = vlv_enable_hdmi; 16389edbd4a0SFrançois Tigeot intel_encoder->post_disable = vlv_hdmi_post_disable; 16399edbd4a0SFrançois Tigeot } else { 1640ba55f2f5SFrançois Tigeot intel_encoder->pre_enable = intel_hdmi_pre_enable; 16419edbd4a0SFrançois Tigeot intel_encoder->enable = intel_enable_hdmi; 16425d0b1887SFrançois Tigeot } 164319df918dSFrançois Tigeot 164419df918dSFrançois Tigeot intel_encoder->type = INTEL_OUTPUT_HDMI; 1645ba55f2f5SFrançois Tigeot if (IS_CHERRYVIEW(dev)) { 1646ba55f2f5SFrançois Tigeot if (port == PORT_D) 1647ba55f2f5SFrançois Tigeot intel_encoder->crtc_mask = 1 << 2; 1648ba55f2f5SFrançois Tigeot else 1649ba55f2f5SFrançois Tigeot intel_encoder->crtc_mask = (1 << 0) | (1 << 1); 1650ba55f2f5SFrançois Tigeot } else { 165119df918dSFrançois Tigeot intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); 1652ba55f2f5SFrançois Tigeot } 1653ba55f2f5SFrançois Tigeot intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG; 1654ba55f2f5SFrançois Tigeot /* 1655ba55f2f5SFrançois Tigeot * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems 1656ba55f2f5SFrançois Tigeot * to work on real hardware. And since g4x can send infoframes to 1657ba55f2f5SFrançois Tigeot * only one port anyway, nothing is lost by allowing it. 1658ba55f2f5SFrançois Tigeot */ 1659ba55f2f5SFrançois Tigeot if (IS_G4X(dev)) 1660ba55f2f5SFrançois Tigeot intel_encoder->cloneable |= 1 << INTEL_OUTPUT_HDMI; 166119df918dSFrançois Tigeot 166219df918dSFrançois Tigeot intel_dig_port->port = port; 16638e26cdf6SFrançois Tigeot intel_dig_port->hdmi.hdmi_reg = hdmi_reg; 166419df918dSFrançois Tigeot intel_dig_port->dp.output_reg = 0; 166519df918dSFrançois Tigeot 166619df918dSFrançois Tigeot intel_hdmi_init_connector(intel_dig_port, intel_connector); 166719df918dSFrançois Tigeot } 1668