diff options
Diffstat (limited to 'drivers/staging/cdv/drv/psb_intel_hdmi.c')
-rw-r--r-- | drivers/staging/cdv/drv/psb_intel_hdmi.c | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/drivers/staging/cdv/drv/psb_intel_hdmi.c b/drivers/staging/cdv/drv/psb_intel_hdmi.c new file mode 100644 index 000000000000..676fc2a75637 --- /dev/null +++ b/drivers/staging/cdv/drv/psb_intel_hdmi.c @@ -0,0 +1,684 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + */ + +#include <drm/drmP.h> +#include <drm/drm.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> +#include "psb_intel_drv.h" +#include "psb_drv.h" +#include "psb_intel_reg.h" +#include "psb_intel_hdmi_reg.h" +#include "psb_intel_hdmi_edid.h" +#include "psb_intel_hdmi.h" +#include <linux/pm_runtime.h> + +static void mdfld_hdmi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + u32 hdmib; + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + PSB_DEBUG_ENTRY("\n"); + + hdmib = (2 << 10); + + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + hdmib |= SDVO_VSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + hdmib |= SDVO_HSYNC_ACTIVE_HIGH; + + if (intel_crtc->pipe == 1) + hdmib |= HDMIB_PIPE_B_SELECT; + + if (hdmi_priv->has_hdmi_audio) { + hdmib |= HDMI_AUDIO_ENABLE; + hdmib |= HDMI_NULL_PACKETS_DURING_VSYNC; + } + + REG_WRITE(hdmi_priv->hdmi_reg, hdmib); + REG_READ(hdmi_priv->hdmi_reg); +} + +static bool mdfld_hdmi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + + return true; +} + + +static void mdfld_hdmi_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + u32 hdmib; + + PSB_DEBUG_ENTRY("%s \n", mode == DRM_MODE_DPMS_ON ? "on" : "off"); + + hdmib = REG_READ(hdmi_priv->hdmi_reg); + + if (mode != DRM_MODE_DPMS_ON) { + REG_WRITE(hdmi_priv->hdmi_reg, hdmib & ~HDMIB_PORT_EN); + } else { + REG_WRITE(hdmi_priv->hdmi_reg, hdmib | HDMIB_PORT_EN); + } + REG_READ(hdmi_priv->hdmi_reg); +} + +static void mdfld_hdmi_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + + PSB_DEBUG_ENTRY("\n"); + + hdmi_priv->save_HDMIB = REG_READ(hdmi_priv->hdmi_reg); +} + +static void mdfld_hdmi_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + + PSB_DEBUG_ENTRY("\n"); + + REG_WRITE(hdmi_priv->hdmi_reg, hdmi_priv->save_HDMIB); + REG_READ(hdmi_priv->hdmi_reg); +} + +#if 0 + +/* HDMI DIP related stuff */ +static int mdfld_hdmi_get_cached_edid_block(struct drm_connector *connector, uint32_t num_block, uint8_t *edid_block, uint32_t size) +{ + struct drm_display_info *displayinfo = &(connector->display_info); + if (num_block >= MAX_EDID_BLOCKS) + { + DRM_ERROR("mdfld_hdmi_get_cached_edid_block() - Invalid EDID block\n"); + return 0; + } + edid_block = &displayinfo->raw_edid[EDID_BLOCK_SIZE*num_block]; + return 1; +} + +///////////////////////////////////////////////////////////////////////// +// INTHDMIENCODER_CreateEELDPacket(): +// This function parses v1.3 base EDID and CEA-861b EDID Timing Extension +// Version3 and creates EELD (Enhanced EDID Like Data) packet. This EELD data contains +// audio configuration information and other details read EDID.This can also contain Vendor specific Data +// +///////////////////////////////////////////////////////////////////////// +static int mdfld_hdmi_create_eeld_packet(struct drm_connector *connector) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; + uint8_t ucEdidBlock[128]; + hdmi_eeld_t *pEEld = NULL; + baseedid_1_x_t *pEdid = NULL; + ce_edid_t *pCeEdid = NULL; + int dwNumOfBytes = 0; + int sizeOfCEADataBlock = 0; + uint8_t * pDataBlock = NULL; + edid_dtd_timing_t *pDTD = NULL; + uint8_t *pData = NULL; + uint8_t ucDataBlockTag = 0; + cea_861b_adb_t *pADB = NULL; + uint8_t i = 0; + uint8_t j = 0; + uint8_t * pSADBlocks = NULL; + uint8_t * pCurrentSADBlocks = NULL; + uint32_t ulNumSADBytes = 0; + //vsdb_byte6_to_byte8_t *pVSDB = NULL; + uint32_t ulIndex = 0; + //uint8_t b48kHzCADPresent = false; + + pEEld = (hdmi_eeld_t *) &hdmi_priv->eeld; + + // Fill Version info + pEEld->cea_edid_rev_id = HDMI_EELD_CEA_EDID_VERSION; + pEEld->eld_ver = HDMI_EELD_VERSION; + + // Fill BaseLine ELD length + // This is 80 bytes as per EELD proposal + pEEld->baseline_eld_length = HDMI_EELD_BASELINE_DATA_LENGTH; + + //Zero out EDID block buffer + memset(ucEdidBlock, 0, sizeof(ucEdidBlock)); + + // Get Extn EDID + if(!mdfld_hdmi_get_cached_edid_block(connector, 1, ucEdidBlock, EDID_BLOCK_SIZE)) + { + return 0; + } + + pCeEdid = (ce_edid_t *) ucEdidBlock; + + //allocate memory (48 bytes) for SAD Blocks buffer + pSADBlocks = kcalloc(1, 48, GFP_KERNEL); + + if(pSADBlocks == NULL) + { + DRM_ERROR("mdfld_hdmi_create_eld_packaet() - Failed to allocate mem for pSADBlocks\n"); + return 0; + } + + pCurrentSADBlocks = pSADBlocks; + + // Now pull out data from CEA Extension EDID + // If Offset <= 4, we will not have CEA DataBlocks + if(pCeEdid->ucDTDOffset > CEA_EDID_HEADER_SZIE) + { + sizeOfCEADataBlock = pCeEdid->ucDTDOffset - CEA_EDID_HEADER_SZIE; + + pDataBlock = (uint8_t *)pCeEdid; + + // skip header (first 4 bytes) in CEA EDID Timing Extension + // and set pointer to start of DataBlocks collection + pDataBlock += CEA_EDID_HEADER_SZIE; + + // General Format of CEA Data Block Collection + // -----------+--------------------+-----------------------------------------+ + // |Byte# |bits5-7 | bits 0-4 | + // -----------|--------------------+-----------------------------------------+ + // | 1 | Video Tag |Length = total #of video bytes following | + // | | Code |this byte (L1) | + // |--------------------+-----------------------------------------+ + // Video | 2 | CEA Short Video Descriptor 1 | + // Data |--------+-----------------------------------------------------| + // Block | 3 | CEA Short Video Descriptor 2 | + // |--------+-----------------------------------------------------| + // | ... | ... | + // |--------------------------------------------------------------+ + // | 1+L1 | CEA Short Video Descriptor L1 | + // -----------+--------------------+-----------------------------------------+ + // | 2+L1 | Audio Tag |Length = total #of audio bytes following | + // | | Code |this byte (L2) | + // |--------------------+-----------------------------------------+ + // Audio | 3+L1 | | + // Data |--------+ | + // Block | 4+L1 | CEA Short Audio Descriptor 1 | + // |--------+ | + // | 5+L1 | | + // |--------------------------------------------------------------+ + // | ... | | + // | | | + // | | | + // | ... | | + // |--------------------------------------------------------------- + // |L1+L2 | | + // |--------| | + // |1+L1+L2 | CEA Short Audio Descriptor L2/3 | + // |--------| | + // |2+L1+L2 | | + // -----------+--------------------------------------------------------------+ + // |3+L1+L2 | Speaker |Length = total #of SA bytes following | + // | | Tag Code |this byte (L1) | + // Speaker |--------------------------------------------------------------+ + // Allocation|4+L1+L2 | | + // Data |--------| | + // Block |5+L1+L2 | Speaker Allocation Data Block Payload(3 bytes) | + // |--------| | + // |6+L1+L2 | | + // -----------+--------------------------------------------------------------+ + // |7+L1+L2 | VSDB Tag |Length = total #of VSDB bytes following | + // | | Code |this byte (L1) | + // Vendor |--------------------------------------------------------------+ + // Specific |8+L1+L2 | | + // Data |--------| | + // Block |9+L1+L2 | 24-bit IEEE Registration Identifier (LSB first) | + // |--------| | + // |10+L1+L2| | + // |--------------------------------------------------------------+ + // | ... | Vendor Specific Data block Payload | + // -----------+--------------------------------------------------------------+ + + while(sizeOfCEADataBlock > 0) + { + // Get the Size of CEA DataBlock in bytes and TAG + dwNumOfBytes = *pDataBlock & CEA_DATABLOCK_LENGTH_MASK; + ucDataBlockTag = (*pDataBlock & CEA_DATABLOCK_TAG_MASK) >> 5; + + switch(ucDataBlockTag) + { + case CEA_AUDIO_DATABLOCK: + // move beyond tag/length byte + ++pDataBlock; + for (i = 0; i < (dwNumOfBytes / 3); ++i, pDataBlock += 3) + { + pADB = (cea_861b_adb_t*)pDataBlock; + switch(pADB->audio_format_code) + { + // uncompressed audio (Linear PCM) + case AUDIO_LPCM: + memcpy(&(hdmi_priv->lpcm_sad),pDataBlock,3); + //save these blocks + memcpy(pCurrentSADBlocks, pDataBlock, 3); + // move pointer in SAD blocks buffer + pCurrentSADBlocks += 3; + // update SADC field + pEEld->sadc += 1; + break; + // compressed audio + case AUDIO_AC3: + case AUDIO_MPEG1: + case AUDIO_MP3: + case AUDIO_MPEG2: + case AUDIO_AAC: + case AUDIO_DTS: + case AUDIO_ATRAC: + case AUDIO_OBA: + case AUDIO_DOLBY_DIGITAL: + case AUDIO_DTS_HD: + case AUDIO_MAT: + case AUDIO_DST: + case AUDIO_WMA_PRO: + //save these blocks + memcpy(pCurrentSADBlocks, pDataBlock, 3); + // move pointer in SAD blocks buffer + pCurrentSADBlocks += 3; + // update SADC field + pEEld->sadc += 1; + break; + } + } + break; + + case CEA_VENDOR_DATABLOCK: + // audio wants data from 6th byte of VSDB onwards + //Sighting 94842: + + // | Byte # | bits[7-0] | + // |--------------------------------------------------------------------| + // | 1-3 |24-bit IEEE Registration Identifier (0x000C03) | + // |--------------------------------------------------------------------| + // | 4-5 | Source Physical Address | + // |--------------------------------------------------------------------| + // | 6 |SupportsAI|DC48bit|DC36bit|Dc30bit|DCY444|Rsvd|Rsvd|DVIDual| + // |--------------------------------------------------------------------| + // | 7 | Max TMDS clock | + // |--------------------------------------------------------------------| + // | 8 |Latency_Field |I_Latency_Field| Reserved bits 5-0 | + // | | _Present | _Present | | + // |--------------------------------------------------------------------| + // | 9 | Video Latency | + // |--------------------------------------------------------------------| + // | 10 | Audio Latency | + // |--------------------------------------------------------------------| + // | 11 | Interlaced Video Latency | + // |--------------------------------------------------------------------| + // | 12 | Interlaced Audio Latency | + // |--------------------------------------------------------------------| + + ++pDataBlock; + // move pointer to next CEA Datablock + pDataBlock += dwNumOfBytes; + break; + + case CEA_SPEAKER_DATABLOCK: + pEEld->speaker_allocation_block = *(++pDataBlock); + // move pointer to next CEA Datablock + pDataBlock += dwNumOfBytes; + break; + + default: + // Move pointer to next CEA DataBlock + pDataBlock += (dwNumOfBytes + 1); + } + // Decrement size of CEA DataBlock + sizeOfCEADataBlock -= (dwNumOfBytes + 1); + } + } + + //Copy all the saved SAD blocks at the end of ELD + //SAD blocks should be written after the Monitor name and VSDB. + //See ELD definition in iHDMI.h + ulNumSADBytes = (pEEld->sadc) * 3; //Size of each SAD block is 3 bytes + + //DCN 460119: Audio does not play on displays which do not provide SAB in EDID. + //Solution: Graphics driver should create a default SAB in ELD with front left and front right + //speakers enabled if the display supports basic audio. + pDataBlock = (uint8_t *)pCeEdid; + if((*(pDataBlock + HDMI_CEA_EXTENSION_BLOCK_BYTE_3) & HDMI_BASIC_AUDIO_SUPPORTED) && (pEEld->speaker_allocation_block == 0)) + { + pEEld->flr = 1; + } + //End of DCN 460119 + + // zero out local buffers + memset(ucEdidBlock, 0, sizeof(ucEdidBlock)); + + // Get base EDID + if(!mdfld_hdmi_get_cached_edid_block(connector, 0, ucEdidBlock, EDID_BLOCK_SIZE)) + { + return 0; + } + + pEdid = (baseedid_1_x_t*) ucEdidBlock; + pDTD = &pEdid->DTD[1]; + + //Update the Manufacturer ID and Product Code here + memcpy(pEEld->manufacturer_id,pEdid->ManufacturerID,2); + memcpy(pEEld->product_id,pEdid->ProductID,2); + + // Now Fill the monitor string name + // Search through DTD blocks, looking for monitor name + for (i = 0; i < MAX_BASEEDID_DTD_BLOCKS - 1; ++i, ++pDTD) + { + // Set a uint8_t pointer to DTD data + pData = (uint8_t *)pDTD; + + // Check the Flag (the first two bytes) to determine + // if this block is used as descriptor + if (pData[0] == 0x00 && pData[1] == 0x00) + { + // And now check Data Type Tag within this descriptor + // Tag = 0xFC, then monitor name stored as ASCII + if (pData[3] == 0xFC) + { + ulIndex = 0; + // Copy monitor name + for (j = 0; (j < 13) && (pData[j+5] != 0x0A); ++j) + { + pEEld->mn_sand_sads[ulIndex] = pData[j+5]; + ulIndex++; + } + pEEld->mnl = j; + break; + } + } + } + + //Check if number of SAD Bytes > 0 and for size within limits of allowed Base line Data size as per EELD spec + if((ulNumSADBytes > 0) && (ulNumSADBytes <= 64)) + { + //Copy the SADs immediately after the Monitor Name String + memcpy(&pEEld->mn_sand_sads[j], pSADBlocks, ulNumSADBytes); + } + + + // Header = 4, Baseline Data = 60 and Vendor (INTEL) specific = 2 + // 4 + 60 + 2 = 66 + hdmi_priv->hdmi_eeld_size = HDMI_EELD_SIZE; + + //free the buffer allocated for SAD blocks + kfree(pSADBlocks); + pSADBlocks = NULL; + pCurrentSADBlocks = NULL; + return 1; +} + +#endif + +static enum drm_connector_status mdfld_hdmi_detect(struct drm_connector *connector, bool force) +{ + struct psb_intel_output *psb_intel_output = to_psb_intel_output(connector); + struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_output->dev_priv; + struct edid *edid = NULL; + enum drm_connector_status status = connector_status_disconnected; + + PSB_DEBUG_ENTRY("\n"); + + edid = drm_get_edid(&psb_intel_output->base, + psb_intel_output->hdmi_i2c_adapter); + + hdmi_priv->has_hdmi_sink = false; + hdmi_priv->has_hdmi_audio = false; + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + status = connector_status_connected; + hdmi_priv->has_hdmi_sink = drm_detect_hdmi_monitor(edid); + hdmi_priv->has_hdmi_audio = drm_detect_monitor_audio(edid); + } + + psb_intel_output->base.display_info.raw_edid = NULL; + kfree(edid); + } + + return status; +} + +static int mdfld_hdmi_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *pEncoder = connector->encoder; + + PSB_DEBUG_ENTRY("connector info, type = %d, type_id=%d, base=0x%p, base.id=0x%x. \n", connector->connector_type, connector->connector_type_id, &connector->base, connector->base.id); + PSB_DEBUG_ENTRY("encoder info, base.id=%d, encoder_type=%d, dev=0x%p, base=0x%p, possible_clones=0x%x. \n", pEncoder->base.id, pEncoder->encoder_type, pEncoder->dev, &pEncoder->base, pEncoder->possible_clones); + PSB_DEBUG_ENTRY("encoder info, possible_crtcs=0x%x, crtc=0x%p. \n", pEncoder->possible_crtcs, pEncoder->crtc); + + if (!strcmp(property->name, "scaling mode") && pEncoder) { + PSB_DEBUG_ENTRY("scaling mode \n"); + } else if (!strcmp(property->name, "backlight") && pEncoder) { + PSB_DEBUG_ENTRY("backlight \n"); + } else if (!strcmp(property->name, "DPMS") && pEncoder) { + PSB_DEBUG_ENTRY("DPMS \n"); + } + + if (!strcmp(property->name, "scaling mode") && pEncoder) { + struct psb_intel_crtc *pPsbCrtc = to_psb_intel_crtc(pEncoder->crtc); + bool bTransitionFromToCentered; + uint64_t curValue; + + if (!pPsbCrtc) + goto set_prop_error; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + goto set_prop_error; + } + + if (drm_connector_property_get_value(connector, property, &curValue)) + goto set_prop_error; + + if (curValue == value) + goto set_prop_done; + + if (drm_connector_property_set_value(connector, property, value)) + goto set_prop_error; + + bTransitionFromToCentered = (curValue == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (pPsbCrtc->saved_mode.hdisplay != 0 && + pPsbCrtc->saved_mode.vdisplay != 0) { + if (bTransitionFromToCentered) { + if (!drm_crtc_helper_set_mode(pEncoder->crtc, &pPsbCrtc->saved_mode, + pEncoder->crtc->x, pEncoder->crtc->y, pEncoder->crtc->fb)) + goto set_prop_error; + } else { + struct drm_encoder_helper_funcs *pEncHFuncs = pEncoder->helper_private; + pEncHFuncs->mode_set(pEncoder, &pPsbCrtc->saved_mode, + &pPsbCrtc->saved_adjusted_mode); + } + } + } +set_prop_done: + return 0; +set_prop_error: + return -1; +} + +/** + * Return the list of HDMI DDC modes if available. + */ +static int mdfld_hdmi_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *psb_intel_output = to_psb_intel_output(connector); + struct edid *edid = NULL; + int ret = 0; + + PSB_DEBUG_ENTRY("\n"); + + edid = drm_get_edid(&psb_intel_output->base, + psb_intel_output->hdmi_i2c_adapter); + if (edid) { + drm_mode_connector_update_edid_property(&psb_intel_output-> + base, edid); + ret = drm_add_edid_modes(&psb_intel_output->base, edid); + kfree(edid); + } + + return ret; +} + +static int mdfld_hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + + PSB_DEBUG_ENTRY("display info. hdisplay = %d, vdisplay = %d. \n", mode->hdisplay, mode->vdisplay); + + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + if (mode->clock < 20000) + return MODE_CLOCK_HIGH; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + /* just in case */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + return MODE_OK; +} + +static const struct drm_encoder_helper_funcs mdfld_hdmi_helper_funcs = { + .dpms = mdfld_hdmi_dpms, + .mode_fixup = mdfld_hdmi_mode_fixup, + .prepare = psb_intel_encoder_prepare, + .mode_set = mdfld_hdmi_mode_set, + .commit = psb_intel_encoder_commit, +}; + +static const struct drm_connector_helper_funcs mdfld_hdmi_connector_helper_funcs = { + .get_modes = mdfld_hdmi_get_modes, + .mode_valid = mdfld_hdmi_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_connector_funcs mdfld_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .save = mdfld_hdmi_save, + .restore = mdfld_hdmi_restore, + .detect = mdfld_hdmi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = mdfld_hdmi_set_property, + .destroy = psb_intel_lvds_destroy, +}; + +void mdfld_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int reg) +{ + struct psb_intel_output *psb_intel_output; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct mid_intel_hdmi_priv *hdmi_priv; + int ddc_bus; + + PSB_DEBUG_ENTRY("\n"); + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + + sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL); + if (!psb_intel_output) + return; + + hdmi_priv = (struct mid_intel_hdmi_priv *)(psb_intel_output + 1); + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + drm_connector_init(dev, &psb_intel_output->base, + &mdfld_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + + drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + psb_intel_output->type = INTEL_OUTPUT_HDMI; + hdmi_priv->hdmi_reg = reg; + hdmi_priv->has_hdmi_sink = false; + psb_intel_output->dev_priv = hdmi_priv; + + drm_encoder_helper_add(encoder, &mdfld_hdmi_helper_funcs); + drm_connector_helper_add(connector, + &mdfld_hdmi_connector_helper_funcs); + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); + + switch (reg) { + case SDVOB: + ddc_bus = GPIOE; + break; + case SDVOC: + ddc_bus = GPIOD; + break; + default: + DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); + goto failed_ddc; + break; + } + + psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, + ddc_bus, (reg == SDVOB) ? "HDMIB":"HDMIC"); + + if (!psb_intel_output->ddc_bus) { + DRM_ERROR("No ddc adapter available!\n"); + goto failed_ddc; + } + psb_intel_output->hdmi_i2c_adapter = &(psb_intel_output->ddc_bus->adapter); + + hdmi_priv->is_hdcp_supported = true; + hdmi_priv->dev = dev; + drm_sysfs_connector_add(connector); + return; + +failed_ddc: + drm_encoder_cleanup(&psb_intel_output->enc); + drm_connector_cleanup(&psb_intel_output->base); + kfree(psb_intel_output); +} |