forked from Minki/linux
- HDCP 2.2 and HDCP 1.4 Gen12 DP MST support (Anshuman)
- Fix DP vswing settings and handling (Imre, Ville) - Various display code clean-up (Jani, Ville) - Various display refactoring, including split out of pps, aux, and fdi (Ja\ ni, Dave) - Add DG1 missing workarounds (Jose) - Fix display color conversion (Chris, Ville) - Try to guess PCH type even without ISA bridge (Zhenyu) - More backlight refactor (Lyude) - Support two CSC module on gen11 and later (Lee) - Async flips for all ilk+ platforms (Ville) - Clear color support for TGL (RK) - Add a helper to read data from a GEM object page (Imre) - VRR/Adaptive Sync Enabling on DP/eDP for TGL+ (Manasi, Ville Aditya) -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEbSBwaO7dZQkcLOKj+mJfZA7rE8oFAmARc1oACgkQ+mJfZA7r E8ovFAgAhBlnhrquGrgtuRmS5tkpLQShqiwrPv7ogDBkcayJ6Is+AO+FoE8kuzZO q/TW2enkcRKFHhQnm9RA0Ayk+rwYqkEOcBU5gvzusdwbEjkygJfH/yA2ukAmaw32 QxP9A2pW+iWsEcqtMuOEu1ahu0Z8gnkCe8DakuBMf/0y5D9DairwaCC8nyZQdEJq YFTxp+pILE57PhOVbKS3EZPbnl5t28NgCtMc8hjlPTWZxRSVV0eDrY2tx9vyhFhU Eq8aLDxPKCRm18sg+J+lDytOvFoljXcglAEwPODVMmDNolG8R6aRQ9T6tG6qfxyI UO1KyeYUHHEIJ4IBJKK+KWeUWLLBhA== =3Jc8 -----END PGP SIGNATURE----- Merge tag 'drm-intel-next-2021-01-27' of git://anongit.freedesktop.org/drm/drm-intel into drm-next - HDCP 2.2 and HDCP 1.4 Gen12 DP MST support (Anshuman) - Fix DP vswing settings and handling (Imre, Ville) - Various display code clean-up (Jani, Ville) - Various display refactoring, including split out of pps, aux, and fdi (Ja\ ni, Dave) - Add DG1 missing workarounds (Jose) - Fix display color conversion (Chris, Ville) - Try to guess PCH type even without ISA bridge (Zhenyu) - More backlight refactor (Lyude) - Support two CSC module on gen11 and later (Lee) - Async flips for all ilk+ platforms (Ville) - Clear color support for TGL (RK) - Add a helper to read data from a GEM object page (Imre) - VRR/Adaptive Sync Enabling on DP/eDP for TGL+ (Manasi, Ville Aditya) Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20210127140822.GA711686@intel.com
This commit is contained in:
commit
32c3d9b0f5
@ -1236,7 +1236,7 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
|
||||
return connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
|
||||
dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 &&
|
||||
dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT &&
|
||||
!drm_dp_has_quirk(desc, 0, DP_DPCD_QUIRK_NO_SINK_COUNT);
|
||||
!drm_dp_has_quirk(desc, DP_DPCD_QUIRK_NO_SINK_COUNT);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_sink_count_cap);
|
||||
|
||||
@ -1957,87 +1957,6 @@ drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
|
||||
#undef DEVICE_ID_ANY
|
||||
#undef DEVICE_ID
|
||||
|
||||
struct edid_quirk {
|
||||
u8 mfg_id[2];
|
||||
u8 prod_id[2];
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
#define MFG(first, second) { (first), (second) }
|
||||
#define PROD_ID(first, second) { (first), (second) }
|
||||
|
||||
/*
|
||||
* Some devices have unreliable OUIDs where they don't set the device ID
|
||||
* correctly, and as a result we need to use the EDID for finding additional
|
||||
* DP quirks in such cases.
|
||||
*/
|
||||
static const struct edid_quirk edid_quirk_list[] = {
|
||||
/* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation
|
||||
* only supports DPCD backlight controls
|
||||
*/
|
||||
{ MFG(0x4c, 0x83), PROD_ID(0x41, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
/*
|
||||
* Some Dell CML 2020 systems have panels support both AUX and PWM
|
||||
* backlight control, and some only support AUX backlight control. All
|
||||
* said panels start up in AUX mode by default, and we don't have any
|
||||
* support for disabling HDR mode on these panels which would be
|
||||
* required to switch to PWM backlight control mode (plus, I'm not
|
||||
* even sure we want PWM backlight controls over DPCD backlight
|
||||
* controls anyway...). Until we have a better way of detecting these,
|
||||
* force DPCD backlight mode on all of them.
|
||||
*/
|
||||
{ MFG(0x06, 0xaf), PROD_ID(0x9b, 0x32), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
{ MFG(0x06, 0xaf), PROD_ID(0xeb, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
{ MFG(0x4d, 0x10), PROD_ID(0xc7, 0x14), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
{ MFG(0x4d, 0x10), PROD_ID(0xe6, 0x14), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
{ MFG(0x4c, 0x83), PROD_ID(0x47, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
{ MFG(0x09, 0xe5), PROD_ID(0xde, 0x08), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) },
|
||||
};
|
||||
|
||||
#undef MFG
|
||||
#undef PROD_ID
|
||||
|
||||
/**
|
||||
* drm_dp_get_edid_quirks() - Check the EDID of a DP device to find additional
|
||||
* DP-specific quirks
|
||||
* @edid: The EDID to check
|
||||
*
|
||||
* While OUIDs are meant to be used to recognize a DisplayPort device, a lot
|
||||
* of manufacturers don't seem to like following standards and neglect to fill
|
||||
* the dev-ID in, making it impossible to only use OUIDs for determining
|
||||
* quirks in some cases. This function can be used to check the EDID and look
|
||||
* up any additional DP quirks. The bits returned by this function correspond
|
||||
* to the quirk bits in &drm_dp_quirk.
|
||||
*
|
||||
* Returns: a bitmask of quirks, if any. The driver can check this using
|
||||
* drm_dp_has_quirk().
|
||||
*/
|
||||
u32 drm_dp_get_edid_quirks(const struct edid *edid)
|
||||
{
|
||||
const struct edid_quirk *quirk;
|
||||
u32 quirks = 0;
|
||||
int i;
|
||||
|
||||
if (!edid)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
|
||||
quirk = &edid_quirk_list[i];
|
||||
if (memcmp(quirk->mfg_id, edid->mfg_id,
|
||||
sizeof(edid->mfg_id)) == 0 &&
|
||||
memcmp(quirk->prod_id, edid->prod_code,
|
||||
sizeof(edid->prod_code)) == 0)
|
||||
quirks |= quirk->quirks;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("DP sink: EDID mfg %*phD prod-ID %*phD quirks: 0x%04x\n",
|
||||
(int)sizeof(edid->mfg_id), edid->mfg_id,
|
||||
(int)sizeof(edid->prod_code), edid->prod_code, quirks);
|
||||
|
||||
return quirks;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_get_edid_quirks);
|
||||
|
||||
/**
|
||||
* drm_dp_read_desc - read sink/branch descriptor from DPCD
|
||||
* @aux: DisplayPort AUX channel
|
||||
|
@ -5824,8 +5824,7 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
|
||||
if (drm_dp_read_desc(port->mgr->aux, &desc, true))
|
||||
return NULL;
|
||||
|
||||
if (drm_dp_has_quirk(&desc, 0,
|
||||
DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
|
||||
if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
|
||||
port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
|
||||
port->parent == port->mgr->mst_primary) {
|
||||
u8 downstreamport;
|
||||
|
@ -201,14 +201,17 @@ i915-y += \
|
||||
display/intel_color.o \
|
||||
display/intel_combo_phy.o \
|
||||
display/intel_connector.o \
|
||||
display/intel_crtc.o \
|
||||
display/intel_csr.o \
|
||||
display/intel_cursor.o \
|
||||
display/intel_display.o \
|
||||
display/intel_display_power.o \
|
||||
display/intel_dpio_phy.o \
|
||||
display/intel_dpll.o \
|
||||
display/intel_dpll_mgr.o \
|
||||
display/intel_dsb.o \
|
||||
display/intel_fbc.o \
|
||||
display/intel_fdi.o \
|
||||
display/intel_fifo_underrun.o \
|
||||
display/intel_frontbuffer.o \
|
||||
display/intel_global_state.o \
|
||||
@ -240,6 +243,7 @@ i915-y += \
|
||||
display/intel_crt.o \
|
||||
display/intel_ddi.o \
|
||||
display/intel_dp.o \
|
||||
display/intel_dp_aux.o \
|
||||
display/intel_dp_aux_backlight.o \
|
||||
display/intel_dp_hdcp.o \
|
||||
display/intel_dp_link_training.o \
|
||||
@ -253,9 +257,11 @@ i915-y += \
|
||||
display/intel_lspcon.o \
|
||||
display/intel_lvds.o \
|
||||
display/intel_panel.o \
|
||||
display/intel_pps.o \
|
||||
display/intel_sdvo.o \
|
||||
display/intel_tv.o \
|
||||
display/intel_vdsc.o \
|
||||
display/intel_vrr.o \
|
||||
display/vlv_dsi.o \
|
||||
display/vlv_dsi_pll.o
|
||||
|
||||
|
@ -452,7 +452,7 @@ void intel_update_plane(struct intel_plane *plane,
|
||||
trace_intel_update_plane(&plane->base, crtc);
|
||||
|
||||
if (crtc_state->uapi.async_flip && plane->async_flip)
|
||||
plane->async_flip(plane, crtc_state, plane_state);
|
||||
plane->async_flip(plane, crtc_state, plane_state, true);
|
||||
else
|
||||
plane->update_plane(plane, crtc_state, plane_state);
|
||||
}
|
||||
|
@ -1485,6 +1485,7 @@ static u32 ivb_csc_mode(const struct intel_crtc_state *crtc_state)
|
||||
|
||||
static int ivb_color_check(struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev);
|
||||
bool limited_color_range = ilk_csc_limited_range(crtc_state);
|
||||
int ret;
|
||||
|
||||
@ -1492,6 +1493,13 @@ static int ivb_color_check(struct intel_crtc_state *crtc_state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
|
||||
crtc_state->hw.ctm) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"YCBCR and CTM together are not possible\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc_state->gamma_enable =
|
||||
(crtc_state->hw.gamma_lut ||
|
||||
crtc_state->hw.degamma_lut) &&
|
||||
@ -1525,12 +1533,20 @@ static u32 glk_gamma_mode(const struct intel_crtc_state *crtc_state)
|
||||
|
||||
static int glk_color_check(struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev);
|
||||
int ret;
|
||||
|
||||
ret = check_luts(crtc_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB &&
|
||||
crtc_state->hw.ctm) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"YCBCR and CTM together are not possible\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc_state->gamma_enable =
|
||||
crtc_state->hw.gamma_lut &&
|
||||
!crtc_state->c8_planes;
|
||||
|
325
drivers/gpu/drm/i915/display/intel_crtc.c
Normal file
325
drivers/gpu/drm/i915/display/intel_crtc.c
Normal file
@ -0,0 +1,325 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
|
||||
#include "intel_atomic.h"
|
||||
#include "intel_atomic_plane.h"
|
||||
#include "intel_color.h"
|
||||
#include "intel_crtc.h"
|
||||
#include "intel_cursor.h"
|
||||
#include "intel_display_debugfs.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_pipe_crc.h"
|
||||
#include "intel_sprite.h"
|
||||
#include "i9xx_plane.h"
|
||||
|
||||
static void assert_vblank_disabled(struct drm_crtc *crtc)
|
||||
{
|
||||
if (I915_STATE_WARN_ON(drm_crtc_vblank_get(crtc) == 0))
|
||||
drm_crtc_vblank_put(crtc);
|
||||
}
|
||||
|
||||
u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)];
|
||||
|
||||
if (!vblank->max_vblank_count)
|
||||
return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
|
||||
|
||||
return crtc->base.funcs->get_vblank_counter(&crtc->base);
|
||||
}
|
||||
|
||||
u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev);
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
u32 mode_flags = crtc->mode_flags;
|
||||
|
||||
/*
|
||||
* From Gen 11, In case of dsi cmd mode, frame counter wouldnt
|
||||
* have updated at the beginning of TE, if we want to use
|
||||
* the hw counter, then we would find it updated in only
|
||||
* the next TE, hence switching to sw counter.
|
||||
*/
|
||||
if (mode_flags & (I915_MODE_FLAG_DSI_USE_TE0 | I915_MODE_FLAG_DSI_USE_TE1))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* On i965gm the hardware frame counter reads
|
||||
* zero when the TV encoder is enabled :(
|
||||
*/
|
||||
if (IS_I965GM(dev_priv) &&
|
||||
(crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT)))
|
||||
return 0;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv))
|
||||
return 0xffffffff; /* full 32 bit counter */
|
||||
else if (INTEL_GEN(dev_priv) >= 3)
|
||||
return 0xffffff; /* only 24 bits of frame count */
|
||||
else
|
||||
return 0; /* Gen2 doesn't have a hardware frame counter */
|
||||
}
|
||||
|
||||
void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
|
||||
assert_vblank_disabled(&crtc->base);
|
||||
drm_crtc_set_max_vblank_count(&crtc->base,
|
||||
intel_crtc_max_vblank_count(crtc_state));
|
||||
drm_crtc_vblank_on(&crtc->base);
|
||||
}
|
||||
|
||||
void intel_crtc_vblank_off(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
|
||||
drm_crtc_vblank_off(&crtc->base);
|
||||
assert_vblank_disabled(&crtc->base);
|
||||
}
|
||||
|
||||
struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc)
|
||||
{
|
||||
struct intel_crtc_state *crtc_state;
|
||||
|
||||
crtc_state = kmalloc(sizeof(*crtc_state), GFP_KERNEL);
|
||||
|
||||
if (crtc_state)
|
||||
intel_crtc_state_reset(crtc_state, crtc);
|
||||
|
||||
return crtc_state;
|
||||
}
|
||||
|
||||
void intel_crtc_state_reset(struct intel_crtc_state *crtc_state,
|
||||
struct intel_crtc *crtc)
|
||||
{
|
||||
memset(crtc_state, 0, sizeof(*crtc_state));
|
||||
|
||||
__drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base);
|
||||
|
||||
crtc_state->cpu_transcoder = INVALID_TRANSCODER;
|
||||
crtc_state->master_transcoder = INVALID_TRANSCODER;
|
||||
crtc_state->hsw_workaround_pipe = INVALID_PIPE;
|
||||
crtc_state->output_format = INTEL_OUTPUT_FORMAT_INVALID;
|
||||
crtc_state->scaler_state.scaler_id = -1;
|
||||
crtc_state->mst_master_transcoder = INVALID_TRANSCODER;
|
||||
}
|
||||
|
||||
static struct intel_crtc *intel_crtc_alloc(void)
|
||||
{
|
||||
struct intel_crtc_state *crtc_state;
|
||||
struct intel_crtc *crtc;
|
||||
|
||||
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
crtc_state = intel_crtc_state_alloc(crtc);
|
||||
if (!crtc_state) {
|
||||
kfree(crtc);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
crtc->base.state = &crtc_state->uapi;
|
||||
crtc->config = crtc_state;
|
||||
|
||||
return crtc;
|
||||
}
|
||||
|
||||
static void intel_crtc_free(struct intel_crtc *crtc)
|
||||
{
|
||||
intel_crtc_destroy_state(&crtc->base, crtc->base.state);
|
||||
kfree(crtc);
|
||||
}
|
||||
|
||||
static void intel_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
|
||||
drm_crtc_cleanup(crtc);
|
||||
kfree(intel_crtc);
|
||||
}
|
||||
|
||||
static int intel_crtc_late_register(struct drm_crtc *crtc)
|
||||
{
|
||||
intel_crtc_debugfs_add(crtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define INTEL_CRTC_FUNCS \
|
||||
.set_config = drm_atomic_helper_set_config, \
|
||||
.destroy = intel_crtc_destroy, \
|
||||
.page_flip = drm_atomic_helper_page_flip, \
|
||||
.atomic_duplicate_state = intel_crtc_duplicate_state, \
|
||||
.atomic_destroy_state = intel_crtc_destroy_state, \
|
||||
.set_crc_source = intel_crtc_set_crc_source, \
|
||||
.verify_crc_source = intel_crtc_verify_crc_source, \
|
||||
.get_crc_sources = intel_crtc_get_crc_sources, \
|
||||
.late_register = intel_crtc_late_register
|
||||
|
||||
static const struct drm_crtc_funcs bdw_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = g4x_get_vblank_counter,
|
||||
.enable_vblank = bdw_enable_vblank,
|
||||
.disable_vblank = bdw_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs ilk_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = g4x_get_vblank_counter,
|
||||
.enable_vblank = ilk_enable_vblank,
|
||||
.disable_vblank = ilk_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs g4x_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = g4x_get_vblank_counter,
|
||||
.enable_vblank = i965_enable_vblank,
|
||||
.disable_vblank = i965_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs i965_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = i915_get_vblank_counter,
|
||||
.enable_vblank = i965_enable_vblank,
|
||||
.disable_vblank = i965_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs i915gm_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = i915_get_vblank_counter,
|
||||
.enable_vblank = i915gm_enable_vblank,
|
||||
.disable_vblank = i915gm_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs i915_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
.get_vblank_counter = i915_get_vblank_counter,
|
||||
.enable_vblank = i8xx_enable_vblank,
|
||||
.disable_vblank = i8xx_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs i8xx_crtc_funcs = {
|
||||
INTEL_CRTC_FUNCS,
|
||||
|
||||
/* no hw vblank counter */
|
||||
.enable_vblank = i8xx_enable_vblank,
|
||||
.disable_vblank = i8xx_disable_vblank,
|
||||
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
|
||||
};
|
||||
|
||||
int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe)
|
||||
{
|
||||
struct intel_plane *primary, *cursor;
|
||||
const struct drm_crtc_funcs *funcs;
|
||||
struct intel_crtc *crtc;
|
||||
int sprite, ret;
|
||||
|
||||
crtc = intel_crtc_alloc();
|
||||
if (IS_ERR(crtc))
|
||||
return PTR_ERR(crtc);
|
||||
|
||||
crtc->pipe = pipe;
|
||||
crtc->num_scalers = RUNTIME_INFO(dev_priv)->num_scalers[pipe];
|
||||
|
||||
primary = intel_primary_plane_create(dev_priv, pipe);
|
||||
if (IS_ERR(primary)) {
|
||||
ret = PTR_ERR(primary);
|
||||
goto fail;
|
||||
}
|
||||
crtc->plane_ids_mask |= BIT(primary->id);
|
||||
|
||||
for_each_sprite(dev_priv, pipe, sprite) {
|
||||
struct intel_plane *plane;
|
||||
|
||||
plane = intel_sprite_plane_create(dev_priv, pipe, sprite);
|
||||
if (IS_ERR(plane)) {
|
||||
ret = PTR_ERR(plane);
|
||||
goto fail;
|
||||
}
|
||||
crtc->plane_ids_mask |= BIT(plane->id);
|
||||
}
|
||||
|
||||
cursor = intel_cursor_plane_create(dev_priv, pipe);
|
||||
if (IS_ERR(cursor)) {
|
||||
ret = PTR_ERR(cursor);
|
||||
goto fail;
|
||||
}
|
||||
crtc->plane_ids_mask |= BIT(cursor->id);
|
||||
|
||||
if (HAS_GMCH(dev_priv)) {
|
||||
if (IS_CHERRYVIEW(dev_priv) ||
|
||||
IS_VALLEYVIEW(dev_priv) || IS_G4X(dev_priv))
|
||||
funcs = &g4x_crtc_funcs;
|
||||
else if (IS_GEN(dev_priv, 4))
|
||||
funcs = &i965_crtc_funcs;
|
||||
else if (IS_I945GM(dev_priv) || IS_I915GM(dev_priv))
|
||||
funcs = &i915gm_crtc_funcs;
|
||||
else if (IS_GEN(dev_priv, 3))
|
||||
funcs = &i915_crtc_funcs;
|
||||
else
|
||||
funcs = &i8xx_crtc_funcs;
|
||||
} else {
|
||||
if (INTEL_GEN(dev_priv) >= 8)
|
||||
funcs = &bdw_crtc_funcs;
|
||||
else
|
||||
funcs = &ilk_crtc_funcs;
|
||||
}
|
||||
|
||||
ret = drm_crtc_init_with_planes(&dev_priv->drm, &crtc->base,
|
||||
&primary->base, &cursor->base,
|
||||
funcs, "pipe %c", pipe_name(pipe));
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->pipe_to_crtc_mapping) ||
|
||||
dev_priv->pipe_to_crtc_mapping[pipe] != NULL);
|
||||
dev_priv->pipe_to_crtc_mapping[pipe] = crtc;
|
||||
|
||||
if (INTEL_GEN(dev_priv) < 9) {
|
||||
enum i9xx_plane_id i9xx_plane = primary->i9xx_plane;
|
||||
|
||||
BUG_ON(i9xx_plane >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
|
||||
dev_priv->plane_to_crtc_mapping[i9xx_plane] != NULL);
|
||||
dev_priv->plane_to_crtc_mapping[i9xx_plane] = crtc;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 10)
|
||||
drm_crtc_create_scaling_filter_property(&crtc->base,
|
||||
BIT(DRM_SCALING_FILTER_DEFAULT) |
|
||||
BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR));
|
||||
|
||||
intel_color_init(crtc);
|
||||
|
||||
intel_crtc_crc_init(crtc);
|
||||
|
||||
drm_WARN_ON(&dev_priv->drm, drm_crtc_index(&crtc->base) != crtc->pipe);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
intel_crtc_free(crtc);
|
||||
|
||||
return ret;
|
||||
}
|
22
drivers/gpu/drm/i915/display/intel_crtc.h
Normal file
22
drivers/gpu/drm/i915/display/intel_crtc.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _INTEL_CRTC_H_
|
||||
#define _INTEL_CRTC_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum pipe;
|
||||
struct drm_i915_private;
|
||||
struct intel_crtc;
|
||||
struct intel_crtc_state;
|
||||
|
||||
u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state);
|
||||
int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe);
|
||||
struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc);
|
||||
void intel_crtc_state_reset(struct intel_crtc_state *crtc_state,
|
||||
struct intel_crtc *crtc);
|
||||
|
||||
#endif
|
@ -46,10 +46,12 @@
|
||||
#include "intel_hotplug.h"
|
||||
#include "intel_lspcon.h"
|
||||
#include "intel_panel.h"
|
||||
#include "intel_pps.h"
|
||||
#include "intel_psr.h"
|
||||
#include "intel_sprite.h"
|
||||
#include "intel_tc.h"
|
||||
#include "intel_vdsc.h"
|
||||
#include "intel_vrr.h"
|
||||
|
||||
struct ddi_buf_trans {
|
||||
u32 trans1; /* balance leg enable, de-emph level */
|
||||
@ -2099,9 +2101,9 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state
|
||||
}
|
||||
}
|
||||
|
||||
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable)
|
||||
int intel_ddi_toggle_hdcp_bits(struct intel_encoder *intel_encoder,
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable, u32 hdcp_mask)
|
||||
{
|
||||
struct drm_device *dev = intel_encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
@ -2116,9 +2118,9 @@ int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
|
||||
|
||||
tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
|
||||
if (enable)
|
||||
tmp |= TRANS_DDI_HDCP_SIGNALLING;
|
||||
tmp |= hdcp_mask;
|
||||
else
|
||||
tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
|
||||
tmp &= ~hdcp_mask;
|
||||
intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder), tmp);
|
||||
intel_display_power_put(dev_priv, intel_encoder->power_domain, wakeref);
|
||||
return ret;
|
||||
@ -2701,15 +2703,11 @@ static void icl_ddi_combo_vswing_program(struct intel_encoder *encoder,
|
||||
ddi_translations = ehl_get_combo_buf_trans(encoder, crtc_state, &n_entries);
|
||||
else
|
||||
ddi_translations = icl_get_combo_buf_trans(encoder, crtc_state, &n_entries);
|
||||
if (!ddi_translations)
|
||||
return;
|
||||
|
||||
if (level >= n_entries) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"DDI translation not found for level %d. Using %d instead.",
|
||||
level, n_entries - 1);
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, !ddi_translations))
|
||||
return;
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, level >= n_entries))
|
||||
level = n_entries - 1;
|
||||
}
|
||||
|
||||
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
@ -2830,13 +2828,11 @@ static void icl_mg_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
|
||||
u32 val;
|
||||
|
||||
ddi_translations = icl_get_mg_buf_trans(encoder, crtc_state, &n_entries);
|
||||
/* The table does not have values for level 3 and level 9. */
|
||||
if (level >= n_entries || level == 3 || level == 9) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"DDI translation not found for level %d. Using %d instead.",
|
||||
level, n_entries - 2);
|
||||
level = n_entries - 2;
|
||||
}
|
||||
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, !ddi_translations))
|
||||
return;
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, level >= n_entries))
|
||||
level = n_entries - 1;
|
||||
|
||||
/* Set MG_TX_LINK_PARAMS cri_use_fs32 to 0. */
|
||||
for (ln = 0; ln < 2; ln++) {
|
||||
@ -2968,7 +2964,9 @@ tgl_dkl_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
|
||||
|
||||
ddi_translations = tgl_get_dkl_buf_trans(encoder, crtc_state, &n_entries);
|
||||
|
||||
if (level >= n_entries)
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, !ddi_translations))
|
||||
return;
|
||||
if (drm_WARN_ON_ONCE(&dev_priv->drm, level >= n_entries))
|
||||
level = n_entries - 1;
|
||||
|
||||
dpcnt_mask = (DKL_TX_PRESHOOT_COEFF_MASK |
|
||||
@ -3555,6 +3553,22 @@ i915_reg_t dp_tp_status_reg(struct intel_encoder *encoder,
|
||||
return DP_TP_STATUS(encoder->port);
|
||||
}
|
||||
|
||||
static void intel_dp_sink_set_msa_timing_par_ignore_state(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
bool enable)
|
||||
{
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
|
||||
if (!crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_DOWNSPREAD_CTRL,
|
||||
enable ? DP_MSA_TIMING_PAR_IGNORE_EN : 0) <= 0)
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"Failed to set MSA_TIMING_PAR_IGNORE %s in the sink\n",
|
||||
enable ? "enable" : "disable");
|
||||
}
|
||||
|
||||
static void intel_dp_sink_set_fec_ready(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
@ -3625,7 +3639,7 @@ static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state,
|
||||
*/
|
||||
|
||||
/* 2. Enable Panel Power if PPS is required */
|
||||
intel_edp_panel_on(intel_dp);
|
||||
intel_pps_on(intel_dp);
|
||||
|
||||
/*
|
||||
* 3. For non-TBT Type-C ports, set FIA lane count
|
||||
@ -3768,7 +3782,7 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state,
|
||||
crtc_state->port_clock,
|
||||
crtc_state->lane_count);
|
||||
|
||||
intel_edp_panel_on(intel_dp);
|
||||
intel_pps_on(intel_dp);
|
||||
|
||||
intel_ddi_clk_select(encoder, crtc_state);
|
||||
|
||||
@ -4010,8 +4024,8 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state,
|
||||
if (INTEL_GEN(dev_priv) >= 12)
|
||||
intel_ddi_disable_pipe_clock(old_crtc_state);
|
||||
|
||||
intel_edp_panel_vdd_on(intel_dp);
|
||||
intel_edp_panel_off(intel_dp);
|
||||
intel_pps_vdd_on(intel_dp);
|
||||
intel_pps_off(intel_dp);
|
||||
|
||||
if (!intel_phy_is_tc(dev_priv, phy) ||
|
||||
dig_port->tc_mode != TC_PORT_TBT_ALT)
|
||||
@ -4062,6 +4076,8 @@ static void intel_ddi_post_disable(struct intel_atomic_state *state,
|
||||
|
||||
intel_disable_pipe(old_crtc_state);
|
||||
|
||||
intel_vrr_disable(old_crtc_state);
|
||||
|
||||
intel_ddi_disable_transcoder_func(old_crtc_state);
|
||||
|
||||
intel_dsc_disable(old_crtc_state);
|
||||
@ -4313,6 +4329,8 @@ static void intel_enable_ddi(struct intel_atomic_state *state,
|
||||
if (!crtc_state->bigjoiner_slave)
|
||||
intel_ddi_enable_transcoder_func(encoder, crtc_state);
|
||||
|
||||
intel_vrr_enable(encoder, crtc_state);
|
||||
|
||||
intel_enable_pipe(crtc_state);
|
||||
|
||||
intel_crtc_vblank_on(crtc_state);
|
||||
@ -4326,7 +4344,7 @@ static void intel_enable_ddi(struct intel_atomic_state *state,
|
||||
if (conn_state->content_protection ==
|
||||
DRM_MODE_CONTENT_PROTECTION_DESIRED)
|
||||
intel_hdcp_enable(to_intel_connector(conn_state->connector),
|
||||
crtc_state->cpu_transcoder,
|
||||
crtc_state,
|
||||
(u8)conn_state->hdcp_content_type);
|
||||
}
|
||||
|
||||
@ -4349,6 +4367,9 @@ static void intel_disable_ddi_dp(struct intel_atomic_state *state,
|
||||
/* Disable the decompression in DP Sink */
|
||||
intel_dp_sink_set_decompression_state(intel_dp, old_crtc_state,
|
||||
false);
|
||||
/* Disable Ignore_MSA bit in DP Sink */
|
||||
intel_dp_sink_set_msa_timing_par_ignore_state(intel_dp, old_crtc_state,
|
||||
false);
|
||||
}
|
||||
|
||||
static void intel_disable_ddi_hdmi(struct intel_atomic_state *state,
|
||||
@ -5039,6 +5060,8 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
|
||||
intel_dp_encoder_flush_work(encoder);
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
if (dig_port)
|
||||
kfree(dig_port->hdcp_port_data.streams);
|
||||
kfree(dig_port);
|
||||
}
|
||||
|
||||
@ -5192,12 +5215,20 @@ intel_ddi_hotplug(struct intel_encoder *encoder,
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
|
||||
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
|
||||
struct intel_dp *intel_dp = &dig_port->dp;
|
||||
enum phy phy = intel_port_to_phy(i915, encoder->port);
|
||||
bool is_tc = intel_phy_is_tc(i915, phy);
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
enum intel_hotplug_state state;
|
||||
int ret;
|
||||
|
||||
if (intel_dp->compliance.test_active &&
|
||||
intel_dp->compliance.test_type == DP_TEST_LINK_PHY_TEST_PATTERN) {
|
||||
intel_dp_phy_test(encoder);
|
||||
/* just do the PHY test and nothing else */
|
||||
return INTEL_HOTPLUG_UNCHANGED;
|
||||
}
|
||||
|
||||
state = intel_encoder_hotplug(encoder, connector);
|
||||
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
@ -50,9 +50,9 @@ u32 bxt_signal_levels(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
u32 ddi_signal_levels(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable);
|
||||
int intel_ddi_toggle_hdcp_bits(struct intel_encoder *intel_encoder,
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable, u32 hdcp_mask);
|
||||
void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder);
|
||||
|
||||
#endif /* __INTEL_DDI_H__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -546,7 +546,6 @@ unsigned int intel_rotation_info_size(const struct intel_rotation_info *rot_info
|
||||
unsigned int intel_remapped_info_size(const struct intel_remapped_info *rem_info);
|
||||
bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv);
|
||||
int intel_display_suspend(struct drm_device *dev);
|
||||
void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv);
|
||||
void intel_encoder_destroy(struct drm_encoder *encoder);
|
||||
struct drm_display_mode *
|
||||
intel_encoder_current_mode(struct intel_encoder *encoder);
|
||||
@ -643,7 +642,7 @@ void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
|
||||
|
||||
bool
|
||||
intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info,
|
||||
uint64_t modifier);
|
||||
u64 modifier);
|
||||
|
||||
int intel_plane_compute_gtt(struct intel_plane_state *plane_state);
|
||||
u32 intel_plane_compute_aligned_offset(int *x, int *y,
|
||||
@ -651,6 +650,9 @@ u32 intel_plane_compute_aligned_offset(int *x, int *y,
|
||||
int color_plane);
|
||||
int intel_plane_pin_fb(struct intel_plane_state *plane_state);
|
||||
void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state);
|
||||
struct intel_encoder *
|
||||
intel_get_crtc_new_encoder(const struct intel_atomic_state *state,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
|
||||
/* modesetting */
|
||||
void intel_modeset_init_hw(struct drm_i915_private *i915);
|
||||
|
@ -1139,7 +1139,6 @@ static ssize_t i915_ipc_status_write(struct file *file, const char __user *ubuf,
|
||||
if (!dev_priv->ipc_enabled && enable)
|
||||
drm_info(&dev_priv->drm,
|
||||
"Enabling IPC: WM will be proper only after next commit\n");
|
||||
dev_priv->wm.distrust_bios_wm = true;
|
||||
dev_priv->ipc_enabled = enable;
|
||||
intel_enable_ipc(dev_priv);
|
||||
}
|
||||
@ -2155,13 +2154,13 @@ static int i915_panel_show(struct seq_file *m, void *data)
|
||||
return -ENODEV;
|
||||
|
||||
seq_printf(m, "Panel power up delay: %d\n",
|
||||
intel_dp->panel_power_up_delay);
|
||||
intel_dp->pps.panel_power_up_delay);
|
||||
seq_printf(m, "Panel power down delay: %d\n",
|
||||
intel_dp->panel_power_down_delay);
|
||||
intel_dp->pps.panel_power_down_delay);
|
||||
seq_printf(m, "Backlight on delay: %d\n",
|
||||
intel_dp->backlight_on_delay);
|
||||
intel_dp->pps.backlight_on_delay);
|
||||
seq_printf(m, "Backlight off delay: %d\n",
|
||||
intel_dp->backlight_off_delay);
|
||||
intel_dp->pps.backlight_off_delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
#include "display/intel_crt.h"
|
||||
#include "display/intel_dp.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_irq.h"
|
||||
@ -16,6 +15,7 @@
|
||||
#include "intel_dpio_phy.h"
|
||||
#include "intel_hotplug.h"
|
||||
#include "intel_pm.h"
|
||||
#include "intel_pps.h"
|
||||
#include "intel_sideband.h"
|
||||
#include "intel_tc.h"
|
||||
#include "intel_vga.h"
|
||||
@ -936,7 +936,7 @@ static void bxt_enable_dc9(struct drm_i915_private *dev_priv)
|
||||
* because PPS registers are always on.
|
||||
*/
|
||||
if (!HAS_PCH_SPLIT(dev_priv))
|
||||
intel_power_sequencer_reset(dev_priv);
|
||||
intel_pps_reset_all(dev_priv);
|
||||
gen9_set_dc_state(dev_priv, DC_STATE_EN_DC9);
|
||||
}
|
||||
|
||||
@ -1446,7 +1446,7 @@ static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv)
|
||||
/* make sure we're done processing display irqs */
|
||||
intel_synchronize_irq(dev_priv);
|
||||
|
||||
intel_power_sequencer_reset(dev_priv);
|
||||
intel_pps_reset_all(dev_priv);
|
||||
|
||||
/* Prevent us from re-enabling polling on accident in late suspend */
|
||||
if (!dev_priv->drm.dev->power.is_suspended)
|
||||
|
@ -228,7 +228,7 @@ struct intel_encoder {
|
||||
struct intel_panel_bl_funcs {
|
||||
/* Connector and platform specific backlight functions */
|
||||
int (*setup)(struct intel_connector *connector, enum pipe pipe);
|
||||
u32 (*get)(struct intel_connector *connector);
|
||||
u32 (*get)(struct intel_connector *connector, enum pipe pipe);
|
||||
void (*set)(const struct drm_connector_state *conn_state, u32 level);
|
||||
void (*disable)(const struct drm_connector_state *conn_state, u32 level);
|
||||
void (*enable)(const struct intel_crtc_state *crtc_state,
|
||||
@ -252,17 +252,28 @@ struct intel_panel {
|
||||
bool alternate_pwm_increment; /* lpt+ */
|
||||
|
||||
/* PWM chip */
|
||||
u32 pwm_level_min;
|
||||
u32 pwm_level_max;
|
||||
bool pwm_enabled;
|
||||
bool util_pin_active_low; /* bxt+ */
|
||||
u8 controller; /* bxt+ only */
|
||||
struct pwm_device *pwm;
|
||||
struct pwm_state pwm_state;
|
||||
|
||||
/* DPCD backlight */
|
||||
u8 pwmgen_bit_count;
|
||||
union {
|
||||
struct {
|
||||
u8 pwmgen_bit_count;
|
||||
} vesa;
|
||||
struct {
|
||||
bool sdr_uses_aux;
|
||||
} intel;
|
||||
} edp;
|
||||
|
||||
struct backlight_device *device;
|
||||
|
||||
const struct intel_panel_bl_funcs *funcs;
|
||||
const struct intel_panel_bl_funcs *pwm_funcs;
|
||||
void (*power)(struct intel_connector *, bool enable);
|
||||
} backlight;
|
||||
};
|
||||
@ -343,6 +354,10 @@ struct intel_hdcp_shim {
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable);
|
||||
|
||||
/* Enable/Disable stream encryption on DP MST Transport Link */
|
||||
int (*stream_encryption)(struct intel_connector *connector,
|
||||
bool enable);
|
||||
|
||||
/* Ensures the link is still protected */
|
||||
bool (*check_link)(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector);
|
||||
@ -374,8 +389,13 @@ struct intel_hdcp_shim {
|
||||
int (*config_stream_type)(struct intel_digital_port *dig_port,
|
||||
bool is_repeater, u8 type);
|
||||
|
||||
/* Enable/Disable HDCP 2.2 stream encryption on DP MST Transport Link */
|
||||
int (*stream_2_2_encryption)(struct intel_connector *connector,
|
||||
bool enable);
|
||||
|
||||
/* HDCP2.2 Link Integrity Check */
|
||||
int (*check_2_2_link)(struct intel_digital_port *dig_port);
|
||||
int (*check_2_2_link)(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector);
|
||||
};
|
||||
|
||||
struct intel_hdcp {
|
||||
@ -402,7 +422,6 @@ struct intel_hdcp {
|
||||
* content can flow only through a link protected by HDCP2.2.
|
||||
*/
|
||||
u8 content_type;
|
||||
struct hdcp_port_data port_data;
|
||||
|
||||
bool is_paired;
|
||||
bool is_repeater;
|
||||
@ -436,6 +455,8 @@ struct intel_hdcp {
|
||||
* Hence caching the transcoder here.
|
||||
*/
|
||||
enum transcoder cpu_transcoder;
|
||||
/* Only used for DP MST stream encryption */
|
||||
enum transcoder stream_transcoder;
|
||||
};
|
||||
|
||||
struct intel_connector {
|
||||
@ -535,7 +556,7 @@ struct intel_plane_state {
|
||||
struct drm_framebuffer *fb;
|
||||
|
||||
u16 alpha;
|
||||
uint16_t pixel_blend_mode;
|
||||
u16 pixel_blend_mode;
|
||||
unsigned int rotation;
|
||||
enum drm_color_encoding color_encoding;
|
||||
enum drm_color_range color_range;
|
||||
@ -610,6 +631,9 @@ struct intel_plane_state {
|
||||
struct drm_intel_sprite_colorkey ckey;
|
||||
|
||||
struct drm_rect psr2_sel_fetch_area;
|
||||
|
||||
/* Clear Color Value */
|
||||
u64 ccval;
|
||||
};
|
||||
|
||||
struct intel_initial_plane_config {
|
||||
@ -669,6 +693,8 @@ struct intel_crtc_scaler_state {
|
||||
#define I915_MODE_FLAG_DSI_USE_TE1 (1<<4)
|
||||
/* Flag to indicate mipi dsi periodic command mode where we do not get TE */
|
||||
#define I915_MODE_FLAG_DSI_PERIODIC_CMD_MODE (1<<5)
|
||||
/* Do tricks to make vblank timestamps sane with VRR? */
|
||||
#define I915_MODE_FLAG_VRR (1<<6)
|
||||
|
||||
struct intel_wm_level {
|
||||
bool enable;
|
||||
@ -1127,6 +1153,13 @@ struct intel_crtc_state {
|
||||
struct intel_dsb *dsb;
|
||||
|
||||
u32 psr2_man_track_ctl;
|
||||
|
||||
/* Variable Refresh Rate state */
|
||||
struct {
|
||||
bool enable;
|
||||
u8 pipeline_full;
|
||||
u16 flipline, vmin, vmax;
|
||||
} vrr;
|
||||
};
|
||||
|
||||
enum intel_pipe_crc_source {
|
||||
@ -1169,6 +1202,8 @@ struct intel_crtc {
|
||||
/* I915_MODE_FLAG_* */
|
||||
u8 mode_flags;
|
||||
|
||||
u16 vmax_vblank_start;
|
||||
|
||||
struct intel_display_power_domain_set enabled_power_domains;
|
||||
struct intel_overlay *overlay;
|
||||
|
||||
@ -1221,6 +1256,7 @@ struct intel_plane {
|
||||
enum pipe pipe;
|
||||
bool has_fbc;
|
||||
bool has_ccs;
|
||||
bool need_async_flip_disable_wa;
|
||||
u32 frontbuffer_bit;
|
||||
|
||||
struct {
|
||||
@ -1257,7 +1293,10 @@ struct intel_plane {
|
||||
const struct intel_plane_state *plane_state);
|
||||
void (*async_flip)(struct intel_plane *plane,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
const struct intel_plane_state *plane_state);
|
||||
const struct intel_plane_state *plane_state,
|
||||
bool async_flip);
|
||||
void (*enable_flip_done)(struct intel_plane *plane);
|
||||
void (*disable_flip_done)(struct intel_plane *plane);
|
||||
};
|
||||
|
||||
struct intel_watermark_params {
|
||||
@ -1344,6 +1383,38 @@ struct intel_dp_pcon_frl {
|
||||
int trained_rate_gbps;
|
||||
};
|
||||
|
||||
struct intel_pps {
|
||||
int panel_power_up_delay;
|
||||
int panel_power_down_delay;
|
||||
int panel_power_cycle_delay;
|
||||
int backlight_on_delay;
|
||||
int backlight_off_delay;
|
||||
struct delayed_work panel_vdd_work;
|
||||
bool want_panel_vdd;
|
||||
unsigned long last_power_on;
|
||||
unsigned long last_backlight_off;
|
||||
ktime_t panel_power_off_time;
|
||||
intel_wakeref_t vdd_wakeref;
|
||||
|
||||
/*
|
||||
* Pipe whose power sequencer is currently locked into
|
||||
* this port. Only relevant on VLV/CHV.
|
||||
*/
|
||||
enum pipe pps_pipe;
|
||||
/*
|
||||
* Pipe currently driving the port. Used for preventing
|
||||
* the use of the PPS for any pipe currentrly driving
|
||||
* external DP as that will mess things up on VLV.
|
||||
*/
|
||||
enum pipe active_pipe;
|
||||
/*
|
||||
* Set if the sequencer may be reset due to a power transition,
|
||||
* requiring a reinitialization. Only relevant on BXT.
|
||||
*/
|
||||
bool pps_reset;
|
||||
struct edp_power_seq pps_delays;
|
||||
};
|
||||
|
||||
struct intel_dp {
|
||||
i915_reg_t output_reg;
|
||||
u32 DP;
|
||||
@ -1380,39 +1451,11 @@ struct intel_dp {
|
||||
int max_link_rate;
|
||||
/* sink or branch descriptor */
|
||||
struct drm_dp_desc desc;
|
||||
u32 edid_quirks;
|
||||
struct drm_dp_aux aux;
|
||||
u32 aux_busy_last_status;
|
||||
u8 train_set[4];
|
||||
int panel_power_up_delay;
|
||||
int panel_power_down_delay;
|
||||
int panel_power_cycle_delay;
|
||||
int backlight_on_delay;
|
||||
int backlight_off_delay;
|
||||
struct delayed_work panel_vdd_work;
|
||||
bool want_panel_vdd;
|
||||
unsigned long last_power_on;
|
||||
unsigned long last_backlight_off;
|
||||
ktime_t panel_power_off_time;
|
||||
intel_wakeref_t vdd_wakeref;
|
||||
|
||||
/*
|
||||
* Pipe whose power sequencer is currently locked into
|
||||
* this port. Only relevant on VLV/CHV.
|
||||
*/
|
||||
enum pipe pps_pipe;
|
||||
/*
|
||||
* Pipe currently driving the port. Used for preventing
|
||||
* the use of the PPS for any pipe currentrly driving
|
||||
* external DP as that will mess things up on VLV.
|
||||
*/
|
||||
enum pipe active_pipe;
|
||||
/*
|
||||
* Set if the sequencer may be reset due to a power transition,
|
||||
* requiring a reinitialization. Only relevant on BXT.
|
||||
*/
|
||||
bool pps_reset;
|
||||
struct edp_power_seq pps_delays;
|
||||
struct intel_pps pps;
|
||||
|
||||
bool can_mst; /* this port supports mst */
|
||||
bool is_mst;
|
||||
@ -1511,10 +1554,14 @@ struct intel_digital_port {
|
||||
enum phy_fia tc_phy_fia;
|
||||
u8 tc_phy_fia_idx;
|
||||
|
||||
/* protects num_hdcp_streams reference count */
|
||||
/* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */
|
||||
struct mutex hdcp_mutex;
|
||||
/* the number of pipes using HDCP signalling out of this port */
|
||||
unsigned int num_hdcp_streams;
|
||||
/* port HDCP auth status */
|
||||
bool hdcp_auth_status;
|
||||
/* HDCP port data need to pass to security f/w */
|
||||
struct hdcp_port_data hdcp_port_data;
|
||||
|
||||
void (*write_infoframe)(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
@ -1825,4 +1872,26 @@ to_intel_frontbuffer(struct drm_framebuffer *fb)
|
||||
return fb ? to_intel_framebuffer(fb)->frontbuffer : NULL;
|
||||
}
|
||||
|
||||
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (dev_priv->params.panel_use_ssc >= 0)
|
||||
return dev_priv->params.panel_use_ssc != 0;
|
||||
return dev_priv->vbt.lvds_use_ssc
|
||||
&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
|
||||
}
|
||||
|
||||
static inline u32 i9xx_dpll_compute_fp(struct dpll *dpll)
|
||||
{
|
||||
return dpll->n << 16 | dpll->m1 << 8 | dpll->m2;
|
||||
}
|
||||
|
||||
static inline u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv,
|
||||
const struct intel_crtc_state *pipe_config)
|
||||
{
|
||||
if (HAS_DDI(dev_priv))
|
||||
return pipe_config->port_clock; /* SPLL */
|
||||
else
|
||||
return dev_priv->fdi_pll_freq;
|
||||
}
|
||||
|
||||
#endif /* __INTEL_DISPLAY_TYPES_H__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -70,16 +70,11 @@ enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *dig_port,
|
||||
void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state,
|
||||
const struct drm_connector_state *conn_state);
|
||||
void intel_edp_backlight_off(const struct drm_connector_state *conn_state);
|
||||
void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
|
||||
void intel_edp_panel_on(struct intel_dp *intel_dp);
|
||||
void intel_edp_panel_off(struct intel_dp *intel_dp);
|
||||
void intel_dp_mst_suspend(struct drm_i915_private *dev_priv);
|
||||
void intel_dp_mst_resume(struct drm_i915_private *dev_priv);
|
||||
int intel_dp_max_link_rate(struct intel_dp *intel_dp);
|
||||
int intel_dp_max_lane_count(struct intel_dp *intel_dp);
|
||||
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
|
||||
void intel_power_sequencer_reset(struct drm_i915_private *dev_priv);
|
||||
u32 intel_dp_pack_aux(const u8 *src, int src_bytes);
|
||||
|
||||
void intel_edp_drrs_enable(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
@ -96,9 +91,6 @@ void
|
||||
intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
u8 dp_train_pat);
|
||||
void
|
||||
intel_dp_set_signal_levels(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
|
||||
u8 *link_bw, u8 *rate_select);
|
||||
bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp);
|
||||
@ -144,9 +136,11 @@ bool intel_dp_initial_fastset_check(struct intel_encoder *encoder,
|
||||
struct intel_crtc_state *crtc_state);
|
||||
void intel_dp_sync_state(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
const struct dpll *vlv_get_dpll(struct drm_i915_private *i915);
|
||||
|
||||
void intel_dp_check_frl_training(struct intel_dp *intel_dp);
|
||||
void intel_dp_pcon_dsc_configure(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_dp_phy_test(struct intel_encoder *encoder);
|
||||
|
||||
#endif /* __INTEL_DP_H__ */
|
||||
|
692
drivers/gpu/drm/i915/display/intel_dp_aux.c
Normal file
692
drivers/gpu/drm/i915/display/intel_dp_aux.c
Normal file
@ -0,0 +1,692 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2020-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_trace.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_dp_aux.h"
|
||||
#include "intel_pps.h"
|
||||
#include "intel_tc.h"
|
||||
|
||||
u32 intel_dp_pack_aux(const u8 *src, int src_bytes)
|
||||
{
|
||||
int i;
|
||||
u32 v = 0;
|
||||
|
||||
if (src_bytes > 4)
|
||||
src_bytes = 4;
|
||||
for (i = 0; i < src_bytes; i++)
|
||||
v |= ((u32)src[i]) << ((3 - i) * 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void intel_dp_unpack_aux(u32 src, u8 *dst, int dst_bytes)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dst_bytes > 4)
|
||||
dst_bytes = 4;
|
||||
for (i = 0; i < dst_bytes; i++)
|
||||
dst[i] = src >> ((3 - i) * 8);
|
||||
}
|
||||
|
||||
static u32
|
||||
intel_dp_aux_wait_done(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
i915_reg_t ch_ctl = intel_dp->aux_ch_ctl_reg(intel_dp);
|
||||
const unsigned int timeout_ms = 10;
|
||||
u32 status;
|
||||
bool done;
|
||||
|
||||
#define C (((status = intel_uncore_read_notrace(&i915->uncore, ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0)
|
||||
done = wait_event_timeout(i915->gmbus_wait_queue, C,
|
||||
msecs_to_jiffies_timeout(timeout_ms));
|
||||
|
||||
/* just trace the final value */
|
||||
trace_i915_reg_rw(false, ch_ctl, status, sizeof(status), true);
|
||||
|
||||
if (!done)
|
||||
drm_err(&i915->drm,
|
||||
"%s: did not complete or timeout within %ums (status 0x%08x)\n",
|
||||
intel_dp->aux.name, timeout_ms, status);
|
||||
#undef C
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static u32 g4x_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
|
||||
if (index)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The clock divider is based off the hrawclk, and would like to run at
|
||||
* 2MHz. So, take the hrawclk value and divide by 2000 and use that
|
||||
*/
|
||||
return DIV_ROUND_CLOSEST(RUNTIME_INFO(dev_priv)->rawclk_freq, 2000);
|
||||
}
|
||||
|
||||
static u32 ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
u32 freq;
|
||||
|
||||
if (index)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The clock divider is based off the cdclk or PCH rawclk, and would
|
||||
* like to run at 2MHz. So, take the cdclk or PCH rawclk value and
|
||||
* divide by 2000 and use that
|
||||
*/
|
||||
if (dig_port->aux_ch == AUX_CH_A)
|
||||
freq = dev_priv->cdclk.hw.cdclk;
|
||||
else
|
||||
freq = RUNTIME_INFO(dev_priv)->rawclk_freq;
|
||||
return DIV_ROUND_CLOSEST(freq, 2000);
|
||||
}
|
||||
|
||||
static u32 hsw_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
|
||||
if (dig_port->aux_ch != AUX_CH_A && HAS_PCH_LPT_H(dev_priv)) {
|
||||
/* Workaround for non-ULT HSW */
|
||||
switch (index) {
|
||||
case 0: return 63;
|
||||
case 1: return 72;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ilk_get_aux_clock_divider(intel_dp, index);
|
||||
}
|
||||
|
||||
static u32 skl_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
/*
|
||||
* SKL doesn't need us to program the AUX clock divider (Hardware will
|
||||
* derive the clock from CDCLK automatically). We still implement the
|
||||
* get_aux_clock_divider vfunc to plug-in into the existing code.
|
||||
*/
|
||||
return index ? 0 : 1;
|
||||
}
|
||||
|
||||
static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp,
|
||||
int send_bytes,
|
||||
u32 aux_clock_divider)
|
||||
{
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_i915_private *dev_priv =
|
||||
to_i915(dig_port->base.base.dev);
|
||||
u32 precharge, timeout;
|
||||
|
||||
if (IS_GEN(dev_priv, 6))
|
||||
precharge = 3;
|
||||
else
|
||||
precharge = 5;
|
||||
|
||||
if (IS_BROADWELL(dev_priv))
|
||||
timeout = DP_AUX_CH_CTL_TIME_OUT_600us;
|
||||
else
|
||||
timeout = DP_AUX_CH_CTL_TIME_OUT_400us;
|
||||
|
||||
return DP_AUX_CH_CTL_SEND_BUSY |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_INTERRUPT |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
timeout |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR |
|
||||
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
||||
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
|
||||
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT);
|
||||
}
|
||||
|
||||
static u32 skl_get_aux_send_ctl(struct intel_dp *intel_dp,
|
||||
int send_bytes,
|
||||
u32 unused)
|
||||
{
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_i915_private *i915 =
|
||||
to_i915(dig_port->base.base.dev);
|
||||
enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
|
||||
u32 ret;
|
||||
|
||||
ret = DP_AUX_CH_CTL_SEND_BUSY |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_INTERRUPT |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_TIME_OUT_MAX |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR |
|
||||
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
|
||||
DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(32) |
|
||||
DP_AUX_CH_CTL_SYNC_PULSE_SKL(32);
|
||||
|
||||
if (intel_phy_is_tc(i915, phy) &&
|
||||
dig_port->tc_mode == TC_PORT_TBT_ALT)
|
||||
ret |= DP_AUX_CH_CTL_TBT_IO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_aux_xfer(struct intel_dp *intel_dp,
|
||||
const u8 *send, int send_bytes,
|
||||
u8 *recv, int recv_size,
|
||||
u32 aux_send_ctl_flags)
|
||||
{
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_i915_private *i915 =
|
||||
to_i915(dig_port->base.base.dev);
|
||||
struct intel_uncore *uncore = &i915->uncore;
|
||||
enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
|
||||
bool is_tc_port = intel_phy_is_tc(i915, phy);
|
||||
i915_reg_t ch_ctl, ch_data[5];
|
||||
u32 aux_clock_divider;
|
||||
enum intel_display_power_domain aux_domain;
|
||||
intel_wakeref_t aux_wakeref;
|
||||
intel_wakeref_t pps_wakeref;
|
||||
int i, ret, recv_bytes;
|
||||
int try, clock = 0;
|
||||
u32 status;
|
||||
bool vdd;
|
||||
|
||||
ch_ctl = intel_dp->aux_ch_ctl_reg(intel_dp);
|
||||
for (i = 0; i < ARRAY_SIZE(ch_data); i++)
|
||||
ch_data[i] = intel_dp->aux_ch_data_reg(intel_dp, i);
|
||||
|
||||
if (is_tc_port)
|
||||
intel_tc_port_lock(dig_port);
|
||||
|
||||
aux_domain = intel_aux_power_domain(dig_port);
|
||||
|
||||
aux_wakeref = intel_display_power_get(i915, aux_domain);
|
||||
pps_wakeref = intel_pps_lock(intel_dp);
|
||||
|
||||
/*
|
||||
* We will be called with VDD already enabled for dpcd/edid/oui reads.
|
||||
* In such cases we want to leave VDD enabled and it's up to upper layers
|
||||
* to turn it off. But for eg. i2c-dev access we need to turn it on/off
|
||||
* ourselves.
|
||||
*/
|
||||
vdd = intel_pps_vdd_on_unlocked(intel_dp);
|
||||
|
||||
/*
|
||||
* dp aux is extremely sensitive to irq latency, hence request the
|
||||
* lowest possible wakeup latency and so prevent the cpu from going into
|
||||
* deep sleep states.
|
||||
*/
|
||||
cpu_latency_qos_update_request(&intel_dp->pm_qos, 0);
|
||||
|
||||
intel_pps_check_power_unlocked(intel_dp);
|
||||
|
||||
/* Try to wait for any previous AUX channel activity */
|
||||
for (try = 0; try < 3; try++) {
|
||||
status = intel_uncore_read_notrace(uncore, ch_ctl);
|
||||
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
/* just trace the final value */
|
||||
trace_i915_reg_rw(false, ch_ctl, status, sizeof(status), true);
|
||||
|
||||
if (try == 3) {
|
||||
const u32 status = intel_uncore_read(uncore, ch_ctl);
|
||||
|
||||
if (status != intel_dp->aux_busy_last_status) {
|
||||
drm_WARN(&i915->drm, 1,
|
||||
"%s: not started (status 0x%08x)\n",
|
||||
intel_dp->aux.name, status);
|
||||
intel_dp->aux_busy_last_status = status;
|
||||
}
|
||||
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Only 5 data registers! */
|
||||
if (drm_WARN_ON(&i915->drm, send_bytes > 20 || recv_size > 20)) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, clock++))) {
|
||||
u32 send_ctl = intel_dp->get_aux_send_ctl(intel_dp,
|
||||
send_bytes,
|
||||
aux_clock_divider);
|
||||
|
||||
send_ctl |= aux_send_ctl_flags;
|
||||
|
||||
/* Must try at least 3 times according to DP spec */
|
||||
for (try = 0; try < 5; try++) {
|
||||
/* Load the send data into the aux channel data registers */
|
||||
for (i = 0; i < send_bytes; i += 4)
|
||||
intel_uncore_write(uncore,
|
||||
ch_data[i >> 2],
|
||||
intel_dp_pack_aux(send + i,
|
||||
send_bytes - i));
|
||||
|
||||
/* Send the command and wait for it to complete */
|
||||
intel_uncore_write(uncore, ch_ctl, send_ctl);
|
||||
|
||||
status = intel_dp_aux_wait_done(intel_dp);
|
||||
|
||||
/* Clear done status and any errors */
|
||||
intel_uncore_write(uncore,
|
||||
ch_ctl,
|
||||
status |
|
||||
DP_AUX_CH_CTL_DONE |
|
||||
DP_AUX_CH_CTL_TIME_OUT_ERROR |
|
||||
DP_AUX_CH_CTL_RECEIVE_ERROR);
|
||||
|
||||
/*
|
||||
* DP CTS 1.2 Core Rev 1.1, 4.2.1.1 & 4.2.1.2
|
||||
* 400us delay required for errors and timeouts
|
||||
* Timeout errors from the HW already meet this
|
||||
* requirement so skip to next iteration
|
||||
*/
|
||||
if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR)
|
||||
continue;
|
||||
|
||||
if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
|
||||
usleep_range(400, 500);
|
||||
continue;
|
||||
}
|
||||
if (status & DP_AUX_CH_CTL_DONE)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if ((status & DP_AUX_CH_CTL_DONE) == 0) {
|
||||
drm_err(&i915->drm, "%s: not done (status 0x%08x)\n",
|
||||
intel_dp->aux.name, status);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
done:
|
||||
/*
|
||||
* Check for timeout or receive error. Timeouts occur when the sink is
|
||||
* not connected.
|
||||
*/
|
||||
if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
|
||||
drm_err(&i915->drm, "%s: receive error (status 0x%08x)\n",
|
||||
intel_dp->aux.name, status);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Timeouts occur when the device isn't connected, so they're "normal"
|
||||
* -- don't fill the kernel log with these
|
||||
*/
|
||||
if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
|
||||
drm_dbg_kms(&i915->drm, "%s: timeout (status 0x%08x)\n",
|
||||
intel_dp->aux.name, status);
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Unload any bytes sent back from the other side */
|
||||
recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
|
||||
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
|
||||
|
||||
/*
|
||||
* By BSpec: "Message sizes of 0 or >20 are not allowed."
|
||||
* We have no idea of what happened so we return -EBUSY so
|
||||
* drm layer takes care for the necessary retries.
|
||||
*/
|
||||
if (recv_bytes == 0 || recv_bytes > 20) {
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"%s: Forbidden recv_bytes = %d on aux transaction\n",
|
||||
intel_dp->aux.name, recv_bytes);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (recv_bytes > recv_size)
|
||||
recv_bytes = recv_size;
|
||||
|
||||
for (i = 0; i < recv_bytes; i += 4)
|
||||
intel_dp_unpack_aux(intel_uncore_read(uncore, ch_data[i >> 2]),
|
||||
recv + i, recv_bytes - i);
|
||||
|
||||
ret = recv_bytes;
|
||||
out:
|
||||
cpu_latency_qos_update_request(&intel_dp->pm_qos, PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
if (vdd)
|
||||
intel_pps_vdd_off_unlocked(intel_dp, false);
|
||||
|
||||
intel_pps_unlock(intel_dp, pps_wakeref);
|
||||
intel_display_power_put_async(i915, aux_domain, aux_wakeref);
|
||||
|
||||
if (is_tc_port)
|
||||
intel_tc_port_unlock(dig_port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define BARE_ADDRESS_SIZE 3
|
||||
#define HEADER_SIZE (BARE_ADDRESS_SIZE + 1)
|
||||
|
||||
static void
|
||||
intel_dp_aux_header(u8 txbuf[HEADER_SIZE],
|
||||
const struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
txbuf[0] = (msg->request << 4) | ((msg->address >> 16) & 0xf);
|
||||
txbuf[1] = (msg->address >> 8) & 0xff;
|
||||
txbuf[2] = msg->address & 0xff;
|
||||
txbuf[3] = msg->size - 1;
|
||||
}
|
||||
|
||||
static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
/*
|
||||
* If we're trying to send the HDCP Aksv, we need to set a the Aksv
|
||||
* select bit to inform the hardware to send the Aksv after our header
|
||||
* since we can't access that data from software.
|
||||
*/
|
||||
if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE &&
|
||||
msg->address == DP_AUX_HDCP_AKSV)
|
||||
return DP_AUX_CH_CTL_AUX_AKSV_SELECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux);
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
u8 txbuf[20], rxbuf[20];
|
||||
size_t txsize, rxsize;
|
||||
u32 flags = intel_dp_aux_xfer_flags(msg);
|
||||
int ret;
|
||||
|
||||
intel_dp_aux_header(txbuf, msg);
|
||||
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
case DP_AUX_I2C_WRITE:
|
||||
case DP_AUX_I2C_WRITE_STATUS_UPDATE:
|
||||
txsize = msg->size ? HEADER_SIZE + msg->size : BARE_ADDRESS_SIZE;
|
||||
rxsize = 2; /* 0 or 1 data bytes */
|
||||
|
||||
if (drm_WARN_ON(&i915->drm, txsize > 20))
|
||||
return -E2BIG;
|
||||
|
||||
drm_WARN_ON(&i915->drm, !msg->buffer != !msg->size);
|
||||
|
||||
if (msg->buffer)
|
||||
memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
|
||||
|
||||
ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
|
||||
rxbuf, rxsize, flags);
|
||||
if (ret > 0) {
|
||||
msg->reply = rxbuf[0] >> 4;
|
||||
|
||||
if (ret > 1) {
|
||||
/* Number of bytes written in a short write. */
|
||||
ret = clamp_t(int, rxbuf[1], 0, msg->size);
|
||||
} else {
|
||||
/* Return payload size. */
|
||||
ret = msg->size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DP_AUX_NATIVE_READ:
|
||||
case DP_AUX_I2C_READ:
|
||||
txsize = msg->size ? HEADER_SIZE : BARE_ADDRESS_SIZE;
|
||||
rxsize = msg->size + 1;
|
||||
|
||||
if (drm_WARN_ON(&i915->drm, rxsize > 20))
|
||||
return -E2BIG;
|
||||
|
||||
ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
|
||||
rxbuf, rxsize, flags);
|
||||
if (ret > 0) {
|
||||
msg->reply = rxbuf[0] >> 4;
|
||||
/*
|
||||
* Assume happy day, and copy the data. The caller is
|
||||
* expected to check msg->reply before touching it.
|
||||
*
|
||||
* Return payload size.
|
||||
*/
|
||||
ret--;
|
||||
memcpy(msg->buffer, rxbuf + 1, ret);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static i915_reg_t g4x_aux_ctl_reg(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
return DP_AUX_CH_CTL(aux_ch);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_CTL(AUX_CH_B);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t g4x_aux_data_reg(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
return DP_AUX_CH_DATA(aux_ch, index);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_DATA(AUX_CH_B, index);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t ilk_aux_ctl_reg(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
return DP_AUX_CH_CTL(aux_ch);
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
return PCH_DP_AUX_CH_CTL(aux_ch);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_CTL(AUX_CH_A);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t ilk_aux_data_reg(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
return DP_AUX_CH_DATA(aux_ch, index);
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
return PCH_DP_AUX_CH_DATA(aux_ch, index);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_DATA(AUX_CH_A, index);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t skl_aux_ctl_reg(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
case AUX_CH_E:
|
||||
case AUX_CH_F:
|
||||
return DP_AUX_CH_CTL(aux_ch);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_CTL(AUX_CH_A);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t skl_aux_data_reg(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_D:
|
||||
case AUX_CH_E:
|
||||
case AUX_CH_F:
|
||||
return DP_AUX_CH_DATA(aux_ch, index);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_DATA(AUX_CH_A, index);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t tgl_aux_ctl_reg(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_USBC1:
|
||||
case AUX_CH_USBC2:
|
||||
case AUX_CH_USBC3:
|
||||
case AUX_CH_USBC4:
|
||||
case AUX_CH_USBC5:
|
||||
case AUX_CH_USBC6:
|
||||
return DP_AUX_CH_CTL(aux_ch);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_CTL(AUX_CH_A);
|
||||
}
|
||||
}
|
||||
|
||||
static i915_reg_t tgl_aux_data_reg(struct intel_dp *intel_dp, int index)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
switch (aux_ch) {
|
||||
case AUX_CH_A:
|
||||
case AUX_CH_B:
|
||||
case AUX_CH_C:
|
||||
case AUX_CH_USBC1:
|
||||
case AUX_CH_USBC2:
|
||||
case AUX_CH_USBC3:
|
||||
case AUX_CH_USBC4:
|
||||
case AUX_CH_USBC5:
|
||||
case AUX_CH_USBC6:
|
||||
return DP_AUX_CH_DATA(aux_ch, index);
|
||||
default:
|
||||
MISSING_CASE(aux_ch);
|
||||
return DP_AUX_CH_DATA(AUX_CH_A, index);
|
||||
}
|
||||
}
|
||||
|
||||
void intel_dp_aux_fini(struct intel_dp *intel_dp)
|
||||
{
|
||||
if (cpu_latency_qos_request_active(&intel_dp->pm_qos))
|
||||
cpu_latency_qos_remove_request(&intel_dp->pm_qos);
|
||||
|
||||
kfree(intel_dp->aux.name);
|
||||
}
|
||||
|
||||
void intel_dp_aux_init(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
|
||||
struct intel_encoder *encoder = &dig_port->base;
|
||||
enum aux_ch aux_ch = dig_port->aux_ch;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 12) {
|
||||
intel_dp->aux_ch_ctl_reg = tgl_aux_ctl_reg;
|
||||
intel_dp->aux_ch_data_reg = tgl_aux_data_reg;
|
||||
} else if (INTEL_GEN(dev_priv) >= 9) {
|
||||
intel_dp->aux_ch_ctl_reg = skl_aux_ctl_reg;
|
||||
intel_dp->aux_ch_data_reg = skl_aux_data_reg;
|
||||
} else if (HAS_PCH_SPLIT(dev_priv)) {
|
||||
intel_dp->aux_ch_ctl_reg = ilk_aux_ctl_reg;
|
||||
intel_dp->aux_ch_data_reg = ilk_aux_data_reg;
|
||||
} else {
|
||||
intel_dp->aux_ch_ctl_reg = g4x_aux_ctl_reg;
|
||||
intel_dp->aux_ch_data_reg = g4x_aux_data_reg;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 9)
|
||||
intel_dp->get_aux_clock_divider = skl_get_aux_clock_divider;
|
||||
else if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv))
|
||||
intel_dp->get_aux_clock_divider = hsw_get_aux_clock_divider;
|
||||
else if (HAS_PCH_SPLIT(dev_priv))
|
||||
intel_dp->get_aux_clock_divider = ilk_get_aux_clock_divider;
|
||||
else
|
||||
intel_dp->get_aux_clock_divider = g4x_get_aux_clock_divider;
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 9)
|
||||
intel_dp->get_aux_send_ctl = skl_get_aux_send_ctl;
|
||||
else
|
||||
intel_dp->get_aux_send_ctl = g4x_get_aux_send_ctl;
|
||||
|
||||
drm_dp_aux_init(&intel_dp->aux);
|
||||
|
||||
/* Failure to allocate our preferred name is not critical */
|
||||
if (INTEL_GEN(dev_priv) >= 12 && aux_ch >= AUX_CH_USBC1)
|
||||
intel_dp->aux.name = kasprintf(GFP_KERNEL, "AUX USBC%c/%s",
|
||||
aux_ch - AUX_CH_USBC1 + '1',
|
||||
encoder->base.name);
|
||||
else
|
||||
intel_dp->aux.name = kasprintf(GFP_KERNEL, "AUX %c/%s",
|
||||
aux_ch_name(aux_ch),
|
||||
encoder->base.name);
|
||||
|
||||
intel_dp->aux.transfer = intel_dp_aux_transfer;
|
||||
cpu_latency_qos_add_request(&intel_dp->pm_qos, PM_QOS_DEFAULT_VALUE);
|
||||
}
|
18
drivers/gpu/drm/i915/display/intel_dp_aux.h
Normal file
18
drivers/gpu/drm/i915/display/intel_dp_aux.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2020-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_DP_AUX_H__
|
||||
#define __INTEL_DP_AUX_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct intel_dp;
|
||||
|
||||
u32 intel_dp_pack_aux(const u8 *src, int src_bytes);
|
||||
|
||||
void intel_dp_aux_fini(struct intel_dp *intel_dp);
|
||||
void intel_dp_aux_init(struct intel_dp *intel_dp);
|
||||
|
||||
#endif /* __INTEL_DP_AUX_H__ */
|
@ -22,8 +22,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Laptops with Intel GPUs which have panels that support controlling the
|
||||
* backlight through DP AUX can actually use two different interfaces: Intel's
|
||||
* proprietary DP AUX backlight interface, and the standard VESA backlight
|
||||
* interface. Unfortunately, at the time of writing this a lot of laptops will
|
||||
* advertise support for the standard VESA backlight interface when they
|
||||
* don't properly support it. However, on these systems the Intel backlight
|
||||
* interface generally does work properly. Additionally, these systems will
|
||||
* usually just indicate that they use PWM backlight controls in their VBIOS
|
||||
* for some reason.
|
||||
*/
|
||||
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_dp_aux_backlight.h"
|
||||
#include "intel_panel.h"
|
||||
|
||||
/* TODO:
|
||||
* Implement HDR, right now we just implement the bare minimum to bring us back into SDR mode so we
|
||||
* can make people's backlights work in the mean time
|
||||
*/
|
||||
|
||||
/*
|
||||
* DP AUX registers for Intel's proprietary HDR backlight interface. We define
|
||||
@ -77,6 +95,178 @@
|
||||
|
||||
#define INTEL_EDP_BRIGHTNESS_OPTIMIZATION_1 0x359
|
||||
|
||||
/* Intel EDP backlight callbacks */
|
||||
static bool
|
||||
intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
struct drm_dp_aux *aux = &intel_dp->aux;
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
int ret;
|
||||
u8 tcon_cap[4];
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, INTEL_EDP_HDR_TCON_CAP0, tcon_cap, sizeof(tcon_cap));
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (!(tcon_cap[1] & INTEL_EDP_HDR_TCON_BRIGHTNESS_NITS_CAP))
|
||||
return false;
|
||||
|
||||
if (tcon_cap[0] >= 1) {
|
||||
drm_dbg_kms(&i915->drm, "Detected Intel HDR backlight interface version %d\n",
|
||||
tcon_cap[0]);
|
||||
} else {
|
||||
drm_dbg_kms(&i915->drm, "Detected unsupported HDR backlight interface version %d\n",
|
||||
tcon_cap[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
panel->backlight.edp.intel.sdr_uses_aux =
|
||||
tcon_cap[2] & INTEL_EDP_SDR_TCON_BRIGHTNESS_AUX_CAP;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32
|
||||
intel_dp_aux_hdr_get_backlight(struct intel_connector *connector, enum pipe pipe)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
u8 tmp;
|
||||
u8 buf[2] = { 0 };
|
||||
|
||||
if (drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &tmp) < 0) {
|
||||
drm_err(&i915->drm, "Failed to read current backlight mode from DPCD\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(tmp & INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE)) {
|
||||
if (!panel->backlight.edp.intel.sdr_uses_aux) {
|
||||
u32 pwm_level = panel->backlight.pwm_funcs->get(connector, pipe);
|
||||
|
||||
return intel_panel_backlight_level_from_pwm(connector, pwm_level);
|
||||
}
|
||||
|
||||
/* Assume 100% brightness if backlight controls aren't enabled yet */
|
||||
return panel->backlight.max;
|
||||
}
|
||||
|
||||
if (drm_dp_dpcd_read(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, sizeof(buf)) < 0) {
|
||||
drm_err(&i915->drm, "Failed to read brightness from DPCD\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (buf[1] << 8 | buf[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_aux_hdr_set_aux_backlight(const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct drm_device *dev = connector->base.dev;
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
u8 buf[4] = { 0 };
|
||||
|
||||
buf[0] = level & 0xFF;
|
||||
buf[1] = (level & 0xFF00) >> 8;
|
||||
|
||||
if (drm_dp_dpcd_write(&intel_dp->aux, INTEL_EDP_BRIGHTNESS_NITS_LSB, buf, 4) < 0)
|
||||
drm_err(dev, "Failed to write brightness level to DPCD\n");
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_aux_hdr_set_backlight(const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
if (panel->backlight.edp.intel.sdr_uses_aux) {
|
||||
intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
|
||||
} else {
|
||||
const u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level);
|
||||
|
||||
intel_panel_set_pwm_level(conn_state, pwm_level);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_aux_hdr_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
int ret;
|
||||
u8 old_ctrl, ctrl;
|
||||
|
||||
ret = drm_dp_dpcd_readb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, &old_ctrl);
|
||||
if (ret < 0) {
|
||||
drm_err(&i915->drm, "Failed to read current backlight control mode: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl = old_ctrl;
|
||||
if (panel->backlight.edp.intel.sdr_uses_aux) {
|
||||
ctrl |= INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
|
||||
intel_dp_aux_hdr_set_aux_backlight(conn_state, level);
|
||||
} else {
|
||||
u32 pwm_level = intel_panel_backlight_level_to_pwm(connector, level);
|
||||
|
||||
panel->backlight.pwm_funcs->enable(crtc_state, conn_state, pwm_level);
|
||||
|
||||
ctrl &= ~INTEL_EDP_HDR_TCON_BRIGHTNESS_AUX_ENABLE;
|
||||
}
|
||||
|
||||
if (ctrl != old_ctrl)
|
||||
if (drm_dp_dpcd_writeb(&intel_dp->aux, INTEL_EDP_HDR_GETSET_CTRL_PARAMS, ctrl) < 0)
|
||||
drm_err(&i915->drm, "Failed to configure DPCD brightness controls\n");
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_aux_hdr_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
/* Nothing to do for AUX based backlight controls */
|
||||
if (panel->backlight.edp.intel.sdr_uses_aux)
|
||||
return;
|
||||
|
||||
/* Note we want the actual pwm_level to be 0, regardless of pwm_min */
|
||||
panel->backlight.pwm_funcs->disable(conn_state, intel_panel_invert_pwm_level(connector, 0));
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_aux_hdr_setup_backlight(struct intel_connector *connector, enum pipe pipe)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
int ret;
|
||||
|
||||
if (panel->backlight.edp.intel.sdr_uses_aux) {
|
||||
drm_dbg_kms(&i915->drm, "SDR backlight is controlled through DPCD\n");
|
||||
} else {
|
||||
drm_dbg_kms(&i915->drm, "SDR backlight is controlled through PWM\n");
|
||||
|
||||
ret = panel->backlight.pwm_funcs->setup(connector, pipe);
|
||||
if (ret < 0) {
|
||||
drm_err(&i915->drm,
|
||||
"Failed to setup SDR backlight controls through PWM: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
panel->backlight.max = 512;
|
||||
panel->backlight.min = 0;
|
||||
panel->backlight.level = intel_dp_aux_hdr_get_backlight(connector, pipe);
|
||||
panel->backlight.enabled = panel->backlight.level != 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* VESA backlight callbacks */
|
||||
static void set_vesa_backlight_enable(struct intel_dp *intel_dp, bool enable)
|
||||
{
|
||||
@ -128,7 +318,7 @@ static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connec
|
||||
* Read the current backlight value from DPCD register(s) based
|
||||
* on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
|
||||
*/
|
||||
static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector)
|
||||
static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct intel_dp *intel_dp = intel_attached_dp(connector);
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
@ -195,7 +385,7 @@ static bool intel_dp_aux_vesa_set_pwm_freq(struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_dp *intel_dp = intel_attached_dp(connector);
|
||||
const u8 pn = connector->panel.backlight.pwmgen_bit_count;
|
||||
const u8 pn = connector->panel.backlight.edp.vesa.pwmgen_bit_count;
|
||||
int freq, fxp, f, fxp_actual, fxp_min, fxp_max;
|
||||
|
||||
freq = dev_priv->vbt.backlight.pwm_freq_hz;
|
||||
@ -236,6 +426,7 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
|
||||
u8 pwmgen_bit_count = panel->backlight.edp.vesa.pwmgen_bit_count;
|
||||
|
||||
if (drm_dp_dpcd_readb(&intel_dp->aux,
|
||||
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
|
||||
@ -256,7 +447,7 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
|
||||
if (drm_dp_dpcd_writeb(&intel_dp->aux,
|
||||
DP_EDP_PWMGEN_BIT_COUNT,
|
||||
panel->backlight.pwmgen_bit_count) < 0)
|
||||
pwmgen_bit_count) < 0)
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"Failed to write aux pwmgen bit count\n");
|
||||
|
||||
@ -364,7 +555,7 @@ static u32 intel_dp_aux_vesa_calc_max_backlight(struct intel_connector *connecto
|
||||
"Failed to write aux pwmgen bit count\n");
|
||||
return max_backlight;
|
||||
}
|
||||
panel->backlight.pwmgen_bit_count = pn;
|
||||
panel->backlight.edp.vesa.pwmgen_bit_count = pn;
|
||||
|
||||
max_backlight = (1 << pn) - 1;
|
||||
|
||||
@ -381,7 +572,7 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = 0;
|
||||
panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector);
|
||||
panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, pipe);
|
||||
panel->backlight.enabled = intel_dp_aux_vesa_backlight_dpcd_mode(connector) &&
|
||||
panel->backlight.level != 0;
|
||||
|
||||
@ -395,9 +586,14 @@ intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector)
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
|
||||
/* Check the eDP Display control capabilities registers to determine if
|
||||
* the panel can support backlight control over the aux channel
|
||||
* the panel can support backlight control over the aux channel.
|
||||
*
|
||||
* TODO: We currently only support AUX only backlight configurations, not backlights which
|
||||
* require a mix of PWM and AUX controls to work. In the mean time, these machines typically
|
||||
* work just fine using normal PWM controls anyway.
|
||||
*/
|
||||
if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
|
||||
(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP) &&
|
||||
(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) {
|
||||
drm_dbg_kms(&i915->drm, "AUX Backlight Control Supported!\n");
|
||||
return true;
|
||||
@ -405,6 +601,14 @@ intel_dp_aux_supports_vesa_backlight(struct intel_connector *connector)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct intel_panel_bl_funcs intel_dp_hdr_bl_funcs = {
|
||||
.setup = intel_dp_aux_hdr_setup_backlight,
|
||||
.enable = intel_dp_aux_hdr_enable_backlight,
|
||||
.disable = intel_dp_aux_hdr_disable_backlight,
|
||||
.set = intel_dp_aux_hdr_set_backlight,
|
||||
.get = intel_dp_aux_hdr_get_backlight,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = {
|
||||
.setup = intel_dp_aux_vesa_setup_backlight,
|
||||
.enable = intel_dp_aux_vesa_enable_backlight,
|
||||
@ -413,36 +617,73 @@ static const struct intel_panel_bl_funcs intel_dp_vesa_bl_funcs = {
|
||||
.get = intel_dp_aux_vesa_get_backlight,
|
||||
};
|
||||
|
||||
int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
|
||||
enum intel_dp_aux_backlight_modparam {
|
||||
INTEL_DP_AUX_BACKLIGHT_AUTO = -1,
|
||||
INTEL_DP_AUX_BACKLIGHT_OFF = 0,
|
||||
INTEL_DP_AUX_BACKLIGHT_ON = 1,
|
||||
INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2,
|
||||
INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3,
|
||||
};
|
||||
|
||||
int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_panel *panel = &intel_connector->panel;
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(intel_connector->encoder);
|
||||
struct drm_device *dev = connector->base.dev;
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
bool try_intel_interface = false, try_vesa_interface = false;
|
||||
|
||||
if (i915->params.enable_dpcd_backlight == 0 ||
|
||||
!intel_dp_aux_supports_vesa_backlight(intel_connector))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* There are a lot of machines that don't advertise the backlight
|
||||
* control interface to use properly in their VBIOS, :\
|
||||
/* Check the VBT and user's module parameters to figure out which
|
||||
* interfaces to probe
|
||||
*/
|
||||
if (i915->vbt.backlight.type !=
|
||||
INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
|
||||
i915->params.enable_dpcd_backlight != 1 &&
|
||||
!drm_dp_has_quirk(&intel_dp->desc, intel_dp->edid_quirks,
|
||||
DP_QUIRK_FORCE_DPCD_BACKLIGHT)) {
|
||||
drm_info(&i915->drm,
|
||||
"Panel advertises DPCD backlight support, but "
|
||||
"VBT disagrees. If your backlight controls "
|
||||
"don't work try booting with "
|
||||
"i915.enable_dpcd_backlight=1. If your machine "
|
||||
"needs this, please file a _new_ bug report on "
|
||||
"drm/i915, see " FDO_BUG_URL " for details.\n");
|
||||
switch (i915->params.enable_dpcd_backlight) {
|
||||
case INTEL_DP_AUX_BACKLIGHT_OFF:
|
||||
return -ENODEV;
|
||||
case INTEL_DP_AUX_BACKLIGHT_AUTO:
|
||||
switch (i915->vbt.backlight.type) {
|
||||
case INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE:
|
||||
try_vesa_interface = true;
|
||||
break;
|
||||
case INTEL_BACKLIGHT_DISPLAY_DDI:
|
||||
try_intel_interface = true;
|
||||
try_vesa_interface = true;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
break;
|
||||
case INTEL_DP_AUX_BACKLIGHT_ON:
|
||||
if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE)
|
||||
try_intel_interface = true;
|
||||
|
||||
try_vesa_interface = true;
|
||||
break;
|
||||
case INTEL_DP_AUX_BACKLIGHT_FORCE_VESA:
|
||||
try_vesa_interface = true;
|
||||
break;
|
||||
case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL:
|
||||
try_intel_interface = true;
|
||||
break;
|
||||
}
|
||||
|
||||
panel->backlight.funcs = &intel_dp_vesa_bl_funcs;
|
||||
/*
|
||||
* A lot of eDP panels in the wild will report supporting both the
|
||||
* Intel proprietary backlight control interface, and the VESA
|
||||
* backlight control interface. Many of these panels are liars though,
|
||||
* and will only work with the Intel interface. So, always probe for
|
||||
* that first.
|
||||
*/
|
||||
if (try_intel_interface && intel_dp_aux_supports_hdr_backlight(connector)) {
|
||||
drm_dbg_kms(dev, "Using Intel proprietary eDP backlight controls\n");
|
||||
panel->backlight.funcs = &intel_dp_hdr_bl_funcs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (try_vesa_interface && intel_dp_aux_supports_vesa_backlight(connector)) {
|
||||
drm_dbg_kms(dev, "Using VESA eDP backlight controls\n");
|
||||
panel->backlight.funcs = &intel_dp_vesa_bl_funcs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -16,6 +16,30 @@
|
||||
#include "intel_dp.h"
|
||||
#include "intel_hdcp.h"
|
||||
|
||||
static unsigned int transcoder_to_stream_enc_status(enum transcoder cpu_transcoder)
|
||||
{
|
||||
u32 stream_enc_mask;
|
||||
|
||||
switch (cpu_transcoder) {
|
||||
case TRANSCODER_A:
|
||||
stream_enc_mask = HDCP_STATUS_STREAM_A_ENC;
|
||||
break;
|
||||
case TRANSCODER_B:
|
||||
stream_enc_mask = HDCP_STATUS_STREAM_B_ENC;
|
||||
break;
|
||||
case TRANSCODER_C:
|
||||
stream_enc_mask = HDCP_STATUS_STREAM_C_ENC;
|
||||
break;
|
||||
case TRANSCODER_D:
|
||||
stream_enc_mask = HDCP_STATUS_STREAM_D_ENC;
|
||||
break;
|
||||
default:
|
||||
stream_enc_mask = 0;
|
||||
}
|
||||
|
||||
return stream_enc_mask;
|
||||
}
|
||||
|
||||
static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout)
|
||||
{
|
||||
long ret;
|
||||
@ -561,7 +585,8 @@ int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *dig_port,
|
||||
}
|
||||
|
||||
static
|
||||
int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port)
|
||||
int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector)
|
||||
{
|
||||
u8 rx_status;
|
||||
int ret;
|
||||
@ -622,46 +647,151 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
|
||||
};
|
||||
|
||||
static int
|
||||
intel_dp_mst_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
|
||||
enum transcoder cpu_transcoder,
|
||||
bool enable)
|
||||
intel_dp_mst_toggle_hdcp_stream_select(struct intel_connector *connector,
|
||||
bool enable)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret;
|
||||
|
||||
if (!enable)
|
||||
usleep_range(6, 60); /* Bspec says >= 6us */
|
||||
|
||||
ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base,
|
||||
cpu_transcoder, enable);
|
||||
ret = intel_ddi_toggle_hdcp_bits(&dig_port->base,
|
||||
hdcp->stream_transcoder, enable,
|
||||
TRANS_DDI_HDCP_SELECT);
|
||||
if (ret)
|
||||
drm_dbg_kms(&i915->drm, "%s HDCP signalling failed (%d)\n",
|
||||
enable ? "Enable" : "Disable", ret);
|
||||
drm_err(&i915->drm, "%s HDCP stream select failed (%d)\n",
|
||||
enable ? "Enable" : "Disable", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_mst_hdcp_stream_encryption(struct intel_connector *connector,
|
||||
bool enable)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
enum port port = dig_port->base.port;
|
||||
enum transcoder cpu_transcoder = hdcp->stream_transcoder;
|
||||
u32 stream_enc_status;
|
||||
int ret;
|
||||
|
||||
ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stream_enc_status = transcoder_to_stream_enc_status(cpu_transcoder);
|
||||
if (!stream_enc_status)
|
||||
return -EINVAL;
|
||||
|
||||
/* Wait for encryption confirmation */
|
||||
if (intel_de_wait_for_register(i915,
|
||||
HDCP_STATUS(i915, cpu_transcoder, port),
|
||||
stream_enc_status,
|
||||
enable ? stream_enc_status : 0,
|
||||
HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n",
|
||||
transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool intel_dp_mst_get_qses_status(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
|
||||
struct drm_dp_query_stream_enc_status_ack_reply reply;
|
||||
struct intel_dp *intel_dp = &dig_port->dp;
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_send_query_stream_enc_status(&intel_dp->mst_mgr,
|
||||
connector->port, &reply);
|
||||
if (ret) {
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"[%s:%d] failed QSES ret=%d\n",
|
||||
connector->base.name, connector->base.base.id, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
drm_dbg_kms(&i915->drm, "[%s:%d] QSES stream auth: %d stream enc: %d\n",
|
||||
connector->base.name, connector->base.base.id,
|
||||
reply.auth_completed, reply.encryption_enabled);
|
||||
|
||||
return reply.auth_completed && reply.encryption_enabled;
|
||||
}
|
||||
|
||||
static
|
||||
bool intel_dp_mst_hdcp_check_link(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
|
||||
struct intel_dp *intel_dp = &dig_port->dp;
|
||||
struct drm_dp_query_stream_enc_status_ack_reply reply;
|
||||
int ret;
|
||||
|
||||
if (!intel_dp_hdcp_check_link(dig_port, connector))
|
||||
return false;
|
||||
|
||||
ret = drm_dp_send_query_stream_enc_status(&intel_dp->mst_mgr,
|
||||
connector->port, &reply);
|
||||
if (ret) {
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"[CONNECTOR:%d:%s] failed QSES ret=%d\n",
|
||||
connector->base.base.id, connector->base.name, ret);
|
||||
return false;
|
||||
return intel_dp_mst_get_qses_status(dig_port, connector);
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_mst_hdcp2_stream_encryption(struct intel_connector *connector,
|
||||
bool enable)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
enum transcoder cpu_transcoder = hdcp->stream_transcoder;
|
||||
enum pipe pipe = (enum pipe)cpu_transcoder;
|
||||
enum port port = dig_port->base.port;
|
||||
int ret;
|
||||
|
||||
drm_WARN_ON(&i915->drm, enable &&
|
||||
!!(intel_de_read(i915, HDCP2_AUTH_STREAM(i915, cpu_transcoder, port))
|
||||
& AUTH_STREAM_TYPE) != data->streams[0].stream_type);
|
||||
|
||||
ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for encryption confirmation */
|
||||
if (intel_de_wait_for_register(i915,
|
||||
HDCP2_STREAM_STATUS(i915, cpu_transcoder, pipe),
|
||||
STREAM_ENCRYPTION_STATUS,
|
||||
enable ? STREAM_ENCRYPTION_STATUS : 0,
|
||||
HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n",
|
||||
transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return reply.auth_completed && reply.encryption_enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DP v2.0 I.3.3 ignore the stream signature L' in QSES reply msg reply.
|
||||
* I.3.5 MST source device may use a QSES msg to query downstream status
|
||||
* for a particular stream.
|
||||
*/
|
||||
static
|
||||
int intel_dp_mst_hdcp2_check_link(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector)
|
||||
{
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We do need to do the Link Check only for the connector involved with
|
||||
* HDCP port authentication and encryption.
|
||||
* We can re-use the hdcp->is_repeater flag to know that the connector
|
||||
* involved with HDCP port authentication and encryption.
|
||||
*/
|
||||
if (hdcp->is_repeater) {
|
||||
ret = intel_dp_hdcp2_check_link(dig_port, connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return intel_dp_mst_get_qses_status(dig_port, connector) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = {
|
||||
@ -673,10 +803,16 @@ static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = {
|
||||
.read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
|
||||
.read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
|
||||
.read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
|
||||
.toggle_signalling = intel_dp_mst_hdcp_toggle_signalling,
|
||||
.toggle_signalling = intel_dp_hdcp_toggle_signalling,
|
||||
.stream_encryption = intel_dp_mst_hdcp_stream_encryption,
|
||||
.check_link = intel_dp_mst_hdcp_check_link,
|
||||
.hdcp_capable = intel_dp_hdcp_capable,
|
||||
|
||||
.write_2_2_msg = intel_dp_hdcp2_write_msg,
|
||||
.read_2_2_msg = intel_dp_hdcp2_read_msg,
|
||||
.config_stream_type = intel_dp_hdcp2_config_stream_type,
|
||||
.stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption,
|
||||
.check_2_2_link = intel_dp_mst_hdcp2_check_link,
|
||||
.hdcp_2_2_capable = intel_dp_hdcp2_capable,
|
||||
.protocol = HDCP_PROTOCOL_DP,
|
||||
};
|
||||
|
||||
@ -693,10 +829,10 @@ int intel_dp_init_hdcp(struct intel_digital_port *dig_port,
|
||||
return 0;
|
||||
|
||||
if (intel_connector->mst_port)
|
||||
return intel_hdcp_init(intel_connector, port,
|
||||
return intel_hdcp_init(intel_connector, dig_port,
|
||||
&intel_dp_mst_hdcp_shim);
|
||||
else if (!intel_dp_is_edp(intel_dp))
|
||||
return intel_hdcp_init(intel_connector, port,
|
||||
return intel_hdcp_init(intel_connector, dig_port,
|
||||
&intel_dp_hdcp_shim);
|
||||
|
||||
return 0;
|
||||
|
@ -334,6 +334,27 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
|
||||
return drm_dp_dpcd_write(&intel_dp->aux, reg, buf, len) == len;
|
||||
}
|
||||
|
||||
void intel_dp_set_signal_levels(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
enum drm_dp_phy dp_phy)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
|
||||
u8 train_set = intel_dp->train_set[0];
|
||||
char phy_name[10];
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm, "Using vswing level %d%s, pre-emphasis level %d%s, at %s\n",
|
||||
train_set & DP_TRAIN_VOLTAGE_SWING_MASK,
|
||||
train_set & DP_TRAIN_MAX_SWING_REACHED ? " (max)" : "",
|
||||
(train_set & DP_TRAIN_PRE_EMPHASIS_MASK) >>
|
||||
DP_TRAIN_PRE_EMPHASIS_SHIFT,
|
||||
train_set & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ?
|
||||
" (max)" : "",
|
||||
intel_dp_phy_name(dp_phy, phy_name, sizeof(phy_name)));
|
||||
|
||||
if (intel_dp_phy_is_downstream_of_source(intel_dp, dp_phy))
|
||||
intel_dp->set_signal_levels(intel_dp, crtc_state);
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_reset_link_train(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
@ -341,7 +362,7 @@ intel_dp_reset_link_train(struct intel_dp *intel_dp,
|
||||
u8 dp_train_pat)
|
||||
{
|
||||
memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
|
||||
intel_dp_set_signal_levels(intel_dp, crtc_state);
|
||||
intel_dp_set_signal_levels(intel_dp, crtc_state, dp_phy);
|
||||
return intel_dp_set_link_train(intel_dp, crtc_state, dp_phy, dp_train_pat);
|
||||
}
|
||||
|
||||
@ -355,7 +376,7 @@ intel_dp_update_link_train(struct intel_dp *intel_dp,
|
||||
DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy);
|
||||
int ret;
|
||||
|
||||
intel_dp_set_signal_levels(intel_dp, crtc_state);
|
||||
intel_dp_set_signal_levels(intel_dp, crtc_state, dp_phy);
|
||||
|
||||
ret = drm_dp_dpcd_write(&intel_dp->aux, reg,
|
||||
intel_dp->train_set, crtc_state->lane_count);
|
||||
@ -413,7 +434,7 @@ intel_dp_prepare_link_train(struct intel_dp *intel_dp,
|
||||
drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
|
||||
&rate_select, 1);
|
||||
|
||||
link_config[0] = 0;
|
||||
link_config[0] = crtc_state->vrr.enable ? DP_MSA_TIMING_PAR_IGNORE_EN : 0;
|
||||
link_config[1] = DP_SET_ANSI_8B10B;
|
||||
drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
|
||||
|
||||
@ -676,9 +697,9 @@ static bool intel_dp_disable_dpcd_training_pattern(struct intel_dp *intel_dp,
|
||||
* @intel_dp: DP struct
|
||||
* @crtc_state: state for CRTC attached to the encoder
|
||||
*
|
||||
* Stop the link training of the @intel_dp port, disabling the test pattern
|
||||
* symbol generation on the port and disabling the training pattern in
|
||||
* the sink's DPCD.
|
||||
* Stop the link training of the @intel_dp port, disabling the training
|
||||
* pattern in the sink's DPCD, and disabling the test pattern symbol
|
||||
* generation on the port.
|
||||
*
|
||||
* What symbols are output on the port after this point is
|
||||
* platform specific: On DDI/VLV/CHV platforms it will be the idle pattern
|
||||
@ -692,10 +713,9 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp,
|
||||
{
|
||||
intel_dp->link_trained = true;
|
||||
|
||||
intel_dp_program_link_training_pattern(intel_dp,
|
||||
crtc_state,
|
||||
DP_TRAINING_PATTERN_DISABLE);
|
||||
intel_dp_disable_dpcd_training_pattern(intel_dp, DP_PHY_DPRX);
|
||||
intel_dp_program_link_training_pattern(intel_dp, crtc_state,
|
||||
DP_TRAINING_PATTERN_DISABLE);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -17,6 +17,9 @@ void intel_dp_get_adjust_train(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
enum drm_dp_phy dp_phy,
|
||||
const u8 link_status[DP_LINK_STATUS_SIZE]);
|
||||
void intel_dp_set_signal_levels(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
enum drm_dp_phy dp_phy);
|
||||
void intel_dp_start_link_train(struct intel_dp *intel_dp,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_dp_stop_link_train(struct intel_dp *intel_dp,
|
||||
|
@ -53,8 +53,7 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
const struct drm_display_mode *adjusted_mode =
|
||||
&crtc_state->hw.adjusted_mode;
|
||||
bool constant_n = drm_dp_has_quirk(&intel_dp->desc, 0,
|
||||
DP_DPCD_QUIRK_CONSTANT_N);
|
||||
bool constant_n = drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_CONSTANT_N);
|
||||
int bpp, slots = -EINVAL;
|
||||
|
||||
crtc_state->lane_count = limits->max_lane_count;
|
||||
@ -569,7 +568,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
|
||||
if (conn_state->content_protection ==
|
||||
DRM_MODE_CONTENT_PROTECTION_DESIRED)
|
||||
intel_hdcp_enable(to_intel_connector(conn_state->connector),
|
||||
pipe_config->cpu_transcoder,
|
||||
pipe_config,
|
||||
(u8)conn_state->hdcp_content_type);
|
||||
}
|
||||
|
||||
@ -829,12 +828,11 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
|
||||
intel_attach_force_audio_property(connector);
|
||||
intel_attach_broadcast_rgb_property(connector);
|
||||
|
||||
|
||||
/* TODO: Figure out how to make HDCP work on GEN12+ */
|
||||
if (INTEL_GEN(dev_priv) < 12) {
|
||||
if (INTEL_GEN(dev_priv) <= 12) {
|
||||
ret = intel_dp_init_hdcp(dig_port, intel_connector);
|
||||
if (ret)
|
||||
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
|
||||
drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP MST init failed, skipping.\n",
|
||||
connector->name, connector->base.id);
|
||||
}
|
||||
|
||||
/*
|
||||
|
1363
drivers/gpu/drm/i915/display/intel_dpll.c
Normal file
1363
drivers/gpu/drm/i915/display/intel_dpll.c
Normal file
File diff suppressed because it is too large
Load Diff
23
drivers/gpu/drm/i915/display/intel_dpll.h
Normal file
23
drivers/gpu/drm/i915/display/intel_dpll.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _INTEL_DPLL_H_
|
||||
#define _INTEL_DPLL_H_
|
||||
|
||||
struct dpll;
|
||||
struct drm_i915_private;
|
||||
struct intel_crtc;
|
||||
struct intel_crtc_state;
|
||||
|
||||
void intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv);
|
||||
int vlv_calc_dpll_params(int refclk, struct dpll *clock);
|
||||
int pnv_calc_dpll_params(int refclk, struct dpll *clock);
|
||||
int i9xx_calc_dpll_params(int refclk, struct dpll *clock);
|
||||
void vlv_compute_dpll(struct intel_crtc *crtc,
|
||||
struct intel_crtc_state *pipe_config);
|
||||
void chv_compute_dpll(struct intel_crtc *crtc,
|
||||
struct intel_crtc_state *pipe_config);
|
||||
|
||||
#endif
|
@ -43,7 +43,7 @@
|
||||
|
||||
#define PANEL_PWM_MAX_VALUE 0xFF
|
||||
|
||||
static u32 dcs_get_backlight(struct intel_connector *connector)
|
||||
static u32 dcs_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct intel_encoder *encoder = intel_attached_encoder(connector);
|
||||
struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
|
||||
|
@ -676,7 +676,7 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc)
|
||||
}
|
||||
|
||||
static bool tiling_is_valid(struct drm_i915_private *dev_priv,
|
||||
uint64_t modifier)
|
||||
u64 modifier)
|
||||
{
|
||||
switch (modifier) {
|
||||
case DRM_FORMAT_MOD_LINEAR:
|
||||
|
683
drivers/gpu/drm/i915/display/intel_fdi.c
Normal file
683
drivers/gpu/drm/i915/display/intel_fdi.c
Normal file
@ -0,0 +1,683 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
#include "intel_atomic.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_fdi.h"
|
||||
|
||||
/* units of 100MHz */
|
||||
static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
if (crtc_state->hw.enable && crtc_state->has_pch_encoder)
|
||||
return crtc_state->fdi_lanes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe,
|
||||
struct intel_crtc_state *pipe_config)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
struct drm_atomic_state *state = pipe_config->uapi.state;
|
||||
struct intel_crtc *other_crtc;
|
||||
struct intel_crtc_state *other_crtc_state;
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"checking fdi config on pipe %c, lanes %i\n",
|
||||
pipe_name(pipe), pipe_config->fdi_lanes);
|
||||
if (pipe_config->fdi_lanes > 4) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"invalid fdi lane config on pipe %c: %i lanes\n",
|
||||
pipe_name(pipe), pipe_config->fdi_lanes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
|
||||
if (pipe_config->fdi_lanes > 2) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"only 2 lanes on haswell, required: %i lanes\n",
|
||||
pipe_config->fdi_lanes);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (INTEL_NUM_PIPES(dev_priv) == 2)
|
||||
return 0;
|
||||
|
||||
/* Ivybridge 3 pipe is really complicated */
|
||||
switch (pipe) {
|
||||
case PIPE_A:
|
||||
return 0;
|
||||
case PIPE_B:
|
||||
if (pipe_config->fdi_lanes <= 2)
|
||||
return 0;
|
||||
|
||||
other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C);
|
||||
other_crtc_state =
|
||||
intel_atomic_get_crtc_state(state, other_crtc);
|
||||
if (IS_ERR(other_crtc_state))
|
||||
return PTR_ERR(other_crtc_state);
|
||||
|
||||
if (pipe_required_fdi_lanes(other_crtc_state) > 0) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"invalid shared fdi lane config on pipe %c: %i lanes\n",
|
||||
pipe_name(pipe), pipe_config->fdi_lanes);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case PIPE_C:
|
||||
if (pipe_config->fdi_lanes > 2) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"only 2 lanes on pipe %c: required %i lanes\n",
|
||||
pipe_name(pipe), pipe_config->fdi_lanes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B);
|
||||
other_crtc_state =
|
||||
intel_atomic_get_crtc_state(state, other_crtc);
|
||||
if (IS_ERR(other_crtc_state))
|
||||
return PTR_ERR(other_crtc_state);
|
||||
|
||||
if (pipe_required_fdi_lanes(other_crtc_state) > 2) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"fdi link B uses too many lanes to enable link C\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
int ilk_fdi_compute_config(struct intel_crtc *intel_crtc,
|
||||
struct intel_crtc_state *pipe_config)
|
||||
{
|
||||
struct drm_device *dev = intel_crtc->base.dev;
|
||||
struct drm_i915_private *i915 = to_i915(dev);
|
||||
const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
|
||||
int lane, link_bw, fdi_dotclock, ret;
|
||||
bool needs_recompute = false;
|
||||
|
||||
retry:
|
||||
/* FDI is a binary signal running at ~2.7GHz, encoding
|
||||
* each output octet as 10 bits. The actual frequency
|
||||
* is stored as a divider into a 100MHz clock, and the
|
||||
* mode pixel clock is stored in units of 1KHz.
|
||||
* Hence the bw of each lane in terms of the mode signal
|
||||
* is:
|
||||
*/
|
||||
link_bw = intel_fdi_link_freq(i915, pipe_config);
|
||||
|
||||
fdi_dotclock = adjusted_mode->crtc_clock;
|
||||
|
||||
lane = ilk_get_lanes_required(fdi_dotclock, link_bw,
|
||||
pipe_config->pipe_bpp);
|
||||
|
||||
pipe_config->fdi_lanes = lane;
|
||||
|
||||
intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock,
|
||||
link_bw, &pipe_config->fdi_m_n, false, false);
|
||||
|
||||
ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config);
|
||||
if (ret == -EDEADLK)
|
||||
return ret;
|
||||
|
||||
if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) {
|
||||
pipe_config->pipe_bpp -= 2*3;
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"fdi link bw constraint, reducing pipe bpp to %i\n",
|
||||
pipe_config->pipe_bpp);
|
||||
needs_recompute = true;
|
||||
pipe_config->bw_constrained = true;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (needs_recompute)
|
||||
return I915_DISPLAY_CONFIG_RETRY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void intel_fdi_normal_train(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp;
|
||||
|
||||
/* enable normal train */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if (IS_IVYBRIDGE(dev_priv)) {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE_IVB;
|
||||
temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
|
||||
} else {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
|
||||
}
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if (HAS_PCH_CPT(dev_priv)) {
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp |= FDI_LINK_TRAIN_NORMAL_CPT;
|
||||
} else {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_NONE;
|
||||
}
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
|
||||
|
||||
/* wait one idle pattern time */
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(1000);
|
||||
|
||||
/* IVB wants error correction enabled */
|
||||
if (IS_IVYBRIDGE(dev_priv))
|
||||
intel_de_write(dev_priv, reg,
|
||||
intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
|
||||
}
|
||||
|
||||
/* The FDI link training functions for ILK/Ibexpeak. */
|
||||
static void ilk_fdi_link_train(struct intel_crtc *crtc,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp, tries;
|
||||
|
||||
/* FDI needs bits from pipe first */
|
||||
assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder);
|
||||
|
||||
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
|
||||
for train result */
|
||||
reg = FDI_RX_IMR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_RX_SYMBOL_LOCK;
|
||||
temp &= ~FDI_RX_BIT_LOCK;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
intel_de_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
/* enable CPU FDI TX and PCH FDI RX */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_DP_PORT_WIDTH_MASK;
|
||||
temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
/* Ironlake workaround, enable clock pointer after FDI enable*/
|
||||
intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
|
||||
FDI_RX_PHASE_SYNC_POINTER_OVR);
|
||||
intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
|
||||
FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN);
|
||||
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
for (tries = 0; tries < 5; tries++) {
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
|
||||
if ((temp & FDI_RX_BIT_LOCK)) {
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n");
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tries == 5)
|
||||
drm_err(&dev_priv->drm, "FDI train 1 fail!\n");
|
||||
|
||||
/* Train 2 */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
for (tries = 0; tries < 5; tries++) {
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
|
||||
if (temp & FDI_RX_SYMBOL_LOCK) {
|
||||
intel_de_write(dev_priv, reg,
|
||||
temp | FDI_RX_SYMBOL_LOCK);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tries == 5)
|
||||
drm_err(&dev_priv->drm, "FDI train 2 fail!\n");
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI train done\n");
|
||||
|
||||
}
|
||||
|
||||
static const int snb_b_fdi_train_param[] = {
|
||||
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
|
||||
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
|
||||
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
|
||||
FDI_LINK_TRAIN_800MV_0DB_SNB_B,
|
||||
};
|
||||
|
||||
/* The FDI link training functions for SNB/Cougarpoint. */
|
||||
static void gen6_fdi_link_train(struct intel_crtc *crtc,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp, i, retry;
|
||||
|
||||
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
|
||||
for train result */
|
||||
reg = FDI_RX_IMR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_RX_SYMBOL_LOCK;
|
||||
temp &= ~FDI_RX_BIT_LOCK;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
/* enable CPU FDI TX and PCH FDI RX */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_DP_PORT_WIDTH_MASK;
|
||||
temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
||||
/* SNB-B */
|
||||
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
|
||||
|
||||
intel_de_write(dev_priv, FDI_RX_MISC(pipe),
|
||||
FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if (HAS_PCH_CPT(dev_priv)) {
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
|
||||
} else {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
}
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
||||
temp |= snb_b_fdi_train_param[i];
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(500);
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
if (temp & FDI_RX_BIT_LOCK) {
|
||||
intel_de_write(dev_priv, reg,
|
||||
temp | FDI_RX_BIT_LOCK);
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 1 done.\n");
|
||||
break;
|
||||
}
|
||||
udelay(50);
|
||||
}
|
||||
if (retry < 5)
|
||||
break;
|
||||
}
|
||||
if (i == 4)
|
||||
drm_err(&dev_priv->drm, "FDI train 1 fail!\n");
|
||||
|
||||
/* Train 2 */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2;
|
||||
if (IS_GEN(dev_priv, 6)) {
|
||||
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
||||
/* SNB-B */
|
||||
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
|
||||
}
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if (HAS_PCH_CPT(dev_priv)) {
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
|
||||
} else {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2;
|
||||
}
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
||||
temp |= snb_b_fdi_train_param[i];
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(500);
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
if (temp & FDI_RX_SYMBOL_LOCK) {
|
||||
intel_de_write(dev_priv, reg,
|
||||
temp | FDI_RX_SYMBOL_LOCK);
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 2 done.\n");
|
||||
break;
|
||||
}
|
||||
udelay(50);
|
||||
}
|
||||
if (retry < 5)
|
||||
break;
|
||||
}
|
||||
if (i == 4)
|
||||
drm_err(&dev_priv->drm, "FDI train 2 fail!\n");
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI train done.\n");
|
||||
}
|
||||
|
||||
/* Manual link training for Ivy Bridge A0 parts */
|
||||
static void ivb_manual_fdi_link_train(struct intel_crtc *crtc,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp, i, j;
|
||||
|
||||
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
|
||||
for train result */
|
||||
reg = FDI_RX_IMR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_RX_SYMBOL_LOCK;
|
||||
temp &= ~FDI_RX_BIT_LOCK;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(150);
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n",
|
||||
intel_de_read(dev_priv, FDI_RX_IIR(pipe)));
|
||||
|
||||
/* Try each vswing and preemphasis setting twice before moving on */
|
||||
for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) {
|
||||
/* disable first in case we need to retry */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB);
|
||||
temp &= ~FDI_TX_ENABLE;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_AUTO;
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp &= ~FDI_RX_ENABLE;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
/* enable CPU FDI TX and PCH FDI RX */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_DP_PORT_WIDTH_MASK;
|
||||
temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
|
||||
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
||||
temp |= snb_b_fdi_train_param[j/2];
|
||||
temp |= FDI_COMPOSITE_SYNC;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
|
||||
|
||||
intel_de_write(dev_priv, FDI_RX_MISC(pipe),
|
||||
FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
|
||||
temp |= FDI_COMPOSITE_SYNC;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(1); /* should be 0.5us */
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
|
||||
if (temp & FDI_RX_BIT_LOCK ||
|
||||
(intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) {
|
||||
intel_de_write(dev_priv, reg,
|
||||
temp | FDI_RX_BIT_LOCK);
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 1 done, level %i.\n",
|
||||
i);
|
||||
break;
|
||||
}
|
||||
udelay(1); /* should be 0.5us */
|
||||
}
|
||||
if (i == 4) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 1 fail on vswing %d\n", j / 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Train 2 */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE_IVB;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2_IVB;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(2); /* should be 1.5us */
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
reg = FDI_RX_IIR(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
|
||||
|
||||
if (temp & FDI_RX_SYMBOL_LOCK ||
|
||||
(intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) {
|
||||
intel_de_write(dev_priv, reg,
|
||||
temp | FDI_RX_SYMBOL_LOCK);
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 2 done, level %i.\n",
|
||||
i);
|
||||
goto train_done;
|
||||
}
|
||||
udelay(2); /* should be 1.5us */
|
||||
}
|
||||
if (i == 4)
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"FDI train 2 fail on vswing %d\n", j / 2);
|
||||
}
|
||||
|
||||
train_done:
|
||||
drm_dbg_kms(&dev_priv->drm, "FDI train done.\n");
|
||||
}
|
||||
|
||||
void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev);
|
||||
enum pipe pipe = intel_crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp;
|
||||
|
||||
/* enable PCH FDI RX PLL, wait warmup plus DMI latency */
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16));
|
||||
temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
|
||||
temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
|
||||
intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(200);
|
||||
|
||||
/* Switch from Rawclk to PCDclk */
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
intel_de_write(dev_priv, reg, temp | FDI_PCDCLK);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(200);
|
||||
|
||||
/* Enable CPU FDI TX PLL, always on for Ironlake */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if ((temp & FDI_TX_PLL_ENABLE) == 0) {
|
||||
intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc)
|
||||
{
|
||||
struct drm_device *dev = intel_crtc->base.dev;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||
enum pipe pipe = intel_crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp;
|
||||
|
||||
/* Switch from PCDclk to Rawclk */
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK);
|
||||
|
||||
/* Disable CPU FDI TX PLL */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(100);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE);
|
||||
|
||||
/* Wait for the clocks to turn off. */
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
void ilk_fdi_disable(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
i915_reg_t reg;
|
||||
u32 temp;
|
||||
|
||||
/* disable CPU FDI tx and PCH FDI rx */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE);
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~(0x7 << 16);
|
||||
temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
|
||||
intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(100);
|
||||
|
||||
/* Ironlake workaround, disable clock pointer after downing FDI */
|
||||
if (HAS_PCH_IBX(dev_priv))
|
||||
intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
|
||||
FDI_RX_PHASE_SYNC_POINTER_OVR);
|
||||
|
||||
/* still set train pattern 1 */
|
||||
reg = FDI_TX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
reg = FDI_RX_CTL(pipe);
|
||||
temp = intel_de_read(dev_priv, reg);
|
||||
if (HAS_PCH_CPT(dev_priv)) {
|
||||
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
|
||||
} else {
|
||||
temp &= ~FDI_LINK_TRAIN_NONE;
|
||||
temp |= FDI_LINK_TRAIN_PATTERN_1;
|
||||
}
|
||||
/* BPC in FDI rx is consistent with that in PIPECONF */
|
||||
temp &= ~(0x07 << 16);
|
||||
temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
|
||||
intel_de_write(dev_priv, reg, temp);
|
||||
|
||||
intel_de_posting_read(dev_priv, reg);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
void
|
||||
intel_fdi_init_hook(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (IS_GEN(dev_priv, 5)) {
|
||||
dev_priv->display.fdi_link_train = ilk_fdi_link_train;
|
||||
} else if (IS_GEN(dev_priv, 6)) {
|
||||
dev_priv->display.fdi_link_train = gen6_fdi_link_train;
|
||||
} else if (IS_IVYBRIDGE(dev_priv)) {
|
||||
/* FIXME: detect B0+ stepping and use auto training */
|
||||
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
|
||||
}
|
||||
}
|
22
drivers/gpu/drm/i915/display/intel_fdi.h
Normal file
22
drivers/gpu/drm/i915/display/intel_fdi.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _INTEL_FDI_H_
|
||||
#define _INTEL_FDI_H_
|
||||
|
||||
struct drm_i915_private;
|
||||
struct intel_crtc;
|
||||
struct intel_crtc_state;
|
||||
|
||||
#define I915_DISPLAY_CONFIG_RETRY 1
|
||||
int ilk_fdi_compute_config(struct intel_crtc *intel_crtc,
|
||||
struct intel_crtc_state *pipe_config);
|
||||
void intel_fdi_normal_train(struct intel_crtc *crtc);
|
||||
void ilk_fdi_disable(struct intel_crtc *crtc);
|
||||
void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc);
|
||||
void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state);
|
||||
void intel_fdi_init_hook(struct drm_i915_private *dev_priv);
|
||||
|
||||
#endif
|
@ -15,6 +15,7 @@
|
||||
#include <drm/drm_hdcp.h>
|
||||
#include <drm/i915_component.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "intel_display_power.h"
|
||||
#include "intel_display_types.h"
|
||||
@ -23,9 +24,78 @@
|
||||
#include "intel_connector.h"
|
||||
|
||||
#define KEY_LOAD_TRIES 5
|
||||
#define ENCRYPT_STATUS_CHANGE_TIMEOUT_MS 50
|
||||
#define HDCP2_LC_RETRY_CNT 3
|
||||
|
||||
static int intel_conn_to_vcpi(struct intel_connector *connector)
|
||||
{
|
||||
/* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */
|
||||
return connector->port ? connector->port->vcpi.vcpi : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* intel_hdcp_required_content_stream selects the most highest common possible HDCP
|
||||
* content_type for all streams in DP MST topology because security f/w doesn't
|
||||
* have any provision to mark content_type for each stream separately, it marks
|
||||
* all available streams with the content_type proivided at the time of port
|
||||
* authentication. This may prohibit the userspace to use type1 content on
|
||||
* HDCP 2.2 capable sink because of other sink are not capable of HDCP 2.2 in
|
||||
* DP MST topology. Though it is not compulsory, security fw should change its
|
||||
* policy to mark different content_types for different streams.
|
||||
*/
|
||||
static int
|
||||
intel_hdcp_required_content_stream(struct intel_digital_port *dig_port)
|
||||
{
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct intel_digital_port *conn_dig_port;
|
||||
struct intel_connector *connector;
|
||||
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
bool enforce_type0 = false;
|
||||
int k;
|
||||
|
||||
data->k = 0;
|
||||
|
||||
if (dig_port->hdcp_auth_status)
|
||||
return 0;
|
||||
|
||||
drm_connector_list_iter_begin(&i915->drm, &conn_iter);
|
||||
for_each_intel_connector_iter(connector, &conn_iter) {
|
||||
if (connector->base.status == connector_status_disconnected)
|
||||
continue;
|
||||
|
||||
if (!intel_encoder_is_mst(intel_attached_encoder(connector)))
|
||||
continue;
|
||||
|
||||
conn_dig_port = intel_attached_dig_port(connector);
|
||||
if (conn_dig_port != dig_port)
|
||||
continue;
|
||||
|
||||
if (!enforce_type0 && !intel_hdcp2_capable(connector))
|
||||
enforce_type0 = true;
|
||||
|
||||
data->streams[data->k].stream_id = intel_conn_to_vcpi(connector);
|
||||
data->k++;
|
||||
|
||||
/* if there is only one active stream */
|
||||
if (dig_port->dp.active_mst_links <= 1)
|
||||
break;
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
if (drm_WARN_ON(&i915->drm, data->k > INTEL_NUM_PIPES(i915) || data->k == 0))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Apply common protection level across all streams in DP MST Topology.
|
||||
* Use highest supported content type for all streams in DP MST Topology.
|
||||
*/
|
||||
for (k = 0; k < data->k; k++)
|
||||
data->streams[k].stream_type =
|
||||
enforce_type0 ? DRM_MODE_HDCP_CONTENT_TYPE0 : DRM_MODE_HDCP_CONTENT_TYPE1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
bool intel_hdcp_is_ksv_valid(u8 *ksv)
|
||||
{
|
||||
@ -762,15 +832,22 @@ static int intel_hdcp_auth(struct intel_connector *connector)
|
||||
if (intel_de_wait_for_set(dev_priv,
|
||||
HDCP_STATUS(dev_priv, cpu_transcoder, port),
|
||||
HDCP_STATUS_ENC,
|
||||
ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
drm_err(&dev_priv->drm, "Timed out waiting for encryption\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: If we have MST-connected devices, we need to enable encryption
|
||||
* on those as well.
|
||||
*/
|
||||
/* DP MST Auth Part 1 Step 2.a and Step 2.b */
|
||||
if (shim->stream_encryption) {
|
||||
ret = shim->stream_encryption(connector, true);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm, "[%s:%d] Failed to enable HDCP 1.4 stream enc\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return ret;
|
||||
}
|
||||
drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s stream encrypted\n",
|
||||
transcoder_name(hdcp->stream_transcoder));
|
||||
}
|
||||
|
||||
if (repeater_present)
|
||||
return intel_hdcp_auth_downstream(connector);
|
||||
@ -792,24 +869,29 @@ static int _intel_hdcp_disable(struct intel_connector *connector)
|
||||
drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP is being disabled...\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
|
||||
/*
|
||||
* If there are other connectors on this port using HDCP, don't disable
|
||||
* it. Instead, toggle the HDCP signalling off on that particular
|
||||
* connector/pipe and exit.
|
||||
*/
|
||||
if (dig_port->num_hdcp_streams > 0) {
|
||||
ret = hdcp->shim->toggle_signalling(dig_port,
|
||||
cpu_transcoder, false);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to disable HDCP signalling\n");
|
||||
return ret;
|
||||
if (hdcp->shim->stream_encryption) {
|
||||
ret = hdcp->shim->stream_encryption(connector, false);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm, "[%s:%d] Failed to disable HDCP 1.4 stream enc\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return ret;
|
||||
}
|
||||
drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s stream encryption disabled\n",
|
||||
transcoder_name(hdcp->stream_transcoder));
|
||||
/*
|
||||
* If there are other connectors on this port using HDCP,
|
||||
* don't disable it until it disabled HDCP encryption for
|
||||
* all connectors in MST topology.
|
||||
*/
|
||||
if (dig_port->num_hdcp_streams > 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdcp->hdcp_encrypted = false;
|
||||
intel_de_write(dev_priv, HDCP_CONF(dev_priv, cpu_transcoder, port), 0);
|
||||
if (intel_de_wait_for_clear(dev_priv,
|
||||
HDCP_STATUS(dev_priv, cpu_transcoder, port),
|
||||
~0, ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
~0, HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
|
||||
drm_err(&dev_priv->drm,
|
||||
"Failed to disable HDCP, timeout clearing status\n");
|
||||
return -ETIMEDOUT;
|
||||
@ -1014,7 +1096,8 @@ static int
|
||||
hdcp2_prepare_ake_init(struct intel_connector *connector,
|
||||
struct hdcp2_ake_init *ake_data)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1043,7 +1126,8 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
|
||||
struct hdcp2_ake_no_stored_km *ek_pub_km,
|
||||
size_t *msg_sz)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1070,7 +1154,8 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
|
||||
static int hdcp2_verify_hprime(struct intel_connector *connector,
|
||||
struct hdcp2_ake_send_hprime *rx_hprime)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1095,7 +1180,8 @@ static int
|
||||
hdcp2_store_pairing_info(struct intel_connector *connector,
|
||||
struct hdcp2_ake_send_pairing_info *pairing_info)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1121,7 +1207,8 @@ static int
|
||||
hdcp2_prepare_lc_init(struct intel_connector *connector,
|
||||
struct hdcp2_lc_init *lc_init)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1147,7 +1234,8 @@ static int
|
||||
hdcp2_verify_lprime(struct intel_connector *connector,
|
||||
struct hdcp2_lc_send_lprime *rx_lprime)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1172,7 +1260,8 @@ hdcp2_verify_lprime(struct intel_connector *connector,
|
||||
static int hdcp2_prepare_skey(struct intel_connector *connector,
|
||||
struct hdcp2_ske_send_eks *ske_data)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1200,7 +1289,8 @@ hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
|
||||
*rep_topology,
|
||||
struct hdcp2_rep_send_ack *rep_send_ack)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1228,7 +1318,8 @@ static int
|
||||
hdcp2_verify_mprime(struct intel_connector *connector,
|
||||
struct hdcp2_rep_stream_ready *stream_ready)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1251,7 +1342,8 @@ hdcp2_verify_mprime(struct intel_connector *connector,
|
||||
|
||||
static int hdcp2_authenticate_port(struct intel_connector *connector)
|
||||
{
|
||||
struct hdcp_port_data *data = &connector->hdcp.port_data;
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1275,6 +1367,7 @@ static int hdcp2_authenticate_port(struct intel_connector *connector)
|
||||
|
||||
static int hdcp2_close_mei_session(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct i915_hdcp_comp_master *comp;
|
||||
int ret;
|
||||
@ -1288,7 +1381,7 @@ static int hdcp2_close_mei_session(struct intel_connector *connector)
|
||||
}
|
||||
|
||||
ret = comp->ops->close_hdcp_session(comp->mei_dev,
|
||||
&connector->hdcp.port_data);
|
||||
&dig_port->hdcp_port_data);
|
||||
mutex_unlock(&dev_priv->hdcp_comp_mutex);
|
||||
|
||||
return ret;
|
||||
@ -1448,13 +1541,14 @@ static
|
||||
int _hdcp2_propagate_stream_management_info(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
union {
|
||||
struct hdcp2_rep_stream_manage stream_manage;
|
||||
struct hdcp2_rep_stream_ready stream_ready;
|
||||
} msgs;
|
||||
const struct intel_hdcp_shim *shim = hdcp->shim;
|
||||
int ret;
|
||||
int ret, streams_size_delta, i;
|
||||
|
||||
if (connector->hdcp.seq_num_m > HDCP_2_2_SEQ_NUM_MAX)
|
||||
return -ERANGE;
|
||||
@ -1463,16 +1557,18 @@ int _hdcp2_propagate_stream_management_info(struct intel_connector *connector)
|
||||
msgs.stream_manage.msg_id = HDCP_2_2_REP_STREAM_MANAGE;
|
||||
drm_hdcp_cpu_to_be24(msgs.stream_manage.seq_num_m, hdcp->seq_num_m);
|
||||
|
||||
/* K no of streams is fixed as 1. Stored as big-endian. */
|
||||
msgs.stream_manage.k = cpu_to_be16(1);
|
||||
msgs.stream_manage.k = cpu_to_be16(data->k);
|
||||
|
||||
/* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */
|
||||
msgs.stream_manage.streams[0].stream_id = 0;
|
||||
msgs.stream_manage.streams[0].stream_type = hdcp->content_type;
|
||||
for (i = 0; i < data->k; i++) {
|
||||
msgs.stream_manage.streams[i].stream_id = data->streams[i].stream_id;
|
||||
msgs.stream_manage.streams[i].stream_type = data->streams[i].stream_type;
|
||||
}
|
||||
|
||||
streams_size_delta = (HDCP_2_2_MAX_CONTENT_STREAMS_CNT - data->k) *
|
||||
sizeof(struct hdcp2_streamid_type);
|
||||
/* Send it to Repeater */
|
||||
ret = shim->write_2_2_msg(dig_port, &msgs.stream_manage,
|
||||
sizeof(msgs.stream_manage));
|
||||
sizeof(msgs.stream_manage) - streams_size_delta);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
@ -1481,8 +1577,8 @@ int _hdcp2_propagate_stream_management_info(struct intel_connector *connector)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
hdcp->port_data.seq_num_m = hdcp->seq_num_m;
|
||||
hdcp->port_data.streams[0].stream_type = hdcp->content_type;
|
||||
data->seq_num_m = hdcp->seq_num_m;
|
||||
|
||||
ret = hdcp2_verify_mprime(connector, &msgs.stream_ready);
|
||||
|
||||
out:
|
||||
@ -1606,6 +1702,36 @@ static int hdcp2_authenticate_sink(struct intel_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdcp2_enable_stream_encryption(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
enum transcoder cpu_transcoder = hdcp->cpu_transcoder;
|
||||
enum port port = dig_port->base.port;
|
||||
int ret = 0;
|
||||
|
||||
if (!(intel_de_read(dev_priv, HDCP2_STATUS(dev_priv, cpu_transcoder, port)) &
|
||||
LINK_ENCRYPTION_STATUS)) {
|
||||
drm_err(&dev_priv->drm, "[%s:%d] HDCP 2.2 Link is not encrypted\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (hdcp->shim->stream_2_2_encryption) {
|
||||
ret = hdcp->shim->stream_2_2_encryption(connector, true);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm, "[%s:%d] Failed to enable HDCP 2.2 stream enc\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return ret;
|
||||
}
|
||||
drm_dbg_kms(&dev_priv->drm, "HDCP 2.2 transcoder: %s stream encrypted\n",
|
||||
transcoder_name(hdcp->stream_transcoder));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdcp2_enable_encryption(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
@ -1641,7 +1767,8 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
|
||||
HDCP2_STATUS(dev_priv, cpu_transcoder,
|
||||
port),
|
||||
LINK_ENCRYPTION_STATUS,
|
||||
ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
|
||||
HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
|
||||
dig_port->hdcp_auth_status = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1665,7 +1792,7 @@ static int hdcp2_disable_encryption(struct intel_connector *connector)
|
||||
HDCP2_STATUS(dev_priv, cpu_transcoder,
|
||||
port),
|
||||
LINK_ENCRYPTION_STATUS,
|
||||
ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
|
||||
HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
|
||||
if (ret == -ETIMEDOUT)
|
||||
drm_dbg_kms(&dev_priv->drm, "Disable Encryption Timedout");
|
||||
|
||||
@ -1714,11 +1841,11 @@ hdcp2_propagate_stream_management_info(struct intel_connector *connector)
|
||||
|
||||
static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret, i, tries = 3;
|
||||
int ret = 0, i, tries = 3;
|
||||
|
||||
for (i = 0; i < tries; i++) {
|
||||
for (i = 0; i < tries && !dig_port->hdcp_auth_status; i++) {
|
||||
ret = hdcp2_authenticate_sink(connector);
|
||||
if (!ret) {
|
||||
ret = hdcp2_propagate_stream_management_info(connector);
|
||||
@ -1728,8 +1855,7 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
hdcp->port_data.streams[0].stream_type =
|
||||
hdcp->content_type;
|
||||
|
||||
ret = hdcp2_authenticate_port(connector);
|
||||
if (!ret)
|
||||
break;
|
||||
@ -1744,7 +1870,7 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
|
||||
drm_dbg_kms(&i915->drm, "Port deauth failed.\n");
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (!ret && !dig_port->hdcp_auth_status) {
|
||||
/*
|
||||
* Ensuring the required 200mSec min time interval between
|
||||
* Session Key Exchange and encryption.
|
||||
@ -1759,12 +1885,16 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
|
||||
}
|
||||
}
|
||||
|
||||
ret = hdcp2_enable_stream_encryption(connector);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _intel_hdcp2_enable(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret;
|
||||
|
||||
@ -1772,6 +1902,16 @@ static int _intel_hdcp2_enable(struct intel_connector *connector)
|
||||
connector->base.name, connector->base.base.id,
|
||||
hdcp->content_type);
|
||||
|
||||
/* Stream which requires encryption */
|
||||
if (!intel_encoder_is_mst(intel_attached_encoder(connector))) {
|
||||
data->k = 1;
|
||||
data->streams[0].stream_type = hdcp->content_type;
|
||||
} else {
|
||||
ret = intel_hdcp_required_content_stream(dig_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hdcp2_authenticate_and_encrypt(connector);
|
||||
if (ret) {
|
||||
drm_dbg_kms(&i915->drm, "HDCP2 Type%d Enabling Failed. (%d)\n",
|
||||
@ -1789,18 +1929,37 @@ static int _intel_hdcp2_enable(struct intel_connector *connector)
|
||||
|
||||
static int _intel_hdcp2_disable(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret;
|
||||
|
||||
drm_dbg_kms(&i915->drm, "[%s:%d] HDCP2.2 is being Disabled\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
|
||||
if (hdcp->shim->stream_2_2_encryption) {
|
||||
ret = hdcp->shim->stream_2_2_encryption(connector, false);
|
||||
if (ret) {
|
||||
drm_err(&i915->drm, "[%s:%d] Failed to disable HDCP 2.2 stream enc\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return ret;
|
||||
}
|
||||
drm_dbg_kms(&i915->drm, "HDCP 2.2 transcoder: %s stream encryption disabled\n",
|
||||
transcoder_name(hdcp->stream_transcoder));
|
||||
|
||||
if (dig_port->num_hdcp_streams > 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = hdcp2_disable_encryption(connector);
|
||||
|
||||
if (hdcp2_deauthenticate_port(connector) < 0)
|
||||
drm_dbg_kms(&i915->drm, "Port deauth failed.\n");
|
||||
|
||||
connector->hdcp.hdcp2_encrypted = false;
|
||||
dig_port->hdcp_auth_status = false;
|
||||
data->k = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1816,6 +1975,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&hdcp->mutex);
|
||||
mutex_lock(&dig_port->hdcp_mutex);
|
||||
cpu_transcoder = hdcp->cpu_transcoder;
|
||||
|
||||
/* hdcp2_check_link is expected only when HDCP2.2 is Enabled */
|
||||
@ -1837,7 +1997,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = hdcp->shim->check_2_2_link(dig_port);
|
||||
ret = hdcp->shim->check_2_2_link(dig_port, connector);
|
||||
if (ret == HDCP_LINK_PROTECTED) {
|
||||
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
|
||||
intel_hdcp_update_value(connector,
|
||||
@ -1893,6 +2053,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&dig_port->hdcp_mutex);
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return ret;
|
||||
}
|
||||
@ -1968,12 +2129,13 @@ static enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder)
|
||||
}
|
||||
|
||||
static int initialize_hdcp_port_data(struct intel_connector *connector,
|
||||
enum port port,
|
||||
struct intel_digital_port *dig_port,
|
||||
const struct intel_hdcp_shim *shim)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
struct hdcp_port_data *data = &hdcp->port_data;
|
||||
enum port port = dig_port->base.port;
|
||||
|
||||
if (INTEL_GEN(dev_priv) < 12)
|
||||
data->fw_ddi = intel_get_mei_fw_ddi_index(port);
|
||||
@ -1994,16 +2156,15 @@ static int initialize_hdcp_port_data(struct intel_connector *connector,
|
||||
data->port_type = (u8)HDCP_PORT_TYPE_INTEGRATED;
|
||||
data->protocol = (u8)shim->protocol;
|
||||
|
||||
data->k = 1;
|
||||
if (!data->streams)
|
||||
data->streams = kcalloc(data->k,
|
||||
data->streams = kcalloc(INTEL_NUM_PIPES(dev_priv),
|
||||
sizeof(struct hdcp2_streamid_type),
|
||||
GFP_KERNEL);
|
||||
if (!data->streams) {
|
||||
drm_err(&dev_priv->drm, "Out of Memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* For SST */
|
||||
data->streams[0].stream_id = 0;
|
||||
data->streams[0].stream_type = hdcp->content_type;
|
||||
|
||||
@ -2046,14 +2207,15 @@ void intel_hdcp_component_init(struct drm_i915_private *dev_priv)
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_hdcp2_init(struct intel_connector *connector, enum port port,
|
||||
static void intel_hdcp2_init(struct intel_connector *connector,
|
||||
struct intel_digital_port *dig_port,
|
||||
const struct intel_hdcp_shim *shim)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_hdcp *hdcp = &connector->hdcp;
|
||||
int ret;
|
||||
|
||||
ret = initialize_hdcp_port_data(connector, port, shim);
|
||||
ret = initialize_hdcp_port_data(connector, dig_port, shim);
|
||||
if (ret) {
|
||||
drm_dbg_kms(&i915->drm, "Mei hdcp data init failed\n");
|
||||
return;
|
||||
@ -2063,7 +2225,7 @@ static void intel_hdcp2_init(struct intel_connector *connector, enum port port,
|
||||
}
|
||||
|
||||
int intel_hdcp_init(struct intel_connector *connector,
|
||||
enum port port,
|
||||
struct intel_digital_port *dig_port,
|
||||
const struct intel_hdcp_shim *shim)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
@ -2073,15 +2235,15 @@ int intel_hdcp_init(struct intel_connector *connector,
|
||||
if (!shim)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_hdcp2_supported(dev_priv) && !connector->mst_port)
|
||||
intel_hdcp2_init(connector, port, shim);
|
||||
if (is_hdcp2_supported(dev_priv))
|
||||
intel_hdcp2_init(connector, dig_port, shim);
|
||||
|
||||
ret =
|
||||
drm_connector_attach_content_protection_property(&connector->base,
|
||||
hdcp->hdcp2_supported);
|
||||
if (ret) {
|
||||
hdcp->hdcp2_supported = false;
|
||||
kfree(hdcp->port_data.streams);
|
||||
kfree(dig_port->hdcp_port_data.streams);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2095,7 +2257,7 @@ int intel_hdcp_init(struct intel_connector *connector,
|
||||
}
|
||||
|
||||
int intel_hdcp_enable(struct intel_connector *connector,
|
||||
enum transcoder cpu_transcoder, u8 content_type)
|
||||
const struct intel_crtc_state *pipe_config, u8 content_type)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
|
||||
@ -2106,15 +2268,28 @@ int intel_hdcp_enable(struct intel_connector *connector,
|
||||
if (!hdcp->shim)
|
||||
return -ENOENT;
|
||||
|
||||
if (!connector->encoder) {
|
||||
drm_err(&dev_priv->drm, "[%s:%d] encoder is not initialized\n",
|
||||
connector->base.name, connector->base.base.id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&hdcp->mutex);
|
||||
mutex_lock(&dig_port->hdcp_mutex);
|
||||
drm_WARN_ON(&dev_priv->drm,
|
||||
hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED);
|
||||
hdcp->content_type = content_type;
|
||||
hdcp->cpu_transcoder = cpu_transcoder;
|
||||
|
||||
if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) {
|
||||
hdcp->cpu_transcoder = pipe_config->mst_master_transcoder;
|
||||
hdcp->stream_transcoder = pipe_config->cpu_transcoder;
|
||||
} else {
|
||||
hdcp->cpu_transcoder = pipe_config->cpu_transcoder;
|
||||
hdcp->stream_transcoder = INVALID_TRANSCODER;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 12)
|
||||
hdcp->port_data.fw_tc = intel_get_mei_fw_tc(cpu_transcoder);
|
||||
dig_port->hdcp_port_data.fw_tc = intel_get_mei_fw_tc(hdcp->cpu_transcoder);
|
||||
|
||||
/*
|
||||
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
|
||||
@ -2234,7 +2409,7 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state,
|
||||
|
||||
if (desired_and_not_enabled || content_protection_type_changed)
|
||||
intel_hdcp_enable(connector,
|
||||
crtc_state->cpu_transcoder,
|
||||
crtc_state,
|
||||
(u8)conn_state->hdcp_content_type);
|
||||
}
|
||||
|
||||
@ -2284,7 +2459,6 @@ void intel_hdcp_cleanup(struct intel_connector *connector)
|
||||
drm_WARN_ON(connector->base.dev, work_pending(&hdcp->prop_work));
|
||||
|
||||
mutex_lock(&hdcp->mutex);
|
||||
kfree(hdcp->port_data.streams);
|
||||
hdcp->shim = NULL;
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS 50
|
||||
|
||||
struct drm_connector;
|
||||
struct drm_connector_state;
|
||||
struct drm_i915_private;
|
||||
@ -16,16 +18,18 @@ struct intel_connector;
|
||||
struct intel_crtc_state;
|
||||
struct intel_encoder;
|
||||
struct intel_hdcp_shim;
|
||||
struct intel_digital_port;
|
||||
enum port;
|
||||
enum transcoder;
|
||||
|
||||
void intel_hdcp_atomic_check(struct drm_connector *connector,
|
||||
struct drm_connector_state *old_state,
|
||||
struct drm_connector_state *new_state);
|
||||
int intel_hdcp_init(struct intel_connector *connector, enum port port,
|
||||
int intel_hdcp_init(struct intel_connector *connector,
|
||||
struct intel_digital_port *dig_port,
|
||||
const struct intel_hdcp_shim *hdcp_shim);
|
||||
int intel_hdcp_enable(struct intel_connector *connector,
|
||||
enum transcoder cpu_transcoder, u8 content_type);
|
||||
const struct intel_crtc_state *pipe_config, u8 content_type);
|
||||
int intel_hdcp_disable(struct intel_connector *connector);
|
||||
void intel_hdcp_update_pipe(struct intel_atomic_state *state,
|
||||
struct intel_encoder *encoder,
|
||||
|
@ -1494,15 +1494,16 @@ static int kbl_repositioning_enc_en_signal(struct intel_connector *connector,
|
||||
usleep_range(25, 50);
|
||||
}
|
||||
|
||||
ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
|
||||
false);
|
||||
ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, cpu_transcoder,
|
||||
false, TRANS_DDI_HDCP_SIGNALLING);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm,
|
||||
"Disable HDCP signalling failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
|
||||
true);
|
||||
|
||||
ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, cpu_transcoder,
|
||||
true, TRANS_DDI_HDCP_SIGNALLING);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm,
|
||||
"Enable HDCP signalling failed (%d)\n", ret);
|
||||
@ -1525,8 +1526,9 @@ int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
|
||||
if (!enable)
|
||||
usleep_range(6, 60); /* Bspec says >= 6us */
|
||||
|
||||
ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
|
||||
enable);
|
||||
ret = intel_ddi_toggle_hdcp_bits(&dig_port->base,
|
||||
cpu_transcoder, enable,
|
||||
TRANS_DDI_HDCP_SIGNALLING);
|
||||
if (ret) {
|
||||
drm_err(&dev_priv->drm, "%s HDCP signalling failed (%d)\n",
|
||||
enable ? "Enable" : "Disable", ret);
|
||||
@ -1731,7 +1733,8 @@ int intel_hdmi_hdcp2_read_msg(struct intel_digital_port *dig_port,
|
||||
}
|
||||
|
||||
static
|
||||
int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port)
|
||||
int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port,
|
||||
struct intel_connector *connector)
|
||||
{
|
||||
u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
|
||||
int ret;
|
||||
@ -3290,7 +3293,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port,
|
||||
intel_hdmi->attached_connector = intel_connector;
|
||||
|
||||
if (is_hdcp_supported(dev_priv, port)) {
|
||||
int ret = intel_hdcp_init(intel_connector, port,
|
||||
int ret = intel_hdcp_init(intel_connector, dig_port,
|
||||
&intel_hdmi_hdcp_shim);
|
||||
if (ret)
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
|
@ -511,40 +511,79 @@ static u32 scale_hw_to_user(struct intel_connector *connector,
|
||||
0, user_max);
|
||||
}
|
||||
|
||||
static u32 intel_panel_compute_brightness(struct intel_connector *connector,
|
||||
u32 val)
|
||||
u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
|
||||
|
||||
if (dev_priv->params.invert_brightness < 0)
|
||||
return val;
|
||||
|
||||
if (dev_priv->params.invert_brightness > 0 ||
|
||||
dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
|
||||
return panel->backlight.max - val + panel->backlight.min;
|
||||
return panel->backlight.pwm_level_max - val + panel->backlight.pwm_level_min;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static u32 lpt_get_backlight(struct intel_connector *connector)
|
||||
void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 val)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
drm_dbg_kms(&i915->drm, "set backlight PWM = %d\n", val);
|
||||
panel->backlight.pwm_funcs->set(conn_state, val);
|
||||
}
|
||||
|
||||
u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
drm_WARN_ON_ONCE(&dev_priv->drm,
|
||||
panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
|
||||
|
||||
val = scale(val, panel->backlight.min, panel->backlight.max,
|
||||
panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
|
||||
|
||||
return intel_panel_invert_pwm_level(connector, val);
|
||||
}
|
||||
|
||||
u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
drm_WARN_ON_ONCE(&dev_priv->drm,
|
||||
panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0);
|
||||
|
||||
if (dev_priv->params.invert_brightness > 0 ||
|
||||
(dev_priv->params.invert_brightness == 0 && dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS))
|
||||
val = panel->backlight.pwm_level_max - (val - panel->backlight.pwm_level_min);
|
||||
|
||||
return scale(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max,
|
||||
panel->backlight.min, panel->backlight.max);
|
||||
}
|
||||
|
||||
static u32 lpt_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
|
||||
return intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
}
|
||||
|
||||
static u32 pch_get_backlight(struct intel_connector *connector)
|
||||
static u32 pch_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
|
||||
return intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
}
|
||||
|
||||
static u32 i9xx_get_backlight(struct intel_connector *connector)
|
||||
static u32 i9xx_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
@ -564,23 +603,17 @@ static u32 i9xx_get_backlight(struct intel_connector *connector)
|
||||
return val;
|
||||
}
|
||||
|
||||
static u32 _vlv_get_backlight(struct drm_i915_private *dev_priv, enum pipe pipe)
|
||||
static u32 vlv_get_backlight(struct intel_connector *connector, enum pipe pipe)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
|
||||
if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
|
||||
return 0;
|
||||
|
||||
return intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
}
|
||||
|
||||
static u32 vlv_get_backlight(struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
enum pipe pipe = intel_connector_get_pipe(connector);
|
||||
|
||||
return _vlv_get_backlight(dev_priv, pipe);
|
||||
}
|
||||
|
||||
static u32 bxt_get_backlight(struct intel_connector *connector)
|
||||
static u32 bxt_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
@ -589,7 +622,7 @@ static u32 bxt_get_backlight(struct intel_connector *connector)
|
||||
BXT_BLC_PWM_DUTY(panel->backlight.controller));
|
||||
}
|
||||
|
||||
static u32 ext_pwm_get_backlight(struct intel_connector *connector)
|
||||
static u32 ext_pwm_get_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
struct pwm_state state;
|
||||
@ -624,12 +657,12 @@ static void i9xx_set_backlight(const struct drm_connector_state *conn_state, u32
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 tmp, mask;
|
||||
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
|
||||
|
||||
if (panel->backlight.combination_mode) {
|
||||
u8 lbpc;
|
||||
|
||||
lbpc = level * 0xfe / panel->backlight.max + 1;
|
||||
lbpc = level * 0xfe / panel->backlight.pwm_level_max + 1;
|
||||
level /= lbpc;
|
||||
pci_write_config_byte(dev_priv->drm.pdev, LBPC, lbpc);
|
||||
}
|
||||
@ -681,9 +714,8 @@ intel_panel_actually_set_backlight(const struct drm_connector_state *conn_state,
|
||||
struct drm_i915_private *i915 = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
drm_dbg_kms(&i915->drm, "set backlight PWM = %d\n", level);
|
||||
drm_dbg_kms(&i915->drm, "set backlight level = %d\n", level);
|
||||
|
||||
level = intel_panel_compute_brightness(connector, level);
|
||||
panel->backlight.funcs->set(conn_state, level);
|
||||
}
|
||||
|
||||
@ -732,7 +764,7 @@ static void lpt_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, level);
|
||||
intel_panel_set_pwm_level(old_conn_state, level);
|
||||
|
||||
/*
|
||||
* Although we don't support or enable CPU PWM with LPT/SPT based
|
||||
@ -760,7 +792,7 @@ static void pch_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
|
||||
tmp = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
|
||||
intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, tmp & ~BLM_PWM_ENABLE);
|
||||
@ -771,7 +803,7 @@ static void pch_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
|
||||
static void i9xx_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
|
||||
{
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
}
|
||||
|
||||
static void i965_disable_backlight(const struct drm_connector_state *old_conn_state, u32 val)
|
||||
@ -779,7 +811,7 @@ static void i965_disable_backlight(const struct drm_connector_state *old_conn_st
|
||||
struct drm_i915_private *dev_priv = to_i915(old_conn_state->connector->dev);
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
|
||||
tmp = intel_de_read(dev_priv, BLC_PWM_CTL2);
|
||||
intel_de_write(dev_priv, BLC_PWM_CTL2, tmp & ~BLM_PWM_ENABLE);
|
||||
@ -792,7 +824,7 @@ static void vlv_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
enum pipe pipe = to_intel_crtc(old_conn_state->crtc)->pipe;
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
|
||||
tmp = intel_de_read(dev_priv, VLV_BLC_PWM_CTL2(pipe));
|
||||
intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe),
|
||||
@ -806,7 +838,7 @@ static void bxt_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
|
||||
tmp = intel_de_read(dev_priv,
|
||||
BXT_BLC_PWM_CTL(panel->backlight.controller));
|
||||
@ -827,7 +859,7 @@ static void cnp_disable_backlight(const struct drm_connector_state *old_conn_sta
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 tmp;
|
||||
|
||||
intel_panel_actually_set_backlight(old_conn_state, val);
|
||||
intel_panel_set_pwm_level(old_conn_state, val);
|
||||
|
||||
tmp = intel_de_read(dev_priv,
|
||||
BXT_BLC_PWM_CTL(panel->backlight.controller));
|
||||
@ -906,7 +938,7 @@ static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_write(dev_priv, SOUTH_CHICKEN1, schicken);
|
||||
}
|
||||
|
||||
pch_ctl2 = panel->backlight.max << 16;
|
||||
pch_ctl2 = panel->backlight.pwm_level_max << 16;
|
||||
intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
|
||||
|
||||
pch_ctl1 = 0;
|
||||
@ -923,7 +955,7 @@ static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
pch_ctl1 | BLM_PCH_PWM_ENABLE);
|
||||
|
||||
/* This won't stick until the above enable. */
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
}
|
||||
|
||||
static void pch_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
@ -958,9 +990,9 @@ static void pch_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_write(dev_priv, BLC_PWM_CPU_CTL2, cpu_ctl2 | BLM_PWM_ENABLE);
|
||||
|
||||
/* This won't stick until the above enable. */
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
|
||||
pch_ctl2 = panel->backlight.max << 16;
|
||||
pch_ctl2 = panel->backlight.pwm_level_max << 16;
|
||||
intel_de_write(dev_priv, BLC_PWM_PCH_CTL2, pch_ctl2);
|
||||
|
||||
pch_ctl1 = 0;
|
||||
@ -987,7 +1019,7 @@ static void i9xx_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_write(dev_priv, BLC_PWM_CTL, 0);
|
||||
}
|
||||
|
||||
freq = panel->backlight.max;
|
||||
freq = panel->backlight.pwm_level_max;
|
||||
if (panel->backlight.combination_mode)
|
||||
freq /= 0xff;
|
||||
|
||||
@ -1001,7 +1033,7 @@ static void i9xx_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_posting_read(dev_priv, BLC_PWM_CTL);
|
||||
|
||||
/* XXX: combine this into above write? */
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
|
||||
/*
|
||||
* Needed to enable backlight on some 855gm models. BLC_HIST_CTL is
|
||||
@ -1028,7 +1060,7 @@ static void i965_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2);
|
||||
}
|
||||
|
||||
freq = panel->backlight.max;
|
||||
freq = panel->backlight.pwm_level_max;
|
||||
if (panel->backlight.combination_mode)
|
||||
freq /= 0xff;
|
||||
|
||||
@ -1044,7 +1076,7 @@ static void i965_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_posting_read(dev_priv, BLC_PWM_CTL2);
|
||||
intel_de_write(dev_priv, BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE);
|
||||
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
}
|
||||
|
||||
static void vlv_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
@ -1063,11 +1095,11 @@ static void vlv_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
intel_de_write(dev_priv, VLV_BLC_PWM_CTL2(pipe), ctl2);
|
||||
}
|
||||
|
||||
ctl = panel->backlight.max << 16;
|
||||
ctl = panel->backlight.pwm_level_max << 16;
|
||||
intel_de_write(dev_priv, VLV_BLC_PWM_CTL(pipe), ctl);
|
||||
|
||||
/* XXX: combine this into above write? */
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
|
||||
ctl2 = 0;
|
||||
if (panel->backlight.active_low_pwm)
|
||||
@ -1116,9 +1148,9 @@ static void bxt_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
|
||||
intel_de_write(dev_priv,
|
||||
BXT_BLC_PWM_FREQ(panel->backlight.controller),
|
||||
panel->backlight.max);
|
||||
panel->backlight.pwm_level_max);
|
||||
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
|
||||
pwm_ctl = 0;
|
||||
if (panel->backlight.active_low_pwm)
|
||||
@ -1152,9 +1184,9 @@ static void cnp_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
|
||||
intel_de_write(dev_priv,
|
||||
BXT_BLC_PWM_FREQ(panel->backlight.controller),
|
||||
panel->backlight.max);
|
||||
panel->backlight.pwm_level_max);
|
||||
|
||||
intel_panel_actually_set_backlight(conn_state, level);
|
||||
intel_panel_set_pwm_level(conn_state, level);
|
||||
|
||||
pwm_ctl = 0;
|
||||
if (panel->backlight.active_low_pwm)
|
||||
@ -1174,7 +1206,6 @@ static void ext_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
level = intel_panel_compute_brightness(connector, level);
|
||||
pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
|
||||
panel->backlight.pwm_state.enabled = true;
|
||||
pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
|
||||
@ -1232,10 +1263,8 @@ static u32 intel_panel_get_backlight(struct intel_connector *connector)
|
||||
|
||||
mutex_lock(&dev_priv->backlight_lock);
|
||||
|
||||
if (panel->backlight.enabled) {
|
||||
val = panel->backlight.funcs->get(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
}
|
||||
if (panel->backlight.enabled)
|
||||
val = panel->backlight.funcs->get(connector, intel_connector_get_pipe(connector));
|
||||
|
||||
mutex_unlock(&dev_priv->backlight_lock);
|
||||
|
||||
@ -1566,13 +1595,13 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector)
|
||||
u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv);
|
||||
u32 pwm;
|
||||
|
||||
if (!panel->backlight.funcs->hz_to_pwm) {
|
||||
if (!panel->backlight.pwm_funcs->hz_to_pwm) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"backlight frequency conversion not supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pwm = panel->backlight.funcs->hz_to_pwm(connector, pwm_freq_hz);
|
||||
pwm = panel->backlight.pwm_funcs->hz_to_pwm(connector, pwm_freq_hz);
|
||||
if (!pwm) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"backlight frequency conversion failed\n");
|
||||
@ -1591,7 +1620,7 @@ static u32 get_backlight_min_vbt(struct intel_connector *connector)
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
int min;
|
||||
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0);
|
||||
drm_WARN_ON(&dev_priv->drm, panel->backlight.pwm_level_max == 0);
|
||||
|
||||
/*
|
||||
* XXX: If the vbt value is 255, it makes min equal to max, which leads
|
||||
@ -1608,7 +1637,7 @@ static u32 get_backlight_min_vbt(struct intel_connector *connector)
|
||||
}
|
||||
|
||||
/* vbt value is a coefficient in range [0..255] */
|
||||
return scale(min, 0, 255, 0, panel->backlight.max);
|
||||
return scale(min, 0, 255, 0, panel->backlight.pwm_level_max);
|
||||
}
|
||||
|
||||
static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
@ -1628,29 +1657,27 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
|
||||
panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
|
||||
|
||||
pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
|
||||
panel->backlight.max = pch_ctl2 >> 16;
|
||||
panel->backlight.pwm_level_max = pch_ctl2 >> 16;
|
||||
|
||||
cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
panel->backlight.enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE;
|
||||
panel->backlight.pwm_enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE;
|
||||
|
||||
cpu_mode = panel->backlight.enabled && HAS_PCH_LPT(dev_priv) &&
|
||||
cpu_mode = panel->backlight.pwm_enabled && HAS_PCH_LPT(dev_priv) &&
|
||||
!(pch_ctl1 & BLM_PCH_OVERRIDE_ENABLE) &&
|
||||
(cpu_ctl2 & BLM_PWM_ENABLE);
|
||||
if (cpu_mode)
|
||||
val = pch_get_backlight(connector);
|
||||
else
|
||||
val = lpt_get_backlight(connector);
|
||||
|
||||
if (cpu_mode) {
|
||||
val = pch_get_backlight(connector, unused);
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"CPU backlight register was enabled, switching to PCH override\n");
|
||||
|
||||
@ -1663,10 +1690,6 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
|
||||
cpu_ctl2 & ~BLM_PWM_ENABLE);
|
||||
}
|
||||
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1674,29 +1697,24 @@ static int pch_setup_backlight(struct intel_connector *connector, enum pipe unus
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 cpu_ctl2, pch_ctl1, pch_ctl2, val;
|
||||
u32 cpu_ctl2, pch_ctl1, pch_ctl2;
|
||||
|
||||
pch_ctl1 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL1);
|
||||
panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
|
||||
|
||||
pch_ctl2 = intel_de_read(dev_priv, BLC_PWM_PCH_CTL2);
|
||||
panel->backlight.max = pch_ctl2 >> 16;
|
||||
panel->backlight.pwm_level_max = pch_ctl2 >> 16;
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = pch_get_backlight(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
cpu_ctl2 = intel_de_read(dev_priv, BLC_PWM_CPU_CTL2);
|
||||
panel->backlight.enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
|
||||
panel->backlight.pwm_enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
|
||||
(pch_ctl1 & BLM_PCH_PWM_ENABLE);
|
||||
|
||||
return 0;
|
||||
@ -1716,27 +1734,26 @@ static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unu
|
||||
if (IS_PINEVIEW(dev_priv))
|
||||
panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV;
|
||||
|
||||
panel->backlight.max = ctl >> 17;
|
||||
panel->backlight.pwm_level_max = ctl >> 17;
|
||||
|
||||
if (!panel->backlight.max) {
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
panel->backlight.max >>= 1;
|
||||
if (!panel->backlight.pwm_level_max) {
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
panel->backlight.pwm_level_max >>= 1;
|
||||
}
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
if (panel->backlight.combination_mode)
|
||||
panel->backlight.max *= 0xff;
|
||||
panel->backlight.pwm_level_max *= 0xff;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = i9xx_get_backlight(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
val = i9xx_get_backlight(connector, unused);
|
||||
val = intel_panel_invert_pwm_level(connector, val);
|
||||
val = clamp(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max);
|
||||
|
||||
panel->backlight.enabled = val != 0;
|
||||
panel->backlight.pwm_enabled = val != 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1745,32 +1762,27 @@ static int i965_setup_backlight(struct intel_connector *connector, enum pipe unu
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 ctl, ctl2, val;
|
||||
u32 ctl, ctl2;
|
||||
|
||||
ctl2 = intel_de_read(dev_priv, BLC_PWM_CTL2);
|
||||
panel->backlight.combination_mode = ctl2 & BLM_COMBINATION_MODE;
|
||||
panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
|
||||
|
||||
ctl = intel_de_read(dev_priv, BLC_PWM_CTL);
|
||||
panel->backlight.max = ctl >> 16;
|
||||
panel->backlight.pwm_level_max = ctl >> 16;
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
if (panel->backlight.combination_mode)
|
||||
panel->backlight.max *= 0xff;
|
||||
panel->backlight.pwm_level_max *= 0xff;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = i9xx_get_backlight(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
|
||||
panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE;
|
||||
panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1779,7 +1791,7 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 ctl, ctl2, val;
|
||||
u32 ctl, ctl2;
|
||||
|
||||
if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B))
|
||||
return -ENODEV;
|
||||
@ -1788,22 +1800,17 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe
|
||||
panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
|
||||
|
||||
ctl = intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe));
|
||||
panel->backlight.max = ctl >> 16;
|
||||
panel->backlight.pwm_level_max = ctl >> 16;
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = _vlv_get_backlight(dev_priv, pipe);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
|
||||
panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE;
|
||||
panel->backlight.pwm_enabled = ctl2 & BLM_PWM_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1828,24 +1835,18 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
}
|
||||
|
||||
panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
|
||||
panel->backlight.max =
|
||||
intel_de_read(dev_priv,
|
||||
BXT_BLC_PWM_FREQ(panel->backlight.controller));
|
||||
panel->backlight.pwm_level_max =
|
||||
intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = bxt_get_backlight(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
|
||||
panel->backlight.enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
|
||||
panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1855,7 +1856,7 @@ cnp_setup_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u32 pwm_ctl, val;
|
||||
u32 pwm_ctl;
|
||||
|
||||
/*
|
||||
* CNP has the BXT implementation of backlight, but with only one
|
||||
@ -1868,24 +1869,18 @@ cnp_setup_backlight(struct intel_connector *connector, enum pipe unused)
|
||||
BXT_BLC_PWM_CTL(panel->backlight.controller));
|
||||
|
||||
panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY;
|
||||
panel->backlight.max =
|
||||
intel_de_read(dev_priv,
|
||||
BXT_BLC_PWM_FREQ(panel->backlight.controller));
|
||||
panel->backlight.pwm_level_max =
|
||||
intel_de_read(dev_priv, BXT_BLC_PWM_FREQ(panel->backlight.controller));
|
||||
|
||||
if (!panel->backlight.max)
|
||||
panel->backlight.max = get_backlight_max_vbt(connector);
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
panel->backlight.pwm_level_max = get_backlight_max_vbt(connector);
|
||||
|
||||
if (!panel->backlight.max)
|
||||
if (!panel->backlight.pwm_level_max)
|
||||
return -ENODEV;
|
||||
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
val = bxt_get_backlight(connector);
|
||||
val = intel_panel_compute_brightness(connector, val);
|
||||
panel->backlight.level = clamp(val, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
|
||||
panel->backlight.enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
|
||||
panel->backlight.pwm_enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1915,8 +1910,8 @@ static int ext_pwm_setup_backlight(struct intel_connector *connector,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
panel->backlight.max = 100; /* 100% */
|
||||
panel->backlight.min = get_backlight_min_vbt(connector);
|
||||
panel->backlight.pwm_level_max = 100; /* 100% */
|
||||
panel->backlight.pwm_level_min = get_backlight_min_vbt(connector);
|
||||
|
||||
if (pwm_is_enabled(panel->backlight.pwm)) {
|
||||
/* PWM is already enabled, use existing settings */
|
||||
@ -1924,10 +1919,8 @@ static int ext_pwm_setup_backlight(struct intel_connector *connector,
|
||||
|
||||
level = pwm_get_relative_duty_cycle(&panel->backlight.pwm_state,
|
||||
100);
|
||||
level = intel_panel_compute_brightness(connector, level);
|
||||
panel->backlight.level = clamp(level, panel->backlight.min,
|
||||
panel->backlight.max);
|
||||
panel->backlight.enabled = true;
|
||||
level = intel_panel_invert_pwm_level(connector, level);
|
||||
panel->backlight.pwm_enabled = true;
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm, "PWM already enabled at freq %ld, VBT freq %d, level %d\n",
|
||||
NSEC_PER_SEC / (unsigned long)panel->backlight.pwm_state.period,
|
||||
@ -1943,6 +1936,58 @@ static int ext_pwm_setup_backlight(struct intel_connector *connector,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
panel->backlight.pwm_funcs->set(conn_state,
|
||||
intel_panel_invert_pwm_level(connector, level));
|
||||
}
|
||||
|
||||
static u32 intel_pwm_get_backlight(struct intel_connector *connector, enum pipe pipe)
|
||||
{
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
return intel_panel_invert_pwm_level(connector,
|
||||
panel->backlight.pwm_funcs->get(connector, pipe));
|
||||
}
|
||||
|
||||
static void intel_pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
|
||||
const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
panel->backlight.pwm_funcs->enable(crtc_state, conn_state,
|
||||
intel_panel_invert_pwm_level(connector, level));
|
||||
}
|
||||
|
||||
static void intel_pwm_disable_backlight(const struct drm_connector_state *conn_state, u32 level)
|
||||
{
|
||||
struct intel_connector *connector = to_intel_connector(conn_state->connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
|
||||
panel->backlight.pwm_funcs->disable(conn_state,
|
||||
intel_panel_invert_pwm_level(connector, level));
|
||||
}
|
||||
|
||||
static int intel_pwm_setup_backlight(struct intel_connector *connector, enum pipe pipe)
|
||||
{
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
int ret = panel->backlight.pwm_funcs->setup(connector, pipe);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
panel->backlight.min = panel->backlight.pwm_level_min;
|
||||
panel->backlight.max = panel->backlight.pwm_level_max;
|
||||
panel->backlight.level = intel_pwm_get_backlight(connector, pipe);
|
||||
panel->backlight.enabled = panel->backlight.pwm_enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void intel_panel_update_backlight(struct intel_atomic_state *state,
|
||||
struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
@ -2016,7 +2061,7 @@ static void intel_panel_destroy_backlight(struct intel_panel *panel)
|
||||
panel->backlight.present = false;
|
||||
}
|
||||
|
||||
static const struct intel_panel_bl_funcs bxt_funcs = {
|
||||
static const struct intel_panel_bl_funcs bxt_pwm_funcs = {
|
||||
.setup = bxt_setup_backlight,
|
||||
.enable = bxt_enable_backlight,
|
||||
.disable = bxt_disable_backlight,
|
||||
@ -2025,7 +2070,7 @@ static const struct intel_panel_bl_funcs bxt_funcs = {
|
||||
.hz_to_pwm = bxt_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs cnp_funcs = {
|
||||
static const struct intel_panel_bl_funcs cnp_pwm_funcs = {
|
||||
.setup = cnp_setup_backlight,
|
||||
.enable = cnp_enable_backlight,
|
||||
.disable = cnp_disable_backlight,
|
||||
@ -2034,7 +2079,7 @@ static const struct intel_panel_bl_funcs cnp_funcs = {
|
||||
.hz_to_pwm = cnp_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs lpt_funcs = {
|
||||
static const struct intel_panel_bl_funcs lpt_pwm_funcs = {
|
||||
.setup = lpt_setup_backlight,
|
||||
.enable = lpt_enable_backlight,
|
||||
.disable = lpt_disable_backlight,
|
||||
@ -2043,7 +2088,7 @@ static const struct intel_panel_bl_funcs lpt_funcs = {
|
||||
.hz_to_pwm = lpt_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs spt_funcs = {
|
||||
static const struct intel_panel_bl_funcs spt_pwm_funcs = {
|
||||
.setup = lpt_setup_backlight,
|
||||
.enable = lpt_enable_backlight,
|
||||
.disable = lpt_disable_backlight,
|
||||
@ -2052,7 +2097,7 @@ static const struct intel_panel_bl_funcs spt_funcs = {
|
||||
.hz_to_pwm = spt_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs pch_funcs = {
|
||||
static const struct intel_panel_bl_funcs pch_pwm_funcs = {
|
||||
.setup = pch_setup_backlight,
|
||||
.enable = pch_enable_backlight,
|
||||
.disable = pch_disable_backlight,
|
||||
@ -2069,7 +2114,7 @@ static const struct intel_panel_bl_funcs ext_pwm_funcs = {
|
||||
.get = ext_pwm_get_backlight,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs vlv_funcs = {
|
||||
static const struct intel_panel_bl_funcs vlv_pwm_funcs = {
|
||||
.setup = vlv_setup_backlight,
|
||||
.enable = vlv_enable_backlight,
|
||||
.disable = vlv_disable_backlight,
|
||||
@ -2078,7 +2123,7 @@ static const struct intel_panel_bl_funcs vlv_funcs = {
|
||||
.hz_to_pwm = vlv_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs i965_funcs = {
|
||||
static const struct intel_panel_bl_funcs i965_pwm_funcs = {
|
||||
.setup = i965_setup_backlight,
|
||||
.enable = i965_enable_backlight,
|
||||
.disable = i965_disable_backlight,
|
||||
@ -2087,7 +2132,7 @@ static const struct intel_panel_bl_funcs i965_funcs = {
|
||||
.hz_to_pwm = i965_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs i9xx_funcs = {
|
||||
static const struct intel_panel_bl_funcs i9xx_pwm_funcs = {
|
||||
.setup = i9xx_setup_backlight,
|
||||
.enable = i9xx_enable_backlight,
|
||||
.disable = i9xx_disable_backlight,
|
||||
@ -2096,6 +2141,14 @@ static const struct intel_panel_bl_funcs i9xx_funcs = {
|
||||
.hz_to_pwm = i9xx_hz_to_pwm,
|
||||
};
|
||||
|
||||
static const struct intel_panel_bl_funcs pwm_bl_funcs = {
|
||||
.setup = intel_pwm_setup_backlight,
|
||||
.enable = intel_pwm_enable_backlight,
|
||||
.disable = intel_pwm_disable_backlight,
|
||||
.set = intel_pwm_set_backlight,
|
||||
.get = intel_pwm_get_backlight,
|
||||
};
|
||||
|
||||
/* Set up chip specific backlight functions */
|
||||
static void
|
||||
intel_panel_init_backlight_funcs(struct intel_panel *panel)
|
||||
@ -2104,36 +2157,39 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
|
||||
container_of(panel, struct intel_connector, panel);
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
|
||||
if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP &&
|
||||
intel_dp_aux_init_backlight_funcs(connector) == 0)
|
||||
return;
|
||||
|
||||
if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI &&
|
||||
intel_dsi_dcs_init_backlight_funcs(connector) == 0)
|
||||
return;
|
||||
|
||||
if (IS_GEN9_LP(dev_priv)) {
|
||||
panel->backlight.funcs = &bxt_funcs;
|
||||
panel->backlight.pwm_funcs = &bxt_pwm_funcs;
|
||||
} else if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) {
|
||||
panel->backlight.funcs = &cnp_funcs;
|
||||
panel->backlight.pwm_funcs = &cnp_pwm_funcs;
|
||||
} else if (INTEL_PCH_TYPE(dev_priv) >= PCH_LPT) {
|
||||
if (HAS_PCH_LPT(dev_priv))
|
||||
panel->backlight.funcs = &lpt_funcs;
|
||||
panel->backlight.pwm_funcs = &lpt_pwm_funcs;
|
||||
else
|
||||
panel->backlight.funcs = &spt_funcs;
|
||||
panel->backlight.pwm_funcs = &spt_pwm_funcs;
|
||||
} else if (HAS_PCH_SPLIT(dev_priv)) {
|
||||
panel->backlight.funcs = &pch_funcs;
|
||||
panel->backlight.pwm_funcs = &pch_pwm_funcs;
|
||||
} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
|
||||
if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) {
|
||||
panel->backlight.funcs = &ext_pwm_funcs;
|
||||
panel->backlight.pwm_funcs = &ext_pwm_funcs;
|
||||
} else {
|
||||
panel->backlight.funcs = &vlv_funcs;
|
||||
panel->backlight.pwm_funcs = &vlv_pwm_funcs;
|
||||
}
|
||||
} else if (IS_GEN(dev_priv, 4)) {
|
||||
panel->backlight.funcs = &i965_funcs;
|
||||
panel->backlight.pwm_funcs = &i965_pwm_funcs;
|
||||
} else {
|
||||
panel->backlight.funcs = &i9xx_funcs;
|
||||
panel->backlight.pwm_funcs = &i9xx_pwm_funcs;
|
||||
}
|
||||
|
||||
if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP &&
|
||||
intel_dp_aux_init_backlight_funcs(connector) == 0)
|
||||
return;
|
||||
|
||||
/* We're using a standard PWM backlight interface */
|
||||
panel->backlight.funcs = &pwm_bl_funcs;
|
||||
}
|
||||
|
||||
enum drm_connector_status
|
||||
|
@ -49,6 +49,10 @@ struct drm_display_mode *
|
||||
intel_panel_edid_fixed_mode(struct intel_connector *connector);
|
||||
struct drm_display_mode *
|
||||
intel_panel_vbt_fixed_mode(struct intel_connector *connector);
|
||||
void intel_panel_set_pwm_level(const struct drm_connector_state *conn_state, u32 level);
|
||||
u32 intel_panel_invert_pwm_level(struct intel_connector *connector, u32 level);
|
||||
u32 intel_panel_backlight_level_to_pwm(struct intel_connector *connector, u32 level);
|
||||
u32 intel_panel_backlight_level_from_pwm(struct intel_connector *connector, u32 val);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
|
||||
int intel_backlight_device_register(struct intel_connector *connector);
|
||||
|
1406
drivers/gpu/drm/i915/display/intel_pps.c
Normal file
1406
drivers/gpu/drm/i915/display/intel_pps.c
Normal file
File diff suppressed because it is too large
Load Diff
52
drivers/gpu/drm/i915/display/intel_pps.h
Normal file
52
drivers/gpu/drm/i915/display/intel_pps.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_PPS_H__
|
||||
#define __INTEL_PPS_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "intel_wakeref.h"
|
||||
|
||||
struct drm_i915_private;
|
||||
struct intel_connector;
|
||||
struct intel_crtc_state;
|
||||
struct intel_dp;
|
||||
struct intel_encoder;
|
||||
|
||||
intel_wakeref_t intel_pps_lock(struct intel_dp *intel_dp);
|
||||
intel_wakeref_t intel_pps_unlock(struct intel_dp *intel_dp, intel_wakeref_t wakeref);
|
||||
|
||||
#define with_intel_pps_lock(dp, wf) \
|
||||
for ((wf) = intel_pps_lock(dp); (wf); (wf) = intel_pps_unlock((dp), (wf)))
|
||||
|
||||
void intel_pps_backlight_on(struct intel_dp *intel_dp);
|
||||
void intel_pps_backlight_off(struct intel_dp *intel_dp);
|
||||
void intel_pps_backlight_power(struct intel_connector *connector, bool enable);
|
||||
|
||||
bool intel_pps_vdd_on_unlocked(struct intel_dp *intel_dp);
|
||||
void intel_pps_vdd_off_unlocked(struct intel_dp *intel_dp, bool sync);
|
||||
void intel_pps_on_unlocked(struct intel_dp *intel_dp);
|
||||
void intel_pps_off_unlocked(struct intel_dp *intel_dp);
|
||||
void intel_pps_check_power_unlocked(struct intel_dp *intel_dp);
|
||||
|
||||
void intel_pps_vdd_on(struct intel_dp *intel_dp);
|
||||
void intel_pps_on(struct intel_dp *intel_dp);
|
||||
void intel_pps_off(struct intel_dp *intel_dp);
|
||||
void intel_pps_vdd_off_sync(struct intel_dp *intel_dp);
|
||||
bool intel_pps_have_power(struct intel_dp *intel_dp);
|
||||
void intel_pps_wait_power_cycle(struct intel_dp *intel_dp);
|
||||
|
||||
void intel_pps_init(struct intel_dp *intel_dp);
|
||||
void intel_pps_encoder_reset(struct intel_dp *intel_dp);
|
||||
void intel_pps_reset_all(struct drm_i915_private *i915);
|
||||
|
||||
void vlv_pps_init(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
|
||||
void intel_pps_unlock_regs_wa(struct drm_i915_private *i915);
|
||||
void intel_pps_setup(struct drm_i915_private *i915);
|
||||
|
||||
#endif /* __INTEL_PPS_H__ */
|
@ -28,9 +28,10 @@
|
||||
#include "i915_drv.h"
|
||||
#include "intel_atomic.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_dp_aux.h"
|
||||
#include "intel_hdmi.h"
|
||||
#include "intel_psr.h"
|
||||
#include "intel_sprite.h"
|
||||
#include "intel_hdmi.h"
|
||||
|
||||
/**
|
||||
* DOC: Panel Self Refresh (PSR/SRD)
|
||||
@ -305,7 +306,7 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
|
||||
drm_dbg_kms(&dev_priv->drm, "eDP panel supports PSR version %x\n",
|
||||
intel_dp->psr_dpcd[0]);
|
||||
|
||||
if (drm_dp_has_quirk(&intel_dp->desc, 0, DP_DPCD_QUIRK_NO_PSR)) {
|
||||
if (drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_NO_PSR)) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"PSR support not currently available for this panel\n");
|
||||
return;
|
||||
@ -811,6 +812,13 @@ void intel_psr_compute_config(struct intel_dp *intel_dp,
|
||||
&crtc_state->hw.adjusted_mode;
|
||||
int psr_setup_time;
|
||||
|
||||
/*
|
||||
* Current PSR panels dont work reliably with VRR enabled
|
||||
* So if VRR is enabled, do not enable PSR.
|
||||
*/
|
||||
if (crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
if (!CAN_PSR(dev_priv))
|
||||
return;
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "intel_dsi.h"
|
||||
#include "intel_sprite.h"
|
||||
#include "i9xx_plane.h"
|
||||
#include "intel_vrr.h"
|
||||
|
||||
int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
|
||||
int usecs)
|
||||
@ -62,6 +63,16 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
|
||||
1000 * adjusted_mode->crtc_htotal);
|
||||
}
|
||||
|
||||
static int intel_mode_vblank_start(const struct drm_display_mode *mode)
|
||||
{
|
||||
int vblank_start = mode->crtc_vblank_start;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
vblank_start = DIV_ROUND_UP(vblank_start, 2);
|
||||
|
||||
return vblank_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pipe_update_start() - start update of a set of display registers
|
||||
* @new_crtc_state: the new crtc state
|
||||
@ -90,9 +101,10 @@ void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state)
|
||||
if (new_crtc_state->uapi.async_flip)
|
||||
return;
|
||||
|
||||
vblank_start = adjusted_mode->crtc_vblank_start;
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
vblank_start = DIV_ROUND_UP(vblank_start, 2);
|
||||
if (new_crtc_state->vrr.enable)
|
||||
vblank_start = intel_vrr_vmax_vblank_start(new_crtc_state);
|
||||
else
|
||||
vblank_start = intel_mode_vblank_start(adjusted_mode);
|
||||
|
||||
/* FIXME needs to be calibrated sensibly */
|
||||
min = vblank_start - intel_usecs_to_scanlines(adjusted_mode,
|
||||
@ -258,6 +270,9 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state)
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
/* Send VRR Push to terminate Vblank */
|
||||
intel_vrr_send_push(new_crtc_state);
|
||||
|
||||
if (intel_vgpu_active(dev_priv))
|
||||
return;
|
||||
|
||||
@ -771,7 +786,8 @@ icl_program_input_csc(struct intel_plane *plane,
|
||||
static void
|
||||
skl_plane_async_flip(struct intel_plane *plane,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
const struct intel_plane_state *plane_state)
|
||||
const struct intel_plane_state *plane_state,
|
||||
bool async_flip)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
|
||||
unsigned long irqflags;
|
||||
@ -782,6 +798,9 @@ skl_plane_async_flip(struct intel_plane *plane,
|
||||
|
||||
plane_ctl |= skl_plane_ctl_crtc(crtc_state);
|
||||
|
||||
if (async_flip)
|
||||
plane_ctl |= PLANE_CTL_ASYNC_FLIP;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
||||
|
||||
intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl);
|
||||
@ -867,6 +886,10 @@ skl_program_plane(struct intel_plane *plane,
|
||||
if (fb->format->is_yuv && icl_is_hdr_plane(dev_priv, plane_id))
|
||||
icl_program_input_csc(plane, crtc_state, plane_state);
|
||||
|
||||
if (fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC)
|
||||
intel_uncore_write64_fw(&dev_priv->uncore,
|
||||
PLANE_CC_VAL(pipe, plane_id), plane_state->ccval);
|
||||
|
||||
skl_write_plane_wm(plane, crtc_state);
|
||||
|
||||
intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id),
|
||||
@ -958,6 +981,28 @@ skl_plane_get_hw_state(struct intel_plane *plane,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
skl_plane_enable_flip_done(struct intel_plane *plane)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(plane->base.dev);
|
||||
enum pipe pipe = plane->pipe;
|
||||
|
||||
spin_lock_irq(&i915->irq_lock);
|
||||
bdw_enable_pipe_irq(i915, pipe, GEN9_PIPE_PLANE_FLIP_DONE(plane->id));
|
||||
spin_unlock_irq(&i915->irq_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
skl_plane_disable_flip_done(struct intel_plane *plane)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(plane->base.dev);
|
||||
enum pipe pipe = plane->pipe;
|
||||
|
||||
spin_lock_irq(&i915->irq_lock);
|
||||
bdw_disable_pipe_irq(i915, pipe, GEN9_PIPE_PLANE_FLIP_DONE(plane->id));
|
||||
spin_unlock_irq(&i915->irq_lock);
|
||||
}
|
||||
|
||||
static void i9xx_plane_linear_gamma(u16 gamma[8])
|
||||
{
|
||||
/* The points are not evenly spaced. */
|
||||
@ -2366,7 +2411,8 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
|
||||
fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
|
||||
fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS ||
|
||||
fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS ||
|
||||
fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS)) {
|
||||
fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS ||
|
||||
fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC)) {
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"Y/Yf tiling not supported in IF-ID mode\n");
|
||||
return -EINVAL;
|
||||
@ -2856,6 +2902,7 @@ static const u64 skl_plane_format_modifiers_ccs[] = {
|
||||
static const u64 gen12_plane_format_modifiers_mc_ccs[] = {
|
||||
I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS,
|
||||
I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS,
|
||||
I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC,
|
||||
I915_FORMAT_MOD_Y_TILED,
|
||||
I915_FORMAT_MOD_X_TILED,
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
@ -2864,6 +2911,7 @@ static const u64 gen12_plane_format_modifiers_mc_ccs[] = {
|
||||
|
||||
static const u64 gen12_plane_format_modifiers_rc_ccs[] = {
|
||||
I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS,
|
||||
I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC,
|
||||
I915_FORMAT_MOD_Y_TILED,
|
||||
I915_FORMAT_MOD_X_TILED,
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
@ -3054,6 +3102,7 @@ static bool gen12_plane_format_mod_supported(struct drm_plane *_plane,
|
||||
case I915_FORMAT_MOD_X_TILED:
|
||||
case I915_FORMAT_MOD_Y_TILED:
|
||||
case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS:
|
||||
case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@ -3290,7 +3339,13 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
|
||||
plane->get_hw_state = skl_plane_get_hw_state;
|
||||
plane->check_plane = skl_plane_check;
|
||||
plane->min_cdclk = skl_plane_min_cdclk;
|
||||
plane->async_flip = skl_plane_async_flip;
|
||||
|
||||
if (plane_id == PLANE_PRIMARY) {
|
||||
plane->need_async_flip_disable_wa = IS_GEN_RANGE(dev_priv, 9, 10);
|
||||
plane->async_flip = skl_plane_async_flip;
|
||||
plane->enable_flip_done = skl_plane_enable_flip_done;
|
||||
plane->disable_flip_done = skl_plane_disable_flip_done;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 11)
|
||||
formats = icl_get_plane_formats(dev_priv, pipe,
|
||||
|
209
drivers/gpu/drm/i915/display/intel_vrr.c
Normal file
209
drivers/gpu/drm/i915/display/intel_vrr.c
Normal file
@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2020 Intel Corporation
|
||||
*
|
||||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_vrr.h"
|
||||
|
||||
bool intel_vrr_is_capable(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_dp *intel_dp;
|
||||
const struct drm_display_info *info = &connector->display_info;
|
||||
struct drm_i915_private *i915 = to_i915(connector->dev);
|
||||
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
|
||||
connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
|
||||
return false;
|
||||
|
||||
intel_dp = intel_attached_dp(to_intel_connector(connector));
|
||||
/*
|
||||
* DP Sink is capable of VRR video timings if
|
||||
* Ignore MSA bit is set in DPCD.
|
||||
* EDID monitor range also should be atleast 10 for reasonable
|
||||
* Adaptive Sync or Variable Refresh Rate end user experience.
|
||||
*/
|
||||
return HAS_VRR(i915) &&
|
||||
drm_dp_sink_can_do_video_without_timing_msa(intel_dp->dpcd) &&
|
||||
info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10;
|
||||
}
|
||||
|
||||
void
|
||||
intel_vrr_check_modeset(struct intel_atomic_state *state)
|
||||
{
|
||||
int i;
|
||||
struct intel_crtc_state *old_crtc_state, *new_crtc_state;
|
||||
struct intel_crtc *crtc;
|
||||
|
||||
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
|
||||
new_crtc_state, i) {
|
||||
if (new_crtc_state->uapi.vrr_enabled !=
|
||||
old_crtc_state->uapi.vrr_enabled)
|
||||
new_crtc_state->uapi.mode_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Without VRR registers get latched at:
|
||||
* vblank_start
|
||||
*
|
||||
* With VRR the earliest registers can get latched is:
|
||||
* intel_vrr_vmin_vblank_start(), which if we want to maintain
|
||||
* the correct min vtotal is >=vblank_start+1
|
||||
*
|
||||
* The latest point registers can get latched is the vmax decision boundary:
|
||||
* intel_vrr_vmax_vblank_start()
|
||||
*
|
||||
* Between those two points the vblank exit starts (and hence registers get
|
||||
* latched) ASAP after a push is sent.
|
||||
*
|
||||
* framestart_delay is programmable 0-3.
|
||||
*/
|
||||
static int intel_vrr_vblank_exit_length(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
|
||||
|
||||
/* The hw imposes the extra scanline before frame start */
|
||||
return crtc_state->vrr.pipeline_full + i915->framestart_delay + 1;
|
||||
}
|
||||
|
||||
int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
/* Min vblank actually determined by flipline that is always >=vmin+1 */
|
||||
return crtc_state->vrr.vmin + 1 - intel_vrr_vblank_exit_length(crtc_state);
|
||||
}
|
||||
|
||||
int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
return crtc_state->vrr.vmax - intel_vrr_vblank_exit_length(crtc_state);
|
||||
}
|
||||
|
||||
void
|
||||
intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct intel_connector *connector =
|
||||
to_intel_connector(conn_state->connector);
|
||||
struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
|
||||
const struct drm_display_info *info = &connector->base.display_info;
|
||||
int vmin, vmax;
|
||||
|
||||
if (!intel_vrr_is_capable(&connector->base))
|
||||
return;
|
||||
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return;
|
||||
|
||||
if (!crtc_state->uapi.vrr_enabled)
|
||||
return;
|
||||
|
||||
vmin = DIV_ROUND_UP(adjusted_mode->crtc_clock * 1000,
|
||||
adjusted_mode->crtc_htotal * info->monitor_range.max_vfreq);
|
||||
vmax = adjusted_mode->crtc_clock * 1000 /
|
||||
(adjusted_mode->crtc_htotal * info->monitor_range.min_vfreq);
|
||||
|
||||
vmin = max_t(int, vmin, adjusted_mode->crtc_vtotal);
|
||||
vmax = max_t(int, vmax, adjusted_mode->crtc_vtotal);
|
||||
|
||||
if (vmin >= vmax)
|
||||
return;
|
||||
|
||||
/*
|
||||
* flipline determines the min vblank length the hardware will
|
||||
* generate, and flipline>=vmin+1, hence we reduce vmin by one
|
||||
* to make sure we can get the actual min vblank length.
|
||||
*/
|
||||
crtc_state->vrr.vmin = vmin - 1;
|
||||
crtc_state->vrr.vmax = vmax;
|
||||
crtc_state->vrr.enable = true;
|
||||
|
||||
crtc_state->vrr.flipline = crtc_state->vrr.vmin + 1;
|
||||
|
||||
/*
|
||||
* FIXME: s/4/framestart_delay+1/ to get consistent
|
||||
* earliest/latest points for register latching regardless
|
||||
* of the framestart_delay used?
|
||||
*
|
||||
* FIXME: this really needs the extra scanline to provide consistent
|
||||
* behaviour for all framestart_delay values. Otherwise with
|
||||
* framestart_delay==3 we will end up extending the min vblank by
|
||||
* one extra line.
|
||||
*/
|
||||
crtc_state->vrr.pipeline_full =
|
||||
min(255, crtc_state->vrr.vmin - adjusted_mode->crtc_vdisplay - 4 - 1);
|
||||
|
||||
crtc_state->mode_flags |= I915_MODE_FLAG_VRR;
|
||||
}
|
||||
|
||||
void intel_vrr_enable(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
|
||||
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
|
||||
u32 trans_vrr_ctl;
|
||||
|
||||
if (!crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
trans_vrr_ctl = VRR_CTL_VRR_ENABLE |
|
||||
VRR_CTL_IGN_MAX_SHIFT | VRR_CTL_FLIP_LINE_EN |
|
||||
VRR_CTL_PIPELINE_FULL(crtc_state->vrr.pipeline_full) |
|
||||
VRR_CTL_PIPELINE_FULL_OVERRIDE;
|
||||
|
||||
intel_de_write(dev_priv, TRANS_VRR_VMIN(cpu_transcoder), crtc_state->vrr.vmin - 1);
|
||||
intel_de_write(dev_priv, TRANS_VRR_VMAX(cpu_transcoder), crtc_state->vrr.vmax - 1);
|
||||
intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), trans_vrr_ctl);
|
||||
intel_de_write(dev_priv, TRANS_VRR_FLIPLINE(cpu_transcoder), crtc_state->vrr.flipline - 1);
|
||||
intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder), TRANS_PUSH_EN);
|
||||
}
|
||||
|
||||
void intel_vrr_send_push(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
|
||||
|
||||
if (!crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder),
|
||||
TRANS_PUSH_EN | TRANS_PUSH_SEND);
|
||||
}
|
||||
|
||||
void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder;
|
||||
|
||||
if (!old_crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), 0);
|
||||
intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder), 0);
|
||||
}
|
||||
|
||||
void intel_vrr_get_config(struct intel_crtc *crtc,
|
||||
struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
|
||||
u32 trans_vrr_ctl;
|
||||
|
||||
trans_vrr_ctl = intel_de_read(dev_priv, TRANS_VRR_CTL(cpu_transcoder));
|
||||
crtc_state->vrr.enable = trans_vrr_ctl & VRR_CTL_VRR_ENABLE;
|
||||
if (!crtc_state->vrr.enable)
|
||||
return;
|
||||
|
||||
if (trans_vrr_ctl & VRR_CTL_PIPELINE_FULL_OVERRIDE)
|
||||
crtc_state->vrr.pipeline_full = REG_FIELD_GET(VRR_CTL_PIPELINE_FULL_MASK, trans_vrr_ctl);
|
||||
if (trans_vrr_ctl & VRR_CTL_FLIP_LINE_EN)
|
||||
crtc_state->vrr.flipline = intel_de_read(dev_priv, TRANS_VRR_FLIPLINE(cpu_transcoder)) + 1;
|
||||
crtc_state->vrr.vmax = intel_de_read(dev_priv, TRANS_VRR_VMAX(cpu_transcoder)) + 1;
|
||||
crtc_state->vrr.vmin = intel_de_read(dev_priv, TRANS_VRR_VMIN(cpu_transcoder)) + 1;
|
||||
|
||||
crtc_state->mode_flags |= I915_MODE_FLAG_VRR;
|
||||
}
|
33
drivers/gpu/drm/i915/display/intel_vrr.h
Normal file
33
drivers/gpu/drm/i915/display/intel_vrr.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2019 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_VRR_H__
|
||||
#define __INTEL_VRR_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_connector;
|
||||
struct drm_connector_state;
|
||||
struct intel_atomic_state;
|
||||
struct intel_crtc;
|
||||
struct intel_crtc_state;
|
||||
struct intel_dp;
|
||||
struct intel_encoder;
|
||||
struct intel_crtc;
|
||||
|
||||
bool intel_vrr_is_capable(struct drm_connector *connector);
|
||||
void intel_vrr_check_modeset(struct intel_atomic_state *state);
|
||||
void intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state);
|
||||
void intel_vrr_enable(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_vrr_send_push(const struct intel_crtc_state *crtc_state);
|
||||
void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state);
|
||||
void intel_vrr_get_config(struct intel_crtc *crtc,
|
||||
struct intel_crtc_state *crtc_state);
|
||||
int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state);
|
||||
int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state);
|
||||
|
||||
#endif /* __INTEL_VRR_H__ */
|
@ -31,6 +31,7 @@
|
||||
#include "i915_gem_mman.h"
|
||||
#include "i915_gem_object.h"
|
||||
#include "i915_globals.h"
|
||||
#include "i915_memcpy.h"
|
||||
#include "i915_trace.h"
|
||||
|
||||
static struct i915_global_object {
|
||||
@ -336,6 +337,70 @@ void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
i915_gem_object_read_from_page_kmap(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
|
||||
{
|
||||
void *src_map;
|
||||
void *src_ptr;
|
||||
|
||||
src_map = kmap_atomic(i915_gem_object_get_page(obj, offset >> PAGE_SHIFT));
|
||||
|
||||
src_ptr = src_map + offset_in_page(offset);
|
||||
if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ))
|
||||
drm_clflush_virt_range(src_ptr, size);
|
||||
memcpy(dst, src_ptr, size);
|
||||
|
||||
kunmap_atomic(src_map);
|
||||
}
|
||||
|
||||
static void
|
||||
i915_gem_object_read_from_page_iomap(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
|
||||
{
|
||||
void __iomem *src_map;
|
||||
void __iomem *src_ptr;
|
||||
dma_addr_t dma = i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT);
|
||||
|
||||
src_map = io_mapping_map_wc(&obj->mm.region->iomap,
|
||||
dma - obj->mm.region->region.start,
|
||||
PAGE_SIZE);
|
||||
|
||||
src_ptr = src_map + offset_in_page(offset);
|
||||
if (!i915_memcpy_from_wc(dst, (void __force *)src_ptr, size))
|
||||
memcpy_fromio(dst, src_ptr, size);
|
||||
|
||||
io_mapping_unmap(src_map);
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_gem_object_read_from_page - read data from the page of a GEM object
|
||||
* @obj: GEM object to read from
|
||||
* @offset: offset within the object
|
||||
* @dst: buffer to store the read data
|
||||
* @size: size to read
|
||||
*
|
||||
* Reads data from @obj at the specified offset. The requested region to read
|
||||
* from can't cross a page boundary. The caller must ensure that @obj pages
|
||||
* are pinned and that @obj is synced wrt. any related writes.
|
||||
*
|
||||
* Returns 0 on success or -ENODEV if the type of @obj's backing store is
|
||||
* unsupported.
|
||||
*/
|
||||
int i915_gem_object_read_from_page(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
|
||||
{
|
||||
GEM_BUG_ON(offset >= obj->base.size);
|
||||
GEM_BUG_ON(offset_in_page(offset) > PAGE_SIZE - size);
|
||||
GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));
|
||||
|
||||
if (i915_gem_object_has_struct_page(obj))
|
||||
i915_gem_object_read_from_page_kmap(obj, offset, dst, size);
|
||||
else if (i915_gem_object_has_iomem(obj))
|
||||
i915_gem_object_read_from_page_iomap(obj, offset, dst, size);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i915_gem_init__objects(struct drm_i915_private *i915)
|
||||
{
|
||||
INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
|
||||
|
@ -218,6 +218,12 @@ i915_gem_object_has_struct_page(const struct drm_i915_gem_object *obj)
|
||||
return i915_gem_object_type_has(obj, I915_GEM_OBJECT_HAS_STRUCT_PAGE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
i915_gem_object_has_iomem(const struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return i915_gem_object_type_has(obj, I915_GEM_OBJECT_HAS_IOMEM);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
i915_gem_object_is_shrinkable(const struct drm_i915_gem_object *obj)
|
||||
{
|
||||
@ -548,6 +554,8 @@ i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
|
||||
__i915_gem_object_invalidate_frontbuffer(obj, origin);
|
||||
}
|
||||
|
||||
int i915_gem_object_read_from_page(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size);
|
||||
|
||||
bool i915_gem_object_is_shmem(const struct drm_i915_gem_object *obj);
|
||||
|
||||
#endif
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "display/intel_hotplug.h"
|
||||
#include "display/intel_overlay.h"
|
||||
#include "display/intel_pipe_crc.h"
|
||||
#include "display/intel_pps.h"
|
||||
#include "display/intel_sprite.h"
|
||||
#include "display/intel_vga.h"
|
||||
|
||||
|
@ -1122,15 +1122,6 @@ struct drm_i915_private {
|
||||
* crtc_state->wm.need_postvbl_update.
|
||||
*/
|
||||
struct mutex wm_mutex;
|
||||
|
||||
/*
|
||||
* Set during HW readout of watermarks/DDB. Some platforms
|
||||
* need to know when we're still using BIOS-provided values
|
||||
* (which we don't fully trust).
|
||||
*
|
||||
* FIXME get rid of this.
|
||||
*/
|
||||
bool distrust_bios_wm;
|
||||
} wm;
|
||||
|
||||
struct dram_info {
|
||||
@ -1182,6 +1173,8 @@ struct drm_i915_private {
|
||||
struct file *mmap_singleton;
|
||||
} gem;
|
||||
|
||||
u8 framestart_delay;
|
||||
|
||||
u8 pch_ssc_use;
|
||||
|
||||
/* For i915gm/i945gm vblank irq workaround */
|
||||
@ -1758,10 +1751,17 @@ tgl_revids_get(struct drm_i915_private *dev_priv)
|
||||
|
||||
#define HAS_DISPLAY(dev_priv) (INTEL_INFO(dev_priv)->pipe_mask != 0)
|
||||
|
||||
#define HAS_VRR(i915) (INTEL_GEN(i915) >= 12)
|
||||
|
||||
/* Only valid when HAS_DISPLAY() is true */
|
||||
#define INTEL_DISPLAY_ENABLED(dev_priv) \
|
||||
(drm_WARN_ON(&(dev_priv)->drm, !HAS_DISPLAY(dev_priv)), !(dev_priv)->params.disable_display)
|
||||
|
||||
static inline bool run_as_guest(void)
|
||||
{
|
||||
return !hypervisor_is_type(X86_HYPER_NATIVE);
|
||||
}
|
||||
|
||||
static inline bool intel_vtd_active(void)
|
||||
{
|
||||
#ifdef CONFIG_INTEL_IOMMU
|
||||
@ -1770,7 +1770,7 @@ static inline bool intel_vtd_active(void)
|
||||
#endif
|
||||
|
||||
/* Running as a guest, we assume the host is enforcing VT'd */
|
||||
return !hypervisor_is_type(X86_HYPER_NATIVE);
|
||||
return run_as_guest();
|
||||
}
|
||||
|
||||
static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv)
|
||||
|
@ -718,25 +718,15 @@ u32 g4x_get_vblank_counter(struct drm_crtc *crtc)
|
||||
return intel_uncore_read(&dev_priv->uncore, PIPE_FRMCOUNT_G4X(pipe));
|
||||
}
|
||||
|
||||
/*
|
||||
* On certain encoders on certain platforms, pipe
|
||||
* scanline register will not work to get the scanline,
|
||||
* since the timings are driven from the PORT or issues
|
||||
* with scanline register updates.
|
||||
* This function will use Framestamp and current
|
||||
* timestamp registers to calculate the scanline.
|
||||
*/
|
||||
static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc)
|
||||
static u32 intel_crtc_scanlines_since_frame_timestamp(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
struct drm_vblank_crtc *vblank =
|
||||
&crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
|
||||
const struct drm_display_mode *mode = &vblank->hwmode;
|
||||
u32 vblank_start = mode->crtc_vblank_start;
|
||||
u32 vtotal = mode->crtc_vtotal;
|
||||
u32 htotal = mode->crtc_htotal;
|
||||
u32 clock = mode->crtc_clock;
|
||||
u32 scanline, scan_prev_time, scan_curr_time, scan_post_time;
|
||||
u32 scan_prev_time, scan_curr_time, scan_post_time;
|
||||
|
||||
/*
|
||||
* To avoid the race condition where we might cross into the
|
||||
@ -763,8 +753,28 @@ static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc)
|
||||
PIPE_FRMTMSTMP(crtc->pipe));
|
||||
} while (scan_post_time != scan_prev_time);
|
||||
|
||||
scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time,
|
||||
clock), 1000 * htotal);
|
||||
return div_u64(mul_u32_u32(scan_curr_time - scan_prev_time,
|
||||
clock), 1000 * htotal);
|
||||
}
|
||||
|
||||
/*
|
||||
* On certain encoders on certain platforms, pipe
|
||||
* scanline register will not work to get the scanline,
|
||||
* since the timings are driven from the PORT or issues
|
||||
* with scanline register updates.
|
||||
* This function will use Framestamp and current
|
||||
* timestamp registers to calculate the scanline.
|
||||
*/
|
||||
static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank =
|
||||
&crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
|
||||
const struct drm_display_mode *mode = &vblank->hwmode;
|
||||
u32 vblank_start = mode->crtc_vblank_start;
|
||||
u32 vtotal = mode->crtc_vtotal;
|
||||
u32 scanline;
|
||||
|
||||
scanline = intel_crtc_scanlines_since_frame_timestamp(crtc);
|
||||
scanline = min(scanline, vtotal - 1);
|
||||
scanline = (scanline + vblank_start) % vtotal;
|
||||
|
||||
@ -883,7 +893,20 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc,
|
||||
if (stime)
|
||||
*stime = ktime_get();
|
||||
|
||||
if (use_scanline_counter) {
|
||||
if (crtc->mode_flags & I915_MODE_FLAG_VRR) {
|
||||
int scanlines = intel_crtc_scanlines_since_frame_timestamp(crtc);
|
||||
|
||||
position = __intel_get_crtc_scanline(crtc);
|
||||
|
||||
/*
|
||||
* Already exiting vblank? If so, shift our position
|
||||
* so it looks like we're already apporaching the full
|
||||
* vblank end. This should make the generated timestamp
|
||||
* more or less match when the active portion will start.
|
||||
*/
|
||||
if (position >= vbl_start && scanlines < position)
|
||||
position = min(crtc->vmax_vblank_start + scanlines, vtotal - 1);
|
||||
} else if (use_scanline_counter) {
|
||||
/* No obvious pixelcount register. Only query vertical
|
||||
* scanout position from Display scan line register.
|
||||
*/
|
||||
@ -2079,7 +2102,7 @@ static void ivb_display_irq_handler(struct drm_i915_private *dev_priv,
|
||||
intel_opregion_asle_intr(dev_priv);
|
||||
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
|
||||
if (de_iir & DE_PIPE_VBLANK_IVB(pipe))
|
||||
intel_handle_vblank(dev_priv, pipe);
|
||||
}
|
||||
|
||||
@ -2822,19 +2845,6 @@ int bdw_enable_vblank(struct drm_crtc *crtc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void skl_enable_flip_done(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&i915->irq_lock, irqflags);
|
||||
|
||||
bdw_enable_pipe_irq(i915, pipe, GEN9_PIPE_PLANE1_FLIP_DONE);
|
||||
|
||||
spin_unlock_irqrestore(&i915->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
/* Called from drm generic code, passed 'crtc' which
|
||||
* we use as a pipe index
|
||||
*/
|
||||
@ -2899,19 +2909,6 @@ void bdw_disable_vblank(struct drm_crtc *crtc)
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
void skl_disable_flip_done(struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&i915->irq_lock, irqflags);
|
||||
|
||||
bdw_disable_pipe_irq(i915, pipe, GEN9_PIPE_PLANE1_FLIP_DONE);
|
||||
|
||||
spin_unlock_irqrestore(&i915->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
static void ibx_irq_reset(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct intel_uncore *uncore = &dev_priv->uncore;
|
||||
|
@ -118,9 +118,6 @@ void i965_disable_vblank(struct drm_crtc *crtc);
|
||||
void ilk_disable_vblank(struct drm_crtc *crtc);
|
||||
void bdw_disable_vblank(struct drm_crtc *crtc);
|
||||
|
||||
void skl_enable_flip_done(struct intel_crtc *crtc);
|
||||
void skl_disable_flip_done(struct intel_crtc *crtc);
|
||||
|
||||
void gen2_irq_reset(struct intel_uncore *uncore);
|
||||
void gen3_irq_reset(struct intel_uncore *uncore, i915_reg_t imr,
|
||||
i915_reg_t iir, i915_reg_t ier);
|
||||
|
@ -185,7 +185,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400,
|
||||
|
||||
i915_param_named(enable_dpcd_backlight, int, 0400,
|
||||
"Enable support for DPCD backlight control"
|
||||
"(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)");
|
||||
"(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enable, 2=force VESA interface, 3=force Intel interface)");
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_I915_GVT)
|
||||
i915_param_named(enable_gvt, bool, 0400,
|
||||
|
@ -4346,13 +4346,13 @@ enum {
|
||||
#define _TRANS_VRR_CTL_B 0x61420
|
||||
#define _TRANS_VRR_CTL_C 0x62420
|
||||
#define _TRANS_VRR_CTL_D 0x63420
|
||||
#define TRANS_VRR_CTL(trans) _MMIO_TRANS2(trans, _TRANS_VRR_CTL_A)
|
||||
#define VRR_CTL_VRR_ENABLE REG_BIT(31)
|
||||
#define VRR_CTL_IGN_MAX_SHIFT REG_BIT(30)
|
||||
#define VRR_CTL_FLIP_LINE_EN REG_BIT(29)
|
||||
#define VRR_CTL_LINE_COUNT_MASK REG_GENMASK(10, 3)
|
||||
#define VRR_CTL_LINE_COUNT(x) REG_FIELD_PREP(VRR_CTL_LINE_COUNT_MASK, (x))
|
||||
#define VRR_CTL_SW_FULLLINE_COUNT REG_BIT(0)
|
||||
#define TRANS_VRR_CTL(trans) _MMIO_TRANS2(trans, _TRANS_VRR_CTL_A)
|
||||
#define VRR_CTL_VRR_ENABLE REG_BIT(31)
|
||||
#define VRR_CTL_IGN_MAX_SHIFT REG_BIT(30)
|
||||
#define VRR_CTL_FLIP_LINE_EN REG_BIT(29)
|
||||
#define VRR_CTL_PIPELINE_FULL_MASK REG_GENMASK(10, 3)
|
||||
#define VRR_CTL_PIPELINE_FULL(x) REG_FIELD_PREP(VRR_CTL_PIPELINE_FULL_MASK, (x))
|
||||
#define VRR_CTL_PIPELINE_FULL_OVERRIDE REG_BIT(0)
|
||||
|
||||
#define _TRANS_VRR_VMAX_A 0x60424
|
||||
#define _TRANS_VRR_VMAX_B 0x61424
|
||||
@ -7070,6 +7070,8 @@ enum {
|
||||
#define _PLANE_KEYMAX_1_A 0x701a0
|
||||
#define _PLANE_KEYMAX_2_A 0x702a0
|
||||
#define PLANE_KEYMAX_ALPHA(a) ((a) << 24)
|
||||
#define _PLANE_CC_VAL_1_A 0x701b4
|
||||
#define _PLANE_CC_VAL_2_A 0x702b4
|
||||
#define _PLANE_AUX_DIST_1_A 0x701c0
|
||||
#define _PLANE_AUX_DIST_2_A 0x702c0
|
||||
#define _PLANE_AUX_OFFSET_1_A 0x701c4
|
||||
@ -7111,6 +7113,13 @@ enum {
|
||||
#define _PLANE_NV12_BUF_CFG_1_A 0x70278
|
||||
#define _PLANE_NV12_BUF_CFG_2_A 0x70378
|
||||
|
||||
#define _PLANE_CC_VAL_1_B 0x711b4
|
||||
#define _PLANE_CC_VAL_2_B 0x712b4
|
||||
#define _PLANE_CC_VAL_1(pipe) _PIPE(pipe, _PLANE_CC_VAL_1_A, _PLANE_CC_VAL_1_B)
|
||||
#define _PLANE_CC_VAL_2(pipe) _PIPE(pipe, _PLANE_CC_VAL_2_A, _PLANE_CC_VAL_2_B)
|
||||
#define PLANE_CC_VAL(pipe, plane) \
|
||||
_MMIO_PLANE(plane, _PLANE_CC_VAL_1(pipe), _PLANE_CC_VAL_2(pipe))
|
||||
|
||||
/* Input CSC Register Definitions */
|
||||
#define _PLANE_INPUT_CSC_RY_GY_1_A 0x701E0
|
||||
#define _PLANE_INPUT_CSC_RY_GY_2_A 0x702E0
|
||||
@ -9872,6 +9881,7 @@ enum skl_power_gate {
|
||||
_PORTD_HDCP2_BASE, \
|
||||
_PORTE_HDCP2_BASE, \
|
||||
_PORTF_HDCP2_BASE) + (x))
|
||||
|
||||
#define PORT_HDCP2_AUTH(port) _PORT_HDCP2_BASE(port, 0x98)
|
||||
#define _TRANSA_HDCP2_AUTH 0x66498
|
||||
#define _TRANSB_HDCP2_AUTH 0x66598
|
||||
@ -9911,6 +9921,44 @@ enum skl_power_gate {
|
||||
TRANS_HDCP2_STATUS(trans) : \
|
||||
PORT_HDCP2_STATUS(port))
|
||||
|
||||
#define _PIPEA_HDCP2_STREAM_STATUS 0x668C0
|
||||
#define _PIPEB_HDCP2_STREAM_STATUS 0x665C0
|
||||
#define _PIPEC_HDCP2_STREAM_STATUS 0x666C0
|
||||
#define _PIPED_HDCP2_STREAM_STATUS 0x667C0
|
||||
#define PIPE_HDCP2_STREAM_STATUS(pipe) _MMIO(_PICK((pipe), \
|
||||
_PIPEA_HDCP2_STREAM_STATUS, \
|
||||
_PIPEB_HDCP2_STREAM_STATUS, \
|
||||
_PIPEC_HDCP2_STREAM_STATUS, \
|
||||
_PIPED_HDCP2_STREAM_STATUS))
|
||||
|
||||
#define _TRANSA_HDCP2_STREAM_STATUS 0x664C0
|
||||
#define _TRANSB_HDCP2_STREAM_STATUS 0x665C0
|
||||
#define TRANS_HDCP2_STREAM_STATUS(trans) _MMIO_TRANS(trans, \
|
||||
_TRANSA_HDCP2_STREAM_STATUS, \
|
||||
_TRANSB_HDCP2_STREAM_STATUS)
|
||||
#define STREAM_ENCRYPTION_STATUS BIT(31)
|
||||
#define STREAM_TYPE_STATUS BIT(30)
|
||||
#define HDCP2_STREAM_STATUS(dev_priv, trans, port) \
|
||||
(INTEL_GEN(dev_priv) >= 12 ? \
|
||||
TRANS_HDCP2_STREAM_STATUS(trans) : \
|
||||
PIPE_HDCP2_STREAM_STATUS(pipe))
|
||||
|
||||
#define _PORTA_HDCP2_AUTH_STREAM 0x66F00
|
||||
#define _PORTB_HDCP2_AUTH_STREAM 0x66F04
|
||||
#define PORT_HDCP2_AUTH_STREAM(port) _MMIO_PORT(port, \
|
||||
_PORTA_HDCP2_AUTH_STREAM, \
|
||||
_PORTB_HDCP2_AUTH_STREAM)
|
||||
#define _TRANSA_HDCP2_AUTH_STREAM 0x66F00
|
||||
#define _TRANSB_HDCP2_AUTH_STREAM 0x66F04
|
||||
#define TRANS_HDCP2_AUTH_STREAM(trans) _MMIO_TRANS(trans, \
|
||||
_TRANSA_HDCP2_AUTH_STREAM, \
|
||||
_TRANSB_HDCP2_AUTH_STREAM)
|
||||
#define AUTH_STREAM_TYPE BIT(31)
|
||||
#define HDCP2_AUTH_STREAM(dev_priv, trans, port) \
|
||||
(INTEL_GEN(dev_priv) >= 12 ? \
|
||||
TRANS_HDCP2_AUTH_STREAM(trans) : \
|
||||
PORT_HDCP2_AUTH_STREAM(port))
|
||||
|
||||
/* Per-pipe DDI Function Control */
|
||||
#define _TRANS_DDI_FUNC_CTL_A 0x60400
|
||||
#define _TRANS_DDI_FUNC_CTL_B 0x61400
|
||||
@ -9960,6 +10008,7 @@ enum skl_power_gate {
|
||||
#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1 << 8)
|
||||
#define TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1 << 7)
|
||||
#define TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1 << 6)
|
||||
#define TRANS_DDI_HDCP_SELECT REG_BIT(5)
|
||||
#define TRANS_DDI_BFI_ENABLE (1 << 4)
|
||||
#define TRANS_DDI_HIGH_TMDS_CHAR_RATE (1 << 4)
|
||||
#define TRANS_DDI_HDMI_SCRAMBLING (1 << 0)
|
||||
|
@ -143,8 +143,9 @@ static bool intel_is_virt_pch(unsigned short id,
|
||||
sdevice == PCI_SUBDEVICE_ID_QEMU));
|
||||
}
|
||||
|
||||
static unsigned short
|
||||
intel_virt_detect_pch(const struct drm_i915_private *dev_priv)
|
||||
static void
|
||||
intel_virt_detect_pch(const struct drm_i915_private *dev_priv,
|
||||
unsigned short *pch_id, enum intel_pch *pch_type)
|
||||
{
|
||||
unsigned short id = 0;
|
||||
|
||||
@ -181,12 +182,21 @@ intel_virt_detect_pch(const struct drm_i915_private *dev_priv)
|
||||
else
|
||||
drm_dbg_kms(&dev_priv->drm, "Assuming no PCH\n");
|
||||
|
||||
return id;
|
||||
*pch_type = intel_pch_type(dev_priv, id);
|
||||
|
||||
/* Sanity check virtual PCH id */
|
||||
if (drm_WARN_ON(&dev_priv->drm,
|
||||
id && *pch_type == PCH_NONE))
|
||||
id = 0;
|
||||
|
||||
*pch_id = id;
|
||||
}
|
||||
|
||||
void intel_detect_pch(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct pci_dev *pch = NULL;
|
||||
unsigned short id;
|
||||
enum intel_pch pch_type;
|
||||
|
||||
/* DG1 has south engine display on the same PCI device */
|
||||
if (IS_DG1(dev_priv)) {
|
||||
@ -206,9 +216,6 @@ void intel_detect_pch(struct drm_i915_private *dev_priv)
|
||||
* of only checking the first one.
|
||||
*/
|
||||
while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) {
|
||||
unsigned short id;
|
||||
enum intel_pch pch_type;
|
||||
|
||||
if (pch->vendor != PCI_VENDOR_ID_INTEL)
|
||||
continue;
|
||||
|
||||
@ -221,14 +228,7 @@ void intel_detect_pch(struct drm_i915_private *dev_priv)
|
||||
break;
|
||||
} else if (intel_is_virt_pch(id, pch->subsystem_vendor,
|
||||
pch->subsystem_device)) {
|
||||
id = intel_virt_detect_pch(dev_priv);
|
||||
pch_type = intel_pch_type(dev_priv, id);
|
||||
|
||||
/* Sanity check virtual PCH id */
|
||||
if (drm_WARN_ON(&dev_priv->drm,
|
||||
id && pch_type == PCH_NONE))
|
||||
id = 0;
|
||||
|
||||
intel_virt_detect_pch(dev_priv, &id, &pch_type);
|
||||
dev_priv->pch_type = pch_type;
|
||||
dev_priv->pch_id = id;
|
||||
break;
|
||||
@ -244,10 +244,15 @@ void intel_detect_pch(struct drm_i915_private *dev_priv)
|
||||
"Display disabled, reverting to NOP PCH\n");
|
||||
dev_priv->pch_type = PCH_NOP;
|
||||
dev_priv->pch_id = 0;
|
||||
} else if (!pch) {
|
||||
if (run_as_guest() && HAS_DISPLAY(dev_priv)) {
|
||||
intel_virt_detect_pch(dev_priv, &id, &pch_type);
|
||||
dev_priv->pch_type = pch_type;
|
||||
dev_priv->pch_id = id;
|
||||
} else {
|
||||
drm_dbg_kms(&dev_priv->drm, "No PCH found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!pch)
|
||||
drm_dbg_kms(&dev_priv->drm, "No PCH found.\n");
|
||||
|
||||
pci_dev_put(pch);
|
||||
}
|
||||
|
@ -4017,30 +4017,10 @@ static int intel_compute_sagv_mask(struct intel_atomic_state *state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate initial DBuf slice offset, based on slice size
|
||||
* and mask(i.e if slice size is 1024 and second slice is enabled
|
||||
* offset would be 1024)
|
||||
*/
|
||||
static unsigned int
|
||||
icl_get_first_dbuf_slice_offset(u32 dbuf_slice_mask,
|
||||
u32 slice_size,
|
||||
u32 ddb_size)
|
||||
static int intel_dbuf_size(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
int ddb_size = INTEL_INFO(dev_priv)->ddb_size;
|
||||
|
||||
if (!dbuf_slice_mask)
|
||||
return 0;
|
||||
|
||||
offset = (ffs(dbuf_slice_mask) - 1) * slice_size;
|
||||
|
||||
WARN_ON(offset >= ddb_size);
|
||||
return offset;
|
||||
}
|
||||
|
||||
u16 intel_get_ddb_size(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u16 ddb_size = INTEL_INFO(dev_priv)->ddb_size;
|
||||
drm_WARN_ON(&dev_priv->drm, ddb_size == 0);
|
||||
|
||||
if (INTEL_GEN(dev_priv) < 11)
|
||||
@ -4049,11 +4029,36 @@ u16 intel_get_ddb_size(struct drm_i915_private *dev_priv)
|
||||
return ddb_size;
|
||||
}
|
||||
|
||||
static int intel_dbuf_slice_size(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
return intel_dbuf_size(dev_priv) /
|
||||
INTEL_INFO(dev_priv)->num_supported_dbuf_slices;
|
||||
}
|
||||
|
||||
static void
|
||||
skl_ddb_entry_for_slices(struct drm_i915_private *dev_priv, u8 slice_mask,
|
||||
struct skl_ddb_entry *ddb)
|
||||
{
|
||||
int slice_size = intel_dbuf_slice_size(dev_priv);
|
||||
|
||||
if (!slice_mask) {
|
||||
ddb->start = 0;
|
||||
ddb->end = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ddb->start = (ffs(slice_mask) - 1) * slice_size;
|
||||
ddb->end = fls(slice_mask) * slice_size;
|
||||
|
||||
WARN_ON(ddb->start >= ddb->end);
|
||||
WARN_ON(ddb->end > intel_dbuf_size(dev_priv));
|
||||
}
|
||||
|
||||
u32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv,
|
||||
const struct skl_ddb_entry *entry)
|
||||
{
|
||||
u32 slice_mask = 0;
|
||||
u16 ddb_size = intel_get_ddb_size(dev_priv);
|
||||
u16 ddb_size = intel_dbuf_size(dev_priv);
|
||||
u16 num_supported_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices;
|
||||
u16 slice_size = ddb_size / num_supported_slices;
|
||||
u16 start_slice;
|
||||
@ -4077,116 +4082,40 @@ u32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv,
|
||||
return slice_mask;
|
||||
}
|
||||
|
||||
static u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state,
|
||||
u8 active_pipes);
|
||||
|
||||
static int
|
||||
skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
|
||||
const struct intel_crtc_state *crtc_state,
|
||||
const u64 total_data_rate,
|
||||
struct skl_ddb_entry *alloc, /* out */
|
||||
int *num_active /* out */)
|
||||
static unsigned int intel_crtc_ddb_weight(const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_atomic_state *state = crtc_state->uapi.state;
|
||||
struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
|
||||
struct drm_crtc *for_crtc = crtc_state->uapi.crtc;
|
||||
const struct intel_crtc *crtc;
|
||||
u32 pipe_width = 0, total_width_in_range = 0, width_before_pipe_in_range = 0;
|
||||
enum pipe for_pipe = to_intel_crtc(for_crtc)->pipe;
|
||||
struct intel_dbuf_state *new_dbuf_state =
|
||||
intel_atomic_get_new_dbuf_state(intel_state);
|
||||
const struct intel_dbuf_state *old_dbuf_state =
|
||||
intel_atomic_get_old_dbuf_state(intel_state);
|
||||
u8 active_pipes = new_dbuf_state->active_pipes;
|
||||
u16 ddb_size;
|
||||
u32 ddb_range_size;
|
||||
u32 i;
|
||||
u32 dbuf_slice_mask;
|
||||
u32 offset;
|
||||
u32 slice_size;
|
||||
u32 total_slice_mask;
|
||||
u32 start, end;
|
||||
int ret;
|
||||
const struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
|
||||
int hdisplay, vdisplay;
|
||||
|
||||
*num_active = hweight8(active_pipes);
|
||||
|
||||
if (!crtc_state->hw.active) {
|
||||
alloc->start = 0;
|
||||
alloc->end = 0;
|
||||
if (!crtc_state->hw.active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ddb_size = intel_get_ddb_size(dev_priv);
|
||||
|
||||
slice_size = ddb_size / INTEL_INFO(dev_priv)->num_supported_dbuf_slices;
|
||||
|
||||
/*
|
||||
* If the state doesn't change the active CRTC's or there is no
|
||||
* modeset request, then there's no need to recalculate;
|
||||
* the existing pipe allocation limits should remain unchanged.
|
||||
* Note that we're safe from racing commits since any racing commit
|
||||
* that changes the active CRTC list or do modeset would need to
|
||||
* grab _all_ crtc locks, including the one we currently hold.
|
||||
*/
|
||||
if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes &&
|
||||
!dev_priv->wm.distrust_bios_wm) {
|
||||
/*
|
||||
* alloc may be cleared by clear_intel_crtc_state,
|
||||
* copy from old state to be sure
|
||||
*
|
||||
* FIXME get rid of this mess
|
||||
*/
|
||||
*alloc = to_intel_crtc_state(for_crtc->state)->wm.skl.ddb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get allowed DBuf slices for correspondent pipe and platform.
|
||||
*/
|
||||
dbuf_slice_mask = skl_compute_dbuf_slices(crtc_state, active_pipes);
|
||||
|
||||
/*
|
||||
* Figure out at which DBuf slice we start, i.e if we start at Dbuf S2
|
||||
* and slice size is 1024, the offset would be 1024
|
||||
*/
|
||||
offset = icl_get_first_dbuf_slice_offset(dbuf_slice_mask,
|
||||
slice_size, ddb_size);
|
||||
|
||||
/*
|
||||
* Figure out total size of allowed DBuf slices, which is basically
|
||||
* a number of allowed slices for that pipe multiplied by slice size.
|
||||
* Inside of this
|
||||
* range ddb entries are still allocated in proportion to display width.
|
||||
*/
|
||||
ddb_range_size = hweight8(dbuf_slice_mask) * slice_size;
|
||||
|
||||
/*
|
||||
* Watermark/ddb requirement highly depends upon width of the
|
||||
* framebuffer, So instead of allocating DDB equally among pipes
|
||||
* distribute DDB based on resolution/width of the display.
|
||||
*/
|
||||
total_slice_mask = dbuf_slice_mask;
|
||||
for_each_new_intel_crtc_in_state(intel_state, crtc, crtc_state, i) {
|
||||
const struct drm_display_mode *pipe_mode =
|
||||
&crtc_state->hw.pipe_mode;
|
||||
enum pipe pipe = crtc->pipe;
|
||||
int hdisplay, vdisplay;
|
||||
u32 pipe_dbuf_slice_mask;
|
||||
drm_mode_get_hv_timing(pipe_mode, &hdisplay, &vdisplay);
|
||||
|
||||
if (!crtc_state->hw.active)
|
||||
continue;
|
||||
return hdisplay;
|
||||
}
|
||||
|
||||
pipe_dbuf_slice_mask = skl_compute_dbuf_slices(crtc_state,
|
||||
active_pipes);
|
||||
static void intel_crtc_dbuf_weights(const struct intel_dbuf_state *dbuf_state,
|
||||
enum pipe for_pipe,
|
||||
unsigned int *weight_start,
|
||||
unsigned int *weight_end,
|
||||
unsigned int *weight_total)
|
||||
{
|
||||
struct drm_i915_private *dev_priv =
|
||||
to_i915(dbuf_state->base.state->base.dev);
|
||||
enum pipe pipe;
|
||||
|
||||
/*
|
||||
* According to BSpec pipe can share one dbuf slice with another
|
||||
* pipes or pipe can use multiple dbufs, in both cases we
|
||||
* account for other pipes only if they have exactly same mask.
|
||||
* However we need to account how many slices we should enable
|
||||
* in total.
|
||||
*/
|
||||
total_slice_mask |= pipe_dbuf_slice_mask;
|
||||
*weight_start = 0;
|
||||
*weight_end = 0;
|
||||
*weight_total = 0;
|
||||
|
||||
for_each_pipe(dev_priv, pipe) {
|
||||
int weight = dbuf_state->weight[pipe];
|
||||
|
||||
/*
|
||||
* Do not account pipes using other slice sets
|
||||
@ -4195,42 +4124,78 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
|
||||
* i.e no partial intersection), so it is enough to check for
|
||||
* equality for now.
|
||||
*/
|
||||
if (dbuf_slice_mask != pipe_dbuf_slice_mask)
|
||||
if (dbuf_state->slices[pipe] != dbuf_state->slices[for_pipe])
|
||||
continue;
|
||||
|
||||
drm_mode_get_hv_timing(pipe_mode, &hdisplay, &vdisplay);
|
||||
*weight_total += weight;
|
||||
if (pipe < for_pipe) {
|
||||
*weight_start += weight;
|
||||
*weight_end += weight;
|
||||
} else if (pipe == for_pipe) {
|
||||
*weight_end += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total_width_in_range += hdisplay;
|
||||
static int
|
||||
skl_crtc_allocate_ddb(struct intel_atomic_state *state, struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
unsigned int weight_total, weight_start, weight_end;
|
||||
const struct intel_dbuf_state *old_dbuf_state =
|
||||
intel_atomic_get_old_dbuf_state(state);
|
||||
struct intel_dbuf_state *new_dbuf_state =
|
||||
intel_atomic_get_new_dbuf_state(state);
|
||||
struct intel_crtc_state *crtc_state;
|
||||
struct skl_ddb_entry ddb_slices;
|
||||
enum pipe pipe = crtc->pipe;
|
||||
u32 ddb_range_size;
|
||||
u32 dbuf_slice_mask;
|
||||
u32 start, end;
|
||||
int ret;
|
||||
|
||||
if (pipe < for_pipe)
|
||||
width_before_pipe_in_range += hdisplay;
|
||||
else if (pipe == for_pipe)
|
||||
pipe_width = hdisplay;
|
||||
if (new_dbuf_state->weight[pipe] == 0) {
|
||||
new_dbuf_state->ddb[pipe].start = 0;
|
||||
new_dbuf_state->ddb[pipe].end = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: For now we always enable slice S1 as per
|
||||
* the Bspec display initialization sequence.
|
||||
*/
|
||||
new_dbuf_state->enabled_slices = total_slice_mask | BIT(DBUF_S1);
|
||||
dbuf_slice_mask = new_dbuf_state->slices[pipe];
|
||||
|
||||
if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) {
|
||||
ret = intel_atomic_serialize_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
skl_ddb_entry_for_slices(dev_priv, dbuf_slice_mask, &ddb_slices);
|
||||
ddb_range_size = skl_ddb_entry_size(&ddb_slices);
|
||||
|
||||
start = ddb_range_size * width_before_pipe_in_range / total_width_in_range;
|
||||
end = ddb_range_size *
|
||||
(width_before_pipe_in_range + pipe_width) / total_width_in_range;
|
||||
intel_crtc_dbuf_weights(new_dbuf_state, pipe,
|
||||
&weight_start, &weight_end, &weight_total);
|
||||
|
||||
alloc->start = offset + start;
|
||||
alloc->end = offset + end;
|
||||
start = ddb_range_size * weight_start / weight_total;
|
||||
end = ddb_range_size * weight_end / weight_total;
|
||||
|
||||
new_dbuf_state->ddb[pipe].start = ddb_slices.start + start;
|
||||
new_dbuf_state->ddb[pipe].end = ddb_slices.start + end;
|
||||
|
||||
out:
|
||||
if (skl_ddb_entry_equal(&old_dbuf_state->ddb[pipe],
|
||||
&new_dbuf_state->ddb[pipe]))
|
||||
return 0;
|
||||
|
||||
ret = intel_atomic_lock_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
crtc_state->wm.skl.ddb = new_dbuf_state->ddb[pipe];
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"[CRTC:%d:%s] dbuf slices 0x%x, ddb (%d - %d), active pipes 0x%x\n",
|
||||
for_crtc->base.id, for_crtc->name,
|
||||
dbuf_slice_mask, alloc->start, alloc->end, active_pipes);
|
||||
"[CRTC:%d:%s] dbuf slices 0x%x -> 0x%x, ddb (%d - %d) -> (%d - %d), active pipes 0x%x -> 0x%x\n",
|
||||
crtc->base.base.id, crtc->base.name,
|
||||
old_dbuf_state->slices[pipe], new_dbuf_state->slices[pipe],
|
||||
old_dbuf_state->ddb[pipe].start, old_dbuf_state->ddb[pipe].end,
|
||||
new_dbuf_state->ddb[pipe].start, new_dbuf_state->ddb[pipe].end,
|
||||
old_dbuf_state->active_pipes, new_dbuf_state->active_pipes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4632,10 +4597,8 @@ static u8 tgl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes)
|
||||
return compute_dbuf_slices(pipe, active_pipes, tgl_allowed_dbufs);
|
||||
}
|
||||
|
||||
static u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state,
|
||||
u8 active_pipes)
|
||||
static u8 skl_compute_dbuf_slices(struct intel_crtc *crtc, u8 active_pipes)
|
||||
{
|
||||
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
|
||||
@ -4798,55 +4761,30 @@ skl_plane_wm_level(const struct intel_crtc_state *crtc_state,
|
||||
}
|
||||
|
||||
static int
|
||||
skl_allocate_pipe_ddb(struct intel_atomic_state *state,
|
||||
struct intel_crtc *crtc)
|
||||
skl_allocate_plane_ddb(struct intel_atomic_state *state,
|
||||
struct intel_crtc *crtc)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
struct intel_crtc_state *crtc_state =
|
||||
intel_atomic_get_new_crtc_state(state, crtc);
|
||||
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
|
||||
struct skl_ddb_entry *alloc = &crtc_state->wm.skl.ddb;
|
||||
const struct intel_dbuf_state *dbuf_state =
|
||||
intel_atomic_get_new_dbuf_state(state);
|
||||
const struct skl_ddb_entry *alloc = &dbuf_state->ddb[crtc->pipe];
|
||||
int num_active = hweight8(dbuf_state->active_pipes);
|
||||
u16 alloc_size, start = 0;
|
||||
u16 total[I915_MAX_PLANES] = {};
|
||||
u16 uv_total[I915_MAX_PLANES] = {};
|
||||
u64 total_data_rate;
|
||||
enum plane_id plane_id;
|
||||
int num_active;
|
||||
u32 blocks;
|
||||
int level;
|
||||
int ret;
|
||||
|
||||
/* Clear the partitioning for disabled planes. */
|
||||
memset(crtc_state->wm.skl.plane_ddb_y, 0, sizeof(crtc_state->wm.skl.plane_ddb_y));
|
||||
memset(crtc_state->wm.skl.plane_ddb_uv, 0, sizeof(crtc_state->wm.skl.plane_ddb_uv));
|
||||
|
||||
if (!crtc_state->hw.active) {
|
||||
struct intel_atomic_state *state =
|
||||
to_intel_atomic_state(crtc_state->uapi.state);
|
||||
struct intel_dbuf_state *new_dbuf_state =
|
||||
intel_atomic_get_new_dbuf_state(state);
|
||||
const struct intel_dbuf_state *old_dbuf_state =
|
||||
intel_atomic_get_old_dbuf_state(state);
|
||||
|
||||
/*
|
||||
* FIXME hack to make sure we compute this sensibly when
|
||||
* turning off all the pipes. Otherwise we leave it at
|
||||
* whatever we had previously, and then runtime PM will
|
||||
* mess it up by turning off all but S1. Remove this
|
||||
* once the dbuf state computation flow becomes sane.
|
||||
*/
|
||||
if (new_dbuf_state->active_pipes == 0) {
|
||||
new_dbuf_state->enabled_slices = BIT(DBUF_S1);
|
||||
|
||||
if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) {
|
||||
ret = intel_atomic_serialize_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
alloc->start = alloc->end = 0;
|
||||
if (!crtc_state->hw.active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (INTEL_GEN(dev_priv) >= 11)
|
||||
total_data_rate =
|
||||
@ -4855,12 +4793,6 @@ skl_allocate_pipe_ddb(struct intel_atomic_state *state,
|
||||
total_data_rate =
|
||||
skl_get_total_relative_data_rate(state, crtc);
|
||||
|
||||
ret = skl_ddb_get_pipe_allocation_limits(dev_priv, crtc_state,
|
||||
total_data_rate,
|
||||
alloc, &num_active);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alloc_size = skl_ddb_entry_size(alloc);
|
||||
if (alloc_size == 0)
|
||||
return 0;
|
||||
@ -5731,6 +5663,18 @@ static bool skl_ddb_entries_overlap(const struct skl_ddb_entry *a,
|
||||
return a->start < b->end && b->start < a->end;
|
||||
}
|
||||
|
||||
static void skl_ddb_entry_union(struct skl_ddb_entry *a,
|
||||
const struct skl_ddb_entry *b)
|
||||
{
|
||||
if (a->end && b->end) {
|
||||
a->start = min(a->start, b->start);
|
||||
a->end = max(a->end, b->end);
|
||||
} else if (b->end) {
|
||||
a->start = b->start;
|
||||
a->end = b->end;
|
||||
}
|
||||
}
|
||||
|
||||
bool skl_ddb_allocation_overlaps(const struct skl_ddb_entry *ddb,
|
||||
const struct skl_ddb_entry *entries,
|
||||
int num_entries, int ignore_idx)
|
||||
@ -5775,20 +5719,106 @@ skl_ddb_add_affected_planes(const struct intel_crtc_state *old_crtc_state,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 intel_dbuf_enabled_slices(const struct intel_dbuf_state *dbuf_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(dbuf_state->base.state->base.dev);
|
||||
u8 enabled_slices;
|
||||
enum pipe pipe;
|
||||
|
||||
/*
|
||||
* FIXME: For now we always enable slice S1 as per
|
||||
* the Bspec display initialization sequence.
|
||||
*/
|
||||
enabled_slices = BIT(DBUF_S1);
|
||||
|
||||
for_each_pipe(dev_priv, pipe)
|
||||
enabled_slices |= dbuf_state->slices[pipe];
|
||||
|
||||
return enabled_slices;
|
||||
}
|
||||
|
||||
static int
|
||||
skl_compute_ddb(struct intel_atomic_state *state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
|
||||
const struct intel_dbuf_state *old_dbuf_state;
|
||||
const struct intel_dbuf_state *new_dbuf_state;
|
||||
struct intel_dbuf_state *new_dbuf_state = NULL;
|
||||
const struct intel_crtc_state *old_crtc_state;
|
||||
struct intel_crtc_state *new_crtc_state;
|
||||
struct intel_crtc *crtc;
|
||||
int ret, i;
|
||||
|
||||
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
new_dbuf_state = intel_atomic_get_dbuf_state(state);
|
||||
if (IS_ERR(new_dbuf_state))
|
||||
return PTR_ERR(new_dbuf_state);
|
||||
|
||||
old_dbuf_state = intel_atomic_get_old_dbuf_state(state);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!new_dbuf_state)
|
||||
return 0;
|
||||
|
||||
new_dbuf_state->active_pipes =
|
||||
intel_calc_active_pipes(state, old_dbuf_state->active_pipes);
|
||||
|
||||
if (old_dbuf_state->active_pipes != new_dbuf_state->active_pipes) {
|
||||
ret = intel_atomic_lock_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_intel_crtc(&dev_priv->drm, crtc) {
|
||||
enum pipe pipe = crtc->pipe;
|
||||
|
||||
new_dbuf_state->slices[pipe] =
|
||||
skl_compute_dbuf_slices(crtc, new_dbuf_state->active_pipes);
|
||||
|
||||
if (old_dbuf_state->slices[pipe] == new_dbuf_state->slices[pipe])
|
||||
continue;
|
||||
|
||||
ret = intel_atomic_lock_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
new_dbuf_state->enabled_slices = intel_dbuf_enabled_slices(new_dbuf_state);
|
||||
|
||||
if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) {
|
||||
ret = intel_atomic_serialize_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"Enabled dbuf slices 0x%x -> 0x%x (out of %d dbuf slices)\n",
|
||||
old_dbuf_state->enabled_slices,
|
||||
new_dbuf_state->enabled_slices,
|
||||
INTEL_INFO(dev_priv)->num_supported_dbuf_slices);
|
||||
}
|
||||
|
||||
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
enum pipe pipe = crtc->pipe;
|
||||
|
||||
new_dbuf_state->weight[pipe] = intel_crtc_ddb_weight(new_crtc_state);
|
||||
|
||||
if (old_dbuf_state->weight[pipe] == new_dbuf_state->weight[pipe])
|
||||
continue;
|
||||
|
||||
ret = intel_atomic_lock_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_intel_crtc(&dev_priv->drm, crtc) {
|
||||
ret = skl_crtc_allocate_ddb(state, crtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
|
||||
new_crtc_state, i) {
|
||||
ret = skl_allocate_pipe_ddb(state, crtc);
|
||||
ret = skl_allocate_plane_ddb(state, crtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -5798,17 +5828,6 @@ skl_compute_ddb(struct intel_atomic_state *state)
|
||||
return ret;
|
||||
}
|
||||
|
||||
old_dbuf_state = intel_atomic_get_old_dbuf_state(state);
|
||||
new_dbuf_state = intel_atomic_get_new_dbuf_state(state);
|
||||
|
||||
if (new_dbuf_state &&
|
||||
new_dbuf_state->enabled_slices != old_dbuf_state->enabled_slices)
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"Enabled dbuf slices 0x%x -> 0x%x (out of %d dbuf slices)\n",
|
||||
old_dbuf_state->enabled_slices,
|
||||
new_dbuf_state->enabled_slices,
|
||||
INTEL_INFO(dev_priv)->num_supported_dbuf_slices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5944,83 +5963,6 @@ skl_print_wm_changes(struct intel_atomic_state *state)
|
||||
}
|
||||
}
|
||||
|
||||
static int intel_add_affected_pipes(struct intel_atomic_state *state,
|
||||
u8 pipe_mask)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
|
||||
struct intel_crtc *crtc;
|
||||
|
||||
for_each_intel_crtc(&dev_priv->drm, crtc) {
|
||||
struct intel_crtc_state *crtc_state;
|
||||
|
||||
if ((pipe_mask & BIT(crtc->pipe)) == 0)
|
||||
continue;
|
||||
|
||||
crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
skl_ddb_add_affected_pipes(struct intel_atomic_state *state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
|
||||
struct intel_crtc_state *crtc_state;
|
||||
struct intel_crtc *crtc;
|
||||
int i, ret;
|
||||
|
||||
if (dev_priv->wm.distrust_bios_wm) {
|
||||
/*
|
||||
* skl_ddb_get_pipe_allocation_limits() currently requires
|
||||
* all active pipes to be included in the state so that
|
||||
* it can redistribute the dbuf among them, and it really
|
||||
* wants to recompute things when distrust_bios_wm is set
|
||||
* so we add all the pipes to the state.
|
||||
*/
|
||||
ret = intel_add_affected_pipes(state, ~0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
struct intel_dbuf_state *new_dbuf_state;
|
||||
const struct intel_dbuf_state *old_dbuf_state;
|
||||
|
||||
new_dbuf_state = intel_atomic_get_dbuf_state(state);
|
||||
if (IS_ERR(new_dbuf_state))
|
||||
return PTR_ERR(new_dbuf_state);
|
||||
|
||||
old_dbuf_state = intel_atomic_get_old_dbuf_state(state);
|
||||
|
||||
new_dbuf_state->active_pipes =
|
||||
intel_calc_active_pipes(state, old_dbuf_state->active_pipes);
|
||||
|
||||
if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes)
|
||||
break;
|
||||
|
||||
ret = intel_atomic_lock_global_state(&new_dbuf_state->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* skl_ddb_get_pipe_allocation_limits() currently requires
|
||||
* all active pipes to be included in the state so that
|
||||
* it can redistribute the dbuf among them.
|
||||
*/
|
||||
ret = intel_add_affected_pipes(state,
|
||||
new_dbuf_state->active_pipes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* To make sure the cursor watermark registers are always consistent
|
||||
* with our computed state the following scenario needs special
|
||||
@ -6088,15 +6030,6 @@ skl_compute_wm(struct intel_atomic_state *state)
|
||||
struct intel_crtc_state *new_crtc_state;
|
||||
int ret, i;
|
||||
|
||||
ret = skl_ddb_add_affected_pipes(state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Calculate WM's for all pipes that are part of this transaction.
|
||||
* Note that skl_ddb_add_affected_pipes may have added more CRTC's that
|
||||
* weren't otherwise being modified if pipe allocations had to change.
|
||||
*/
|
||||
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
ret = skl_build_pipe_wm(state, crtc);
|
||||
if (ret)
|
||||
@ -6255,20 +6188,49 @@ void skl_pipe_wm_get_hw_state(struct intel_crtc *crtc,
|
||||
|
||||
void skl_wm_get_hw_state(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct intel_dbuf_state *dbuf_state =
|
||||
to_intel_dbuf_state(dev_priv->dbuf.obj.state);
|
||||
struct intel_crtc *crtc;
|
||||
struct intel_crtc_state *crtc_state;
|
||||
|
||||
for_each_intel_crtc(&dev_priv->drm, crtc) {
|
||||
crtc_state = to_intel_crtc_state(crtc->base.state);
|
||||
struct intel_crtc_state *crtc_state =
|
||||
to_intel_crtc_state(crtc->base.state);
|
||||
enum pipe pipe = crtc->pipe;
|
||||
enum plane_id plane_id;
|
||||
|
||||
skl_pipe_wm_get_hw_state(crtc, &crtc_state->wm.skl.optimal);
|
||||
crtc_state->wm.skl.raw = crtc_state->wm.skl.optimal;
|
||||
|
||||
memset(&dbuf_state->ddb[pipe], 0, sizeof(dbuf_state->ddb[pipe]));
|
||||
|
||||
for_each_plane_id_on_crtc(crtc, plane_id) {
|
||||
struct skl_ddb_entry *ddb_y =
|
||||
&crtc_state->wm.skl.plane_ddb_y[plane_id];
|
||||
struct skl_ddb_entry *ddb_uv =
|
||||
&crtc_state->wm.skl.plane_ddb_uv[plane_id];
|
||||
|
||||
skl_ddb_get_hw_plane_state(dev_priv, crtc->pipe,
|
||||
plane_id, ddb_y, ddb_uv);
|
||||
|
||||
skl_ddb_entry_union(&dbuf_state->ddb[pipe], ddb_y);
|
||||
skl_ddb_entry_union(&dbuf_state->ddb[pipe], ddb_uv);
|
||||
}
|
||||
|
||||
dbuf_state->slices[pipe] =
|
||||
skl_compute_dbuf_slices(crtc, dbuf_state->active_pipes);
|
||||
|
||||
dbuf_state->weight[pipe] = intel_crtc_ddb_weight(crtc_state);
|
||||
|
||||
crtc_state->wm.skl.ddb = dbuf_state->ddb[pipe];
|
||||
|
||||
drm_dbg_kms(&dev_priv->drm,
|
||||
"[CRTC:%d:%s] dbuf slices 0x%x, ddb (%d - %d), active pipes 0x%x\n",
|
||||
crtc->base.base.id, crtc->base.name,
|
||||
dbuf_state->slices[pipe], dbuf_state->ddb[pipe].start,
|
||||
dbuf_state->ddb[pipe].end, dbuf_state->active_pipes);
|
||||
}
|
||||
|
||||
if (dev_priv->active_pipes) {
|
||||
/* Fully recompute DDB on first atomic commit */
|
||||
dev_priv->wm.distrust_bios_wm = true;
|
||||
}
|
||||
dbuf_state->enabled_slices = dev_priv->dbuf.enabled_slices;
|
||||
}
|
||||
|
||||
static void ilk_pipe_wm_get_hw_state(struct intel_crtc *crtc)
|
||||
@ -7103,24 +7065,26 @@ static void icl_init_clock_gating(struct drm_i915_private *dev_priv)
|
||||
0, CNL_DELAY_PMRSP);
|
||||
}
|
||||
|
||||
static void tgl_init_clock_gating(struct drm_i915_private *dev_priv)
|
||||
static void gen12lp_init_clock_gating(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
/* Wa_1409120013:tgl */
|
||||
/* Wa_1409120013:tgl,rkl,adl_s,dg1 */
|
||||
intel_uncore_write(&dev_priv->uncore, ILK_DPFC_CHICKEN,
|
||||
ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL);
|
||||
ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL);
|
||||
|
||||
/* Wa_1409825376:tgl (pre-prod)*/
|
||||
if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B1))
|
||||
intel_uncore_write(&dev_priv->uncore, GEN9_CLKGATE_DIS_3, intel_uncore_read(&dev_priv->uncore, GEN9_CLKGATE_DIS_3) |
|
||||
TGL_VRH_GATING_DIS);
|
||||
|
||||
/* Wa_14011059788:tgl */
|
||||
/* Wa_14011059788:tgl,rkl,adl_s,dg1 */
|
||||
intel_uncore_rmw(&dev_priv->uncore, GEN10_DFR_RATIO_EN_AND_CHICKEN,
|
||||
0, DFR_DISABLE);
|
||||
}
|
||||
|
||||
static void dg1_init_clock_gating(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
gen12lp_init_clock_gating(dev_priv);
|
||||
|
||||
/* Wa_1409836686:dg1[a0] */
|
||||
if (IS_DG1_REVID(dev_priv, DG1_REVID_A0, DG1_REVID_A0))
|
||||
intel_uncore_write(&dev_priv->uncore, GEN9_CLKGATE_DIS_3, intel_uncore_read(&dev_priv->uncore, GEN9_CLKGATE_DIS_3) |
|
||||
@ -7583,7 +7547,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv)
|
||||
if (IS_DG1(dev_priv))
|
||||
dev_priv->display.init_clock_gating = dg1_init_clock_gating;
|
||||
else if (IS_GEN(dev_priv, 12))
|
||||
dev_priv->display.init_clock_gating = tgl_init_clock_gating;
|
||||
dev_priv->display.init_clock_gating = gen12lp_init_clock_gating;
|
||||
else if (IS_GEN(dev_priv, 11))
|
||||
dev_priv->display.init_clock_gating = icl_init_clock_gating;
|
||||
else if (IS_CANNONLAKE(dev_priv))
|
||||
|
@ -9,8 +9,10 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "display/intel_bw.h"
|
||||
#include "display/intel_display.h"
|
||||
#include "display/intel_global_state.h"
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
|
||||
struct drm_device;
|
||||
@ -40,7 +42,6 @@ void skl_pipe_ddb_get_hw_state(struct intel_crtc *crtc,
|
||||
struct skl_ddb_entry *ddb_y,
|
||||
struct skl_ddb_entry *ddb_uv);
|
||||
void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv);
|
||||
u16 intel_get_ddb_size(struct drm_i915_private *dev_priv);
|
||||
u32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv,
|
||||
const struct skl_ddb_entry *entry);
|
||||
void skl_pipe_wm_get_hw_state(struct intel_crtc *crtc,
|
||||
@ -69,6 +70,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable);
|
||||
struct intel_dbuf_state {
|
||||
struct intel_global_state base;
|
||||
|
||||
struct skl_ddb_entry ddb[I915_MAX_PIPES];
|
||||
unsigned int weight[I915_MAX_PIPES];
|
||||
u8 slices[I915_MAX_PIPES];
|
||||
|
||||
u8 enabled_slices;
|
||||
u8 active_pipes;
|
||||
};
|
||||
|
@ -1420,16 +1420,14 @@ void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
|
||||
static bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl)
|
||||
{
|
||||
u8 *dpcd = ctrl->panel->dpcd;
|
||||
u32 edid_quirks = 0;
|
||||
|
||||
edid_quirks = drm_dp_get_edid_quirks(ctrl->panel->edid);
|
||||
/*
|
||||
* For better interop experience, used a fixed NVID=0x8000
|
||||
* whenever connected to a VGA dongle downstream.
|
||||
*/
|
||||
if (drm_dp_is_branch(dpcd))
|
||||
return (drm_dp_has_quirk(&ctrl->panel->desc, edid_quirks,
|
||||
DP_DPCD_QUIRK_CONSTANT_N));
|
||||
return (drm_dp_has_quirk(&ctrl->panel->desc,
|
||||
DP_DPCD_QUIRK_CONSTANT_N));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -569,8 +569,7 @@ static int mei_hdcp_verify_mprime(struct device *dev,
|
||||
verify_mprime_in->header.api_version = HDCP_API_VERSION;
|
||||
verify_mprime_in->header.command_id = WIRED_REPEATER_AUTH_STREAM_REQ;
|
||||
verify_mprime_in->header.status = ME_HDCP_STATUS_SUCCESS;
|
||||
verify_mprime_in->header.buffer_len =
|
||||
WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_MIN_IN;
|
||||
verify_mprime_in->header.buffer_len = cmd_size - sizeof(verify_mprime_in->header);
|
||||
|
||||
verify_mprime_in->port.integrated_port_type = data->port_type;
|
||||
verify_mprime_in->port.physical_port = (u8)data->fw_ddi;
|
||||
|
@ -2029,16 +2029,13 @@ struct drm_dp_desc {
|
||||
|
||||
int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
|
||||
bool is_branch);
|
||||
u32 drm_dp_get_edid_quirks(const struct edid *edid);
|
||||
|
||||
/**
|
||||
* enum drm_dp_quirk - Display Port sink/branch device specific quirks
|
||||
*
|
||||
* Display Port sink and branch devices in the wild have a variety of bugs, try
|
||||
* to collect them here. The quirks are shared, but it's up to the drivers to
|
||||
* implement workarounds for them. Note that because some devices have
|
||||
* unreliable OUIDs, the EDID of sinks should also be checked for quirks using
|
||||
* drm_dp_get_edid_quirks().
|
||||
* implement workarounds for them.
|
||||
*/
|
||||
enum drm_dp_quirk {
|
||||
/**
|
||||
@ -2070,16 +2067,6 @@ enum drm_dp_quirk {
|
||||
* The DSC caps can be read from the physical aux instead.
|
||||
*/
|
||||
DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD,
|
||||
/**
|
||||
* @DP_QUIRK_FORCE_DPCD_BACKLIGHT:
|
||||
*
|
||||
* The device is telling the truth when it says that it uses DPCD
|
||||
* backlight controls, even if the system's firmware disagrees. This
|
||||
* quirk should be checked against both the ident and panel EDID.
|
||||
* When present, the driver should honor the DPCD backlight
|
||||
* capabilities advertised.
|
||||
*/
|
||||
DP_QUIRK_FORCE_DPCD_BACKLIGHT,
|
||||
/**
|
||||
* @DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS:
|
||||
*
|
||||
@ -2092,16 +2079,14 @@ enum drm_dp_quirk {
|
||||
/**
|
||||
* drm_dp_has_quirk() - does the DP device have a specific quirk
|
||||
* @desc: Device descriptor filled by drm_dp_read_desc()
|
||||
* @edid_quirks: Optional quirk bitmask filled by drm_dp_get_edid_quirks()
|
||||
* @quirk: Quirk to query for
|
||||
*
|
||||
* Return true if DP device identified by @desc has @quirk.
|
||||
*/
|
||||
static inline bool
|
||||
drm_dp_has_quirk(const struct drm_dp_desc *desc, u32 edid_quirks,
|
||||
enum drm_dp_quirk quirk)
|
||||
drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
|
||||
{
|
||||
return (desc->quirks | edid_quirks) & BIT(quirk);
|
||||
return desc->quirks & BIT(quirk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_DP_CEC
|
||||
|
@ -101,11 +101,11 @@
|
||||
|
||||
/* Following Macros take a byte at a time for bit(s) masking */
|
||||
/*
|
||||
* TODO: This has to be changed for DP MST, as multiple stream on
|
||||
* same port is possible.
|
||||
* For HDCP2.2 on HDMI and DP SST this value is always 1.
|
||||
* TODO: HDCP_2_2_MAX_CONTENT_STREAMS_CNT is based upon actual
|
||||
* H/W MST streams capacity.
|
||||
* This required to be moved out to platform specific header.
|
||||
*/
|
||||
#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 1
|
||||
#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 4
|
||||
#define HDCP_2_2_TXCAP_MASK_LEN 2
|
||||
#define HDCP_2_2_RXCAPS_LEN 3
|
||||
#define HDCP_2_2_RX_REPEATER(x) ((x) & BIT(0))
|
||||
|
@ -527,6 +527,25 @@ extern "C" {
|
||||
*/
|
||||
#define I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS fourcc_mod_code(INTEL, 7)
|
||||
|
||||
/*
|
||||
* Intel Color Control Surface with Clear Color (CCS) for Gen-12 render
|
||||
* compression.
|
||||
*
|
||||
* The main surface is Y-tiled and is at plane index 0 whereas CCS is linear
|
||||
* and at index 1. The clear color is stored at index 2, and the pitch should
|
||||
* be ignored. The clear color structure is 256 bits. The first 128 bits
|
||||
* represents Raw Clear Color Red, Green, Blue and Alpha color each represented
|
||||
* by 32 bits. The raw clear color is consumed by the 3d engine and generates
|
||||
* the converted clear color of size 64 bits. The first 32 bits store the Lower
|
||||
* Converted Clear Color value and the next 32 bits store the Higher Converted
|
||||
* Clear Color value when applicable. The Converted Clear Color values are
|
||||
* consumed by the DE. The last 64 bits are used to store Color Discard Enable
|
||||
* and Depth Clear Value Valid which are ignored by the DE. A CCS cache line
|
||||
* corresponds to an area of 4x1 tiles in the main surface. The main surface
|
||||
* pitch is required to be a multiple of 4 tile widths.
|
||||
*/
|
||||
#define I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC fourcc_mod_code(INTEL, 8)
|
||||
|
||||
/*
|
||||
* Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user