2010-12-15 07:14:24 +10: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 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_edid.h>
# include <drm/drm_crtc_helper.h>
2017-10-12 17:13:49 -07:00
# include "udl_connector.h"
2010-12-15 07:14:24 +10:00
# include "udl_drv.h"
2017-10-12 17:13:50 -07:00
static bool udl_get_edid_block ( struct udl_device * udl , int block_idx ,
u8 * buff )
2010-12-15 07:14:24 +10:00
{
int ret , i ;
2017-10-12 17:13:50 -07:00
u8 * read_buff ;
2010-12-15 07:14:24 +10:00
2017-10-12 17:13:50 -07:00
read_buff = kmalloc ( 2 , GFP_KERNEL ) ;
if ( ! read_buff )
return false ;
2013-01-11 12:08:57 +01:00
2010-12-15 07:14:24 +10:00
for ( i = 0 ; i < EDID_LENGTH ; i + + ) {
2017-10-12 17:13:50 -07:00
int bval = ( i + block_idx * EDID_LENGTH ) < < 8 ;
2014-08-29 12:12:45 +02:00
ret = usb_control_msg ( udl - > udev ,
2017-10-12 17:13:50 -07:00
usb_rcvctrlpipe ( udl - > udev , 0 ) ,
( 0x02 ) , ( 0x80 | ( 0x02 < < 5 ) ) , bval ,
0xA1 , read_buff , 2 , HZ ) ;
2010-12-15 07:14:24 +10:00
if ( ret < 1 ) {
DRM_ERROR ( " Read EDID byte %d failed err %x \n " , i , ret ) ;
2017-10-12 17:13:50 -07:00
kfree ( read_buff ) ;
return false ;
2010-12-15 07:14:24 +10:00
}
2017-10-12 17:13:50 -07:00
buff [ i ] = read_buff [ 1 ] ;
2010-12-15 07:14:24 +10:00
}
2017-10-12 17:13:50 -07:00
kfree ( read_buff ) ;
return true ;
2010-12-15 07:14:24 +10:00
}
2017-10-12 17:13:50 -07:00
static bool udl_get_edid ( struct udl_device * udl , u8 * * result_buff ,
int * result_buff_size )
2010-12-15 07:14:24 +10:00
{
2017-10-12 17:13:50 -07:00
int i , extensions ;
u8 * block_buff = NULL , * buff_ptr ;
block_buff = kmalloc ( EDID_LENGTH , GFP_KERNEL ) ;
if ( block_buff = = NULL )
return false ;
if ( udl_get_edid_block ( udl , 0 , block_buff ) & &
memchr_inv ( block_buff , 0 , EDID_LENGTH ) ) {
extensions = ( ( struct edid * ) block_buff ) - > extensions ;
if ( extensions > 0 ) {
/* we have to read all extensions one by one */
* result_buff_size = EDID_LENGTH * ( extensions + 1 ) ;
* result_buff = kmalloc ( * result_buff_size , GFP_KERNEL ) ;
buff_ptr = * result_buff ;
if ( buff_ptr = = NULL ) {
kfree ( block_buff ) ;
return false ;
}
memcpy ( buff_ptr , block_buff , EDID_LENGTH ) ;
kfree ( block_buff ) ;
buff_ptr + = EDID_LENGTH ;
for ( i = 1 ; i < extensions ; + + i ) {
if ( udl_get_edid_block ( udl , i , buff_ptr ) ) {
buff_ptr + = EDID_LENGTH ;
} else {
kfree ( * result_buff ) ;
* result_buff = NULL ;
return false ;
}
}
return true ;
}
/* we have only base edid block */
* result_buff = block_buff ;
* result_buff_size = EDID_LENGTH ;
return true ;
2013-04-12 13:25:20 +10:00
}
2010-12-15 07:14:24 +10:00
2017-10-12 17:13:50 -07:00
kfree ( block_buff ) ;
return false ;
2010-12-15 07:14:24 +10:00
}
static int udl_get_modes ( struct drm_connector * connector )
{
2017-10-12 17:13:49 -07:00
struct udl_drm_connector * udl_connector =
container_of ( connector ,
struct udl_drm_connector ,
connector ) ;
drm_mode_connector_update_edid_property ( connector , udl_connector - > edid ) ;
if ( udl_connector - > edid )
return drm_add_edid_modes ( connector , udl_connector - > edid ) ;
return 0 ;
2010-12-15 07:14:24 +10:00
}
static int udl_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
2012-09-25 16:17:43 +10: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 07:14:24 +10:00
return 0 ;
}
static enum drm_connector_status
udl_detect ( struct drm_connector * connector , bool force )
{
2017-10-12 17:13:50 -07:00
u8 * edid_buff = NULL ;
int edid_buff_size = 0 ;
2017-10-12 17:13:49 -07:00
struct udl_device * udl = connector - > dev - > dev_private ;
struct udl_drm_connector * udl_connector =
container_of ( connector ,
struct udl_drm_connector ,
connector ) ;
2017-10-12 17:13:50 -07:00
/* cleanup previous edid */
2017-10-12 17:13:49 -07:00
if ( udl_connector - > edid ! = NULL ) {
kfree ( udl_connector - > edid ) ;
udl_connector - > edid = NULL ;
}
2017-10-12 17:13:50 -07:00
if ( ! udl_get_edid ( udl , & edid_buff , & edid_buff_size ) )
2010-12-15 07:14:24 +10:00
return connector_status_disconnected ;
2017-10-12 17:13:49 -07:00
2017-10-12 17:13:50 -07:00
udl_connector - > edid = ( struct edid * ) edid_buff ;
2010-12-15 07:14:24 +10:00
return connector_status_connected ;
}
2012-11-19 05:27:43 +00:00
static struct drm_encoder *
udl_best_single_encoder ( struct drm_connector * connector )
2010-12-15 07:14:24 +10:00
{
int enc_id = connector - > encoder_ids [ 0 ] ;
2017-03-14 23:25:07 -07:00
return drm_encoder_find ( connector - > dev , NULL , enc_id ) ;
2010-12-15 07:14:24 +10:00
}
2012-11-19 05:27:43 +00:00
static int udl_connector_set_property ( struct drm_connector * connector ,
struct drm_property * property ,
uint64_t val )
2010-12-15 07:14:24 +10:00
{
return 0 ;
}
static void udl_connector_destroy ( struct drm_connector * connector )
{
2017-10-12 17:13:49 -07:00
struct udl_drm_connector * udl_connector =
container_of ( connector ,
struct udl_drm_connector ,
connector ) ;
2014-05-29 16:57:41 +01:00
drm_connector_unregister ( connector ) ;
2010-12-15 07:14:24 +10:00
drm_connector_cleanup ( connector ) ;
2017-10-12 17:13:49 -07:00
kfree ( udl_connector - > edid ) ;
2010-12-15 07:14:24 +10:00
kfree ( connector ) ;
}
2015-12-15 12:21:14 +01:00
static const struct drm_connector_helper_funcs udl_connector_helper_funcs = {
2010-12-15 07:14:24 +10:00
. get_modes = udl_get_modes ,
. mode_valid = udl_mode_valid ,
. best_encoder = udl_best_single_encoder ,
} ;
2015-12-15 12:21:14 +01:00
static const struct drm_connector_funcs udl_connector_funcs = {
2010-12-15 07:14:24 +10: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 )
{
2017-10-12 17:13:49 -07:00
struct udl_drm_connector * udl_connector ;
2010-12-15 07:14:24 +10:00
struct drm_connector * connector ;
2017-10-12 17:13:49 -07:00
udl_connector = kzalloc ( sizeof ( struct udl_drm_connector ) , GFP_KERNEL ) ;
if ( ! udl_connector )
2010-12-15 07:14:24 +10:00
return - ENOMEM ;
2017-10-12 17:13:49 -07:00
connector = & udl_connector - > connector ;
drm_connector_init ( dev , connector , & udl_connector_funcs ,
DRM_MODE_CONNECTOR_DVII ) ;
2010-12-15 07:14:24 +10:00
drm_connector_helper_add ( connector , & udl_connector_helper_funcs ) ;
2014-05-29 16:57:41 +01:00
drm_connector_register ( connector ) ;
2010-12-15 07:14:24 +10:00
drm_mode_connector_attach_encoder ( connector , encoder ) ;
2017-10-12 17:13:49 -07:00
connector - > polled = DRM_CONNECTOR_POLL_HPD |
DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT ;
2010-12-15 07:14:24 +10:00
return 0 ;
}