2013-05-24 14:21:08 +03:00
/*
* HDMI Connector driver
*
* Copyright ( C ) 2013 Texas Instruments
* Author : Tomi Valkeinen < tomi . valkeinen @ ti . 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 .
*/
2016-05-11 18:05:39 +02:00
# include <linux/gpio/consumer.h>
2013-05-24 14:21:08 +03:00
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2013-07-30 10:37:34 +03:00
# include <linux/of.h>
2014-04-17 10:18:58 +03:00
# include <linux/of_gpio.h>
2013-05-24 14:21:08 +03:00
# include <drm/drm_edid.h>
# include <video/omap-panel-data.h>
2016-05-27 14:40:49 +03:00
# include "../dss/omapdss.h"
2013-05-24 14:21:08 +03:00
static const struct omap_video_timings hdmic_default_timings = {
2016-09-22 14:06:46 +03:00
. hactive = 640 ,
2016-09-22 14:06:47 +03:00
. vactive = 480 ,
2013-04-10 14:12:14 +03:00
. pixelclock = 25175000 ,
2016-09-22 14:06:48 +03:00
. hsync_len = 96 ,
2016-09-22 14:06:49 +03:00
. hfront_porch = 16 ,
2016-09-22 14:06:50 +03:00
. hback_porch = 48 ,
2016-09-22 14:06:51 +03:00
. vsync_len = 2 ,
2013-05-24 14:21:08 +03:00
. vfp = 11 ,
. vbp = 31 ,
. vsync_level = OMAPDSS_SIG_ACTIVE_LOW ,
. hsync_level = OMAPDSS_SIG_ACTIVE_LOW ,
. interlace = false ,
} ;
struct panel_drv_data {
struct omap_dss_device dssdev ;
struct omap_dss_device * in ;
struct device * dev ;
struct omap_video_timings timings ;
2014-04-17 10:18:58 +03:00
int hpd_gpio ;
2013-05-24 14:21:08 +03:00
} ;
# define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int hdmic_connect ( struct omap_dss_device * dssdev )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
int r ;
dev_dbg ( ddata - > dev , " connect \n " ) ;
if ( omapdss_device_is_connected ( dssdev ) )
return 0 ;
r = in - > ops . hdmi - > connect ( in , dssdev ) ;
if ( r )
return r ;
return 0 ;
}
static void hdmic_disconnect ( struct omap_dss_device * dssdev )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
dev_dbg ( ddata - > dev , " disconnect \n " ) ;
if ( ! omapdss_device_is_connected ( dssdev ) )
return ;
in - > ops . hdmi - > disconnect ( in , dssdev ) ;
}
static int hdmic_enable ( struct omap_dss_device * dssdev )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
int r ;
dev_dbg ( ddata - > dev , " enable \n " ) ;
if ( ! omapdss_device_is_connected ( dssdev ) )
return - ENODEV ;
if ( omapdss_device_is_enabled ( dssdev ) )
return 0 ;
in - > ops . hdmi - > set_timings ( in , & ddata - > timings ) ;
r = in - > ops . hdmi - > enable ( in ) ;
if ( r )
return r ;
dssdev - > state = OMAP_DSS_DISPLAY_ACTIVE ;
return r ;
}
static void hdmic_disable ( struct omap_dss_device * dssdev )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
dev_dbg ( ddata - > dev , " disable \n " ) ;
if ( ! omapdss_device_is_enabled ( dssdev ) )
return ;
in - > ops . hdmi - > disable ( in ) ;
dssdev - > state = OMAP_DSS_DISPLAY_DISABLED ;
}
static void hdmic_set_timings ( struct omap_dss_device * dssdev ,
struct omap_video_timings * timings )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
ddata - > timings = * timings ;
dssdev - > panel . timings = * timings ;
in - > ops . hdmi - > set_timings ( in , timings ) ;
}
static void hdmic_get_timings ( struct omap_dss_device * dssdev ,
struct omap_video_timings * timings )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
* timings = ddata - > timings ;
}
static int hdmic_check_timings ( struct omap_dss_device * dssdev ,
struct omap_video_timings * timings )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
return in - > ops . hdmi - > check_timings ( in , timings ) ;
}
static int hdmic_read_edid ( struct omap_dss_device * dssdev ,
u8 * edid , int len )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
return in - > ops . hdmi - > read_edid ( in , edid , len ) ;
}
static bool hdmic_detect ( struct omap_dss_device * dssdev )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
2014-04-17 10:18:58 +03:00
if ( gpio_is_valid ( ddata - > hpd_gpio ) )
return gpio_get_value_cansleep ( ddata - > hpd_gpio ) ;
else
return in - > ops . hdmi - > detect ( in ) ;
2013-05-24 14:21:08 +03:00
}
2014-06-18 12:58:02 +03:00
static int hdmic_set_hdmi_mode ( struct omap_dss_device * dssdev , bool hdmi_mode )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
return in - > ops . hdmi - > set_hdmi_mode ( in , hdmi_mode ) ;
}
static int hdmic_set_infoframe ( struct omap_dss_device * dssdev ,
const struct hdmi_avi_infoframe * avi )
{
struct panel_drv_data * ddata = to_panel_data ( dssdev ) ;
struct omap_dss_device * in = ddata - > in ;
return in - > ops . hdmi - > set_infoframe ( in , avi ) ;
}
2013-05-24 14:21:08 +03:00
static struct omap_dss_driver hdmic_driver = {
. connect = hdmic_connect ,
. disconnect = hdmic_disconnect ,
. enable = hdmic_enable ,
. disable = hdmic_disable ,
. set_timings = hdmic_set_timings ,
. get_timings = hdmic_get_timings ,
. check_timings = hdmic_check_timings ,
. get_resolution = omapdss_default_get_resolution ,
. read_edid = hdmic_read_edid ,
. detect = hdmic_detect ,
2014-06-18 12:58:02 +03:00
. set_hdmi_mode = hdmic_set_hdmi_mode ,
. set_hdmi_infoframe = hdmic_set_infoframe ,
2013-05-24 14:21:08 +03:00
} ;
2013-07-30 10:37:34 +03:00
static int hdmic_probe_of ( struct platform_device * pdev )
{
struct panel_drv_data * ddata = platform_get_drvdata ( pdev ) ;
struct device_node * node = pdev - > dev . of_node ;
struct omap_dss_device * in ;
2014-04-17 10:18:58 +03:00
int gpio ;
/* HPD GPIO */
gpio = of_get_named_gpio ( node , " hpd-gpios " , 0 ) ;
if ( gpio_is_valid ( gpio ) )
ddata - > hpd_gpio = gpio ;
else
ddata - > hpd_gpio = - ENODEV ;
2013-07-30 10:37:34 +03:00
in = omapdss_of_find_source_for_first_ep ( node ) ;
if ( IS_ERR ( in ) ) {
dev_err ( & pdev - > dev , " failed to find video source \n " ) ;
return PTR_ERR ( in ) ;
}
ddata - > in = in ;
return 0 ;
}
2013-05-24 14:21:08 +03:00
static int hdmic_probe ( struct platform_device * pdev )
{
struct panel_drv_data * ddata ;
struct omap_dss_device * dssdev ;
int r ;
ddata = devm_kzalloc ( & pdev - > dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
if ( ! ddata )
return - ENOMEM ;
platform_set_drvdata ( pdev , ddata ) ;
ddata - > dev = & pdev - > dev ;
2016-02-18 17:20:54 +02:00
if ( ! pdev - > dev . of_node )
2013-05-24 14:21:08 +03:00
return - ENODEV ;
2016-02-18 17:20:54 +02:00
r = hdmic_probe_of ( pdev ) ;
if ( r )
return r ;
2013-05-24 14:21:08 +03:00
2014-04-17 10:18:58 +03:00
if ( gpio_is_valid ( ddata - > hpd_gpio ) ) {
r = devm_gpio_request_one ( & pdev - > dev , ddata - > hpd_gpio ,
GPIOF_DIR_IN , " hdmi_hpd " ) ;
if ( r )
goto err_reg ;
}
2013-05-24 14:21:08 +03:00
ddata - > timings = hdmic_default_timings ;
dssdev = & ddata - > dssdev ;
dssdev - > driver = & hdmic_driver ;
dssdev - > dev = & pdev - > dev ;
dssdev - > type = OMAP_DISPLAY_TYPE_HDMI ;
dssdev - > owner = THIS_MODULE ;
dssdev - > panel . timings = hdmic_default_timings ;
r = omapdss_register_display ( dssdev ) ;
if ( r ) {
dev_err ( & pdev - > dev , " Failed to register panel \n " ) ;
goto err_reg ;
}
return 0 ;
err_reg :
omap_dss_put_device ( ddata - > in ) ;
return r ;
}
static int __exit hdmic_remove ( struct platform_device * pdev )
{
struct panel_drv_data * ddata = platform_get_drvdata ( pdev ) ;
struct omap_dss_device * dssdev = & ddata - > dssdev ;
struct omap_dss_device * in = ddata - > in ;
omapdss_unregister_display ( & ddata - > dssdev ) ;
hdmic_disable ( dssdev ) ;
hdmic_disconnect ( dssdev ) ;
omap_dss_put_device ( in ) ;
return 0 ;
}
2013-07-30 10:37:34 +03:00
static const struct of_device_id hdmic_of_match [ ] = {
{ . compatible = " omapdss,hdmi-connector " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , hdmic_of_match ) ;
2013-05-24 14:21:08 +03:00
static struct platform_driver hdmi_connector_driver = {
. probe = hdmic_probe ,
. remove = __exit_p ( hdmic_remove ) ,
. driver = {
. name = " connector-hdmi " ,
2013-07-30 10:37:34 +03:00
. of_match_table = hdmic_of_match ,
2014-10-16 09:54:25 +03:00
. suppress_bind_attrs = true ,
2013-05-24 14:21:08 +03:00
} ,
} ;
module_platform_driver ( hdmi_connector_driver ) ;
MODULE_AUTHOR ( " Tomi Valkeinen <tomi.valkeinen@ti.com> " ) ;
MODULE_DESCRIPTION ( " HDMI Connector driver " ) ;
MODULE_LICENSE ( " GPL " ) ;