cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nouveau_dp.c (7795B)


      1/*
      2 * Copyright 2009 Red Hat Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 * OTHER DEALINGS IN THE SOFTWARE.
     21 *
     22 * Authors: Ben Skeggs
     23 */
     24
     25#include <drm/display/drm_dp_helper.h>
     26
     27#include "nouveau_drv.h"
     28#include "nouveau_connector.h"
     29#include "nouveau_encoder.h"
     30#include "nouveau_crtc.h"
     31
     32#include <nvif/class.h>
     33#include <nvif/cl5070.h>
     34
     35MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)");
     36static int nouveau_mst = 1;
     37module_param_named(mst, nouveau_mst, int, 0400);
     38
     39static bool
     40nouveau_dp_has_sink_count(struct drm_connector *connector,
     41			  struct nouveau_encoder *outp)
     42{
     43	return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc);
     44}
     45
     46static enum drm_connector_status
     47nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
     48		      struct nouveau_encoder *outp)
     49{
     50	struct drm_connector *connector = &nv_connector->base;
     51	struct drm_dp_aux *aux = &nv_connector->aux;
     52	struct nv50_mstm *mstm = NULL;
     53	enum drm_connector_status status = connector_status_disconnected;
     54	int ret;
     55	u8 *dpcd = outp->dp.dpcd;
     56
     57	ret = drm_dp_read_dpcd_caps(aux, dpcd);
     58	if (ret < 0)
     59		goto out;
     60
     61	ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd));
     62	if (ret < 0)
     63		goto out;
     64
     65	if (nouveau_mst) {
     66		mstm = outp->dp.mstm;
     67		if (mstm)
     68			mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd);
     69	}
     70
     71	if (nouveau_dp_has_sink_count(connector, outp)) {
     72		ret = drm_dp_read_sink_count(aux);
     73		if (ret < 0)
     74			goto out;
     75
     76		outp->dp.sink_count = ret;
     77
     78		/*
     79		 * Dongle connected, but no display. Don't bother reading
     80		 * downstream port info
     81		 */
     82		if (!outp->dp.sink_count)
     83			return connector_status_disconnected;
     84	}
     85
     86	ret = drm_dp_read_downstream_info(aux, dpcd,
     87					  outp->dp.downstream_ports);
     88	if (ret < 0)
     89		goto out;
     90
     91	status = connector_status_connected;
     92out:
     93	if (status != connector_status_connected) {
     94		/* Clear any cached info */
     95		outp->dp.sink_count = 0;
     96	}
     97	return status;
     98}
     99
    100int
    101nouveau_dp_detect(struct nouveau_connector *nv_connector,
    102		  struct nouveau_encoder *nv_encoder)
    103{
    104	struct drm_device *dev = nv_encoder->base.base.dev;
    105	struct nouveau_drm *drm = nouveau_drm(dev);
    106	struct drm_connector *connector = &nv_connector->base;
    107	struct nv50_mstm *mstm = nv_encoder->dp.mstm;
    108	enum drm_connector_status status;
    109	u8 *dpcd = nv_encoder->dp.dpcd;
    110	int ret = NOUVEAU_DP_NONE;
    111
    112	/* If we've already read the DPCD on an eDP device, we don't need to
    113	 * reread it as it won't change
    114	 */
    115	if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
    116	    dpcd[DP_DPCD_REV] != 0)
    117		return NOUVEAU_DP_SST;
    118
    119	mutex_lock(&nv_encoder->dp.hpd_irq_lock);
    120	if (mstm) {
    121		/* If we're not ready to handle MST state changes yet, just
    122		 * report the last status of the connector. We'll reprobe it
    123		 * once we've resumed.
    124		 */
    125		if (mstm->suspended) {
    126			if (mstm->is_mst)
    127				ret = NOUVEAU_DP_MST;
    128			else if (connector->status ==
    129				 connector_status_connected)
    130				ret = NOUVEAU_DP_SST;
    131
    132			goto out;
    133		}
    134	}
    135
    136	status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder);
    137	if (status == connector_status_disconnected)
    138		goto out;
    139
    140	/* If we're in MST mode, we're done here */
    141	if (mstm && mstm->can_mst && mstm->is_mst) {
    142		ret = NOUVEAU_DP_MST;
    143		goto out;
    144	}
    145
    146	nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE];
    147	nv_encoder->dp.link_nr =
    148		dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
    149
    150	if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) {
    151		struct drm_dp_aux *aux = &nv_connector->aux;
    152		int ret, i;
    153		u8 sink_rates[16];
    154
    155		ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, sink_rates, sizeof(sink_rates));
    156		if (ret == sizeof(sink_rates)) {
    157			for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) {
    158				int val = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10;
    159				if (val && (i == 0 || val > nv_encoder->dp.link_bw))
    160					nv_encoder->dp.link_bw = val;
    161			}
    162		}
    163	}
    164
    165	NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n",
    166		 nv_encoder->dp.link_nr, nv_encoder->dp.link_bw,
    167		 dpcd[DP_DPCD_REV]);
    168	NV_DEBUG(drm, "encoder: %dx%d\n",
    169		 nv_encoder->dcb->dpconf.link_nr,
    170		 nv_encoder->dcb->dpconf.link_bw);
    171
    172	if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr)
    173		nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
    174	if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw)
    175		nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw;
    176
    177	NV_DEBUG(drm, "maximum: %dx%d\n",
    178		 nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
    179
    180	if (mstm && mstm->can_mst) {
    181		ret = nv50_mstm_detect(nv_encoder);
    182		if (ret == 1) {
    183			ret = NOUVEAU_DP_MST;
    184			goto out;
    185		} else if (ret != 0) {
    186			goto out;
    187		}
    188	}
    189	ret = NOUVEAU_DP_SST;
    190
    191out:
    192	if (mstm && !mstm->suspended && ret != NOUVEAU_DP_MST)
    193		nv50_mstm_remove(mstm);
    194
    195	mutex_unlock(&nv_encoder->dp.hpd_irq_lock);
    196	return ret;
    197}
    198
    199void nouveau_dp_irq(struct nouveau_drm *drm,
    200		    struct nouveau_connector *nv_connector)
    201{
    202	struct drm_connector *connector = &nv_connector->base;
    203	struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
    204	struct nv50_mstm *mstm;
    205	int ret;
    206	bool send_hpd = false;
    207
    208	if (!outp)
    209		return;
    210
    211	mstm = outp->dp.mstm;
    212	NV_DEBUG(drm, "service %s\n", connector->name);
    213
    214	mutex_lock(&outp->dp.hpd_irq_lock);
    215
    216	if (mstm && mstm->is_mst) {
    217		if (!nv50_mstm_service(drm, nv_connector, mstm))
    218			send_hpd = true;
    219	} else {
    220		drm_dp_cec_irq(&nv_connector->aux);
    221
    222		if (nouveau_dp_has_sink_count(connector, outp)) {
    223			ret = drm_dp_read_sink_count(&nv_connector->aux);
    224			if (ret != outp->dp.sink_count)
    225				send_hpd = true;
    226			if (ret >= 0)
    227				outp->dp.sink_count = ret;
    228		}
    229	}
    230
    231	mutex_unlock(&outp->dp.hpd_irq_lock);
    232
    233	if (send_hpd)
    234		nouveau_connector_hpd(connector);
    235}
    236
    237/* TODO:
    238 * - Use the minimum possible BPC here, once we add support for the max bpc
    239 *   property.
    240 * - Validate against the DP caps advertised by the GPU (we don't check these
    241 *   yet)
    242 */
    243enum drm_mode_status
    244nv50_dp_mode_valid(struct drm_connector *connector,
    245		   struct nouveau_encoder *outp,
    246		   const struct drm_display_mode *mode,
    247		   unsigned *out_clock)
    248{
    249	const unsigned int min_clock = 25000;
    250	unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock;
    251	const u8 bpp = connector->display_info.bpc * 3;
    252
    253	if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace)
    254		return MODE_NO_INTERLACE;
    255
    256	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
    257		clock *= 2;
    258
    259	max_rate = outp->dp.link_nr * outp->dp.link_bw;
    260	mode_rate = DIV_ROUND_UP(clock * bpp, 8);
    261	if (mode_rate > max_rate)
    262		return MODE_CLOCK_HIGH;
    263
    264	ds_max_dotclock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, outp->dp.downstream_ports);
    265	if (ds_max_dotclock && clock > ds_max_dotclock)
    266		return MODE_CLOCK_HIGH;
    267
    268	if (clock < min_clock)
    269		return MODE_CLOCK_LOW;
    270
    271	if (out_clock)
    272		*out_clock = clock;
    273
    274	return MODE_OK;
    275}