2009-12-11 19:24:15 +10:00
/*
* Copyright ( C ) 2008 Maarten Maathuis .
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining
* a copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sublicense , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial
* portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE COPYRIGHT OWNER ( S ) AND / OR ITS SUPPLIERS BE
* LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION
* OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*
*/
2010-01-18 11:42:37 +10:00
# include <acpi/button.h>
2012-09-10 14:20:51 +10:00
# include <linux/pm_runtime.h>
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_edid.h>
# include <drm/drm_crtc_helper.h>
2010-01-18 11:42:37 +10:00
2009-12-11 19:24:15 +10:00
# include "nouveau_reg.h"
2012-07-31 16:16:21 +10:00
# include "nouveau_drm.h"
2013-03-21 15:45:11 +10:00
# include "dispnv04/hw.h"
2012-07-26 08:51:21 +10:00
# include "nouveau_acpi.h"
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
# include "nouveau_display.h"
# include "nouveau_connector.h"
2009-12-11 19:24:15 +10:00
# include "nouveau_encoder.h"
# include "nouveau_crtc.h"
2012-07-31 16:16:21 +10:00
2014-08-10 04:10:20 +10:00
# include <nvif/event.h>
2012-07-31 16:16:21 +10:00
MODULE_PARM_DESC ( tv_disable , " Disable TV-out detection " ) ;
2014-08-18 22:43:24 +02:00
int nouveau_tv_disable = 0 ;
2012-07-31 16:16:21 +10:00
module_param_named ( tv_disable , nouveau_tv_disable , int , 0400 ) ;
MODULE_PARM_DESC ( ignorelid , " Ignore ACPI lid status " ) ;
2014-08-18 22:43:24 +02:00
int nouveau_ignorelid = 0 ;
2012-07-31 16:16:21 +10:00
module_param_named ( ignorelid , nouveau_ignorelid , int , 0400 ) ;
MODULE_PARM_DESC ( duallink , " Allow dual-link TMDS (default: enabled) " ) ;
2014-08-18 22:43:24 +02:00
int nouveau_duallink = 1 ;
2012-07-31 16:16:21 +10:00
module_param_named ( duallink , nouveau_duallink , int , 0400 ) ;
2009-12-11 19:24:15 +10:00
2011-08-02 19:29:37 +10:00
struct nouveau_encoder *
2011-07-12 13:32:07 +10:00
find_encoder ( struct drm_connector * connector , int type )
2009-12-11 19:24:15 +10:00
{
struct drm_device * dev = connector - > dev ;
struct nouveau_encoder * nv_encoder ;
2014-07-17 23:30:03 -04:00
struct drm_encoder * enc ;
2009-12-11 19:24:15 +10:00
int i , id ;
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
id = connector - > encoder_ids [ i ] ;
if ( ! id )
break ;
2014-07-17 23:30:03 -04:00
enc = drm_encoder_find ( dev , id ) ;
if ( ! enc )
2009-12-11 19:24:15 +10:00
continue ;
2014-07-17 23:30:03 -04:00
nv_encoder = nouveau_encoder ( enc ) ;
2009-12-11 19:24:15 +10:00
2014-05-31 01:48:06 +10:00
if ( type = = DCB_OUTPUT_ANY | |
( nv_encoder - > dcb & & nv_encoder - > dcb - > type = = type ) )
2009-12-11 19:24:15 +10:00
return nv_encoder ;
}
return NULL ;
}
struct nouveau_connector *
nouveau_encoder_connector_get ( struct nouveau_encoder * encoder )
{
struct drm_device * dev = to_drm_encoder ( encoder ) - > dev ;
struct drm_connector * drm_connector ;
list_for_each_entry ( drm_connector , & dev - > mode_config . connector_list , head ) {
if ( drm_connector - > encoder = = to_drm_encoder ( encoder ) )
return nouveau_connector ( drm_connector ) ;
}
return NULL ;
}
static void
2010-11-11 16:14:56 +10:00
nouveau_connector_destroy ( struct drm_connector * connector )
2009-12-11 19:24:15 +10:00
{
2010-11-11 16:14:56 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2014-08-10 04:10:28 +10:00
nvif_notify_fini ( & nv_connector - > hpd ) ;
2010-01-11 22:42:21 +01:00
kfree ( nv_connector - > edid ) ;
2014-05-29 16:57:41 +01:00
drm_connector_unregister ( connector ) ;
2010-11-11 16:14:56 +10:00
drm_connector_cleanup ( connector ) ;
2014-05-30 16:20:58 +10:00
if ( nv_connector - > aux . transfer )
drm_dp_aux_unregister ( & nv_connector - > aux ) ;
2010-11-11 16:14:56 +10:00
kfree ( connector ) ;
2009-12-11 19:24:15 +10:00
}
2014-06-06 18:09:55 +10:00
static struct nouveau_encoder *
nouveau_connector_ddc_detect ( struct drm_connector * connector )
2009-12-11 19:24:15 +10:00
{
struct drm_device * dev = connector - > dev ;
2012-12-10 18:53:43 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2015-01-14 15:36:34 +10:00
struct nvkm_gpio * gpio = nvxx_gpio ( & drm - > device ) ;
2014-06-06 18:09:55 +10:00
struct nouveau_encoder * nv_encoder ;
2014-07-17 23:30:03 -04:00
struct drm_encoder * encoder ;
2012-12-10 18:53:43 +10:00
int i , panel = - ENODEV ;
/* eDP panels need powering on by us (if the VBIOS doesn't default it
* to on ) before doing any AUX channel transactions . LVDS panel power
* is handled by the SOR itself , and not required for LVDS DDC .
*/
if ( nv_connector - > type = = DCB_CONNECTOR_eDP ) {
2015-08-20 14:54:20 +10:00
panel = nvkm_gpio_get ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff ) ;
2012-12-10 18:53:43 +10:00
if ( panel = = 0 ) {
2015-08-20 14:54:20 +10:00
nvkm_gpio_set ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff , 1 ) ;
2012-12-10 18:53:43 +10:00
msleep ( 300 ) ;
}
}
2009-12-11 19:24:15 +10:00
2014-06-06 18:09:55 +10:00
for ( i = 0 ; nv_encoder = NULL , i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
int id = connector - > encoder_ids [ i ] ;
if ( id = = 0 )
2009-12-11 19:24:15 +10:00
break ;
2014-07-17 23:30:03 -04:00
encoder = drm_encoder_find ( dev , id ) ;
if ( ! encoder )
2009-12-11 19:24:15 +10:00
continue ;
2014-07-17 23:30:03 -04:00
nv_encoder = nouveau_encoder ( encoder ) ;
2010-08-08 21:35:57 +02:00
2014-06-06 18:09:55 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_DP ) {
int ret = nouveau_dp_detect ( nv_encoder ) ;
if ( ret = = 0 )
break ;
} else
if ( nv_encoder - > i2c ) {
2015-08-20 14:54:15 +10:00
if ( nvkm_probe_i2c ( nv_encoder - > i2c , 0x50 ) )
2014-06-06 18:09:55 +10:00
break ;
2009-12-11 19:24:15 +10:00
}
}
2012-12-10 18:53:43 +10:00
/* eDP panel not detected, restore panel power GPIO to previous
* state to avoid confusing the SOR for other output types .
*/
2014-06-06 18:09:55 +10:00
if ( ! nv_encoder & & panel = = 0 )
2015-08-20 14:54:20 +10:00
nvkm_gpio_set ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff , panel ) ;
2012-12-10 18:53:43 +10:00
2014-06-06 18:09:55 +10:00
return nv_encoder ;
2009-12-11 19:24:15 +10:00
}
2010-09-09 14:33:17 +02:00
static struct nouveau_encoder *
nouveau_connector_of_detect ( struct drm_connector * connector )
{
# ifdef __powerpc__
struct drm_device * dev = connector - > dev ;
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder ;
struct device_node * cn , * dn = pci_device_to_OF_node ( dev - > pdev ) ;
if ( ! dn | |
2012-07-11 10:44:20 +10:00
! ( ( nv_encoder = find_encoder ( connector , DCB_OUTPUT_TMDS ) ) | |
( nv_encoder = find_encoder ( connector , DCB_OUTPUT_ANALOG ) ) ) )
2010-09-09 14:33:17 +02:00
return NULL ;
for_each_child_of_node ( dn , cn ) {
const char * name = of_get_property ( cn , " name " , NULL ) ;
const void * edid = of_get_property ( cn , " EDID " , NULL ) ;
int idx = name ? name [ strlen ( name ) - 1 ] - ' A ' : 0 ;
if ( nv_encoder - > dcb - > i2c_index = = idx & & edid ) {
nv_connector - > edid =
kmemdup ( edid , EDID_LENGTH , GFP_KERNEL ) ;
of_node_put ( cn ) ;
return nv_encoder ;
}
}
# endif
return NULL ;
}
2009-12-11 19:24:15 +10:00
static void
nouveau_connector_set_encoder ( struct drm_connector * connector ,
struct nouveau_encoder * nv_encoder )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2009-12-11 19:24:15 +10:00
struct drm_device * dev = connector - > dev ;
if ( nv_connector - > detected_encoder = = nv_encoder )
return ;
nv_connector - > detected_encoder = nv_encoder ;
2014-08-10 04:10:22 +10:00
if ( drm - > device . info . family > = NV_DEVICE_INFO_V0_TESLA ) {
2011-10-19 13:06:48 +10:00
connector - > interlace_allowed = true ;
connector - > doublescan_allowed = true ;
} else
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_LVDS | |
nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS ) {
2009-12-11 19:24:15 +10:00
connector - > doublescan_allowed = false ;
connector - > interlace_allowed = false ;
} else {
connector - > doublescan_allowed = true ;
2014-08-10 04:10:22 +10:00
if ( drm - > device . info . family = = NV_DEVICE_INFO_V0_KELVIN | |
( drm - > device . info . family = = NV_DEVICE_INFO_V0_CELSIUS & &
2013-09-05 04:45:02 -04:00
( dev - > pdev - > device & 0x0ff0 ) ! = 0x0100 & &
( dev - > pdev - > device & 0x0ff0 ) ! = 0x0150 ) )
2009-12-11 19:24:15 +10:00
/* HW is broken */
connector - > interlace_allowed = false ;
else
connector - > interlace_allowed = true ;
}
2011-11-18 10:23:59 +10:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I ) {
2012-10-11 20:42:04 -05:00
drm_object_property_set_value ( & connector - > base ,
2009-12-11 19:24:15 +10:00
dev - > mode_config . dvi_i_subconnector_property ,
2012-07-11 10:44:20 +10:00
nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS ?
2009-12-11 19:24:15 +10:00
DRM_MODE_SUBCONNECTOR_DVID :
DRM_MODE_SUBCONNECTOR_DVIA ) ;
}
}
static enum drm_connector_status
2010-09-14 11:07:23 +01:00
nouveau_connector_detect ( struct drm_connector * connector , bool force )
2009-12-11 19:24:15 +10:00
{
struct drm_device * dev = connector - > dev ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-11 19:24:15 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = NULL ;
2011-07-12 13:32:07 +10:00
struct nouveau_encoder * nv_partner ;
2015-08-20 14:54:15 +10:00
struct i2c_adapter * i2c ;
2010-07-20 03:08:25 +02:00
int type ;
2012-09-10 14:20:51 +10:00
int ret ;
enum drm_connector_status conn_status = connector_status_disconnected ;
2009-12-11 19:24:15 +10:00
2010-01-14 15:54:23 +01:00
/* Cleanup the previous EDID block. */
if ( nv_connector - > edid ) {
drm_mode_connector_update_edid_property ( connector , NULL ) ;
kfree ( nv_connector - > edid ) ;
nv_connector - > edid = NULL ;
}
2010-01-11 22:42:21 +01:00
2012-09-10 14:20:51 +10:00
ret = pm_runtime_get_sync ( connector - > dev - > dev ) ;
2014-02-12 14:00:59 +09:00
if ( ret < 0 & & ret ! = - EACCES )
2012-09-10 14:20:51 +10:00
return conn_status ;
2014-06-06 18:09:55 +10:00
nv_encoder = nouveau_connector_ddc_detect ( connector ) ;
if ( nv_encoder & & ( i2c = nv_encoder - > i2c ) ! = NULL ) {
2015-08-20 14:54:15 +10:00
nv_connector - > edid = drm_get_edid ( connector , i2c ) ;
2009-12-11 19:24:15 +10:00
drm_mode_connector_update_edid_property ( connector ,
nv_connector - > edid ) ;
if ( ! nv_connector - > edid ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " DDC responded, but no EDID for %s \n " ,
2014-06-03 14:56:18 +03:00
connector - > name ) ;
2010-01-14 15:47:03 +01:00
goto detect_analog ;
2009-12-11 19:24:15 +10:00
}
/* Override encoder type for DVI-I based on whether EDID
* says the display is digital or analog , both use the
* same i2c channel so the value returned from ddc_detect
* isn ' t necessarily correct .
*/
2011-07-12 13:32:07 +10:00
nv_partner = NULL ;
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS )
nv_partner = find_encoder ( connector , DCB_OUTPUT_ANALOG ) ;
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_ANALOG )
nv_partner = find_encoder ( connector , DCB_OUTPUT_TMDS ) ;
if ( nv_partner & & ( ( nv_encoder - > dcb - > type = = DCB_OUTPUT_ANALOG & &
nv_partner - > dcb - > type = = DCB_OUTPUT_TMDS ) | |
( nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS & &
nv_partner - > dcb - > type = = DCB_OUTPUT_ANALOG ) ) ) {
2009-12-11 19:24:15 +10:00
if ( nv_connector - > edid - > input & DRM_EDID_INPUT_DIGITAL )
2012-07-11 10:44:20 +10:00
type = DCB_OUTPUT_TMDS ;
2009-12-11 19:24:15 +10:00
else
2012-07-11 10:44:20 +10:00
type = DCB_OUTPUT_ANALOG ;
2009-12-11 19:24:15 +10:00
2011-07-12 13:32:07 +10:00
nv_encoder = find_encoder ( connector , type ) ;
2009-12-11 19:24:15 +10:00
}
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
2012-09-10 14:20:51 +10:00
conn_status = connector_status_connected ;
goto out ;
2009-12-11 19:24:15 +10:00
}
2010-09-09 14:33:17 +02:00
nv_encoder = nouveau_connector_of_detect ( connector ) ;
if ( nv_encoder ) {
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
2012-09-10 14:20:51 +10:00
conn_status = connector_status_connected ;
goto out ;
2010-09-09 14:33:17 +02:00
}
2010-01-14 15:47:03 +01:00
detect_analog :
2012-07-11 10:44:20 +10:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_ANALOG ) ;
2010-03-15 09:43:51 +10:00
if ( ! nv_encoder & & ! nouveau_tv_disable )
2012-07-11 10:44:20 +10:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_TV ) ;
2010-10-26 02:17:56 +02:00
if ( nv_encoder & & force ) {
2009-12-11 19:24:15 +10:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2015-03-11 11:51:04 +02:00
const struct drm_encoder_helper_funcs * helper =
2009-12-11 19:24:15 +10:00
encoder - > helper_private ;
if ( helper - > detect ( encoder , connector ) = =
connector_status_connected ) {
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
2012-09-10 14:20:51 +10:00
conn_status = connector_status_connected ;
goto out ;
2009-12-11 19:24:15 +10:00
}
}
2012-09-10 14:20:51 +10:00
out :
pm_runtime_mark_last_busy ( connector - > dev - > dev ) ;
pm_runtime_put_autosuspend ( connector - > dev - > dev ) ;
return conn_status ;
2009-12-11 19:24:15 +10:00
}
2010-06-01 13:32:42 +10:00
static enum drm_connector_status
2010-09-14 11:07:23 +01:00
nouveau_connector_detect_lvds ( struct drm_connector * connector , bool force )
2010-06-01 13:32:42 +10:00
{
struct drm_device * dev = connector - > dev ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2010-06-01 13:32:42 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = NULL ;
enum drm_connector_status status = connector_status_disconnected ;
/* Cleanup the previous EDID block. */
if ( nv_connector - > edid ) {
drm_mode_connector_update_edid_property ( connector , NULL ) ;
kfree ( nv_connector - > edid ) ;
nv_connector - > edid = NULL ;
}
2012-07-11 10:44:20 +10:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_LVDS ) ;
2010-06-01 13:32:42 +10:00
if ( ! nv_encoder )
return connector_status_disconnected ;
2010-07-12 15:33:07 +10:00
/* Try retrieving EDID via DDC */
2012-07-31 16:16:21 +10:00
if ( ! drm - > vbios . fp_no_ddc ) {
2010-09-14 11:07:23 +01:00
status = nouveau_connector_detect ( connector , force ) ;
2010-06-01 13:32:42 +10:00
if ( status = = connector_status_connected )
goto out ;
}
2010-07-12 15:33:07 +10:00
/* On some laptops (Sony, i'm looking at you) there appears to
* be no direct way of accessing the panel ' s EDID . The only
* option available to us appears to be to ask ACPI for help . .
*
* It ' s important this check ' s before trying straps , one of the
* said manufacturer ' s laptops are configured in such a way
* the nouveau decides an entry in the VBIOS FP mode table is
* valid - it ' s not ( rh # 613284 )
*/
if ( nv_encoder - > dcb - > lvdsconf . use_acpi_for_edid ) {
2012-11-08 17:19:38 +01:00
if ( ( nv_connector - > edid = nouveau_acpi_edid ( dev , connector ) ) ) {
2010-07-12 15:33:07 +10:00
status = connector_status_connected ;
goto out ;
}
}
2010-06-01 13:32:42 +10:00
/* If no EDID found above, and the VBIOS indicates a hardcoded
* modeline is avalilable for the panel , set it as the panel ' s
* native mode and exit .
*/
2012-07-31 16:16:21 +10:00
if ( nouveau_bios_fp_mode ( dev , NULL ) & & ( drm - > vbios . fp_no_ddc | |
2010-06-01 13:32:42 +10:00
nv_encoder - > dcb - > lvdsconf . use_straps_for_mode ) ) {
status = connector_status_connected ;
goto out ;
}
/* Still nothing, some VBIOS images have a hardcoded EDID block
* stored for the panel stored in them .
*/
2012-07-31 16:16:21 +10:00
if ( ! drm - > vbios . fp_no_ddc ) {
2010-06-01 13:32:42 +10:00
struct edid * edid =
( struct edid * ) nouveau_bios_embedded_edid ( dev ) ;
if ( edid ) {
2013-01-27 17:04:48 +01:00
nv_connector - > edid =
kmemdup ( edid , EDID_LENGTH , GFP_KERNEL ) ;
if ( nv_connector - > edid )
status = connector_status_connected ;
2010-06-01 13:32:42 +10:00
}
}
out :
# if defined(CONFIG_ACPI_BUTTON) || \
( defined ( CONFIG_ACPI_BUTTON_MODULE ) & & defined ( MODULE ) )
if ( status = = connector_status_connected & &
! nouveau_ignorelid & & ! acpi_lid_open ( ) )
status = connector_status_unknown ;
# endif
drm_mode_connector_update_edid_property ( connector , nv_connector - > edid ) ;
2010-06-20 16:57:57 +02:00
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
2010-06-01 13:32:42 +10:00
return status ;
}
2009-12-11 19:24:15 +10:00
static void
nouveau_connector_force ( struct drm_connector * connector )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2010-02-24 14:01:40 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2009-12-11 19:24:15 +10:00
struct nouveau_encoder * nv_encoder ;
int type ;
2011-11-18 10:23:59 +10:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I ) {
2009-12-11 19:24:15 +10:00
if ( connector - > force = = DRM_FORCE_ON_DIGITAL )
2012-07-11 10:44:20 +10:00
type = DCB_OUTPUT_TMDS ;
2009-12-11 19:24:15 +10:00
else
2012-07-11 10:44:20 +10:00
type = DCB_OUTPUT_ANALOG ;
2009-12-11 19:24:15 +10:00
} else
2012-07-11 10:44:20 +10:00
type = DCB_OUTPUT_ANY ;
2009-12-11 19:24:15 +10:00
2011-07-12 13:32:07 +10:00
nv_encoder = find_encoder ( connector , type ) ;
2009-12-11 19:24:15 +10:00
if ( ! nv_encoder ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " can't find encoder to force %s on! \n " ,
2014-06-03 14:56:18 +03:00
connector - > name ) ;
2009-12-11 19:24:15 +10:00
connector - > status = connector_status_disconnected ;
return ;
}
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
}
static int
nouveau_connector_set_property ( struct drm_connector * connector ,
struct drm_property * property , uint64_t value )
{
2012-07-31 16:16:21 +10:00
struct nouveau_display * disp = nouveau_display ( connector - > dev ) ;
2009-12-11 19:24:15 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2010-07-20 16:48:08 +02:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 19:24:15 +10:00
struct drm_device * dev = connector - > dev ;
2011-10-06 13:29:05 +10:00
struct nouveau_crtc * nv_crtc ;
2009-12-11 19:24:15 +10:00
int ret ;
2011-10-06 13:29:05 +10:00
nv_crtc = NULL ;
if ( connector - > encoder & & connector - > encoder - > crtc )
nv_crtc = nouveau_crtc ( connector - > encoder - > crtc ) ;
2009-12-11 19:24:15 +10:00
/* Scaling mode */
if ( property = = dev - > mode_config . scaling_mode_property ) {
bool modeset = false ;
switch ( value ) {
case DRM_MODE_SCALE_NONE :
2014-12-22 17:19:26 +10:00
/* We allow 'None' for EDID modes, even on a fixed
* panel ( some exist with support for lower refresh
* rates , which people might want to use for power
* saving purposes ) .
*
* Non - EDID modes will force the use of GPU scaling
* to the native mode regardless of this setting .
*/
switch ( nv_connector - > type ) {
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG :
case DCB_CONNECTOR_eDP :
/* ... except prior to G80, where the code
* doesn ' t support such things .
*/
if ( disp - > disp . oclass < NV50_DISP )
return - EINVAL ;
break ;
default :
break ;
}
break ;
2009-12-11 19:24:15 +10:00
case DRM_MODE_SCALE_FULLSCREEN :
case DRM_MODE_SCALE_CENTER :
case DRM_MODE_SCALE_ASPECT :
break ;
default :
return - EINVAL ;
}
/* Changing between GPU and panel scaling requires a full
* modeset
*/
if ( ( nv_connector - > scaling_mode = = DRM_MODE_SCALE_NONE ) | |
( value = = DRM_MODE_SCALE_NONE ) )
modeset = true ;
nv_connector - > scaling_mode = value ;
if ( ! nv_crtc )
return 0 ;
if ( modeset | | ! nv_crtc - > set_scale ) {
ret = drm_crtc_helper_set_mode ( & nv_crtc - > base ,
& nv_crtc - > base . mode ,
nv_crtc - > base . x ,
nv_crtc - > base . y , NULL ) ;
if ( ! ret )
return - EINVAL ;
} else {
2011-10-17 10:38:10 +10:00
ret = nv_crtc - > set_scale ( nv_crtc , true ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
return ret ;
}
return 0 ;
}
2011-10-06 13:29:05 +10:00
/* Underscan */
if ( property = = disp - > underscan_property ) {
if ( nv_connector - > underscan ! = value ) {
nv_connector - > underscan = value ;
if ( ! nv_crtc | | ! nv_crtc - > set_scale )
return 0 ;
2011-10-17 10:38:10 +10:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 13:29:05 +10:00
}
return 0 ;
}
if ( property = = disp - > underscan_hborder_property ) {
if ( nv_connector - > underscan_hborder ! = value ) {
nv_connector - > underscan_hborder = value ;
if ( ! nv_crtc | | ! nv_crtc - > set_scale )
return 0 ;
2011-10-17 10:38:10 +10:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 13:29:05 +10:00
}
return 0 ;
}
if ( property = = disp - > underscan_vborder_property ) {
if ( nv_connector - > underscan_vborder ! = value ) {
nv_connector - > underscan_vborder = value ;
if ( ! nv_crtc | | ! nv_crtc - > set_scale )
return 0 ;
2011-10-17 10:38:10 +10:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 13:29:05 +10:00
}
return 0 ;
}
2009-12-11 19:24:15 +10:00
/* Dithering */
2011-10-17 12:23:41 +10:00
if ( property = = disp - > dithering_mode ) {
nv_connector - > dithering_mode = value ;
if ( ! nv_crtc | | ! nv_crtc - > set_dither )
return 0 ;
return nv_crtc - > set_dither ( nv_crtc , true ) ;
}
2009-12-11 19:24:15 +10:00
2011-10-17 12:23:41 +10:00
if ( property = = disp - > dithering_depth ) {
nv_connector - > dithering_depth = value ;
2009-12-11 19:24:15 +10:00
if ( ! nv_crtc | | ! nv_crtc - > set_dither )
return 0 ;
2011-10-17 10:38:10 +10:00
return nv_crtc - > set_dither ( nv_crtc , true ) ;
2009-12-11 19:24:15 +10:00
}
2012-01-21 23:13:26 +01:00
if ( nv_crtc & & nv_crtc - > set_color_vibrance ) {
/* Hue */
if ( property = = disp - > vibrant_hue_property ) {
nv_crtc - > vibrant_hue = value - 90 ;
return nv_crtc - > set_color_vibrance ( nv_crtc , true ) ;
}
/* Saturation */
if ( property = = disp - > color_vibrance_property ) {
nv_crtc - > color_vibrance = value - 100 ;
return nv_crtc - > set_color_vibrance ( nv_crtc , true ) ;
}
}
2012-07-11 10:44:20 +10:00
if ( nv_encoder & & nv_encoder - > dcb - > type = = DCB_OUTPUT_TV )
2010-07-20 16:48:08 +02:00
return get_slave_funcs ( encoder ) - > set_property (
encoder , connector , property , value ) ;
2009-12-11 19:24:15 +10:00
return - EINVAL ;
}
static struct drm_display_mode *
2010-05-28 10:13:16 +10:00
nouveau_connector_native_mode ( struct drm_connector * connector )
2009-12-11 19:24:15 +10:00
{
2015-03-11 11:51:04 +02:00
const struct drm_connector_helper_funcs * helper = connector - > helper_private ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2010-05-28 10:13:16 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct drm_device * dev = connector - > dev ;
2009-12-11 19:24:15 +10:00
struct drm_display_mode * mode , * largest = NULL ;
int high_w = 0 , high_h = 0 , high_v = 0 ;
2010-05-28 10:13:16 +10:00
list_for_each_entry ( mode , & nv_connector - > base . probed_modes , head ) {
2011-02-07 08:41:18 +10:00
mode - > vrefresh = drm_mode_vrefresh ( mode ) ;
2010-07-18 16:19:16 +02:00
if ( helper - > mode_valid ( connector , mode ) ! = MODE_OK | |
( mode - > flags & DRM_MODE_FLAG_INTERLACE ) )
2010-05-28 10:13:16 +10:00
continue ;
/* Use preferred mode if there is one.. */
2009-12-11 19:24:15 +10:00
if ( mode - > type & DRM_MODE_TYPE_PREFERRED ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " native mode from preferred \n " ) ;
2009-12-11 19:24:15 +10:00
return drm_mode_duplicate ( dev , mode ) ;
}
2010-05-28 10:13:16 +10:00
/* Otherwise, take the resolution with the largest width, then
* height , then vertical refresh
*/
2009-12-11 19:24:15 +10:00
if ( mode - > hdisplay < high_w )
continue ;
if ( mode - > hdisplay = = high_w & & mode - > vdisplay < high_h )
continue ;
if ( mode - > hdisplay = = high_w & & mode - > vdisplay = = high_h & &
mode - > vrefresh < high_v )
continue ;
high_w = mode - > hdisplay ;
high_h = mode - > vdisplay ;
high_v = mode - > vrefresh ;
largest = mode ;
}
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " native mode from largest: %dx%d@%d \n " ,
2009-12-11 19:24:15 +10:00
high_w , high_h , high_v ) ;
return largest ? drm_mode_duplicate ( dev , largest ) : NULL ;
}
struct moderec {
int hdisplay ;
int vdisplay ;
} ;
static struct moderec scaler_modes [ ] = {
{ 1920 , 1200 } ,
{ 1920 , 1080 } ,
{ 1680 , 1050 } ,
{ 1600 , 1200 } ,
{ 1400 , 1050 } ,
{ 1280 , 1024 } ,
{ 1280 , 960 } ,
{ 1152 , 864 } ,
{ 1024 , 768 } ,
{ 800 , 600 } ,
{ 720 , 400 } ,
{ 640 , 480 } ,
{ 640 , 400 } ,
{ 640 , 350 } ,
{ }
} ;
static int
nouveau_connector_scaler_modes_add ( struct drm_connector * connector )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct drm_display_mode * native = nv_connector - > native_mode , * m ;
struct drm_device * dev = connector - > dev ;
struct moderec * mode = & scaler_modes [ 0 ] ;
int modes = 0 ;
if ( ! native )
return 0 ;
while ( mode - > hdisplay ) {
if ( mode - > hdisplay < = native - > hdisplay & &
2014-12-22 17:28:35 +10:00
mode - > vdisplay < = native - > vdisplay & &
( mode - > hdisplay ! = native - > hdisplay | |
mode - > vdisplay ! = native - > vdisplay ) ) {
2009-12-11 19:24:15 +10:00
m = drm_cvt_mode ( dev , mode - > hdisplay , mode - > vdisplay ,
drm_mode_vrefresh ( native ) , false ,
false , false ) ;
if ( ! m )
continue ;
drm_mode_probed_add ( connector , m ) ;
modes + + ;
}
mode + + ;
}
return modes ;
}
2011-10-17 10:24:49 +10:00
static void
nouveau_connector_detect_depth ( struct drm_connector * connector )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2011-10-17 10:24:49 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2012-07-31 16:16:21 +10:00
struct nvbios * bios = & drm - > vbios ;
2011-10-17 10:24:49 +10:00
struct drm_display_mode * mode = nv_connector - > native_mode ;
bool duallink ;
/* if the edid is feeling nice enough to provide this info, use it */
if ( nv_connector - > edid & & connector - > display_info . bpc )
return ;
2012-05-05 00:39:21 +10:00
/* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */
if ( nv_connector - > type = = DCB_CONNECTOR_eDP ) {
connector - > display_info . bpc = 6 ;
return ;
}
/* we're out of options unless we're LVDS, default to 8bpc */
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type ! = DCB_OUTPUT_LVDS ) {
2012-03-24 16:26:13 +10:00
connector - > display_info . bpc = 8 ;
2011-10-17 10:24:49 +10:00
return ;
2012-03-24 16:26:13 +10:00
}
connector - > display_info . bpc = 6 ;
2011-10-17 10:24:49 +10:00
/* LVDS: panel straps */
if ( bios - > fp_no_ddc ) {
if ( bios - > fp . if_is_24bit )
connector - > display_info . bpc = 8 ;
return ;
}
/* LVDS: DDC panel, need to first determine the number of links to
* know which if_is_24bit flag to check . . .
*/
if ( nv_connector - > edid & &
2011-11-18 10:23:59 +10:00
nv_connector - > type = = DCB_CONNECTOR_LVDS_SPWG )
2011-10-17 10:24:49 +10:00
duallink = ( ( u8 * ) nv_connector - > edid ) [ 121 ] = = 2 ;
else
duallink = mode - > clock > = bios - > fp . duallink_transition_clk ;
if ( ( ! duallink & & ( bios - > fp . strapless_is_24bit & 1 ) ) | |
( duallink & & ( bios - > fp . strapless_is_24bit & 2 ) ) )
connector - > display_info . bpc = 8 ;
}
2009-12-11 19:24:15 +10:00
static int
nouveau_connector_get_modes ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-11 19:24:15 +10:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2010-07-20 16:48:08 +02:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 19:24:15 +10:00
int ret = 0 ;
2010-06-01 13:32:42 +10:00
/* destroy the native mode, the attached monitor could have changed.
2009-12-11 19:24:15 +10:00
*/
2010-06-01 13:32:42 +10:00
if ( nv_connector - > native_mode ) {
2009-12-11 19:24:15 +10:00
drm_mode_destroy ( dev , nv_connector - > native_mode ) ;
nv_connector - > native_mode = NULL ;
}
if ( nv_connector - > edid )
ret = drm_add_edid_modes ( connector , nv_connector - > edid ) ;
2010-06-01 13:32:42 +10:00
else
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_LVDS & &
2010-06-01 13:32:42 +10:00
( nv_encoder - > dcb - > lvdsconf . use_straps_for_mode | |
2012-07-31 16:16:21 +10:00
drm - > vbios . fp_no_ddc ) & & nouveau_bios_fp_mode ( dev , NULL ) ) {
2010-09-23 16:37:39 +10:00
struct drm_display_mode mode ;
nouveau_bios_fp_mode ( dev , & mode ) ;
nv_connector - > native_mode = drm_mode_duplicate ( dev , & mode ) ;
2010-06-01 13:32:42 +10:00
}
2009-12-11 19:24:15 +10:00
2011-11-15 14:31:13 +10:00
/* Determine display colour depth for everything except LVDS now,
* DP requires this before mode_valid ( ) is called .
*/
if ( connector - > connector_type ! = DRM_MODE_CONNECTOR_LVDS )
nouveau_connector_detect_depth ( connector ) ;
2009-12-11 19:24:15 +10:00
/* Find the native mode if this is a digital panel, if we didn't
* find any modes through DDC previously add the native mode to
* the list of modes .
*/
if ( ! nv_connector - > native_mode )
nv_connector - > native_mode =
2010-05-28 10:13:16 +10:00
nouveau_connector_native_mode ( connector ) ;
2009-12-11 19:24:15 +10:00
if ( ret = = 0 & & nv_connector - > native_mode ) {
struct drm_display_mode * mode ;
mode = drm_mode_duplicate ( dev , nv_connector - > native_mode ) ;
drm_mode_probed_add ( connector , mode ) ;
ret = 1 ;
}
2011-11-15 14:31:13 +10:00
/* Determine LVDS colour depth, must happen after determining
* " native " mode as some VBIOS tables require us to use the
* pixel clock as part of the lookup . . .
2011-10-17 10:24:49 +10:00
*/
2011-11-15 14:31:13 +10:00
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_LVDS )
nouveau_connector_detect_depth ( connector ) ;
2011-10-17 10:24:49 +10:00
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_TV )
2010-07-20 16:48:08 +02:00
ret = get_slave_funcs ( encoder ) - > get_modes ( encoder , connector ) ;
2009-12-11 19:24:15 +10:00
2011-11-18 10:23:59 +10:00
if ( nv_connector - > type = = DCB_CONNECTOR_LVDS | |
nv_connector - > type = = DCB_CONNECTOR_LVDS_SPWG | |
nv_connector - > type = = DCB_CONNECTOR_eDP )
2009-12-11 19:24:15 +10:00
ret + = nouveau_connector_scaler_modes_add ( connector ) ;
return ret ;
}
2010-10-02 16:00:35 +02:00
static unsigned
get_tmds_link_bandwidth ( struct drm_connector * connector )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2012-07-11 10:44:20 +10:00
struct dcb_output * dcb = nv_connector - > detected_encoder - > dcb ;
2010-10-02 16:00:35 +02:00
if ( dcb - > location ! = DCB_LOC_ON_CHIP | |
2014-08-10 04:10:22 +10:00
drm - > device . info . chipset > = 0x46 )
2010-10-02 16:00:35 +02:00
return 165000 ;
2014-08-10 04:10:22 +10:00
else if ( drm - > device . info . chipset > = 0x40 )
2010-10-02 16:00:35 +02:00
return 155000 ;
2014-08-10 04:10:22 +10:00
else if ( drm - > device . info . chipset > = 0x18 )
2010-10-02 16:00:35 +02:00
return 135000 ;
else
return 112000 ;
}
2009-12-11 19:24:15 +10:00
static int
nouveau_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2010-07-20 16:48:08 +02:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 19:24:15 +10:00
unsigned min_clock = 25000 , max_clock = min_clock ;
unsigned clock = mode - > clock ;
switch ( nv_encoder - > dcb - > type ) {
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_LVDS :
2010-05-28 10:13:16 +10:00
if ( nv_connector - > native_mode & &
( mode - > hdisplay > nv_connector - > native_mode - > hdisplay | |
mode - > vdisplay > nv_connector - > native_mode - > vdisplay ) )
2009-12-11 19:24:15 +10:00
return MODE_PANEL ;
min_clock = 0 ;
max_clock = 400000 ;
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_TMDS :
2010-10-02 16:00:35 +02:00
max_clock = get_tmds_link_bandwidth ( connector ) ;
if ( nouveau_duallink & & nv_encoder - > dcb - > duallink_possible )
max_clock * = 2 ;
2009-12-11 19:24:15 +10:00
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_ANALOG :
2009-12-11 19:24:15 +10:00
max_clock = nv_encoder - > dcb - > crtconf . maxfreq ;
if ( ! max_clock )
max_clock = 350000 ;
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_TV :
2010-07-20 16:48:08 +02:00
return get_slave_funcs ( encoder ) - > mode_valid ( encoder , mode ) ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_DP :
2011-08-04 09:55:44 +10:00
max_clock = nv_encoder - > dp . link_nr ;
max_clock * = nv_encoder - > dp . link_bw ;
2011-11-15 14:31:13 +10:00
clock = clock * ( connector - > display_info . bpc * 3 ) / 10 ;
2009-12-11 19:24:15 +10:00
break ;
2010-02-24 10:31:39 +10:00
default :
BUG_ON ( 1 ) ;
return MODE_BAD ;
2009-12-11 19:24:15 +10:00
}
if ( clock < min_clock )
return MODE_CLOCK_LOW ;
if ( clock > max_clock )
return MODE_CLOCK_HIGH ;
return MODE_OK ;
}
static struct drm_encoder *
nouveau_connector_best_encoder ( struct drm_connector * connector )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
if ( nv_connector - > detected_encoder )
return to_drm_encoder ( nv_connector - > detected_encoder ) ;
return NULL ;
}
static const struct drm_connector_helper_funcs
nouveau_connector_helper_funcs = {
. get_modes = nouveau_connector_get_modes ,
. mode_valid = nouveau_connector_mode_valid ,
. best_encoder = nouveau_connector_best_encoder ,
} ;
static const struct drm_connector_funcs
nouveau_connector_funcs = {
. dpms = drm_helper_connector_dpms ,
. detect = nouveau_connector_detect ,
. destroy = nouveau_connector_destroy ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = nouveau_connector_set_property ,
. force = nouveau_connector_force
} ;
2010-06-01 13:32:42 +10:00
static const struct drm_connector_funcs
nouveau_connector_funcs_lvds = {
. dpms = drm_helper_connector_dpms ,
. detect = nouveau_connector_detect_lvds ,
. destroy = nouveau_connector_destroy ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = nouveau_connector_set_property ,
. force = nouveau_connector_force
} ;
2009-12-11 19:24:15 +10:00
2015-07-21 11:34:55 +02:00
static int
2014-05-31 01:48:06 +10:00
nouveau_connector_dp_dpms ( struct drm_connector * connector , int mode )
{
struct nouveau_encoder * nv_encoder = NULL ;
if ( connector - > encoder )
nv_encoder = nouveau_encoder ( connector - > encoder ) ;
if ( nv_encoder & & nv_encoder - > dcb & &
nv_encoder - > dcb - > type = = DCB_OUTPUT_DP ) {
if ( mode = = DRM_MODE_DPMS_ON ) {
u8 data = DP_SET_POWER_D0 ;
2015-08-20 14:54:15 +10:00
nvkm_wraux ( nv_encoder - > aux , DP_SET_POWER , & data , 1 ) ;
2014-05-31 01:48:06 +10:00
usleep_range ( 1000 , 2000 ) ;
} else {
u8 data = DP_SET_POWER_D3 ;
2015-08-20 14:54:15 +10:00
nvkm_wraux ( nv_encoder - > aux , DP_SET_POWER , & data , 1 ) ;
2014-05-31 01:48:06 +10:00
}
}
2015-07-21 11:34:55 +02:00
return drm_helper_connector_dpms ( connector , mode ) ;
2014-05-31 01:48:06 +10:00
}
static const struct drm_connector_funcs
nouveau_connector_funcs_dp = {
. dpms = nouveau_connector_dp_dpms ,
. detect = nouveau_connector_detect ,
. destroy = nouveau_connector_destroy ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = nouveau_connector_set_property ,
. force = nouveau_connector_force
} ;
2014-08-10 04:10:20 +10:00
static int
2014-08-10 04:10:28 +10:00
nouveau_connector_hotplug ( struct nvif_notify * notify )
2013-02-03 12:56:16 +10:00
{
struct nouveau_connector * nv_connector =
2014-08-10 04:10:20 +10:00
container_of ( notify , typeof ( * nv_connector ) , hpd ) ;
2013-02-03 12:56:16 +10:00
struct drm_connector * connector = & nv_connector - > base ;
2014-05-30 12:49:17 +10:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2014-08-10 04:10:20 +10:00
const struct nvif_notify_conn_rep_v0 * rep = notify - > data ;
2014-05-30 12:49:17 +10:00
const char * name = connector - > name ;
2013-02-03 12:56:16 +10:00
2014-08-10 04:10:20 +10:00
if ( rep - > mask & NVIF_NOTIFY_CONN_V0_IRQ ) {
2014-05-30 12:49:17 +10:00
} else {
2014-08-10 04:10:20 +10:00
bool plugged = ( rep - > mask ! = NVIF_NOTIFY_CONN_V0_UNPLUG ) ;
2013-02-03 12:56:16 +10:00
2014-05-30 12:49:17 +10:00
NV_DEBUG ( drm , " %splugged %s \n " , plugged ? " " : " un " , name ) ;
2013-02-03 12:56:16 +10:00
2014-05-30 12:49:17 +10:00
if ( plugged )
drm_helper_connector_dpms ( connector , DRM_MODE_DPMS_ON ) ;
else
drm_helper_connector_dpms ( connector , DRM_MODE_DPMS_OFF ) ;
drm_helper_hpd_irq_event ( connector - > dev ) ;
}
2014-08-10 04:10:28 +10:00
return NVIF_NOTIFY_KEEP ;
2013-02-03 12:56:16 +10:00
}
2014-05-30 16:20:58 +10:00
static ssize_t
2015-08-20 14:54:15 +10:00
nouveau_connector_aux_xfer ( struct drm_dp_aux * obj , struct drm_dp_aux_msg * msg )
2014-05-30 16:20:58 +10:00
{
struct nouveau_connector * nv_connector =
2015-08-20 14:54:15 +10:00
container_of ( obj , typeof ( * nv_connector ) , aux ) ;
2014-05-30 16:20:58 +10:00
struct nouveau_encoder * nv_encoder ;
2015-08-20 14:54:15 +10:00
struct nvkm_i2c_aux * aux ;
2014-05-30 16:20:58 +10:00
int ret ;
nv_encoder = find_encoder ( & nv_connector - > base , DCB_OUTPUT_DP ) ;
2015-08-20 14:54:15 +10:00
if ( ! nv_encoder | | ! ( aux = nv_encoder - > aux ) )
2014-05-30 16:20:58 +10:00
return - ENODEV ;
if ( WARN_ON ( msg - > size > 16 ) )
return - E2BIG ;
if ( msg - > size = = 0 )
return msg - > size ;
2015-08-20 14:54:15 +10:00
ret = nvkm_i2c_aux_acquire ( aux ) ;
2014-05-30 16:20:58 +10:00
if ( ret )
return ret ;
2015-08-20 14:54:15 +10:00
ret = nvkm_i2c_aux_xfer ( aux , false , msg - > request , msg - > address ,
msg - > buffer , msg - > size ) ;
nvkm_i2c_aux_release ( aux ) ;
2014-05-30 16:20:58 +10:00
if ( ret > = 0 ) {
msg - > reply = ret ;
return msg - > size ;
}
return ret ;
}
2011-11-18 10:23:59 +10:00
static int
drm_conntype_from_dcb ( enum dcb_connector_type dcb )
{
switch ( dcb ) {
case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA ;
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV ;
2012-03-22 11:27:15 +10:00
case DCB_CONNECTOR_DMS59_0 :
case DCB_CONNECTOR_DMS59_1 :
2011-11-18 10:23:59 +10:00
case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII ;
case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID ;
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG : return DRM_MODE_CONNECTOR_LVDS ;
2012-01-12 16:17:16 +10:00
case DCB_CONNECTOR_DMS59_DP0 :
case DCB_CONNECTOR_DMS59_DP1 :
2011-11-18 10:23:59 +10:00
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort ;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP ;
case DCB_CONNECTOR_HDMI_0 :
2014-02-24 13:41:08 +10:00
case DCB_CONNECTOR_HDMI_1 :
case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA ;
2011-11-18 10:23:59 +10:00
default :
break ;
}
return DRM_MODE_CONNECTOR_Unknown ;
}
2010-06-28 14:35:50 +10:00
struct drm_connector *
nouveau_connector_create ( struct drm_device * dev , int index )
2009-12-11 19:24:15 +10:00
{
2010-06-01 13:32:42 +10:00
const struct drm_connector_funcs * funcs = & nouveau_connector_funcs ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_display * disp = nouveau_display ( dev ) ;
2009-12-11 19:24:15 +10:00
struct nouveau_connector * nv_connector = NULL ;
struct drm_connector * connector ;
2010-06-16 15:52:44 +02:00
int type , ret = 0 ;
2011-11-18 10:23:59 +10:00
bool dummy ;
2009-12-11 19:24:15 +10:00
2011-11-18 10:23:59 +10:00
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
nv_connector = nouveau_connector ( connector ) ;
if ( nv_connector - > index = = index )
return connector ;
2009-12-11 19:24:15 +10:00
}
2010-02-24 12:07:31 +10:00
nv_connector = kzalloc ( sizeof ( * nv_connector ) , GFP_KERNEL ) ;
if ( ! nv_connector )
2010-06-28 14:35:50 +10:00
return ERR_PTR ( - ENOMEM ) ;
2011-11-18 10:23:59 +10:00
2010-02-24 12:07:31 +10:00
connector = & nv_connector - > base ;
2011-11-18 10:23:59 +10:00
nv_connector - > index = index ;
/* attempt to parse vbios connector type and hotplug gpio */
2012-07-11 10:44:20 +10:00
nv_connector - > dcb = olddcb_conn ( dev , index ) ;
2011-11-18 10:23:59 +10:00
if ( nv_connector - > dcb ) {
u32 entry = ROM16 ( nv_connector - > dcb [ 0 ] ) ;
2012-07-11 10:44:20 +10:00
if ( olddcb_conntab ( dev ) [ 3 ] > = 4 )
2011-11-18 10:23:59 +10:00
entry | = ( u32 ) ROM16 ( nv_connector - > dcb [ 2 ] ) < < 16 ;
nv_connector - > type = nv_connector - > dcb [ 0 ] ;
if ( drm_conntype_from_dcb ( nv_connector - > type ) = =
DRM_MODE_CONNECTOR_Unknown ) {
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " unknown connector type %02x \n " ,
2011-11-18 10:23:59 +10:00
nv_connector - > type ) ;
nv_connector - > type = DCB_CONNECTOR_NONE ;
}
2010-02-24 12:07:31 +10:00
2011-11-18 10:23:59 +10:00
/* Gigabyte NX85T */
if ( nv_match_device ( dev , 0x0421 , 0x1458 , 0x344c ) ) {
if ( nv_connector - > type = = DCB_CONNECTOR_HDMI_1 )
nv_connector - > type = DCB_CONNECTOR_DVI_I ;
}
2009-12-11 19:24:15 +10:00
2011-11-18 10:23:59 +10:00
/* Gigabyte GV-NX86T512H */
if ( nv_match_device ( dev , 0x0402 , 0x1458 , 0x3455 ) ) {
if ( nv_connector - > type = = DCB_CONNECTOR_HDMI_1 )
nv_connector - > type = DCB_CONNECTOR_DVI_I ;
}
} else {
nv_connector - > type = DCB_CONNECTOR_NONE ;
}
/* no vbios data, or an unknown dcb connector type - attempt to
* figure out something suitable ourselves
*/
if ( nv_connector - > type = = DCB_CONNECTOR_NONE ) {
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct dcb_table * dcbt = & drm - > vbios . dcb ;
2011-11-18 10:23:59 +10:00
u32 encoders = 0 ;
int i ;
for ( i = 0 ; i < dcbt - > entries ; i + + ) {
if ( dcbt - > entry [ i ] . connector = = nv_connector - > index )
encoders | = ( 1 < < dcbt - > entry [ i ] . type ) ;
}
2009-12-11 19:24:15 +10:00
2012-07-11 10:44:20 +10:00
if ( encoders & ( 1 < < DCB_OUTPUT_DP ) ) {
if ( encoders & ( 1 < < DCB_OUTPUT_TMDS ) )
2011-11-18 10:23:59 +10:00
nv_connector - > type = DCB_CONNECTOR_DP ;
else
nv_connector - > type = DCB_CONNECTOR_eDP ;
} else
2012-07-11 10:44:20 +10:00
if ( encoders & ( 1 < < DCB_OUTPUT_TMDS ) ) {
if ( encoders & ( 1 < < DCB_OUTPUT_ANALOG ) )
2011-11-18 10:23:59 +10:00
nv_connector - > type = DCB_CONNECTOR_DVI_I ;
else
nv_connector - > type = DCB_CONNECTOR_DVI_D ;
} else
2012-07-11 10:44:20 +10:00
if ( encoders & ( 1 < < DCB_OUTPUT_ANALOG ) ) {
2011-11-18 10:23:59 +10:00
nv_connector - > type = DCB_CONNECTOR_VGA ;
} else
2012-07-11 10:44:20 +10:00
if ( encoders & ( 1 < < DCB_OUTPUT_LVDS ) ) {
2011-11-18 10:23:59 +10:00
nv_connector - > type = DCB_CONNECTOR_LVDS ;
} else
2012-07-11 10:44:20 +10:00
if ( encoders & ( 1 < < DCB_OUTPUT_TV ) ) {
2011-11-18 10:23:59 +10:00
nv_connector - > type = DCB_CONNECTOR_TV_0 ;
}
}
2010-06-16 15:52:44 +02:00
2014-05-30 16:20:58 +10:00
switch ( ( type = drm_conntype_from_dcb ( nv_connector - > type ) ) ) {
case DRM_MODE_CONNECTOR_LVDS :
2011-11-18 10:23:59 +10:00
ret = nouveau_bios_parse_lvds_table ( dev , 0 , & dummy , & dummy ) ;
2010-06-16 15:52:44 +02:00
if ( ret ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " Error parsing LVDS table, disabling \n " ) ;
2011-11-18 10:23:59 +10:00
kfree ( nv_connector ) ;
return ERR_PTR ( ret ) ;
2010-06-16 15:52:44 +02:00
}
2011-11-18 10:23:59 +10:00
funcs = & nouveau_connector_funcs_lvds ;
2014-05-30 16:20:58 +10:00
break ;
case DRM_MODE_CONNECTOR_DisplayPort :
case DRM_MODE_CONNECTOR_eDP :
nv_connector - > aux . dev = dev - > dev ;
nv_connector - > aux . transfer = nouveau_connector_aux_xfer ;
ret = drm_dp_aux_register ( & nv_connector - > aux ) ;
if ( ret ) {
NV_ERROR ( drm , " failed to register aux channel \n " ) ;
kfree ( nv_connector ) ;
return ERR_PTR ( ret ) ;
}
2014-05-31 01:48:06 +10:00
funcs = & nouveau_connector_funcs_dp ;
2014-05-30 16:20:58 +10:00
break ;
default :
funcs = & nouveau_connector_funcs ;
break ;
2010-02-24 12:07:31 +10:00
}
2011-11-18 10:23:59 +10:00
/* defaults, will get overridden in detect() */
connector - > interlace_allowed = false ;
connector - > doublescan_allowed = false ;
drm_connector_init ( dev , connector , funcs , type ) ;
drm_connector_helper_add ( connector , & nouveau_connector_helper_funcs ) ;
2009-12-11 19:24:15 +10:00
/* Init DVI-I specific properties */
2011-11-18 10:23:59 +10:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I )
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base , dev - > mode_config . dvi_i_subconnector_property , 0 ) ;
2009-12-11 19:24:15 +10:00
2011-10-06 13:29:05 +10:00
/* Add overscan compensation options to digital outputs */
2011-10-17 12:23:41 +10:00
if ( disp - > underscan_property & &
2012-03-22 11:27:15 +10:00
( type = = DRM_MODE_CONNECTOR_DVID | |
type = = DRM_MODE_CONNECTOR_DVII | |
type = = DRM_MODE_CONNECTOR_HDMIA | |
type = = DRM_MODE_CONNECTOR_DisplayPort ) ) {
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 13:29:05 +10:00
disp - > underscan_property ,
UNDERSCAN_OFF ) ;
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 13:29:05 +10:00
disp - > underscan_hborder_property ,
0 ) ;
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 13:29:05 +10:00
disp - > underscan_vborder_property ,
0 ) ;
}
2012-01-21 23:13:26 +01:00
/* Add hue and saturation options */
if ( disp - > vibrant_hue_property )
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2012-01-21 23:13:26 +01:00
disp - > vibrant_hue_property ,
90 ) ;
if ( disp - > color_vibrance_property )
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2012-01-21 23:13:26 +01:00
disp - > color_vibrance_property ,
150 ) ;
2014-12-22 18:15:55 +10:00
/* default scaling mode */
2011-11-18 10:23:59 +10:00
switch ( nv_connector - > type ) {
2014-12-22 18:19:45 +10:00
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG :
case DCB_CONNECTOR_eDP :
/* see note in nouveau_connector_set_property() */
if ( disp - > disp . oclass < NV50_DISP ) {
nv_connector - > scaling_mode = DRM_MODE_SCALE_FULLSCREEN ;
break ;
}
2010-02-24 14:01:40 +10:00
nv_connector - > scaling_mode = DRM_MODE_SCALE_NONE ;
break ;
default :
2014-12-22 18:19:45 +10:00
nv_connector - > scaling_mode = DRM_MODE_SCALE_NONE ;
2014-12-22 18:15:55 +10:00
break ;
}
2010-02-24 14:01:40 +10:00
2014-12-22 18:15:55 +10:00
/* scaling mode property */
switch ( nv_connector - > type ) {
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 :
break ;
case DCB_CONNECTOR_VGA :
if ( disp - > disp . oclass < NV50_DISP )
break ; /* can only scale on DFPs */
/* fall-through */
default :
drm_object_attach_property ( & connector - > base , dev - > mode_config .
scaling_mode_property ,
nv_connector - > scaling_mode ) ;
break ;
}
/* dithering properties */
switch ( nv_connector - > type ) {
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 :
case DCB_CONNECTOR_VGA :
break ;
default :
2011-10-17 12:23:41 +10:00
if ( disp - > dithering_mode ) {
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2014-12-22 18:15:55 +10:00
disp - > dithering_mode ,
nv_connector - >
dithering_mode ) ;
nv_connector - > dithering_mode = DITHERING_MODE_AUTO ;
2011-10-17 12:23:41 +10:00
}
if ( disp - > dithering_depth ) {
2012-10-11 20:42:04 -05:00
drm_object_attach_property ( & connector - > base ,
2014-12-22 18:15:55 +10:00
disp - > dithering_depth ,
nv_connector - >
dithering_depth ) ;
nv_connector - > dithering_depth = DITHERING_DEPTH_AUTO ;
2011-10-17 12:23:41 +10:00
}
2010-02-24 14:01:40 +10:00
break ;
2009-12-11 19:24:15 +10:00
}
2015-08-20 14:54:15 +10:00
ret = nvif_notify_init ( & disp - > disp , nouveau_connector_hotplug , true ,
NV04_DISP_NTFY_CONN ,
2014-08-10 04:10:20 +10:00
& ( struct nvif_notify_conn_req_v0 ) {
. mask = NVIF_NOTIFY_CONN_V0_ANY ,
. conn = index ,
} ,
sizeof ( struct nvif_notify_conn_req_v0 ) ,
sizeof ( struct nvif_notify_conn_rep_v0 ) ,
& nv_connector - > hpd ) ;
2014-05-30 12:49:17 +10:00
if ( ret )
connector - > polled = DRM_CONNECTOR_POLL_CONNECT ;
else
2013-02-03 12:56:16 +10:00
connector - > polled = DRM_CONNECTOR_POLL_HPD ;
2010-11-11 16:14:56 +10:00
2014-05-29 16:57:41 +01:00
drm_connector_register ( connector ) ;
2011-11-18 10:23:59 +10:00
return connector ;
2009-12-11 19:24:15 +10:00
}