2009-12-11 12:24:15 +03: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 04:42:37 +03:00
# include <acpi/button.h>
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/drm_edid.h>
# include <drm/drm_crtc_helper.h>
2010-01-18 04:42:37 +03:00
2009-12-11 12:24:15 +03:00
# include "nouveau_reg.h"
2012-07-31 10:16:21 +04:00
# include "nouveau_drm.h"
2013-03-21 09:45:11 +04:00
# include "dispnv04/hw.h"
2012-07-26 02:51:21 +04:00
# include "nouveau_acpi.h"
2009-12-11 12:24:15 +03:00
2012-07-31 10:16:21 +04:00
# include "nouveau_display.h"
# include "nouveau_connector.h"
2009-12-11 12:24:15 +03:00
# include "nouveau_encoder.h"
# include "nouveau_crtc.h"
2012-07-31 10:16:21 +04:00
# include <subdev/i2c.h>
# include <subdev/gpio.h>
MODULE_PARM_DESC ( tv_disable , " Disable TV-out detection " ) ;
static int nouveau_tv_disable = 0 ;
module_param_named ( tv_disable , nouveau_tv_disable , int , 0400 ) ;
MODULE_PARM_DESC ( ignorelid , " Ignore ACPI lid status " ) ;
static int nouveau_ignorelid = 0 ;
module_param_named ( ignorelid , nouveau_ignorelid , int , 0400 ) ;
MODULE_PARM_DESC ( duallink , " Allow dual-link TMDS (default: enabled) " ) ;
static int nouveau_duallink = 1 ;
module_param_named ( duallink , nouveau_duallink , int , 0400 ) ;
2009-12-11 12:24:15 +03:00
2011-08-02 13:29:37 +04:00
struct nouveau_encoder *
2011-07-12 07:32:07 +04:00
find_encoder ( struct drm_connector * connector , int type )
2009-12-11 12:24:15 +03:00
{
struct drm_device * dev = connector - > dev ;
struct nouveau_encoder * nv_encoder ;
struct drm_mode_object * obj ;
int i , id ;
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
id = connector - > encoder_ids [ i ] ;
if ( ! id )
break ;
obj = drm_mode_object_find ( dev , id , DRM_MODE_OBJECT_ENCODER ) ;
if ( ! obj )
continue ;
nv_encoder = nouveau_encoder ( obj_to_encoder ( obj ) ) ;
2012-07-11 04:44:20 +04:00
if ( type = = DCB_OUTPUT_ANY | | nv_encoder - > dcb - > type = = type )
2009-12-11 12:24:15 +03: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 09:14:56 +03:00
nouveau_connector_destroy ( struct drm_connector * connector )
2009-12-11 12:24:15 +03:00
{
2010-11-11 09:14:56 +03:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2010-01-12 00:42:21 +03:00
kfree ( nv_connector - > edid ) ;
2010-11-11 09:14:56 +03:00
drm_sysfs_connector_remove ( connector ) ;
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
2009-12-11 12:24:15 +03:00
}
2012-07-10 08:36:38 +04:00
static struct nouveau_i2c_port *
2009-12-11 12:24:15 +03:00
nouveau_connector_ddc_detect ( struct drm_connector * connector ,
struct nouveau_encoder * * pnv_encoder )
{
struct drm_device * dev = connector - > dev ;
2012-12-10 12:53:43 +04:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2012-12-10 12:53:43 +04:00
struct nouveau_gpio * gpio = nouveau_gpio ( drm - > device ) ;
struct nouveau_i2c_port * port = NULL ;
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 ) {
panel = gpio - > get ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff ) ;
if ( panel = = 0 ) {
gpio - > set ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff , 1 ) ;
msleep ( 300 ) ;
}
}
2009-12-11 12:24:15 +03:00
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
struct nouveau_encoder * nv_encoder ;
struct drm_mode_object * obj ;
int id ;
id = connector - > encoder_ids [ i ] ;
if ( ! id )
break ;
obj = drm_mode_object_find ( dev , id , DRM_MODE_OBJECT_ENCODER ) ;
if ( ! obj )
continue ;
nv_encoder = nouveau_encoder ( obj_to_encoder ( obj ) ) ;
2010-08-08 23:35:57 +04:00
2013-02-11 14:15:03 +04:00
port = nv_encoder - > i2c ;
2012-07-31 10:16:21 +04:00
if ( port & & nv_probe_i2c ( port , 0x50 ) ) {
2009-12-11 12:24:15 +03:00
* pnv_encoder = nv_encoder ;
2012-12-10 12:53:43 +04:00
break ;
2009-12-11 12:24:15 +03:00
}
2012-12-10 12:53:43 +04:00
port = NULL ;
2009-12-11 12:24:15 +03:00
}
2012-12-10 12:53:43 +04:00
/* eDP panel not detected, restore panel power GPIO to previous
* state to avoid confusing the SOR for other output types .
*/
if ( ! port & & panel = = 0 )
gpio - > set ( gpio , 0 , DCB_GPIO_PANEL_POWER , 0xff , panel ) ;
return port ;
2009-12-11 12:24:15 +03:00
}
2010-09-09 16:33:17 +04: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 04:44:20 +04:00
! ( ( nv_encoder = find_encoder ( connector , DCB_OUTPUT_TMDS ) ) | |
( nv_encoder = find_encoder ( connector , DCB_OUTPUT_ANALOG ) ) ) )
2010-09-09 16:33:17 +04: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 12:24:15 +03: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 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2009-12-11 12:24:15 +03:00
struct drm_device * dev = connector - > dev ;
if ( nv_connector - > detected_encoder = = nv_encoder )
return ;
nv_connector - > detected_encoder = nv_encoder ;
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type > = NV_50 ) {
2011-10-19 07:06:48 +04:00
connector - > interlace_allowed = true ;
connector - > doublescan_allowed = true ;
} else
2012-07-11 04:44:20 +04:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_LVDS | |
nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS ) {
2009-12-11 12:24:15 +03:00
connector - > doublescan_allowed = false ;
connector - > interlace_allowed = false ;
} else {
connector - > doublescan_allowed = true ;
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type = = NV_20 | |
( nv_device ( drm - > device ) - > card_type = = NV_10 & &
2009-12-11 12:24:15 +03:00
( dev - > pci_device & 0x0ff0 ) ! = 0x0100 & &
( dev - > pci_device & 0x0ff0 ) ! = 0x0150 ) )
/* HW is broken */
connector - > interlace_allowed = false ;
else
connector - > interlace_allowed = true ;
}
2011-11-18 04:23:59 +04:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I ) {
2012-10-12 05:42:04 +04:00
drm_object_property_set_value ( & connector - > base ,
2009-12-11 12:24:15 +03:00
dev - > mode_config . dvi_i_subconnector_property ,
2012-07-11 04:44:20 +04:00
nv_encoder - > dcb - > type = = DCB_OUTPUT_TMDS ?
2009-12-11 12:24:15 +03:00
DRM_MODE_SUBCONNECTOR_DVID :
DRM_MODE_SUBCONNECTOR_DVIA ) ;
}
}
static enum drm_connector_status
2010-09-14 14:07:23 +04:00
nouveau_connector_detect ( struct drm_connector * connector , bool force )
2009-12-11 12:24:15 +03:00
{
struct drm_device * dev = connector - > dev ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-11 12:24:15 +03:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = NULL ;
2011-07-12 07:32:07 +04:00
struct nouveau_encoder * nv_partner ;
2012-07-10 08:36:38 +04:00
struct nouveau_i2c_port * i2c ;
2010-07-20 05:08:25 +04:00
int type ;
2009-12-11 12:24:15 +03:00
2010-01-14 17:54:23 +03: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-12 00:42:21 +03:00
2009-12-11 12:24:15 +03:00
i2c = nouveau_connector_ddc_detect ( connector , & nv_encoder ) ;
if ( i2c ) {
nv_connector - > edid = drm_get_edid ( connector , & i2c - > adapter ) ;
drm_mode_connector_update_edid_property ( connector ,
nv_connector - > edid ) ;
if ( ! nv_connector - > edid ) {
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " DDC responded, but no EDID for %s \n " ,
2009-12-11 12:24:15 +03:00
drm_get_connector_name ( connector ) ) ;
2010-01-14 17:47:03 +03:00
goto detect_analog ;
2009-12-11 12:24:15 +03:00
}
2012-07-11 04:44:20 +04:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_DP & &
2009-12-11 12:24:15 +03:00
! nouveau_dp_detect ( to_drm_encoder ( nv_encoder ) ) ) {
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " Detected %s, but failed init \n " ,
2009-12-11 12:24:15 +03:00
drm_get_connector_name ( connector ) ) ;
return connector_status_disconnected ;
}
/* 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 07:32:07 +04:00
nv_partner = NULL ;
2012-07-11 04:44:20 +04: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 12:24:15 +03:00
if ( nv_connector - > edid - > input & DRM_EDID_INPUT_DIGITAL )
2012-07-11 04:44:20 +04:00
type = DCB_OUTPUT_TMDS ;
2009-12-11 12:24:15 +03:00
else
2012-07-11 04:44:20 +04:00
type = DCB_OUTPUT_ANALOG ;
2009-12-11 12:24:15 +03:00
2011-07-12 07:32:07 +04:00
nv_encoder = find_encoder ( connector , type ) ;
2009-12-11 12:24:15 +03:00
}
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
return connector_status_connected ;
}
2010-09-09 16:33:17 +04:00
nv_encoder = nouveau_connector_of_detect ( connector ) ;
if ( nv_encoder ) {
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
return connector_status_connected ;
}
2010-01-14 17:47:03 +03:00
detect_analog :
2012-07-11 04:44:20 +04:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_ANALOG ) ;
2010-03-15 02:43:51 +03:00
if ( ! nv_encoder & & ! nouveau_tv_disable )
2012-07-11 04:44:20 +04:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_TV ) ;
2010-10-26 04:17:56 +04:00
if ( nv_encoder & & force ) {
2009-12-11 12:24:15 +03:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
struct drm_encoder_helper_funcs * helper =
encoder - > helper_private ;
if ( helper - > detect ( encoder , connector ) = =
connector_status_connected ) {
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
return connector_status_connected ;
}
}
return connector_status_disconnected ;
}
2010-06-01 07:32:42 +04:00
static enum drm_connector_status
2010-09-14 14:07:23 +04:00
nouveau_connector_detect_lvds ( struct drm_connector * connector , bool force )
2010-06-01 07:32:42 +04:00
{
struct drm_device * dev = connector - > dev ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2010-06-01 07:32:42 +04: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 04:44:20 +04:00
nv_encoder = find_encoder ( connector , DCB_OUTPUT_LVDS ) ;
2010-06-01 07:32:42 +04:00
if ( ! nv_encoder )
return connector_status_disconnected ;
2010-07-12 09:33:07 +04:00
/* Try retrieving EDID via DDC */
2012-07-31 10:16:21 +04:00
if ( ! drm - > vbios . fp_no_ddc ) {
2010-09-14 14:07:23 +04:00
status = nouveau_connector_detect ( connector , force ) ;
2010-06-01 07:32:42 +04:00
if ( status = = connector_status_connected )
goto out ;
}
2010-07-12 09:33:07 +04: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 20:19:38 +04:00
if ( ( nv_connector - > edid = nouveau_acpi_edid ( dev , connector ) ) ) {
2010-07-12 09:33:07 +04:00
status = connector_status_connected ;
goto out ;
}
}
2010-06-01 07:32:42 +04: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 10:16:21 +04:00
if ( nouveau_bios_fp_mode ( dev , NULL ) & & ( drm - > vbios . fp_no_ddc | |
2010-06-01 07:32:42 +04: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 10:16:21 +04:00
if ( ! drm - > vbios . fp_no_ddc ) {
2010-06-01 07:32:42 +04:00
struct edid * edid =
( struct edid * ) nouveau_bios_embedded_edid ( dev ) ;
if ( edid ) {
2013-01-27 20:04:48 +04:00
nv_connector - > edid =
kmemdup ( edid , EDID_LENGTH , GFP_KERNEL ) ;
if ( nv_connector - > edid )
status = connector_status_connected ;
2010-06-01 07:32:42 +04: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 18:57:57 +04:00
nouveau_connector_set_encoder ( connector , nv_encoder ) ;
2010-06-01 07:32:42 +04:00
return status ;
}
2009-12-11 12:24:15 +03:00
static void
nouveau_connector_force ( struct drm_connector * connector )
{
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2010-02-24 07:01:40 +03:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2009-12-11 12:24:15 +03:00
struct nouveau_encoder * nv_encoder ;
int type ;
2011-11-18 04:23:59 +04:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I ) {
2009-12-11 12:24:15 +03:00
if ( connector - > force = = DRM_FORCE_ON_DIGITAL )
2012-07-11 04:44:20 +04:00
type = DCB_OUTPUT_TMDS ;
2009-12-11 12:24:15 +03:00
else
2012-07-11 04:44:20 +04:00
type = DCB_OUTPUT_ANALOG ;
2009-12-11 12:24:15 +03:00
} else
2012-07-11 04:44:20 +04:00
type = DCB_OUTPUT_ANY ;
2009-12-11 12:24:15 +03:00
2011-07-12 07:32:07 +04:00
nv_encoder = find_encoder ( connector , type ) ;
2009-12-11 12:24:15 +03:00
if ( ! nv_encoder ) {
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " can't find encoder to force %s on! \n " ,
2009-12-11 12:24:15 +03:00
drm_get_connector_name ( connector ) ) ;
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 10:16:21 +04:00
struct nouveau_display * disp = nouveau_display ( connector - > dev ) ;
2009-12-11 12:24:15 +03:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2010-07-20 18:48:08 +04:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 12:24:15 +03:00
struct drm_device * dev = connector - > dev ;
2011-10-06 07:29:05 +04:00
struct nouveau_crtc * nv_crtc ;
2009-12-11 12:24:15 +03:00
int ret ;
2011-10-06 07:29:05 +04:00
nv_crtc = NULL ;
if ( connector - > encoder & & connector - > encoder - > crtc )
nv_crtc = nouveau_crtc ( connector - > encoder - > crtc ) ;
2009-12-11 12:24:15 +03:00
/* Scaling mode */
if ( property = = dev - > mode_config . scaling_mode_property ) {
bool modeset = false ;
switch ( value ) {
case DRM_MODE_SCALE_NONE :
case DRM_MODE_SCALE_FULLSCREEN :
case DRM_MODE_SCALE_CENTER :
case DRM_MODE_SCALE_ASPECT :
break ;
default :
return - EINVAL ;
}
/* LVDS always needs gpu scaling */
2011-04-18 03:57:48 +04:00
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_LVDS & &
2009-12-11 12:24:15 +03:00
value = = DRM_MODE_SCALE_NONE )
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 04:38:10 +04:00
ret = nv_crtc - > set_scale ( nv_crtc , true ) ;
2009-12-11 12:24:15 +03:00
if ( ret )
return ret ;
}
return 0 ;
}
2011-10-06 07:29:05 +04: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 04:38:10 +04:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 07:29:05 +04: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 04:38:10 +04:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 07:29:05 +04: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 04:38:10 +04:00
return nv_crtc - > set_scale ( nv_crtc , true ) ;
2011-10-06 07:29:05 +04:00
}
return 0 ;
}
2009-12-11 12:24:15 +03:00
/* Dithering */
2011-10-17 06:23:41 +04: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 12:24:15 +03:00
2011-10-17 06:23:41 +04:00
if ( property = = disp - > dithering_depth ) {
nv_connector - > dithering_depth = value ;
2009-12-11 12:24:15 +03:00
if ( ! nv_crtc | | ! nv_crtc - > set_dither )
return 0 ;
2011-10-17 04:38:10 +04:00
return nv_crtc - > set_dither ( nv_crtc , true ) ;
2009-12-11 12:24:15 +03:00
}
2012-01-22 02:13:26 +04: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 04:44:20 +04:00
if ( nv_encoder & & nv_encoder - > dcb - > type = = DCB_OUTPUT_TV )
2010-07-20 18:48:08 +04:00
return get_slave_funcs ( encoder ) - > set_property (
encoder , connector , property , value ) ;
2009-12-11 12:24:15 +03:00
return - EINVAL ;
}
static struct drm_display_mode *
2010-05-28 04:13:16 +04:00
nouveau_connector_native_mode ( struct drm_connector * connector )
2009-12-11 12:24:15 +03:00
{
2010-05-28 04:13:16 +04:00
struct drm_connector_helper_funcs * helper = connector - > helper_private ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2010-05-28 04:13:16 +04:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct drm_device * dev = connector - > dev ;
2009-12-11 12:24:15 +03:00
struct drm_display_mode * mode , * largest = NULL ;
int high_w = 0 , high_h = 0 , high_v = 0 ;
2010-05-28 04:13:16 +04:00
list_for_each_entry ( mode , & nv_connector - > base . probed_modes , head ) {
2011-02-07 01:41:18 +03:00
mode - > vrefresh = drm_mode_vrefresh ( mode ) ;
2010-07-18 18:19:16 +04:00
if ( helper - > mode_valid ( connector , mode ) ! = MODE_OK | |
( mode - > flags & DRM_MODE_FLAG_INTERLACE ) )
2010-05-28 04:13:16 +04:00
continue ;
/* Use preferred mode if there is one.. */
2009-12-11 12:24:15 +03:00
if ( mode - > type & DRM_MODE_TYPE_PREFERRED ) {
2012-07-31 10:16:21 +04:00
NV_DEBUG ( drm , " native mode from preferred \n " ) ;
2009-12-11 12:24:15 +03:00
return drm_mode_duplicate ( dev , mode ) ;
}
2010-05-28 04:13:16 +04:00
/* Otherwise, take the resolution with the largest width, then
* height , then vertical refresh
*/
2009-12-11 12:24:15 +03: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 10:16:21 +04:00
NV_DEBUG ( drm , " native mode from largest: %dx%d@%d \n " ,
2009-12-11 12:24:15 +03: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 & &
mode - > vdisplay < = native - > vdisplay ) {
m = drm_cvt_mode ( dev , mode - > hdisplay , mode - > vdisplay ,
drm_mode_vrefresh ( native ) , false ,
false , false ) ;
if ( ! m )
continue ;
m - > type | = DRM_MODE_TYPE_DRIVER ;
drm_mode_probed_add ( connector , m ) ;
modes + + ;
}
mode + + ;
}
return modes ;
}
2011-10-17 04:24:49 +04:00
static void
nouveau_connector_detect_depth ( struct drm_connector * connector )
{
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2011-10-17 04:24:49 +04:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2012-07-31 10:16:21 +04:00
struct nvbios * bios = & drm - > vbios ;
2011-10-17 04:24:49 +04:00
struct drm_display_mode * mode = nv_connector - > native_mode ;
bool duallink ;
/* if the edid is feeling nice enough to provide this info, use it */
if ( nv_connector - > edid & & connector - > display_info . bpc )
return ;
2012-05-04 18:39:21 +04: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 04:44:20 +04:00
if ( nv_encoder - > dcb - > type ! = DCB_OUTPUT_LVDS ) {
2012-03-24 10:26:13 +04:00
connector - > display_info . bpc = 8 ;
2011-10-17 04:24:49 +04:00
return ;
2012-03-24 10:26:13 +04:00
}
connector - > display_info . bpc = 6 ;
2011-10-17 04:24:49 +04: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 04:23:59 +04:00
nv_connector - > type = = DCB_CONNECTOR_LVDS_SPWG )
2011-10-17 04:24:49 +04: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 12:24:15 +03:00
static int
nouveau_connector_get_modes ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-11 12:24:15 +03:00
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
struct nouveau_encoder * nv_encoder = nv_connector - > detected_encoder ;
2010-07-20 18:48:08 +04:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 12:24:15 +03:00
int ret = 0 ;
2010-06-01 07:32:42 +04:00
/* destroy the native mode, the attached monitor could have changed.
2009-12-11 12:24:15 +03:00
*/
2010-06-01 07:32:42 +04:00
if ( nv_connector - > native_mode ) {
2009-12-11 12:24:15 +03: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 07:32:42 +04:00
else
2012-07-11 04:44:20 +04:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_LVDS & &
2010-06-01 07:32:42 +04:00
( nv_encoder - > dcb - > lvdsconf . use_straps_for_mode | |
2012-07-31 10:16:21 +04:00
drm - > vbios . fp_no_ddc ) & & nouveau_bios_fp_mode ( dev , NULL ) ) {
2010-09-23 10:37:39 +04:00
struct drm_display_mode mode ;
nouveau_bios_fp_mode ( dev , & mode ) ;
nv_connector - > native_mode = drm_mode_duplicate ( dev , & mode ) ;
2010-06-01 07:32:42 +04:00
}
2009-12-11 12:24:15 +03:00
2011-11-15 08:31:13 +04: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 12:24:15 +03: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 04:13:16 +04:00
nouveau_connector_native_mode ( connector ) ;
2009-12-11 12:24:15 +03: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 08:31:13 +04: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 04:24:49 +04:00
*/
2011-11-15 08:31:13 +04:00
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_LVDS )
nouveau_connector_detect_depth ( connector ) ;
2011-10-17 04:24:49 +04:00
2012-07-11 04:44:20 +04:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_TV )
2010-07-20 18:48:08 +04:00
ret = get_slave_funcs ( encoder ) - > get_modes ( encoder , connector ) ;
2009-12-11 12:24:15 +03:00
2011-11-18 04:23:59 +04:00
if ( nv_connector - > type = = DCB_CONNECTOR_LVDS | |
nv_connector - > type = = DCB_CONNECTOR_LVDS_SPWG | |
nv_connector - > type = = DCB_CONNECTOR_eDP )
2009-12-11 12:24:15 +03:00
ret + = nouveau_connector_scaler_modes_add ( connector ) ;
return ret ;
}
2010-10-02 18:00:35 +04:00
static unsigned
get_tmds_link_bandwidth ( struct drm_connector * connector )
{
struct nouveau_connector * nv_connector = nouveau_connector ( connector ) ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( connector - > dev ) ;
2012-07-11 04:44:20 +04:00
struct dcb_output * dcb = nv_connector - > detected_encoder - > dcb ;
2010-10-02 18:00:35 +04:00
if ( dcb - > location ! = DCB_LOC_ON_CHIP | |
2012-07-31 10:16:21 +04:00
nv_device ( drm - > device ) - > chipset > = 0x46 )
2010-10-02 18:00:35 +04:00
return 165000 ;
2012-07-31 10:16:21 +04:00
else if ( nv_device ( drm - > device ) - > chipset > = 0x40 )
2010-10-02 18:00:35 +04:00
return 155000 ;
2012-07-31 10:16:21 +04:00
else if ( nv_device ( drm - > device ) - > chipset > = 0x18 )
2010-10-02 18:00:35 +04:00
return 135000 ;
else
return 112000 ;
}
2009-12-11 12:24:15 +03: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 18:48:08 +04:00
struct drm_encoder * encoder = to_drm_encoder ( nv_encoder ) ;
2009-12-11 12:24:15 +03:00
unsigned min_clock = 25000 , max_clock = min_clock ;
unsigned clock = mode - > clock ;
switch ( nv_encoder - > dcb - > type ) {
2012-07-11 04:44:20 +04:00
case DCB_OUTPUT_LVDS :
2010-05-28 04:13:16 +04:00
if ( nv_connector - > native_mode & &
( mode - > hdisplay > nv_connector - > native_mode - > hdisplay | |
mode - > vdisplay > nv_connector - > native_mode - > vdisplay ) )
2009-12-11 12:24:15 +03:00
return MODE_PANEL ;
min_clock = 0 ;
max_clock = 400000 ;
break ;
2012-07-11 04:44:20 +04:00
case DCB_OUTPUT_TMDS :
2010-10-02 18:00:35 +04:00
max_clock = get_tmds_link_bandwidth ( connector ) ;
if ( nouveau_duallink & & nv_encoder - > dcb - > duallink_possible )
max_clock * = 2 ;
2009-12-11 12:24:15 +03:00
break ;
2012-07-11 04:44:20 +04:00
case DCB_OUTPUT_ANALOG :
2009-12-11 12:24:15 +03:00
max_clock = nv_encoder - > dcb - > crtconf . maxfreq ;
if ( ! max_clock )
max_clock = 350000 ;
break ;
2012-07-11 04:44:20 +04:00
case DCB_OUTPUT_TV :
2010-07-20 18:48:08 +04:00
return get_slave_funcs ( encoder ) - > mode_valid ( encoder , mode ) ;
2012-07-11 04:44:20 +04:00
case DCB_OUTPUT_DP :
2011-08-04 03:55:44 +04:00
max_clock = nv_encoder - > dp . link_nr ;
max_clock * = nv_encoder - > dp . link_bw ;
2011-11-15 08:31:13 +04:00
clock = clock * ( connector - > display_info . bpc * 3 ) / 10 ;
2009-12-11 12:24:15 +03:00
break ;
2010-02-24 03:31:39 +03:00
default :
BUG_ON ( 1 ) ;
return MODE_BAD ;
2009-12-11 12:24:15 +03: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 ,
. save = NULL ,
. restore = NULL ,
. 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 07:32:42 +04:00
static const struct drm_connector_funcs
nouveau_connector_funcs_lvds = {
. dpms = drm_helper_connector_dpms ,
. save = NULL ,
. restore = NULL ,
. 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 12:24:15 +03:00
2013-02-03 06:56:16 +04:00
static void
nouveau_connector_hotplug_work ( struct work_struct * work )
{
struct nouveau_connector * nv_connector =
container_of ( work , struct nouveau_connector , hpd_work ) ;
struct drm_connector * connector = & nv_connector - > base ;
struct drm_device * dev = connector - > dev ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_gpio * gpio = nouveau_gpio ( drm - > device ) ;
bool plugged = gpio - > get ( gpio , 0 , nv_connector - > hpd . func , 0xff ) ;
NV_DEBUG ( drm , " %splugged %s \n " , plugged ? " " : " un " ,
drm_get_connector_name ( connector ) ) ;
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 ( dev ) ;
}
static int
nouveau_connector_hotplug ( struct nouveau_eventh * event , int index )
{
struct nouveau_connector * nv_connector =
container_of ( event , struct nouveau_connector , hpd_func ) ;
schedule_work ( & nv_connector - > hpd_work ) ;
return NVKM_EVENT_KEEP ;
}
2011-11-18 04:23:59 +04: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 05:27:15 +04:00
case DCB_CONNECTOR_DMS59_0 :
case DCB_CONNECTOR_DMS59_1 :
2011-11-18 04:23:59 +04: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 10:17:16 +04:00
case DCB_CONNECTOR_DMS59_DP0 :
case DCB_CONNECTOR_DMS59_DP1 :
2011-11-18 04:23:59 +04:00
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort ;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP ;
case DCB_CONNECTOR_HDMI_0 :
case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA ;
default :
break ;
}
return DRM_MODE_CONNECTOR_Unknown ;
}
2010-06-28 08:35:50 +04:00
struct drm_connector *
nouveau_connector_create ( struct drm_device * dev , int index )
2009-12-11 12:24:15 +03:00
{
2010-06-01 07:32:42 +04:00
const struct drm_connector_funcs * funcs = & nouveau_connector_funcs ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_gpio * gpio = nouveau_gpio ( drm - > device ) ;
struct nouveau_display * disp = nouveau_display ( dev ) ;
2009-12-11 12:24:15 +03:00
struct nouveau_connector * nv_connector = NULL ;
struct drm_connector * connector ;
2010-06-16 17:52:44 +04:00
int type , ret = 0 ;
2011-11-18 04:23:59 +04:00
bool dummy ;
2009-12-11 12:24:15 +03:00
2011-11-18 04:23:59 +04: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 12:24:15 +03:00
}
2010-02-24 05:07:31 +03:00
nv_connector = kzalloc ( sizeof ( * nv_connector ) , GFP_KERNEL ) ;
if ( ! nv_connector )
2010-06-28 08:35:50 +04:00
return ERR_PTR ( - ENOMEM ) ;
2011-11-18 04:23:59 +04:00
2010-02-24 05:07:31 +03:00
connector = & nv_connector - > base ;
2013-02-03 06:56:16 +04:00
INIT_WORK ( & nv_connector - > hpd_work , nouveau_connector_hotplug_work ) ;
2011-11-18 04:23:59 +04:00
nv_connector - > index = index ;
/* attempt to parse vbios connector type and hotplug gpio */
2012-07-11 04:44:20 +04:00
nv_connector - > dcb = olddcb_conn ( dev , index ) ;
2011-11-18 04:23:59 +04:00
if ( nv_connector - > dcb ) {
static const u8 hpd [ 16 ] = {
0xff , 0x07 , 0x08 , 0xff , 0xff , 0x51 , 0x52 , 0xff ,
0xff , 0xff , 0xff , 0xff , 0xff , 0x5e , 0x5f , 0x60 ,
} ;
u32 entry = ROM16 ( nv_connector - > dcb [ 0 ] ) ;
2012-07-11 04:44:20 +04:00
if ( olddcb_conntab ( dev ) [ 3 ] > = 4 )
2011-11-18 04:23:59 +04:00
entry | = ( u32 ) ROM16 ( nv_connector - > dcb [ 2 ] ) < < 16 ;
2013-02-03 14:29:53 +04:00
ret = gpio - > find ( gpio , 0 , hpd [ ffs ( ( entry & 0x07033000 ) > > 12 ) ] ,
DCB_GPIO_UNUSED , & nv_connector - > hpd ) ;
2013-02-03 06:56:16 +04:00
nv_connector - > hpd_func . func = nouveau_connector_hotplug ;
2013-02-03 14:29:53 +04:00
if ( ret )
nv_connector - > hpd . func = DCB_GPIO_UNUSED ;
2011-11-18 04:23:59 +04:00
nv_connector - > type = nv_connector - > dcb [ 0 ] ;
if ( drm_conntype_from_dcb ( nv_connector - > type ) = =
DRM_MODE_CONNECTOR_Unknown ) {
2012-07-31 10:16:21 +04:00
NV_WARN ( drm , " unknown connector type %02x \n " ,
2011-11-18 04:23:59 +04:00
nv_connector - > type ) ;
nv_connector - > type = DCB_CONNECTOR_NONE ;
}
2010-02-24 05:07:31 +03:00
2011-11-18 04:23:59 +04: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 12:24:15 +03:00
2011-11-18 04:23:59 +04: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 ;
2013-02-03 14:29:53 +04:00
nv_connector - > hpd . func = DCB_GPIO_UNUSED ;
2011-11-18 04:23:59 +04:00
}
/* 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 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct dcb_table * dcbt = & drm - > vbios . dcb ;
2011-11-18 04:23:59 +04: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 12:24:15 +03:00
2012-07-11 04:44:20 +04:00
if ( encoders & ( 1 < < DCB_OUTPUT_DP ) ) {
if ( encoders & ( 1 < < DCB_OUTPUT_TMDS ) )
2011-11-18 04:23:59 +04:00
nv_connector - > type = DCB_CONNECTOR_DP ;
else
nv_connector - > type = DCB_CONNECTOR_eDP ;
} else
2012-07-11 04:44:20 +04:00
if ( encoders & ( 1 < < DCB_OUTPUT_TMDS ) ) {
if ( encoders & ( 1 < < DCB_OUTPUT_ANALOG ) )
2011-11-18 04:23:59 +04:00
nv_connector - > type = DCB_CONNECTOR_DVI_I ;
else
nv_connector - > type = DCB_CONNECTOR_DVI_D ;
} else
2012-07-11 04:44:20 +04:00
if ( encoders & ( 1 < < DCB_OUTPUT_ANALOG ) ) {
2011-11-18 04:23:59 +04:00
nv_connector - > type = DCB_CONNECTOR_VGA ;
} else
2012-07-11 04:44:20 +04:00
if ( encoders & ( 1 < < DCB_OUTPUT_LVDS ) ) {
2011-11-18 04:23:59 +04:00
nv_connector - > type = DCB_CONNECTOR_LVDS ;
} else
2012-07-11 04:44:20 +04:00
if ( encoders & ( 1 < < DCB_OUTPUT_TV ) ) {
2011-11-18 04:23:59 +04:00
nv_connector - > type = DCB_CONNECTOR_TV_0 ;
}
}
2010-06-16 17:52:44 +04:00
2011-11-18 04:23:59 +04:00
type = drm_conntype_from_dcb ( nv_connector - > type ) ;
if ( type = = DRM_MODE_CONNECTOR_LVDS ) {
ret = nouveau_bios_parse_lvds_table ( dev , 0 , & dummy , & dummy ) ;
2010-06-16 17:52:44 +04:00
if ( ret ) {
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " Error parsing LVDS table, disabling \n " ) ;
2011-11-18 04:23:59 +04:00
kfree ( nv_connector ) ;
return ERR_PTR ( ret ) ;
2010-06-16 17:52:44 +04:00
}
2011-11-18 04:23:59 +04:00
funcs = & nouveau_connector_funcs_lvds ;
} else {
funcs = & nouveau_connector_funcs ;
2010-02-24 05:07:31 +03:00
}
2011-11-18 04:23:59 +04: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 12:24:15 +03:00
/* Init DVI-I specific properties */
2011-11-18 04:23:59 +04:00
if ( nv_connector - > type = = DCB_CONNECTOR_DVI_I )
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base , dev - > mode_config . dvi_i_subconnector_property , 0 ) ;
2009-12-11 12:24:15 +03:00
2011-10-06 07:29:05 +04:00
/* Add overscan compensation options to digital outputs */
2011-10-17 06:23:41 +04:00
if ( disp - > underscan_property & &
2012-03-22 05:27:15 +04:00
( type = = DRM_MODE_CONNECTOR_DVID | |
type = = DRM_MODE_CONNECTOR_DVII | |
type = = DRM_MODE_CONNECTOR_HDMIA | |
type = = DRM_MODE_CONNECTOR_DisplayPort ) ) {
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 07:29:05 +04:00
disp - > underscan_property ,
UNDERSCAN_OFF ) ;
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 07:29:05 +04:00
disp - > underscan_hborder_property ,
0 ) ;
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2011-10-06 07:29:05 +04:00
disp - > underscan_vborder_property ,
0 ) ;
}
2012-01-22 02:13:26 +04:00
/* Add hue and saturation options */
if ( disp - > vibrant_hue_property )
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2012-01-22 02:13:26 +04:00
disp - > vibrant_hue_property ,
90 ) ;
if ( disp - > color_vibrance_property )
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2012-01-22 02:13:26 +04:00
disp - > color_vibrance_property ,
150 ) ;
2011-11-18 04:23:59 +04:00
switch ( nv_connector - > type ) {
2010-02-24 07:01:40 +03:00
case DCB_CONNECTOR_VGA :
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type > = NV_50 ) {
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2009-12-11 12:24:15 +03:00
dev - > mode_config . scaling_mode_property ,
nv_connector - > scaling_mode ) ;
}
2010-02-24 07:01:40 +03:00
/* fall-through */
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 :
nv_connector - > scaling_mode = DRM_MODE_SCALE_NONE ;
break ;
default :
nv_connector - > scaling_mode = DRM_MODE_SCALE_FULLSCREEN ;
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2010-02-24 07:01:40 +03:00
dev - > mode_config . scaling_mode_property ,
nv_connector - > scaling_mode ) ;
2011-10-17 06:23:41 +04:00
if ( disp - > dithering_mode ) {
nv_connector - > dithering_mode = DITHERING_MODE_AUTO ;
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2011-10-17 06:23:41 +04:00
disp - > dithering_mode ,
nv_connector - > dithering_mode ) ;
}
if ( disp - > dithering_depth ) {
nv_connector - > dithering_depth = DITHERING_DEPTH_AUTO ;
2012-10-12 05:42:04 +04:00
drm_object_attach_property ( & connector - > base ,
2011-10-17 06:23:41 +04:00
disp - > dithering_depth ,
nv_connector - > dithering_depth ) ;
}
2010-02-24 07:01:40 +03:00
break ;
2009-12-11 12:24:15 +03:00
}
2011-11-21 10:41:48 +04:00
connector - > polled = DRM_CONNECTOR_POLL_CONNECT ;
2013-02-03 06:56:16 +04:00
if ( nv_connector - > hpd . func ! = DCB_GPIO_UNUSED )
connector - > polled = DRM_CONNECTOR_POLL_HPD ;
2010-11-11 09:14:56 +03:00
2009-12-11 12:24:15 +03:00
drm_sysfs_connector_add ( connector ) ;
2011-11-18 04:23:59 +04:00
return connector ;
2009-12-11 12:24:15 +03:00
}