Path: blob/master/drivers/gpu/drm/i915/intel_hdmi.c
15112 views
/*1* Copyright 2006 Dave Airlie <[email protected]>2* Copyright © 2006-2009 Intel Corporation3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* the rights to use, copy, modify, merge, publish, distribute, sublicense,8* and/or sell copies of the Software, and to permit persons to whom the9* Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING20* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21* DEALINGS IN THE SOFTWARE.22*23* Authors:24* Eric Anholt <[email protected]>25* Jesse Barnes <[email protected]>26*/2728#include <linux/i2c.h>29#include <linux/slab.h>30#include <linux/delay.h>31#include "drmP.h"32#include "drm.h"33#include "drm_crtc.h"34#include "drm_edid.h"35#include "intel_drv.h"36#include "i915_drm.h"37#include "i915_drv.h"3839struct intel_hdmi {40struct intel_encoder base;41u32 sdvox_reg;42int ddc_bus;43uint32_t color_range;44bool has_hdmi_sink;45bool has_audio;46int force_audio;47};4849static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)50{51return container_of(encoder, struct intel_hdmi, base.base);52}5354static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)55{56return container_of(intel_attached_encoder(connector),57struct intel_hdmi, base);58}5960void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)61{62uint8_t *data = (uint8_t *)avi_if;63uint8_t sum = 0;64unsigned i;6566avi_if->checksum = 0;67avi_if->ecc = 0;6869for (i = 0; i < sizeof(*avi_if); i++)70sum += data[i];7172avi_if->checksum = 0x100 - sum;73}7475static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)76{77struct dip_infoframe avi_if = {78.type = DIP_TYPE_AVI,79.ver = DIP_VERSION_AVI,80.len = DIP_LEN_AVI,81};82uint32_t *data = (uint32_t *)&avi_if;83struct drm_device *dev = encoder->dev;84struct drm_i915_private *dev_priv = dev->dev_private;85struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);86u32 port;87unsigned i;8889if (!intel_hdmi->has_hdmi_sink)90return;9192/* XXX first guess at handling video port, is this corrent? */93if (intel_hdmi->sdvox_reg == SDVOB)94port = VIDEO_DIP_PORT_B;95else if (intel_hdmi->sdvox_reg == SDVOC)96port = VIDEO_DIP_PORT_C;97else98return;99100I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |101VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);102103intel_dip_infoframe_csum(&avi_if);104for (i = 0; i < sizeof(avi_if); i += 4) {105I915_WRITE(VIDEO_DIP_DATA, *data);106data++;107}108109I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |110VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |111VIDEO_DIP_ENABLE_AVI);112}113114static void intel_hdmi_mode_set(struct drm_encoder *encoder,115struct drm_display_mode *mode,116struct drm_display_mode *adjusted_mode)117{118struct drm_device *dev = encoder->dev;119struct drm_i915_private *dev_priv = dev->dev_private;120struct drm_crtc *crtc = encoder->crtc;121struct intel_crtc *intel_crtc = to_intel_crtc(crtc);122struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);123u32 sdvox;124125sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;126sdvox |= intel_hdmi->color_range;127if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)128sdvox |= SDVO_VSYNC_ACTIVE_HIGH;129if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)130sdvox |= SDVO_HSYNC_ACTIVE_HIGH;131132/* Required on CPT */133if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))134sdvox |= HDMI_MODE_SELECT;135136if (intel_hdmi->has_audio) {137sdvox |= SDVO_AUDIO_ENABLE;138sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;139}140141if (intel_crtc->pipe == 1) {142if (HAS_PCH_CPT(dev))143sdvox |= PORT_TRANS_B_SEL_CPT;144else145sdvox |= SDVO_PIPE_B_SELECT;146}147148I915_WRITE(intel_hdmi->sdvox_reg, sdvox);149POSTING_READ(intel_hdmi->sdvox_reg);150151intel_hdmi_set_avi_infoframe(encoder);152}153154static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)155{156struct drm_device *dev = encoder->dev;157struct drm_i915_private *dev_priv = dev->dev_private;158struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);159u32 temp;160161temp = I915_READ(intel_hdmi->sdvox_reg);162163/* HW workaround, need to toggle enable bit off and on for 12bpc, but164* we do this anyway which shows more stable in testing.165*/166if (HAS_PCH_SPLIT(dev)) {167I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);168POSTING_READ(intel_hdmi->sdvox_reg);169}170171if (mode != DRM_MODE_DPMS_ON) {172temp &= ~SDVO_ENABLE;173} else {174temp |= SDVO_ENABLE;175}176177I915_WRITE(intel_hdmi->sdvox_reg, temp);178POSTING_READ(intel_hdmi->sdvox_reg);179180/* HW workaround, need to write this twice for issue that may result181* in first write getting masked.182*/183if (HAS_PCH_SPLIT(dev)) {184I915_WRITE(intel_hdmi->sdvox_reg, temp);185POSTING_READ(intel_hdmi->sdvox_reg);186}187}188189static int intel_hdmi_mode_valid(struct drm_connector *connector,190struct drm_display_mode *mode)191{192if (mode->clock > 165000)193return MODE_CLOCK_HIGH;194if (mode->clock < 20000)195return MODE_CLOCK_LOW;196197if (mode->flags & DRM_MODE_FLAG_DBLSCAN)198return MODE_NO_DBLESCAN;199200return MODE_OK;201}202203static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,204struct drm_display_mode *mode,205struct drm_display_mode *adjusted_mode)206{207return true;208}209210static enum drm_connector_status211intel_hdmi_detect(struct drm_connector *connector, bool force)212{213struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);214struct drm_i915_private *dev_priv = connector->dev->dev_private;215struct edid *edid;216enum drm_connector_status status = connector_status_disconnected;217218intel_hdmi->has_hdmi_sink = false;219intel_hdmi->has_audio = false;220edid = drm_get_edid(connector,221&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);222223if (edid) {224if (edid->input & DRM_EDID_INPUT_DIGITAL) {225status = connector_status_connected;226intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid);227intel_hdmi->has_audio = drm_detect_monitor_audio(edid);228}229connector->display_info.raw_edid = NULL;230kfree(edid);231}232233if (status == connector_status_connected) {234if (intel_hdmi->force_audio)235intel_hdmi->has_audio = intel_hdmi->force_audio > 0;236}237238return status;239}240241static int intel_hdmi_get_modes(struct drm_connector *connector)242{243struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);244struct drm_i915_private *dev_priv = connector->dev->dev_private;245246/* We should parse the EDID data and find out if it's an HDMI sink so247* we can send audio to it.248*/249250return intel_ddc_get_modes(connector,251&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);252}253254static bool255intel_hdmi_detect_audio(struct drm_connector *connector)256{257struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);258struct drm_i915_private *dev_priv = connector->dev->dev_private;259struct edid *edid;260bool has_audio = false;261262edid = drm_get_edid(connector,263&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);264if (edid) {265if (edid->input & DRM_EDID_INPUT_DIGITAL)266has_audio = drm_detect_monitor_audio(edid);267268connector->display_info.raw_edid = NULL;269kfree(edid);270}271272return has_audio;273}274275static int276intel_hdmi_set_property(struct drm_connector *connector,277struct drm_property *property,278uint64_t val)279{280struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);281struct drm_i915_private *dev_priv = connector->dev->dev_private;282int ret;283284ret = drm_connector_property_set_value(connector, property, val);285if (ret)286return ret;287288if (property == dev_priv->force_audio_property) {289int i = val;290bool has_audio;291292if (i == intel_hdmi->force_audio)293return 0;294295intel_hdmi->force_audio = i;296297if (i == 0)298has_audio = intel_hdmi_detect_audio(connector);299else300has_audio = i > 0;301302if (has_audio == intel_hdmi->has_audio)303return 0;304305intel_hdmi->has_audio = has_audio;306goto done;307}308309if (property == dev_priv->broadcast_rgb_property) {310if (val == !!intel_hdmi->color_range)311return 0;312313intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;314goto done;315}316317return -EINVAL;318319done:320if (intel_hdmi->base.base.crtc) {321struct drm_crtc *crtc = intel_hdmi->base.base.crtc;322drm_crtc_helper_set_mode(crtc, &crtc->mode,323crtc->x, crtc->y,324crtc->fb);325}326327return 0;328}329330static void intel_hdmi_destroy(struct drm_connector *connector)331{332drm_sysfs_connector_remove(connector);333drm_connector_cleanup(connector);334kfree(connector);335}336337static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {338.dpms = intel_hdmi_dpms,339.mode_fixup = intel_hdmi_mode_fixup,340.prepare = intel_encoder_prepare,341.mode_set = intel_hdmi_mode_set,342.commit = intel_encoder_commit,343};344345static const struct drm_connector_funcs intel_hdmi_connector_funcs = {346.dpms = drm_helper_connector_dpms,347.detect = intel_hdmi_detect,348.fill_modes = drm_helper_probe_single_connector_modes,349.set_property = intel_hdmi_set_property,350.destroy = intel_hdmi_destroy,351};352353static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {354.get_modes = intel_hdmi_get_modes,355.mode_valid = intel_hdmi_mode_valid,356.best_encoder = intel_best_encoder,357};358359static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {360.destroy = intel_encoder_destroy,361};362363static void364intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)365{366intel_attach_force_audio_property(connector);367intel_attach_broadcast_rgb_property(connector);368}369370void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)371{372struct drm_i915_private *dev_priv = dev->dev_private;373struct drm_connector *connector;374struct intel_encoder *intel_encoder;375struct intel_connector *intel_connector;376struct intel_hdmi *intel_hdmi;377378intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);379if (!intel_hdmi)380return;381382intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);383if (!intel_connector) {384kfree(intel_hdmi);385return;386}387388intel_encoder = &intel_hdmi->base;389drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,390DRM_MODE_ENCODER_TMDS);391392connector = &intel_connector->base;393drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,394DRM_MODE_CONNECTOR_HDMIA);395drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);396397intel_encoder->type = INTEL_OUTPUT_HDMI;398399connector->polled = DRM_CONNECTOR_POLL_HPD;400connector->interlace_allowed = 0;401connector->doublescan_allowed = 0;402intel_encoder->crtc_mask = (1 << 0) | (1 << 1);403404/* Set up the DDC bus. */405if (sdvox_reg == SDVOB) {406intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);407intel_hdmi->ddc_bus = GMBUS_PORT_DPB;408dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;409} else if (sdvox_reg == SDVOC) {410intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);411intel_hdmi->ddc_bus = GMBUS_PORT_DPC;412dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;413} else if (sdvox_reg == HDMIB) {414intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);415intel_hdmi->ddc_bus = GMBUS_PORT_DPB;416dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;417} else if (sdvox_reg == HDMIC) {418intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);419intel_hdmi->ddc_bus = GMBUS_PORT_DPC;420dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;421} else if (sdvox_reg == HDMID) {422intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);423intel_hdmi->ddc_bus = GMBUS_PORT_DPD;424dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;425}426427intel_hdmi->sdvox_reg = sdvox_reg;428429drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);430431intel_hdmi_add_properties(intel_hdmi, connector);432433intel_connector_attach_encoder(intel_connector, intel_encoder);434drm_sysfs_connector_add(connector);435436/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written437* 0xd. Failure to do so will result in spurious interrupts being438* generated on the port when a cable is not attached.439*/440if (IS_G4X(dev) && !IS_GM45(dev)) {441u32 temp = I915_READ(PEG_BAND_GAP_DATA);442I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);443}444}445446447