2009-12-11 17:24:15 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Maarten Maathuis.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the
|
|
|
|
* next paragraph) shall be included in all copies or substantial
|
|
|
|
* portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
|
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-01-18 09:42:37 +08:00
|
|
|
#include <acpi/button.h>
|
|
|
|
|
2012-09-10 12:20:51 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
drm/nouveau: Switch DDC when reading the EDID
The pre-retina MacBook Pro uses an LVDS panel and a gmux controller
to switch the panel between its two GPUs. The panel mode in VBIOS
is notoriously bogus on these machines.
Use drm_get_edid_switcheroo() in lieu of drm_get_edid() on LVDS
if the vga_switcheroo handler is capable of temporarily switching
the panel's DDC lines to the discrete GPU. This allows us to retrieve
the EDID if the panel is currently muxed to the integrated GPU.
Likewise, ask vga_switcheroo to switch DDC before probing LVDS
connectors.
This only enables EDID probing on the pre-retina MBP (2008 - 2013).
The retina MBP (2012 - present) uses eDP and gmux is not capable of
switching AUX separately from the main link on these models.
This will be addressed in later patches.
List of pre-retina MBPs with dual GPUs, either or both Nvidia:
[MBP 5,1 2008 nvidia MCP79 + G96 pre-retina 15"]
[MBP 5,2 2009 nvidia MCP79 + G96 pre-retina 17"]
[MBP 5,3 2009 nvidia MCP79 + G96 pre-retina 15"]
[MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina 15"]
[MBP 6,1 2010 intel ILK + nvidia GT216 pre-retina 17"]
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
v3: Commit newly added due to introduction of drm_get_edid_switcheroo()
wrapper which drivers need to opt-in to.
v5: Rebase on "vga_switcheroo: Add handler flags infrastructure",
i.e. call drm_get_edid_switcheroo() only if the handler
indicates that DDC is switchable.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Lukas Wunner <lukas@wunner.de>
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/e9466eb3d66b5b30f1e93c3b3da79d8b9ad0830f.1452525860.git.lukas@wunner.de
2016-01-12 03:09:20 +08:00
|
|
|
#include <linux/vga_switcheroo.h>
|
2012-09-10 12:20:51 +08:00
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
#include <drm/drm_atomic_helper.h>
|
2012-10-03 01:01:07 +08:00
|
|
|
#include <drm/drm_edid.h>
|
|
|
|
#include <drm/drm_crtc_helper.h>
|
2019-01-18 05:03:34 +08:00
|
|
|
#include <drm/drm_probe_helper.h>
|
2016-12-22 16:50:42 +08:00
|
|
|
#include <drm/drm_atomic.h>
|
2010-01-18 09:42:37 +08:00
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
#include "nouveau_reg.h"
|
2016-05-20 07:22:55 +08:00
|
|
|
#include "nouveau_drv.h"
|
2013-03-21 13:45:11 +08:00
|
|
|
#include "dispnv04/hw.h"
|
2020-05-12 06:41:27 +08:00
|
|
|
#include "dispnv50/disp.h"
|
2012-07-26 06:51:21 +08:00
|
|
|
#include "nouveau_acpi.h"
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2012-07-31 14:16:21 +08:00
|
|
|
#include "nouveau_display.h"
|
|
|
|
#include "nouveau_connector.h"
|
2009-12-11 17:24:15 +08:00
|
|
|
#include "nouveau_encoder.h"
|
|
|
|
#include "nouveau_crtc.h"
|
2012-07-31 14:16:21 +08:00
|
|
|
|
2015-11-08 10:23:16 +08:00
|
|
|
#include <nvif/class.h>
|
2015-11-08 08:44:19 +08:00
|
|
|
#include <nvif/cl0046.h>
|
2014-08-10 02:10:20 +08:00
|
|
|
#include <nvif/event.h>
|
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
struct drm_display_mode *
|
|
|
|
nouveau_conn_native_mode(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
const struct drm_connector_helper_funcs *helper = connector->helper_private;
|
|
|
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct drm_display_mode *mode, *largest = NULL;
|
|
|
|
int high_w = 0, high_h = 0, high_v = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(mode, &connector->probed_modes, head) {
|
|
|
|
if (helper->mode_valid(connector, mode) != MODE_OK ||
|
|
|
|
(mode->flags & DRM_MODE_FLAG_INTERLACE))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Use preferred mode if there is one.. */
|
|
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
|
|
|
|
NV_DEBUG(drm, "native mode from preferred\n");
|
|
|
|
return drm_mode_duplicate(dev, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, take the resolution with the largest width, then
|
|
|
|
* height, then vertical refresh
|
|
|
|
*/
|
|
|
|
if (mode->hdisplay < high_w)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (mode->hdisplay == high_w && mode->vdisplay < high_h)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
|
2020-04-29 01:19:27 +08:00
|
|
|
drm_mode_vrefresh(mode) < high_v)
|
2016-11-04 15:20:35 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
high_w = mode->hdisplay;
|
|
|
|
high_h = mode->vdisplay;
|
2020-04-29 01:19:27 +08:00
|
|
|
high_v = drm_mode_vrefresh(mode);
|
2016-11-04 15:20:35 +08:00
|
|
|
largest = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
NV_DEBUG(drm, "native mode from largest: %dx%d@%d\n",
|
|
|
|
high_w, high_h, high_v);
|
|
|
|
return largest ? drm_mode_duplicate(dev, largest) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nouveau_conn_atomic_get_property(struct drm_connector *connector,
|
|
|
|
const struct drm_connector_state *state,
|
|
|
|
struct drm_property *property, u64 *val)
|
|
|
|
{
|
|
|
|
struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
|
|
|
|
struct nouveau_display *disp = nouveau_display(connector->dev);
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
|
|
|
|
if (property == dev->mode_config.scaling_mode_property)
|
|
|
|
*val = asyc->scaler.mode;
|
|
|
|
else if (property == disp->underscan_property)
|
|
|
|
*val = asyc->scaler.underscan.mode;
|
|
|
|
else if (property == disp->underscan_hborder_property)
|
|
|
|
*val = asyc->scaler.underscan.hborder;
|
|
|
|
else if (property == disp->underscan_vborder_property)
|
|
|
|
*val = asyc->scaler.underscan.vborder;
|
|
|
|
else if (property == disp->dithering_mode)
|
|
|
|
*val = asyc->dither.mode;
|
|
|
|
else if (property == disp->dithering_depth)
|
|
|
|
*val = asyc->dither.depth;
|
|
|
|
else if (property == disp->vibrant_hue_property)
|
|
|
|
*val = asyc->procamp.vibrant_hue;
|
|
|
|
else if (property == disp->color_vibrance_property)
|
|
|
|
*val = asyc->procamp.color_vibrance;
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nouveau_conn_atomic_set_property(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *state,
|
|
|
|
struct drm_property *property, u64 val)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
|
|
|
|
struct nouveau_display *disp = nouveau_display(dev);
|
|
|
|
|
|
|
|
if (property == dev->mode_config.scaling_mode_property) {
|
|
|
|
switch (val) {
|
|
|
|
case DRM_MODE_SCALE_NONE:
|
|
|
|
/* We allow 'None' for EDID modes, even on a fixed
|
|
|
|
* panel (some exist with support for lower refresh
|
|
|
|
* rates, which people might want to use for power-
|
|
|
|
* saving purposes).
|
|
|
|
*
|
|
|
|
* Non-EDID modes will force the use of GPU scaling
|
|
|
|
* to the native mode regardless of this setting.
|
|
|
|
*/
|
|
|
|
switch (connector->connector_type) {
|
|
|
|
case DRM_MODE_CONNECTOR_LVDS:
|
|
|
|
case DRM_MODE_CONNECTOR_eDP:
|
|
|
|
/* ... except prior to G80, where the code
|
|
|
|
* doesn't support such things.
|
|
|
|
*/
|
2018-05-08 18:39:47 +08:00
|
|
|
if (disp->disp.object.oclass < NV50_DISP)
|
2016-11-04 15:20:35 +08:00
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DRM_MODE_SCALE_FULLSCREEN:
|
|
|
|
case DRM_MODE_SCALE_CENTER:
|
|
|
|
case DRM_MODE_SCALE_ASPECT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asyc->scaler.mode != val) {
|
|
|
|
asyc->scaler.mode = val;
|
|
|
|
asyc->set.scaler = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->underscan_property) {
|
|
|
|
if (asyc->scaler.underscan.mode != val) {
|
|
|
|
asyc->scaler.underscan.mode = val;
|
|
|
|
asyc->set.scaler = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->underscan_hborder_property) {
|
|
|
|
if (asyc->scaler.underscan.hborder != val) {
|
|
|
|
asyc->scaler.underscan.hborder = val;
|
|
|
|
asyc->set.scaler = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->underscan_vborder_property) {
|
|
|
|
if (asyc->scaler.underscan.vborder != val) {
|
|
|
|
asyc->scaler.underscan.vborder = val;
|
|
|
|
asyc->set.scaler = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->dithering_mode) {
|
|
|
|
if (asyc->dither.mode != val) {
|
|
|
|
asyc->dither.mode = val;
|
|
|
|
asyc->set.dither = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->dithering_depth) {
|
|
|
|
if (asyc->dither.mode != val) {
|
|
|
|
asyc->dither.depth = val;
|
|
|
|
asyc->set.dither = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->vibrant_hue_property) {
|
|
|
|
if (asyc->procamp.vibrant_hue != val) {
|
|
|
|
asyc->procamp.vibrant_hue = val;
|
|
|
|
asyc->set.procamp = true;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (property == disp->color_vibrance_property) {
|
|
|
|
if (asyc->procamp.color_vibrance != val) {
|
|
|
|
asyc->procamp.color_vibrance = val;
|
|
|
|
asyc->set.procamp = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nouveau_conn_atomic_destroy_state(struct drm_connector *connector,
|
|
|
|
struct drm_connector_state *state)
|
|
|
|
{
|
|
|
|
struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
|
|
|
|
__drm_atomic_helper_connector_destroy_state(&asyc->state);
|
|
|
|
kfree(asyc);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct drm_connector_state *
|
|
|
|
nouveau_conn_atomic_duplicate_state(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct nouveau_conn_atom *armc = nouveau_conn_atom(connector->state);
|
|
|
|
struct nouveau_conn_atom *asyc;
|
|
|
|
if (!(asyc = kmalloc(sizeof(*asyc), GFP_KERNEL)))
|
|
|
|
return NULL;
|
|
|
|
__drm_atomic_helper_connector_duplicate_state(connector, &asyc->state);
|
|
|
|
asyc->dither = armc->dither;
|
|
|
|
asyc->scaler = armc->scaler;
|
|
|
|
asyc->procamp = armc->procamp;
|
|
|
|
asyc->set.mask = 0;
|
|
|
|
return &asyc->state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nouveau_conn_reset(struct drm_connector *connector)
|
|
|
|
{
|
2019-10-24 16:52:53 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
2016-11-04 15:20:35 +08:00
|
|
|
struct nouveau_conn_atom *asyc;
|
|
|
|
|
2019-10-24 16:52:53 +08:00
|
|
|
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);
|
|
|
|
} else {
|
|
|
|
asyc = &nv_connector->properties_state;
|
|
|
|
}
|
2016-11-04 15:20:35 +08:00
|
|
|
|
|
|
|
asyc->dither.mode = DITHERING_MODE_AUTO;
|
|
|
|
asyc->dither.depth = DITHERING_DEPTH_AUTO;
|
|
|
|
asyc->scaler.mode = DRM_MODE_SCALE_NONE;
|
|
|
|
asyc->scaler.underscan.mode = UNDERSCAN_OFF;
|
|
|
|
asyc->procamp.color_vibrance = 150;
|
|
|
|
asyc->procamp.vibrant_hue = 90;
|
|
|
|
|
2018-05-08 18:39:47 +08:00
|
|
|
if (nouveau_display(connector->dev)->disp.object.oclass < NV50_DISP) {
|
2016-11-04 15:20:35 +08:00
|
|
|
switch (connector->connector_type) {
|
|
|
|
case DRM_MODE_CONNECTOR_LVDS:
|
|
|
|
/* See note in nouveau_conn_atomic_set_property(). */
|
|
|
|
asyc->scaler.mode = DRM_MODE_SCALE_FULLSCREEN;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
void
|
|
|
|
nouveau_conn_attach_properties(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct nouveau_display *disp = nouveau_display(dev);
|
2019-10-24 16:52:53 +08:00
|
|
|
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;
|
2016-11-04 15:20:35 +08:00
|
|
|
|
|
|
|
/* Init DVI-I specific properties. */
|
|
|
|
if (connector->connector_type == DRM_MODE_CONNECTOR_DVII)
|
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.
|
|
|
|
dvi_i_subconnector_property, 0);
|
|
|
|
|
|
|
|
/* Add overscan compensation options to digital outputs. */
|
|
|
|
if (disp->underscan_property &&
|
|
|
|
(connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
|
|
|
|
connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
|
|
|
|
connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
|
|
|
|
connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort)) {
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->underscan_property,
|
|
|
|
UNDERSCAN_OFF);
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->underscan_hborder_property, 0);
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->underscan_vborder_property, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add hue and saturation options. */
|
|
|
|
if (disp->vibrant_hue_property)
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->vibrant_hue_property,
|
|
|
|
armc->procamp.vibrant_hue);
|
|
|
|
if (disp->color_vibrance_property)
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->color_vibrance_property,
|
|
|
|
armc->procamp.color_vibrance);
|
|
|
|
|
|
|
|
/* Scaling mode property. */
|
|
|
|
switch (connector->connector_type) {
|
|
|
|
case DRM_MODE_CONNECTOR_TV:
|
|
|
|
break;
|
|
|
|
case DRM_MODE_CONNECTOR_VGA:
|
2018-05-08 18:39:47 +08:00
|
|
|
if (disp->disp.object.oclass < NV50_DISP)
|
2016-11-04 15:20:35 +08:00
|
|
|
break; /* Can only scale on DFPs. */
|
2020-07-08 01:36:28 +08:00
|
|
|
fallthrough;
|
2016-11-04 15:20:35 +08:00
|
|
|
default:
|
|
|
|
drm_object_attach_property(&connector->base, dev->mode_config.
|
|
|
|
scaling_mode_property,
|
|
|
|
armc->scaler.mode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dithering properties. */
|
|
|
|
switch (connector->connector_type) {
|
|
|
|
case DRM_MODE_CONNECTOR_TV:
|
|
|
|
case DRM_MODE_CONNECTOR_VGA:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (disp->dithering_mode) {
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->dithering_mode,
|
|
|
|
armc->dither.mode);
|
|
|
|
}
|
|
|
|
if (disp->dithering_depth) {
|
|
|
|
drm_object_attach_property(&connector->base,
|
|
|
|
disp->dithering_depth,
|
|
|
|
armc->dither.depth);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-31 14:16:21 +08:00
|
|
|
MODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
|
2014-08-19 04:43:24 +08:00
|
|
|
int nouveau_tv_disable = 0;
|
2012-07-31 14:16:21 +08:00
|
|
|
module_param_named(tv_disable, nouveau_tv_disable, int, 0400);
|
|
|
|
|
|
|
|
MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
|
2014-08-19 04:43:24 +08:00
|
|
|
int nouveau_ignorelid = 0;
|
2012-07-31 14:16:21 +08:00
|
|
|
module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
|
|
|
|
|
|
|
|
MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
|
2014-08-19 04:43:24 +08:00
|
|
|
int nouveau_duallink = 1;
|
2012-07-31 14:16:21 +08:00
|
|
|
module_param_named(duallink, nouveau_duallink, int, 0400);
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2015-11-04 10:00:11 +08:00
|
|
|
MODULE_PARM_DESC(hdmimhz, "Force a maximum HDMI pixel clock (in MHz)");
|
|
|
|
int nouveau_hdmimhz = 0;
|
|
|
|
module_param_named(hdmimhz, nouveau_hdmimhz, int, 0400);
|
|
|
|
|
2011-08-02 17:29:37 +08:00
|
|
|
struct nouveau_encoder *
|
2011-07-12 11:32:07 +08:00
|
|
|
find_encoder(struct drm_connector *connector, int type)
|
2009-12-11 17:24:15 +08:00
|
|
|
{
|
|
|
|
struct nouveau_encoder *nv_encoder;
|
2014-07-18 11:30:03 +08:00
|
|
|
struct drm_encoder *enc;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2019-09-14 07:28:57 +08:00
|
|
|
drm_connector_for_each_possible_encoder(connector, enc) {
|
2014-07-18 11:30:03 +08:00
|
|
|
nv_encoder = nouveau_encoder(enc);
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2014-05-30 23:48:06 +08:00
|
|
|
if (type == DCB_OUTPUT_ANY ||
|
|
|
|
(nv_encoder->dcb && nv_encoder->dcb->type == type))
|
2009-12-11 17:24:15 +08:00
|
|
|
return nv_encoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-11-11 14:14:56 +08:00
|
|
|
nouveau_connector_destroy(struct drm_connector *connector)
|
2009-12-11 17:24:15 +08:00
|
|
|
{
|
2010-11-11 14:14:56 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
2020-06-08 12:47:37 +08:00
|
|
|
nvif_notify_dtor(&nv_connector->hpd);
|
2010-01-12 05:42:21 +08:00
|
|
|
kfree(nv_connector->edid);
|
2014-05-29 23:57:41 +08:00
|
|
|
drm_connector_unregister(connector);
|
2010-11-11 14:14:56 +08:00
|
|
|
drm_connector_cleanup(connector);
|
2018-08-27 16:06:51 +08:00
|
|
|
if (nv_connector->aux.transfer) {
|
|
|
|
drm_dp_cec_unregister_connector(&nv_connector->aux);
|
2014-05-30 14:20:58 +08:00
|
|
|
drm_dp_aux_unregister(&nv_connector->aux);
|
2018-07-13 01:13:52 +08:00
|
|
|
kfree(nv_connector->aux.name);
|
2018-08-27 16:06:51 +08:00
|
|
|
}
|
2010-11-11 14:14:56 +08:00
|
|
|
kfree(connector);
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2014-06-06 16:09:55 +08:00
|
|
|
static struct nouveau_encoder *
|
|
|
|
nouveau_connector_ddc_detect(struct drm_connector *connector)
|
2009-12-11 17:24:15 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
2018-08-31 01:16:28 +08:00
|
|
|
struct nouveau_encoder *nv_encoder = NULL, *found = NULL;
|
2014-07-18 11:30:03 +08:00
|
|
|
struct drm_encoder *encoder;
|
2019-09-14 07:28:57 +08:00
|
|
|
int ret;
|
2018-08-31 01:16:28 +08:00
|
|
|
bool switcheroo_ddc = false;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2019-09-14 07:28:57 +08:00
|
|
|
drm_connector_for_each_possible_encoder(connector, encoder) {
|
2014-07-18 11:30:03 +08:00
|
|
|
nv_encoder = nouveau_encoder(encoder);
|
2010-08-09 03:35:57 +08:00
|
|
|
|
2018-08-31 01:16:28 +08:00
|
|
|
switch (nv_encoder->dcb->type) {
|
|
|
|
case DCB_OUTPUT_DP:
|
2020-08-27 02:24:38 +08:00
|
|
|
ret = nouveau_dp_detect(nouveau_connector(connector),
|
|
|
|
nv_encoder);
|
2016-11-04 15:20:36 +08:00
|
|
|
if (ret == NOUVEAU_DP_MST)
|
|
|
|
return NULL;
|
2018-08-31 01:16:28 +08:00
|
|
|
else if (ret == NOUVEAU_DP_SST)
|
|
|
|
found = nv_encoder;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case DCB_OUTPUT_LVDS:
|
|
|
|
switcheroo_ddc = !!(vga_switcheroo_handler_flags() &
|
|
|
|
VGA_SWITCHEROO_CAN_SWITCH_DDC);
|
2020-07-08 01:36:28 +08:00
|
|
|
fallthrough;
|
2018-08-31 01:16:28 +08:00
|
|
|
default:
|
|
|
|
if (!nv_encoder->i2c)
|
drm/nouveau: Switch DDC when reading the EDID
The pre-retina MacBook Pro uses an LVDS panel and a gmux controller
to switch the panel between its two GPUs. The panel mode in VBIOS
is notoriously bogus on these machines.
Use drm_get_edid_switcheroo() in lieu of drm_get_edid() on LVDS
if the vga_switcheroo handler is capable of temporarily switching
the panel's DDC lines to the discrete GPU. This allows us to retrieve
the EDID if the panel is currently muxed to the integrated GPU.
Likewise, ask vga_switcheroo to switch DDC before probing LVDS
connectors.
This only enables EDID probing on the pre-retina MBP (2008 - 2013).
The retina MBP (2012 - present) uses eDP and gmux is not capable of
switching AUX separately from the main link on these models.
This will be addressed in later patches.
List of pre-retina MBPs with dual GPUs, either or both Nvidia:
[MBP 5,1 2008 nvidia MCP79 + G96 pre-retina 15"]
[MBP 5,2 2009 nvidia MCP79 + G96 pre-retina 17"]
[MBP 5,3 2009 nvidia MCP79 + G96 pre-retina 15"]
[MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina 15"]
[MBP 6,1 2010 intel ILK + nvidia GT216 pre-retina 17"]
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
v3: Commit newly added due to introduction of drm_get_edid_switcheroo()
wrapper which drivers need to opt-in to.
v5: Rebase on "vga_switcheroo: Add handler flags infrastructure",
i.e. call drm_get_edid_switcheroo() only if the handler
indicates that DDC is switchable.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Lukas Wunner <lukas@wunner.de>
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/e9466eb3d66b5b30f1e93c3b3da79d8b9ad0830f.1452525860.git.lukas@wunner.de
2016-01-12 03:09:20 +08:00
|
|
|
break;
|
2018-08-31 01:16:28 +08:00
|
|
|
|
|
|
|
if (switcheroo_ddc)
|
|
|
|
vga_switcheroo_lock_ddc(dev->pdev);
|
2015-08-20 12:54:15 +08:00
|
|
|
if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
|
2018-08-31 01:16:28 +08:00
|
|
|
found = nv_encoder;
|
|
|
|
if (switcheroo_ddc)
|
|
|
|
vga_switcheroo_unlock_ddc(dev->pdev);
|
|
|
|
|
|
|
|
break;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
2018-08-31 01:16:28 +08:00
|
|
|
if (found)
|
|
|
|
break;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2018-08-31 01:16:28 +08:00
|
|
|
return found;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2010-09-09 20:33:17 +08:00
|
|
|
static struct nouveau_encoder *
|
|
|
|
nouveau_connector_of_detect(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
#ifdef __powerpc__
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder;
|
|
|
|
struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev);
|
|
|
|
|
|
|
|
if (!dn ||
|
2012-07-11 08:44:20 +08:00
|
|
|
!((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) ||
|
|
|
|
(nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG))))
|
2010-09-09 20:33:17 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for_each_child_of_node(dn, cn) {
|
|
|
|
const char *name = of_get_property(cn, "name", NULL);
|
|
|
|
const void *edid = of_get_property(cn, "EDID", NULL);
|
|
|
|
int idx = name ? name[strlen(name) - 1] - 'A' : 0;
|
|
|
|
|
|
|
|
if (nv_encoder->dcb->i2c_index == idx && edid) {
|
|
|
|
nv_connector->edid =
|
|
|
|
kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
|
|
|
|
of_node_put(cn);
|
|
|
|
return nv_encoder;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
static void
|
|
|
|
nouveau_connector_set_encoder(struct drm_connector *connector,
|
|
|
|
struct nouveau_encoder *nv_encoder)
|
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
2009-12-11 17:24:15 +08:00
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
|
|
|
|
if (nv_connector->detected_encoder == nv_encoder)
|
|
|
|
return;
|
|
|
|
nv_connector->detected_encoder = nv_encoder;
|
|
|
|
|
2016-05-18 11:57:42 +08:00
|
|
|
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
|
2020-05-12 06:41:24 +08:00
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
|
|
|
|
connector->interlace_allowed =
|
|
|
|
nv_encoder->caps.dp_interlace;
|
|
|
|
else
|
|
|
|
connector->interlace_allowed = true;
|
2011-10-19 11:06:48 +08:00
|
|
|
connector->doublescan_allowed = true;
|
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS ||
|
|
|
|
nv_encoder->dcb->type == DCB_OUTPUT_TMDS) {
|
2009-12-11 17:24:15 +08:00
|
|
|
connector->doublescan_allowed = false;
|
|
|
|
connector->interlace_allowed = false;
|
|
|
|
} else {
|
|
|
|
connector->doublescan_allowed = true;
|
2016-05-18 11:57:42 +08:00
|
|
|
if (drm->client.device.info.family == NV_DEVICE_INFO_V0_KELVIN ||
|
|
|
|
(drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS &&
|
2013-09-05 16:45:02 +08:00
|
|
|
(dev->pdev->device & 0x0ff0) != 0x0100 &&
|
|
|
|
(dev->pdev->device & 0x0ff0) != 0x0150))
|
2009-12-11 17:24:15 +08:00
|
|
|
/* HW is broken */
|
|
|
|
connector->interlace_allowed = false;
|
|
|
|
else
|
|
|
|
connector->interlace_allowed = true;
|
|
|
|
}
|
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
|
2012-10-12 09:42:04 +08:00
|
|
|
drm_object_property_set_value(&connector->base,
|
2009-12-11 17:24:15 +08:00
|
|
|
dev->mode_config.dvi_i_subconnector_property,
|
2012-07-11 08:44:20 +08:00
|
|
|
nv_encoder->dcb->type == DCB_OUTPUT_TMDS ?
|
2009-12-11 17:24:15 +08:00
|
|
|
DRM_MODE_SUBCONNECTOR_DVID :
|
|
|
|
DRM_MODE_SUBCONNECTOR_DVIA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 02:24:54 +08:00
|
|
|
static void
|
|
|
|
nouveau_connector_set_edid(struct nouveau_connector *nv_connector,
|
|
|
|
struct edid *edid)
|
|
|
|
{
|
drm/nouveau/kms: Fix NULL pointer dereference in nouveau_connector_detect_depth
This oops manifests itself on the following hardware:
01:00.0 VGA compatible controller: NVIDIA Corporation G98M [GeForce G 103M] (rev a1)
Oct 09 14:17:46 lp-sasha kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: #PF: supervisor read access in kernel mode
Oct 09 14:17:46 lp-sasha kernel: #PF: error_code(0x0000) - not-present page
Oct 09 14:17:46 lp-sasha kernel: PGD 0 P4D 0
Oct 09 14:17:46 lp-sasha kernel: Oops: 0000 [#1] SMP PTI
Oct 09 14:17:46 lp-sasha kernel: CPU: 1 PID: 191 Comm: systemd-udevd Not tainted 5.9.0-rc8-next-20201009 #38
Oct 09 14:17:46 lp-sasha kernel: Hardware name: Hewlett-Packard Compaq Presario CQ61 Notebook PC/306A, BIOS F.03 03/23/2009
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
Oct 09 14:17:46 lp-sasha kernel: Call Trace:
Oct 09 14:17:46 lp-sasha kernel: nouveau_connector_get_modes+0x1e6/0x240 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? kfree+0xb9/0x240
Oct 09 14:17:46 lp-sasha kernel: ? drm_connector_list_iter_next+0x7c/0xa0
Oct 09 14:17:46 lp-sasha kernel: drm_helper_probe_single_connector_modes+0x1ba/0x7c0
Oct 09 14:17:46 lp-sasha kernel: drm_client_modeset_probe+0x27e/0x1360
Oct 09 14:17:46 lp-sasha kernel: ? nvif_object_sclass_put+0xc/0x20 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_cli_init+0x3cc/0x440 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? ktime_get_mono_fast_ns+0x49/0xa0
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_drm_open+0x4e/0x180 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: __drm_fb_helper_initial_config_and_unlock+0x3f/0x4a0
Oct 09 14:17:46 lp-sasha kernel: ? drm_file_alloc+0x18f/0x260
Oct 09 14:17:46 lp-sasha kernel: ? mutex_lock+0x9/0x40
Oct 09 14:17:46 lp-sasha kernel: ? drm_client_init+0x110/0x160
Oct 09 14:17:46 lp-sasha kernel: nouveau_fbcon_init+0x14d/0x1c0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_device_init+0x1c0/0x880 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_probe+0x11a/0x1e0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: pci_device_probe+0xcd/0x140
Oct 09 14:17:46 lp-sasha kernel: really_probe+0xd8/0x400
Oct 09 14:17:46 lp-sasha kernel: driver_probe_device+0x4a/0xa0
Oct 09 14:17:46 lp-sasha kernel: device_driver_attach+0x9c/0xc0
Oct 09 14:17:46 lp-sasha kernel: __driver_attach+0x6f/0x100
Oct 09 14:17:46 lp-sasha kernel: ? device_driver_attach+0xc0/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_for_each_dev+0x75/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_add_driver+0x106/0x1c0
Oct 09 14:17:46 lp-sasha kernel: driver_register+0x86/0xe0
Oct 09 14:17:46 lp-sasha kernel: ? 0xffffffffa044e000
Oct 09 14:17:46 lp-sasha kernel: do_one_initcall+0x48/0x1e0
Oct 09 14:17:46 lp-sasha kernel: ? _cond_resched+0x11/0x60
Oct 09 14:17:46 lp-sasha kernel: ? kmem_cache_alloc_trace+0x19c/0x1e0
Oct 09 14:17:46 lp-sasha kernel: do_init_module+0x57/0x220
Oct 09 14:17:46 lp-sasha kernel: __do_sys_finit_module+0xa0/0xe0
Oct 09 14:17:46 lp-sasha kernel: do_syscall_64+0x33/0x40
Oct 09 14:17:46 lp-sasha kernel: entry_SYSCALL_64_after_hwframe+0x44/0xa9
Oct 09 14:17:46 lp-sasha kernel: RIP: 0033:0x7fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e3 70 0c 00 f7 d8 64 89 01 48
Oct 09 14:17:46 lp-sasha kernel: RSP: 002b:00007ffc8ad38a98 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
Oct 09 14:17:46 lp-sasha kernel: RAX: ffffffffffffffda RBX: 0000563f6e7fd530 RCX: 00007fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000000 RSI: 00007fd01a19f95d RDI: 000000000000000f
Oct 09 14:17:46 lp-sasha kernel: RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
Oct 09 14:17:46 lp-sasha kernel: R10: 000000000000000f R11: 0000000000000246 R12: 00007fd01a19f95d
Oct 09 14:17:46 lp-sasha kernel: R13: 0000000000000000 R14: 0000563f6e7fbc10 R15: 0000563f6e7fd530
Oct 09 14:17:46 lp-sasha kernel: Modules linked in: nouveau(+) ttm xt_string xt_mark xt_LOG vgem v4l2_dv_timings uvcvideo ulpi udf ts_kmp ts_fsm ts_bm snd_aloop sil164 qat_dh895xccvf nf_nat_sip nf_nat_irc nf_nat_ftp nf_nat nf_log_ipv6 nf_log_ipv4 nf_log_common ltc2990 lcd intel_qat input_leds i2c_mux gspca_main videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc drivetemp cuse fuse crc_itu_t coretemp ch7006 ath5k ath algif_hash
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: ---[ end trace 0ddafe218ad30017 ]---
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
The disassembly:
Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
All code
========
0: 0a 00 or (%rax),%al
2: 00 48 8b add %cl,-0x75(%rax)
5: 49 rex.WB
6: 48 c7 87 b8 00 00 00 movq $0x6,0xb8(%rdi)
d: 06 00 00 00
11: 80 b9 4d 0a 00 00 00 cmpb $0x0,0xa4d(%rcx)
18: 75 1e jne 0x38
1a: 83 fa 41 cmp $0x41,%edx
1d: 75 05 jne 0x24
1f: 48 85 c0 test %rax,%rax
22: 75 29 jne 0x4d
24: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
2a:* 39 06 cmp %eax,(%rsi) <-- trapping instruction
2c: 7c 25 jl 0x53
2e: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
35: 75 b7 jne 0xffffffffffffffee
37: c3 retq
38: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
3f: 75 .byte 0x75
Code starting with the faulting instruction
===========================================
0: 39 06 cmp %eax,(%rsi)
2: 7c 25 jl 0x29
4: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
b: 75 b7 jne 0xffffffffffffffc4
d: c3 retq
e: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
15: 75 .byte 0x75
objdump -SF --disassemble=nouveau_connector_detect_depth
[...]
if (nv_connector->edid &&
c85e1: 83 fa 41 cmp $0x41,%edx
c85e4: 75 05 jne c85eb <nouveau_connector_detect_depth+0x6b> (File Offset: 0xc866b)
c85e6: 48 85 c0 test %rax,%rax
c85e9: 75 29 jne c8614 <nouveau_connector_detect_depth+0x94> (File Offset: 0xc8694)
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
c85eb: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
c85f1: 39 06 cmp %eax,(%rsi)
c85f3: 7c 25 jl c861a <nouveau_connector_detect_depth+0x9a> (File Offset: 0xc869a)
( duallink && (bios->fp.strapless_is_24bit & 2)))
c85f5: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
c85fc: 75 b7 jne c85b5 <nouveau_connector_detect_depth+0x35> (File Offset: 0xc8635)
connector->display_info.bpc = 8;
[...]
% scripts/faddr2line /lib/modules/5.9.0-rc8-next-20201009/kernel/drivers/gpu/drm/nouveau/nouveau.ko nouveau_connector_detect_depth+0x71/0xc0
nouveau_connector_detect_depth+0x71/0xc0:
nouveau_connector_detect_depth at /home/sasha/linux-next/drivers/gpu/drm/nouveau/nouveau_connector.c:891
It is actually line 889. See the disassembly below.
889 duallink = mode->clock >= bios->fp.duallink_transition_clk;
The NULL pointer being dereferenced is mode.
Git bisect has identified the following commit as bad:
f28e32d3906e drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Here is the chain of events that causes the oops.
On entry to nouveau_connector_detect_lvds, edid is set to NULL. The call
to nouveau_connector_detect sets nv_connector->edid to valid memory,
with status set to connector_status_connected and the flow of execution
branching to the out label.
The subsequent call to nouveau_connector_set_edid erronously clears
nv_connector->edid, via the local edid pointer which remains set to NULL.
Fix this by setting edid to the value of the just acquired
nv_connector->edid and executing the body of nouveau_connector_set_edid
only if nv_connector->edid and edid point to different memory addresses
thus preventing nv_connector->edid from being turned into a dangling
pointer.
Fixes: f28e32d3906e ("drm/nouveau/kms: Don't change EDID when it hasn't actually changed")
Signed-off-by: Alexander Kapshuk <alexander.kapshuk@gmail.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2020-10-13 20:47:25 +08:00
|
|
|
if (nv_connector->edid != edid) {
|
|
|
|
struct edid *old_edid = nv_connector->edid;
|
2020-08-27 02:24:54 +08:00
|
|
|
|
drm/nouveau/kms: Fix NULL pointer dereference in nouveau_connector_detect_depth
This oops manifests itself on the following hardware:
01:00.0 VGA compatible controller: NVIDIA Corporation G98M [GeForce G 103M] (rev a1)
Oct 09 14:17:46 lp-sasha kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: #PF: supervisor read access in kernel mode
Oct 09 14:17:46 lp-sasha kernel: #PF: error_code(0x0000) - not-present page
Oct 09 14:17:46 lp-sasha kernel: PGD 0 P4D 0
Oct 09 14:17:46 lp-sasha kernel: Oops: 0000 [#1] SMP PTI
Oct 09 14:17:46 lp-sasha kernel: CPU: 1 PID: 191 Comm: systemd-udevd Not tainted 5.9.0-rc8-next-20201009 #38
Oct 09 14:17:46 lp-sasha kernel: Hardware name: Hewlett-Packard Compaq Presario CQ61 Notebook PC/306A, BIOS F.03 03/23/2009
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
Oct 09 14:17:46 lp-sasha kernel: Call Trace:
Oct 09 14:17:46 lp-sasha kernel: nouveau_connector_get_modes+0x1e6/0x240 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? kfree+0xb9/0x240
Oct 09 14:17:46 lp-sasha kernel: ? drm_connector_list_iter_next+0x7c/0xa0
Oct 09 14:17:46 lp-sasha kernel: drm_helper_probe_single_connector_modes+0x1ba/0x7c0
Oct 09 14:17:46 lp-sasha kernel: drm_client_modeset_probe+0x27e/0x1360
Oct 09 14:17:46 lp-sasha kernel: ? nvif_object_sclass_put+0xc/0x20 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_cli_init+0x3cc/0x440 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? ktime_get_mono_fast_ns+0x49/0xa0
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_drm_open+0x4e/0x180 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: __drm_fb_helper_initial_config_and_unlock+0x3f/0x4a0
Oct 09 14:17:46 lp-sasha kernel: ? drm_file_alloc+0x18f/0x260
Oct 09 14:17:46 lp-sasha kernel: ? mutex_lock+0x9/0x40
Oct 09 14:17:46 lp-sasha kernel: ? drm_client_init+0x110/0x160
Oct 09 14:17:46 lp-sasha kernel: nouveau_fbcon_init+0x14d/0x1c0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_device_init+0x1c0/0x880 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_probe+0x11a/0x1e0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: pci_device_probe+0xcd/0x140
Oct 09 14:17:46 lp-sasha kernel: really_probe+0xd8/0x400
Oct 09 14:17:46 lp-sasha kernel: driver_probe_device+0x4a/0xa0
Oct 09 14:17:46 lp-sasha kernel: device_driver_attach+0x9c/0xc0
Oct 09 14:17:46 lp-sasha kernel: __driver_attach+0x6f/0x100
Oct 09 14:17:46 lp-sasha kernel: ? device_driver_attach+0xc0/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_for_each_dev+0x75/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_add_driver+0x106/0x1c0
Oct 09 14:17:46 lp-sasha kernel: driver_register+0x86/0xe0
Oct 09 14:17:46 lp-sasha kernel: ? 0xffffffffa044e000
Oct 09 14:17:46 lp-sasha kernel: do_one_initcall+0x48/0x1e0
Oct 09 14:17:46 lp-sasha kernel: ? _cond_resched+0x11/0x60
Oct 09 14:17:46 lp-sasha kernel: ? kmem_cache_alloc_trace+0x19c/0x1e0
Oct 09 14:17:46 lp-sasha kernel: do_init_module+0x57/0x220
Oct 09 14:17:46 lp-sasha kernel: __do_sys_finit_module+0xa0/0xe0
Oct 09 14:17:46 lp-sasha kernel: do_syscall_64+0x33/0x40
Oct 09 14:17:46 lp-sasha kernel: entry_SYSCALL_64_after_hwframe+0x44/0xa9
Oct 09 14:17:46 lp-sasha kernel: RIP: 0033:0x7fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e3 70 0c 00 f7 d8 64 89 01 48
Oct 09 14:17:46 lp-sasha kernel: RSP: 002b:00007ffc8ad38a98 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
Oct 09 14:17:46 lp-sasha kernel: RAX: ffffffffffffffda RBX: 0000563f6e7fd530 RCX: 00007fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000000 RSI: 00007fd01a19f95d RDI: 000000000000000f
Oct 09 14:17:46 lp-sasha kernel: RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
Oct 09 14:17:46 lp-sasha kernel: R10: 000000000000000f R11: 0000000000000246 R12: 00007fd01a19f95d
Oct 09 14:17:46 lp-sasha kernel: R13: 0000000000000000 R14: 0000563f6e7fbc10 R15: 0000563f6e7fd530
Oct 09 14:17:46 lp-sasha kernel: Modules linked in: nouveau(+) ttm xt_string xt_mark xt_LOG vgem v4l2_dv_timings uvcvideo ulpi udf ts_kmp ts_fsm ts_bm snd_aloop sil164 qat_dh895xccvf nf_nat_sip nf_nat_irc nf_nat_ftp nf_nat nf_log_ipv6 nf_log_ipv4 nf_log_common ltc2990 lcd intel_qat input_leds i2c_mux gspca_main videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc drivetemp cuse fuse crc_itu_t coretemp ch7006 ath5k ath algif_hash
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: ---[ end trace 0ddafe218ad30017 ]---
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
The disassembly:
Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
All code
========
0: 0a 00 or (%rax),%al
2: 00 48 8b add %cl,-0x75(%rax)
5: 49 rex.WB
6: 48 c7 87 b8 00 00 00 movq $0x6,0xb8(%rdi)
d: 06 00 00 00
11: 80 b9 4d 0a 00 00 00 cmpb $0x0,0xa4d(%rcx)
18: 75 1e jne 0x38
1a: 83 fa 41 cmp $0x41,%edx
1d: 75 05 jne 0x24
1f: 48 85 c0 test %rax,%rax
22: 75 29 jne 0x4d
24: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
2a:* 39 06 cmp %eax,(%rsi) <-- trapping instruction
2c: 7c 25 jl 0x53
2e: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
35: 75 b7 jne 0xffffffffffffffee
37: c3 retq
38: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
3f: 75 .byte 0x75
Code starting with the faulting instruction
===========================================
0: 39 06 cmp %eax,(%rsi)
2: 7c 25 jl 0x29
4: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
b: 75 b7 jne 0xffffffffffffffc4
d: c3 retq
e: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
15: 75 .byte 0x75
objdump -SF --disassemble=nouveau_connector_detect_depth
[...]
if (nv_connector->edid &&
c85e1: 83 fa 41 cmp $0x41,%edx
c85e4: 75 05 jne c85eb <nouveau_connector_detect_depth+0x6b> (File Offset: 0xc866b)
c85e6: 48 85 c0 test %rax,%rax
c85e9: 75 29 jne c8614 <nouveau_connector_detect_depth+0x94> (File Offset: 0xc8694)
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
c85eb: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
c85f1: 39 06 cmp %eax,(%rsi)
c85f3: 7c 25 jl c861a <nouveau_connector_detect_depth+0x9a> (File Offset: 0xc869a)
( duallink && (bios->fp.strapless_is_24bit & 2)))
c85f5: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
c85fc: 75 b7 jne c85b5 <nouveau_connector_detect_depth+0x35> (File Offset: 0xc8635)
connector->display_info.bpc = 8;
[...]
% scripts/faddr2line /lib/modules/5.9.0-rc8-next-20201009/kernel/drivers/gpu/drm/nouveau/nouveau.ko nouveau_connector_detect_depth+0x71/0xc0
nouveau_connector_detect_depth+0x71/0xc0:
nouveau_connector_detect_depth at /home/sasha/linux-next/drivers/gpu/drm/nouveau/nouveau_connector.c:891
It is actually line 889. See the disassembly below.
889 duallink = mode->clock >= bios->fp.duallink_transition_clk;
The NULL pointer being dereferenced is mode.
Git bisect has identified the following commit as bad:
f28e32d3906e drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Here is the chain of events that causes the oops.
On entry to nouveau_connector_detect_lvds, edid is set to NULL. The call
to nouveau_connector_detect sets nv_connector->edid to valid memory,
with status set to connector_status_connected and the flow of execution
branching to the out label.
The subsequent call to nouveau_connector_set_edid erronously clears
nv_connector->edid, via the local edid pointer which remains set to NULL.
Fix this by setting edid to the value of the just acquired
nv_connector->edid and executing the body of nouveau_connector_set_edid
only if nv_connector->edid and edid point to different memory addresses
thus preventing nv_connector->edid from being turned into a dangling
pointer.
Fixes: f28e32d3906e ("drm/nouveau/kms: Don't change EDID when it hasn't actually changed")
Signed-off-by: Alexander Kapshuk <alexander.kapshuk@gmail.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2020-10-13 20:47:25 +08:00
|
|
|
drm_connector_update_edid_property(&nv_connector->base, edid);
|
|
|
|
kfree(old_edid);
|
|
|
|
nv_connector->edid = edid;
|
|
|
|
}
|
2020-08-27 02:24:54 +08:00
|
|
|
}
|
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
static enum drm_connector_status
|
2010-09-14 18:07:23 +08:00
|
|
|
nouveau_connector_detect(struct drm_connector *connector, bool force)
|
2009-12-11 17:24:15 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
2009-12-11 17:24:15 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = NULL;
|
2011-07-12 11:32:07 +08:00
|
|
|
struct nouveau_encoder *nv_partner;
|
2015-08-20 12:54:15 +08:00
|
|
|
struct i2c_adapter *i2c;
|
2010-07-20 09:08:25 +08:00
|
|
|
int type;
|
2012-09-10 12:20:51 +08:00
|
|
|
int ret;
|
|
|
|
enum drm_connector_status conn_status = connector_status_disconnected;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2018-08-16 03:00:14 +08:00
|
|
|
/* 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
|
|
|
|
* prevent the autosuspend timer from elapsing during this operation
|
|
|
|
* if possible.
|
2018-02-11 17:38:28 +08:00
|
|
|
*/
|
2018-08-16 03:00:14 +08:00
|
|
|
if (drm_kms_helper_is_poll_worker()) {
|
|
|
|
pm_runtime_get_noresume(dev->dev);
|
|
|
|
} else {
|
|
|
|
ret = pm_runtime_get_sync(dev->dev);
|
2020-06-14 09:22:23 +08:00
|
|
|
if (ret < 0 && ret != -EACCES) {
|
|
|
|
pm_runtime_put_autosuspend(dev->dev);
|
2020-08-27 02:24:54 +08:00
|
|
|
nouveau_connector_set_edid(nv_connector, NULL);
|
2018-02-11 17:38:28 +08:00
|
|
|
return conn_status;
|
2020-06-14 09:22:23 +08:00
|
|
|
}
|
2018-02-11 17:38:28 +08:00
|
|
|
}
|
2012-09-10 12:20:51 +08:00
|
|
|
|
2014-06-06 16:09:55 +08:00
|
|
|
nv_encoder = nouveau_connector_ddc_detect(connector);
|
|
|
|
if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
|
2020-08-27 02:24:54 +08:00
|
|
|
struct edid *new_edid;
|
|
|
|
|
drm/nouveau: Switch DDC when reading the EDID
The pre-retina MacBook Pro uses an LVDS panel and a gmux controller
to switch the panel between its two GPUs. The panel mode in VBIOS
is notoriously bogus on these machines.
Use drm_get_edid_switcheroo() in lieu of drm_get_edid() on LVDS
if the vga_switcheroo handler is capable of temporarily switching
the panel's DDC lines to the discrete GPU. This allows us to retrieve
the EDID if the panel is currently muxed to the integrated GPU.
Likewise, ask vga_switcheroo to switch DDC before probing LVDS
connectors.
This only enables EDID probing on the pre-retina MBP (2008 - 2013).
The retina MBP (2012 - present) uses eDP and gmux is not capable of
switching AUX separately from the main link on these models.
This will be addressed in later patches.
List of pre-retina MBPs with dual GPUs, either or both Nvidia:
[MBP 5,1 2008 nvidia MCP79 + G96 pre-retina 15"]
[MBP 5,2 2009 nvidia MCP79 + G96 pre-retina 17"]
[MBP 5,3 2009 nvidia MCP79 + G96 pre-retina 15"]
[MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina 15"]
[MBP 6,1 2010 intel ILK + nvidia GT216 pre-retina 17"]
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
v3: Commit newly added due to introduction of drm_get_edid_switcheroo()
wrapper which drivers need to opt-in to.
v5: Rebase on "vga_switcheroo: Add handler flags infrastructure",
i.e. call drm_get_edid_switcheroo() only if the handler
indicates that DDC is switchable.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Lukas Wunner <lukas@wunner.de>
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/e9466eb3d66b5b30f1e93c3b3da79d8b9ad0830f.1452525860.git.lukas@wunner.de
2016-01-12 03:09:20 +08:00
|
|
|
if ((vga_switcheroo_handler_flags() &
|
|
|
|
VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
|
|
|
|
nv_connector->type == DCB_CONNECTOR_LVDS)
|
2020-08-27 02:24:54 +08:00
|
|
|
new_edid = drm_get_edid_switcheroo(connector, i2c);
|
drm/nouveau: Switch DDC when reading the EDID
The pre-retina MacBook Pro uses an LVDS panel and a gmux controller
to switch the panel between its two GPUs. The panel mode in VBIOS
is notoriously bogus on these machines.
Use drm_get_edid_switcheroo() in lieu of drm_get_edid() on LVDS
if the vga_switcheroo handler is capable of temporarily switching
the panel's DDC lines to the discrete GPU. This allows us to retrieve
the EDID if the panel is currently muxed to the integrated GPU.
Likewise, ask vga_switcheroo to switch DDC before probing LVDS
connectors.
This only enables EDID probing on the pre-retina MBP (2008 - 2013).
The retina MBP (2012 - present) uses eDP and gmux is not capable of
switching AUX separately from the main link on these models.
This will be addressed in later patches.
List of pre-retina MBPs with dual GPUs, either or both Nvidia:
[MBP 5,1 2008 nvidia MCP79 + G96 pre-retina 15"]
[MBP 5,2 2009 nvidia MCP79 + G96 pre-retina 17"]
[MBP 5,3 2009 nvidia MCP79 + G96 pre-retina 15"]
[MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina 15"]
[MBP 6,1 2010 intel ILK + nvidia GT216 pre-retina 17"]
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
v3: Commit newly added due to introduction of drm_get_edid_switcheroo()
wrapper which drivers need to opt-in to.
v5: Rebase on "vga_switcheroo: Add handler flags infrastructure",
i.e. call drm_get_edid_switcheroo() only if the handler
indicates that DDC is switchable.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Lukas Wunner <lukas@wunner.de>
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/e9466eb3d66b5b30f1e93c3b3da79d8b9ad0830f.1452525860.git.lukas@wunner.de
2016-01-12 03:09:20 +08:00
|
|
|
else
|
2020-08-27 02:24:54 +08:00
|
|
|
new_edid = drm_get_edid(connector, i2c);
|
drm/nouveau: Switch DDC when reading the EDID
The pre-retina MacBook Pro uses an LVDS panel and a gmux controller
to switch the panel between its two GPUs. The panel mode in VBIOS
is notoriously bogus on these machines.
Use drm_get_edid_switcheroo() in lieu of drm_get_edid() on LVDS
if the vga_switcheroo handler is capable of temporarily switching
the panel's DDC lines to the discrete GPU. This allows us to retrieve
the EDID if the panel is currently muxed to the integrated GPU.
Likewise, ask vga_switcheroo to switch DDC before probing LVDS
connectors.
This only enables EDID probing on the pre-retina MBP (2008 - 2013).
The retina MBP (2012 - present) uses eDP and gmux is not capable of
switching AUX separately from the main link on these models.
This will be addressed in later patches.
List of pre-retina MBPs with dual GPUs, either or both Nvidia:
[MBP 5,1 2008 nvidia MCP79 + G96 pre-retina 15"]
[MBP 5,2 2009 nvidia MCP79 + G96 pre-retina 17"]
[MBP 5,3 2009 nvidia MCP79 + G96 pre-retina 15"]
[MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina 15"]
[MBP 6,1 2010 intel ILK + nvidia GT216 pre-retina 17"]
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
v3: Commit newly added due to introduction of drm_get_edid_switcheroo()
wrapper which drivers need to opt-in to.
v5: Rebase on "vga_switcheroo: Add handler flags infrastructure",
i.e. call drm_get_edid_switcheroo() only if the handler
indicates that DDC is switchable.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Lukas Wunner <lukas@wunner.de>
[MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"]
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/e9466eb3d66b5b30f1e93c3b3da79d8b9ad0830f.1452525860.git.lukas@wunner.de
2016-01-12 03:09:20 +08:00
|
|
|
|
2020-08-27 02:24:54 +08:00
|
|
|
nouveau_connector_set_edid(nv_connector, new_edid);
|
2009-12-11 17:24:15 +08:00
|
|
|
if (!nv_connector->edid) {
|
2012-07-31 14:16:21 +08:00
|
|
|
NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
|
2014-06-03 19:56:18 +08:00
|
|
|
connector->name);
|
2010-01-14 22:47:03 +08:00
|
|
|
goto detect_analog;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Override encoder type for DVI-I based on whether EDID
|
|
|
|
* says the display is digital or analog, both use the
|
|
|
|
* same i2c channel so the value returned from ddc_detect
|
|
|
|
* isn't necessarily correct.
|
|
|
|
*/
|
2011-07-12 11:32:07 +08:00
|
|
|
nv_partner = NULL;
|
2012-07-11 08:44:20 +08:00
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS)
|
|
|
|
nv_partner = find_encoder(connector, DCB_OUTPUT_ANALOG);
|
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG)
|
|
|
|
nv_partner = find_encoder(connector, DCB_OUTPUT_TMDS);
|
|
|
|
|
|
|
|
if (nv_partner && ((nv_encoder->dcb->type == DCB_OUTPUT_ANALOG &&
|
|
|
|
nv_partner->dcb->type == DCB_OUTPUT_TMDS) ||
|
|
|
|
(nv_encoder->dcb->type == DCB_OUTPUT_TMDS &&
|
|
|
|
nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) {
|
2009-12-11 17:24:15 +08:00
|
|
|
if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
|
2012-07-11 08:44:20 +08:00
|
|
|
type = DCB_OUTPUT_TMDS;
|
2009-12-11 17:24:15 +08:00
|
|
|
else
|
2012-07-11 08:44:20 +08:00
|
|
|
type = DCB_OUTPUT_ANALOG;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2011-07-12 11:32:07 +08:00
|
|
|
nv_encoder = find_encoder(connector, type);
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
2012-09-10 12:20:51 +08:00
|
|
|
conn_status = connector_status_connected;
|
2018-08-27 16:06:51 +08:00
|
|
|
drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
|
2012-09-10 12:20:51 +08:00
|
|
|
goto out;
|
2020-08-27 02:24:54 +08:00
|
|
|
} else {
|
|
|
|
nouveau_connector_set_edid(nv_connector, NULL);
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2010-09-09 20:33:17 +08:00
|
|
|
nv_encoder = nouveau_connector_of_detect(connector);
|
|
|
|
if (nv_encoder) {
|
|
|
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
2012-09-10 12:20:51 +08:00
|
|
|
conn_status = connector_status_connected;
|
|
|
|
goto out;
|
2010-09-09 20:33:17 +08:00
|
|
|
}
|
|
|
|
|
2010-01-14 22:47:03 +08:00
|
|
|
detect_analog:
|
2012-07-11 08:44:20 +08:00
|
|
|
nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG);
|
2010-03-15 07:43:51 +08:00
|
|
|
if (!nv_encoder && !nouveau_tv_disable)
|
2012-07-11 08:44:20 +08:00
|
|
|
nv_encoder = find_encoder(connector, DCB_OUTPUT_TV);
|
2010-10-26 08:17:56 +08:00
|
|
|
if (nv_encoder && force) {
|
2009-12-11 17:24:15 +08:00
|
|
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
2015-03-11 17:51:04 +08:00
|
|
|
const struct drm_encoder_helper_funcs *helper =
|
2009-12-11 17:24:15 +08:00
|
|
|
encoder->helper_private;
|
|
|
|
|
|
|
|
if (helper->detect(encoder, connector) ==
|
|
|
|
connector_status_connected) {
|
|
|
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
2012-09-10 12:20:51 +08:00
|
|
|
conn_status = connector_status_connected;
|
|
|
|
goto out;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-10 12:20:51 +08:00
|
|
|
out:
|
2020-08-27 02:24:47 +08:00
|
|
|
if (!nv_connector->edid)
|
|
|
|
drm_dp_cec_unset_edid(&nv_connector->aux);
|
2012-09-10 12:20:51 +08:00
|
|
|
|
2018-08-16 03:00:14 +08:00
|
|
|
pm_runtime_mark_last_busy(dev->dev);
|
|
|
|
pm_runtime_put_autosuspend(dev->dev);
|
2012-09-10 12:20:51 +08:00
|
|
|
|
|
|
|
return conn_status;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2010-06-01 11:32:42 +08:00
|
|
|
static enum drm_connector_status
|
2010-09-14 18:07:23 +08:00
|
|
|
nouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
|
2010-06-01 11:32:42 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
2010-06-01 11:32:42 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = NULL;
|
2020-08-27 02:24:54 +08:00
|
|
|
struct edid *edid = NULL;
|
2010-06-01 11:32:42 +08:00
|
|
|
enum drm_connector_status status = connector_status_disconnected;
|
|
|
|
|
2012-07-11 08:44:20 +08:00
|
|
|
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
|
2010-06-01 11:32:42 +08:00
|
|
|
if (!nv_encoder)
|
2020-08-27 02:24:54 +08:00
|
|
|
goto out;
|
2010-06-01 11:32:42 +08:00
|
|
|
|
2010-07-12 13:33:07 +08:00
|
|
|
/* Try retrieving EDID via DDC */
|
2012-07-31 14:16:21 +08:00
|
|
|
if (!drm->vbios.fp_no_ddc) {
|
2010-09-14 18:07:23 +08:00
|
|
|
status = nouveau_connector_detect(connector, force);
|
drm/nouveau/kms: Fix NULL pointer dereference in nouveau_connector_detect_depth
This oops manifests itself on the following hardware:
01:00.0 VGA compatible controller: NVIDIA Corporation G98M [GeForce G 103M] (rev a1)
Oct 09 14:17:46 lp-sasha kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: #PF: supervisor read access in kernel mode
Oct 09 14:17:46 lp-sasha kernel: #PF: error_code(0x0000) - not-present page
Oct 09 14:17:46 lp-sasha kernel: PGD 0 P4D 0
Oct 09 14:17:46 lp-sasha kernel: Oops: 0000 [#1] SMP PTI
Oct 09 14:17:46 lp-sasha kernel: CPU: 1 PID: 191 Comm: systemd-udevd Not tainted 5.9.0-rc8-next-20201009 #38
Oct 09 14:17:46 lp-sasha kernel: Hardware name: Hewlett-Packard Compaq Presario CQ61 Notebook PC/306A, BIOS F.03 03/23/2009
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
Oct 09 14:17:46 lp-sasha kernel: Call Trace:
Oct 09 14:17:46 lp-sasha kernel: nouveau_connector_get_modes+0x1e6/0x240 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? kfree+0xb9/0x240
Oct 09 14:17:46 lp-sasha kernel: ? drm_connector_list_iter_next+0x7c/0xa0
Oct 09 14:17:46 lp-sasha kernel: drm_helper_probe_single_connector_modes+0x1ba/0x7c0
Oct 09 14:17:46 lp-sasha kernel: drm_client_modeset_probe+0x27e/0x1360
Oct 09 14:17:46 lp-sasha kernel: ? nvif_object_sclass_put+0xc/0x20 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_cli_init+0x3cc/0x440 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? ktime_get_mono_fast_ns+0x49/0xa0
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_drm_open+0x4e/0x180 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: __drm_fb_helper_initial_config_and_unlock+0x3f/0x4a0
Oct 09 14:17:46 lp-sasha kernel: ? drm_file_alloc+0x18f/0x260
Oct 09 14:17:46 lp-sasha kernel: ? mutex_lock+0x9/0x40
Oct 09 14:17:46 lp-sasha kernel: ? drm_client_init+0x110/0x160
Oct 09 14:17:46 lp-sasha kernel: nouveau_fbcon_init+0x14d/0x1c0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_device_init+0x1c0/0x880 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_probe+0x11a/0x1e0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: pci_device_probe+0xcd/0x140
Oct 09 14:17:46 lp-sasha kernel: really_probe+0xd8/0x400
Oct 09 14:17:46 lp-sasha kernel: driver_probe_device+0x4a/0xa0
Oct 09 14:17:46 lp-sasha kernel: device_driver_attach+0x9c/0xc0
Oct 09 14:17:46 lp-sasha kernel: __driver_attach+0x6f/0x100
Oct 09 14:17:46 lp-sasha kernel: ? device_driver_attach+0xc0/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_for_each_dev+0x75/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_add_driver+0x106/0x1c0
Oct 09 14:17:46 lp-sasha kernel: driver_register+0x86/0xe0
Oct 09 14:17:46 lp-sasha kernel: ? 0xffffffffa044e000
Oct 09 14:17:46 lp-sasha kernel: do_one_initcall+0x48/0x1e0
Oct 09 14:17:46 lp-sasha kernel: ? _cond_resched+0x11/0x60
Oct 09 14:17:46 lp-sasha kernel: ? kmem_cache_alloc_trace+0x19c/0x1e0
Oct 09 14:17:46 lp-sasha kernel: do_init_module+0x57/0x220
Oct 09 14:17:46 lp-sasha kernel: __do_sys_finit_module+0xa0/0xe0
Oct 09 14:17:46 lp-sasha kernel: do_syscall_64+0x33/0x40
Oct 09 14:17:46 lp-sasha kernel: entry_SYSCALL_64_after_hwframe+0x44/0xa9
Oct 09 14:17:46 lp-sasha kernel: RIP: 0033:0x7fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e3 70 0c 00 f7 d8 64 89 01 48
Oct 09 14:17:46 lp-sasha kernel: RSP: 002b:00007ffc8ad38a98 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
Oct 09 14:17:46 lp-sasha kernel: RAX: ffffffffffffffda RBX: 0000563f6e7fd530 RCX: 00007fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000000 RSI: 00007fd01a19f95d RDI: 000000000000000f
Oct 09 14:17:46 lp-sasha kernel: RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
Oct 09 14:17:46 lp-sasha kernel: R10: 000000000000000f R11: 0000000000000246 R12: 00007fd01a19f95d
Oct 09 14:17:46 lp-sasha kernel: R13: 0000000000000000 R14: 0000563f6e7fbc10 R15: 0000563f6e7fd530
Oct 09 14:17:46 lp-sasha kernel: Modules linked in: nouveau(+) ttm xt_string xt_mark xt_LOG vgem v4l2_dv_timings uvcvideo ulpi udf ts_kmp ts_fsm ts_bm snd_aloop sil164 qat_dh895xccvf nf_nat_sip nf_nat_irc nf_nat_ftp nf_nat nf_log_ipv6 nf_log_ipv4 nf_log_common ltc2990 lcd intel_qat input_leds i2c_mux gspca_main videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc drivetemp cuse fuse crc_itu_t coretemp ch7006 ath5k ath algif_hash
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: ---[ end trace 0ddafe218ad30017 ]---
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
The disassembly:
Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
All code
========
0: 0a 00 or (%rax),%al
2: 00 48 8b add %cl,-0x75(%rax)
5: 49 rex.WB
6: 48 c7 87 b8 00 00 00 movq $0x6,0xb8(%rdi)
d: 06 00 00 00
11: 80 b9 4d 0a 00 00 00 cmpb $0x0,0xa4d(%rcx)
18: 75 1e jne 0x38
1a: 83 fa 41 cmp $0x41,%edx
1d: 75 05 jne 0x24
1f: 48 85 c0 test %rax,%rax
22: 75 29 jne 0x4d
24: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
2a:* 39 06 cmp %eax,(%rsi) <-- trapping instruction
2c: 7c 25 jl 0x53
2e: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
35: 75 b7 jne 0xffffffffffffffee
37: c3 retq
38: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
3f: 75 .byte 0x75
Code starting with the faulting instruction
===========================================
0: 39 06 cmp %eax,(%rsi)
2: 7c 25 jl 0x29
4: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
b: 75 b7 jne 0xffffffffffffffc4
d: c3 retq
e: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
15: 75 .byte 0x75
objdump -SF --disassemble=nouveau_connector_detect_depth
[...]
if (nv_connector->edid &&
c85e1: 83 fa 41 cmp $0x41,%edx
c85e4: 75 05 jne c85eb <nouveau_connector_detect_depth+0x6b> (File Offset: 0xc866b)
c85e6: 48 85 c0 test %rax,%rax
c85e9: 75 29 jne c8614 <nouveau_connector_detect_depth+0x94> (File Offset: 0xc8694)
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
c85eb: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
c85f1: 39 06 cmp %eax,(%rsi)
c85f3: 7c 25 jl c861a <nouveau_connector_detect_depth+0x9a> (File Offset: 0xc869a)
( duallink && (bios->fp.strapless_is_24bit & 2)))
c85f5: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
c85fc: 75 b7 jne c85b5 <nouveau_connector_detect_depth+0x35> (File Offset: 0xc8635)
connector->display_info.bpc = 8;
[...]
% scripts/faddr2line /lib/modules/5.9.0-rc8-next-20201009/kernel/drivers/gpu/drm/nouveau/nouveau.ko nouveau_connector_detect_depth+0x71/0xc0
nouveau_connector_detect_depth+0x71/0xc0:
nouveau_connector_detect_depth at /home/sasha/linux-next/drivers/gpu/drm/nouveau/nouveau_connector.c:891
It is actually line 889. See the disassembly below.
889 duallink = mode->clock >= bios->fp.duallink_transition_clk;
The NULL pointer being dereferenced is mode.
Git bisect has identified the following commit as bad:
f28e32d3906e drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Here is the chain of events that causes the oops.
On entry to nouveau_connector_detect_lvds, edid is set to NULL. The call
to nouveau_connector_detect sets nv_connector->edid to valid memory,
with status set to connector_status_connected and the flow of execution
branching to the out label.
The subsequent call to nouveau_connector_set_edid erronously clears
nv_connector->edid, via the local edid pointer which remains set to NULL.
Fix this by setting edid to the value of the just acquired
nv_connector->edid and executing the body of nouveau_connector_set_edid
only if nv_connector->edid and edid point to different memory addresses
thus preventing nv_connector->edid from being turned into a dangling
pointer.
Fixes: f28e32d3906e ("drm/nouveau/kms: Don't change EDID when it hasn't actually changed")
Signed-off-by: Alexander Kapshuk <alexander.kapshuk@gmail.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2020-10-13 20:47:25 +08:00
|
|
|
if (status == connector_status_connected) {
|
|
|
|
edid = nv_connector->edid;
|
2010-06-01 11:32:42 +08:00
|
|
|
goto out;
|
drm/nouveau/kms: Fix NULL pointer dereference in nouveau_connector_detect_depth
This oops manifests itself on the following hardware:
01:00.0 VGA compatible controller: NVIDIA Corporation G98M [GeForce G 103M] (rev a1)
Oct 09 14:17:46 lp-sasha kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: #PF: supervisor read access in kernel mode
Oct 09 14:17:46 lp-sasha kernel: #PF: error_code(0x0000) - not-present page
Oct 09 14:17:46 lp-sasha kernel: PGD 0 P4D 0
Oct 09 14:17:46 lp-sasha kernel: Oops: 0000 [#1] SMP PTI
Oct 09 14:17:46 lp-sasha kernel: CPU: 1 PID: 191 Comm: systemd-udevd Not tainted 5.9.0-rc8-next-20201009 #38
Oct 09 14:17:46 lp-sasha kernel: Hardware name: Hewlett-Packard Compaq Presario CQ61 Notebook PC/306A, BIOS F.03 03/23/2009
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
Oct 09 14:17:46 lp-sasha kernel: Call Trace:
Oct 09 14:17:46 lp-sasha kernel: nouveau_connector_get_modes+0x1e6/0x240 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? kfree+0xb9/0x240
Oct 09 14:17:46 lp-sasha kernel: ? drm_connector_list_iter_next+0x7c/0xa0
Oct 09 14:17:46 lp-sasha kernel: drm_helper_probe_single_connector_modes+0x1ba/0x7c0
Oct 09 14:17:46 lp-sasha kernel: drm_client_modeset_probe+0x27e/0x1360
Oct 09 14:17:46 lp-sasha kernel: ? nvif_object_sclass_put+0xc/0x20 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_cli_init+0x3cc/0x440 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: ? ktime_get_mono_fast_ns+0x49/0xa0
Oct 09 14:17:46 lp-sasha kernel: ? nouveau_drm_open+0x4e/0x180 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: __drm_fb_helper_initial_config_and_unlock+0x3f/0x4a0
Oct 09 14:17:46 lp-sasha kernel: ? drm_file_alloc+0x18f/0x260
Oct 09 14:17:46 lp-sasha kernel: ? mutex_lock+0x9/0x40
Oct 09 14:17:46 lp-sasha kernel: ? drm_client_init+0x110/0x160
Oct 09 14:17:46 lp-sasha kernel: nouveau_fbcon_init+0x14d/0x1c0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_device_init+0x1c0/0x880 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: nouveau_drm_probe+0x11a/0x1e0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: pci_device_probe+0xcd/0x140
Oct 09 14:17:46 lp-sasha kernel: really_probe+0xd8/0x400
Oct 09 14:17:46 lp-sasha kernel: driver_probe_device+0x4a/0xa0
Oct 09 14:17:46 lp-sasha kernel: device_driver_attach+0x9c/0xc0
Oct 09 14:17:46 lp-sasha kernel: __driver_attach+0x6f/0x100
Oct 09 14:17:46 lp-sasha kernel: ? device_driver_attach+0xc0/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_for_each_dev+0x75/0xc0
Oct 09 14:17:46 lp-sasha kernel: bus_add_driver+0x106/0x1c0
Oct 09 14:17:46 lp-sasha kernel: driver_register+0x86/0xe0
Oct 09 14:17:46 lp-sasha kernel: ? 0xffffffffa044e000
Oct 09 14:17:46 lp-sasha kernel: do_one_initcall+0x48/0x1e0
Oct 09 14:17:46 lp-sasha kernel: ? _cond_resched+0x11/0x60
Oct 09 14:17:46 lp-sasha kernel: ? kmem_cache_alloc_trace+0x19c/0x1e0
Oct 09 14:17:46 lp-sasha kernel: do_init_module+0x57/0x220
Oct 09 14:17:46 lp-sasha kernel: __do_sys_finit_module+0xa0/0xe0
Oct 09 14:17:46 lp-sasha kernel: do_syscall_64+0x33/0x40
Oct 09 14:17:46 lp-sasha kernel: entry_SYSCALL_64_after_hwframe+0x44/0xa9
Oct 09 14:17:46 lp-sasha kernel: RIP: 0033:0x7fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e3 70 0c 00 f7 d8 64 89 01 48
Oct 09 14:17:46 lp-sasha kernel: RSP: 002b:00007ffc8ad38a98 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
Oct 09 14:17:46 lp-sasha kernel: RAX: ffffffffffffffda RBX: 0000563f6e7fd530 RCX: 00007fd01a060d5d
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000000 RSI: 00007fd01a19f95d RDI: 000000000000000f
Oct 09 14:17:46 lp-sasha kernel: RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000007
Oct 09 14:17:46 lp-sasha kernel: R10: 000000000000000f R11: 0000000000000246 R12: 00007fd01a19f95d
Oct 09 14:17:46 lp-sasha kernel: R13: 0000000000000000 R14: 0000563f6e7fbc10 R15: 0000563f6e7fd530
Oct 09 14:17:46 lp-sasha kernel: Modules linked in: nouveau(+) ttm xt_string xt_mark xt_LOG vgem v4l2_dv_timings uvcvideo ulpi udf ts_kmp ts_fsm ts_bm snd_aloop sil164 qat_dh895xccvf nf_nat_sip nf_nat_irc nf_nat_ftp nf_nat nf_log_ipv6 nf_log_ipv4 nf_log_common ltc2990 lcd intel_qat input_leds i2c_mux gspca_main videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc drivetemp cuse fuse crc_itu_t coretemp ch7006 ath5k ath algif_hash
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: ---[ end trace 0ddafe218ad30017 ]---
Oct 09 14:17:46 lp-sasha kernel: RIP: 0010:nouveau_connector_detect_depth+0x71/0xc0 [nouveau]
Oct 09 14:17:46 lp-sasha kernel: Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
Oct 09 14:17:46 lp-sasha kernel: RSP: 0018:ffffc9000028f8c0 EFLAGS: 00010297
Oct 09 14:17:46 lp-sasha kernel: RAX: 0000000000014c08 RBX: ffff8880369d4000 RCX: ffff8880369d3000
Oct 09 14:17:46 lp-sasha kernel: RDX: 0000000000000040 RSI: 0000000000000000 RDI: ffff8880369d4000
Oct 09 14:17:46 lp-sasha kernel: RBP: ffff88800601cc00 R08: ffff8880051da298 R09: ffffffff8226201a
Oct 09 14:17:46 lp-sasha kernel: R10: ffff88800469aa80 R11: ffff888004c84ff8 R12: 0000000000000000
Oct 09 14:17:46 lp-sasha kernel: R13: ffff8880051da000 R14: 0000000000002000 R15: 0000000000000003
Oct 09 14:17:46 lp-sasha kernel: FS: 00007fd0192b3440(0000) GS:ffff8880bc900000(0000) knlGS:0000000000000000
Oct 09 14:17:46 lp-sasha kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Oct 09 14:17:46 lp-sasha kernel: CR2: 0000000000000000 CR3: 0000000004976000 CR4: 00000000000006e0
The disassembly:
Code: 0a 00 00 48 8b 49 48 c7 87 b8 00 00 00 06 00 00 00 80 b9 4d 0a 00 00 00 75 1e 83 fa 41 75 05 48 85 c0 75 29 8b 81 10 0d 00 00 <39> 06 7c 25 f6 81 14 0d 00 00 02 75 b7 c3 80 b9 0c 0d 00 00 00 75
All code
========
0: 0a 00 or (%rax),%al
2: 00 48 8b add %cl,-0x75(%rax)
5: 49 rex.WB
6: 48 c7 87 b8 00 00 00 movq $0x6,0xb8(%rdi)
d: 06 00 00 00
11: 80 b9 4d 0a 00 00 00 cmpb $0x0,0xa4d(%rcx)
18: 75 1e jne 0x38
1a: 83 fa 41 cmp $0x41,%edx
1d: 75 05 jne 0x24
1f: 48 85 c0 test %rax,%rax
22: 75 29 jne 0x4d
24: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
2a:* 39 06 cmp %eax,(%rsi) <-- trapping instruction
2c: 7c 25 jl 0x53
2e: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
35: 75 b7 jne 0xffffffffffffffee
37: c3 retq
38: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
3f: 75 .byte 0x75
Code starting with the faulting instruction
===========================================
0: 39 06 cmp %eax,(%rsi)
2: 7c 25 jl 0x29
4: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
b: 75 b7 jne 0xffffffffffffffc4
d: c3 retq
e: 80 b9 0c 0d 00 00 00 cmpb $0x0,0xd0c(%rcx)
15: 75 .byte 0x75
objdump -SF --disassemble=nouveau_connector_detect_depth
[...]
if (nv_connector->edid &&
c85e1: 83 fa 41 cmp $0x41,%edx
c85e4: 75 05 jne c85eb <nouveau_connector_detect_depth+0x6b> (File Offset: 0xc866b)
c85e6: 48 85 c0 test %rax,%rax
c85e9: 75 29 jne c8614 <nouveau_connector_detect_depth+0x94> (File Offset: 0xc8694)
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
c85eb: 8b 81 10 0d 00 00 mov 0xd10(%rcx),%eax
c85f1: 39 06 cmp %eax,(%rsi)
c85f3: 7c 25 jl c861a <nouveau_connector_detect_depth+0x9a> (File Offset: 0xc869a)
( duallink && (bios->fp.strapless_is_24bit & 2)))
c85f5: f6 81 14 0d 00 00 02 testb $0x2,0xd14(%rcx)
c85fc: 75 b7 jne c85b5 <nouveau_connector_detect_depth+0x35> (File Offset: 0xc8635)
connector->display_info.bpc = 8;
[...]
% scripts/faddr2line /lib/modules/5.9.0-rc8-next-20201009/kernel/drivers/gpu/drm/nouveau/nouveau.ko nouveau_connector_detect_depth+0x71/0xc0
nouveau_connector_detect_depth+0x71/0xc0:
nouveau_connector_detect_depth at /home/sasha/linux-next/drivers/gpu/drm/nouveau/nouveau_connector.c:891
It is actually line 889. See the disassembly below.
889 duallink = mode->clock >= bios->fp.duallink_transition_clk;
The NULL pointer being dereferenced is mode.
Git bisect has identified the following commit as bad:
f28e32d3906e drm/nouveau/kms: Don't change EDID when it hasn't actually changed
Here is the chain of events that causes the oops.
On entry to nouveau_connector_detect_lvds, edid is set to NULL. The call
to nouveau_connector_detect sets nv_connector->edid to valid memory,
with status set to connector_status_connected and the flow of execution
branching to the out label.
The subsequent call to nouveau_connector_set_edid erronously clears
nv_connector->edid, via the local edid pointer which remains set to NULL.
Fix this by setting edid to the value of the just acquired
nv_connector->edid and executing the body of nouveau_connector_set_edid
only if nv_connector->edid and edid point to different memory addresses
thus preventing nv_connector->edid from being turned into a dangling
pointer.
Fixes: f28e32d3906e ("drm/nouveau/kms: Don't change EDID when it hasn't actually changed")
Signed-off-by: Alexander Kapshuk <alexander.kapshuk@gmail.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2020-10-13 20:47:25 +08:00
|
|
|
}
|
2010-06-01 11:32:42 +08:00
|
|
|
}
|
|
|
|
|
2010-07-12 13:33:07 +08:00
|
|
|
/* On some laptops (Sony, i'm looking at you) there appears to
|
|
|
|
* be no direct way of accessing the panel's EDID. The only
|
|
|
|
* option available to us appears to be to ask ACPI for help..
|
|
|
|
*
|
|
|
|
* It's important this check's before trying straps, one of the
|
|
|
|
* said manufacturer's laptops are configured in such a way
|
|
|
|
* the nouveau decides an entry in the VBIOS FP mode table is
|
|
|
|
* valid - it's not (rh#613284)
|
|
|
|
*/
|
|
|
|
if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
|
2020-08-27 02:24:54 +08:00
|
|
|
edid = nouveau_acpi_edid(dev, connector);
|
|
|
|
if (edid) {
|
2010-07-12 13:33:07 +08:00
|
|
|
status = connector_status_connected;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-01 11:32:42 +08:00
|
|
|
/* If no EDID found above, and the VBIOS indicates a hardcoded
|
|
|
|
* modeline is avalilable for the panel, set it as the panel's
|
|
|
|
* native mode and exit.
|
|
|
|
*/
|
2012-07-31 14:16:21 +08:00
|
|
|
if (nouveau_bios_fp_mode(dev, NULL) && (drm->vbios.fp_no_ddc ||
|
2010-06-01 11:32:42 +08:00
|
|
|
nv_encoder->dcb->lvdsconf.use_straps_for_mode)) {
|
|
|
|
status = connector_status_connected;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Still nothing, some VBIOS images have a hardcoded EDID block
|
|
|
|
* stored for the panel stored in them.
|
|
|
|
*/
|
2012-07-31 14:16:21 +08:00
|
|
|
if (!drm->vbios.fp_no_ddc) {
|
2020-08-27 02:24:54 +08:00
|
|
|
edid = (struct edid *)nouveau_bios_embedded_edid(dev);
|
2010-06-01 11:32:42 +08:00
|
|
|
if (edid) {
|
2020-08-27 02:24:54 +08:00
|
|
|
edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
|
|
|
|
if (edid)
|
2013-01-28 00:04:48 +08:00
|
|
|
status = connector_status_connected;
|
2010-06-01 11:32:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
#if defined(CONFIG_ACPI_BUTTON) || \
|
|
|
|
(defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE))
|
|
|
|
if (status == connector_status_connected &&
|
|
|
|
!nouveau_ignorelid && !acpi_lid_open())
|
|
|
|
status = connector_status_unknown;
|
|
|
|
#endif
|
|
|
|
|
2020-08-27 02:24:54 +08:00
|
|
|
nouveau_connector_set_edid(nv_connector, edid);
|
2010-06-20 22:57:57 +08:00
|
|
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
2010-06-01 11:32:42 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
static void
|
|
|
|
nouveau_connector_force(struct drm_connector *connector)
|
|
|
|
{
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
2010-02-24 12:01:40 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
2009-12-11 17:24:15 +08:00
|
|
|
struct nouveau_encoder *nv_encoder;
|
|
|
|
int type;
|
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
|
2009-12-11 17:24:15 +08:00
|
|
|
if (connector->force == DRM_FORCE_ON_DIGITAL)
|
2012-07-11 08:44:20 +08:00
|
|
|
type = DCB_OUTPUT_TMDS;
|
2009-12-11 17:24:15 +08:00
|
|
|
else
|
2012-07-11 08:44:20 +08:00
|
|
|
type = DCB_OUTPUT_ANALOG;
|
2009-12-11 17:24:15 +08:00
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
type = DCB_OUTPUT_ANY;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2011-07-12 11:32:07 +08:00
|
|
|
nv_encoder = find_encoder(connector, type);
|
2009-12-11 17:24:15 +08:00
|
|
|
if (!nv_encoder) {
|
2012-07-31 14:16:21 +08:00
|
|
|
NV_ERROR(drm, "can't find encoder to force %s on!\n",
|
2014-06-03 19:56:18 +08:00
|
|
|
connector->name);
|
2009-12-11 17:24:15 +08:00
|
|
|
connector->status = connector_status_disconnected;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nouveau_connector_set_property(struct drm_connector *connector,
|
|
|
|
struct drm_property *property, uint64_t value)
|
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
2019-10-24 16:52:53 +08:00
|
|
|
struct nouveau_conn_atom *asyc = &nv_connector->properties_state;
|
2010-07-20 22:48:08 +08:00
|
|
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
2009-12-11 17:24:15 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
ret = connector->funcs->atomic_set_property(&nv_connector->base,
|
|
|
|
&asyc->state,
|
|
|
|
property, value);
|
|
|
|
if (ret) {
|
|
|
|
if (nv_encoder && nv_encoder->dcb->type == DCB_OUTPUT_TV)
|
|
|
|
return get_slave_funcs(encoder)->set_property(
|
|
|
|
encoder, connector, property, value);
|
|
|
|
return ret;
|
2011-10-06 11:29:05 +08:00
|
|
|
}
|
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
nv_connector->scaling_mode = asyc->scaler.mode;
|
|
|
|
nv_connector->dithering_mode = asyc->dither.mode;
|
2011-10-06 11:29:05 +08:00
|
|
|
|
2016-11-04 15:20:36 +08:00
|
|
|
if (connector->encoder && connector->encoder->crtc) {
|
|
|
|
ret = drm_crtc_helper_set_mode(connector->encoder->crtc,
|
|
|
|
&connector->encoder->crtc->mode,
|
|
|
|
connector->encoder->crtc->x,
|
|
|
|
connector->encoder->crtc->y,
|
|
|
|
NULL);
|
|
|
|
if (!ret)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
return 0;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct moderec {
|
|
|
|
int hdisplay;
|
|
|
|
int vdisplay;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct moderec scaler_modes[] = {
|
|
|
|
{ 1920, 1200 },
|
|
|
|
{ 1920, 1080 },
|
|
|
|
{ 1680, 1050 },
|
|
|
|
{ 1600, 1200 },
|
|
|
|
{ 1400, 1050 },
|
|
|
|
{ 1280, 1024 },
|
|
|
|
{ 1280, 960 },
|
|
|
|
{ 1152, 864 },
|
|
|
|
{ 1024, 768 },
|
|
|
|
{ 800, 600 },
|
|
|
|
{ 720, 400 },
|
|
|
|
{ 640, 480 },
|
|
|
|
{ 640, 400 },
|
|
|
|
{ 640, 350 },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nouveau_connector_scaler_modes_add(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct drm_display_mode *native = nv_connector->native_mode, *m;
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct moderec *mode = &scaler_modes[0];
|
|
|
|
int modes = 0;
|
|
|
|
|
|
|
|
if (!native)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (mode->hdisplay) {
|
|
|
|
if (mode->hdisplay <= native->hdisplay &&
|
2014-12-22 15:28:35 +08:00
|
|
|
mode->vdisplay <= native->vdisplay &&
|
|
|
|
(mode->hdisplay != native->hdisplay ||
|
|
|
|
mode->vdisplay != native->vdisplay)) {
|
2009-12-11 17:24:15 +08:00
|
|
|
m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
|
|
|
|
drm_mode_vrefresh(native), false,
|
|
|
|
false, false);
|
|
|
|
if (!m)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
drm_mode_probed_add(connector, m);
|
|
|
|
modes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mode++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return modes;
|
|
|
|
}
|
|
|
|
|
2011-10-17 08:24:49 +08:00
|
|
|
static void
|
|
|
|
nouveau_connector_detect_depth(struct drm_connector *connector)
|
|
|
|
{
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
2011-10-17 08:24:49 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nvbios *bios = &drm->vbios;
|
2011-10-17 08:24:49 +08:00
|
|
|
struct drm_display_mode *mode = nv_connector->native_mode;
|
|
|
|
bool duallink;
|
|
|
|
|
|
|
|
/* if the edid is feeling nice enough to provide this info, use it */
|
|
|
|
if (nv_connector->edid && connector->display_info.bpc)
|
|
|
|
return;
|
|
|
|
|
2012-05-04 22:39:21 +08:00
|
|
|
/* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */
|
|
|
|
if (nv_connector->type == DCB_CONNECTOR_eDP) {
|
|
|
|
connector->display_info.bpc = 6;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we're out of options unless we're LVDS, default to 8bpc */
|
2012-07-11 08:44:20 +08:00
|
|
|
if (nv_encoder->dcb->type != DCB_OUTPUT_LVDS) {
|
2012-03-24 14:26:13 +08:00
|
|
|
connector->display_info.bpc = 8;
|
2011-10-17 08:24:49 +08:00
|
|
|
return;
|
2012-03-24 14:26:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
connector->display_info.bpc = 6;
|
2011-10-17 08:24:49 +08:00
|
|
|
|
|
|
|
/* LVDS: panel straps */
|
|
|
|
if (bios->fp_no_ddc) {
|
|
|
|
if (bios->fp.if_is_24bit)
|
|
|
|
connector->display_info.bpc = 8;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LVDS: DDC panel, need to first determine the number of links to
|
|
|
|
* know which if_is_24bit flag to check...
|
|
|
|
*/
|
|
|
|
if (nv_connector->edid &&
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
|
2011-10-17 08:24:49 +08:00
|
|
|
duallink = ((u8 *)nv_connector->edid)[121] == 2;
|
|
|
|
else
|
|
|
|
duallink = mode->clock >= bios->fp.duallink_transition_clk;
|
|
|
|
|
|
|
|
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
|
|
|
|
( duallink && (bios->fp.strapless_is_24bit & 2)))
|
|
|
|
connector->display_info.bpc = 8;
|
|
|
|
}
|
|
|
|
|
2018-09-07 05:43:23 +08:00
|
|
|
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)
|
|
|
|
{
|
2018-09-07 05:43:24 +08:00
|
|
|
nouveau_backlight_fini(connector);
|
2018-09-07 05:43:23 +08:00
|
|
|
}
|
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
static int
|
|
|
|
nouveau_connector_get_modes(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = connector->dev;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
2009-12-11 17:24:15 +08:00
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
2010-07-20 22:48:08 +08:00
|
|
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
2009-12-11 17:24:15 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2010-06-01 11:32:42 +08:00
|
|
|
/* destroy the native mode, the attached monitor could have changed.
|
2009-12-11 17:24:15 +08:00
|
|
|
*/
|
2010-06-01 11:32:42 +08:00
|
|
|
if (nv_connector->native_mode) {
|
2009-12-11 17:24:15 +08:00
|
|
|
drm_mode_destroy(dev, nv_connector->native_mode);
|
|
|
|
nv_connector->native_mode = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nv_connector->edid)
|
|
|
|
ret = drm_add_edid_modes(connector, nv_connector->edid);
|
2010-06-01 11:32:42 +08:00
|
|
|
else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS &&
|
2010-06-01 11:32:42 +08:00
|
|
|
(nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
|
2012-07-31 14:16:21 +08:00
|
|
|
drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) {
|
2010-09-23 14:37:39 +08:00
|
|
|
struct drm_display_mode mode;
|
|
|
|
|
|
|
|
nouveau_bios_fp_mode(dev, &mode);
|
|
|
|
nv_connector->native_mode = drm_mode_duplicate(dev, &mode);
|
2010-06-01 11:32:42 +08:00
|
|
|
}
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2011-11-15 12:31:13 +08:00
|
|
|
/* Determine display colour depth for everything except LVDS now,
|
|
|
|
* DP requires this before mode_valid() is called.
|
|
|
|
*/
|
|
|
|
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
|
|
|
|
nouveau_connector_detect_depth(connector);
|
|
|
|
|
2009-12-11 17:24:15 +08:00
|
|
|
/* Find the native mode if this is a digital panel, if we didn't
|
|
|
|
* find any modes through DDC previously add the native mode to
|
|
|
|
* the list of modes.
|
|
|
|
*/
|
|
|
|
if (!nv_connector->native_mode)
|
2016-11-04 15:20:35 +08:00
|
|
|
nv_connector->native_mode = nouveau_conn_native_mode(connector);
|
2009-12-11 17:24:15 +08:00
|
|
|
if (ret == 0 && nv_connector->native_mode) {
|
|
|
|
struct drm_display_mode *mode;
|
|
|
|
|
|
|
|
mode = drm_mode_duplicate(dev, nv_connector->native_mode);
|
|
|
|
drm_mode_probed_add(connector, mode);
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
2011-11-15 12:31:13 +08:00
|
|
|
/* Determine LVDS colour depth, must happen after determining
|
|
|
|
* "native" mode as some VBIOS tables require us to use the
|
|
|
|
* pixel clock as part of the lookup...
|
2011-10-17 08:24:49 +08:00
|
|
|
*/
|
2011-11-15 12:31:13 +08:00
|
|
|
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
|
|
|
nouveau_connector_detect_depth(connector);
|
2011-10-17 08:24:49 +08:00
|
|
|
|
2012-07-11 08:44:20 +08:00
|
|
|
if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
|
2010-07-20 22:48:08 +08:00
|
|
|
ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
if (nv_connector->type == DCB_CONNECTOR_LVDS ||
|
|
|
|
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG ||
|
|
|
|
nv_connector->type == DCB_CONNECTOR_eDP)
|
2009-12-11 17:24:15 +08:00
|
|
|
ret += nouveau_connector_scaler_modes_add(connector);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-10-02 22:00:35 +08:00
|
|
|
static unsigned
|
2018-09-04 08:57:37 +08:00
|
|
|
get_tmds_link_bandwidth(struct drm_connector *connector)
|
2010-10-02 22:00:35 +08:00
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
2018-09-04 08:57:37 +08:00
|
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
2012-07-11 08:44:20 +08:00
|
|
|
struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
|
2018-09-04 08:57:37 +08:00
|
|
|
struct drm_display_info *info = NULL;
|
2019-05-29 07:58:18 +08:00
|
|
|
unsigned duallink_scale =
|
2018-09-04 08:57:37 +08:00
|
|
|
nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1;
|
|
|
|
|
2019-05-29 07:58:18 +08:00
|
|
|
if (drm_detect_hdmi_monitor(nv_connector->edid)) {
|
2018-09-04 08:57:37 +08:00
|
|
|
info = &nv_connector->base.display_info;
|
2019-05-29 07:58:18 +08:00
|
|
|
duallink_scale = 1;
|
|
|
|
}
|
2010-10-02 22:00:35 +08:00
|
|
|
|
2018-09-04 08:57:37 +08:00
|
|
|
if (info) {
|
2015-11-04 10:00:11 +08:00
|
|
|
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.
|
|
|
|
*/
|
2018-09-04 08:57:37 +08:00
|
|
|
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;
|
|
|
|
}
|
2016-05-18 11:57:42 +08:00
|
|
|
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
|
2015-11-04 10:00:11 +08:00
|
|
|
return 297000;
|
2016-05-18 11:57:42 +08:00
|
|
|
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
|
2015-11-04 10:00:11 +08:00
|
|
|
return 225000;
|
|
|
|
}
|
2019-05-29 07:58:18 +08:00
|
|
|
|
2010-10-02 22:00:35 +08:00
|
|
|
if (dcb->location != DCB_LOC_ON_CHIP ||
|
2016-05-18 11:57:42 +08:00
|
|
|
drm->client.device.info.chipset >= 0x46)
|
2018-09-04 08:57:37 +08:00
|
|
|
return 165000 * duallink_scale;
|
2016-05-18 11:57:42 +08:00
|
|
|
else if (drm->client.device.info.chipset >= 0x40)
|
2018-09-04 08:57:37 +08:00
|
|
|
return 155000 * duallink_scale;
|
2016-05-18 11:57:42 +08:00
|
|
|
else if (drm->client.device.info.chipset >= 0x18)
|
2018-09-04 08:57:37 +08:00
|
|
|
return 135000 * duallink_scale;
|
2010-10-02 22:00:35 +08:00
|
|
|
else
|
2018-09-04 08:57:37 +08:00
|
|
|
return 112000 * duallink_scale;
|
2010-10-02 22:00:35 +08:00
|
|
|
}
|
|
|
|
|
2018-04-24 21:15:10 +08:00
|
|
|
static enum drm_mode_status
|
2009-12-11 17:24:15 +08:00
|
|
|
nouveau_connector_mode_valid(struct drm_connector *connector,
|
|
|
|
struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
2010-07-20 22:48:08 +08:00
|
|
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
2020-09-30 06:31:31 +08:00
|
|
|
unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
|
|
|
switch (nv_encoder->dcb->type) {
|
2012-07-11 08:44:20 +08:00
|
|
|
case DCB_OUTPUT_LVDS:
|
2010-05-28 08:13:16 +08:00
|
|
|
if (nv_connector->native_mode &&
|
|
|
|
(mode->hdisplay > nv_connector->native_mode->hdisplay ||
|
|
|
|
mode->vdisplay > nv_connector->native_mode->vdisplay))
|
2009-12-11 17:24:15 +08:00
|
|
|
return MODE_PANEL;
|
|
|
|
|
|
|
|
min_clock = 0;
|
|
|
|
max_clock = 400000;
|
|
|
|
break;
|
2012-07-11 08:44:20 +08:00
|
|
|
case DCB_OUTPUT_TMDS:
|
2018-09-04 08:57:37 +08:00
|
|
|
max_clock = get_tmds_link_bandwidth(connector);
|
2009-12-11 17:24:15 +08:00
|
|
|
break;
|
2012-07-11 08:44:20 +08:00
|
|
|
case DCB_OUTPUT_ANALOG:
|
2009-12-11 17:24:15 +08:00
|
|
|
max_clock = nv_encoder->dcb->crtconf.maxfreq;
|
|
|
|
if (!max_clock)
|
|
|
|
max_clock = 350000;
|
|
|
|
break;
|
2012-07-11 08:44:20 +08:00
|
|
|
case DCB_OUTPUT_TV:
|
2010-07-20 22:48:08 +08:00
|
|
|
return get_slave_funcs(encoder)->mode_valid(encoder, mode);
|
2012-07-11 08:44:20 +08:00
|
|
|
case DCB_OUTPUT_DP:
|
2020-05-12 06:41:27 +08:00
|
|
|
return nv50_dp_mode_valid(connector, nv_encoder, mode, NULL);
|
2010-02-24 08:31:39 +08:00
|
|
|
default:
|
2016-03-03 10:56:33 +08:00
|
|
|
BUG();
|
2010-02-24 08:31:39 +08:00
|
|
|
return MODE_BAD;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2020-09-30 06:31:31 +08:00
|
|
|
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
|
|
|
clock *= 2;
|
|
|
|
|
|
|
|
if (clock < min_clock)
|
|
|
|
return MODE_CLOCK_LOW;
|
|
|
|
if (clock > max_clock)
|
|
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
|
|
|
|
return MODE_OK;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_encoder *
|
|
|
|
nouveau_connector_best_encoder(struct drm_connector *connector)
|
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
|
|
|
|
|
|
if (nv_connector->detected_encoder)
|
|
|
|
return to_drm_encoder(nv_connector->detected_encoder);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_connector_helper_funcs
|
|
|
|
nouveau_connector_helper_funcs = {
|
|
|
|
.get_modes = nouveau_connector_get_modes,
|
|
|
|
.mode_valid = nouveau_connector_mode_valid,
|
|
|
|
.best_encoder = nouveau_connector_best_encoder,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct drm_connector_funcs
|
|
|
|
nouveau_connector_funcs = {
|
2017-07-25 16:01:21 +08:00
|
|
|
.dpms = drm_helper_connector_dpms,
|
2016-11-04 15:20:35 +08:00
|
|
|
.reset = nouveau_conn_reset,
|
2009-12-11 17:24:15 +08:00
|
|
|
.detect = nouveau_connector_detect,
|
2016-11-04 15:20:35 +08:00
|
|
|
.force = nouveau_connector_force,
|
2009-12-11 17:24:15 +08:00
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
|
|
.set_property = nouveau_connector_set_property,
|
2016-11-04 15:20:35 +08:00
|
|
|
.destroy = nouveau_connector_destroy,
|
|
|
|
.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
|
|
|
|
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
|
|
|
.atomic_set_property = nouveau_conn_atomic_set_property,
|
|
|
|
.atomic_get_property = nouveau_conn_atomic_get_property,
|
2018-09-07 05:43:23 +08:00
|
|
|
.late_register = nouveau_connector_late_register,
|
|
|
|
.early_unregister = nouveau_connector_early_unregister,
|
2009-12-11 17:24:15 +08:00
|
|
|
};
|
|
|
|
|
2010-06-01 11:32:42 +08:00
|
|
|
static const struct drm_connector_funcs
|
|
|
|
nouveau_connector_funcs_lvds = {
|
2017-07-25 16:01:21 +08:00
|
|
|
.dpms = drm_helper_connector_dpms,
|
2016-11-04 15:20:35 +08:00
|
|
|
.reset = nouveau_conn_reset,
|
2010-06-01 11:32:42 +08:00
|
|
|
.detect = nouveau_connector_detect_lvds,
|
2016-11-04 15:20:35 +08:00
|
|
|
.force = nouveau_connector_force,
|
2010-06-01 11:32:42 +08:00
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
|
|
.set_property = nouveau_connector_set_property,
|
2016-11-04 15:20:35 +08:00
|
|
|
.destroy = nouveau_connector_destroy,
|
|
|
|
.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
|
|
|
|
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
|
|
|
.atomic_set_property = nouveau_conn_atomic_set_property,
|
|
|
|
.atomic_get_property = nouveau_conn_atomic_get_property,
|
2018-09-07 05:43:23 +08:00
|
|
|
.late_register = nouveau_connector_late_register,
|
|
|
|
.early_unregister = nouveau_connector_early_unregister,
|
2010-06-01 11:32:42 +08:00
|
|
|
};
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2020-08-27 02:24:48 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-08-10 02:10:20 +08:00
|
|
|
static int
|
2014-08-10 02:10:28 +08:00
|
|
|
nouveau_connector_hotplug(struct nvif_notify *notify)
|
2013-02-03 10:56:16 +08:00
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector =
|
2014-08-10 02:10:20 +08:00
|
|
|
container_of(notify, typeof(*nv_connector), hpd);
|
2013-02-03 10:56:16 +08:00
|
|
|
struct drm_connector *connector = &nv_connector->base;
|
drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling
First some backstory here: Currently, we keep track of whether or not
we've enabled MST or not by trying to piggy-back off the MST helpers.
This means that in order to check whether MST is enabled or not, we
actually need to grab drm_dp_mst_topology_mgr.lock.
Back when I originally wrote this, I did this piggy-backing with the
intention that I'd eventually be teaching our MST helpers how to recover
when an MST device has stopped responding, which in turn would require
the MST helpers having a way of disabling MST independently of the
driver. Note that this was before I reworked locking in the MST helpers,
so at the time we were sticking random things under &mgr->lock - which
grabbing this lock was meant to protect against.
This never came to fruition because doing such a reset safely turned out
to be a lot more painful and impossible then it sounds, and also just
risks us working around issues with our MST handlers that should be
properly fixed instead. Even if it did though, simply calling
drm_dp_mst_topology_mgr_set_mst() from the MST helpers (with the
exception of when we're tearing down our MST managers, that's always OK)
wouldn't have been a bad idea, since drivers like nouveau and i915 need
to do their own book keeping immediately after disabling MST.
So-implementing that would likely require adding a hook for
helper-triggered MST disables anyway.
So, fast forward to now - we want to start adding support for all of the
miscellaneous bits of the DP protocol (for both SST and MST) we're
missing before moving on to supporting more complicated features like
supporting different BPP values on MST, DSC, etc. Since many of these
features only exist on SST and make use of DP HPD IRQs, we want to be
able to atomically check whether we're servicing an MST IRQ or SST IRQ
in nouveau_connector_hotplug(). Currently we literally don't do this at
all, and just handle any kind of possible DP IRQ we could get including
ESIs - even if MST isn't actually enabled.
This would be very complicated and difficult to fix if we need to hold
&mgr->lock while handling SST IRQs to ensure that the MST topology
state doesn't change under us. What we really want here is to do our own
tracking of whether MST is enabled or not, similar to drivers like i915,
and define our own locking order to decomplicate things and avoid
hitting locking issues in the future.
So, let's do this by refactoring our MST probing/enabling code to use
our own MST bookkeeping, along with adding a lock for protecting DP
state that needs to be checked outside of our connector probing
functions. While we're at it, we also remove a bunch of unneeded steps
we perform when probing/enabling MST:
* Enabling bits in MSTM_CTRL before calling drm_dp_mst_topology_mgr_set_mst().
I don't think these ever actually did anything, since the nvif methods
for enabling MST don't actually do anything DPCD related and merely
indicate to nvkm that we've turned on MST.
* Checking the MSTM_CTRL bit is intact when checking the state of an
enabled MST topology in nv50_mstm_detect(). I just added this to be safe
originally, but now that we try reading the DPCD when probing DP
connectors it shouldn't be needed as that will abort our hotplug probing
if the device was removed well before we start checking for MST..
* All of the duplicate DPCD version checks.
This leaves us with much nicer looking code, a much more sensible
locking scheme, and an easy way of checking whether MST is enabled or
not for handling DP HPD IRQs.
v2:
* Get rid of accidental newlines
v4:
* Fix uninitialized usage of mstm in nv50_mstm_detect() - thanks kernel
bot!
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200826182456.322681-9-lyude@redhat.com
2020-08-27 02:24:44 +08:00
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
2014-08-10 02:10:20 +08:00
|
|
|
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
|
2019-07-04 06:35:37 +08:00
|
|
|
bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
|
|
|
|
|
|
|
|
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
|
drm/nouveau/kms/nv50-: Refactor and cleanup DP HPD handling
First some backstory here: Currently, we keep track of whether or not
we've enabled MST or not by trying to piggy-back off the MST helpers.
This means that in order to check whether MST is enabled or not, we
actually need to grab drm_dp_mst_topology_mgr.lock.
Back when I originally wrote this, I did this piggy-backing with the
intention that I'd eventually be teaching our MST helpers how to recover
when an MST device has stopped responding, which in turn would require
the MST helpers having a way of disabling MST independently of the
driver. Note that this was before I reworked locking in the MST helpers,
so at the time we were sticking random things under &mgr->lock - which
grabbing this lock was meant to protect against.
This never came to fruition because doing such a reset safely turned out
to be a lot more painful and impossible then it sounds, and also just
risks us working around issues with our MST handlers that should be
properly fixed instead. Even if it did though, simply calling
drm_dp_mst_topology_mgr_set_mst() from the MST helpers (with the
exception of when we're tearing down our MST managers, that's always OK)
wouldn't have been a bad idea, since drivers like nouveau and i915 need
to do their own book keeping immediately after disabling MST.
So-implementing that would likely require adding a hook for
helper-triggered MST disables anyway.
So, fast forward to now - we want to start adding support for all of the
miscellaneous bits of the DP protocol (for both SST and MST) we're
missing before moving on to supporting more complicated features like
supporting different BPP values on MST, DSC, etc. Since many of these
features only exist on SST and make use of DP HPD IRQs, we want to be
able to atomically check whether we're servicing an MST IRQ or SST IRQ
in nouveau_connector_hotplug(). Currently we literally don't do this at
all, and just handle any kind of possible DP IRQ we could get including
ESIs - even if MST isn't actually enabled.
This would be very complicated and difficult to fix if we need to hold
&mgr->lock while handling SST IRQs to ensure that the MST topology
state doesn't change under us. What we really want here is to do our own
tracking of whether MST is enabled or not, similar to drivers like i915,
and define our own locking order to decomplicate things and avoid
hitting locking issues in the future.
So, let's do this by refactoring our MST probing/enabling code to use
our own MST bookkeeping, along with adding a lock for protecting DP
state that needs to be checked outside of our connector probing
functions. While we're at it, we also remove a bunch of unneeded steps
we perform when probing/enabling MST:
* Enabling bits in MSTM_CTRL before calling drm_dp_mst_topology_mgr_set_mst().
I don't think these ever actually did anything, since the nvif methods
for enabling MST don't actually do anything DPCD related and merely
indicate to nvkm that we've turned on MST.
* Checking the MSTM_CTRL bit is intact when checking the state of an
enabled MST topology in nv50_mstm_detect(). I just added this to be safe
originally, but now that we try reading the DPCD when probing DP
connectors it shouldn't be needed as that will abort our hotplug probing
if the device was removed well before we start checking for MST..
* All of the duplicate DPCD version checks.
This leaves us with much nicer looking code, a much more sensible
locking scheme, and an easy way of checking whether MST is enabled or
not for handling DP HPD IRQs.
v2:
* Get rid of accidental newlines
v4:
* Fix uninitialized usage of mstm in nv50_mstm_detect() - thanks kernel
bot!
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200826182456.322681-9-lyude@redhat.com
2020-08-27 02:24:44 +08:00
|
|
|
nouveau_dp_irq(drm, nv_connector);
|
2019-07-04 06:35:37 +08:00
|
|
|
return NVIF_NOTIFY_KEEP;
|
|
|
|
}
|
drm/nouveau: Fix deadlocks in nouveau_connector_detect()
When we disable hotplugging on the GPU, we need to be able to
synchronize with each connector's hotplug interrupt handler before the
interrupt is finally disabled. This can be a problem however, since
nouveau_connector_detect() currently grabs a runtime power reference
when handling connector probing. This will deadlock the runtime suspend
handler like so:
[ 861.480896] INFO: task kworker/0:2:61 blocked for more than 120 seconds.
[ 861.483290] Tainted: G O 4.18.0-rc6Lyude-Test+ #1
[ 861.485158] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 861.486332] kworker/0:2 D 0 61 2 0x80000000
[ 861.487044] Workqueue: events nouveau_display_hpd_work [nouveau]
[ 861.487737] Call Trace:
[ 861.488394] __schedule+0x322/0xaf0
[ 861.489070] schedule+0x33/0x90
[ 861.489744] rpm_resume+0x19c/0x850
[ 861.490392] ? finish_wait+0x90/0x90
[ 861.491068] __pm_runtime_resume+0x4e/0x90
[ 861.491753] nouveau_display_hpd_work+0x22/0x60 [nouveau]
[ 861.492416] process_one_work+0x231/0x620
[ 861.493068] worker_thread+0x44/0x3a0
[ 861.493722] kthread+0x12b/0x150
[ 861.494342] ? wq_pool_ids_show+0x140/0x140
[ 861.494991] ? kthread_create_worker_on_cpu+0x70/0x70
[ 861.495648] ret_from_fork+0x3a/0x50
[ 861.496304] INFO: task kworker/6:2:320 blocked for more than 120 seconds.
[ 861.496968] Tainted: G O 4.18.0-rc6Lyude-Test+ #1
[ 861.497654] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 861.498341] kworker/6:2 D 0 320 2 0x80000080
[ 861.499045] Workqueue: pm pm_runtime_work
[ 861.499739] Call Trace:
[ 861.500428] __schedule+0x322/0xaf0
[ 861.501134] ? wait_for_completion+0x104/0x190
[ 861.501851] schedule+0x33/0x90
[ 861.502564] schedule_timeout+0x3a5/0x590
[ 861.503284] ? mark_held_locks+0x58/0x80
[ 861.503988] ? _raw_spin_unlock_irq+0x2c/0x40
[ 861.504710] ? wait_for_completion+0x104/0x190
[ 861.505417] ? trace_hardirqs_on_caller+0xf4/0x190
[ 861.506136] ? wait_for_completion+0x104/0x190
[ 861.506845] wait_for_completion+0x12c/0x190
[ 861.507555] ? wake_up_q+0x80/0x80
[ 861.508268] flush_work+0x1c9/0x280
[ 861.508990] ? flush_workqueue_prep_pwqs+0x1b0/0x1b0
[ 861.509735] nvif_notify_put+0xb1/0xc0 [nouveau]
[ 861.510482] nouveau_display_fini+0xbd/0x170 [nouveau]
[ 861.511241] nouveau_display_suspend+0x67/0x120 [nouveau]
[ 861.511969] nouveau_do_suspend+0x5e/0x2d0 [nouveau]
[ 861.512715] nouveau_pmops_runtime_suspend+0x47/0xb0 [nouveau]
[ 861.513435] pci_pm_runtime_suspend+0x6b/0x180
[ 861.514165] ? pci_has_legacy_pm_support+0x70/0x70
[ 861.514897] __rpm_callback+0x7a/0x1d0
[ 861.515618] ? pci_has_legacy_pm_support+0x70/0x70
[ 861.516313] rpm_callback+0x24/0x80
[ 861.517027] ? pci_has_legacy_pm_support+0x70/0x70
[ 861.517741] rpm_suspend+0x142/0x6b0
[ 861.518449] pm_runtime_work+0x97/0xc0
[ 861.519144] process_one_work+0x231/0x620
[ 861.519831] worker_thread+0x44/0x3a0
[ 861.520522] kthread+0x12b/0x150
[ 861.521220] ? wq_pool_ids_show+0x140/0x140
[ 861.521925] ? kthread_create_worker_on_cpu+0x70/0x70
[ 861.522622] ret_from_fork+0x3a/0x50
[ 861.523299] INFO: task kworker/6:0:1329 blocked for more than 120 seconds.
[ 861.523977] Tainted: G O 4.18.0-rc6Lyude-Test+ #1
[ 861.524644] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 861.525349] kworker/6:0 D 0 1329 2 0x80000000
[ 861.526073] Workqueue: events nvif_notify_work [nouveau]
[ 861.526751] Call Trace:
[ 861.527411] __schedule+0x322/0xaf0
[ 861.528089] schedule+0x33/0x90
[ 861.528758] rpm_resume+0x19c/0x850
[ 861.529399] ? finish_wait+0x90/0x90
[ 861.530073] __pm_runtime_resume+0x4e/0x90
[ 861.530798] nouveau_connector_detect+0x7e/0x510 [nouveau]
[ 861.531459] ? ww_mutex_lock+0x47/0x80
[ 861.532097] ? ww_mutex_lock+0x47/0x80
[ 861.532819] ? drm_modeset_lock+0x88/0x130 [drm]
[ 861.533481] drm_helper_probe_detect_ctx+0xa0/0x100 [drm_kms_helper]
[ 861.534127] drm_helper_hpd_irq_event+0xa4/0x120 [drm_kms_helper]
[ 861.534940] nouveau_connector_hotplug+0x98/0x120 [nouveau]
[ 861.535556] nvif_notify_work+0x2d/0xb0 [nouveau]
[ 861.536221] process_one_work+0x231/0x620
[ 861.536994] worker_thread+0x44/0x3a0
[ 861.537757] kthread+0x12b/0x150
[ 861.538463] ? wq_pool_ids_show+0x140/0x140
[ 861.539102] ? kthread_create_worker_on_cpu+0x70/0x70
[ 861.539815] ret_from_fork+0x3a/0x50
[ 861.540521]
Showing all locks held in the system:
[ 861.541696] 2 locks held by kworker/0:2/61:
[ 861.542406] #0: 000000002dbf8af5 ((wq_completion)"events"){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.543071] #1: 0000000076868126 ((work_completion)(&drm->hpd_work)){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.543814] 1 lock held by khungtaskd/64:
[ 861.544535] #0: 0000000059db4b53 (rcu_read_lock){....}, at: debug_show_all_locks+0x23/0x185
[ 861.545160] 3 locks held by kworker/6:2/320:
[ 861.545896] #0: 00000000d9e1bc59 ((wq_completion)"pm"){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.546702] #1: 00000000c9f92d84 ((work_completion)(&dev->power.work)){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.547443] #2: 000000004afc5de1 (drm_connector_list_iter){.+.+}, at: nouveau_display_fini+0x96/0x170 [nouveau]
[ 861.548146] 1 lock held by dmesg/983:
[ 861.548889] 2 locks held by zsh/1250:
[ 861.549605] #0: 00000000348e3cf6 (&tty->ldisc_sem){++++}, at: ldsem_down_read+0x37/0x40
[ 861.550393] #1: 000000007009a7a8 (&ldata->atomic_read_lock){+.+.}, at: n_tty_read+0xc1/0x870
[ 861.551122] 6 locks held by kworker/6:0/1329:
[ 861.551957] #0: 000000002dbf8af5 ((wq_completion)"events"){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.552765] #1: 00000000ddb499ad ((work_completion)(¬ify->work)#2){+.+.}, at: process_one_work+0x1b3/0x620
[ 861.553582] #2: 000000006e013cbe (&dev->mode_config.mutex){+.+.}, at: drm_helper_hpd_irq_event+0x6c/0x120 [drm_kms_helper]
[ 861.554357] #3: 000000004afc5de1 (drm_connector_list_iter){.+.+}, at: drm_helper_hpd_irq_event+0x78/0x120 [drm_kms_helper]
[ 861.555227] #4: 0000000044f294d9 (crtc_ww_class_acquire){+.+.}, at: drm_helper_probe_detect_ctx+0x3d/0x100 [drm_kms_helper]
[ 861.556133] #5: 00000000db193642 (crtc_ww_class_mutex){+.+.}, at: drm_modeset_lock+0x4b/0x130 [drm]
[ 861.557864] =============================================
[ 861.559507] NMI backtrace for cpu 2
[ 861.560363] CPU: 2 PID: 64 Comm: khungtaskd Tainted: G O 4.18.0-rc6Lyude-Test+ #1
[ 861.561197] Hardware name: LENOVO 20EQS64N0B/20EQS64N0B, BIOS N1EET78W (1.51 ) 05/18/2018
[ 861.561948] Call Trace:
[ 861.562757] dump_stack+0x8e/0xd3
[ 861.563516] nmi_cpu_backtrace.cold.3+0x14/0x5a
[ 861.564269] ? lapic_can_unplug_cpu.cold.27+0x42/0x42
[ 861.565029] nmi_trigger_cpumask_backtrace+0xa1/0xae
[ 861.565789] arch_trigger_cpumask_backtrace+0x19/0x20
[ 861.566558] watchdog+0x316/0x580
[ 861.567355] kthread+0x12b/0x150
[ 861.568114] ? reset_hung_task_detector+0x20/0x20
[ 861.568863] ? kthread_create_worker_on_cpu+0x70/0x70
[ 861.569598] ret_from_fork+0x3a/0x50
[ 861.570370] Sending NMI from CPU 2 to CPUs 0-1,3-7:
[ 861.571426] NMI backtrace for cpu 6 skipped: idling at intel_idle+0x7f/0x120
[ 861.571429] NMI backtrace for cpu 7 skipped: idling at intel_idle+0x7f/0x120
[ 861.571432] NMI backtrace for cpu 3 skipped: idling at intel_idle+0x7f/0x120
[ 861.571464] NMI backtrace for cpu 5 skipped: idling at intel_idle+0x7f/0x120
[ 861.571467] NMI backtrace for cpu 0 skipped: idling at intel_idle+0x7f/0x120
[ 861.571469] NMI backtrace for cpu 4 skipped: idling at intel_idle+0x7f/0x120
[ 861.571472] NMI backtrace for cpu 1 skipped: idling at intel_idle+0x7f/0x120
[ 861.572428] Kernel panic - not syncing: hung_task: blocked tasks
So: fix this by making it so that normal hotplug handling /only/ happens
so long as the GPU is currently awake without any pending runtime PM
requests. In the event that a hotplug occurs while the device is
suspending or resuming, we can simply defer our response until the GPU
is fully runtime resumed again.
Changes since v4:
- Use a new trick I came up with using pm_runtime_get() instead of the
hackish junk we had before
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Karol Herbst <kherbst@redhat.com>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
Cc: stable@vger.kernel.org
Cc: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
2018-08-16 03:00:15 +08:00
|
|
|
|
2020-08-27 02:24:48 +08:00
|
|
|
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", connector->name);
|
|
|
|
nouveau_connector_hpd(connector);
|
2019-07-04 06:35:37 +08:00
|
|
|
|
2014-08-10 02:10:28 +08:00
|
|
|
return NVIF_NOTIFY_KEEP;
|
2013-02-03 10:56:16 +08:00
|
|
|
}
|
|
|
|
|
2014-05-30 14:20:58 +08:00
|
|
|
static ssize_t
|
2015-08-20 12:54:15 +08:00
|
|
|
nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg)
|
2014-05-30 14:20:58 +08:00
|
|
|
{
|
|
|
|
struct nouveau_connector *nv_connector =
|
2015-08-20 12:54:15 +08:00
|
|
|
container_of(obj, typeof(*nv_connector), aux);
|
2014-05-30 14:20:58 +08:00
|
|
|
struct nouveau_encoder *nv_encoder;
|
2015-08-20 12:54:15 +08:00
|
|
|
struct nvkm_i2c_aux *aux;
|
2017-03-01 07:01:08 +08:00
|
|
|
u8 size = msg->size;
|
2014-05-30 14:20:58 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
|
2015-08-20 12:54:15 +08:00
|
|
|
if (!nv_encoder || !(aux = nv_encoder->aux))
|
2014-05-30 14:20:58 +08:00
|
|
|
return -ENODEV;
|
|
|
|
if (WARN_ON(msg->size > 16))
|
|
|
|
return -E2BIG;
|
|
|
|
|
2015-08-20 12:54:15 +08:00
|
|
|
ret = nvkm_i2c_aux_acquire(aux);
|
2014-05-30 14:20:58 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-08-20 12:54:15 +08:00
|
|
|
ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address,
|
2017-03-01 07:01:08 +08:00
|
|
|
msg->buffer, &size);
|
2015-08-20 12:54:15 +08:00
|
|
|
nvkm_i2c_aux_release(aux);
|
2014-05-30 14:20:58 +08:00
|
|
|
if (ret >= 0) {
|
|
|
|
msg->reply = ret;
|
2017-03-01 07:01:08 +08:00
|
|
|
return size;
|
2014-05-30 14:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
static int
|
|
|
|
drm_conntype_from_dcb(enum dcb_connector_type dcb)
|
|
|
|
{
|
|
|
|
switch (dcb) {
|
|
|
|
case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA;
|
|
|
|
case DCB_CONNECTOR_TV_0 :
|
|
|
|
case DCB_CONNECTOR_TV_1 :
|
|
|
|
case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV;
|
2012-03-22 09:27:15 +08:00
|
|
|
case DCB_CONNECTOR_DMS59_0 :
|
|
|
|
case DCB_CONNECTOR_DMS59_1 :
|
2011-11-18 08:23:59 +08:00
|
|
|
case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII;
|
|
|
|
case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID;
|
|
|
|
case DCB_CONNECTOR_LVDS :
|
|
|
|
case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
|
2012-01-12 14:17:16 +08:00
|
|
|
case DCB_CONNECTOR_DMS59_DP0:
|
|
|
|
case DCB_CONNECTOR_DMS59_DP1:
|
2018-12-11 12:50:02 +08:00
|
|
|
case DCB_CONNECTOR_DP :
|
|
|
|
case DCB_CONNECTOR_USB_C : return DRM_MODE_CONNECTOR_DisplayPort;
|
2011-11-18 08:23:59 +08:00
|
|
|
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
|
|
|
|
case DCB_CONNECTOR_HDMI_0 :
|
2014-02-24 11:41:08 +08:00
|
|
|
case DCB_CONNECTOR_HDMI_1 :
|
|
|
|
case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA;
|
2017-07-26 08:21:43 +08:00
|
|
|
case DCB_CONNECTOR_WFD : return DRM_MODE_CONNECTOR_VIRTUAL;
|
2011-11-18 08:23:59 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DRM_MODE_CONNECTOR_Unknown;
|
|
|
|
}
|
|
|
|
|
2010-06-28 12:35:50 +08:00
|
|
|
struct drm_connector *
|
2018-07-13 01:13:52 +08:00
|
|
|
nouveau_connector_create(struct drm_device *dev,
|
|
|
|
const struct dcb_output *dcbe)
|
2009-12-11 17:24:15 +08:00
|
|
|
{
|
2010-06-01 11:32:42 +08:00
|
|
|
const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
|
|
|
struct nouveau_display *disp = nouveau_display(dev);
|
2009-12-11 17:24:15 +08:00
|
|
|
struct nouveau_connector *nv_connector = NULL;
|
|
|
|
struct drm_connector *connector;
|
2018-07-14 01:06:32 +08:00
|
|
|
struct drm_connector_list_iter conn_iter;
|
2018-07-13 01:13:52 +08:00
|
|
|
char aux_name[48] = {0};
|
|
|
|
int index = dcbe->connector;
|
2010-06-16 21:52:44 +08:00
|
|
|
int type, ret = 0;
|
2011-11-18 08:23:59 +08:00
|
|
|
bool dummy;
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2018-07-14 01:06:32 +08:00
|
|
|
drm_connector_list_iter_begin(dev, &conn_iter);
|
2018-07-14 01:06:33 +08:00
|
|
|
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector = nouveau_connector(connector);
|
2018-07-14 01:06:32 +08:00
|
|
|
if (nv_connector->index == index) {
|
|
|
|
drm_connector_list_iter_end(&conn_iter);
|
2011-11-18 08:23:59 +08:00
|
|
|
return connector;
|
2018-07-14 01:06:32 +08:00
|
|
|
}
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
2018-07-14 01:06:32 +08:00
|
|
|
drm_connector_list_iter_end(&conn_iter);
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2010-02-24 10:07:31 +08:00
|
|
|
nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
|
|
|
|
if (!nv_connector)
|
2010-06-28 12:35:50 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2011-11-18 08:23:59 +08:00
|
|
|
|
2010-02-24 10:07:31 +08:00
|
|
|
connector = &nv_connector->base;
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->index = index;
|
|
|
|
|
|
|
|
/* attempt to parse vbios connector type and hotplug gpio */
|
2012-07-11 08:44:20 +08:00
|
|
|
nv_connector->dcb = olddcb_conn(dev, index);
|
2011-11-18 08:23:59 +08:00
|
|
|
if (nv_connector->dcb) {
|
|
|
|
u32 entry = ROM16(nv_connector->dcb[0]);
|
2012-07-11 08:44:20 +08:00
|
|
|
if (olddcb_conntab(dev)[3] >= 4)
|
2011-11-18 08:23:59 +08:00
|
|
|
entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
|
|
|
|
|
|
|
|
nv_connector->type = nv_connector->dcb[0];
|
|
|
|
if (drm_conntype_from_dcb(nv_connector->type) ==
|
|
|
|
DRM_MODE_CONNECTOR_Unknown) {
|
2012-07-31 14:16:21 +08:00
|
|
|
NV_WARN(drm, "unknown connector type %02x\n",
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type);
|
|
|
|
nv_connector->type = DCB_CONNECTOR_NONE;
|
|
|
|
}
|
2010-02-24 10:07:31 +08:00
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
/* Gigabyte NX85T */
|
|
|
|
if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) {
|
|
|
|
if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
|
|
|
|
nv_connector->type = DCB_CONNECTOR_DVI_I;
|
|
|
|
}
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
/* Gigabyte GV-NX86T512H */
|
|
|
|
if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
|
|
|
|
if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
|
|
|
|
nv_connector->type = DCB_CONNECTOR_DVI_I;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nv_connector->type = DCB_CONNECTOR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no vbios data, or an unknown dcb connector type - attempt to
|
|
|
|
* figure out something suitable ourselves
|
|
|
|
*/
|
|
|
|
if (nv_connector->type == DCB_CONNECTOR_NONE) {
|
2012-07-31 14:16:21 +08:00
|
|
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
|
|
|
struct dcb_table *dcbt = &drm->vbios.dcb;
|
2011-11-18 08:23:59 +08:00
|
|
|
u32 encoders = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < dcbt->entries; i++) {
|
|
|
|
if (dcbt->entry[i].connector == nv_connector->index)
|
|
|
|
encoders |= (1 << dcbt->entry[i].type);
|
|
|
|
}
|
2009-12-11 17:24:15 +08:00
|
|
|
|
2012-07-11 08:44:20 +08:00
|
|
|
if (encoders & (1 << DCB_OUTPUT_DP)) {
|
|
|
|
if (encoders & (1 << DCB_OUTPUT_TMDS))
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type = DCB_CONNECTOR_DP;
|
|
|
|
else
|
|
|
|
nv_connector->type = DCB_CONNECTOR_eDP;
|
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (encoders & (1 << DCB_OUTPUT_TMDS)) {
|
|
|
|
if (encoders & (1 << DCB_OUTPUT_ANALOG))
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type = DCB_CONNECTOR_DVI_I;
|
|
|
|
else
|
|
|
|
nv_connector->type = DCB_CONNECTOR_DVI_D;
|
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (encoders & (1 << DCB_OUTPUT_ANALOG)) {
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type = DCB_CONNECTOR_VGA;
|
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (encoders & (1 << DCB_OUTPUT_LVDS)) {
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type = DCB_CONNECTOR_LVDS;
|
|
|
|
} else
|
2012-07-11 08:44:20 +08:00
|
|
|
if (encoders & (1 << DCB_OUTPUT_TV)) {
|
2011-11-18 08:23:59 +08:00
|
|
|
nv_connector->type = DCB_CONNECTOR_TV_0;
|
|
|
|
}
|
|
|
|
}
|
2010-06-16 21:52:44 +08:00
|
|
|
|
2014-05-30 14:20:58 +08:00
|
|
|
switch ((type = drm_conntype_from_dcb(nv_connector->type))) {
|
|
|
|
case DRM_MODE_CONNECTOR_LVDS:
|
2011-11-18 08:23:59 +08:00
|
|
|
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
|
2010-06-16 21:52:44 +08:00
|
|
|
if (ret) {
|
2012-07-31 14:16:21 +08:00
|
|
|
NV_ERROR(drm, "Error parsing LVDS table, disabling\n");
|
2011-11-18 08:23:59 +08:00
|
|
|
kfree(nv_connector);
|
|
|
|
return ERR_PTR(ret);
|
2010-06-16 21:52:44 +08:00
|
|
|
}
|
2011-11-18 08:23:59 +08:00
|
|
|
|
|
|
|
funcs = &nouveau_connector_funcs_lvds;
|
2014-05-30 14:20:58 +08:00
|
|
|
break;
|
|
|
|
case DRM_MODE_CONNECTOR_DisplayPort:
|
|
|
|
case DRM_MODE_CONNECTOR_eDP:
|
2019-07-24 07:28:02 +08:00
|
|
|
nv_connector->aux.dev = connector->kdev;
|
2014-05-30 14:20:58 +08:00
|
|
|
nv_connector->aux.transfer = nouveau_connector_aux_xfer;
|
2018-07-13 01:13:52 +08:00
|
|
|
snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x",
|
|
|
|
dcbe->hasht, dcbe->hashm);
|
|
|
|
nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL);
|
2014-05-30 14:20:58 +08:00
|
|
|
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);
|
|
|
|
}
|
2016-11-04 15:20:36 +08:00
|
|
|
funcs = &nouveau_connector_funcs;
|
2014-05-30 14:20:58 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
funcs = &nouveau_connector_funcs;
|
|
|
|
break;
|
2010-02-24 10:07:31 +08:00
|
|
|
}
|
|
|
|
|
2017-04-12 01:11:25 +08:00
|
|
|
/* HDMI 3D support */
|
2018-05-08 18:39:47 +08:00
|
|
|
if ((disp->disp.object.oclass >= G82_DISP)
|
2017-04-12 01:11:25 +08:00
|
|
|
&& ((type == DRM_MODE_CONNECTOR_DisplayPort)
|
|
|
|
|| (type == DRM_MODE_CONNECTOR_eDP)
|
|
|
|
|| (type == DRM_MODE_CONNECTOR_HDMIA)))
|
|
|
|
connector->stereo_allowed = true;
|
|
|
|
|
2011-11-18 08:23:59 +08:00
|
|
|
/* defaults, will get overridden in detect() */
|
|
|
|
connector->interlace_allowed = false;
|
|
|
|
connector->doublescan_allowed = false;
|
|
|
|
|
|
|
|
drm_connector_init(dev, connector, funcs, type);
|
|
|
|
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
|
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
connector->funcs->reset(connector);
|
|
|
|
nouveau_conn_attach_properties(connector);
|
2012-01-22 06:13:26 +08:00
|
|
|
|
2016-11-04 15:20:35 +08:00
|
|
|
/* Default scaling mode */
|
2011-11-18 08:23:59 +08:00
|
|
|
switch (nv_connector->type) {
|
2014-12-22 16:19:45 +08:00
|
|
|
case DCB_CONNECTOR_LVDS:
|
|
|
|
case DCB_CONNECTOR_LVDS_SPWG:
|
|
|
|
case DCB_CONNECTOR_eDP:
|
|
|
|
/* see note in nouveau_connector_set_property() */
|
2018-05-08 18:39:47 +08:00
|
|
|
if (disp->disp.object.oclass < NV50_DISP) {
|
2014-12-22 16:19:45 +08:00
|
|
|
nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
|
|
|
|
break;
|
|
|
|
}
|
2010-02-24 12:01:40 +08:00
|
|
|
nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
|
|
|
|
break;
|
|
|
|
default:
|
2014-12-22 16:19:45 +08:00
|
|
|
nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
|
2014-12-22 16:15:55 +08:00
|
|
|
break;
|
|
|
|
}
|
2010-02-24 12:01:40 +08:00
|
|
|
|
2014-12-22 16:15:55 +08:00
|
|
|
/* dithering properties */
|
|
|
|
switch (nv_connector->type) {
|
|
|
|
case DCB_CONNECTOR_TV_0:
|
|
|
|
case DCB_CONNECTOR_TV_1:
|
|
|
|
case DCB_CONNECTOR_TV_3:
|
|
|
|
case DCB_CONNECTOR_VGA:
|
|
|
|
break;
|
|
|
|
default:
|
2016-11-04 15:20:35 +08:00
|
|
|
nv_connector->dithering_mode = DITHERING_MODE_AUTO;
|
2010-02-24 12:01:40 +08:00
|
|
|
break;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|
|
|
|
|
2018-08-27 16:06:51 +08:00
|
|
|
switch (type) {
|
|
|
|
case DRM_MODE_CONNECTOR_DisplayPort:
|
|
|
|
case DRM_MODE_CONNECTOR_eDP:
|
2019-08-14 18:44:59 +08:00
|
|
|
drm_dp_cec_register_connector(&nv_connector->aux, connector);
|
2018-08-27 16:06:51 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-06-08 12:47:37 +08:00
|
|
|
ret = nvif_notify_ctor(&disp->disp.object, "kmsHotplug",
|
|
|
|
nouveau_connector_hotplug,
|
2018-05-08 18:39:47 +08:00
|
|
|
true, NV04_DISP_NTFY_CONN,
|
2014-08-10 02:10:20 +08:00
|
|
|
&(struct nvif_notify_conn_req_v0) {
|
|
|
|
.mask = NVIF_NOTIFY_CONN_V0_ANY,
|
|
|
|
.conn = index,
|
|
|
|
},
|
|
|
|
sizeof(struct nvif_notify_conn_req_v0),
|
|
|
|
sizeof(struct nvif_notify_conn_rep_v0),
|
|
|
|
&nv_connector->hpd);
|
2014-05-30 10:49:17 +08:00
|
|
|
if (ret)
|
|
|
|
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
|
|
|
else
|
2013-02-03 10:56:16 +08:00
|
|
|
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
2010-11-11 14:14:56 +08:00
|
|
|
|
2014-05-29 23:57:41 +08:00
|
|
|
drm_connector_register(connector);
|
2011-11-18 08:23:59 +08:00
|
|
|
return connector;
|
2009-12-11 17:24:15 +08:00
|
|
|
}
|