- 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:
Dave Airlie 2021-01-29 17:04:36 +10:00
commit 32c3d9b0f5
56 changed files with 6885 additions and 5532 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View 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;
}

View 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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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__ */

View 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);
}

View 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__ */

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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);
}
/*

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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);

View File

@ -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:

View 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;
}
}

View 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

View File

@ -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);
}

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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);

File diff suppressed because it is too large Load Diff

View 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__ */

View File

@ -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;

View File

@ -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,

View 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;
}

View 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__ */

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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);
}

View File

@ -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))

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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))

View File

@ -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
*