2013-01-22 16:02:21 -06:00
/*
* Copyright ( C ) 2012 Texas Instruments
* Author : Rob Clark < robdclark @ gmail . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/i2c.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/pinctrl/consumer.h>
# include <drm/drm_encoder_slave.h>
# include "tilcdc_drv.h"
struct slave_module {
struct tilcdc_module base ;
struct i2c_adapter * i2c ;
} ;
# define to_slave_module(x) container_of(x, struct slave_module, base)
static const struct tilcdc_panel_info slave_info = {
. bpp = 16 ,
. ac_bias = 255 ,
. ac_bias_intrpt = 0 ,
. dma_burst_sz = 16 ,
. fdd = 0x80 ,
. tft_alt_mode = 0 ,
. sync_edge = 0 ,
. sync_ctrl = 1 ,
. raster_order = 0 ,
} ;
/*
* Encoder :
*/
struct slave_encoder {
struct drm_encoder_slave base ;
struct slave_module * mod ;
} ;
# define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base)
static inline struct drm_encoder_slave_funcs *
get_slave_funcs ( struct drm_encoder * enc )
{
return to_encoder_slave ( enc ) - > slave_funcs ;
}
static void slave_encoder_destroy ( struct drm_encoder * encoder )
{
struct slave_encoder * slave_encoder = to_slave_encoder ( encoder ) ;
if ( get_slave_funcs ( encoder ) )
get_slave_funcs ( encoder ) - > destroy ( encoder ) ;
drm_encoder_cleanup ( encoder ) ;
kfree ( slave_encoder ) ;
}
static void slave_encoder_prepare ( struct drm_encoder * encoder )
{
drm_i2c_encoder_prepare ( encoder ) ;
tilcdc_crtc_set_panel_info ( encoder - > crtc , & slave_info ) ;
}
2013-08-14 21:43:33 +02:00
static bool slave_encoder_fixup ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
/*
* tilcdc does not generate VESA - complient sync but aligns
* VS on the second edge of HS instead of first edge .
* We use adjusted_mode , to fixup sync by aligning both rising
* edges and add HSKEW offset to let the slave encoder fix it up .
*/
adjusted_mode - > hskew = mode - > hsync_end - mode - > hsync_start ;
adjusted_mode - > flags | = DRM_MODE_FLAG_HSKEW ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC ) {
adjusted_mode - > flags | = DRM_MODE_FLAG_PHSYNC ;
adjusted_mode - > flags & = ~ DRM_MODE_FLAG_NHSYNC ;
} else {
adjusted_mode - > flags | = DRM_MODE_FLAG_NHSYNC ;
adjusted_mode - > flags & = ~ DRM_MODE_FLAG_PHSYNC ;
}
return drm_i2c_encoder_mode_fixup ( encoder , mode , adjusted_mode ) ;
}
2013-01-22 16:02:21 -06:00
static const struct drm_encoder_funcs slave_encoder_funcs = {
. destroy = slave_encoder_destroy ,
} ;
static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = {
. dpms = drm_i2c_encoder_dpms ,
2013-08-14 21:43:33 +02:00
. mode_fixup = slave_encoder_fixup ,
2013-01-22 16:02:21 -06:00
. prepare = slave_encoder_prepare ,
. commit = drm_i2c_encoder_commit ,
. mode_set = drm_i2c_encoder_mode_set ,
. save = drm_i2c_encoder_save ,
. restore = drm_i2c_encoder_restore ,
} ;
static const struct i2c_board_info info = {
I2C_BOARD_INFO ( " tda998x " , 0x70 )
} ;
static struct drm_encoder * slave_encoder_create ( struct drm_device * dev ,
struct slave_module * mod )
{
struct slave_encoder * slave_encoder ;
struct drm_encoder * encoder ;
int ret ;
slave_encoder = kzalloc ( sizeof ( * slave_encoder ) , GFP_KERNEL ) ;
if ( ! slave_encoder ) {
dev_err ( dev - > dev , " allocation failed \n " ) ;
return NULL ;
}
slave_encoder - > mod = mod ;
encoder = & slave_encoder - > base . base ;
encoder - > possible_crtcs = 1 ;
ret = drm_encoder_init ( dev , encoder , & slave_encoder_funcs ,
DRM_MODE_ENCODER_TMDS ) ;
if ( ret )
goto fail ;
drm_encoder_helper_add ( encoder , & slave_encoder_helper_funcs ) ;
ret = drm_i2c_encoder_init ( dev , to_encoder_slave ( encoder ) , mod - > i2c , & info ) ;
if ( ret )
goto fail ;
return encoder ;
fail :
slave_encoder_destroy ( encoder ) ;
return NULL ;
}
/*
* Connector :
*/
struct slave_connector {
struct drm_connector base ;
struct drm_encoder * encoder ; /* our connected encoder */
struct slave_module * mod ;
} ;
# define to_slave_connector(x) container_of(x, struct slave_connector, base)
static void slave_connector_destroy ( struct drm_connector * connector )
{
struct slave_connector * slave_connector = to_slave_connector ( connector ) ;
2014-07-09 17:12:57 +05:30
drm_connector_unregister ( connector ) ;
2013-01-22 16:02:21 -06:00
drm_connector_cleanup ( connector ) ;
kfree ( slave_connector ) ;
}
static enum drm_connector_status slave_connector_detect (
struct drm_connector * connector ,
bool force )
{
struct drm_encoder * encoder = to_slave_connector ( connector ) - > encoder ;
return get_slave_funcs ( encoder ) - > detect ( encoder , connector ) ;
}
static int slave_connector_get_modes ( struct drm_connector * connector )
{
struct drm_encoder * encoder = to_slave_connector ( connector ) - > encoder ;
return get_slave_funcs ( encoder ) - > get_modes ( encoder , connector ) ;
}
static int slave_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct drm_encoder * encoder = to_slave_connector ( connector ) - > encoder ;
struct tilcdc_drm_private * priv = connector - > dev - > dev_private ;
int ret ;
ret = tilcdc_crtc_mode_valid ( priv - > crtc , mode ) ;
if ( ret ! = MODE_OK )
return ret ;
return get_slave_funcs ( encoder ) - > mode_valid ( encoder , mode ) ;
}
static struct drm_encoder * slave_connector_best_encoder (
struct drm_connector * connector )
{
struct slave_connector * slave_connector = to_slave_connector ( connector ) ;
return slave_connector - > encoder ;
}
static int slave_connector_set_property ( struct drm_connector * connector ,
struct drm_property * property , uint64_t value )
{
struct drm_encoder * encoder = to_slave_connector ( connector ) - > encoder ;
return get_slave_funcs ( encoder ) - > set_property ( encoder ,
connector , property , value ) ;
}
static const struct drm_connector_funcs slave_connector_funcs = {
. destroy = slave_connector_destroy ,
. dpms = drm_helper_connector_dpms ,
. detect = slave_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = slave_connector_set_property ,
} ;
static const struct drm_connector_helper_funcs slave_connector_helper_funcs = {
. get_modes = slave_connector_get_modes ,
. mode_valid = slave_connector_mode_valid ,
. best_encoder = slave_connector_best_encoder ,
} ;
static struct drm_connector * slave_connector_create ( struct drm_device * dev ,
struct slave_module * mod , struct drm_encoder * encoder )
{
struct slave_connector * slave_connector ;
struct drm_connector * connector ;
int ret ;
slave_connector = kzalloc ( sizeof ( * slave_connector ) , GFP_KERNEL ) ;
if ( ! slave_connector ) {
dev_err ( dev - > dev , " allocation failed \n " ) ;
return NULL ;
}
slave_connector - > encoder = encoder ;
slave_connector - > mod = mod ;
connector = & slave_connector - > base ;
drm_connector_init ( dev , connector , & slave_connector_funcs ,
DRM_MODE_CONNECTOR_HDMIA ) ;
drm_connector_helper_add ( connector , & slave_connector_helper_funcs ) ;
connector - > polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT ;
connector - > interlace_allowed = 0 ;
connector - > doublescan_allowed = 0 ;
get_slave_funcs ( encoder ) - > create_resources ( encoder , connector ) ;
ret = drm_mode_connector_attach_encoder ( connector , encoder ) ;
if ( ret )
goto fail ;
2014-05-29 16:57:41 +01:00
drm_connector_register ( connector ) ;
2013-01-22 16:02:21 -06:00
return connector ;
fail :
slave_connector_destroy ( connector ) ;
return NULL ;
}
/*
* Module :
*/
static int slave_modeset_init ( struct tilcdc_module * mod , struct drm_device * dev )
{
struct slave_module * slave_mod = to_slave_module ( mod ) ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
struct drm_encoder * encoder ;
struct drm_connector * connector ;
encoder = slave_encoder_create ( dev , slave_mod ) ;
if ( ! encoder )
return - ENOMEM ;
connector = slave_connector_create ( dev , slave_mod , encoder ) ;
if ( ! connector )
return - ENOMEM ;
priv - > encoders [ priv - > num_encoders + + ] = encoder ;
priv - > connectors [ priv - > num_connectors + + ] = connector ;
return 0 ;
}
static const struct tilcdc_module_ops slave_module_ops = {
. modeset_init = slave_modeset_init ,
} ;
/*
* Device :
*/
static struct of_device_id slave_of_match [ ] ;
static int slave_probe ( struct platform_device * pdev )
{
struct device_node * node = pdev - > dev . of_node ;
struct device_node * i2c_node ;
struct slave_module * slave_mod ;
struct tilcdc_module * mod ;
struct pinctrl * pinctrl ;
uint32_t i2c_phandle ;
2013-06-21 13:52:27 -05:00
struct i2c_adapter * slavei2c ;
2013-01-22 16:02:21 -06:00
int ret = - EINVAL ;
/* bail out early if no DT data: */
if ( ! node ) {
dev_err ( & pdev - > dev , " device-tree data is missing \n " ) ;
return - ENXIO ;
}
2013-06-21 13:52:27 -05:00
/* Bail out early if i2c not specified */
2013-01-22 16:02:21 -06:00
if ( of_property_read_u32 ( node , " i2c " , & i2c_phandle ) ) {
dev_err ( & pdev - > dev , " could not get i2c bus phandle \n " ) ;
2013-06-21 13:52:27 -05:00
return ret ;
2013-01-22 16:02:21 -06:00
}
i2c_node = of_find_node_by_phandle ( i2c_phandle ) ;
if ( ! i2c_node ) {
dev_err ( & pdev - > dev , " could not get i2c bus node \n " ) ;
2013-06-21 13:52:27 -05:00
return ret ;
2013-01-22 16:02:21 -06:00
}
2013-06-21 13:52:27 -05:00
/* but defer the probe if it can't be initialized it might come later */
slavei2c = of_find_i2c_adapter_by_node ( i2c_node ) ;
of_node_put ( i2c_node ) ;
if ( ! slavei2c ) {
ret = - EPROBE_DEFER ;
tilcdc_slave_probedefer ( true ) ;
2013-01-22 16:02:21 -06:00
dev_err ( & pdev - > dev , " could not get i2c \n " ) ;
2013-06-21 13:52:27 -05:00
return ret ;
2013-01-22 16:02:21 -06:00
}
2013-06-21 13:52:27 -05:00
slave_mod = kzalloc ( sizeof ( * slave_mod ) , GFP_KERNEL ) ;
2014-06-17 11:17:10 -03:00
if ( ! slave_mod ) {
ret = - ENOMEM ;
goto fail_adapter ;
}
2013-01-22 16:02:21 -06:00
2013-06-21 13:52:27 -05:00
mod = & slave_mod - > base ;
2014-06-17 11:17:10 -03:00
pdev - > dev . platform_data = mod ;
2013-01-22 16:02:21 -06:00
2013-06-21 13:52:27 -05:00
mod - > preferred_bpp = slave_info . bpp ;
slave_mod - > i2c = slavei2c ;
tilcdc_module_init ( mod , " slave " , & slave_module_ops ) ;
pinctrl = devm_pinctrl_get_select_default ( & pdev - > dev ) ;
if ( IS_ERR ( pinctrl ) )
dev_warn ( & pdev - > dev , " pins are not configured \n " ) ;
tilcdc_slave_probedefer ( false ) ;
return 0 ;
2014-06-17 11:17:10 -03:00
fail_adapter :
i2c_put_adapter ( slavei2c ) ;
return ret ;
2013-01-22 16:02:21 -06:00
}
static int slave_remove ( struct platform_device * pdev )
{
2014-06-17 11:17:10 -03:00
struct tilcdc_module * mod = dev_get_platdata ( & pdev - > dev ) ;
struct slave_module * slave_mod = to_slave_module ( mod ) ;
tilcdc_module_cleanup ( mod ) ;
kfree ( slave_mod ) ;
2013-01-22 16:02:21 -06:00
return 0 ;
}
static struct of_device_id slave_of_match [ ] = {
{ . compatible = " ti,tilcdc,slave " , } ,
{ } ,
} ;
struct platform_driver slave_driver = {
. probe = slave_probe ,
. remove = slave_remove ,
. driver = {
. owner = THIS_MODULE ,
. name = " slave " ,
. of_match_table = slave_of_match ,
} ,
} ;
int __init tilcdc_slave_init ( void )
{
return platform_driver_register ( & slave_driver ) ;
}
void __exit tilcdc_slave_fini ( void )
{
platform_driver_unregister ( & slave_driver ) ;
}