From 6778948f9de86c3cfaf36725a7c87dcff9ba247f Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 08:20:59 +0000
Subject: [PATCH] kernel_5.10 no rt
---
kernel/drivers/gpu/drm/nouveau/nouveau_connector.c | 293 +++++++++++++++++++++++++++++++++-------------------------
1 files changed, 167 insertions(+), 126 deletions(-)
diff --git a/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c
index b71afde..9542fc6 100644
--- a/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/kernel/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -29,15 +29,16 @@
#include <linux/pm_runtime.h>
#include <linux/vga_switcheroo.h>
-#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
#include <drm/drm_atomic.h>
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "dispnv04/hw.h"
+#include "dispnv50/disp.h"
#include "nouveau_acpi.h"
#include "nouveau_display.h"
@@ -59,7 +60,6 @@
int high_w = 0, high_h = 0, high_v = 0;
list_for_each_entry(mode, &connector->probed_modes, head) {
- mode->vrefresh = drm_mode_vrefresh(mode);
if (helper->mode_valid(connector, mode) != MODE_OK ||
(mode->flags & DRM_MODE_FLAG_INTERLACE))
continue;
@@ -80,12 +80,12 @@
continue;
if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
- mode->vrefresh < high_v)
+ drm_mode_vrefresh(mode) < high_v)
continue;
high_w = mode->hdisplay;
high_h = mode->vdisplay;
- high_v = mode->vrefresh;
+ high_v = drm_mode_vrefresh(mode);
largest = mode;
}
@@ -245,14 +245,22 @@
void
nouveau_conn_reset(struct drm_connector *connector)
{
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_conn_atom *asyc;
- if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL))))
- return;
+ if (drm_drv_uses_atomic_modeset(connector->dev)) {
+ if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL))))
+ return;
- if (connector->state)
- nouveau_conn_atomic_destroy_state(connector, connector->state);
- __drm_atomic_helper_connector_reset(connector, &asyc->state);
+ if (connector->state)
+ nouveau_conn_atomic_destroy_state(connector,
+ connector->state);
+
+ __drm_atomic_helper_connector_reset(connector, &asyc->state);
+ } else {
+ asyc = &nv_connector->properties_state;
+ }
+
asyc->dither.mode = DITHERING_MODE_AUTO;
asyc->dither.depth = DITHERING_DEPTH_AUTO;
asyc->scaler.mode = DRM_MODE_SCALE_NONE;
@@ -276,8 +284,14 @@
nouveau_conn_attach_properties(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
- struct nouveau_conn_atom *armc = nouveau_conn_atom(connector->state);
struct nouveau_display *disp = nouveau_display(dev);
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_conn_atom *armc;
+
+ if (drm_drv_uses_atomic_modeset(connector->dev))
+ armc = nouveau_conn_atom(connector->state);
+ else
+ armc = &nv_connector->properties_state;
/* Init DVI-I specific properties. */
if (connector->connector_type == DRM_MODE_CONNECTOR_DVII)
@@ -316,7 +330,7 @@
case DRM_MODE_CONNECTOR_VGA:
if (disp->disp.object.oclass < NV50_DISP)
break; /* Can only scale on DFPs. */
- /* Fall-through. */
+ fallthrough;
default:
drm_object_attach_property(&connector->base, dev->mode_config.
scaling_mode_property,
@@ -365,9 +379,8 @@
{
struct nouveau_encoder *nv_encoder;
struct drm_encoder *enc;
- int i;
- drm_connector_for_each_possible_encoder(connector, enc, i) {
+ drm_connector_for_each_possible_encoder(connector, enc) {
nv_encoder = nouveau_encoder(enc);
if (type == DCB_OUTPUT_ANY ||
@@ -378,30 +391,19 @@
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)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
- nvif_notify_fini(&nv_connector->hpd);
+ nvif_notify_dtor(&nv_connector->hpd);
kfree(nv_connector->edid);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
- if (nv_connector->aux.transfer)
+ if (nv_connector->aux.transfer) {
+ drm_dp_cec_unregister_connector(&nv_connector->aux);
drm_dp_aux_unregister(&nv_connector->aux);
+ kfree(nv_connector->aux.name);
+ }
kfree(connector);
}
@@ -411,15 +413,16 @@
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder = NULL, *found = NULL;
struct drm_encoder *encoder;
- int i, ret;
+ int ret;
bool switcheroo_ddc = false;
- drm_connector_for_each_possible_encoder(connector, encoder, i) {
+ drm_connector_for_each_possible_encoder(connector, encoder) {
nv_encoder = nouveau_encoder(encoder);
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)
@@ -429,7 +432,7 @@
case DCB_OUTPUT_LVDS:
switcheroo_ddc = !!(vga_switcheroo_handler_flags() &
VGA_SWITCHEROO_CAN_SWITCH_DDC);
- /* fall-through */
+ fallthrough;
default:
if (!nv_encoder->i2c)
break;
@@ -493,7 +496,12 @@
nv_connector->detected_encoder = nv_encoder;
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
- connector->interlace_allowed = true;
+ if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
+ connector->interlace_allowed =
+ nv_encoder->caps.dp_interlace;
+ else
+ connector->interlace_allowed =
+ drm->client.device.info.family < NV_DEVICE_INFO_V0_VOLTA;
connector->doublescan_allowed = true;
} else
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS ||
@@ -521,6 +529,19 @@
}
}
+static void
+nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
+ struct edid *edid)
+{
+ if (nv_connector->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)
{
@@ -534,13 +555,6 @@
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
@@ -553,22 +567,23 @@
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);
@@ -600,7 +615,10 @@
nouveau_connector_set_encoder(connector, nv_encoder);
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);
@@ -625,10 +643,11 @@
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);
@@ -643,24 +662,20 @@
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) {
status = nouveau_connector_detect(connector, force);
- if (status == connector_status_connected)
+ if (status == connector_status_connected) {
+ edid = nv_connector->edid;
goto out;
+ }
}
/* On some laptops (Sony, i'm looking at you) there appears to
@@ -673,7 +688,8 @@
* 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;
}
@@ -693,12 +709,10 @@
* 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;
}
}
@@ -711,7 +725,7 @@
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;
}
@@ -747,9 +761,9 @@
nouveau_connector_set_property(struct drm_connector *connector,
struct drm_property *property, uint64_t value)
{
- struct nouveau_conn_atom *asyc = nouveau_conn_atom(connector->state);
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct nouveau_conn_atom *asyc = &nv_connector->properties_state;
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
int ret;
@@ -885,6 +899,22 @@
}
static int
+nouveau_connector_late_register(struct drm_connector *connector)
+{
+ int ret;
+
+ ret = nouveau_backlight_init(connector);
+
+ return ret;
+}
+
+static void
+nouveau_connector_early_unregister(struct drm_connector *connector)
+{
+ nouveau_backlight_fini(connector);
+}
+
+static int
nouveau_connector_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -952,32 +982,50 @@
}
static unsigned
-get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
+get_tmds_link_bandwidth(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct nouveau_drm *drm = nouveau_drm(connector->dev);
struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
+ struct drm_display_info *info = NULL;
+ unsigned duallink_scale =
+ nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1;
- if (hdmi) {
+ if (drm_detect_hdmi_monitor(nv_connector->edid)) {
+ info = &nv_connector->base.display_info;
+ duallink_scale = 1;
+ }
+
+ if (info) {
if (nouveau_hdmimhz > 0)
return nouveau_hdmimhz * 1000;
/* Note: these limits are conservative, some Fermi's
* can do 297 MHz. Unclear how this can be determined.
*/
+ if (drm->client.device.info.chipset >= 0x120) {
+ const int max_tmds_clock =
+ info->hdmi.scdc.scrambling.supported ?
+ 594000 : 340000;
+ return info->max_tmds_clock ?
+ min(info->max_tmds_clock, max_tmds_clock) :
+ max_tmds_clock;
+ }
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
return 297000;
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
return 225000;
}
+
if (dcb->location != DCB_LOC_ON_CHIP ||
drm->client.device.info.chipset >= 0x46)
- return 165000;
+ return 165000 * duallink_scale;
else if (drm->client.device.info.chipset >= 0x40)
- return 155000;
+ return 155000 * duallink_scale;
else if (drm->client.device.info.chipset >= 0x18)
- return 135000;
+ return 135000 * duallink_scale;
else
- return 112000;
+ return 112000 * duallink_scale;
}
static enum drm_mode_status
@@ -987,9 +1035,7 @@
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
- unsigned min_clock = 25000, max_clock = min_clock;
- unsigned clock = mode->clock;
- bool hdmi;
+ unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock;
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_LVDS:
@@ -1002,11 +1048,7 @@
max_clock = 400000;
break;
case DCB_OUTPUT_TMDS:
- hdmi = drm_detect_hdmi_monitor(nv_connector->edid);
- max_clock = get_tmds_link_bandwidth(connector, hdmi);
- if (!hdmi && nouveau_duallink &&
- nv_encoder->dcb->duallink_possible)
- max_clock *= 2;
+ max_clock = get_tmds_link_bandwidth(connector);
break;
case DCB_OUTPUT_ANALOG:
max_clock = nv_encoder->dcb->crtconf.maxfreq;
@@ -1016,10 +1058,7 @@
case DCB_OUTPUT_TV:
return get_slave_funcs(encoder)->mode_valid(encoder, mode);
case DCB_OUTPUT_DP:
- max_clock = nv_encoder->dp.link_nr;
- max_clock *= nv_encoder->dp.link_bw;
- clock = clock * (connector->display_info.bpc * 3) / 10;
- break;
+ return nv50_dp_mode_valid(connector, nv_encoder, mode, NULL);
default:
BUG();
return MODE_BAD;
@@ -1030,7 +1069,6 @@
if (clock < min_clock)
return MODE_CLOCK_LOW;
-
if (clock > max_clock)
return MODE_CLOCK_HIGH;
@@ -1068,6 +1106,8 @@
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
.atomic_set_property = nouveau_conn_atomic_set_property,
.atomic_get_property = nouveau_conn_atomic_get_property,
+ .late_register = nouveau_connector_late_register,
+ .early_unregister = nouveau_connector_early_unregister,
};
static const struct drm_connector_funcs
@@ -1083,7 +1123,23 @@
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
.atomic_set_property = nouveau_conn_atomic_set_property,
.atomic_get_property = nouveau_conn_atomic_get_property,
+ .late_register = nouveau_connector_late_register,
+ .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)
@@ -1091,49 +1147,19 @@
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;
-
- 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);
-
- 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;
- }
+ bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
- NV_DEBUG(drm, "service %s\n", name);
- if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
- nv50_mstm_service(nv_encoder->dp.mstm);
- } else {
- bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
-
- 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);
+ nouveau_dp_irq(drm, nv_connector);
+ return NVIF_NOTIFY_KEEP;
}
- pm_runtime_mark_last_busy(drm->dev->dev);
- pm_runtime_put_autosuspend(drm->dev->dev);
+ NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", connector->name);
+ nouveau_connector_hpd(connector);
+
return NVIF_NOTIFY_KEEP;
}
@@ -1184,7 +1210,9 @@
case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
case DCB_CONNECTOR_DMS59_DP0:
case DCB_CONNECTOR_DMS59_DP1:
- case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort;
+ case DCB_CONNECTOR_DP :
+ case DCB_CONNECTOR_mDP :
+ case DCB_CONNECTOR_USB_C : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 :
case DCB_CONNECTOR_HDMI_1 :
@@ -1198,7 +1226,8 @@
}
struct drm_connector *
-nouveau_connector_create(struct drm_device *dev, int index)
+nouveau_connector_create(struct drm_device *dev,
+ const struct dcb_output *dcbe)
{
const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
struct nouveau_drm *drm = nouveau_drm(dev);
@@ -1206,6 +1235,8 @@
struct nouveau_connector *nv_connector = NULL;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
+ char aux_name[48] = {0};
+ int index = dcbe->connector;
int type, ret = 0;
bool dummy;
@@ -1306,15 +1337,17 @@
break;
case DRM_MODE_CONNECTOR_DisplayPort:
case DRM_MODE_CONNECTOR_eDP:
- nv_connector->aux.dev = dev->dev;
+ nv_connector->aux.dev = connector->kdev;
nv_connector->aux.transfer = nouveau_connector_aux_xfer;
+ snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x",
+ dcbe->hasht, dcbe->hashm);
+ nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL);
ret = drm_dp_aux_register(&nv_connector->aux);
if (ret) {
NV_ERROR(drm, "failed to register aux channel\n");
kfree(nv_connector);
return ERR_PTR(ret);
}
-
funcs = &nouveau_connector_funcs;
break;
default:
@@ -1368,7 +1401,15 @@
break;
}
- ret = nvif_notify_init(&disp->disp.object, nouveau_connector_hotplug,
+ switch (type) {
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_eDP:
+ drm_dp_cec_register_connector(&nv_connector->aux, connector);
+ break;
+ }
+
+ ret = nvif_notify_ctor(&disp->disp.object, "kmsHotplug",
+ nouveau_connector_hotplug,
true, NV04_DISP_NTFY_CONN,
&(struct nvif_notify_conn_req_v0) {
.mask = NVIF_NOTIFY_CONN_V0_ANY,
--
Gitblit v1.6.2