18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maarten Maathuis.
38c2ecf20Sopenharmony_ci * All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
68c2ecf20Sopenharmony_ci * a copy of this software and associated documentation files (the
78c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
88c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
98c2ecf20Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to
108c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
118c2ecf20Sopenharmony_ci * the following conditions:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
148c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial
158c2ecf20Sopenharmony_ci * portions of the Software.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
188c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
198c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
208c2ecf20Sopenharmony_ci * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
218c2ecf20Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
228c2ecf20Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
238c2ecf20Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <acpi/button.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
308c2ecf20Sopenharmony_ci#include <linux/vga_switcheroo.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
338c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
348c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h>
358c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
368c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "nouveau_reg.h"
398c2ecf20Sopenharmony_ci#include "nouveau_drv.h"
408c2ecf20Sopenharmony_ci#include "dispnv04/hw.h"
418c2ecf20Sopenharmony_ci#include "dispnv50/disp.h"
428c2ecf20Sopenharmony_ci#include "nouveau_acpi.h"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include "nouveau_display.h"
458c2ecf20Sopenharmony_ci#include "nouveau_connector.h"
468c2ecf20Sopenharmony_ci#include "nouveau_encoder.h"
478c2ecf20Sopenharmony_ci#include "nouveau_crtc.h"
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <nvif/class.h>
508c2ecf20Sopenharmony_ci#include <nvif/cl0046.h>
518c2ecf20Sopenharmony_ci#include <nvif/event.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct drm_display_mode *
548c2ecf20Sopenharmony_cinouveau_conn_native_mode(struct drm_connector *connector)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	const struct drm_connector_helper_funcs *helper = connector->helper_private;
578c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
588c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
598c2ecf20Sopenharmony_ci	struct drm_display_mode *mode, *largest = NULL;
608c2ecf20Sopenharmony_ci	int high_w = 0, high_h = 0, high_v = 0;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	list_for_each_entry(mode, &connector->probed_modes, head) {
638c2ecf20Sopenharmony_ci		if (helper->mode_valid(connector, mode) != MODE_OK ||
648c2ecf20Sopenharmony_ci		    (mode->flags & DRM_MODE_FLAG_INTERLACE))
658c2ecf20Sopenharmony_ci			continue;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		/* Use preferred mode if there is one.. */
688c2ecf20Sopenharmony_ci		if (mode->type & DRM_MODE_TYPE_PREFERRED) {
698c2ecf20Sopenharmony_ci			NV_DEBUG(drm, "native mode from preferred\n");
708c2ecf20Sopenharmony_ci			return drm_mode_duplicate(dev, mode);
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		/* Otherwise, take the resolution with the largest width, then
748c2ecf20Sopenharmony_ci		 * height, then vertical refresh
758c2ecf20Sopenharmony_ci		 */
768c2ecf20Sopenharmony_ci		if (mode->hdisplay < high_w)
778c2ecf20Sopenharmony_ci			continue;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (mode->hdisplay == high_w && mode->vdisplay < high_h)
808c2ecf20Sopenharmony_ci			continue;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
838c2ecf20Sopenharmony_ci		    drm_mode_vrefresh(mode) < high_v)
848c2ecf20Sopenharmony_ci			continue;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		high_w = mode->hdisplay;
878c2ecf20Sopenharmony_ci		high_h = mode->vdisplay;
888c2ecf20Sopenharmony_ci		high_v = drm_mode_vrefresh(mode);
898c2ecf20Sopenharmony_ci		largest = mode;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	NV_DEBUG(drm, "native mode from largest: %dx%d@%d\n",
938c2ecf20Sopenharmony_ci		      high_w, high_h, high_v);
948c2ecf20Sopenharmony_ci	return largest ? drm_mode_duplicate(dev, largest) : NULL;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint
988c2ecf20Sopenharmony_cinouveau_conn_atomic_get_property(struct drm_connector *connector,
998c2ecf20Sopenharmony_ci				 const struct drm_connector_state *state,
1008c2ecf20Sopenharmony_ci				 struct drm_property *property, u64 *val)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
1038c2ecf20Sopenharmony_ci	struct nouveau_display *disp = nouveau_display(connector->dev);
1048c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (property == dev->mode_config.scaling_mode_property)
1078c2ecf20Sopenharmony_ci		*val = asyc->scaler.mode;
1088c2ecf20Sopenharmony_ci	else if (property == disp->underscan_property)
1098c2ecf20Sopenharmony_ci		*val = asyc->scaler.underscan.mode;
1108c2ecf20Sopenharmony_ci	else if (property == disp->underscan_hborder_property)
1118c2ecf20Sopenharmony_ci		*val = asyc->scaler.underscan.hborder;
1128c2ecf20Sopenharmony_ci	else if (property == disp->underscan_vborder_property)
1138c2ecf20Sopenharmony_ci		*val = asyc->scaler.underscan.vborder;
1148c2ecf20Sopenharmony_ci	else if (property == disp->dithering_mode)
1158c2ecf20Sopenharmony_ci		*val = asyc->dither.mode;
1168c2ecf20Sopenharmony_ci	else if (property == disp->dithering_depth)
1178c2ecf20Sopenharmony_ci		*val = asyc->dither.depth;
1188c2ecf20Sopenharmony_ci	else if (property == disp->vibrant_hue_property)
1198c2ecf20Sopenharmony_ci		*val = asyc->procamp.vibrant_hue;
1208c2ecf20Sopenharmony_ci	else if (property == disp->color_vibrance_property)
1218c2ecf20Sopenharmony_ci		*val = asyc->procamp.color_vibrance;
1228c2ecf20Sopenharmony_ci	else
1238c2ecf20Sopenharmony_ci		return -EINVAL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ciint
1298c2ecf20Sopenharmony_cinouveau_conn_atomic_set_property(struct drm_connector *connector,
1308c2ecf20Sopenharmony_ci				 struct drm_connector_state *state,
1318c2ecf20Sopenharmony_ci				 struct drm_property *property, u64 val)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
1348c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
1358c2ecf20Sopenharmony_ci	struct nouveau_display *disp = nouveau_display(dev);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (property == dev->mode_config.scaling_mode_property) {
1388c2ecf20Sopenharmony_ci		switch (val) {
1398c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_NONE:
1408c2ecf20Sopenharmony_ci			/* We allow 'None' for EDID modes, even on a fixed
1418c2ecf20Sopenharmony_ci			 * panel (some exist with support for lower refresh
1428c2ecf20Sopenharmony_ci			 * rates, which people might want to use for power-
1438c2ecf20Sopenharmony_ci			 * saving purposes).
1448c2ecf20Sopenharmony_ci			 *
1458c2ecf20Sopenharmony_ci			 * Non-EDID modes will force the use of GPU scaling
1468c2ecf20Sopenharmony_ci			 * to the native mode regardless of this setting.
1478c2ecf20Sopenharmony_ci			 */
1488c2ecf20Sopenharmony_ci			switch (connector->connector_type) {
1498c2ecf20Sopenharmony_ci			case DRM_MODE_CONNECTOR_LVDS:
1508c2ecf20Sopenharmony_ci			case DRM_MODE_CONNECTOR_eDP:
1518c2ecf20Sopenharmony_ci				/* ... except prior to G80, where the code
1528c2ecf20Sopenharmony_ci				 * doesn't support such things.
1538c2ecf20Sopenharmony_ci				 */
1548c2ecf20Sopenharmony_ci				if (disp->disp.object.oclass < NV50_DISP)
1558c2ecf20Sopenharmony_ci					return -EINVAL;
1568c2ecf20Sopenharmony_ci				break;
1578c2ecf20Sopenharmony_ci			default:
1588c2ecf20Sopenharmony_ci				break;
1598c2ecf20Sopenharmony_ci			}
1608c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_FULLSCREEN:
1618c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_CENTER:
1628c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_ASPECT:
1638c2ecf20Sopenharmony_ci			break;
1648c2ecf20Sopenharmony_ci		default:
1658c2ecf20Sopenharmony_ci			return -EINVAL;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		if (asyc->scaler.mode != val) {
1698c2ecf20Sopenharmony_ci			asyc->scaler.mode = val;
1708c2ecf20Sopenharmony_ci			asyc->set.scaler = true;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci	} else
1738c2ecf20Sopenharmony_ci	if (property == disp->underscan_property) {
1748c2ecf20Sopenharmony_ci		if (asyc->scaler.underscan.mode != val) {
1758c2ecf20Sopenharmony_ci			asyc->scaler.underscan.mode = val;
1768c2ecf20Sopenharmony_ci			asyc->set.scaler = true;
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci	} else
1798c2ecf20Sopenharmony_ci	if (property == disp->underscan_hborder_property) {
1808c2ecf20Sopenharmony_ci		if (asyc->scaler.underscan.hborder != val) {
1818c2ecf20Sopenharmony_ci			asyc->scaler.underscan.hborder = val;
1828c2ecf20Sopenharmony_ci			asyc->set.scaler = true;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	} else
1858c2ecf20Sopenharmony_ci	if (property == disp->underscan_vborder_property) {
1868c2ecf20Sopenharmony_ci		if (asyc->scaler.underscan.vborder != val) {
1878c2ecf20Sopenharmony_ci			asyc->scaler.underscan.vborder = val;
1888c2ecf20Sopenharmony_ci			asyc->set.scaler = true;
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci	} else
1918c2ecf20Sopenharmony_ci	if (property == disp->dithering_mode) {
1928c2ecf20Sopenharmony_ci		if (asyc->dither.mode != val) {
1938c2ecf20Sopenharmony_ci			asyc->dither.mode = val;
1948c2ecf20Sopenharmony_ci			asyc->set.dither = true;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	} else
1978c2ecf20Sopenharmony_ci	if (property == disp->dithering_depth) {
1988c2ecf20Sopenharmony_ci		if (asyc->dither.mode != val) {
1998c2ecf20Sopenharmony_ci			asyc->dither.depth = val;
2008c2ecf20Sopenharmony_ci			asyc->set.dither = true;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	} else
2038c2ecf20Sopenharmony_ci	if (property == disp->vibrant_hue_property) {
2048c2ecf20Sopenharmony_ci		if (asyc->procamp.vibrant_hue != val) {
2058c2ecf20Sopenharmony_ci			asyc->procamp.vibrant_hue = val;
2068c2ecf20Sopenharmony_ci			asyc->set.procamp = true;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci	} else
2098c2ecf20Sopenharmony_ci	if (property == disp->color_vibrance_property) {
2108c2ecf20Sopenharmony_ci		if (asyc->procamp.color_vibrance != val) {
2118c2ecf20Sopenharmony_ci			asyc->procamp.color_vibrance = val;
2128c2ecf20Sopenharmony_ci			asyc->set.procamp = true;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci	} else {
2158c2ecf20Sopenharmony_ci		return -EINVAL;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_civoid
2228c2ecf20Sopenharmony_cinouveau_conn_atomic_destroy_state(struct drm_connector *connector,
2238c2ecf20Sopenharmony_ci				  struct drm_connector_state *state)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc = nouveau_conn_atom(state);
2268c2ecf20Sopenharmony_ci	__drm_atomic_helper_connector_destroy_state(&asyc->state);
2278c2ecf20Sopenharmony_ci	kfree(asyc);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistruct drm_connector_state *
2318c2ecf20Sopenharmony_cinouveau_conn_atomic_duplicate_state(struct drm_connector *connector)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *armc = nouveau_conn_atom(connector->state);
2348c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc;
2358c2ecf20Sopenharmony_ci	if (!(asyc = kmalloc(sizeof(*asyc), GFP_KERNEL)))
2368c2ecf20Sopenharmony_ci		return NULL;
2378c2ecf20Sopenharmony_ci	__drm_atomic_helper_connector_duplicate_state(connector, &asyc->state);
2388c2ecf20Sopenharmony_ci	asyc->dither = armc->dither;
2398c2ecf20Sopenharmony_ci	asyc->scaler = armc->scaler;
2408c2ecf20Sopenharmony_ci	asyc->procamp = armc->procamp;
2418c2ecf20Sopenharmony_ci	asyc->set.mask = 0;
2428c2ecf20Sopenharmony_ci	return &asyc->state;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_civoid
2468c2ecf20Sopenharmony_cinouveau_conn_reset(struct drm_connector *connector)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
2498c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(connector->dev)) {
2528c2ecf20Sopenharmony_ci		if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL))))
2538c2ecf20Sopenharmony_ci			return;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		if (connector->state)
2568c2ecf20Sopenharmony_ci			nouveau_conn_atomic_destroy_state(connector,
2578c2ecf20Sopenharmony_ci							  connector->state);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		__drm_atomic_helper_connector_reset(connector, &asyc->state);
2608c2ecf20Sopenharmony_ci	} else {
2618c2ecf20Sopenharmony_ci		asyc = &nv_connector->properties_state;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	asyc->dither.mode = DITHERING_MODE_AUTO;
2658c2ecf20Sopenharmony_ci	asyc->dither.depth = DITHERING_DEPTH_AUTO;
2668c2ecf20Sopenharmony_ci	asyc->scaler.mode = DRM_MODE_SCALE_NONE;
2678c2ecf20Sopenharmony_ci	asyc->scaler.underscan.mode = UNDERSCAN_OFF;
2688c2ecf20Sopenharmony_ci	asyc->procamp.color_vibrance = 150;
2698c2ecf20Sopenharmony_ci	asyc->procamp.vibrant_hue = 90;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (nouveau_display(connector->dev)->disp.object.oclass < NV50_DISP) {
2728c2ecf20Sopenharmony_ci		switch (connector->connector_type) {
2738c2ecf20Sopenharmony_ci		case DRM_MODE_CONNECTOR_LVDS:
2748c2ecf20Sopenharmony_ci			/* See note in nouveau_conn_atomic_set_property(). */
2758c2ecf20Sopenharmony_ci			asyc->scaler.mode = DRM_MODE_SCALE_FULLSCREEN;
2768c2ecf20Sopenharmony_ci			break;
2778c2ecf20Sopenharmony_ci		default:
2788c2ecf20Sopenharmony_ci			break;
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_civoid
2848c2ecf20Sopenharmony_cinouveau_conn_attach_properties(struct drm_connector *connector)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
2878c2ecf20Sopenharmony_ci	struct nouveau_display *disp = nouveau_display(dev);
2888c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
2898c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *armc;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(connector->dev))
2928c2ecf20Sopenharmony_ci		armc = nouveau_conn_atom(connector->state);
2938c2ecf20Sopenharmony_ci	else
2948c2ecf20Sopenharmony_ci		armc = &nv_connector->properties_state;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Init DVI-I specific properties. */
2978c2ecf20Sopenharmony_ci	if (connector->connector_type == DRM_MODE_CONNECTOR_DVII)
2988c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base, dev->mode_config.
2998c2ecf20Sopenharmony_ci					   dvi_i_subconnector_property, 0);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Add overscan compensation options to digital outputs. */
3028c2ecf20Sopenharmony_ci	if (disp->underscan_property &&
3038c2ecf20Sopenharmony_ci	    (connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
3048c2ecf20Sopenharmony_ci	     connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
3058c2ecf20Sopenharmony_ci	     connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
3068c2ecf20Sopenharmony_ci	     connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort)) {
3078c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base,
3088c2ecf20Sopenharmony_ci					   disp->underscan_property,
3098c2ecf20Sopenharmony_ci					   UNDERSCAN_OFF);
3108c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base,
3118c2ecf20Sopenharmony_ci					   disp->underscan_hborder_property, 0);
3128c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base,
3138c2ecf20Sopenharmony_ci					   disp->underscan_vborder_property, 0);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Add hue and saturation options. */
3178c2ecf20Sopenharmony_ci	if (disp->vibrant_hue_property)
3188c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base,
3198c2ecf20Sopenharmony_ci					   disp->vibrant_hue_property,
3208c2ecf20Sopenharmony_ci					   armc->procamp.vibrant_hue);
3218c2ecf20Sopenharmony_ci	if (disp->color_vibrance_property)
3228c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base,
3238c2ecf20Sopenharmony_ci					   disp->color_vibrance_property,
3248c2ecf20Sopenharmony_ci					   armc->procamp.color_vibrance);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Scaling mode property. */
3278c2ecf20Sopenharmony_ci	switch (connector->connector_type) {
3288c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_TV:
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_VGA:
3318c2ecf20Sopenharmony_ci		if (disp->disp.object.oclass < NV50_DISP)
3328c2ecf20Sopenharmony_ci			break; /* Can only scale on DFPs. */
3338c2ecf20Sopenharmony_ci		fallthrough;
3348c2ecf20Sopenharmony_ci	default:
3358c2ecf20Sopenharmony_ci		drm_object_attach_property(&connector->base, dev->mode_config.
3368c2ecf20Sopenharmony_ci					   scaling_mode_property,
3378c2ecf20Sopenharmony_ci					   armc->scaler.mode);
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Dithering properties. */
3428c2ecf20Sopenharmony_ci	switch (connector->connector_type) {
3438c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_TV:
3448c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_VGA:
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	default:
3478c2ecf20Sopenharmony_ci		if (disp->dithering_mode) {
3488c2ecf20Sopenharmony_ci			drm_object_attach_property(&connector->base,
3498c2ecf20Sopenharmony_ci						   disp->dithering_mode,
3508c2ecf20Sopenharmony_ci						   armc->dither.mode);
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci		if (disp->dithering_depth) {
3538c2ecf20Sopenharmony_ci			drm_object_attach_property(&connector->base,
3548c2ecf20Sopenharmony_ci						   disp->dithering_depth,
3558c2ecf20Sopenharmony_ci						   armc->dither.depth);
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
3628c2ecf20Sopenharmony_ciint nouveau_tv_disable = 0;
3638c2ecf20Sopenharmony_cimodule_param_named(tv_disable, nouveau_tv_disable, int, 0400);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
3668c2ecf20Sopenharmony_ciint nouveau_ignorelid = 0;
3678c2ecf20Sopenharmony_cimodule_param_named(ignorelid, nouveau_ignorelid, int, 0400);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
3708c2ecf20Sopenharmony_ciint nouveau_duallink = 1;
3718c2ecf20Sopenharmony_cimodule_param_named(duallink, nouveau_duallink, int, 0400);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hdmimhz, "Force a maximum HDMI pixel clock (in MHz)");
3748c2ecf20Sopenharmony_ciint nouveau_hdmimhz = 0;
3758c2ecf20Sopenharmony_cimodule_param_named(hdmimhz, nouveau_hdmimhz, int, 0400);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistruct nouveau_encoder *
3788c2ecf20Sopenharmony_cifind_encoder(struct drm_connector *connector, int type)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder;
3818c2ecf20Sopenharmony_ci	struct drm_encoder *enc;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	drm_connector_for_each_possible_encoder(connector, enc) {
3848c2ecf20Sopenharmony_ci		nv_encoder = nouveau_encoder(enc);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		if (type == DCB_OUTPUT_ANY ||
3878c2ecf20Sopenharmony_ci		    (nv_encoder->dcb && nv_encoder->dcb->type == type))
3888c2ecf20Sopenharmony_ci			return nv_encoder;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return NULL;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void
3958c2ecf20Sopenharmony_cinouveau_connector_destroy(struct drm_connector *connector)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
3988c2ecf20Sopenharmony_ci	nvif_notify_dtor(&nv_connector->hpd);
3998c2ecf20Sopenharmony_ci	kfree(nv_connector->edid);
4008c2ecf20Sopenharmony_ci	drm_connector_unregister(connector);
4018c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
4028c2ecf20Sopenharmony_ci	if (nv_connector->aux.transfer) {
4038c2ecf20Sopenharmony_ci		drm_dp_cec_unregister_connector(&nv_connector->aux);
4048c2ecf20Sopenharmony_ci		drm_dp_aux_unregister(&nv_connector->aux);
4058c2ecf20Sopenharmony_ci		kfree(nv_connector->aux.name);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	kfree(connector);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic struct nouveau_encoder *
4118c2ecf20Sopenharmony_cinouveau_connector_ddc_detect(struct drm_connector *connector)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
4148c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = NULL, *found = NULL;
4158c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
4168c2ecf20Sopenharmony_ci	int ret;
4178c2ecf20Sopenharmony_ci	bool switcheroo_ddc = false;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	drm_connector_for_each_possible_encoder(connector, encoder) {
4208c2ecf20Sopenharmony_ci		nv_encoder = nouveau_encoder(encoder);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		switch (nv_encoder->dcb->type) {
4238c2ecf20Sopenharmony_ci		case DCB_OUTPUT_DP:
4248c2ecf20Sopenharmony_ci			ret = nouveau_dp_detect(nouveau_connector(connector),
4258c2ecf20Sopenharmony_ci						nv_encoder);
4268c2ecf20Sopenharmony_ci			if (ret == NOUVEAU_DP_MST)
4278c2ecf20Sopenharmony_ci				return NULL;
4288c2ecf20Sopenharmony_ci			else if (ret == NOUVEAU_DP_SST)
4298c2ecf20Sopenharmony_ci				found = nv_encoder;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci			break;
4328c2ecf20Sopenharmony_ci		case DCB_OUTPUT_LVDS:
4338c2ecf20Sopenharmony_ci			switcheroo_ddc = !!(vga_switcheroo_handler_flags() &
4348c2ecf20Sopenharmony_ci					    VGA_SWITCHEROO_CAN_SWITCH_DDC);
4358c2ecf20Sopenharmony_ci			fallthrough;
4368c2ecf20Sopenharmony_ci		default:
4378c2ecf20Sopenharmony_ci			if (!nv_encoder->i2c)
4388c2ecf20Sopenharmony_ci				break;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci			if (switcheroo_ddc)
4418c2ecf20Sopenharmony_ci				vga_switcheroo_lock_ddc(dev->pdev);
4428c2ecf20Sopenharmony_ci			if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
4438c2ecf20Sopenharmony_ci				found = nv_encoder;
4448c2ecf20Sopenharmony_ci			if (switcheroo_ddc)
4458c2ecf20Sopenharmony_ci				vga_switcheroo_unlock_ddc(dev->pdev);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci			break;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci		if (found)
4508c2ecf20Sopenharmony_ci			break;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return found;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic struct nouveau_encoder *
4578c2ecf20Sopenharmony_cinouveau_connector_of_detect(struct drm_connector *connector)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci#ifdef __powerpc__
4608c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
4618c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
4628c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder;
4638c2ecf20Sopenharmony_ci	struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!dn ||
4668c2ecf20Sopenharmony_ci	    !((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) ||
4678c2ecf20Sopenharmony_ci	      (nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG))))
4688c2ecf20Sopenharmony_ci		return NULL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	for_each_child_of_node(dn, cn) {
4718c2ecf20Sopenharmony_ci		const char *name = of_get_property(cn, "name", NULL);
4728c2ecf20Sopenharmony_ci		const void *edid = of_get_property(cn, "EDID", NULL);
4738c2ecf20Sopenharmony_ci		int idx = name ? name[strlen(name) - 1] - 'A' : 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		if (nv_encoder->dcb->i2c_index == idx && edid) {
4768c2ecf20Sopenharmony_ci			nv_connector->edid =
4778c2ecf20Sopenharmony_ci				kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
4788c2ecf20Sopenharmony_ci			of_node_put(cn);
4798c2ecf20Sopenharmony_ci			return nv_encoder;
4808c2ecf20Sopenharmony_ci		}
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci#endif
4838c2ecf20Sopenharmony_ci	return NULL;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void
4878c2ecf20Sopenharmony_cinouveau_connector_set_encoder(struct drm_connector *connector,
4888c2ecf20Sopenharmony_ci			      struct nouveau_encoder *nv_encoder)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
4918c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
4928c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (nv_connector->detected_encoder == nv_encoder)
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci	nv_connector->detected_encoder = nv_encoder;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
4998c2ecf20Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
5008c2ecf20Sopenharmony_ci			connector->interlace_allowed =
5018c2ecf20Sopenharmony_ci				nv_encoder->caps.dp_interlace;
5028c2ecf20Sopenharmony_ci		else
5038c2ecf20Sopenharmony_ci			connector->interlace_allowed =
5048c2ecf20Sopenharmony_ci				drm->client.device.info.family < NV_DEVICE_INFO_V0_VOLTA;
5058c2ecf20Sopenharmony_ci		connector->doublescan_allowed = true;
5068c2ecf20Sopenharmony_ci	} else
5078c2ecf20Sopenharmony_ci	if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS ||
5088c2ecf20Sopenharmony_ci	    nv_encoder->dcb->type == DCB_OUTPUT_TMDS) {
5098c2ecf20Sopenharmony_ci		connector->doublescan_allowed = false;
5108c2ecf20Sopenharmony_ci		connector->interlace_allowed = false;
5118c2ecf20Sopenharmony_ci	} else {
5128c2ecf20Sopenharmony_ci		connector->doublescan_allowed = true;
5138c2ecf20Sopenharmony_ci		if (drm->client.device.info.family == NV_DEVICE_INFO_V0_KELVIN ||
5148c2ecf20Sopenharmony_ci		    (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS &&
5158c2ecf20Sopenharmony_ci		     (dev->pdev->device & 0x0ff0) != 0x0100 &&
5168c2ecf20Sopenharmony_ci		     (dev->pdev->device & 0x0ff0) != 0x0150))
5178c2ecf20Sopenharmony_ci			/* HW is broken */
5188c2ecf20Sopenharmony_ci			connector->interlace_allowed = false;
5198c2ecf20Sopenharmony_ci		else
5208c2ecf20Sopenharmony_ci			connector->interlace_allowed = true;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
5248c2ecf20Sopenharmony_ci		drm_object_property_set_value(&connector->base,
5258c2ecf20Sopenharmony_ci			dev->mode_config.dvi_i_subconnector_property,
5268c2ecf20Sopenharmony_ci			nv_encoder->dcb->type == DCB_OUTPUT_TMDS ?
5278c2ecf20Sopenharmony_ci			DRM_MODE_SUBCONNECTOR_DVID :
5288c2ecf20Sopenharmony_ci			DRM_MODE_SUBCONNECTOR_DVIA);
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void
5338c2ecf20Sopenharmony_cinouveau_connector_set_edid(struct nouveau_connector *nv_connector,
5348c2ecf20Sopenharmony_ci			   struct edid *edid)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	if (nv_connector->edid != edid) {
5378c2ecf20Sopenharmony_ci		struct edid *old_edid = nv_connector->edid;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		drm_connector_update_edid_property(&nv_connector->base, edid);
5408c2ecf20Sopenharmony_ci		kfree(old_edid);
5418c2ecf20Sopenharmony_ci		nv_connector->edid = edid;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic enum drm_connector_status
5468c2ecf20Sopenharmony_cinouveau_connector_detect(struct drm_connector *connector, bool force)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
5498c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
5508c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
5518c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = NULL;
5528c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_partner;
5538c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
5548c2ecf20Sopenharmony_ci	int type;
5558c2ecf20Sopenharmony_ci	int ret;
5568c2ecf20Sopenharmony_ci	enum drm_connector_status conn_status = connector_status_disconnected;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* Outputs are only polled while runtime active, so resuming the
5598c2ecf20Sopenharmony_ci	 * device here is unnecessary (and would deadlock upon runtime suspend
5608c2ecf20Sopenharmony_ci	 * because it waits for polling to finish). We do however, want to
5618c2ecf20Sopenharmony_ci	 * prevent the autosuspend timer from elapsing during this operation
5628c2ecf20Sopenharmony_ci	 * if possible.
5638c2ecf20Sopenharmony_ci	 */
5648c2ecf20Sopenharmony_ci	if (drm_kms_helper_is_poll_worker()) {
5658c2ecf20Sopenharmony_ci		pm_runtime_get_noresume(dev->dev);
5668c2ecf20Sopenharmony_ci	} else {
5678c2ecf20Sopenharmony_ci		ret = pm_runtime_get_sync(dev->dev);
5688c2ecf20Sopenharmony_ci		if (ret < 0 && ret != -EACCES) {
5698c2ecf20Sopenharmony_ci			pm_runtime_put_autosuspend(dev->dev);
5708c2ecf20Sopenharmony_ci			nouveau_connector_set_edid(nv_connector, NULL);
5718c2ecf20Sopenharmony_ci			return conn_status;
5728c2ecf20Sopenharmony_ci		}
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	nv_encoder = nouveau_connector_ddc_detect(connector);
5768c2ecf20Sopenharmony_ci	if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
5778c2ecf20Sopenharmony_ci		struct edid *new_edid;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		if ((vga_switcheroo_handler_flags() &
5808c2ecf20Sopenharmony_ci		     VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
5818c2ecf20Sopenharmony_ci		    nv_connector->type == DCB_CONNECTOR_LVDS)
5828c2ecf20Sopenharmony_ci			new_edid = drm_get_edid_switcheroo(connector, i2c);
5838c2ecf20Sopenharmony_ci		else
5848c2ecf20Sopenharmony_ci			new_edid = drm_get_edid(connector, i2c);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		nouveau_connector_set_edid(nv_connector, new_edid);
5878c2ecf20Sopenharmony_ci		if (!nv_connector->edid) {
5888c2ecf20Sopenharmony_ci			NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
5898c2ecf20Sopenharmony_ci				 connector->name);
5908c2ecf20Sopenharmony_ci			goto detect_analog;
5918c2ecf20Sopenharmony_ci		}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		/* Override encoder type for DVI-I based on whether EDID
5948c2ecf20Sopenharmony_ci		 * says the display is digital or analog, both use the
5958c2ecf20Sopenharmony_ci		 * same i2c channel so the value returned from ddc_detect
5968c2ecf20Sopenharmony_ci		 * isn't necessarily correct.
5978c2ecf20Sopenharmony_ci		 */
5988c2ecf20Sopenharmony_ci		nv_partner = NULL;
5998c2ecf20Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS)
6008c2ecf20Sopenharmony_ci			nv_partner = find_encoder(connector, DCB_OUTPUT_ANALOG);
6018c2ecf20Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG)
6028c2ecf20Sopenharmony_ci			nv_partner = find_encoder(connector, DCB_OUTPUT_TMDS);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		if (nv_partner && ((nv_encoder->dcb->type == DCB_OUTPUT_ANALOG &&
6058c2ecf20Sopenharmony_ci				    nv_partner->dcb->type == DCB_OUTPUT_TMDS) ||
6068c2ecf20Sopenharmony_ci				   (nv_encoder->dcb->type == DCB_OUTPUT_TMDS &&
6078c2ecf20Sopenharmony_ci				    nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) {
6088c2ecf20Sopenharmony_ci			if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
6098c2ecf20Sopenharmony_ci				type = DCB_OUTPUT_TMDS;
6108c2ecf20Sopenharmony_ci			else
6118c2ecf20Sopenharmony_ci				type = DCB_OUTPUT_ANALOG;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci			nv_encoder = find_encoder(connector, type);
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		nouveau_connector_set_encoder(connector, nv_encoder);
6178c2ecf20Sopenharmony_ci		conn_status = connector_status_connected;
6188c2ecf20Sopenharmony_ci		drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
6198c2ecf20Sopenharmony_ci		goto out;
6208c2ecf20Sopenharmony_ci	} else {
6218c2ecf20Sopenharmony_ci		nouveau_connector_set_edid(nv_connector, NULL);
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	nv_encoder = nouveau_connector_of_detect(connector);
6258c2ecf20Sopenharmony_ci	if (nv_encoder) {
6268c2ecf20Sopenharmony_ci		nouveau_connector_set_encoder(connector, nv_encoder);
6278c2ecf20Sopenharmony_ci		conn_status = connector_status_connected;
6288c2ecf20Sopenharmony_ci		goto out;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cidetect_analog:
6328c2ecf20Sopenharmony_ci	nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG);
6338c2ecf20Sopenharmony_ci	if (!nv_encoder && !nouveau_tv_disable)
6348c2ecf20Sopenharmony_ci		nv_encoder = find_encoder(connector, DCB_OUTPUT_TV);
6358c2ecf20Sopenharmony_ci	if (nv_encoder && force) {
6368c2ecf20Sopenharmony_ci		struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
6378c2ecf20Sopenharmony_ci		const struct drm_encoder_helper_funcs *helper =
6388c2ecf20Sopenharmony_ci						encoder->helper_private;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci		if (helper->detect(encoder, connector) ==
6418c2ecf20Sopenharmony_ci						connector_status_connected) {
6428c2ecf20Sopenharmony_ci			nouveau_connector_set_encoder(connector, nv_encoder);
6438c2ecf20Sopenharmony_ci			conn_status = connector_status_connected;
6448c2ecf20Sopenharmony_ci			goto out;
6458c2ecf20Sopenharmony_ci		}
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci out:
6498c2ecf20Sopenharmony_ci	if (!nv_connector->edid)
6508c2ecf20Sopenharmony_ci		drm_dp_cec_unset_edid(&nv_connector->aux);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev->dev);
6538c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev->dev);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	return conn_status;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic enum drm_connector_status
6598c2ecf20Sopenharmony_cinouveau_connector_detect_lvds(struct drm_connector *connector, bool force)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
6628c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
6638c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
6648c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = NULL;
6658c2ecf20Sopenharmony_ci	struct edid *edid = NULL;
6668c2ecf20Sopenharmony_ci	enum drm_connector_status status = connector_status_disconnected;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
6698c2ecf20Sopenharmony_ci	if (!nv_encoder)
6708c2ecf20Sopenharmony_ci		goto out;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* Try retrieving EDID via DDC */
6738c2ecf20Sopenharmony_ci	if (!drm->vbios.fp_no_ddc) {
6748c2ecf20Sopenharmony_ci		status = nouveau_connector_detect(connector, force);
6758c2ecf20Sopenharmony_ci		if (status == connector_status_connected) {
6768c2ecf20Sopenharmony_ci			edid = nv_connector->edid;
6778c2ecf20Sopenharmony_ci			goto out;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* On some laptops (Sony, i'm looking at you) there appears to
6828c2ecf20Sopenharmony_ci	 * be no direct way of accessing the panel's EDID.  The only
6838c2ecf20Sopenharmony_ci	 * option available to us appears to be to ask ACPI for help..
6848c2ecf20Sopenharmony_ci	 *
6858c2ecf20Sopenharmony_ci	 * It's important this check's before trying straps, one of the
6868c2ecf20Sopenharmony_ci	 * said manufacturer's laptops are configured in such a way
6878c2ecf20Sopenharmony_ci	 * the nouveau decides an entry in the VBIOS FP mode table is
6888c2ecf20Sopenharmony_ci	 * valid - it's not (rh#613284)
6898c2ecf20Sopenharmony_ci	 */
6908c2ecf20Sopenharmony_ci	if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
6918c2ecf20Sopenharmony_ci		edid = nouveau_acpi_edid(dev, connector);
6928c2ecf20Sopenharmony_ci		if (edid) {
6938c2ecf20Sopenharmony_ci			status = connector_status_connected;
6948c2ecf20Sopenharmony_ci			goto out;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* If no EDID found above, and the VBIOS indicates a hardcoded
6998c2ecf20Sopenharmony_ci	 * modeline is avalilable for the panel, set it as the panel's
7008c2ecf20Sopenharmony_ci	 * native mode and exit.
7018c2ecf20Sopenharmony_ci	 */
7028c2ecf20Sopenharmony_ci	if (nouveau_bios_fp_mode(dev, NULL) && (drm->vbios.fp_no_ddc ||
7038c2ecf20Sopenharmony_ci	    nv_encoder->dcb->lvdsconf.use_straps_for_mode)) {
7048c2ecf20Sopenharmony_ci		status = connector_status_connected;
7058c2ecf20Sopenharmony_ci		goto out;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* Still nothing, some VBIOS images have a hardcoded EDID block
7098c2ecf20Sopenharmony_ci	 * stored for the panel stored in them.
7108c2ecf20Sopenharmony_ci	 */
7118c2ecf20Sopenharmony_ci	if (!drm->vbios.fp_no_ddc) {
7128c2ecf20Sopenharmony_ci		edid = (struct edid *)nouveau_bios_embedded_edid(dev);
7138c2ecf20Sopenharmony_ci		if (edid) {
7148c2ecf20Sopenharmony_ci			edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
7158c2ecf20Sopenharmony_ci			if (edid)
7168c2ecf20Sopenharmony_ci				status = connector_status_connected;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ciout:
7218c2ecf20Sopenharmony_ci#if defined(CONFIG_ACPI_BUTTON) || \
7228c2ecf20Sopenharmony_ci	(defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE))
7238c2ecf20Sopenharmony_ci	if (status == connector_status_connected &&
7248c2ecf20Sopenharmony_ci	    !nouveau_ignorelid && !acpi_lid_open())
7258c2ecf20Sopenharmony_ci		status = connector_status_unknown;
7268c2ecf20Sopenharmony_ci#endif
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	nouveau_connector_set_edid(nv_connector, edid);
7298c2ecf20Sopenharmony_ci	if (nv_encoder)
7308c2ecf20Sopenharmony_ci		nouveau_connector_set_encoder(connector, nv_encoder);
7318c2ecf20Sopenharmony_ci	return status;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic void
7358c2ecf20Sopenharmony_cinouveau_connector_force(struct drm_connector *connector)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
7388c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
7398c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder;
7408c2ecf20Sopenharmony_ci	int type;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
7438c2ecf20Sopenharmony_ci		if (connector->force == DRM_FORCE_ON_DIGITAL)
7448c2ecf20Sopenharmony_ci			type = DCB_OUTPUT_TMDS;
7458c2ecf20Sopenharmony_ci		else
7468c2ecf20Sopenharmony_ci			type = DCB_OUTPUT_ANALOG;
7478c2ecf20Sopenharmony_ci	} else
7488c2ecf20Sopenharmony_ci		type = DCB_OUTPUT_ANY;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	nv_encoder = find_encoder(connector, type);
7518c2ecf20Sopenharmony_ci	if (!nv_encoder) {
7528c2ecf20Sopenharmony_ci		NV_ERROR(drm, "can't find encoder to force %s on!\n",
7538c2ecf20Sopenharmony_ci			 connector->name);
7548c2ecf20Sopenharmony_ci		connector->status = connector_status_disconnected;
7558c2ecf20Sopenharmony_ci		return;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	nouveau_connector_set_encoder(connector, nv_encoder);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int
7628c2ecf20Sopenharmony_cinouveau_connector_set_property(struct drm_connector *connector,
7638c2ecf20Sopenharmony_ci			       struct drm_property *property, uint64_t value)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
7668c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
7678c2ecf20Sopenharmony_ci	struct nouveau_conn_atom *asyc = &nv_connector->properties_state;
7688c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
7698c2ecf20Sopenharmony_ci	int ret;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	ret = connector->funcs->atomic_set_property(&nv_connector->base,
7728c2ecf20Sopenharmony_ci						    &asyc->state,
7738c2ecf20Sopenharmony_ci						    property, value);
7748c2ecf20Sopenharmony_ci	if (ret) {
7758c2ecf20Sopenharmony_ci		if (nv_encoder && nv_encoder->dcb->type == DCB_OUTPUT_TV)
7768c2ecf20Sopenharmony_ci			return get_slave_funcs(encoder)->set_property(
7778c2ecf20Sopenharmony_ci				encoder, connector, property, value);
7788c2ecf20Sopenharmony_ci		return ret;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	nv_connector->scaling_mode = asyc->scaler.mode;
7828c2ecf20Sopenharmony_ci	nv_connector->dithering_mode = asyc->dither.mode;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (connector->encoder && connector->encoder->crtc) {
7858c2ecf20Sopenharmony_ci		ret = drm_crtc_helper_set_mode(connector->encoder->crtc,
7868c2ecf20Sopenharmony_ci					      &connector->encoder->crtc->mode,
7878c2ecf20Sopenharmony_ci					       connector->encoder->crtc->x,
7888c2ecf20Sopenharmony_ci					       connector->encoder->crtc->y,
7898c2ecf20Sopenharmony_ci					       NULL);
7908c2ecf20Sopenharmony_ci		if (!ret)
7918c2ecf20Sopenharmony_ci			return -EINVAL;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return 0;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistruct moderec {
7988c2ecf20Sopenharmony_ci	int hdisplay;
7998c2ecf20Sopenharmony_ci	int vdisplay;
8008c2ecf20Sopenharmony_ci};
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic struct moderec scaler_modes[] = {
8038c2ecf20Sopenharmony_ci	{ 1920, 1200 },
8048c2ecf20Sopenharmony_ci	{ 1920, 1080 },
8058c2ecf20Sopenharmony_ci	{ 1680, 1050 },
8068c2ecf20Sopenharmony_ci	{ 1600, 1200 },
8078c2ecf20Sopenharmony_ci	{ 1400, 1050 },
8088c2ecf20Sopenharmony_ci	{ 1280, 1024 },
8098c2ecf20Sopenharmony_ci	{ 1280, 960 },
8108c2ecf20Sopenharmony_ci	{ 1152, 864 },
8118c2ecf20Sopenharmony_ci	{ 1024, 768 },
8128c2ecf20Sopenharmony_ci	{ 800, 600 },
8138c2ecf20Sopenharmony_ci	{ 720, 400 },
8148c2ecf20Sopenharmony_ci	{ 640, 480 },
8158c2ecf20Sopenharmony_ci	{ 640, 400 },
8168c2ecf20Sopenharmony_ci	{ 640, 350 },
8178c2ecf20Sopenharmony_ci	{}
8188c2ecf20Sopenharmony_ci};
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic int
8218c2ecf20Sopenharmony_cinouveau_connector_scaler_modes_add(struct drm_connector *connector)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
8248c2ecf20Sopenharmony_ci	struct drm_display_mode *native = nv_connector->native_mode, *m;
8258c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
8268c2ecf20Sopenharmony_ci	struct moderec *mode = &scaler_modes[0];
8278c2ecf20Sopenharmony_ci	int modes = 0;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (!native)
8308c2ecf20Sopenharmony_ci		return 0;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	while (mode->hdisplay) {
8338c2ecf20Sopenharmony_ci		if (mode->hdisplay <= native->hdisplay &&
8348c2ecf20Sopenharmony_ci		    mode->vdisplay <= native->vdisplay &&
8358c2ecf20Sopenharmony_ci		    (mode->hdisplay != native->hdisplay ||
8368c2ecf20Sopenharmony_ci		     mode->vdisplay != native->vdisplay)) {
8378c2ecf20Sopenharmony_ci			m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
8388c2ecf20Sopenharmony_ci					 drm_mode_vrefresh(native), false,
8398c2ecf20Sopenharmony_ci					 false, false);
8408c2ecf20Sopenharmony_ci			if (!m)
8418c2ecf20Sopenharmony_ci				continue;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci			drm_mode_probed_add(connector, m);
8448c2ecf20Sopenharmony_ci			modes++;
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		mode++;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	return modes;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic void
8548c2ecf20Sopenharmony_cinouveau_connector_detect_depth(struct drm_connector *connector)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
8578c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
8588c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
8598c2ecf20Sopenharmony_ci	struct nvbios *bios = &drm->vbios;
8608c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = nv_connector->native_mode;
8618c2ecf20Sopenharmony_ci	bool duallink;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* if the edid is feeling nice enough to provide this info, use it */
8648c2ecf20Sopenharmony_ci	if (nv_connector->edid && connector->display_info.bpc)
8658c2ecf20Sopenharmony_ci		return;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */
8688c2ecf20Sopenharmony_ci	if (nv_connector->type == DCB_CONNECTOR_eDP) {
8698c2ecf20Sopenharmony_ci		connector->display_info.bpc = 6;
8708c2ecf20Sopenharmony_ci		return;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* we're out of options unless we're LVDS, default to 8bpc */
8748c2ecf20Sopenharmony_ci	if (nv_encoder->dcb->type != DCB_OUTPUT_LVDS) {
8758c2ecf20Sopenharmony_ci		connector->display_info.bpc = 8;
8768c2ecf20Sopenharmony_ci		return;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	connector->display_info.bpc = 6;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* LVDS: panel straps */
8828c2ecf20Sopenharmony_ci	if (bios->fp_no_ddc) {
8838c2ecf20Sopenharmony_ci		if (bios->fp.if_is_24bit)
8848c2ecf20Sopenharmony_ci			connector->display_info.bpc = 8;
8858c2ecf20Sopenharmony_ci		return;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/* LVDS: DDC panel, need to first determine the number of links to
8898c2ecf20Sopenharmony_ci	 * know which if_is_24bit flag to check...
8908c2ecf20Sopenharmony_ci	 */
8918c2ecf20Sopenharmony_ci	if (nv_connector->edid &&
8928c2ecf20Sopenharmony_ci	    nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
8938c2ecf20Sopenharmony_ci		duallink = ((u8 *)nv_connector->edid)[121] == 2;
8948c2ecf20Sopenharmony_ci	else
8958c2ecf20Sopenharmony_ci		duallink = mode->clock >= bios->fp.duallink_transition_clk;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
8988c2ecf20Sopenharmony_ci	    ( duallink && (bios->fp.strapless_is_24bit & 2)))
8998c2ecf20Sopenharmony_ci		connector->display_info.bpc = 8;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic int
9038c2ecf20Sopenharmony_cinouveau_connector_late_register(struct drm_connector *connector)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	int ret;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	ret = nouveau_backlight_init(connector);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	return ret;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_cistatic void
9138c2ecf20Sopenharmony_cinouveau_connector_early_unregister(struct drm_connector *connector)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	nouveau_backlight_fini(connector);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int
9198c2ecf20Sopenharmony_cinouveau_connector_get_modes(struct drm_connector *connector)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
9228c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
9238c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
9248c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
9258c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
9268c2ecf20Sopenharmony_ci	int ret = 0;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	/* destroy the native mode, the attached monitor could have changed.
9298c2ecf20Sopenharmony_ci	 */
9308c2ecf20Sopenharmony_ci	if (nv_connector->native_mode) {
9318c2ecf20Sopenharmony_ci		drm_mode_destroy(dev, nv_connector->native_mode);
9328c2ecf20Sopenharmony_ci		nv_connector->native_mode = NULL;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (nv_connector->edid)
9368c2ecf20Sopenharmony_ci		ret = drm_add_edid_modes(connector, nv_connector->edid);
9378c2ecf20Sopenharmony_ci	else
9388c2ecf20Sopenharmony_ci	if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS &&
9398c2ecf20Sopenharmony_ci	    (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
9408c2ecf20Sopenharmony_ci	     drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) {
9418c2ecf20Sopenharmony_ci		struct drm_display_mode mode;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci		nouveau_bios_fp_mode(dev, &mode);
9448c2ecf20Sopenharmony_ci		nv_connector->native_mode = drm_mode_duplicate(dev, &mode);
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/* Determine display colour depth for everything except LVDS now,
9488c2ecf20Sopenharmony_ci	 * DP requires this before mode_valid() is called.
9498c2ecf20Sopenharmony_ci	 */
9508c2ecf20Sopenharmony_ci	if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
9518c2ecf20Sopenharmony_ci		nouveau_connector_detect_depth(connector);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* Find the native mode if this is a digital panel, if we didn't
9548c2ecf20Sopenharmony_ci	 * find any modes through DDC previously add the native mode to
9558c2ecf20Sopenharmony_ci	 * the list of modes.
9568c2ecf20Sopenharmony_ci	 */
9578c2ecf20Sopenharmony_ci	if (!nv_connector->native_mode)
9588c2ecf20Sopenharmony_ci		nv_connector->native_mode = nouveau_conn_native_mode(connector);
9598c2ecf20Sopenharmony_ci	if (ret == 0 && nv_connector->native_mode) {
9608c2ecf20Sopenharmony_ci		struct drm_display_mode *mode;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		mode = drm_mode_duplicate(dev, nv_connector->native_mode);
9638c2ecf20Sopenharmony_ci		drm_mode_probed_add(connector, mode);
9648c2ecf20Sopenharmony_ci		ret = 1;
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Determine LVDS colour depth, must happen after determining
9688c2ecf20Sopenharmony_ci	 * "native" mode as some VBIOS tables require us to use the
9698c2ecf20Sopenharmony_ci	 * pixel clock as part of the lookup...
9708c2ecf20Sopenharmony_ci	 */
9718c2ecf20Sopenharmony_ci	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode)
9728c2ecf20Sopenharmony_ci		nouveau_connector_detect_depth(connector);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
9758c2ecf20Sopenharmony_ci		ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (nv_connector->type == DCB_CONNECTOR_LVDS ||
9788c2ecf20Sopenharmony_ci	    nv_connector->type == DCB_CONNECTOR_LVDS_SPWG ||
9798c2ecf20Sopenharmony_ci	    nv_connector->type == DCB_CONNECTOR_eDP)
9808c2ecf20Sopenharmony_ci		ret += nouveau_connector_scaler_modes_add(connector);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	return ret;
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic unsigned
9868c2ecf20Sopenharmony_ciget_tmds_link_bandwidth(struct drm_connector *connector)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
9898c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
9908c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
9918c2ecf20Sopenharmony_ci	struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
9928c2ecf20Sopenharmony_ci	struct drm_display_info *info = NULL;
9938c2ecf20Sopenharmony_ci	unsigned duallink_scale =
9948c2ecf20Sopenharmony_ci		nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (drm_detect_hdmi_monitor(nv_connector->edid)) {
9978c2ecf20Sopenharmony_ci		info = &nv_connector->base.display_info;
9988c2ecf20Sopenharmony_ci		duallink_scale = 1;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (info) {
10028c2ecf20Sopenharmony_ci		if (nouveau_hdmimhz > 0)
10038c2ecf20Sopenharmony_ci			return nouveau_hdmimhz * 1000;
10048c2ecf20Sopenharmony_ci		/* Note: these limits are conservative, some Fermi's
10058c2ecf20Sopenharmony_ci		 * can do 297 MHz. Unclear how this can be determined.
10068c2ecf20Sopenharmony_ci		 */
10078c2ecf20Sopenharmony_ci		if (drm->client.device.info.chipset >= 0x120) {
10088c2ecf20Sopenharmony_ci			const int max_tmds_clock =
10098c2ecf20Sopenharmony_ci				info->hdmi.scdc.scrambling.supported ?
10108c2ecf20Sopenharmony_ci				594000 : 340000;
10118c2ecf20Sopenharmony_ci			return info->max_tmds_clock ?
10128c2ecf20Sopenharmony_ci				min(info->max_tmds_clock, max_tmds_clock) :
10138c2ecf20Sopenharmony_ci				max_tmds_clock;
10148c2ecf20Sopenharmony_ci		}
10158c2ecf20Sopenharmony_ci		if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
10168c2ecf20Sopenharmony_ci			return 297000;
10178c2ecf20Sopenharmony_ci		if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
10188c2ecf20Sopenharmony_ci			return 225000;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (dcb->location != DCB_LOC_ON_CHIP ||
10228c2ecf20Sopenharmony_ci	    drm->client.device.info.chipset >= 0x46)
10238c2ecf20Sopenharmony_ci		return 165000 * duallink_scale;
10248c2ecf20Sopenharmony_ci	else if (drm->client.device.info.chipset >= 0x40)
10258c2ecf20Sopenharmony_ci		return 155000 * duallink_scale;
10268c2ecf20Sopenharmony_ci	else if (drm->client.device.info.chipset >= 0x18)
10278c2ecf20Sopenharmony_ci		return 135000 * duallink_scale;
10288c2ecf20Sopenharmony_ci	else
10298c2ecf20Sopenharmony_ci		return 112000 * duallink_scale;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic enum drm_mode_status
10338c2ecf20Sopenharmony_cinouveau_connector_mode_valid(struct drm_connector *connector,
10348c2ecf20Sopenharmony_ci			     struct drm_display_mode *mode)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
10378c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
10388c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
10398c2ecf20Sopenharmony_ci	unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	switch (nv_encoder->dcb->type) {
10428c2ecf20Sopenharmony_ci	case DCB_OUTPUT_LVDS:
10438c2ecf20Sopenharmony_ci		if (nv_connector->native_mode &&
10448c2ecf20Sopenharmony_ci		    (mode->hdisplay > nv_connector->native_mode->hdisplay ||
10458c2ecf20Sopenharmony_ci		     mode->vdisplay > nv_connector->native_mode->vdisplay))
10468c2ecf20Sopenharmony_ci			return MODE_PANEL;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		min_clock = 0;
10498c2ecf20Sopenharmony_ci		max_clock = 400000;
10508c2ecf20Sopenharmony_ci		break;
10518c2ecf20Sopenharmony_ci	case DCB_OUTPUT_TMDS:
10528c2ecf20Sopenharmony_ci		max_clock = get_tmds_link_bandwidth(connector);
10538c2ecf20Sopenharmony_ci		break;
10548c2ecf20Sopenharmony_ci	case DCB_OUTPUT_ANALOG:
10558c2ecf20Sopenharmony_ci		max_clock = nv_encoder->dcb->crtconf.maxfreq;
10568c2ecf20Sopenharmony_ci		if (!max_clock)
10578c2ecf20Sopenharmony_ci			max_clock = 350000;
10588c2ecf20Sopenharmony_ci		break;
10598c2ecf20Sopenharmony_ci	case DCB_OUTPUT_TV:
10608c2ecf20Sopenharmony_ci		return get_slave_funcs(encoder)->mode_valid(encoder, mode);
10618c2ecf20Sopenharmony_ci	case DCB_OUTPUT_DP:
10628c2ecf20Sopenharmony_ci		return nv50_dp_mode_valid(connector, nv_encoder, mode, NULL);
10638c2ecf20Sopenharmony_ci	default:
10648c2ecf20Sopenharmony_ci		BUG();
10658c2ecf20Sopenharmony_ci		return MODE_BAD;
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
10698c2ecf20Sopenharmony_ci		clock *= 2;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (clock < min_clock)
10728c2ecf20Sopenharmony_ci		return MODE_CLOCK_LOW;
10738c2ecf20Sopenharmony_ci	if (clock > max_clock)
10748c2ecf20Sopenharmony_ci		return MODE_CLOCK_HIGH;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	return MODE_OK;
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic struct drm_encoder *
10808c2ecf20Sopenharmony_cinouveau_connector_best_encoder(struct drm_connector *connector)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = nouveau_connector(connector);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (nv_connector->detected_encoder)
10858c2ecf20Sopenharmony_ci		return to_drm_encoder(nv_connector->detected_encoder);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	return NULL;
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs
10918c2ecf20Sopenharmony_cinouveau_connector_helper_funcs = {
10928c2ecf20Sopenharmony_ci	.get_modes = nouveau_connector_get_modes,
10938c2ecf20Sopenharmony_ci	.mode_valid = nouveau_connector_mode_valid,
10948c2ecf20Sopenharmony_ci	.best_encoder = nouveau_connector_best_encoder,
10958c2ecf20Sopenharmony_ci};
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs
10988c2ecf20Sopenharmony_cinouveau_connector_funcs = {
10998c2ecf20Sopenharmony_ci	.dpms = drm_helper_connector_dpms,
11008c2ecf20Sopenharmony_ci	.reset = nouveau_conn_reset,
11018c2ecf20Sopenharmony_ci	.detect = nouveau_connector_detect,
11028c2ecf20Sopenharmony_ci	.force = nouveau_connector_force,
11038c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
11048c2ecf20Sopenharmony_ci	.set_property = nouveau_connector_set_property,
11058c2ecf20Sopenharmony_ci	.destroy = nouveau_connector_destroy,
11068c2ecf20Sopenharmony_ci	.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
11078c2ecf20Sopenharmony_ci	.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
11088c2ecf20Sopenharmony_ci	.atomic_set_property = nouveau_conn_atomic_set_property,
11098c2ecf20Sopenharmony_ci	.atomic_get_property = nouveau_conn_atomic_get_property,
11108c2ecf20Sopenharmony_ci	.late_register = nouveau_connector_late_register,
11118c2ecf20Sopenharmony_ci	.early_unregister = nouveau_connector_early_unregister,
11128c2ecf20Sopenharmony_ci};
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs
11158c2ecf20Sopenharmony_cinouveau_connector_funcs_lvds = {
11168c2ecf20Sopenharmony_ci	.dpms = drm_helper_connector_dpms,
11178c2ecf20Sopenharmony_ci	.reset = nouveau_conn_reset,
11188c2ecf20Sopenharmony_ci	.detect = nouveau_connector_detect_lvds,
11198c2ecf20Sopenharmony_ci	.force = nouveau_connector_force,
11208c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
11218c2ecf20Sopenharmony_ci	.set_property = nouveau_connector_set_property,
11228c2ecf20Sopenharmony_ci	.destroy = nouveau_connector_destroy,
11238c2ecf20Sopenharmony_ci	.atomic_duplicate_state = nouveau_conn_atomic_duplicate_state,
11248c2ecf20Sopenharmony_ci	.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
11258c2ecf20Sopenharmony_ci	.atomic_set_property = nouveau_conn_atomic_set_property,
11268c2ecf20Sopenharmony_ci	.atomic_get_property = nouveau_conn_atomic_get_property,
11278c2ecf20Sopenharmony_ci	.late_register = nouveau_connector_late_register,
11288c2ecf20Sopenharmony_ci	.early_unregister = nouveau_connector_early_unregister,
11298c2ecf20Sopenharmony_ci};
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_civoid
11328c2ecf20Sopenharmony_cinouveau_connector_hpd(struct drm_connector *connector)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(connector->dev);
11358c2ecf20Sopenharmony_ci	u32 mask = drm_connector_mask(connector);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	mutex_lock(&drm->hpd_lock);
11388c2ecf20Sopenharmony_ci	if (!(drm->hpd_pending & mask)) {
11398c2ecf20Sopenharmony_ci		drm->hpd_pending |= mask;
11408c2ecf20Sopenharmony_ci		schedule_work(&drm->hpd_work);
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci	mutex_unlock(&drm->hpd_lock);
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic int
11468c2ecf20Sopenharmony_cinouveau_connector_hotplug(struct nvif_notify *notify)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector =
11498c2ecf20Sopenharmony_ci		container_of(notify, typeof(*nv_connector), hpd);
11508c2ecf20Sopenharmony_ci	struct drm_connector *connector = &nv_connector->base;
11518c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
11528c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
11538c2ecf20Sopenharmony_ci	const struct nvif_notify_conn_rep_v0 *rep = notify->data;
11548c2ecf20Sopenharmony_ci	bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
11578c2ecf20Sopenharmony_ci		nouveau_dp_irq(drm, nv_connector);
11588c2ecf20Sopenharmony_ci		return NVIF_NOTIFY_KEEP;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", connector->name);
11628c2ecf20Sopenharmony_ci	nouveau_connector_hpd(connector);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	return NVIF_NOTIFY_KEEP;
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic ssize_t
11688c2ecf20Sopenharmony_cinouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector =
11718c2ecf20Sopenharmony_ci		container_of(obj, typeof(*nv_connector), aux);
11728c2ecf20Sopenharmony_ci	struct nouveau_encoder *nv_encoder;
11738c2ecf20Sopenharmony_ci	struct nvkm_i2c_aux *aux;
11748c2ecf20Sopenharmony_ci	u8 size = msg->size;
11758c2ecf20Sopenharmony_ci	int ret;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
11788c2ecf20Sopenharmony_ci	if (!nv_encoder || !(aux = nv_encoder->aux))
11798c2ecf20Sopenharmony_ci		return -ENODEV;
11808c2ecf20Sopenharmony_ci	if (WARN_ON(msg->size > 16))
11818c2ecf20Sopenharmony_ci		return -E2BIG;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	ret = nvkm_i2c_aux_acquire(aux);
11848c2ecf20Sopenharmony_ci	if (ret)
11858c2ecf20Sopenharmony_ci		return ret;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address,
11888c2ecf20Sopenharmony_ci				msg->buffer, &size);
11898c2ecf20Sopenharmony_ci	nvkm_i2c_aux_release(aux);
11908c2ecf20Sopenharmony_ci	if (ret >= 0) {
11918c2ecf20Sopenharmony_ci		msg->reply = ret;
11928c2ecf20Sopenharmony_ci		return size;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	return ret;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int
11998c2ecf20Sopenharmony_cidrm_conntype_from_dcb(enum dcb_connector_type dcb)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	switch (dcb) {
12028c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_VGA      : return DRM_MODE_CONNECTOR_VGA;
12038c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_0     :
12048c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_1     :
12058c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_3     : return DRM_MODE_CONNECTOR_TV;
12068c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DMS59_0  :
12078c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DMS59_1  :
12088c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DVI_I    : return DRM_MODE_CONNECTOR_DVII;
12098c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DVI_D    : return DRM_MODE_CONNECTOR_DVID;
12108c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_LVDS     :
12118c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
12128c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DMS59_DP0:
12138c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DMS59_DP1:
12148c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_DP       :
12158c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_mDP      :
12168c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_USB_C    : return DRM_MODE_CONNECTOR_DisplayPort;
12178c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_eDP      : return DRM_MODE_CONNECTOR_eDP;
12188c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_HDMI_0   :
12198c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_HDMI_1   :
12208c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_HDMI_C   : return DRM_MODE_CONNECTOR_HDMIA;
12218c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_WFD	    : return DRM_MODE_CONNECTOR_VIRTUAL;
12228c2ecf20Sopenharmony_ci	default:
12238c2ecf20Sopenharmony_ci		break;
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return DRM_MODE_CONNECTOR_Unknown;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistruct drm_connector *
12308c2ecf20Sopenharmony_cinouveau_connector_create(struct drm_device *dev,
12318c2ecf20Sopenharmony_ci			 const struct dcb_output *dcbe)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
12348c2ecf20Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
12358c2ecf20Sopenharmony_ci	struct nouveau_display *disp = nouveau_display(dev);
12368c2ecf20Sopenharmony_ci	struct nouveau_connector *nv_connector = NULL;
12378c2ecf20Sopenharmony_ci	struct drm_connector *connector;
12388c2ecf20Sopenharmony_ci	struct drm_connector_list_iter conn_iter;
12398c2ecf20Sopenharmony_ci	char aux_name[48] = {0};
12408c2ecf20Sopenharmony_ci	int index = dcbe->connector;
12418c2ecf20Sopenharmony_ci	int type, ret = 0;
12428c2ecf20Sopenharmony_ci	bool dummy;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	drm_connector_list_iter_begin(dev, &conn_iter);
12458c2ecf20Sopenharmony_ci	nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
12468c2ecf20Sopenharmony_ci		nv_connector = nouveau_connector(connector);
12478c2ecf20Sopenharmony_ci		if (nv_connector->index == index) {
12488c2ecf20Sopenharmony_ci			drm_connector_list_iter_end(&conn_iter);
12498c2ecf20Sopenharmony_ci			return connector;
12508c2ecf20Sopenharmony_ci		}
12518c2ecf20Sopenharmony_ci	}
12528c2ecf20Sopenharmony_ci	drm_connector_list_iter_end(&conn_iter);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
12558c2ecf20Sopenharmony_ci	if (!nv_connector)
12568c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	connector = &nv_connector->base;
12598c2ecf20Sopenharmony_ci	nv_connector->index = index;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* attempt to parse vbios connector type and hotplug gpio */
12628c2ecf20Sopenharmony_ci	nv_connector->dcb = olddcb_conn(dev, index);
12638c2ecf20Sopenharmony_ci	if (nv_connector->dcb) {
12648c2ecf20Sopenharmony_ci		u32 entry = ROM16(nv_connector->dcb[0]);
12658c2ecf20Sopenharmony_ci		if (olddcb_conntab(dev)[3] >= 4)
12668c2ecf20Sopenharmony_ci			entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci		nv_connector->type = nv_connector->dcb[0];
12698c2ecf20Sopenharmony_ci		if (drm_conntype_from_dcb(nv_connector->type) ==
12708c2ecf20Sopenharmony_ci					  DRM_MODE_CONNECTOR_Unknown) {
12718c2ecf20Sopenharmony_ci			NV_WARN(drm, "unknown connector type %02x\n",
12728c2ecf20Sopenharmony_ci				nv_connector->type);
12738c2ecf20Sopenharmony_ci			nv_connector->type = DCB_CONNECTOR_NONE;
12748c2ecf20Sopenharmony_ci		}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci		/* Gigabyte NX85T */
12778c2ecf20Sopenharmony_ci		if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) {
12788c2ecf20Sopenharmony_ci			if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
12798c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_DVI_I;
12808c2ecf20Sopenharmony_ci		}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		/* Gigabyte GV-NX86T512H */
12838c2ecf20Sopenharmony_ci		if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
12848c2ecf20Sopenharmony_ci			if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
12858c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_DVI_I;
12868c2ecf20Sopenharmony_ci		}
12878c2ecf20Sopenharmony_ci	} else {
12888c2ecf20Sopenharmony_ci		nv_connector->type = DCB_CONNECTOR_NONE;
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/* no vbios data, or an unknown dcb connector type - attempt to
12928c2ecf20Sopenharmony_ci	 * figure out something suitable ourselves
12938c2ecf20Sopenharmony_ci	 */
12948c2ecf20Sopenharmony_ci	if (nv_connector->type == DCB_CONNECTOR_NONE) {
12958c2ecf20Sopenharmony_ci		struct nouveau_drm *drm = nouveau_drm(dev);
12968c2ecf20Sopenharmony_ci		struct dcb_table *dcbt = &drm->vbios.dcb;
12978c2ecf20Sopenharmony_ci		u32 encoders = 0;
12988c2ecf20Sopenharmony_ci		int i;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		for (i = 0; i < dcbt->entries; i++) {
13018c2ecf20Sopenharmony_ci			if (dcbt->entry[i].connector == nv_connector->index)
13028c2ecf20Sopenharmony_ci				encoders |= (1 << dcbt->entry[i].type);
13038c2ecf20Sopenharmony_ci		}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci		if (encoders & (1 << DCB_OUTPUT_DP)) {
13068c2ecf20Sopenharmony_ci			if (encoders & (1 << DCB_OUTPUT_TMDS))
13078c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_DP;
13088c2ecf20Sopenharmony_ci			else
13098c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_eDP;
13108c2ecf20Sopenharmony_ci		} else
13118c2ecf20Sopenharmony_ci		if (encoders & (1 << DCB_OUTPUT_TMDS)) {
13128c2ecf20Sopenharmony_ci			if (encoders & (1 << DCB_OUTPUT_ANALOG))
13138c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_DVI_I;
13148c2ecf20Sopenharmony_ci			else
13158c2ecf20Sopenharmony_ci				nv_connector->type = DCB_CONNECTOR_DVI_D;
13168c2ecf20Sopenharmony_ci		} else
13178c2ecf20Sopenharmony_ci		if (encoders & (1 << DCB_OUTPUT_ANALOG)) {
13188c2ecf20Sopenharmony_ci			nv_connector->type = DCB_CONNECTOR_VGA;
13198c2ecf20Sopenharmony_ci		} else
13208c2ecf20Sopenharmony_ci		if (encoders & (1 << DCB_OUTPUT_LVDS)) {
13218c2ecf20Sopenharmony_ci			nv_connector->type = DCB_CONNECTOR_LVDS;
13228c2ecf20Sopenharmony_ci		} else
13238c2ecf20Sopenharmony_ci		if (encoders & (1 << DCB_OUTPUT_TV)) {
13248c2ecf20Sopenharmony_ci			nv_connector->type = DCB_CONNECTOR_TV_0;
13258c2ecf20Sopenharmony_ci		}
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	switch ((type = drm_conntype_from_dcb(nv_connector->type))) {
13298c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_LVDS:
13308c2ecf20Sopenharmony_ci		ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
13318c2ecf20Sopenharmony_ci		if (ret) {
13328c2ecf20Sopenharmony_ci			NV_ERROR(drm, "Error parsing LVDS table, disabling\n");
13338c2ecf20Sopenharmony_ci			kfree(nv_connector);
13348c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci		funcs = &nouveau_connector_funcs_lvds;
13388c2ecf20Sopenharmony_ci		break;
13398c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_DisplayPort:
13408c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_eDP:
13418c2ecf20Sopenharmony_ci		nv_connector->aux.dev = connector->kdev;
13428c2ecf20Sopenharmony_ci		nv_connector->aux.transfer = nouveau_connector_aux_xfer;
13438c2ecf20Sopenharmony_ci		snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x",
13448c2ecf20Sopenharmony_ci			 dcbe->hasht, dcbe->hashm);
13458c2ecf20Sopenharmony_ci		nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL);
13468c2ecf20Sopenharmony_ci		ret = drm_dp_aux_register(&nv_connector->aux);
13478c2ecf20Sopenharmony_ci		if (ret) {
13488c2ecf20Sopenharmony_ci			NV_ERROR(drm, "failed to register aux channel\n");
13498c2ecf20Sopenharmony_ci			kfree(nv_connector);
13508c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
13518c2ecf20Sopenharmony_ci		}
13528c2ecf20Sopenharmony_ci		funcs = &nouveau_connector_funcs;
13538c2ecf20Sopenharmony_ci		break;
13548c2ecf20Sopenharmony_ci	default:
13558c2ecf20Sopenharmony_ci		funcs = &nouveau_connector_funcs;
13568c2ecf20Sopenharmony_ci		break;
13578c2ecf20Sopenharmony_ci	}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	/* HDMI 3D support */
13608c2ecf20Sopenharmony_ci	if ((disp->disp.object.oclass >= G82_DISP)
13618c2ecf20Sopenharmony_ci	    && ((type == DRM_MODE_CONNECTOR_DisplayPort)
13628c2ecf20Sopenharmony_ci		|| (type == DRM_MODE_CONNECTOR_eDP)
13638c2ecf20Sopenharmony_ci		|| (type == DRM_MODE_CONNECTOR_HDMIA)))
13648c2ecf20Sopenharmony_ci		connector->stereo_allowed = true;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* defaults, will get overridden in detect() */
13678c2ecf20Sopenharmony_ci	connector->interlace_allowed = false;
13688c2ecf20Sopenharmony_ci	connector->doublescan_allowed = false;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	drm_connector_init(dev, connector, funcs, type);
13718c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	connector->funcs->reset(connector);
13748c2ecf20Sopenharmony_ci	nouveau_conn_attach_properties(connector);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	/* Default scaling mode */
13778c2ecf20Sopenharmony_ci	switch (nv_connector->type) {
13788c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_LVDS:
13798c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_LVDS_SPWG:
13808c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_eDP:
13818c2ecf20Sopenharmony_ci		/* see note in nouveau_connector_set_property() */
13828c2ecf20Sopenharmony_ci		if (disp->disp.object.oclass < NV50_DISP) {
13838c2ecf20Sopenharmony_ci			nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
13848c2ecf20Sopenharmony_ci			break;
13858c2ecf20Sopenharmony_ci		}
13868c2ecf20Sopenharmony_ci		nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
13878c2ecf20Sopenharmony_ci		break;
13888c2ecf20Sopenharmony_ci	default:
13898c2ecf20Sopenharmony_ci		nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
13908c2ecf20Sopenharmony_ci		break;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	/* dithering properties */
13948c2ecf20Sopenharmony_ci	switch (nv_connector->type) {
13958c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_0:
13968c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_1:
13978c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_TV_3:
13988c2ecf20Sopenharmony_ci	case DCB_CONNECTOR_VGA:
13998c2ecf20Sopenharmony_ci		break;
14008c2ecf20Sopenharmony_ci	default:
14018c2ecf20Sopenharmony_ci		nv_connector->dithering_mode = DITHERING_MODE_AUTO;
14028c2ecf20Sopenharmony_ci		break;
14038c2ecf20Sopenharmony_ci	}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	switch (type) {
14068c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_DisplayPort:
14078c2ecf20Sopenharmony_ci	case DRM_MODE_CONNECTOR_eDP:
14088c2ecf20Sopenharmony_ci		drm_dp_cec_register_connector(&nv_connector->aux, connector);
14098c2ecf20Sopenharmony_ci		break;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	ret = nvif_notify_ctor(&disp->disp.object, "kmsHotplug",
14138c2ecf20Sopenharmony_ci			       nouveau_connector_hotplug,
14148c2ecf20Sopenharmony_ci			       true, NV04_DISP_NTFY_CONN,
14158c2ecf20Sopenharmony_ci			       &(struct nvif_notify_conn_req_v0) {
14168c2ecf20Sopenharmony_ci				.mask = NVIF_NOTIFY_CONN_V0_ANY,
14178c2ecf20Sopenharmony_ci				.conn = index,
14188c2ecf20Sopenharmony_ci			       },
14198c2ecf20Sopenharmony_ci			       sizeof(struct nvif_notify_conn_req_v0),
14208c2ecf20Sopenharmony_ci			       sizeof(struct nvif_notify_conn_rep_v0),
14218c2ecf20Sopenharmony_ci			       &nv_connector->hpd);
14228c2ecf20Sopenharmony_ci	if (ret)
14238c2ecf20Sopenharmony_ci		connector->polled = DRM_CONNECTOR_POLL_CONNECT;
14248c2ecf20Sopenharmony_ci	else
14258c2ecf20Sopenharmony_ci		connector->polled = DRM_CONNECTOR_POLL_HPD;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	drm_connector_register(connector);
14288c2ecf20Sopenharmony_ci	return connector;
14298c2ecf20Sopenharmony_ci}
1430