2010-12-15 00:14:24 +03:00
/*
* Copyright ( C ) 2012 Red Hat
* based in parts on udlfb . c :
* Copyright ( C ) 2009 Roberto De Ioris < roberto @ unbit . it >
* Copyright ( C ) 2009 Jaya Kumar < jayakumar . lkml @ gmail . com >
* Copyright ( C ) 2009 Bernie Thompson < bernie @ plugable . com >
*
* This file is subject to the terms and conditions of the GNU General Public
* License v2 . See the file COPYING in the main directory of this archive for
* more details .
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_edid.h>
# include <drm/drm_crtc_helper.h>
2010-12-15 00:14:24 +03:00
# include "udl_drv.h"
/* dummy connector to just get EDID,
all UDL appear to have a DVI - D */
static u8 * udl_get_edid ( struct udl_device * udl )
{
u8 * block ;
2013-01-11 15:08:57 +04:00
char * rbuf ;
2010-12-15 00:14:24 +03:00
int ret , i ;
block = kmalloc ( EDID_LENGTH , GFP_KERNEL ) ;
if ( block = = NULL )
return NULL ;
2013-01-11 15:08:57 +04:00
rbuf = kmalloc ( 2 , GFP_KERNEL ) ;
if ( rbuf = = NULL )
goto error ;
2010-12-15 00:14:24 +03:00
for ( i = 0 ; i < EDID_LENGTH ; i + + ) {
2014-08-29 14:12:45 +04:00
ret = usb_control_msg ( udl - > udev ,
usb_rcvctrlpipe ( udl - > udev , 0 ) , ( 0x02 ) ,
2010-12-15 00:14:24 +03:00
( 0x80 | ( 0x02 < < 5 ) ) , i < < 8 , 0xA1 , rbuf , 2 ,
HZ ) ;
if ( ret < 1 ) {
DRM_ERROR ( " Read EDID byte %d failed err %x \n " , i , ret ) ;
goto error ;
}
block [ i ] = rbuf [ 1 ] ;
}
2013-01-11 15:08:57 +04:00
kfree ( rbuf ) ;
2010-12-15 00:14:24 +03:00
return block ;
error :
kfree ( block ) ;
2013-01-11 15:08:57 +04:00
kfree ( rbuf ) ;
2010-12-15 00:14:24 +03:00
return NULL ;
}
static int udl_get_modes ( struct drm_connector * connector )
{
struct udl_device * udl = connector - > dev - > dev_private ;
struct edid * edid ;
int ret ;
edid = ( struct edid * ) udl_get_edid ( udl ) ;
2013-04-12 07:25:20 +04:00
if ( ! edid ) {
drm_mode_connector_update_edid_property ( connector , NULL ) ;
return 0 ;
}
2010-12-15 00:14:24 +03:00
2013-01-11 15:08:56 +04:00
/*
* We only read the main block , but if the monitor reports extension
* blocks then the drm edid code expects them to be present , so patch
* the extension count to 0.
*/
edid - > checksum + = edid - > extensions ;
edid - > extensions = 0 ;
2010-12-15 00:14:24 +03:00
drm_mode_connector_update_edid_property ( connector , edid ) ;
ret = drm_add_edid_modes ( connector , edid ) ;
kfree ( edid ) ;
return ret ;
}
static int udl_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
2012-09-25 10:17:43 +04:00
struct udl_device * udl = connector - > dev - > dev_private ;
if ( ! udl - > sku_pixel_limit )
return 0 ;
if ( mode - > vdisplay * mode - > hdisplay > udl - > sku_pixel_limit )
return MODE_VIRTUAL_Y ;
2010-12-15 00:14:24 +03:00
return 0 ;
}
static enum drm_connector_status
udl_detect ( struct drm_connector * connector , bool force )
{
2017-08-02 14:56:02 +03:00
if ( drm_dev_is_unplugged ( connector - > dev ) )
2010-12-15 00:14:24 +03:00
return connector_status_disconnected ;
return connector_status_connected ;
}
2012-11-19 09:27:43 +04:00
static struct drm_encoder *
udl_best_single_encoder ( struct drm_connector * connector )
2010-12-15 00:14:24 +03:00
{
int enc_id = connector - > encoder_ids [ 0 ] ;
2014-07-18 07:30:01 +04:00
return drm_encoder_find ( connector - > dev , enc_id ) ;
2010-12-15 00:14:24 +03:00
}
2012-11-19 09:27:43 +04:00
static int udl_connector_set_property ( struct drm_connector * connector ,
struct drm_property * property ,
uint64_t val )
2010-12-15 00:14:24 +03:00
{
return 0 ;
}
static void udl_connector_destroy ( struct drm_connector * connector )
{
2014-05-29 19:57:41 +04:00
drm_connector_unregister ( connector ) ;
2010-12-15 00:14:24 +03:00
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
2015-12-15 14:21:14 +03:00
static const struct drm_connector_helper_funcs udl_connector_helper_funcs = {
2010-12-15 00:14:24 +03:00
. get_modes = udl_get_modes ,
. mode_valid = udl_mode_valid ,
. best_encoder = udl_best_single_encoder ,
} ;
2015-12-15 14:21:14 +03:00
static const struct drm_connector_funcs udl_connector_funcs = {
2010-12-15 00:14:24 +03:00
. dpms = drm_helper_connector_dpms ,
. detect = udl_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = udl_connector_destroy ,
. set_property = udl_connector_set_property ,
} ;
int udl_connector_init ( struct drm_device * dev , struct drm_encoder * encoder )
{
struct drm_connector * connector ;
connector = kzalloc ( sizeof ( struct drm_connector ) , GFP_KERNEL ) ;
if ( ! connector )
return - ENOMEM ;
drm_connector_init ( dev , connector , & udl_connector_funcs , DRM_MODE_CONNECTOR_DVII ) ;
drm_connector_helper_add ( connector , & udl_connector_helper_funcs ) ;
2014-05-29 19:57:41 +04:00
drm_connector_register ( connector ) ;
2010-12-15 00:14:24 +03:00
drm_mode_connector_attach_encoder ( connector , encoder ) ;
return 0 ;
}