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