/* * Copyright 2007-11 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie * Alex Deucher
*/
/* Mac laptops with multiple GPUs use the gmux driver for backlight * so don't register a backlight device
*/ if ((rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) &&
(rdev->pdev->device == 0x6741) &&
!(dmi_match(DMI_PRODUCT_NAME, "iMac12,1") || dmi_match(DMI_PRODUCT_NAME, "iMac12,2"))) return;
if (!radeon_encoder->enc_priv) return;
if (!rdev->is_atom_bios) return;
if (!(rdev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) return;
if (!acpi_video_backlight_use_native()) {
drm_info(dev, "Skipping radeon atom DIG backlight registration\n"); return;
}
bd->props.brightness = radeon_atom_backlight_get_brightness(bd); /* Set a reasonable default here if the level is 0 otherwise * fbdev will attempt to turn the backlight on after console * unblanking and it will try and restore 0 which turns the backlight * off again.
*/ if (bd->props.brightness == 0)
bd->props.brightness = RADEON_MAX_BL_LEVEL;
bd->props.power = BACKLIGHT_POWER_ON;
backlight_update_status(bd);
DRM_INFO("radeon atom DIG backlight initialized\n");
rdev->mode_info.bl_encoder = radeon_encoder;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); break;
}
args.ucAction = action;
if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT))
args.ucDacStandard = ATOM_DAC1_PS2; elseif (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
args.ucDacStandard = ATOM_DAC1_CV; else { switch (dac_info->tv_std) { case TV_STD_PAL: case TV_STD_PAL_M: case TV_STD_SCART_PAL: case TV_STD_SECAM: case TV_STD_PAL_CN:
args.ucDacStandard = ATOM_DAC1_PAL; break; case TV_STD_NTSC: case TV_STD_NTSC_J: case TV_STD_PAL_60: default:
args.ucDacStandard = ATOM_DAC1_NTSC; break;
}
}
args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
switch (bpc) { case 0: return PANEL_BPC_UNDEFINE; case 6: return PANEL_6BIT_PER_COLOR; case 8: default: return PANEL_8BIT_PER_COLOR; case 10: return PANEL_10BIT_PER_COLOR; case 12: return PANEL_12BIT_PER_COLOR; case 16: return PANEL_16BIT_PER_COLOR;
}
}
union lvds_encoder_control {
LVDS_ENCODER_CONTROL_PS_ALLOCATION v1;
LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
};
void
atombios_digital_setup(struct drm_encoder *encoder, int action)
{ struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; union lvds_encoder_control args; int index = 0; int hdmi_detected = 0;
uint8_t frev, crev;
if (!dig) return;
if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
hdmi_detected = 1;
memset(&args, 0, sizeof(args));
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_LVDS:
index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); break; case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl); break; case ENCODER_OBJECT_ID_INTERNAL_LVTM1: if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); else
index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl); break;
}
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) return;
switch (frev) { case 1: case 2: switch (crev) { case 1:
args.v1.ucMisc = 0;
args.v1.ucAction = action; if (hdmi_detected)
args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
} else { if (dig->linkb)
args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock))
args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; /*if (pScrn->rgbBits == 8) */
args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB;
} break; case 2: case 3:
args.v2.ucMisc = 0;
args.v2.ucAction = action; if (crev == 3) { if (dig->coherent_mode)
args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
} if (hdmi_detected)
args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
args.v2.ucTruncate = 0;
args.v2.ucSpatial = 0;
args.v2.ucTemporal = 0;
args.v2.ucFRC = 0; if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) {
args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN; if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
} if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) {
args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN; if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH; if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
}
} else { if (dig->linkb)
args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock))
args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
} break; default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev); break;
} break; default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev); break;
}
/* dp bridges are always DP */ if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) return ATOM_ENCODER_MODE_DP;
/* DVO is always DVO */ if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DVO1) ||
(radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)) return ATOM_ENCODER_MODE_DVO;
connector = radeon_get_connector_for_encoder(encoder); /* if we don't have an active device yet, just use one of * the connectors tied to the encoder.
*/ if (!connector)
connector = radeon_get_connector_for_encoder_init(encoder);
radeon_connector = to_radeon_connector(connector);
switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ if (radeon_audio != 0) { if (radeon_connector->use_digital &&
(radeon_connector->audio == RADEON_AUDIO_ENABLE)) return ATOM_ENCODER_MODE_HDMI; elseif (connector->display_info.is_hdmi &&
(radeon_connector->audio == RADEON_AUDIO_AUTO)) return ATOM_ENCODER_MODE_HDMI; elseif (radeon_connector->use_digital) return ATOM_ENCODER_MODE_DVI; else return ATOM_ENCODER_MODE_CRT;
} elseif (radeon_connector->use_digital) { return ATOM_ENCODER_MODE_DVI;
} else { return ATOM_ENCODER_MODE_CRT;
} break; case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_HDMIA: default: if (radeon_audio != 0) { if (radeon_connector->audio == RADEON_AUDIO_ENABLE) return ATOM_ENCODER_MODE_HDMI; elseif (connector->display_info.is_hdmi &&
(radeon_connector->audio == RADEON_AUDIO_AUTO)) return ATOM_ENCODER_MODE_HDMI; else return ATOM_ENCODER_MODE_DVI;
} else { return ATOM_ENCODER_MODE_DVI;
} break; case DRM_MODE_CONNECTOR_LVDS: return ATOM_ENCODER_MODE_LVDS; break; case DRM_MODE_CONNECTOR_DisplayPort:
dig_connector = radeon_connector->con_priv; if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { if (radeon_audio != 0 &&
connector->display_info.has_audio &&
ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) return ATOM_ENCODER_MODE_DP_AUDIO; return ATOM_ENCODER_MODE_DP;
} elseif (radeon_audio != 0) { if (radeon_connector->audio == RADEON_AUDIO_ENABLE) return ATOM_ENCODER_MODE_HDMI; elseif (connector->display_info.is_hdmi &&
(radeon_connector->audio == RADEON_AUDIO_AUTO)) return ATOM_ENCODER_MODE_HDMI; else return ATOM_ENCODER_MODE_DVI;
} else { return ATOM_ENCODER_MODE_DVI;
} break; case DRM_MODE_CONNECTOR_eDP: if (radeon_audio != 0 &&
connector->display_info.has_audio &&
ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) return ATOM_ENCODER_MODE_DP_AUDIO; return ATOM_ENCODER_MODE_DP; case DRM_MODE_CONNECTOR_DVIA: case DRM_MODE_CONNECTOR_VGA: return ATOM_ENCODER_MODE_CRT; break; case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_9PinDIN: /* fix me */ return ATOM_ENCODER_MODE_TV; /*return ATOM_ENCODER_MODE_CV;*/ break;
}
}
/* * DIG Encoder/Transmitter Setup * * DCE 3.0/3.1 * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA. * Supports up to 3 digital outputs * - 2 DIG encoder blocks. * DIG1 can drive UNIPHY link A or link B * DIG2 can drive UNIPHY link B or LVTMA * * DCE 3.2 * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B). * Supports up to 5 digital outputs * - 2 DIG encoder blocks. * DIG1/2 can drive UNIPHY0/1/2 link A or link B * * DCE 4.0/5.0/6.0 * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). * Supports up to 6 digital outputs * - 6 DIG encoder blocks. * - DIG to PHY mapping is hardcoded * DIG1 drives UNIPHY0 link A, A+B * DIG2 drives UNIPHY0 link B * DIG3 drives UNIPHY1 link A, A+B * DIG4 drives UNIPHY1 link B * DIG5 drives UNIPHY2 link A, A+B * DIG6 drives UNIPHY2 link B * * DCE 4.1 * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). * Supports up to 6 digital outputs * - 2 DIG encoder blocks. * llano * DIG1/2 can drive UNIPHY0/1/2 link A or link B * ontario * DIG1 drives UNIPHY0/1/2 link A * DIG2 drives UNIPHY0/1/2 link B * * Routing * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links) * Examples: * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI * crtc1 -> dig1 -> UNIPHY0 link B -> DP * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
*/
/* no dig encoder assigned */ if (dig->dig_encoder == -1) return;
memset(&args, 0, sizeof(args));
if (ASIC_IS_DCE4(rdev))
index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl); else { if (dig->dig_encoder)
index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl); else
index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
}
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) return;
switch (frev) { case 1: switch (crev) { case 1:
args.v1.ucAction = action;
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
args.v3.ucPanelMode = panel_mode; else
args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
void
atombios_dig_transmitter_setup2(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set, int fe)
{ struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct drm_connector *connector; union dig_transmitter_control args; int index = 0;
uint8_t frev, crev; bool is_dp = false; int pll_id = 0; int dp_clock = 0; int dp_lane_count = 0; int connector_object_id = 0; int igp_lane_info = 0; int dig_encoder = dig->dig_encoder; int hpd_id = RADEON_HPD_NONE;
if (action == ATOM_TRANSMITTER_ACTION_INIT) {
connector = radeon_get_connector_for_encoder_init(encoder); /* just needed to avoid bailing in the encoder check. the encoder * isn't used for init
*/
dig_encoder = 0;
} else
connector = radeon_get_connector_for_encoder(encoder);
/* no dig encoder assigned */ if (dig_encoder == -1) return;
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)))
is_dp = true;
memset(&args, 0, sizeof(args));
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl); break;
}
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) return;
if (dig->linkb)
args.v3.acConfig.ucLinkSel = 1; if (dig_encoder & 1)
args.v3.acConfig.ucEncoderSel = 1;
/* Select the PLL for the PHY * DP PHY should be clocked from external src if there is * one.
*/ /* On DCE4, if there is an external clock, it generates the DP ref clock */ if (is_dp && rdev->clock.dp_extclk)
args.v3.acConfig.ucRefClkSource = 2; /* external src */ else
args.v3.acConfig.ucRefClkSource = pll_id;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
args.v3.acConfig.ucTransmitterSel = 0; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
args.v3.acConfig.ucTransmitterSel = 1; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
args.v3.acConfig.ucTransmitterSel = 2; break;
}
if (dig->linkb)
args.v4.acConfig.ucLinkSel = 1; if (dig_encoder & 1)
args.v4.acConfig.ucEncoderSel = 1;
/* Select the PLL for the PHY * DP PHY should be clocked from external src if there is * one.
*/ /* On DCE5 DCPLL usually generates the DP ref clock */ if (is_dp) { if (rdev->clock.dp_extclk)
args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK; else
args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL;
} else
args.v4.acConfig.ucRefClkSource = pll_id;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
args.v4.acConfig.ucTransmitterSel = 0; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
args.v4.acConfig.ucTransmitterSel = 1; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
args.v4.acConfig.ucTransmitterSel = 2; break;
}
if (is_dp)
args.v4.acConfig.fCoherentMode = 1; /* DP requires coherent */ elseif (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode)
args.v4.acConfig.fCoherentMode = 1; if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock))
args.v4.acConfig.fDualLinkConnector = 1;
} break; case 5:
args.v5.ucAction = action; if (is_dp)
args.v5.usSymClock = cpu_to_le16(dp_clock / 10); else
args.v5.usSymClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: if (dig->linkb)
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYB; else
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYA; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: if (dig->linkb)
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYD; else
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYC; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: if (dig->linkb)
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYF; else
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYG; break;
} if (is_dp)
args.v5.ucLaneNum = dp_lane_count; elseif (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock))
args.v5.ucLaneNum = 8; else
args.v5.ucLaneNum = 4;
args.v5.ucConnObjId = connector_object_id;
args.v5.ucDigMode = atombios_get_encoder_mode(encoder);
staticvoid
radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
{ struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; int index = 0;
memset(&args, 0, sizeof(args));
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_DVO1: case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_LVDS:
index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_LVTM1: if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); else
index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); elseif (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else
index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); elseif (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT))
index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else
index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); break; default: return;
}
switch (mode) { case DRM_MODE_DPMS_ON:
args.ucAction = ATOM_ENABLE; /* workaround for DVOOutputControl on some RS690 systems */ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) {
u32 reg = RREG32(RADEON_BIOS_3_SCRATCH);
WREG32(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE);
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
WREG32(RADEON_BIOS_3_SCRATCH, reg);
} else
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args)); if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (rdev->mode_info.bl_encoder) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_LVDS: case ENCODER_OBJECT_ID_INTERNAL_LVTM1: case ENCODER_OBJECT_ID_INTERNAL_DVO1: case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
radeon_atom_encoder_dpms_avivo(encoder, mode); break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
radeon_atom_encoder_dpms_dig(encoder, mode); break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: if (ASIC_IS_DCE5(rdev)) { switch (mode) { case DRM_MODE_DPMS_ON:
atombios_dvo_setup(encoder, ATOM_ENABLE); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF:
atombios_dvo_setup(encoder, ATOM_DISABLE); break;
}
} elseif (ASIC_IS_DCE3(rdev))
radeon_atom_encoder_dpms_dig(encoder, mode); else
radeon_atom_encoder_dpms_avivo(encoder, mode); break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: if (ASIC_IS_DCE5(rdev)) { switch (mode) { case DRM_MODE_DPMS_ON:
atombios_dac_setup(encoder, ATOM_ENABLE); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF:
atombios_dac_setup(encoder, ATOM_DISABLE); break;
}
} else
radeon_atom_encoder_dpms_avivo(encoder, mode); break; default: return;
}
if (fe_idx >= 0) {
enc_idx = fe_idx; goto assigned;
} if (ASIC_IS_DCE6(rdev)) { /* DCE6 */ switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: if (dig->linkb)
enc_idx = 1; else
enc_idx = 0; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: if (dig->linkb)
enc_idx = 3; else
enc_idx = 2; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: if (dig->linkb)
enc_idx = 5; else
enc_idx = 4; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
enc_idx = 6; break;
} goto assigned;
} elseif (ASIC_IS_DCE4(rdev)) { /* DCE4/5 */ if (ASIC_IS_DCE41(rdev) && !ASIC_IS_DCE61(rdev)) { /* ontario follows DCE4 */ if (rdev->family == CHIP_PALM) { if (dig->linkb)
enc_idx = 1; else
enc_idx = 0;
} else /* llano follows DCE3.2 */
enc_idx = radeon_crtc->crtc_id;
} else { switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: if (dig->linkb)
enc_idx = 1; else
enc_idx = 0; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: if (dig->linkb)
enc_idx = 3; else
enc_idx = 2; break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: if (dig->linkb)
enc_idx = 5; else
enc_idx = 4; break;
}
} goto assigned;
}
/* * On DCE32 any encoder can drive any block so usually just use crtc id, * but Apple thinks different at least on iMac10,1 and iMac11,2, so there use linkb, * otherwise the internal eDP panel will stay dark.
*/ if (ASIC_IS_DCE32(rdev)) { if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1") ||
dmi_match(DMI_PRODUCT_NAME, "iMac11,2"))
enc_idx = (dig->linkb) ? 1 : 0; else
enc_idx = radeon_crtc->crtc_id;
goto assigned;
}
/* on DCE3 - LVTMA can only be driven by DIGB */
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { struct radeon_encoder *radeon_test_encoder;
if (encoder == test_encoder) continue;
if (!radeon_encoder_is_digital(test_encoder)) continue;
if (dig->dig_encoder >= 0)
dig_enc_in_use |= (1 << dig->dig_encoder);
}
if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) { if (dig_enc_in_use & 0x2)
DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n"); return 1;
} if (!(dig_enc_in_use & 1)) return 0; return 1;
assigned: if (enc_idx == -1) {
DRM_ERROR("Got encoder index incorrect - returning 0\n"); return 0;
} if (rdev->mode_info.active_encoders & (1 << enc_idx))
DRM_ERROR("chosen encoder in use %d\n", enc_idx);
/* This only needs to be called once at startup */ void
radeon_atom_encoder_init(struct radeon_device *rdev)
{ struct drm_device *dev = rdev_to_drm(rdev); struct drm_encoder *encoder;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); break; default: break;
}
/* need to call this here rather than in prepare() since we need some crtc info */
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) { if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
atombios_yuv_setup(encoder, true); else
atombios_yuv_setup(encoder, false);
}
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_LVDS: case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE); break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* handled in dpms */ break; case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_DVO1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
atombios_dvo_setup(encoder, ATOM_ENABLE); break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
atombios_dac_setup(encoder, ATOM_ENABLE); if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) { if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
atombios_tv_setup(encoder, ATOM_ENABLE); else
atombios_tv_setup(encoder, ATOM_DISABLE);
} break;
}
if ((radeon_encoder->active_device &
(ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
(radeon_encoder_get_dp_bridge_encoder_id(encoder) !=
ENCODER_OBJECT_ID_NONE)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; if (dig) { if (dig->dig_encoder >= 0)
radeon_atom_release_dig_encoder(rdev, dig->dig_encoder);
dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder, -1); if (radeon_encoder->active_device & ATOM_DEVICE_DFP_SUPPORT) { if (rdev->family >= CHIP_R600)
dig->afmt = rdev->mode_info.afmt[dig->dig_encoder]; else /* RS600/690/740 have only 1 afmt block */
dig->afmt = rdev->mode_info.afmt[0];
}
}
}
radeon_atom_output_lock(encoder, true);
if (connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector);
/* select the clock/data port if it uses a router */ if (radeon_connector->router.cd_valid)
radeon_router_select_cd_port(radeon_connector);
/* turn eDP panel on for mode set */ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_ON);
}
/* this is needed for the pll/ss setup to work correctly in some cases */
atombios_set_encoder_crtc_source(encoder); /* set up the FMT blocks */ if (ASIC_IS_DCE8(rdev))
dce8_program_fmt(encoder); elseif (ASIC_IS_DCE4(rdev))
dce4_program_fmt(encoder); elseif (ASIC_IS_DCE3(rdev))
dce3_program_fmt(encoder); elseif (ASIC_IS_AVIVO(rdev))
avivo_program_fmt(encoder);
}
staticvoid radeon_atom_encoder_commit(struct drm_encoder *encoder)
{ /* need to call this here as we need the crtc set up */
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
radeon_atom_output_lock(encoder, false);
}
/* check for pre-DCE3 cards with shared encoders; * can't really use the links individually, so don't disable * the encoder if it's in use by another connector
*/ if (!ASIC_IS_DCE3(rdev)) { struct drm_encoder *other_encoder; struct radeon_encoder *other_radeon_encoder;
switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_LVDS: case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_DISABLE); break; case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* handled in dpms */ break; case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_DVO1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
atombios_dvo_setup(encoder, ATOM_DISABLE); break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
atombios_dac_setup(encoder, ATOM_DISABLE); if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
atombios_tv_setup(encoder, ATOM_DISABLE); break;
}
disable_done: if (radeon_encoder_is_digital(encoder)) { if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) { if (rdev->asic->display.hdmi_enable)
radeon_hdmi_enable(rdev, encoder, false);
} if (atombios_get_encoder_mode(encoder) != ATOM_ENCODER_MODE_DP_MST) {
dig = radeon_encoder->enc_priv;
radeon_atom_release_dig_encoder(rdev, dig->dig_encoder);
dig->dig_encoder = -1;
radeon_encoder->active_device = 0;
}
} else
radeon_encoder->active_device = 0;
}
/* these are handled by the primary encoders */ staticvoid radeon_atom_ext_prepare(struct drm_encoder *encoder)
{
/* see if we already added it */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
radeon_encoder = to_radeon_encoder(encoder); if (radeon_encoder->encoder_enum == encoder_enum) {
radeon_encoder->devices |= supported_device; return;
}
}
/* add a new one */
radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); if (!radeon_encoder) return;
encoder = &radeon_encoder->base; switch (rdev->num_crtc) { case 1:
encoder->possible_crtcs = 0x1; break; case 2: default:
encoder->possible_crtcs = 0x3; break; case 4:
encoder->possible_crtcs = 0xf; break; case 6:
encoder->possible_crtcs = 0x3f; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.