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:
Dave Airlie 2020-09-09 12:27:12 +10:00
commit 877d8c0743
33 changed files with 864 additions and 422 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = &ampire_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 = &ampire_am_1280800n3tzqw_t00h,
}, {
.compatible = "ampire,am-480272h3tmqw-t01h",
.data = &ampire_am_480272h3tmqw_t01h,
}, {

View File

@ -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(&gtt->ttm, bo, page_flags)) {
kfree(gtt);
return NULL;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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