2019-05-29 17:12:36 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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 >
*/
2012-10-02 21:01:07 +04:00
# include <drm/drm_crtc_helper.h>
2019-01-18 00:03:34 +03:00
# include <drm/drm_probe_helper.h>
2019-07-16 09:42:09 +03:00
2017-10-13 03:13:49 +03:00
# include "udl_connector.h"
2010-12-15 00:14:24 +03:00
# include "udl_drv.h"
2019-03-15 01:53:39 +03:00
static int udl_get_edid_block ( void * data , u8 * buf , unsigned int block ,
size_t len )
2010-12-15 00:14:24 +03:00
{
int ret , i ;
2017-10-13 03:13:50 +03:00
u8 * read_buff ;
2019-03-15 01:53:39 +03:00
struct udl_device * udl = data ;
2010-12-15 00:14:24 +03:00
2017-10-13 03:13:50 +03:00
read_buff = kmalloc ( 2 , GFP_KERNEL ) ;
if ( ! read_buff )
2019-03-15 01:53:39 +03:00
return - 1 ;
2013-01-11 15:08:57 +04:00
2019-03-15 01:53:39 +03:00
for ( i = 0 ; i < len ; i + + ) {
int bval = ( i + block * EDID_LENGTH ) < < 8 ;
2014-08-29 14:12:45 +04:00
ret = usb_control_msg ( udl - > udev ,
2017-10-13 03:13:50 +03:00
usb_rcvctrlpipe ( udl - > udev , 0 ) ,
( 0x02 ) , ( 0x80 | ( 0x02 < < 5 ) ) , bval ,
0xA1 , read_buff , 2 , HZ ) ;
2010-12-15 00:14:24 +03:00
if ( ret < 1 ) {
DRM_ERROR ( " Read EDID byte %d failed err %x \n " , i , ret ) ;
2017-10-13 03:13:50 +03:00
kfree ( read_buff ) ;
2019-03-15 01:53:39 +03:00
return - 1 ;
2010-12-15 00:14:24 +03:00
}
2019-03-15 01:53:39 +03:00
buf [ i ] = read_buff [ 1 ] ;
2010-12-15 00:14:24 +03:00
}
2017-10-13 03:13:50 +03:00
kfree ( read_buff ) ;
2019-03-15 01:53:39 +03:00
return 0 ;
2010-12-15 00:14:24 +03:00
}
static int udl_get_modes ( struct drm_connector * connector )
{
2017-10-13 03:13:49 +03:00
struct udl_drm_connector * udl_connector =
container_of ( connector ,
struct udl_drm_connector ,
connector ) ;
2018-07-09 11:40:06 +03:00
drm_connector_update_edid_property ( connector , udl_connector - > edid ) ;
2017-10-13 03:13:49 +03:00
if ( udl_connector - > edid )
return drm_add_edid_modes ( connector , udl_connector - > edid ) ;
return 0 ;
2010-12-15 00:14:24 +03:00
}
2018-04-24 16:15:17 +03:00
static enum drm_mode_status udl_mode_valid ( struct drm_connector * connector ,
2010-12-15 00:14:24 +03:00
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-10-13 03:13:49 +03: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-13 03:13:50 +03:00
/* cleanup previous edid */
2017-10-13 03:13:49 +03:00
if ( udl_connector - > edid ! = NULL ) {
kfree ( udl_connector - > edid ) ;
udl_connector - > edid = NULL ;
}
2019-03-15 01:53:39 +03:00
udl_connector - > edid = drm_do_get_edid ( connector , udl_get_edid_block , udl ) ;
if ( ! udl_connector - > edid )
2010-12-15 00:14:24 +03:00
return connector_status_disconnected ;
2017-10-13 03:13:49 +03:00
2010-12-15 00:14:24 +03:00
return connector_status_connected ;
}
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 )
{
2017-10-13 03:13:49 +03:00
struct udl_drm_connector * udl_connector =
container_of ( connector ,
struct udl_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 ) ;
2017-10-13 03:13:49 +03:00
kfree ( udl_connector - > edid ) ;
2010-12-15 00:14:24 +03:00
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 ,
} ;
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 )
{
2017-10-13 03:13:49 +03:00
struct udl_drm_connector * udl_connector ;
2010-12-15 00:14:24 +03:00
struct drm_connector * connector ;
2017-10-13 03:13:49 +03:00
udl_connector = kzalloc ( sizeof ( struct udl_drm_connector ) , GFP_KERNEL ) ;
if ( ! udl_connector )
2010-12-15 00:14:24 +03:00
return - ENOMEM ;
2017-10-13 03:13:49 +03:00
connector = & udl_connector - > connector ;
drm_connector_init ( dev , connector , & udl_connector_funcs ,
DRM_MODE_CONNECTOR_DVII ) ;
2010-12-15 00:14:24 +03:00
drm_connector_helper_add ( connector , & udl_connector_helper_funcs ) ;
2014-05-29 19:57:41 +04:00
drm_connector_register ( connector ) ;
2018-07-09 11:40:07 +03:00
drm_connector_attach_encoder ( connector , encoder ) ;
2017-10-13 03:13:49 +03:00
connector - > polled = DRM_CONNECTOR_POLL_HPD |
DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT ;
2010-12-15 00:14:24 +03:00
return 0 ;
}