Merge tag 'topic/nouveau-i915-dp-helpers-and-cleanup-2020-08-31-1' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
UAPI Changes: None Cross-subsystem Changes: * Moves a bunch of miscellaneous DP code from the i915 driver into a set of shared DRM DP helpers Core Changes: * New DRM DP helpers (see above) Driver Changes: * Implements usage of the aforementioned DP helpers in the nouveau driver, along with some other various HPD related cleanup for nouveau Signed-off-by: Dave Airlie <airlied@redhat.com> From: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/11e59ebdea7ee4f46803a21fe9b21443d2b9c401.camel@redhat.com
This commit is contained in:
Коммит
877d8c0743
|
@ -29,6 +29,8 @@ properties:
|
|||
# compatible must be listed in alphabetical order, ordered by compatible.
|
||||
# The description in the comment is mandatory for each compatible.
|
||||
|
||||
# Ampire AM-1280800N3TZQW-T00H 10.1" WQVGA TFT LCD panel
|
||||
- ampire,am-1280800n3tzqw-t00h
|
||||
# Ampire AM-480272H3TMQW-T01H 4.3" WQVGA TFT LCD panel
|
||||
- ampire,am-480272h3tmqw-t01h
|
||||
# Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
|
||||
|
|
|
@ -473,7 +473,7 @@ error:
|
|||
* help move buffers to and from VRAM.
|
||||
*/
|
||||
static int amdgpu_move_blit(struct ttm_buffer_object *bo,
|
||||
bool evict, bool no_wait_gpu,
|
||||
bool evict,
|
||||
struct ttm_resource *new_mem,
|
||||
struct ttm_resource *old_mem)
|
||||
{
|
||||
|
@ -571,7 +571,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
|
|||
}
|
||||
|
||||
/* blit VRAM to GTT */
|
||||
r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu, &tmp_mem, old_mem);
|
||||
r = amdgpu_move_blit(bo, evict, &tmp_mem, old_mem);
|
||||
if (unlikely(r)) {
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
@ -621,7 +621,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
|
|||
}
|
||||
|
||||
/* copy to VRAM */
|
||||
r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu, new_mem, old_mem);
|
||||
r = amdgpu_move_blit(bo, evict, new_mem, old_mem);
|
||||
if (unlikely(r)) {
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
|
|||
new_mem->mem_type == TTM_PL_VRAM) {
|
||||
r = amdgpu_move_ram_vram(bo, evict, ctx, new_mem);
|
||||
} else {
|
||||
r = amdgpu_move_blit(bo, evict, ctx->no_wait_gpu,
|
||||
r = amdgpu_move_blit(bo, evict,
|
||||
new_mem, old_mem);
|
||||
}
|
||||
|
||||
|
|
|
@ -960,13 +960,13 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
|||
static int lt9611_parse_dt(struct device *dev,
|
||||
struct lt9611 *lt9611)
|
||||
{
|
||||
lt9611->dsi0_node = of_graph_get_remote_node(dev->of_node, 1, -1);
|
||||
lt9611->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
if (!lt9611->dsi0_node) {
|
||||
dev_err(lt9611->dev, "failed to get remote node for primary dsi\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
lt9611->dsi1_node = of_graph_get_remote_node(dev->of_node, 2, -1);
|
||||
lt9611->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
|
||||
|
||||
lt9611->ac_mode = of_property_read_bool(dev->of_node, "lt,ac-mode");
|
||||
|
||||
|
|
|
@ -423,6 +423,133 @@ bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
|
||||
|
||||
static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK;
|
||||
|
||||
if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4)
|
||||
port_count = 4;
|
||||
|
||||
return port_count;
|
||||
}
|
||||
|
||||
static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
u8 dpcd_ext[6];
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Prior to DP1.3 the bit represented by
|
||||
* DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
|
||||
* If it is set DP_DPCD_REV at 0000h could be at a value less than
|
||||
* the true capability of the panel. The only way to check is to
|
||||
* then compare 0000h and 2200h.
|
||||
*/
|
||||
if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
|
||||
return 0;
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
|
||||
sizeof(dpcd_ext));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(dpcd_ext))
|
||||
return -EIO;
|
||||
|
||||
if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
|
||||
DRM_DEBUG_KMS("%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
|
||||
aux->name, dpcd[DP_DPCD_REV],
|
||||
dpcd_ext[DP_DPCD_REV]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
|
||||
return 0;
|
||||
|
||||
DRM_DEBUG_KMS("%s: Base DPCD: %*ph\n",
|
||||
aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
|
||||
|
||||
memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if
|
||||
* available
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @dpcd: Buffer to store the resulting DPCD in
|
||||
*
|
||||
* Attempts to read the base DPCD caps for @aux. Additionally, this function
|
||||
* checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if
|
||||
* present.
|
||||
*
|
||||
* Returns: %0 if the DPCD was read successfully, negative error code
|
||||
* otherwise.
|
||||
*/
|
||||
int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
|
||||
return -EIO;
|
||||
|
||||
ret = drm_dp_read_extended_dpcd_caps(aux, dpcd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s: DPCD: %*ph\n",
|
||||
aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_dpcd_caps);
|
||||
|
||||
/**
|
||||
* drm_dp_read_downstream_info() - read DPCD downstream port info if available
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @dpcd: A cached copy of the port's DPCD
|
||||
* @downstream_ports: buffer to store the downstream port info in
|
||||
*
|
||||
* See also:
|
||||
* drm_dp_downstream_max_clock()
|
||||
* drm_dp_downstream_max_bpc()
|
||||
*
|
||||
* Returns: 0 if either the downstream port info was read successfully or
|
||||
* there was no downstream info to read, or a negative error code otherwise.
|
||||
*/
|
||||
int drm_dp_read_downstream_info(struct drm_dp_aux *aux,
|
||||
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS])
|
||||
{
|
||||
int ret;
|
||||
u8 len;
|
||||
|
||||
memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS);
|
||||
|
||||
/* No downstream info to read */
|
||||
if (!drm_dp_is_branch(dpcd) ||
|
||||
dpcd[DP_DPCD_REV] < DP_DPCD_REV_10 ||
|
||||
!(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT))
|
||||
return 0;
|
||||
|
||||
len = drm_dp_downstream_port_count(dpcd);
|
||||
if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE)
|
||||
len *= 4;
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret == len ? 0 : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_downstream_info);
|
||||
|
||||
/**
|
||||
* drm_dp_downstream_max_clock() - extract branch device max
|
||||
* pixel rate for legacy VGA
|
||||
|
@ -431,7 +558,11 @@ EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
|
|||
* @dpcd: DisplayPort configuration data
|
||||
* @port_cap: port capabilities
|
||||
*
|
||||
* Returns max clock in kHz on success or 0 if max clock not defined
|
||||
* See also:
|
||||
* drm_dp_read_downstream_info()
|
||||
* drm_dp_downstream_max_bpc()
|
||||
*
|
||||
* Returns: Max clock in kHz on success or 0 if max clock not defined
|
||||
*/
|
||||
int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const u8 port_cap[4])
|
||||
|
@ -462,7 +593,11 @@ EXPORT_SYMBOL(drm_dp_downstream_max_clock);
|
|||
* @dpcd: DisplayPort configuration data
|
||||
* @port_cap: port capabilities
|
||||
*
|
||||
* Returns max bpc on success or 0 if max bpc not defined
|
||||
* See also:
|
||||
* drm_dp_read_downstream_info()
|
||||
* drm_dp_downstream_max_clock()
|
||||
*
|
||||
* Returns: Max bpc on success or 0 if max bpc not defined
|
||||
*/
|
||||
int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const u8 port_cap[4])
|
||||
|
@ -668,6 +803,54 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_dp_set_subconnector_property);
|
||||
|
||||
/**
|
||||
* drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink
|
||||
* count
|
||||
* @connector: The DRM connector to check
|
||||
* @dpcd: A cached copy of the connector's DPCD RX capabilities
|
||||
* @desc: A cached copy of the connector's DP descriptor
|
||||
*
|
||||
* See also: drm_dp_read_sink_count()
|
||||
*
|
||||
* Returns: %True if the (e)DP connector has a valid sink count that should
|
||||
* be probed, %false otherwise.
|
||||
*/
|
||||
bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
|
||||
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const struct drm_dp_desc *desc)
|
||||
{
|
||||
/* Some eDP panels don't set a valid value for the sink count */
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_sink_count_cap);
|
||||
|
||||
/**
|
||||
* drm_dp_read_sink_count() - Retrieve the sink count for a given sink
|
||||
* @aux: The DP AUX channel to use
|
||||
*
|
||||
* See also: drm_dp_read_sink_count_cap()
|
||||
*
|
||||
* Returns: The current sink count reported by @aux, or a negative error code
|
||||
* otherwise.
|
||||
*/
|
||||
int drm_dp_read_sink_count(struct drm_dp_aux *aux)
|
||||
{
|
||||
u8 count;
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 1)
|
||||
return -EIO;
|
||||
|
||||
return DP_GET_SINK_COUNT(count);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_sink_count);
|
||||
|
||||
/*
|
||||
* I2C-over-AUX implementation
|
||||
*/
|
||||
|
|
|
@ -3486,6 +3486,28 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count)
|
|||
return dp_link_bw * dp_link_count / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_read_mst_cap() - check whether or not a sink supports MST
|
||||
* @aux: The DP AUX channel to use
|
||||
* @dpcd: A cached copy of the DPCD capabilities for this sink
|
||||
*
|
||||
* Returns: %True if the sink supports MST, %false otherwise
|
||||
*/
|
||||
bool drm_dp_read_mst_cap(struct drm_dp_aux *aux,
|
||||
const u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
u8 mstm_cap;
|
||||
|
||||
if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_12)
|
||||
return false;
|
||||
|
||||
if (drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &mstm_cap) != 1)
|
||||
return false;
|
||||
|
||||
return mstm_cap & DP_MST_CAP;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_mst_cap);
|
||||
|
||||
/**
|
||||
* drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
|
||||
* @mgr: manager to set state for
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_HISI_HIBMC
|
||||
tristate "DRM Support for Hisilicon Hibmc"
|
||||
depends on DRM && PCI && MMU && ARM64
|
||||
depends on DRM && PCI && ARM64
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_VRAM_HELPER
|
||||
select DRM_TTM
|
||||
|
|
|
@ -4449,62 +4449,6 @@ intel_dp_link_down(struct intel_encoder *encoder,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_extended_receiver_capabilities(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
u8 dpcd_ext[6];
|
||||
|
||||
/*
|
||||
* Prior to DP1.3 the bit represented by
|
||||
* DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
|
||||
* if it is set DP_DPCD_REV at 0000h could be at a value less than
|
||||
* the true capability of the panel. The only way to check is to
|
||||
* then compare 0000h and 2200h.
|
||||
*/
|
||||
if (!(intel_dp->dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
|
||||
return;
|
||||
|
||||
if (drm_dp_dpcd_read(&intel_dp->aux, DP_DP13_DPCD_REV,
|
||||
&dpcd_ext, sizeof(dpcd_ext)) != sizeof(dpcd_ext)) {
|
||||
drm_err(&i915->drm,
|
||||
"DPCD failed read at extended capabilities\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"DPCD extended DPCD rev less than base DPCD rev\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memcmp(intel_dp->dpcd, dpcd_ext, sizeof(dpcd_ext)))
|
||||
return;
|
||||
|
||||
drm_dbg_kms(&i915->drm, "Base DPCD: %*ph\n",
|
||||
(int)sizeof(intel_dp->dpcd), intel_dp->dpcd);
|
||||
|
||||
memcpy(intel_dp->dpcd, dpcd_ext, sizeof(dpcd_ext));
|
||||
}
|
||||
|
||||
bool
|
||||
intel_dp_read_dpcd(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
|
||||
if (drm_dp_dpcd_read(&intel_dp->aux, 0x000, intel_dp->dpcd,
|
||||
sizeof(intel_dp->dpcd)) < 0)
|
||||
return false; /* aux transfer failed */
|
||||
|
||||
intel_dp_extended_receiver_capabilities(intel_dp);
|
||||
|
||||
drm_dbg_kms(&i915->drm, "DPCD: %*ph\n", (int)sizeof(intel_dp->dpcd),
|
||||
intel_dp->dpcd);
|
||||
|
||||
return intel_dp->dpcd[DP_DPCD_REV] != 0;
|
||||
}
|
||||
|
||||
bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp)
|
||||
{
|
||||
u8 dprx = 0;
|
||||
|
@ -4563,7 +4507,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
|
|||
/* this function is meant to be called only once */
|
||||
drm_WARN_ON(&dev_priv->drm, intel_dp->dpcd[DP_DPCD_REV] != 0);
|
||||
|
||||
if (!intel_dp_read_dpcd(intel_dp))
|
||||
if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd) != 0)
|
||||
return false;
|
||||
|
||||
drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc,
|
||||
|
@ -4634,11 +4578,23 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_has_sink_count(struct intel_dp *intel_dp)
|
||||
{
|
||||
if (!intel_dp->attached_connector)
|
||||
return false;
|
||||
|
||||
return drm_dp_read_sink_count_cap(&intel_dp->attached_connector->base,
|
||||
intel_dp->dpcd,
|
||||
&intel_dp->desc);
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_get_dpcd(struct intel_dp *intel_dp)
|
||||
{
|
||||
if (!intel_dp_read_dpcd(intel_dp))
|
||||
int ret;
|
||||
|
||||
if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -4653,18 +4609,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
|
|||
intel_dp_set_common_rates(intel_dp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some eDP panels do not set a valid value for sink count, that is why
|
||||
* it don't care about read it here and in intel_edp_init_dpcd().
|
||||
*/
|
||||
if (!intel_dp_is_edp(intel_dp) &&
|
||||
!drm_dp_has_quirk(&intel_dp->desc, 0,
|
||||
DP_DPCD_QUIRK_NO_SINK_COUNT)) {
|
||||
u8 count;
|
||||
ssize_t r;
|
||||
|
||||
r = drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &count);
|
||||
if (r < 1)
|
||||
if (intel_dp_has_sink_count(intel_dp)) {
|
||||
ret = drm_dp_read_sink_count(&intel_dp->aux);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -4672,7 +4619,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
|
|||
* a member variable in intel_dp will track any changes
|
||||
* between short pulse interrupts.
|
||||
*/
|
||||
intel_dp->sink_count = DP_GET_SINK_COUNT(count);
|
||||
intel_dp->sink_count = ret;
|
||||
|
||||
/*
|
||||
* SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
|
||||
|
@ -4685,32 +4632,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!drm_dp_is_branch(intel_dp->dpcd))
|
||||
return true; /* native DP sink */
|
||||
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] == 0x10)
|
||||
return true; /* no per-port downstream info */
|
||||
|
||||
if (drm_dp_dpcd_read(&intel_dp->aux, DP_DOWNSTREAM_PORT_0,
|
||||
intel_dp->downstream_ports,
|
||||
DP_MAX_DOWNSTREAM_PORTS) < 0)
|
||||
return false; /* downstream port status fetch failed */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_sink_can_mst(struct intel_dp *intel_dp)
|
||||
{
|
||||
u8 mstm_cap;
|
||||
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
|
||||
return false;
|
||||
|
||||
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_MSTM_CAP, &mstm_cap) != 1)
|
||||
return false;
|
||||
|
||||
return mstm_cap & DP_MST_CAP;
|
||||
return drm_dp_read_downstream_info(&intel_dp->aux, intel_dp->dpcd,
|
||||
intel_dp->downstream_ports) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -4720,7 +4643,7 @@ intel_dp_can_mst(struct intel_dp *intel_dp)
|
|||
|
||||
return i915->params.enable_dp_mst &&
|
||||
intel_dp->can_mst &&
|
||||
intel_dp_sink_can_mst(intel_dp);
|
||||
drm_dp_read_mst_cap(&intel_dp->aux, intel_dp->dpcd);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -4729,7 +4652,7 @@ intel_dp_configure_mst(struct intel_dp *intel_dp)
|
|||
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
|
||||
struct intel_encoder *encoder =
|
||||
&dp_to_dig_port(intel_dp)->base;
|
||||
bool sink_can_mst = intel_dp_sink_can_mst(intel_dp);
|
||||
bool sink_can_mst = drm_dp_read_mst_cap(&intel_dp->aux, intel_dp->dpcd);
|
||||
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n",
|
||||
|
@ -5963,9 +5886,8 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
|
|||
return connector_status_connected;
|
||||
|
||||
/* If we're HPD-aware, SINK_COUNT changes dynamically */
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
|
||||
if (intel_dp_has_sink_count(intel_dp) &&
|
||||
intel_dp->downstream_ports[0] & DP_DS_PORT_HPD) {
|
||||
|
||||
return intel_dp->sink_count ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,6 @@ bool intel_dp_source_supports_hbr3(struct intel_dp *intel_dp);
|
|||
bool
|
||||
intel_dp_get_link_status(struct intel_dp *intel_dp, u8 *link_status);
|
||||
|
||||
bool intel_dp_read_dpcd(struct intel_dp *intel_dp);
|
||||
bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp);
|
||||
int intel_dp_link_required(int pixel_clock, int bpp);
|
||||
int intel_dp_max_data_rate(int max_link_clock, int max_lanes);
|
||||
|
|
|
@ -571,7 +571,7 @@ bool lspcon_init(struct intel_digital_port *dig_port)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!intel_dp_read_dpcd(dp)) {
|
||||
if (drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd) != 0) {
|
||||
DRM_ERROR("LSPCON DPCD read failed\n");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -419,7 +419,7 @@ static void nv04_dac_commit(struct drm_encoder *encoder)
|
|||
helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
|
||||
nouveau_encoder_connector_get(nv_encoder)->base.name,
|
||||
nv04_encoder_get_connector(nv_encoder)->base.name,
|
||||
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,8 @@ static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
|
|||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
struct nouveau_connector *nv_connector =
|
||||
nv04_encoder_get_connector(nv_encoder);
|
||||
|
||||
if (!nv_connector->native_mode ||
|
||||
nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
|
||||
|
@ -478,7 +479,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
|
|||
helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
|
||||
nouveau_encoder_connector_get(nv_encoder)->base.name,
|
||||
nv04_encoder_get_connector(nv_encoder)->base.name,
|
||||
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
||||
}
|
||||
|
||||
|
@ -591,7 +592,7 @@ static void nv04_dfp_restore(struct drm_encoder *encoder)
|
|||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) {
|
||||
struct nouveau_connector *connector =
|
||||
nouveau_encoder_connector_get(nv_encoder);
|
||||
nv04_encoder_get_connector(nv_encoder);
|
||||
|
||||
if (connector && connector->native_mode)
|
||||
call_lvds_script(dev, nv_encoder->dcb, head,
|
||||
|
|
|
@ -35,9 +35,28 @@
|
|||
|
||||
#include <nvif/if0004.h>
|
||||
|
||||
static void
|
||||
nv04_display_fini(struct drm_device *dev, bool suspend)
|
||||
struct nouveau_connector *
|
||||
nv04_encoder_get_connector(struct nouveau_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = to_drm_encoder(encoder)->dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct nouveau_connector *nv_connector = NULL;
|
||||
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
if (connector->encoder == to_drm_encoder(encoder))
|
||||
nv_connector = nouveau_connector(connector);
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
return nv_connector;
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_display_fini(struct drm_device *dev, bool runtime, bool suspend)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv04_display *disp = nv04_display(dev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
|
@ -49,6 +68,9 @@ nv04_display_fini(struct drm_device *dev, bool suspend)
|
|||
if (nv_two_heads(dev))
|
||||
NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0);
|
||||
|
||||
if (!runtime)
|
||||
cancel_work_sync(&drm->hpd_work);
|
||||
|
||||
if (!suspend)
|
||||
return;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "nouveau_display.h"
|
||||
|
||||
struct nouveau_encoder;
|
||||
|
||||
enum nv04_fp_display_regs {
|
||||
FP_DISPLAY_END,
|
||||
FP_TOTAL,
|
||||
|
@ -93,6 +95,8 @@ nv04_display(struct drm_device *dev)
|
|||
|
||||
/* nv04_display.c */
|
||||
int nv04_display_create(struct drm_device *);
|
||||
struct nouveau_connector *
|
||||
nv04_encoder_get_connector(struct nouveau_encoder *nv_encoder);
|
||||
|
||||
/* nv04_crtc.c */
|
||||
int nv04_crtc_create(struct drm_device *, int index);
|
||||
|
|
|
@ -172,7 +172,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
|
|||
helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
|
||||
nouveau_encoder_connector_get(nv_encoder)->base.name,
|
||||
nv04_encoder_get_connector(nv_encoder)->base.name,
|
||||
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
||||
}
|
||||
|
||||
|
|
|
@ -599,7 +599,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder)
|
|||
helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n",
|
||||
nouveau_encoder_connector_get(nv_encoder)->base.name,
|
||||
nv04_encoder_get_connector(nv_encoder)->base.name,
|
||||
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
|
||||
}
|
||||
|
||||
|
|
|
@ -417,6 +417,40 @@ nv50_outp_atomic_check(struct drm_encoder *encoder,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct nouveau_connector *
|
||||
nv50_outp_get_new_connector(struct nouveau_encoder *outp,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *connector_state;
|
||||
struct drm_encoder *encoder = to_drm_encoder(outp);
|
||||
int i;
|
||||
|
||||
for_each_new_connector_in_state(state, connector, connector_state, i) {
|
||||
if (connector_state->best_encoder == encoder)
|
||||
return nouveau_connector(connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nouveau_connector *
|
||||
nv50_outp_get_old_connector(struct nouveau_encoder *outp,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_state *connector_state;
|
||||
struct drm_encoder *encoder = to_drm_encoder(outp);
|
||||
int i;
|
||||
|
||||
for_each_old_connector_in_state(state, connector, connector_state, i) {
|
||||
if (connector_state->best_encoder == encoder)
|
||||
return nouveau_connector(connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* DAC
|
||||
*****************************************************************************/
|
||||
|
@ -558,16 +592,31 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
|
|||
struct nouveau_drm *drm = nouveau_drm(drm_dev);
|
||||
struct drm_encoder *encoder;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct nouveau_connector *nv_connector;
|
||||
struct drm_connector *connector;
|
||||
struct nouveau_crtc *nv_crtc;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
int ret = 0;
|
||||
|
||||
*enabled = false;
|
||||
|
||||
drm_for_each_encoder(encoder, drm->dev) {
|
||||
struct nouveau_connector *nv_connector = NULL;
|
||||
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
|
||||
drm_connector_list_iter_begin(drm_dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
if (connector->state->best_encoder == encoder) {
|
||||
nv_connector = nouveau_connector(connector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
if (!nv_connector)
|
||||
continue;
|
||||
|
||||
nv_crtc = nouveau_crtc(encoder->crtc);
|
||||
if (!nv_connector || !nv_crtc || nv_encoder->or != port ||
|
||||
if (!nv_crtc || nv_encoder->or != port ||
|
||||
nv_crtc->index != dev_id)
|
||||
continue;
|
||||
*enabled = nv_encoder->audio;
|
||||
|
@ -578,6 +627,7 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -671,7 +721,8 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
||||
nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
@ -692,7 +743,7 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
(0x0100 << nv_crtc->index),
|
||||
};
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
|
||||
if (!drm_detect_monitor_audio(nv_connector->edid))
|
||||
return;
|
||||
|
||||
|
@ -729,7 +780,8 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
||||
nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
@ -758,7 +810,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
int ret;
|
||||
int size;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
|
||||
if (!drm_detect_hdmi_monitor(nv_connector->edid))
|
||||
return;
|
||||
|
||||
|
@ -804,7 +856,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
+ args.pwr.vendor_infoframe_length;
|
||||
nvif_mthd(&disp->disp->object, 0, &args, size);
|
||||
|
||||
nv50_audio_enable(encoder, mode);
|
||||
nv50_audio_enable(encoder, state, mode);
|
||||
|
||||
/* If SCDC is supported by the downstream monitor, update
|
||||
* divider / scrambling settings to what we programmed above.
|
||||
|
@ -833,16 +885,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
#define nv50_mstc(p) container_of((p), struct nv50_mstc, connector)
|
||||
#define nv50_msto(p) container_of((p), struct nv50_msto, encoder)
|
||||
|
||||
struct nv50_mstm {
|
||||
struct nouveau_encoder *outp;
|
||||
|
||||
struct drm_dp_mst_topology_mgr mgr;
|
||||
|
||||
bool modified;
|
||||
bool disabled;
|
||||
int links;
|
||||
};
|
||||
|
||||
struct nv50_mstc {
|
||||
struct nv50_mstm *mstm;
|
||||
struct drm_dp_mst_port *port;
|
||||
|
@ -1222,7 +1264,10 @@ nv50_mstc_detect(struct drm_connector *connector,
|
|||
|
||||
ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr,
|
||||
mstc->port);
|
||||
if (ret != connector_status_connected)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
pm_runtime_mark_last_busy(connector->dev->dev);
|
||||
pm_runtime_put_autosuspend(connector->dev->dev);
|
||||
return ret;
|
||||
|
@ -1371,41 +1416,51 @@ nv50_mstm = {
|
|||
.add_connector = nv50_mstm_add_connector,
|
||||
};
|
||||
|
||||
void
|
||||
nv50_mstm_service(struct nv50_mstm *mstm)
|
||||
bool
|
||||
nv50_mstm_service(struct nouveau_drm *drm,
|
||||
struct nouveau_connector *nv_connector,
|
||||
struct nv50_mstm *mstm)
|
||||
{
|
||||
struct drm_dp_aux *aux = mstm ? mstm->mgr.aux : NULL;
|
||||
bool handled = true;
|
||||
int ret;
|
||||
struct drm_dp_aux *aux = &nv_connector->aux;
|
||||
bool handled = true, ret = true;
|
||||
int rc;
|
||||
u8 esi[8] = {};
|
||||
|
||||
if (!aux)
|
||||
return;
|
||||
|
||||
while (handled) {
|
||||
ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8);
|
||||
if (ret != 8) {
|
||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||
return;
|
||||
rc = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8);
|
||||
if (rc != 8) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
drm_dp_mst_hpd_irq(&mstm->mgr, esi, &handled);
|
||||
if (!handled)
|
||||
break;
|
||||
|
||||
drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], 3);
|
||||
rc = drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1],
|
||||
3);
|
||||
if (rc != 3) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
NV_DEBUG(drm, "Failed to handle ESI on %s: %d\n",
|
||||
nv_connector->base.name, rc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_mstm_remove(struct nv50_mstm *mstm)
|
||||
{
|
||||
if (mstm)
|
||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||
mstm->is_mst = false;
|
||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
|
||||
nv50_mstm_enable(struct nv50_mstm *mstm, int state)
|
||||
{
|
||||
struct nouveau_encoder *outp = mstm->outp;
|
||||
struct {
|
||||
|
@ -1420,106 +1475,85 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
|
|||
};
|
||||
struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
|
||||
struct nvif_object *disp = &drm->display->disp.object;
|
||||
int ret;
|
||||
|
||||
if (dpcd >= 0x12) {
|
||||
/* Even if we're enabling MST, start with disabling the
|
||||
* branching unit to clear any sink-side MST topology state
|
||||
* that wasn't set by us
|
||||
*/
|
||||
ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (state) {
|
||||
/* Now, start initializing */
|
||||
ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL,
|
||||
DP_MST_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return nvif_mthd(disp, 0, &args, sizeof(args));
|
||||
}
|
||||
|
||||
int
|
||||
nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow)
|
||||
nv50_mstm_detect(struct nouveau_encoder *outp)
|
||||
{
|
||||
struct nv50_mstm *mstm = outp->dp.mstm;
|
||||
struct drm_dp_aux *aux;
|
||||
int ret;
|
||||
bool old_state, new_state;
|
||||
u8 mstm_ctrl;
|
||||
|
||||
if (!mstm)
|
||||
if (!mstm || !mstm->can_mst)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&mstm->mgr.lock);
|
||||
|
||||
old_state = mstm->mgr.mst_state;
|
||||
new_state = old_state;
|
||||
aux = mstm->mgr.aux;
|
||||
|
||||
if (old_state) {
|
||||
/* Just check that the MST hub is still as we expect it */
|
||||
ret = drm_dp_dpcd_readb(aux, DP_MSTM_CTRL, &mstm_ctrl);
|
||||
if (ret < 0 || !(mstm_ctrl & DP_MST_EN)) {
|
||||
DRM_DEBUG_KMS("Hub gone, disabling MST topology\n");
|
||||
new_state = false;
|
||||
}
|
||||
} else if (dpcd[0] >= 0x12) {
|
||||
ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &dpcd[1]);
|
||||
if (ret < 0)
|
||||
goto probe_error;
|
||||
/* Clear any leftover MST state we didn't set ourselves by first
|
||||
* disabling MST if it was already enabled
|
||||
*/
|
||||
ret = drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(dpcd[1] & DP_MST_CAP))
|
||||
dpcd[0] = 0x11;
|
||||
else
|
||||
new_state = allow;
|
||||
/* And start enabling */
|
||||
ret = nv50_mstm_enable(mstm, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true);
|
||||
if (ret) {
|
||||
nv50_mstm_enable(mstm, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (new_state == old_state) {
|
||||
mutex_unlock(&mstm->mgr.lock);
|
||||
return new_state;
|
||||
}
|
||||
|
||||
ret = nv50_mstm_enable(mstm, dpcd[0], new_state);
|
||||
if (ret)
|
||||
goto probe_error;
|
||||
|
||||
mutex_unlock(&mstm->mgr.lock);
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, new_state);
|
||||
if (ret)
|
||||
return nv50_mstm_enable(mstm, dpcd[0], 0);
|
||||
|
||||
return new_state;
|
||||
|
||||
probe_error:
|
||||
mutex_unlock(&mstm->mgr.lock);
|
||||
return ret;
|
||||
mstm->is_mst = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_mstm_fini(struct nv50_mstm *mstm)
|
||||
nv50_mstm_fini(struct nouveau_encoder *outp)
|
||||
{
|
||||
if (mstm && mstm->mgr.mst_state)
|
||||
struct nv50_mstm *mstm = outp->dp.mstm;
|
||||
|
||||
if (!mstm)
|
||||
return;
|
||||
|
||||
/* Don't change the MST state of this connector until we've finished
|
||||
* resuming, since we can't safely grab hpd_irq_lock in our resume
|
||||
* path to protect mstm->is_mst without potentially deadlocking
|
||||
*/
|
||||
mutex_lock(&outp->dp.hpd_irq_lock);
|
||||
mstm->suspended = true;
|
||||
mutex_unlock(&outp->dp.hpd_irq_lock);
|
||||
|
||||
if (mstm->is_mst)
|
||||
drm_dp_mst_topology_mgr_suspend(&mstm->mgr);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_mstm_init(struct nv50_mstm *mstm, bool runtime)
|
||||
nv50_mstm_init(struct nouveau_encoder *outp, bool runtime)
|
||||
{
|
||||
int ret;
|
||||
struct nv50_mstm *mstm = outp->dp.mstm;
|
||||
int ret = 0;
|
||||
|
||||
if (!mstm || !mstm->mgr.mst_state)
|
||||
if (!mstm)
|
||||
return;
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
|
||||
if (ret == -1) {
|
||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||
drm_kms_helper_hotplug_event(mstm->mgr.dev);
|
||||
if (mstm->is_mst) {
|
||||
ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime);
|
||||
if (ret == -1)
|
||||
nv50_mstm_remove(mstm);
|
||||
}
|
||||
|
||||
mutex_lock(&outp->dp.hpd_irq_lock);
|
||||
mstm->suspended = false;
|
||||
mutex_unlock(&outp->dp.hpd_irq_lock);
|
||||
|
||||
if (ret == -1)
|
||||
drm_kms_helper_hotplug_event(mstm->mgr.dev);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1541,17 +1575,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
|
|||
struct drm_device *dev = outp->base.base.dev;
|
||||
struct nv50_mstm *mstm;
|
||||
int ret;
|
||||
u8 dpcd;
|
||||
|
||||
/* This is a workaround for some monitors not functioning
|
||||
* correctly in MST mode on initial module load. I think
|
||||
* some bad interaction with the VBIOS may be responsible.
|
||||
*
|
||||
* A good ol' off and on again seems to work here ;)
|
||||
*/
|
||||
ret = drm_dp_dpcd_readb(aux, DP_DPCD_REV, &dpcd);
|
||||
if (ret >= 0 && dpcd >= 0x12)
|
||||
drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0);
|
||||
|
||||
if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
@ -1590,23 +1613,27 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head,
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_sor_disable(struct drm_encoder *encoder)
|
||||
nv50_sor_disable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
|
||||
struct nouveau_connector *nv_connector =
|
||||
nv50_outp_get_old_connector(nv_encoder, state);
|
||||
|
||||
nv_encoder->crtc = NULL;
|
||||
|
||||
if (nv_crtc) {
|
||||
struct nvkm_i2c_aux *aux = nv_encoder->aux;
|
||||
struct drm_dp_aux *aux = &nv_connector->aux;
|
||||
u8 pwr;
|
||||
|
||||
if (aux) {
|
||||
int ret = nvkm_rdaux(aux, DP_SET_POWER, &pwr, 1);
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
|
||||
int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr);
|
||||
|
||||
if (ret == 0) {
|
||||
pwr &= ~DP_SET_POWER_MASK;
|
||||
pwr |= DP_SET_POWER_D3;
|
||||
nvkm_wraux(aux, DP_SET_POWER, &pwr, 1);
|
||||
drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1618,7 +1645,8 @@ nv50_sor_disable(struct drm_encoder *encoder)
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_sor_enable(struct drm_encoder *encoder)
|
||||
nv50_sor_enable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
||||
|
@ -1642,7 +1670,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM;
|
||||
u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
|
||||
nv_encoder->crtc = encoder->crtc;
|
||||
|
||||
if ((disp->disp->object.oclass == GT214_DISP ||
|
||||
|
@ -1669,7 +1697,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
|
||||
}
|
||||
|
||||
nv50_hdmi_enable(&nv_encoder->base.base, mode);
|
||||
nv50_hdmi_enable(&nv_encoder->base.base, state, mode);
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
|
||||
|
@ -1710,7 +1738,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
else
|
||||
proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B;
|
||||
|
||||
nv50_audio_enable(encoder, mode);
|
||||
nv50_audio_enable(encoder, state, mode);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
|
@ -1723,8 +1751,8 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
static const struct drm_encoder_helper_funcs
|
||||
nv50_sor_help = {
|
||||
.atomic_check = nv50_outp_atomic_check,
|
||||
.enable = nv50_sor_enable,
|
||||
.disable = nv50_sor_disable,
|
||||
.atomic_enable = nv50_sor_enable,
|
||||
.atomic_disable = nv50_sor_disable,
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -1733,6 +1761,10 @@ nv50_sor_destroy(struct drm_encoder *encoder)
|
|||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
nv50_mstm_del(&nv_encoder->dp.mstm);
|
||||
drm_encoder_cleanup(encoder);
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
|
||||
mutex_destroy(&nv_encoder->dp.hpd_irq_lock);
|
||||
|
||||
kfree(encoder);
|
||||
}
|
||||
|
||||
|
@ -1792,6 +1824,8 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
struct nvkm_i2c_aux *aux =
|
||||
nvkm_i2c_aux_find(i2c, dcbe->i2c_index);
|
||||
|
||||
mutex_init(&nv_encoder->dp.hpd_irq_lock);
|
||||
|
||||
if (aux) {
|
||||
if (disp->disp->object.oclass < GF110_DISP) {
|
||||
/* HW has no support for address-only
|
||||
|
@ -2083,7 +2117,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
outp->clr.mask, outp->set.mask);
|
||||
|
||||
if (outp->clr.mask) {
|
||||
help->disable(encoder);
|
||||
help->atomic_disable(encoder, state);
|
||||
interlock[NV50_DISP_INTERLOCK_CORE] |= 1;
|
||||
if (outp->flush_disable) {
|
||||
nv50_disp_atomic_commit_wndw(state, interlock);
|
||||
|
@ -2122,7 +2156,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
outp->set.mask, outp->clr.mask);
|
||||
|
||||
if (outp->set.mask) {
|
||||
help->enable(encoder);
|
||||
help->atomic_enable(encoder, state);
|
||||
interlock[NV50_DISP_INTERLOCK_CORE] = 1;
|
||||
}
|
||||
|
||||
|
@ -2490,9 +2524,9 @@ nv50_disp_func = {
|
|||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
nv50_display_fini(struct drm_device *dev, bool suspend)
|
||||
nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_plane *plane;
|
||||
|
||||
|
@ -2504,11 +2538,12 @@ nv50_display_fini(struct drm_device *dev, bool suspend)
|
|||
}
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
nv50_mstm_fini(nv_encoder->dp.mstm);
|
||||
}
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST)
|
||||
nv50_mstm_fini(nouveau_encoder(encoder));
|
||||
}
|
||||
|
||||
if (!runtime)
|
||||
cancel_work_sync(&drm->hpd_work);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2525,7 +2560,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
|
|||
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
|
||||
struct nouveau_encoder *nv_encoder =
|
||||
nouveau_encoder(encoder);
|
||||
nv50_mstm_init(nv_encoder->dp.mstm, runtime);
|
||||
nv50_mstm_init(nv_encoder, runtime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -391,20 +391,6 @@ find_encoder(struct drm_connector *connector, int type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = to_drm_encoder(encoder)->dev;
|
||||
struct drm_connector *drm_connector;
|
||||
|
||||
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
|
||||
if (drm_connector->encoder == to_drm_encoder(encoder))
|
||||
return nouveau_connector(drm_connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
|
@ -435,7 +421,8 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
|||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case DCB_OUTPUT_DP:
|
||||
ret = nouveau_dp_detect(nv_encoder);
|
||||
ret = nouveau_dp_detect(nouveau_connector(connector),
|
||||
nv_encoder);
|
||||
if (ret == NOUVEAU_DP_MST)
|
||||
return NULL;
|
||||
else if (ret == NOUVEAU_DP_SST)
|
||||
|
@ -541,6 +528,17 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
|
||||
struct edid *edid)
|
||||
{
|
||||
struct edid *old_edid = nv_connector->edid;
|
||||
|
||||
drm_connector_update_edid_property(&nv_connector->base, edid);
|
||||
kfree(old_edid);
|
||||
nv_connector->edid = edid;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
nouveau_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
|
@ -554,13 +552,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
|
|||
int ret;
|
||||
enum drm_connector_status conn_status = connector_status_disconnected;
|
||||
|
||||
/* Cleanup the previous EDID block. */
|
||||
if (nv_connector->edid) {
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
kfree(nv_connector->edid);
|
||||
nv_connector->edid = NULL;
|
||||
}
|
||||
|
||||
/* Outputs are only polled while runtime active, so resuming the
|
||||
* device here is unnecessary (and would deadlock upon runtime suspend
|
||||
* because it waits for polling to finish). We do however, want to
|
||||
|
@ -573,22 +564,23 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
|
|||
ret = pm_runtime_get_sync(dev->dev);
|
||||
if (ret < 0 && ret != -EACCES) {
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
nouveau_connector_set_edid(nv_connector, NULL);
|
||||
return conn_status;
|
||||
}
|
||||
}
|
||||
|
||||
nv_encoder = nouveau_connector_ddc_detect(connector);
|
||||
if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
|
||||
struct edid *new_edid;
|
||||
|
||||
if ((vga_switcheroo_handler_flags() &
|
||||
VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
|
||||
nv_connector->type == DCB_CONNECTOR_LVDS)
|
||||
nv_connector->edid = drm_get_edid_switcheroo(connector,
|
||||
i2c);
|
||||
new_edid = drm_get_edid_switcheroo(connector, i2c);
|
||||
else
|
||||
nv_connector->edid = drm_get_edid(connector, i2c);
|
||||
new_edid = drm_get_edid(connector, i2c);
|
||||
|
||||
drm_connector_update_edid_property(connector,
|
||||
nv_connector->edid);
|
||||
nouveau_connector_set_edid(nv_connector, new_edid);
|
||||
if (!nv_connector->edid) {
|
||||
NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
|
||||
connector->name);
|
||||
|
@ -622,6 +614,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
|
|||
conn_status = connector_status_connected;
|
||||
drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
|
||||
goto out;
|
||||
} else {
|
||||
nouveau_connector_set_edid(nv_connector, NULL);
|
||||
}
|
||||
|
||||
nv_encoder = nouveau_connector_of_detect(connector);
|
||||
|
@ -646,10 +640,11 @@ detect_analog:
|
|||
conn_status = connector_status_connected;
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
if (!nv_connector->edid)
|
||||
drm_dp_cec_unset_edid(&nv_connector->aux);
|
||||
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
|
@ -664,18 +659,12 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
|
|||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||
struct nouveau_encoder *nv_encoder = NULL;
|
||||
struct edid *edid = NULL;
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
||||
/* Cleanup the previous EDID block. */
|
||||
if (nv_connector->edid) {
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
kfree(nv_connector->edid);
|
||||
nv_connector->edid = NULL;
|
||||
}
|
||||
|
||||
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
|
||||
if (!nv_encoder)
|
||||
return connector_status_disconnected;
|
||||
goto out;
|
||||
|
||||
/* Try retrieving EDID via DDC */
|
||||
if (!drm->vbios.fp_no_ddc) {
|
||||
|
@ -694,7 +683,8 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
|
|||
* valid - it's not (rh#613284)
|
||||
*/
|
||||
if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
|
||||
if ((nv_connector->edid = nouveau_acpi_edid(dev, connector))) {
|
||||
edid = nouveau_acpi_edid(dev, connector);
|
||||
if (edid) {
|
||||
status = connector_status_connected;
|
||||
goto out;
|
||||
}
|
||||
|
@ -714,12 +704,10 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
|
|||
* stored for the panel stored in them.
|
||||
*/
|
||||
if (!drm->vbios.fp_no_ddc) {
|
||||
struct edid *edid =
|
||||
(struct edid *)nouveau_bios_embedded_edid(dev);
|
||||
edid = (struct edid *)nouveau_bios_embedded_edid(dev);
|
||||
if (edid) {
|
||||
nv_connector->edid =
|
||||
kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
|
||||
if (nv_connector->edid)
|
||||
edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
|
||||
if (edid)
|
||||
status = connector_status_connected;
|
||||
}
|
||||
}
|
||||
|
@ -732,7 +720,7 @@ out:
|
|||
status = connector_status_unknown;
|
||||
#endif
|
||||
|
||||
drm_connector_update_edid_property(connector, nv_connector->edid);
|
||||
nouveau_connector_set_edid(nv_connector, edid);
|
||||
nouveau_connector_set_encoder(connector, nv_encoder);
|
||||
return status;
|
||||
}
|
||||
|
@ -1150,59 +1138,39 @@ nouveau_connector_funcs_lvds = {
|
|||
.early_unregister = nouveau_connector_early_unregister,
|
||||
};
|
||||
|
||||
void
|
||||
nouveau_connector_hpd(struct drm_connector *connector)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
||||
u32 mask = drm_connector_mask(connector);
|
||||
|
||||
mutex_lock(&drm->hpd_lock);
|
||||
if (!(drm->hpd_pending & mask)) {
|
||||
drm->hpd_pending |= mask;
|
||||
schedule_work(&drm->hpd_work);
|
||||
}
|
||||
mutex_unlock(&drm->hpd_lock);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_connector_hotplug(struct nvif_notify *notify)
|
||||
{
|
||||
struct nouveau_connector *nv_connector =
|
||||
container_of(notify, typeof(*nv_connector), hpd);
|
||||
struct drm_connector *connector = &nv_connector->base;
|
||||
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
|
||||
const char *name = connector->name;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
int ret;
|
||||
bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
|
||||
|
||||
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
|
||||
NV_DEBUG(drm, "service %s\n", name);
|
||||
drm_dp_cec_irq(&nv_connector->aux);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
|
||||
nv50_mstm_service(nv_encoder->dp.mstm);
|
||||
|
||||
nouveau_dp_irq(drm, nv_connector);
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get(drm->dev->dev);
|
||||
if (ret == 0) {
|
||||
/* We can't block here if there's a pending PM request
|
||||
* running, as we'll deadlock nouveau_display_fini() when it
|
||||
* calls nvif_put() on our nvif_notify struct. So, simply
|
||||
* defer the hotplug event until the device finishes resuming
|
||||
*/
|
||||
NV_DEBUG(drm, "Deferring HPD on %s until runtime resume\n",
|
||||
name);
|
||||
schedule_work(&drm->hpd_work);
|
||||
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", connector->name);
|
||||
nouveau_connector_hpd(connector);
|
||||
|
||||
pm_runtime_put_noidle(drm->dev->dev);
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
} else if (ret != 1 && ret != -EACCES) {
|
||||
NV_WARN(drm, "HPD on %s dropped due to RPM failure: %d\n",
|
||||
name, ret);
|
||||
return NVIF_NOTIFY_DROP;
|
||||
}
|
||||
|
||||
if (!plugged)
|
||||
drm_dp_cec_unset_edid(&nv_connector->aux);
|
||||
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
|
||||
if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
|
||||
if (!plugged)
|
||||
nv50_mstm_remove(nv_encoder->dp.mstm);
|
||||
}
|
||||
|
||||
drm_helper_hpd_irq_event(connector->dev);
|
||||
|
||||
pm_runtime_mark_last_busy(drm->dev->dev);
|
||||
pm_runtime_put_autosuspend(drm->dev->dev);
|
||||
return NVIF_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
|
|||
|
||||
struct drm_connector *
|
||||
nouveau_connector_create(struct drm_device *, const struct dcb_output *);
|
||||
void nouveau_connector_hpd(struct drm_connector *connector);
|
||||
|
||||
extern int nouveau_tv_disable;
|
||||
extern int nouveau_ignorelid;
|
||||
|
|
|
@ -457,16 +457,70 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
|
|||
} \
|
||||
} while(0)
|
||||
|
||||
void
|
||||
nouveau_display_hpd_resume(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
mutex_lock(&drm->hpd_lock);
|
||||
drm->hpd_pending = ~0;
|
||||
mutex_unlock(&drm->hpd_lock);
|
||||
|
||||
schedule_work(&drm->hpd_work);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_display_hpd_work(struct work_struct *work)
|
||||
{
|
||||
struct nouveau_drm *drm = container_of(work, typeof(*drm), hpd_work);
|
||||
struct drm_device *dev = drm->dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
u32 pending;
|
||||
bool changed = false;
|
||||
|
||||
pm_runtime_get_sync(drm->dev->dev);
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
drm_helper_hpd_irq_event(drm->dev);
|
||||
mutex_lock(&drm->hpd_lock);
|
||||
pending = drm->hpd_pending;
|
||||
drm->hpd_pending = 0;
|
||||
mutex_unlock(&drm->hpd_lock);
|
||||
|
||||
/* Nothing to do, exit early without updating the last busy counter */
|
||||
if (!pending)
|
||||
goto noop;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
|
||||
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
|
||||
enum drm_connector_status old_status = connector->status;
|
||||
u64 old_epoch_counter = connector->epoch_counter;
|
||||
|
||||
if (!(pending & drm_connector_mask(connector)))
|
||||
continue;
|
||||
|
||||
connector->status = drm_helper_probe_detect(connector, NULL,
|
||||
false);
|
||||
if (old_epoch_counter == connector->epoch_counter)
|
||||
continue;
|
||||
|
||||
changed = true;
|
||||
drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n",
|
||||
connector->base.id, connector->name,
|
||||
drm_get_connector_status_name(old_status),
|
||||
drm_get_connector_status_name(connector->status),
|
||||
old_epoch_counter, connector->epoch_counter);
|
||||
}
|
||||
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
if (changed)
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
|
||||
pm_runtime_mark_last_busy(drm->dev->dev);
|
||||
noop:
|
||||
pm_runtime_put_sync(drm->dev->dev);
|
||||
}
|
||||
|
||||
|
@ -490,12 +544,11 @@ nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
|
|||
*/
|
||||
pm_runtime_put_autosuspend(drm->dev->dev);
|
||||
} else if (ret == 0) {
|
||||
/* This may be the only indication we receive
|
||||
* of a connector hotplug on a runtime
|
||||
* suspended GPU, schedule hpd_work to check.
|
||||
/* We've started resuming the GPU already, so
|
||||
* it will handle scheduling a full reprobe
|
||||
* itself
|
||||
*/
|
||||
NV_DEBUG(drm, "ACPI requested connector reprobe\n");
|
||||
schedule_work(&drm->hpd_work);
|
||||
pm_runtime_put_noidle(drm->dev->dev);
|
||||
} else {
|
||||
NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n",
|
||||
|
@ -569,7 +622,7 @@ nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
|
|||
cancel_work_sync(&drm->hpd_work);
|
||||
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
disp->fini(dev, suspend);
|
||||
disp->fini(dev, runtime, suspend);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -686,6 +739,7 @@ nouveau_display_create(struct drm_device *dev)
|
|||
}
|
||||
|
||||
INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
|
||||
mutex_init(&drm->hpd_lock);
|
||||
#ifdef CONFIG_ACPI
|
||||
drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
|
||||
register_acpi_notifier(&drm->acpi_nb);
|
||||
|
@ -705,9 +759,10 @@ void
|
|||
nouveau_display_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
|
||||
unregister_acpi_notifier(&drm->acpi_nb);
|
||||
#endif
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
|
@ -719,6 +774,7 @@ nouveau_display_destroy(struct drm_device *dev)
|
|||
nvif_disp_dtor(&disp->disp);
|
||||
|
||||
nouveau_drm(dev)->display = NULL;
|
||||
mutex_destroy(&drm->hpd_lock);
|
||||
kfree(disp);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ struct nouveau_display {
|
|||
void *priv;
|
||||
void (*dtor)(struct drm_device *);
|
||||
int (*init)(struct drm_device *, bool resume, bool runtime);
|
||||
void (*fini)(struct drm_device *, bool suspend);
|
||||
void (*fini)(struct drm_device *, bool suspend, bool runtime);
|
||||
|
||||
struct nvif_disp disp;
|
||||
|
||||
|
@ -45,6 +45,7 @@ nouveau_display(struct drm_device *dev)
|
|||
int nouveau_display_create(struct drm_device *dev);
|
||||
void nouveau_display_destroy(struct drm_device *dev);
|
||||
int nouveau_display_init(struct drm_device *dev, bool resume, bool runtime);
|
||||
void nouveau_display_hpd_resume(struct drm_device *dev);
|
||||
void nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime);
|
||||
int nouveau_display_suspend(struct drm_device *dev, bool runtime);
|
||||
void nouveau_display_resume(struct drm_device *dev, bool runtime);
|
||||
|
|
|
@ -36,50 +36,123 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)");
|
|||
static int nouveau_mst = 1;
|
||||
module_param_named(mst, nouveau_mst, int, 0400);
|
||||
|
||||
static void
|
||||
nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_aux *aux, u8 *dpcd)
|
||||
static bool
|
||||
nouveau_dp_has_sink_count(struct drm_connector *connector,
|
||||
struct nouveau_encoder *outp)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
u8 buf[3];
|
||||
return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc);
|
||||
}
|
||||
|
||||
if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
|
||||
return;
|
||||
static enum drm_connector_status
|
||||
nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
|
||||
struct nouveau_encoder *outp)
|
||||
{
|
||||
struct drm_connector *connector = &nv_connector->base;
|
||||
struct drm_dp_aux *aux = &nv_connector->aux;
|
||||
struct nv50_mstm *mstm = NULL;
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
int ret;
|
||||
u8 *dpcd = outp->dp.dpcd;
|
||||
|
||||
if (!nvkm_rdaux(aux, DP_SINK_OUI, buf, 3))
|
||||
NV_DEBUG(drm, "Sink OUI: %02hx%02hx%02hx\n",
|
||||
buf[0], buf[1], buf[2]);
|
||||
ret = drm_dp_read_dpcd_caps(aux, dpcd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (!nvkm_rdaux(aux, DP_BRANCH_OUI, buf, 3))
|
||||
NV_DEBUG(drm, "Branch OUI: %02hx%02hx%02hx\n",
|
||||
buf[0], buf[1], buf[2]);
|
||||
ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (nouveau_mst) {
|
||||
mstm = outp->dp.mstm;
|
||||
if (mstm)
|
||||
mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd);
|
||||
}
|
||||
|
||||
if (nouveau_dp_has_sink_count(connector, outp)) {
|
||||
ret = drm_dp_read_sink_count(aux);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
outp->dp.sink_count = ret;
|
||||
|
||||
/*
|
||||
* Dongle connected, but no display. Don't bother reading
|
||||
* downstream port info
|
||||
*/
|
||||
if (!outp->dp.sink_count)
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
ret = drm_dp_read_downstream_info(aux, dpcd,
|
||||
outp->dp.downstream_ports);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
status = connector_status_connected;
|
||||
out:
|
||||
if (status != connector_status_connected) {
|
||||
/* Clear any cached info */
|
||||
outp->dp.sink_count = 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
|
||||
nouveau_dp_detect(struct nouveau_connector *nv_connector,
|
||||
struct nouveau_encoder *nv_encoder)
|
||||
{
|
||||
struct drm_device *dev = nv_encoder->base.base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nvkm_i2c_aux *aux;
|
||||
u8 dpcd[8];
|
||||
int ret;
|
||||
struct drm_connector *connector = &nv_connector->base;
|
||||
struct nv50_mstm *mstm = nv_encoder->dp.mstm;
|
||||
enum drm_connector_status status;
|
||||
u8 *dpcd = nv_encoder->dp.dpcd;
|
||||
int ret = NOUVEAU_DP_NONE;
|
||||
|
||||
aux = nv_encoder->aux;
|
||||
if (!aux)
|
||||
return -ENODEV;
|
||||
/* If we've already read the DPCD on an eDP device, we don't need to
|
||||
* reread it as it won't change
|
||||
*/
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
|
||||
dpcd[DP_DPCD_REV] != 0)
|
||||
return NOUVEAU_DP_SST;
|
||||
|
||||
ret = nvkm_rdaux(aux, DP_DPCD_REV, dpcd, sizeof(dpcd));
|
||||
if (ret)
|
||||
return ret;
|
||||
mutex_lock(&nv_encoder->dp.hpd_irq_lock);
|
||||
if (mstm) {
|
||||
/* If we're not ready to handle MST state changes yet, just
|
||||
* report the last status of the connector. We'll reprobe it
|
||||
* once we've resumed.
|
||||
*/
|
||||
if (mstm->suspended) {
|
||||
if (mstm->is_mst)
|
||||
ret = NOUVEAU_DP_MST;
|
||||
else if (connector->status ==
|
||||
connector_status_connected)
|
||||
ret = NOUVEAU_DP_SST;
|
||||
|
||||
nv_encoder->dp.link_bw = 27000 * dpcd[1];
|
||||
nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder);
|
||||
if (status == connector_status_disconnected)
|
||||
goto out;
|
||||
|
||||
/* If we're in MST mode, we're done here */
|
||||
if (mstm && mstm->can_mst && mstm->is_mst) {
|
||||
ret = NOUVEAU_DP_MST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE];
|
||||
nv_encoder->dp.link_nr =
|
||||
dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
||||
|
||||
NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n",
|
||||
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]);
|
||||
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw,
|
||||
dpcd[DP_DPCD_REV]);
|
||||
NV_DEBUG(drm, "encoder: %dx%d\n",
|
||||
nv_encoder->dcb->dpconf.link_nr,
|
||||
nv_encoder->dcb->dpconf.link_bw);
|
||||
nv_encoder->dcb->dpconf.link_nr,
|
||||
nv_encoder->dcb->dpconf.link_bw);
|
||||
|
||||
if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr)
|
||||
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
|
||||
|
@ -87,23 +160,68 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
|
|||
nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw;
|
||||
|
||||
NV_DEBUG(drm, "maximum: %dx%d\n",
|
||||
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
|
||||
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
|
||||
|
||||
nouveau_dp_probe_oui(dev, aux, dpcd);
|
||||
if (mstm && mstm->can_mst) {
|
||||
ret = nv50_mstm_detect(nv_encoder);
|
||||
if (ret == 1) {
|
||||
ret = NOUVEAU_DP_MST;
|
||||
goto out;
|
||||
} else if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = NOUVEAU_DP_SST;
|
||||
|
||||
ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst);
|
||||
if (ret == 1)
|
||||
return NOUVEAU_DP_MST;
|
||||
if (ret == 0)
|
||||
return NOUVEAU_DP_SST;
|
||||
out:
|
||||
if (mstm && !mstm->suspended && ret != NOUVEAU_DP_MST)
|
||||
nv50_mstm_remove(mstm);
|
||||
|
||||
mutex_unlock(&nv_encoder->dp.hpd_irq_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nouveau_dp_irq(struct nouveau_drm *drm,
|
||||
struct nouveau_connector *nv_connector)
|
||||
{
|
||||
struct drm_connector *connector = &nv_connector->base;
|
||||
struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
|
||||
struct nv50_mstm *mstm;
|
||||
int ret;
|
||||
bool send_hpd = false;
|
||||
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
mstm = outp->dp.mstm;
|
||||
NV_DEBUG(drm, "service %s\n", connector->name);
|
||||
|
||||
mutex_lock(&outp->dp.hpd_irq_lock);
|
||||
|
||||
if (mstm && mstm->is_mst) {
|
||||
if (!nv50_mstm_service(drm, nv_connector, mstm))
|
||||
send_hpd = true;
|
||||
} else {
|
||||
drm_dp_cec_irq(&nv_connector->aux);
|
||||
|
||||
if (nouveau_dp_has_sink_count(connector, outp)) {
|
||||
ret = drm_dp_read_sink_count(&nv_connector->aux);
|
||||
if (ret != outp->dp.sink_count)
|
||||
send_hpd = true;
|
||||
if (ret >= 0)
|
||||
outp->dp.sink_count = ret;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&outp->dp.hpd_irq_lock);
|
||||
|
||||
if (send_hpd)
|
||||
nouveau_connector_hpd(connector);
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
* - Use the minimum possible BPC here, once we add support for the max bpc
|
||||
* property.
|
||||
* - Validate the mode against downstream port caps (see
|
||||
* drm_dp_downstream_max_clock())
|
||||
* - Validate against the DP caps advertised by the GPU (we don't check these
|
||||
* yet)
|
||||
*/
|
||||
|
@ -114,15 +232,19 @@ nv50_dp_mode_valid(struct drm_connector *connector,
|
|||
unsigned *out_clock)
|
||||
{
|
||||
const unsigned min_clock = 25000;
|
||||
unsigned max_clock, clock;
|
||||
unsigned max_clock, ds_clock, clock;
|
||||
enum drm_mode_status ret;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
max_clock = outp->dp.link_nr * outp->dp.link_bw;
|
||||
clock = mode->clock * (connector->display_info.bpc * 3) / 10;
|
||||
ds_clock = drm_dp_downstream_max_clock(outp->dp.dpcd,
|
||||
outp->dp.downstream_ports);
|
||||
if (ds_clock)
|
||||
max_clock = min(max_clock, ds_clock);
|
||||
|
||||
clock = mode->clock * (connector->display_info.bpc * 3) / 10;
|
||||
ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock,
|
||||
&clock);
|
||||
if (out_clock)
|
||||
|
|
|
@ -953,7 +953,7 @@ nouveau_pmops_resume(struct device *dev)
|
|||
ret = nouveau_do_resume(drm_dev, false);
|
||||
|
||||
/* Monitors may have been connected / disconnected during suspend */
|
||||
schedule_work(&nouveau_drm(drm_dev)->hpd_work);
|
||||
nouveau_display_hpd_resume(drm_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1036,7 +1036,7 @@ nouveau_pmops_runtime_resume(struct device *dev)
|
|||
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
|
||||
|
||||
/* Monitors may have been connected / disconnected during suspend */
|
||||
schedule_work(&nouveau_drm(drm_dev)->hpd_work);
|
||||
nouveau_display_hpd_resume(drm_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -198,6 +198,8 @@ struct nouveau_drm {
|
|||
struct nvbios vbios;
|
||||
struct nouveau_display *display;
|
||||
struct work_struct hpd_work;
|
||||
struct mutex hpd_lock;
|
||||
u32 hpd_pending;
|
||||
struct work_struct fbcon_work;
|
||||
int fbcon_new_state;
|
||||
#ifdef CONFIG_ACPI
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <drm/drm_dp_mst_helper.h>
|
||||
#include "dispnv04/disp.h"
|
||||
struct nv50_head_atom;
|
||||
struct nouveau_connector;
|
||||
|
||||
#define NV_DPMS_CLEARED 0x80
|
||||
|
||||
|
@ -64,6 +65,17 @@ struct nouveau_encoder {
|
|||
struct nv50_mstm *mstm;
|
||||
int link_nr;
|
||||
int link_bw;
|
||||
|
||||
/* Protects DP state that needs to be accessed outside
|
||||
* connector reprobing contexts
|
||||
*/
|
||||
struct mutex hpd_irq_lock;
|
||||
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
|
||||
struct drm_dp_desc desc;
|
||||
|
||||
u8 sink_count;
|
||||
} dp;
|
||||
};
|
||||
|
||||
|
@ -77,6 +89,21 @@ struct nouveau_encoder {
|
|||
struct nv50_head_atom *, u8 proto, u8 depth);
|
||||
};
|
||||
|
||||
struct nv50_mstm {
|
||||
struct nouveau_encoder *outp;
|
||||
|
||||
struct drm_dp_mst_topology_mgr mgr;
|
||||
|
||||
/* Protected under nouveau_encoder->dp.hpd_irq_lock */
|
||||
bool can_mst;
|
||||
bool is_mst;
|
||||
bool suspended;
|
||||
|
||||
bool modified;
|
||||
bool disabled;
|
||||
int links;
|
||||
};
|
||||
|
||||
struct nouveau_encoder *
|
||||
find_encoder(struct drm_connector *connector, int type);
|
||||
|
||||
|
@ -100,20 +127,29 @@ get_slave_funcs(struct drm_encoder *enc)
|
|||
|
||||
/* nouveau_dp.c */
|
||||
enum nouveau_dp_status {
|
||||
NOUVEAU_DP_NONE,
|
||||
NOUVEAU_DP_SST,
|
||||
NOUVEAU_DP_MST,
|
||||
};
|
||||
|
||||
int nouveau_dp_detect(struct nouveau_encoder *);
|
||||
int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *);
|
||||
void nouveau_dp_irq(struct nouveau_drm *drm,
|
||||
struct nouveau_connector *nv_connector);
|
||||
enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *,
|
||||
struct nouveau_encoder *,
|
||||
const struct drm_display_mode *,
|
||||
unsigned *clock);
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
||||
nv50_outp_get_new_connector(struct nouveau_encoder *outp,
|
||||
struct drm_atomic_state *state);
|
||||
struct nouveau_connector *
|
||||
nv50_outp_get_old_connector(struct nouveau_encoder *outp,
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
int nv50_mstm_detect(struct nv50_mstm *, u8 dpcd[8], int allow);
|
||||
void nv50_mstm_remove(struct nv50_mstm *);
|
||||
void nv50_mstm_service(struct nv50_mstm *);
|
||||
int nv50_mstm_detect(struct nouveau_encoder *encoder);
|
||||
void nv50_mstm_remove(struct nv50_mstm *mstm);
|
||||
bool nv50_mstm_service(struct nouveau_drm *drm,
|
||||
struct nouveau_connector *nv_connector,
|
||||
struct nv50_mstm *mstm);
|
||||
#endif /* __NOUVEAU_ENCODER_H__ */
|
||||
|
|
|
@ -552,8 +552,7 @@ static int rad_panel_probe(struct mipi_dsi_device *dsi)
|
|||
panel->dsi = dsi;
|
||||
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
|
||||
MIPI_DSI_CLOCK_NON_CONTINUOUS;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO;
|
||||
|
||||
ret = of_property_read_u32(np, "video-mode", &video_mode);
|
||||
if (!ret) {
|
||||
|
|
|
@ -659,6 +659,32 @@ static void panel_simple_shutdown(struct device *dev)
|
|||
drm_panel_unprepare(&panel->base);
|
||||
}
|
||||
|
||||
static const struct drm_display_mode ampire_am_1280800n3tzqw_t00h_mode = {
|
||||
.clock = 71100,
|
||||
.hdisplay = 1280,
|
||||
.hsync_start = 1280 + 40,
|
||||
.hsync_end = 1280 + 40 + 80,
|
||||
.htotal = 1280 + 40 + 80 + 40,
|
||||
.vdisplay = 800,
|
||||
.vsync_start = 800 + 3,
|
||||
.vsync_end = 800 + 3 + 10,
|
||||
.vtotal = 800 + 3 + 10 + 10,
|
||||
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
|
||||
};
|
||||
|
||||
static const struct panel_desc ampire_am_1280800n3tzqw_t00h = {
|
||||
.modes = &ire_am_1280800n3tzqw_t00h_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 217,
|
||||
.height = 136,
|
||||
},
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
|
||||
.clock = 9000,
|
||||
.hdisplay = 480,
|
||||
|
@ -3052,7 +3078,7 @@ static const struct drm_display_mode ortustech_com43h4m85ulc_mode = {
|
|||
static const struct panel_desc ortustech_com43h4m85ulc = {
|
||||
.modes = &ortustech_com43h4m85ulc_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 8,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 56,
|
||||
.height = 93,
|
||||
|
@ -3875,6 +3901,9 @@ static const struct panel_desc arm_rtsm = {
|
|||
|
||||
static const struct of_device_id platform_of_match[] = {
|
||||
{
|
||||
.compatible = "ampire,am-1280800n3tzqw-t00h",
|
||||
.data = &ire_am_1280800n3tzqw_t00h,
|
||||
}, {
|
||||
.compatible = "ampire,am-480272h3tmqw-t01h",
|
||||
.data = &ire_am_480272h3tmqw_t01h,
|
||||
}, {
|
||||
|
|
|
@ -422,7 +422,6 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
|
|||
*/
|
||||
struct radeon_ttm_tt {
|
||||
struct ttm_dma_tt ttm;
|
||||
struct radeon_device *rdev;
|
||||
u64 offset;
|
||||
|
||||
uint64_t userptr;
|
||||
|
@ -525,6 +524,7 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
|
|||
struct ttm_resource *bo_mem)
|
||||
{
|
||||
struct radeon_ttm_tt *gtt = (void*)ttm;
|
||||
struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
|
||||
uint32_t flags = RADEON_GART_PAGE_VALID | RADEON_GART_PAGE_READ |
|
||||
RADEON_GART_PAGE_WRITE;
|
||||
int r;
|
||||
|
@ -541,7 +541,7 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
|
|||
}
|
||||
if (ttm->caching_state == tt_cached)
|
||||
flags |= RADEON_GART_PAGE_SNOOP;
|
||||
r = radeon_gart_bind(gtt->rdev, gtt->offset, ttm->num_pages,
|
||||
r = radeon_gart_bind(rdev, gtt->offset, ttm->num_pages,
|
||||
ttm->pages, gtt->ttm.dma_address, flags);
|
||||
if (r) {
|
||||
DRM_ERROR("failed to bind %lu pages at 0x%08X\n",
|
||||
|
@ -554,8 +554,9 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
|
|||
static void radeon_ttm_backend_unbind(struct ttm_tt *ttm)
|
||||
{
|
||||
struct radeon_ttm_tt *gtt = (void *)ttm;
|
||||
struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
|
||||
|
||||
radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages);
|
||||
radeon_gart_unbind(rdev, gtt->offset, ttm->num_pages);
|
||||
|
||||
if (gtt->userptr)
|
||||
radeon_ttm_tt_unpin_userptr(ttm);
|
||||
|
@ -594,7 +595,6 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
|
|||
return NULL;
|
||||
}
|
||||
gtt->ttm.ttm.func = &radeon_backend_func;
|
||||
gtt->rdev = rdev;
|
||||
if (ttm_dma_tt_init(>t->ttm, bo, page_flags)) {
|
||||
kfree(gtt);
|
||||
return NULL;
|
||||
|
|
|
@ -474,9 +474,7 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
|
|||
SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
|
||||
SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
|
||||
|
||||
reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
|
||||
SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
|
||||
SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
|
||||
reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0;
|
||||
if (sun4i_tcon_get_pixel_depth(encoder) == 24)
|
||||
reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
|
||||
else
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_VIRTIO_GPU
|
||||
tristate "Virtio GPU driver"
|
||||
depends on DRM && VIRTIO && MMU
|
||||
depends on DRM && VIRTIO && VIRTIO_MENU && MMU
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
select VIRTIO_DMA_SHARED_BUFFER
|
||||
|
|
|
@ -32,8 +32,6 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
|
|||
src_offset = composer->offset
|
||||
+ (i * composer->pitch)
|
||||
+ (j * composer->cpp);
|
||||
/* XRGB format ignores Alpha channel */
|
||||
bitmap_clear(vaddr_out + src_offset, 24, 8);
|
||||
crc = crc32_le(crc, vaddr_out + src_offset,
|
||||
sizeof(u32));
|
||||
}
|
||||
|
@ -42,27 +40,51 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
|
|||
return crc;
|
||||
}
|
||||
|
||||
static u8 blend_channel(u8 src, u8 dst, u8 alpha)
|
||||
{
|
||||
u32 pre_blend;
|
||||
u8 new_color;
|
||||
|
||||
pre_blend = (src * 255 + dst * (255 - alpha));
|
||||
|
||||
/* Faster div by 255 */
|
||||
new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
|
||||
|
||||
return new_color;
|
||||
}
|
||||
|
||||
static void alpha_blending(const u8 *argb_src, u8 *argb_dst)
|
||||
{
|
||||
u8 alpha;
|
||||
|
||||
alpha = argb_src[3];
|
||||
argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
|
||||
argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
|
||||
argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
|
||||
/* Opaque primary */
|
||||
argb_dst[3] = 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* blend - blend value at vaddr_src with value at vaddr_dst
|
||||
* @vaddr_dst: destination address
|
||||
* @vaddr_src: source address
|
||||
* @dest_composer: destination framebuffer's metadata
|
||||
* @dst_composer: destination framebuffer's metadata
|
||||
* @src_composer: source framebuffer's metadata
|
||||
*
|
||||
* Blend value at vaddr_src with value at vaddr_dst.
|
||||
* Currently, this function write value of vaddr_src on value
|
||||
* at vaddr_dst using buffer's metadata to locate the new values
|
||||
* from vaddr_src and their destination at vaddr_dst.
|
||||
*
|
||||
* TODO: Use the alpha value to blend vaddr_src with vaddr_dst
|
||||
* instead of overwriting it.
|
||||
* Blend the vaddr_src value with the vaddr_dst value using the pre-multiplied
|
||||
* alpha blending equation, since DRM currently assumes that the pixel color
|
||||
* values have already been pre-multiplied with the alpha channel values. See
|
||||
* more drm_plane_create_blend_mode_property(). This function uses buffer's
|
||||
* metadata to locate the new composite values at vaddr_dst.
|
||||
*/
|
||||
static void blend(void *vaddr_dst, void *vaddr_src,
|
||||
struct vkms_composer *dest_composer,
|
||||
struct vkms_composer *dst_composer,
|
||||
struct vkms_composer *src_composer)
|
||||
{
|
||||
int i, j, j_dst, i_dst;
|
||||
int offset_src, offset_dst;
|
||||
u8 *pixel_dst, *pixel_src;
|
||||
|
||||
int x_src = src_composer->src.x1 >> 16;
|
||||
int y_src = src_composer->src.y1 >> 16;
|
||||
|
@ -77,15 +99,16 @@ static void blend(void *vaddr_dst, void *vaddr_src,
|
|||
|
||||
for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
|
||||
for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
|
||||
offset_dst = dest_composer->offset
|
||||
+ (i_dst * dest_composer->pitch)
|
||||
+ (j_dst++ * dest_composer->cpp);
|
||||
offset_dst = dst_composer->offset
|
||||
+ (i_dst * dst_composer->pitch)
|
||||
+ (j_dst++ * dst_composer->cpp);
|
||||
offset_src = src_composer->offset
|
||||
+ (i * src_composer->pitch)
|
||||
+ (j * src_composer->cpp);
|
||||
|
||||
memcpy(vaddr_dst + offset_dst,
|
||||
vaddr_src + offset_src, sizeof(u32));
|
||||
pixel_src = (u8 *)(vaddr_src + offset_src);
|
||||
pixel_dst = (u8 *)(vaddr_dst + offset_dst);
|
||||
alpha_blending(pixel_src, pixel_dst);
|
||||
}
|
||||
i_dst++;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,11 @@ static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
|
|||
struct vkms_output *output = &vkmsdev->output;
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
|
||||
if (!READ_ONCE(vblank->enabled)) {
|
||||
*vblank_time = ktime_get();
|
||||
return true;
|
||||
}
|
||||
|
||||
*vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
|
||||
|
||||
if (WARN_ON(*vblank_time == vblank->time))
|
||||
|
|
|
@ -1607,12 +1607,18 @@ static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
|
|||
return drm_dp_dpcd_write(aux, offset, &value, 1);
|
||||
}
|
||||
|
||||
int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE]);
|
||||
|
||||
int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
|
||||
u8 status[DP_LINK_STATUS_SIZE]);
|
||||
|
||||
bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
|
||||
u8 real_edid_checksum);
|
||||
|
||||
int drm_dp_read_downstream_info(struct drm_dp_aux *aux,
|
||||
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]);
|
||||
int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const u8 port_cap[4]);
|
||||
int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
|
@ -1628,6 +1634,12 @@ void drm_dp_set_subconnector_property(struct drm_connector *connector,
|
|||
const u8 *dpcd,
|
||||
const u8 port_cap[4]);
|
||||
|
||||
struct drm_dp_desc;
|
||||
bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
|
||||
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
|
||||
const struct drm_dp_desc *desc);
|
||||
int drm_dp_read_sink_count(struct drm_dp_aux *aux);
|
||||
|
||||
void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
|
||||
void drm_dp_aux_init(struct drm_dp_aux *aux);
|
||||
int drm_dp_aux_register(struct drm_dp_aux *aux);
|
||||
|
@ -1686,7 +1698,8 @@ enum drm_dp_quirk {
|
|||
* @DP_DPCD_QUIRK_NO_SINK_COUNT:
|
||||
*
|
||||
* The device does not set SINK_COUNT to a non-zero value.
|
||||
* The driver should ignore SINK_COUNT during detection.
|
||||
* The driver should ignore SINK_COUNT during detection. Note that
|
||||
* drm_dp_read_sink_count_cap() automatically checks for this quirk.
|
||||
*/
|
||||
DP_DPCD_QUIRK_NO_SINK_COUNT,
|
||||
/**
|
||||
|
|
|
@ -728,10 +728,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
|
|||
|
||||
void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr);
|
||||
|
||||
|
||||
bool drm_dp_read_mst_cap(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
|
||||
int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state);
|
||||
|
||||
|
||||
int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled);
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче