162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2008 Maarten Maathuis. 362306a36Sopenharmony_ci * All Rights Reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 662306a36Sopenharmony_ci * a copy of this software and associated documentation files (the 762306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 862306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 962306a36Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 1062306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1162306a36Sopenharmony_ci * the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1462306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial 1562306a36Sopenharmony_ci * portions of the Software. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1862306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1962306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 2062306a36Sopenharmony_ci * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 2162306a36Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 2262306a36Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 2362306a36Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <acpi/button.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3062306a36Sopenharmony_ci#include <linux/vga_switcheroo.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3362306a36Sopenharmony_ci#include <drm/drm_edid.h> 3462306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "nouveau_reg.h" 3962306a36Sopenharmony_ci#include "nouveau_drv.h" 4062306a36Sopenharmony_ci#include "dispnv04/hw.h" 4162306a36Sopenharmony_ci#include "dispnv50/disp.h" 4262306a36Sopenharmony_ci#include "nouveau_acpi.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include "nouveau_display.h" 4562306a36Sopenharmony_ci#include "nouveau_connector.h" 4662306a36Sopenharmony_ci#include "nouveau_encoder.h" 4762306a36Sopenharmony_ci#include "nouveau_crtc.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <nvif/class.h> 5062306a36Sopenharmony_ci#include <nvif/if0011.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct drm_display_mode * 5362306a36Sopenharmony_cinouveau_conn_native_mode(struct drm_connector *connector) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci const struct drm_connector_helper_funcs *helper = connector->helper_private; 5662306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 5762306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 5862306a36Sopenharmony_ci struct drm_display_mode *mode, *largest = NULL; 5962306a36Sopenharmony_ci int high_w = 0, high_h = 0, high_v = 0; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci list_for_each_entry(mode, &connector->probed_modes, head) { 6262306a36Sopenharmony_ci if (helper->mode_valid(connector, mode) != MODE_OK || 6362306a36Sopenharmony_ci (mode->flags & DRM_MODE_FLAG_INTERLACE)) 6462306a36Sopenharmony_ci continue; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Use preferred mode if there is one.. */ 6762306a36Sopenharmony_ci if (mode->type & DRM_MODE_TYPE_PREFERRED) { 6862306a36Sopenharmony_ci NV_DEBUG(drm, "native mode from preferred\n"); 6962306a36Sopenharmony_ci return drm_mode_duplicate(dev, mode); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Otherwise, take the resolution with the largest width, then 7362306a36Sopenharmony_ci * height, then vertical refresh 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci if (mode->hdisplay < high_w) 7662306a36Sopenharmony_ci continue; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (mode->hdisplay == high_w && mode->vdisplay < high_h) 7962306a36Sopenharmony_ci continue; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (mode->hdisplay == high_w && mode->vdisplay == high_h && 8262306a36Sopenharmony_ci drm_mode_vrefresh(mode) < high_v) 8362306a36Sopenharmony_ci continue; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci high_w = mode->hdisplay; 8662306a36Sopenharmony_ci high_h = mode->vdisplay; 8762306a36Sopenharmony_ci high_v = drm_mode_vrefresh(mode); 8862306a36Sopenharmony_ci largest = mode; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci NV_DEBUG(drm, "native mode from largest: %dx%d@%d\n", 9262306a36Sopenharmony_ci high_w, high_h, high_v); 9362306a36Sopenharmony_ci return largest ? drm_mode_duplicate(dev, largest) : NULL; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint 9762306a36Sopenharmony_cinouveau_conn_atomic_get_property(struct drm_connector *connector, 9862306a36Sopenharmony_ci const struct drm_connector_state *state, 9962306a36Sopenharmony_ci struct drm_property *property, u64 *val) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct nouveau_conn_atom *asyc = nouveau_conn_atom(state); 10262306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(connector->dev); 10362306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (property == dev->mode_config.scaling_mode_property) 10662306a36Sopenharmony_ci *val = asyc->scaler.mode; 10762306a36Sopenharmony_ci else if (property == disp->underscan_property) 10862306a36Sopenharmony_ci *val = asyc->scaler.underscan.mode; 10962306a36Sopenharmony_ci else if (property == disp->underscan_hborder_property) 11062306a36Sopenharmony_ci *val = asyc->scaler.underscan.hborder; 11162306a36Sopenharmony_ci else if (property == disp->underscan_vborder_property) 11262306a36Sopenharmony_ci *val = asyc->scaler.underscan.vborder; 11362306a36Sopenharmony_ci else if (property == disp->dithering_mode) 11462306a36Sopenharmony_ci *val = asyc->dither.mode; 11562306a36Sopenharmony_ci else if (property == disp->dithering_depth) 11662306a36Sopenharmony_ci *val = asyc->dither.depth; 11762306a36Sopenharmony_ci else if (property == disp->vibrant_hue_property) 11862306a36Sopenharmony_ci *val = asyc->procamp.vibrant_hue; 11962306a36Sopenharmony_ci else if (property == disp->color_vibrance_property) 12062306a36Sopenharmony_ci *val = asyc->procamp.color_vibrance; 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint 12862306a36Sopenharmony_cinouveau_conn_atomic_set_property(struct drm_connector *connector, 12962306a36Sopenharmony_ci struct drm_connector_state *state, 13062306a36Sopenharmony_ci struct drm_property *property, u64 val) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 13362306a36Sopenharmony_ci struct nouveau_conn_atom *asyc = nouveau_conn_atom(state); 13462306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (property == dev->mode_config.scaling_mode_property) { 13762306a36Sopenharmony_ci switch (val) { 13862306a36Sopenharmony_ci case DRM_MODE_SCALE_NONE: 13962306a36Sopenharmony_ci /* We allow 'None' for EDID modes, even on a fixed 14062306a36Sopenharmony_ci * panel (some exist with support for lower refresh 14162306a36Sopenharmony_ci * rates, which people might want to use for power- 14262306a36Sopenharmony_ci * saving purposes). 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Non-EDID modes will force the use of GPU scaling 14562306a36Sopenharmony_ci * to the native mode regardless of this setting. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci switch (connector->connector_type) { 14862306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 14962306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 15062306a36Sopenharmony_ci /* ... except prior to G80, where the code 15162306a36Sopenharmony_ci * doesn't support such things. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci if (disp->disp.object.oclass < NV50_DISP) 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci default: 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci case DRM_MODE_SCALE_FULLSCREEN: 16162306a36Sopenharmony_ci case DRM_MODE_SCALE_CENTER: 16262306a36Sopenharmony_ci case DRM_MODE_SCALE_ASPECT: 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (asyc->scaler.mode != val) { 16962306a36Sopenharmony_ci asyc->scaler.mode = val; 17062306a36Sopenharmony_ci asyc->set.scaler = true; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } else 17362306a36Sopenharmony_ci if (property == disp->underscan_property) { 17462306a36Sopenharmony_ci if (asyc->scaler.underscan.mode != val) { 17562306a36Sopenharmony_ci asyc->scaler.underscan.mode = val; 17662306a36Sopenharmony_ci asyc->set.scaler = true; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } else 17962306a36Sopenharmony_ci if (property == disp->underscan_hborder_property) { 18062306a36Sopenharmony_ci if (asyc->scaler.underscan.hborder != val) { 18162306a36Sopenharmony_ci asyc->scaler.underscan.hborder = val; 18262306a36Sopenharmony_ci asyc->set.scaler = true; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci } else 18562306a36Sopenharmony_ci if (property == disp->underscan_vborder_property) { 18662306a36Sopenharmony_ci if (asyc->scaler.underscan.vborder != val) { 18762306a36Sopenharmony_ci asyc->scaler.underscan.vborder = val; 18862306a36Sopenharmony_ci asyc->set.scaler = true; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } else 19162306a36Sopenharmony_ci if (property == disp->dithering_mode) { 19262306a36Sopenharmony_ci if (asyc->dither.mode != val) { 19362306a36Sopenharmony_ci asyc->dither.mode = val; 19462306a36Sopenharmony_ci asyc->set.dither = true; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } else 19762306a36Sopenharmony_ci if (property == disp->dithering_depth) { 19862306a36Sopenharmony_ci if (asyc->dither.mode != val) { 19962306a36Sopenharmony_ci asyc->dither.depth = val; 20062306a36Sopenharmony_ci asyc->set.dither = true; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } else 20362306a36Sopenharmony_ci if (property == disp->vibrant_hue_property) { 20462306a36Sopenharmony_ci if (asyc->procamp.vibrant_hue != val) { 20562306a36Sopenharmony_ci asyc->procamp.vibrant_hue = val; 20662306a36Sopenharmony_ci asyc->set.procamp = true; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci } else 20962306a36Sopenharmony_ci if (property == disp->color_vibrance_property) { 21062306a36Sopenharmony_ci if (asyc->procamp.color_vibrance != val) { 21162306a36Sopenharmony_ci asyc->procamp.color_vibrance = val; 21262306a36Sopenharmony_ci asyc->set.procamp = true; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_civoid 22262306a36Sopenharmony_cinouveau_conn_atomic_destroy_state(struct drm_connector *connector, 22362306a36Sopenharmony_ci struct drm_connector_state *state) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct nouveau_conn_atom *asyc = nouveau_conn_atom(state); 22662306a36Sopenharmony_ci __drm_atomic_helper_connector_destroy_state(&asyc->state); 22762306a36Sopenharmony_ci kfree(asyc); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistruct drm_connector_state * 23162306a36Sopenharmony_cinouveau_conn_atomic_duplicate_state(struct drm_connector *connector) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct nouveau_conn_atom *armc = nouveau_conn_atom(connector->state); 23462306a36Sopenharmony_ci struct nouveau_conn_atom *asyc; 23562306a36Sopenharmony_ci if (!(asyc = kmalloc(sizeof(*asyc), GFP_KERNEL))) 23662306a36Sopenharmony_ci return NULL; 23762306a36Sopenharmony_ci __drm_atomic_helper_connector_duplicate_state(connector, &asyc->state); 23862306a36Sopenharmony_ci asyc->dither = armc->dither; 23962306a36Sopenharmony_ci asyc->scaler = armc->scaler; 24062306a36Sopenharmony_ci asyc->procamp = armc->procamp; 24162306a36Sopenharmony_ci asyc->set.mask = 0; 24262306a36Sopenharmony_ci return &asyc->state; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid 24662306a36Sopenharmony_cinouveau_conn_reset(struct drm_connector *connector) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 24962306a36Sopenharmony_ci struct nouveau_conn_atom *asyc; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(connector->dev)) { 25262306a36Sopenharmony_ci if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL)))) 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (connector->state) 25662306a36Sopenharmony_ci nouveau_conn_atomic_destroy_state(connector, 25762306a36Sopenharmony_ci connector->state); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci __drm_atomic_helper_connector_reset(connector, &asyc->state); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci asyc = &nv_connector->properties_state; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci asyc->dither.mode = DITHERING_MODE_AUTO; 26562306a36Sopenharmony_ci asyc->dither.depth = DITHERING_DEPTH_AUTO; 26662306a36Sopenharmony_ci asyc->scaler.mode = DRM_MODE_SCALE_NONE; 26762306a36Sopenharmony_ci asyc->scaler.underscan.mode = UNDERSCAN_OFF; 26862306a36Sopenharmony_ci asyc->procamp.color_vibrance = 150; 26962306a36Sopenharmony_ci asyc->procamp.vibrant_hue = 90; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (nouveau_display(connector->dev)->disp.object.oclass < NV50_DISP) { 27262306a36Sopenharmony_ci switch (connector->connector_type) { 27362306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 27462306a36Sopenharmony_ci /* See note in nouveau_conn_atomic_set_property(). */ 27562306a36Sopenharmony_ci asyc->scaler.mode = DRM_MODE_SCALE_FULLSCREEN; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_civoid 28462306a36Sopenharmony_cinouveau_conn_attach_properties(struct drm_connector *connector) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 28762306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 28862306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 28962306a36Sopenharmony_ci struct nouveau_conn_atom *armc; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(connector->dev)) 29262306a36Sopenharmony_ci armc = nouveau_conn_atom(connector->state); 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci armc = &nv_connector->properties_state; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Init DVI-I specific properties. */ 29762306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) 29862306a36Sopenharmony_ci drm_object_attach_property(&connector->base, dev->mode_config. 29962306a36Sopenharmony_ci dvi_i_subconnector_property, 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Add overscan compensation options to digital outputs. */ 30262306a36Sopenharmony_ci if (disp->underscan_property && 30362306a36Sopenharmony_ci (connector->connector_type == DRM_MODE_CONNECTOR_DVID || 30462306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVII || 30562306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || 30662306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort)) { 30762306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 30862306a36Sopenharmony_ci disp->underscan_property, 30962306a36Sopenharmony_ci UNDERSCAN_OFF); 31062306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 31162306a36Sopenharmony_ci disp->underscan_hborder_property, 0); 31262306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 31362306a36Sopenharmony_ci disp->underscan_vborder_property, 0); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Add hue and saturation options. */ 31762306a36Sopenharmony_ci if (disp->vibrant_hue_property) 31862306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 31962306a36Sopenharmony_ci disp->vibrant_hue_property, 32062306a36Sopenharmony_ci armc->procamp.vibrant_hue); 32162306a36Sopenharmony_ci if (disp->color_vibrance_property) 32262306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 32362306a36Sopenharmony_ci disp->color_vibrance_property, 32462306a36Sopenharmony_ci armc->procamp.color_vibrance); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Scaling mode property. */ 32762306a36Sopenharmony_ci switch (connector->connector_type) { 32862306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_TV: 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_VGA: 33162306a36Sopenharmony_ci if (disp->disp.object.oclass < NV50_DISP) 33262306a36Sopenharmony_ci break; /* Can only scale on DFPs. */ 33362306a36Sopenharmony_ci fallthrough; 33462306a36Sopenharmony_ci default: 33562306a36Sopenharmony_ci drm_object_attach_property(&connector->base, dev->mode_config. 33662306a36Sopenharmony_ci scaling_mode_property, 33762306a36Sopenharmony_ci armc->scaler.mode); 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Dithering properties. */ 34262306a36Sopenharmony_ci switch (connector->connector_type) { 34362306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_TV: 34462306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_VGA: 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci default: 34762306a36Sopenharmony_ci if (disp->dithering_mode) { 34862306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 34962306a36Sopenharmony_ci disp->dithering_mode, 35062306a36Sopenharmony_ci armc->dither.mode); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci if (disp->dithering_depth) { 35362306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 35462306a36Sopenharmony_ci disp->dithering_depth, 35562306a36Sopenharmony_ci armc->dither.depth); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciMODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); 36262306a36Sopenharmony_ciint nouveau_tv_disable = 0; 36362306a36Sopenharmony_cimodule_param_named(tv_disable, nouveau_tv_disable, int, 0400); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ciMODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status"); 36662306a36Sopenharmony_ciint nouveau_ignorelid = 0; 36762306a36Sopenharmony_cimodule_param_named(ignorelid, nouveau_ignorelid, int, 0400); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciMODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); 37062306a36Sopenharmony_ciint nouveau_duallink = 1; 37162306a36Sopenharmony_cimodule_param_named(duallink, nouveau_duallink, int, 0400); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciMODULE_PARM_DESC(hdmimhz, "Force a maximum HDMI pixel clock (in MHz)"); 37462306a36Sopenharmony_ciint nouveau_hdmimhz = 0; 37562306a36Sopenharmony_cimodule_param_named(hdmimhz, nouveau_hdmimhz, int, 0400); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistruct nouveau_encoder * 37862306a36Sopenharmony_cifind_encoder(struct drm_connector *connector, int type) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder; 38162306a36Sopenharmony_ci struct drm_encoder *enc; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci drm_connector_for_each_possible_encoder(connector, enc) { 38462306a36Sopenharmony_ci nv_encoder = nouveau_encoder(enc); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (type == DCB_OUTPUT_ANY || 38762306a36Sopenharmony_ci (nv_encoder->dcb && nv_encoder->dcb->type == type)) 38862306a36Sopenharmony_ci return nv_encoder; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return NULL; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void 39562306a36Sopenharmony_cinouveau_connector_destroy(struct drm_connector *connector) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 39862306a36Sopenharmony_ci nvif_event_dtor(&nv_connector->irq); 39962306a36Sopenharmony_ci nvif_event_dtor(&nv_connector->hpd); 40062306a36Sopenharmony_ci kfree(nv_connector->edid); 40162306a36Sopenharmony_ci drm_connector_unregister(connector); 40262306a36Sopenharmony_ci drm_connector_cleanup(connector); 40362306a36Sopenharmony_ci if (nv_connector->aux.transfer) { 40462306a36Sopenharmony_ci drm_dp_cec_unregister_connector(&nv_connector->aux); 40562306a36Sopenharmony_ci kfree(nv_connector->aux.name); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci nvif_conn_dtor(&nv_connector->conn); 40862306a36Sopenharmony_ci kfree(connector); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic struct nouveau_encoder * 41262306a36Sopenharmony_cinouveau_connector_ddc_detect(struct drm_connector *connector) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 41562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 41662306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = NULL, *found = NULL; 41762306a36Sopenharmony_ci struct drm_encoder *encoder; 41862306a36Sopenharmony_ci int ret; 41962306a36Sopenharmony_ci bool switcheroo_ddc = false; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci drm_connector_for_each_possible_encoder(connector, encoder) { 42262306a36Sopenharmony_ci nv_encoder = nouveau_encoder(encoder); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci switch (nv_encoder->dcb->type) { 42562306a36Sopenharmony_ci case DCB_OUTPUT_DP: 42662306a36Sopenharmony_ci ret = nouveau_dp_detect(nouveau_connector(connector), 42762306a36Sopenharmony_ci nv_encoder); 42862306a36Sopenharmony_ci if (ret == NOUVEAU_DP_MST) 42962306a36Sopenharmony_ci return NULL; 43062306a36Sopenharmony_ci else if (ret == NOUVEAU_DP_SST) 43162306a36Sopenharmony_ci found = nv_encoder; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci case DCB_OUTPUT_LVDS: 43562306a36Sopenharmony_ci switcheroo_ddc = !!(vga_switcheroo_handler_flags() & 43662306a36Sopenharmony_ci VGA_SWITCHEROO_CAN_SWITCH_DDC); 43762306a36Sopenharmony_ci fallthrough; 43862306a36Sopenharmony_ci default: 43962306a36Sopenharmony_ci if (!nv_encoder->i2c) 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (switcheroo_ddc) 44362306a36Sopenharmony_ci vga_switcheroo_lock_ddc(pdev); 44462306a36Sopenharmony_ci if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) 44562306a36Sopenharmony_ci found = nv_encoder; 44662306a36Sopenharmony_ci if (switcheroo_ddc) 44762306a36Sopenharmony_ci vga_switcheroo_unlock_ddc(pdev); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci if (found) 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return found; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct nouveau_encoder * 45962306a36Sopenharmony_cinouveau_connector_of_detect(struct drm_connector *connector) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci#ifdef __powerpc__ 46262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 46362306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 46462306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder; 46562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 46662306a36Sopenharmony_ci struct device_node *cn, *dn = pci_device_to_OF_node(pdev); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!dn || 46962306a36Sopenharmony_ci !((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) || 47062306a36Sopenharmony_ci (nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG)))) 47162306a36Sopenharmony_ci return NULL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci for_each_child_of_node(dn, cn) { 47462306a36Sopenharmony_ci const char *name = of_get_property(cn, "name", NULL); 47562306a36Sopenharmony_ci const void *edid = of_get_property(cn, "EDID", NULL); 47662306a36Sopenharmony_ci int idx = name ? name[strlen(name) - 1] - 'A' : 0; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (nv_encoder->dcb->i2c_index == idx && edid) { 47962306a36Sopenharmony_ci nv_connector->edid = 48062306a36Sopenharmony_ci kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 48162306a36Sopenharmony_ci of_node_put(cn); 48262306a36Sopenharmony_ci return nv_encoder; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci#endif 48662306a36Sopenharmony_ci return NULL; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void 49062306a36Sopenharmony_cinouveau_connector_set_encoder(struct drm_connector *connector, 49162306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 49462306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 49562306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 49662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (nv_connector->detected_encoder == nv_encoder) 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci nv_connector->detected_encoder = nv_encoder; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { 50362306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_DP) 50462306a36Sopenharmony_ci connector->interlace_allowed = 50562306a36Sopenharmony_ci nv_encoder->caps.dp_interlace; 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci connector->interlace_allowed = 50862306a36Sopenharmony_ci drm->client.device.info.family < NV_DEVICE_INFO_V0_VOLTA; 50962306a36Sopenharmony_ci connector->doublescan_allowed = true; 51062306a36Sopenharmony_ci } else 51162306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || 51262306a36Sopenharmony_ci nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { 51362306a36Sopenharmony_ci connector->doublescan_allowed = false; 51462306a36Sopenharmony_ci connector->interlace_allowed = false; 51562306a36Sopenharmony_ci } else { 51662306a36Sopenharmony_ci connector->doublescan_allowed = true; 51762306a36Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_KELVIN || 51862306a36Sopenharmony_ci (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS && 51962306a36Sopenharmony_ci (pdev->device & 0x0ff0) != 0x0100 && 52062306a36Sopenharmony_ci (pdev->device & 0x0ff0) != 0x0150)) 52162306a36Sopenharmony_ci /* HW is broken */ 52262306a36Sopenharmony_ci connector->interlace_allowed = false; 52362306a36Sopenharmony_ci else 52462306a36Sopenharmony_ci connector->interlace_allowed = true; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_DVI_I) { 52862306a36Sopenharmony_ci drm_object_property_set_value(&connector->base, 52962306a36Sopenharmony_ci dev->mode_config.dvi_i_subconnector_property, 53062306a36Sopenharmony_ci nv_encoder->dcb->type == DCB_OUTPUT_TMDS ? 53162306a36Sopenharmony_ci DRM_MODE_SUBCONNECTOR_DVID : 53262306a36Sopenharmony_ci DRM_MODE_SUBCONNECTOR_DVIA); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void 53762306a36Sopenharmony_cinouveau_connector_set_edid(struct nouveau_connector *nv_connector, 53862306a36Sopenharmony_ci struct edid *edid) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci if (nv_connector->edid != edid) { 54162306a36Sopenharmony_ci struct edid *old_edid = nv_connector->edid; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci drm_connector_update_edid_property(&nv_connector->base, edid); 54462306a36Sopenharmony_ci kfree(old_edid); 54562306a36Sopenharmony_ci nv_connector->edid = edid; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic enum drm_connector_status 55062306a36Sopenharmony_cinouveau_connector_detect(struct drm_connector *connector, bool force) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 55362306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 55462306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 55562306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = NULL; 55662306a36Sopenharmony_ci struct nouveau_encoder *nv_partner; 55762306a36Sopenharmony_ci struct i2c_adapter *i2c; 55862306a36Sopenharmony_ci int type; 55962306a36Sopenharmony_ci int ret; 56062306a36Sopenharmony_ci enum drm_connector_status conn_status = connector_status_disconnected; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Outputs are only polled while runtime active, so resuming the 56362306a36Sopenharmony_ci * device here is unnecessary (and would deadlock upon runtime suspend 56462306a36Sopenharmony_ci * because it waits for polling to finish). We do however, want to 56562306a36Sopenharmony_ci * prevent the autosuspend timer from elapsing during this operation 56662306a36Sopenharmony_ci * if possible. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci if (drm_kms_helper_is_poll_worker()) { 56962306a36Sopenharmony_ci pm_runtime_get_noresume(dev->dev); 57062306a36Sopenharmony_ci } else { 57162306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev->dev); 57262306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 57362306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 57462306a36Sopenharmony_ci nouveau_connector_set_edid(nv_connector, NULL); 57562306a36Sopenharmony_ci return conn_status; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci nv_encoder = nouveau_connector_ddc_detect(connector); 58062306a36Sopenharmony_ci if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { 58162306a36Sopenharmony_ci struct edid *new_edid; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if ((vga_switcheroo_handler_flags() & 58462306a36Sopenharmony_ci VGA_SWITCHEROO_CAN_SWITCH_DDC) && 58562306a36Sopenharmony_ci nv_connector->type == DCB_CONNECTOR_LVDS) 58662306a36Sopenharmony_ci new_edid = drm_get_edid_switcheroo(connector, i2c); 58762306a36Sopenharmony_ci else 58862306a36Sopenharmony_ci new_edid = drm_get_edid(connector, i2c); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci nouveau_connector_set_edid(nv_connector, new_edid); 59162306a36Sopenharmony_ci if (!nv_connector->edid) { 59262306a36Sopenharmony_ci NV_ERROR(drm, "DDC responded, but no EDID for %s\n", 59362306a36Sopenharmony_ci connector->name); 59462306a36Sopenharmony_ci goto detect_analog; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Override encoder type for DVI-I based on whether EDID 59862306a36Sopenharmony_ci * says the display is digital or analog, both use the 59962306a36Sopenharmony_ci * same i2c channel so the value returned from ddc_detect 60062306a36Sopenharmony_ci * isn't necessarily correct. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci nv_partner = NULL; 60362306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) 60462306a36Sopenharmony_ci nv_partner = find_encoder(connector, DCB_OUTPUT_ANALOG); 60562306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG) 60662306a36Sopenharmony_ci nv_partner = find_encoder(connector, DCB_OUTPUT_TMDS); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (nv_partner && ((nv_encoder->dcb->type == DCB_OUTPUT_ANALOG && 60962306a36Sopenharmony_ci nv_partner->dcb->type == DCB_OUTPUT_TMDS) || 61062306a36Sopenharmony_ci (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && 61162306a36Sopenharmony_ci nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) { 61262306a36Sopenharmony_ci if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) 61362306a36Sopenharmony_ci type = DCB_OUTPUT_TMDS; 61462306a36Sopenharmony_ci else 61562306a36Sopenharmony_ci type = DCB_OUTPUT_ANALOG; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci nv_encoder = find_encoder(connector, type); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci nouveau_connector_set_encoder(connector, nv_encoder); 62162306a36Sopenharmony_ci conn_status = connector_status_connected; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_DP) 62462306a36Sopenharmony_ci drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci goto out; 62762306a36Sopenharmony_ci } else { 62862306a36Sopenharmony_ci nouveau_connector_set_edid(nv_connector, NULL); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci nv_encoder = nouveau_connector_of_detect(connector); 63262306a36Sopenharmony_ci if (nv_encoder) { 63362306a36Sopenharmony_ci nouveau_connector_set_encoder(connector, nv_encoder); 63462306a36Sopenharmony_ci conn_status = connector_status_connected; 63562306a36Sopenharmony_ci goto out; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cidetect_analog: 63962306a36Sopenharmony_ci nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG); 64062306a36Sopenharmony_ci if (!nv_encoder && !nouveau_tv_disable) 64162306a36Sopenharmony_ci nv_encoder = find_encoder(connector, DCB_OUTPUT_TV); 64262306a36Sopenharmony_ci if (nv_encoder && force) { 64362306a36Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(nv_encoder); 64462306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *helper = 64562306a36Sopenharmony_ci encoder->helper_private; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (helper->detect(encoder, connector) == 64862306a36Sopenharmony_ci connector_status_connected) { 64962306a36Sopenharmony_ci nouveau_connector_set_encoder(connector, nv_encoder); 65062306a36Sopenharmony_ci conn_status = connector_status_connected; 65162306a36Sopenharmony_ci goto out; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci out: 65662306a36Sopenharmony_ci if (!nv_connector->edid) 65762306a36Sopenharmony_ci drm_dp_cec_unset_edid(&nv_connector->aux); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 66062306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return conn_status; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic enum drm_connector_status 66662306a36Sopenharmony_cinouveau_connector_detect_lvds(struct drm_connector *connector, bool force) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 66962306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 67062306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 67162306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = NULL; 67262306a36Sopenharmony_ci struct edid *edid = NULL; 67362306a36Sopenharmony_ci enum drm_connector_status status = connector_status_disconnected; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); 67662306a36Sopenharmony_ci if (!nv_encoder) 67762306a36Sopenharmony_ci goto out; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Try retrieving EDID via DDC */ 68062306a36Sopenharmony_ci if (!drm->vbios.fp_no_ddc) { 68162306a36Sopenharmony_ci status = nouveau_connector_detect(connector, force); 68262306a36Sopenharmony_ci if (status == connector_status_connected) { 68362306a36Sopenharmony_ci edid = nv_connector->edid; 68462306a36Sopenharmony_ci goto out; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* On some laptops (Sony, i'm looking at you) there appears to 68962306a36Sopenharmony_ci * be no direct way of accessing the panel's EDID. The only 69062306a36Sopenharmony_ci * option available to us appears to be to ask ACPI for help.. 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * It's important this check's before trying straps, one of the 69362306a36Sopenharmony_ci * said manufacturer's laptops are configured in such a way 69462306a36Sopenharmony_ci * the nouveau decides an entry in the VBIOS FP mode table is 69562306a36Sopenharmony_ci * valid - it's not (rh#613284) 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) { 69862306a36Sopenharmony_ci edid = nouveau_acpi_edid(dev, connector); 69962306a36Sopenharmony_ci if (edid) { 70062306a36Sopenharmony_ci status = connector_status_connected; 70162306a36Sopenharmony_ci goto out; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* If no EDID found above, and the VBIOS indicates a hardcoded 70662306a36Sopenharmony_ci * modeline is avalilable for the panel, set it as the panel's 70762306a36Sopenharmony_ci * native mode and exit. 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci if (nouveau_bios_fp_mode(dev, NULL) && (drm->vbios.fp_no_ddc || 71062306a36Sopenharmony_ci nv_encoder->dcb->lvdsconf.use_straps_for_mode)) { 71162306a36Sopenharmony_ci status = connector_status_connected; 71262306a36Sopenharmony_ci goto out; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Still nothing, some VBIOS images have a hardcoded EDID block 71662306a36Sopenharmony_ci * stored for the panel stored in them. 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci if (!drm->vbios.fp_no_ddc) { 71962306a36Sopenharmony_ci edid = (struct edid *)nouveau_bios_embedded_edid(dev); 72062306a36Sopenharmony_ci if (edid) { 72162306a36Sopenharmony_ci edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 72262306a36Sopenharmony_ci if (edid) 72362306a36Sopenharmony_ci status = connector_status_connected; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ciout: 72862306a36Sopenharmony_ci#if defined(CONFIG_ACPI_BUTTON) || \ 72962306a36Sopenharmony_ci (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE)) 73062306a36Sopenharmony_ci if (status == connector_status_connected && 73162306a36Sopenharmony_ci !nouveau_ignorelid && !acpi_lid_open()) 73262306a36Sopenharmony_ci status = connector_status_unknown; 73362306a36Sopenharmony_ci#endif 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci nouveau_connector_set_edid(nv_connector, edid); 73662306a36Sopenharmony_ci if (nv_encoder) 73762306a36Sopenharmony_ci nouveau_connector_set_encoder(connector, nv_encoder); 73862306a36Sopenharmony_ci return status; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic void 74262306a36Sopenharmony_cinouveau_connector_force(struct drm_connector *connector) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 74562306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 74662306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder; 74762306a36Sopenharmony_ci int type; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_DVI_I) { 75062306a36Sopenharmony_ci if (connector->force == DRM_FORCE_ON_DIGITAL) 75162306a36Sopenharmony_ci type = DCB_OUTPUT_TMDS; 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci type = DCB_OUTPUT_ANALOG; 75462306a36Sopenharmony_ci } else 75562306a36Sopenharmony_ci type = DCB_OUTPUT_ANY; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci nv_encoder = find_encoder(connector, type); 75862306a36Sopenharmony_ci if (!nv_encoder) { 75962306a36Sopenharmony_ci NV_ERROR(drm, "can't find encoder to force %s on!\n", 76062306a36Sopenharmony_ci connector->name); 76162306a36Sopenharmony_ci connector->status = connector_status_disconnected; 76262306a36Sopenharmony_ci return; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci nouveau_connector_set_encoder(connector, nv_encoder); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int 76962306a36Sopenharmony_cinouveau_connector_set_property(struct drm_connector *connector, 77062306a36Sopenharmony_ci struct drm_property *property, uint64_t value) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 77362306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; 77462306a36Sopenharmony_ci struct nouveau_conn_atom *asyc = &nv_connector->properties_state; 77562306a36Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(nv_encoder); 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci ret = connector->funcs->atomic_set_property(&nv_connector->base, 77962306a36Sopenharmony_ci &asyc->state, 78062306a36Sopenharmony_ci property, value); 78162306a36Sopenharmony_ci if (ret) { 78262306a36Sopenharmony_ci if (nv_encoder && nv_encoder->dcb->type == DCB_OUTPUT_TV) 78362306a36Sopenharmony_ci return get_slave_funcs(encoder)->set_property( 78462306a36Sopenharmony_ci encoder, connector, property, value); 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci nv_connector->scaling_mode = asyc->scaler.mode; 78962306a36Sopenharmony_ci nv_connector->dithering_mode = asyc->dither.mode; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (connector->encoder && connector->encoder->crtc) { 79262306a36Sopenharmony_ci ret = drm_crtc_helper_set_mode(connector->encoder->crtc, 79362306a36Sopenharmony_ci &connector->encoder->crtc->mode, 79462306a36Sopenharmony_ci connector->encoder->crtc->x, 79562306a36Sopenharmony_ci connector->encoder->crtc->y, 79662306a36Sopenharmony_ci NULL); 79762306a36Sopenharmony_ci if (!ret) 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistruct moderec { 80562306a36Sopenharmony_ci int hdisplay; 80662306a36Sopenharmony_ci int vdisplay; 80762306a36Sopenharmony_ci}; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic struct moderec scaler_modes[] = { 81062306a36Sopenharmony_ci { 1920, 1200 }, 81162306a36Sopenharmony_ci { 1920, 1080 }, 81262306a36Sopenharmony_ci { 1680, 1050 }, 81362306a36Sopenharmony_ci { 1600, 1200 }, 81462306a36Sopenharmony_ci { 1400, 1050 }, 81562306a36Sopenharmony_ci { 1280, 1024 }, 81662306a36Sopenharmony_ci { 1280, 960 }, 81762306a36Sopenharmony_ci { 1152, 864 }, 81862306a36Sopenharmony_ci { 1024, 768 }, 81962306a36Sopenharmony_ci { 800, 600 }, 82062306a36Sopenharmony_ci { 720, 400 }, 82162306a36Sopenharmony_ci { 640, 480 }, 82262306a36Sopenharmony_ci { 640, 400 }, 82362306a36Sopenharmony_ci { 640, 350 }, 82462306a36Sopenharmony_ci {} 82562306a36Sopenharmony_ci}; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int 82862306a36Sopenharmony_cinouveau_connector_scaler_modes_add(struct drm_connector *connector) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 83162306a36Sopenharmony_ci struct drm_display_mode *native = nv_connector->native_mode, *m; 83262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 83362306a36Sopenharmony_ci struct moderec *mode = &scaler_modes[0]; 83462306a36Sopenharmony_ci int modes = 0; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (!native) 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci while (mode->hdisplay) { 84062306a36Sopenharmony_ci if (mode->hdisplay <= native->hdisplay && 84162306a36Sopenharmony_ci mode->vdisplay <= native->vdisplay && 84262306a36Sopenharmony_ci (mode->hdisplay != native->hdisplay || 84362306a36Sopenharmony_ci mode->vdisplay != native->vdisplay)) { 84462306a36Sopenharmony_ci m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay, 84562306a36Sopenharmony_ci drm_mode_vrefresh(native), false, 84662306a36Sopenharmony_ci false, false); 84762306a36Sopenharmony_ci if (!m) 84862306a36Sopenharmony_ci continue; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci drm_mode_probed_add(connector, m); 85162306a36Sopenharmony_ci modes++; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci mode++; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return modes; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void 86162306a36Sopenharmony_cinouveau_connector_detect_depth(struct drm_connector *connector) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 86462306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 86562306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; 86662306a36Sopenharmony_ci struct nvbios *bios = &drm->vbios; 86762306a36Sopenharmony_ci struct drm_display_mode *mode = nv_connector->native_mode; 86862306a36Sopenharmony_ci bool duallink; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* if the edid is feeling nice enough to provide this info, use it */ 87162306a36Sopenharmony_ci if (nv_connector->edid && connector->display_info.bpc) 87262306a36Sopenharmony_ci return; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ 87562306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_eDP) { 87662306a36Sopenharmony_ci connector->display_info.bpc = 6; 87762306a36Sopenharmony_ci return; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* we're out of options unless we're LVDS, default to 8bpc */ 88162306a36Sopenharmony_ci if (nv_encoder->dcb->type != DCB_OUTPUT_LVDS) { 88262306a36Sopenharmony_ci connector->display_info.bpc = 8; 88362306a36Sopenharmony_ci return; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci connector->display_info.bpc = 6; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* LVDS: panel straps */ 88962306a36Sopenharmony_ci if (bios->fp_no_ddc) { 89062306a36Sopenharmony_ci if (bios->fp.if_is_24bit) 89162306a36Sopenharmony_ci connector->display_info.bpc = 8; 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* LVDS: DDC panel, need to first determine the number of links to 89662306a36Sopenharmony_ci * know which if_is_24bit flag to check... 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci if (nv_connector->edid && 89962306a36Sopenharmony_ci nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) 90062306a36Sopenharmony_ci duallink = ((u8 *)nv_connector->edid)[121] == 2; 90162306a36Sopenharmony_ci else 90262306a36Sopenharmony_ci duallink = mode->clock >= bios->fp.duallink_transition_clk; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || 90562306a36Sopenharmony_ci ( duallink && (bios->fp.strapless_is_24bit & 2))) 90662306a36Sopenharmony_ci connector->display_info.bpc = 8; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int 91062306a36Sopenharmony_cinouveau_connector_late_register(struct drm_connector *connector) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci int ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ret = nouveau_backlight_init(connector); 91562306a36Sopenharmony_ci if (ret) 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || 91962306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { 92062306a36Sopenharmony_ci ret = drm_dp_aux_register(&nouveau_connector(connector)->aux); 92162306a36Sopenharmony_ci if (ret) 92262306a36Sopenharmony_ci goto backlight_fini; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_cibacklight_fini: 92762306a36Sopenharmony_ci nouveau_backlight_fini(connector); 92862306a36Sopenharmony_ci return ret; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void 93262306a36Sopenharmony_cinouveau_connector_early_unregister(struct drm_connector *connector) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || 93562306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) 93662306a36Sopenharmony_ci drm_dp_aux_unregister(&nouveau_connector(connector)->aux); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci nouveau_backlight_fini(connector); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int 94262306a36Sopenharmony_cinouveau_connector_get_modes(struct drm_connector *connector) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 94562306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 94662306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 94762306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; 94862306a36Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(nv_encoder); 94962306a36Sopenharmony_ci int ret = 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* destroy the native mode, the attached monitor could have changed. 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci if (nv_connector->native_mode) { 95462306a36Sopenharmony_ci drm_mode_destroy(dev, nv_connector->native_mode); 95562306a36Sopenharmony_ci nv_connector->native_mode = NULL; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (nv_connector->edid) 95962306a36Sopenharmony_ci ret = drm_add_edid_modes(connector, nv_connector->edid); 96062306a36Sopenharmony_ci else 96162306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && 96262306a36Sopenharmony_ci (nv_encoder->dcb->lvdsconf.use_straps_for_mode || 96362306a36Sopenharmony_ci drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { 96462306a36Sopenharmony_ci struct drm_display_mode mode; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci nouveau_bios_fp_mode(dev, &mode); 96762306a36Sopenharmony_ci nv_connector->native_mode = drm_mode_duplicate(dev, &mode); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Determine display colour depth for everything except LVDS now, 97162306a36Sopenharmony_ci * DP requires this before mode_valid() is called. 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) 97462306a36Sopenharmony_ci nouveau_connector_detect_depth(connector); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Find the native mode if this is a digital panel, if we didn't 97762306a36Sopenharmony_ci * find any modes through DDC previously add the native mode to 97862306a36Sopenharmony_ci * the list of modes. 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_ci if (!nv_connector->native_mode) 98162306a36Sopenharmony_ci nv_connector->native_mode = nouveau_conn_native_mode(connector); 98262306a36Sopenharmony_ci if (ret == 0 && nv_connector->native_mode) { 98362306a36Sopenharmony_ci struct drm_display_mode *mode; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci mode = drm_mode_duplicate(dev, nv_connector->native_mode); 98662306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 98762306a36Sopenharmony_ci ret = 1; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Determine LVDS colour depth, must happen after determining 99162306a36Sopenharmony_ci * "native" mode as some VBIOS tables require us to use the 99262306a36Sopenharmony_ci * pixel clock as part of the lookup... 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode) 99562306a36Sopenharmony_ci nouveau_connector_detect_depth(connector); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_TV) 99862306a36Sopenharmony_ci ret = get_slave_funcs(encoder)->get_modes(encoder, connector); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_LVDS || 100162306a36Sopenharmony_ci nv_connector->type == DCB_CONNECTOR_LVDS_SPWG || 100262306a36Sopenharmony_ci nv_connector->type == DCB_CONNECTOR_eDP) 100362306a36Sopenharmony_ci ret += nouveau_connector_scaler_modes_add(connector); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return ret; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic unsigned 100962306a36Sopenharmony_ciget_tmds_link_bandwidth(struct drm_connector *connector) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 101262306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; 101362306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 101462306a36Sopenharmony_ci struct dcb_output *dcb = nv_connector->detected_encoder->dcb; 101562306a36Sopenharmony_ci struct drm_display_info *info = NULL; 101662306a36Sopenharmony_ci unsigned duallink_scale = 101762306a36Sopenharmony_ci nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (drm_detect_hdmi_monitor(nv_connector->edid)) { 102062306a36Sopenharmony_ci info = &nv_connector->base.display_info; 102162306a36Sopenharmony_ci duallink_scale = 1; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (info) { 102562306a36Sopenharmony_ci if (nouveau_hdmimhz > 0) 102662306a36Sopenharmony_ci return nouveau_hdmimhz * 1000; 102762306a36Sopenharmony_ci /* Note: these limits are conservative, some Fermi's 102862306a36Sopenharmony_ci * can do 297 MHz. Unclear how this can be determined. 102962306a36Sopenharmony_ci */ 103062306a36Sopenharmony_ci if (drm->client.device.info.chipset >= 0x120) { 103162306a36Sopenharmony_ci const int max_tmds_clock = 103262306a36Sopenharmony_ci info->hdmi.scdc.scrambling.supported ? 103362306a36Sopenharmony_ci 594000 : 340000; 103462306a36Sopenharmony_ci return info->max_tmds_clock ? 103562306a36Sopenharmony_ci min(info->max_tmds_clock, max_tmds_clock) : 103662306a36Sopenharmony_ci max_tmds_clock; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) 103962306a36Sopenharmony_ci return 297000; 104062306a36Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) 104162306a36Sopenharmony_ci return 225000; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (dcb->location != DCB_LOC_ON_CHIP || 104562306a36Sopenharmony_ci drm->client.device.info.chipset >= 0x46) 104662306a36Sopenharmony_ci return 165000 * duallink_scale; 104762306a36Sopenharmony_ci else if (drm->client.device.info.chipset >= 0x40) 104862306a36Sopenharmony_ci return 155000 * duallink_scale; 104962306a36Sopenharmony_ci else if (drm->client.device.info.chipset >= 0x18) 105062306a36Sopenharmony_ci return 135000 * duallink_scale; 105162306a36Sopenharmony_ci else 105262306a36Sopenharmony_ci return 112000 * duallink_scale; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic enum drm_mode_status 105662306a36Sopenharmony_cinouveau_connector_mode_valid(struct drm_connector *connector, 105762306a36Sopenharmony_ci struct drm_display_mode *mode) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 106062306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; 106162306a36Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(nv_encoder); 106262306a36Sopenharmony_ci unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci switch (nv_encoder->dcb->type) { 106562306a36Sopenharmony_ci case DCB_OUTPUT_LVDS: 106662306a36Sopenharmony_ci if (nv_connector->native_mode && 106762306a36Sopenharmony_ci (mode->hdisplay > nv_connector->native_mode->hdisplay || 106862306a36Sopenharmony_ci mode->vdisplay > nv_connector->native_mode->vdisplay)) 106962306a36Sopenharmony_ci return MODE_PANEL; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci min_clock = 0; 107262306a36Sopenharmony_ci max_clock = 400000; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci case DCB_OUTPUT_TMDS: 107562306a36Sopenharmony_ci max_clock = get_tmds_link_bandwidth(connector); 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci case DCB_OUTPUT_ANALOG: 107862306a36Sopenharmony_ci max_clock = nv_encoder->dcb->crtconf.maxfreq; 107962306a36Sopenharmony_ci if (!max_clock) 108062306a36Sopenharmony_ci max_clock = 350000; 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case DCB_OUTPUT_TV: 108362306a36Sopenharmony_ci return get_slave_funcs(encoder)->mode_valid(encoder, mode); 108462306a36Sopenharmony_ci case DCB_OUTPUT_DP: 108562306a36Sopenharmony_ci return nv50_dp_mode_valid(nv_encoder, mode, NULL); 108662306a36Sopenharmony_ci default: 108762306a36Sopenharmony_ci BUG(); 108862306a36Sopenharmony_ci return MODE_BAD; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) 109262306a36Sopenharmony_ci clock *= 2; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (clock < min_clock) 109562306a36Sopenharmony_ci return MODE_CLOCK_LOW; 109662306a36Sopenharmony_ci if (clock > max_clock) 109762306a36Sopenharmony_ci return MODE_CLOCK_HIGH; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return MODE_OK; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic struct drm_encoder * 110362306a36Sopenharmony_cinouveau_connector_best_encoder(struct drm_connector *connector) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (nv_connector->detected_encoder) 110862306a36Sopenharmony_ci return to_drm_encoder(nv_connector->detected_encoder); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return NULL; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic int 111462306a36Sopenharmony_cinouveau_connector_atomic_check(struct drm_connector *connector, struct drm_atomic_state *state) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct nouveau_connector *nv_conn = nouveau_connector(connector); 111762306a36Sopenharmony_ci struct drm_connector_state *conn_state = 111862306a36Sopenharmony_ci drm_atomic_get_new_connector_state(state, connector); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (!nv_conn->dp_encoder || !nv50_has_mst(nouveau_drm(connector->dev))) 112162306a36Sopenharmony_ci return 0; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return drm_dp_mst_root_conn_atomic_check(conn_state, &nv_conn->dp_encoder->dp.mstm->mgr); 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs 112762306a36Sopenharmony_cinouveau_connector_helper_funcs = { 112862306a36Sopenharmony_ci .get_modes = nouveau_connector_get_modes, 112962306a36Sopenharmony_ci .mode_valid = nouveau_connector_mode_valid, 113062306a36Sopenharmony_ci .best_encoder = nouveau_connector_best_encoder, 113162306a36Sopenharmony_ci .atomic_check = nouveau_connector_atomic_check, 113262306a36Sopenharmony_ci}; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic const struct drm_connector_funcs 113562306a36Sopenharmony_cinouveau_connector_funcs = { 113662306a36Sopenharmony_ci .dpms = drm_helper_connector_dpms, 113762306a36Sopenharmony_ci .reset = nouveau_conn_reset, 113862306a36Sopenharmony_ci .detect = nouveau_connector_detect, 113962306a36Sopenharmony_ci .force = nouveau_connector_force, 114062306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 114162306a36Sopenharmony_ci .set_property = nouveau_connector_set_property, 114262306a36Sopenharmony_ci .destroy = nouveau_connector_destroy, 114362306a36Sopenharmony_ci .atomic_duplicate_state = nouveau_conn_atomic_duplicate_state, 114462306a36Sopenharmony_ci .atomic_destroy_state = nouveau_conn_atomic_destroy_state, 114562306a36Sopenharmony_ci .atomic_set_property = nouveau_conn_atomic_set_property, 114662306a36Sopenharmony_ci .atomic_get_property = nouveau_conn_atomic_get_property, 114762306a36Sopenharmony_ci .late_register = nouveau_connector_late_register, 114862306a36Sopenharmony_ci .early_unregister = nouveau_connector_early_unregister, 114962306a36Sopenharmony_ci}; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cistatic const struct drm_connector_funcs 115262306a36Sopenharmony_cinouveau_connector_funcs_lvds = { 115362306a36Sopenharmony_ci .dpms = drm_helper_connector_dpms, 115462306a36Sopenharmony_ci .reset = nouveau_conn_reset, 115562306a36Sopenharmony_ci .detect = nouveau_connector_detect_lvds, 115662306a36Sopenharmony_ci .force = nouveau_connector_force, 115762306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 115862306a36Sopenharmony_ci .set_property = nouveau_connector_set_property, 115962306a36Sopenharmony_ci .destroy = nouveau_connector_destroy, 116062306a36Sopenharmony_ci .atomic_duplicate_state = nouveau_conn_atomic_duplicate_state, 116162306a36Sopenharmony_ci .atomic_destroy_state = nouveau_conn_atomic_destroy_state, 116262306a36Sopenharmony_ci .atomic_set_property = nouveau_conn_atomic_set_property, 116362306a36Sopenharmony_ci .atomic_get_property = nouveau_conn_atomic_get_property, 116462306a36Sopenharmony_ci .late_register = nouveau_connector_late_register, 116562306a36Sopenharmony_ci .early_unregister = nouveau_connector_early_unregister, 116662306a36Sopenharmony_ci}; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_civoid 116962306a36Sopenharmony_cinouveau_connector_hpd(struct nouveau_connector *nv_connector, u64 bits) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(nv_connector->base.dev); 117262306a36Sopenharmony_ci u32 mask = drm_connector_mask(&nv_connector->base); 117362306a36Sopenharmony_ci unsigned long flags; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_lock_irqsave(&drm->hpd_lock, flags); 117662306a36Sopenharmony_ci if (!(drm->hpd_pending & mask)) { 117762306a36Sopenharmony_ci nv_connector->hpd_pending |= bits; 117862306a36Sopenharmony_ci drm->hpd_pending |= mask; 117962306a36Sopenharmony_ci schedule_work(&drm->hpd_work); 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci spin_unlock_irqrestore(&drm->hpd_lock, flags); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int 118562306a36Sopenharmony_cinouveau_connector_irq(struct nvif_event *event, void *repv, u32 repc) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct nouveau_connector *nv_connector = container_of(event, typeof(*nv_connector), irq); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci schedule_work(&nv_connector->irq_work); 119062306a36Sopenharmony_ci return NVIF_EVENT_KEEP; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic int 119462306a36Sopenharmony_cinouveau_connector_hotplug(struct nvif_event *event, void *repv, u32 repc) 119562306a36Sopenharmony_ci{ 119662306a36Sopenharmony_ci struct nouveau_connector *nv_connector = container_of(event, typeof(*nv_connector), hpd); 119762306a36Sopenharmony_ci struct nvif_conn_event_v0 *rep = repv; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci nouveau_connector_hpd(nv_connector, rep->types); 120062306a36Sopenharmony_ci return NVIF_EVENT_KEEP; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic ssize_t 120462306a36Sopenharmony_cinouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct nouveau_connector *nv_connector = 120762306a36Sopenharmony_ci container_of(obj, typeof(*nv_connector), aux); 120862306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder; 120962306a36Sopenharmony_ci struct nvkm_i2c_aux *aux; 121062306a36Sopenharmony_ci u8 size = msg->size; 121162306a36Sopenharmony_ci int ret; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); 121462306a36Sopenharmony_ci if (!nv_encoder || !(aux = nv_encoder->aux)) 121562306a36Sopenharmony_ci return -ENODEV; 121662306a36Sopenharmony_ci if (WARN_ON(msg->size > 16)) 121762306a36Sopenharmony_ci return -E2BIG; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci ret = nvkm_i2c_aux_acquire(aux); 122062306a36Sopenharmony_ci if (ret) 122162306a36Sopenharmony_ci return ret; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address, 122462306a36Sopenharmony_ci msg->buffer, &size); 122562306a36Sopenharmony_ci nvkm_i2c_aux_release(aux); 122662306a36Sopenharmony_ci if (ret >= 0) { 122762306a36Sopenharmony_ci msg->reply = ret; 122862306a36Sopenharmony_ci return size; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci return ret; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic int 123562306a36Sopenharmony_cidrm_conntype_from_dcb(enum dcb_connector_type dcb) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci switch (dcb) { 123862306a36Sopenharmony_ci case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA; 123962306a36Sopenharmony_ci case DCB_CONNECTOR_TV_0 : 124062306a36Sopenharmony_ci case DCB_CONNECTOR_TV_1 : 124162306a36Sopenharmony_ci case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV; 124262306a36Sopenharmony_ci case DCB_CONNECTOR_DMS59_0 : 124362306a36Sopenharmony_ci case DCB_CONNECTOR_DMS59_1 : 124462306a36Sopenharmony_ci case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII; 124562306a36Sopenharmony_ci case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; 124662306a36Sopenharmony_ci case DCB_CONNECTOR_LVDS : 124762306a36Sopenharmony_ci case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; 124862306a36Sopenharmony_ci case DCB_CONNECTOR_DMS59_DP0: 124962306a36Sopenharmony_ci case DCB_CONNECTOR_DMS59_DP1: 125062306a36Sopenharmony_ci case DCB_CONNECTOR_DP : 125162306a36Sopenharmony_ci case DCB_CONNECTOR_mDP : 125262306a36Sopenharmony_ci case DCB_CONNECTOR_USB_C : return DRM_MODE_CONNECTOR_DisplayPort; 125362306a36Sopenharmony_ci case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; 125462306a36Sopenharmony_ci case DCB_CONNECTOR_HDMI_0 : 125562306a36Sopenharmony_ci case DCB_CONNECTOR_HDMI_1 : 125662306a36Sopenharmony_ci case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA; 125762306a36Sopenharmony_ci case DCB_CONNECTOR_WFD : return DRM_MODE_CONNECTOR_VIRTUAL; 125862306a36Sopenharmony_ci default: 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci return DRM_MODE_CONNECTOR_Unknown; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistruct drm_connector * 126662306a36Sopenharmony_cinouveau_connector_create(struct drm_device *dev, 126762306a36Sopenharmony_ci const struct dcb_output *dcbe) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; 127062306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 127162306a36Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 127262306a36Sopenharmony_ci struct nouveau_connector *nv_connector = NULL; 127362306a36Sopenharmony_ci struct drm_connector *connector; 127462306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 127562306a36Sopenharmony_ci char aux_name[48] = {0}; 127662306a36Sopenharmony_ci int index = dcbe->connector; 127762306a36Sopenharmony_ci int type, ret = 0; 127862306a36Sopenharmony_ci bool dummy; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 128162306a36Sopenharmony_ci nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { 128262306a36Sopenharmony_ci nv_connector = nouveau_connector(connector); 128362306a36Sopenharmony_ci if (nv_connector->index == index) { 128462306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 128562306a36Sopenharmony_ci return connector; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); 129162306a36Sopenharmony_ci if (!nv_connector) 129262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci connector = &nv_connector->base; 129562306a36Sopenharmony_ci nv_connector->index = index; 129662306a36Sopenharmony_ci INIT_WORK(&nv_connector->irq_work, nouveau_dp_irq); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* attempt to parse vbios connector type and hotplug gpio */ 129962306a36Sopenharmony_ci nv_connector->dcb = olddcb_conn(dev, index); 130062306a36Sopenharmony_ci if (nv_connector->dcb) { 130162306a36Sopenharmony_ci u32 entry = ROM16(nv_connector->dcb[0]); 130262306a36Sopenharmony_ci if (olddcb_conntab(dev)[3] >= 4) 130362306a36Sopenharmony_ci entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci nv_connector->type = nv_connector->dcb[0]; 130662306a36Sopenharmony_ci if (drm_conntype_from_dcb(nv_connector->type) == 130762306a36Sopenharmony_ci DRM_MODE_CONNECTOR_Unknown) { 130862306a36Sopenharmony_ci NV_WARN(drm, "unknown connector type %02x\n", 130962306a36Sopenharmony_ci nv_connector->type); 131062306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_NONE; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Gigabyte NX85T */ 131462306a36Sopenharmony_ci if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { 131562306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_HDMI_1) 131662306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_DVI_I; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* Gigabyte GV-NX86T512H */ 132062306a36Sopenharmony_ci if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { 132162306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_HDMI_1) 132262306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_DVI_I; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci } else { 132562306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_NONE; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* no vbios data, or an unknown dcb connector type - attempt to 132962306a36Sopenharmony_ci * figure out something suitable ourselves 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_NONE) { 133262306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 133362306a36Sopenharmony_ci struct dcb_table *dcbt = &drm->vbios.dcb; 133462306a36Sopenharmony_ci u32 encoders = 0; 133562306a36Sopenharmony_ci int i; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci for (i = 0; i < dcbt->entries; i++) { 133862306a36Sopenharmony_ci if (dcbt->entry[i].connector == nv_connector->index) 133962306a36Sopenharmony_ci encoders |= (1 << dcbt->entry[i].type); 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_DP)) { 134362306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_TMDS)) 134462306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_DP; 134562306a36Sopenharmony_ci else 134662306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_eDP; 134762306a36Sopenharmony_ci } else 134862306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_TMDS)) { 134962306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_ANALOG)) 135062306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_DVI_I; 135162306a36Sopenharmony_ci else 135262306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_DVI_D; 135362306a36Sopenharmony_ci } else 135462306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_ANALOG)) { 135562306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_VGA; 135662306a36Sopenharmony_ci } else 135762306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_LVDS)) { 135862306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_LVDS; 135962306a36Sopenharmony_ci } else 136062306a36Sopenharmony_ci if (encoders & (1 << DCB_OUTPUT_TV)) { 136162306a36Sopenharmony_ci nv_connector->type = DCB_CONNECTOR_TV_0; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci switch ((type = drm_conntype_from_dcb(nv_connector->type))) { 136662306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 136762306a36Sopenharmony_ci ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); 136862306a36Sopenharmony_ci if (ret) { 136962306a36Sopenharmony_ci NV_ERROR(drm, "Error parsing LVDS table, disabling\n"); 137062306a36Sopenharmony_ci kfree(nv_connector); 137162306a36Sopenharmony_ci return ERR_PTR(ret); 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci funcs = &nouveau_connector_funcs_lvds; 137562306a36Sopenharmony_ci break; 137662306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_DisplayPort: 137762306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 137862306a36Sopenharmony_ci nv_connector->aux.dev = connector->kdev; 137962306a36Sopenharmony_ci nv_connector->aux.drm_dev = dev; 138062306a36Sopenharmony_ci nv_connector->aux.transfer = nouveau_connector_aux_xfer; 138162306a36Sopenharmony_ci snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", 138262306a36Sopenharmony_ci dcbe->hasht, dcbe->hashm); 138362306a36Sopenharmony_ci nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL); 138462306a36Sopenharmony_ci if (!nv_connector->aux.name) { 138562306a36Sopenharmony_ci kfree(nv_connector); 138662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci drm_dp_aux_init(&nv_connector->aux); 138962306a36Sopenharmony_ci break; 139062306a36Sopenharmony_ci default: 139162306a36Sopenharmony_ci funcs = &nouveau_connector_funcs; 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* HDMI 3D support */ 139662306a36Sopenharmony_ci if ((disp->disp.object.oclass >= G82_DISP) 139762306a36Sopenharmony_ci && ((type == DRM_MODE_CONNECTOR_DisplayPort) 139862306a36Sopenharmony_ci || (type == DRM_MODE_CONNECTOR_eDP) 139962306a36Sopenharmony_ci || (type == DRM_MODE_CONNECTOR_HDMIA))) 140062306a36Sopenharmony_ci connector->stereo_allowed = true; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci /* defaults, will get overridden in detect() */ 140362306a36Sopenharmony_ci connector->interlace_allowed = false; 140462306a36Sopenharmony_ci connector->doublescan_allowed = false; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci drm_connector_init(dev, connector, funcs, type); 140762306a36Sopenharmony_ci drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); 140862306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_CONNECT; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (nv_connector->dcb && (disp->disp.conn_mask & BIT(nv_connector->index))) { 141162306a36Sopenharmony_ci ret = nvif_conn_ctor(&disp->disp, nv_connector->base.name, nv_connector->index, 141262306a36Sopenharmony_ci &nv_connector->conn); 141362306a36Sopenharmony_ci if (ret) { 141462306a36Sopenharmony_ci goto drm_conn_err; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci ret = nvif_conn_event_ctor(&nv_connector->conn, "kmsHotplug", 141862306a36Sopenharmony_ci nouveau_connector_hotplug, 141962306a36Sopenharmony_ci NVIF_CONN_EVENT_V0_PLUG | NVIF_CONN_EVENT_V0_UNPLUG, 142062306a36Sopenharmony_ci &nv_connector->hpd); 142162306a36Sopenharmony_ci if (ret == 0) 142262306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci if (nv_connector->aux.transfer) { 142562306a36Sopenharmony_ci ret = nvif_conn_event_ctor(&nv_connector->conn, "kmsDpIrq", 142662306a36Sopenharmony_ci nouveau_connector_irq, NVIF_CONN_EVENT_V0_IRQ, 142762306a36Sopenharmony_ci &nv_connector->irq); 142862306a36Sopenharmony_ci if (ret) { 142962306a36Sopenharmony_ci nvif_event_dtor(&nv_connector->hpd); 143062306a36Sopenharmony_ci nvif_conn_dtor(&nv_connector->conn); 143162306a36Sopenharmony_ci goto drm_conn_err; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci connector->funcs->reset(connector); 143762306a36Sopenharmony_ci nouveau_conn_attach_properties(connector); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* Default scaling mode */ 144062306a36Sopenharmony_ci switch (nv_connector->type) { 144162306a36Sopenharmony_ci case DCB_CONNECTOR_LVDS: 144262306a36Sopenharmony_ci case DCB_CONNECTOR_LVDS_SPWG: 144362306a36Sopenharmony_ci case DCB_CONNECTOR_eDP: 144462306a36Sopenharmony_ci /* see note in nouveau_connector_set_property() */ 144562306a36Sopenharmony_ci if (disp->disp.object.oclass < NV50_DISP) { 144662306a36Sopenharmony_ci nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; 144762306a36Sopenharmony_ci break; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci default: 145262306a36Sopenharmony_ci nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; 145362306a36Sopenharmony_ci break; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* dithering properties */ 145762306a36Sopenharmony_ci switch (nv_connector->type) { 145862306a36Sopenharmony_ci case DCB_CONNECTOR_TV_0: 145962306a36Sopenharmony_ci case DCB_CONNECTOR_TV_1: 146062306a36Sopenharmony_ci case DCB_CONNECTOR_TV_3: 146162306a36Sopenharmony_ci case DCB_CONNECTOR_VGA: 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci default: 146462306a36Sopenharmony_ci nv_connector->dithering_mode = DITHERING_MODE_AUTO; 146562306a36Sopenharmony_ci break; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci switch (type) { 146962306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_DisplayPort: 147062306a36Sopenharmony_ci nv_connector->dp_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); 147162306a36Sopenharmony_ci fallthrough; 147262306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 147362306a36Sopenharmony_ci drm_dp_cec_register_connector(&nv_connector->aux, connector); 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci drm_connector_register(connector); 147862306a36Sopenharmony_ci return connector; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cidrm_conn_err: 148162306a36Sopenharmony_ci drm_connector_cleanup(connector); 148262306a36Sopenharmony_ci kfree(nv_connector); 148362306a36Sopenharmony_ci return ERR_PTR(ret); 148462306a36Sopenharmony_ci} 1485