2012-12-18 17:34:16 -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/pinctrl/pinmux.h>
# include <linux/pinctrl/consumer.h>
# include <linux/backlight.h>
2014-09-02 09:51:22 -03:00
# include <linux/gpio/consumer.h>
2012-12-18 17:34:16 -06:00
# include <video/display_timing.h>
# include <video/of_display_timing.h>
# include <video/videomode.h>
2016-04-13 18:49:29 +03:00
# include <drm/drm_atomic_helper.h>
2012-12-18 17:34:16 -06:00
# include "tilcdc_drv.h"
struct panel_module {
struct tilcdc_module base ;
struct tilcdc_panel_info * info ;
struct display_timings * timings ;
struct backlight_device * backlight ;
2014-09-02 09:51:22 -03:00
struct gpio_desc * enable_gpio ;
2012-12-18 17:34:16 -06:00
} ;
# define to_panel_module(x) container_of(x, struct panel_module, base)
/*
* Encoder :
*/
struct panel_encoder {
struct drm_encoder base ;
struct panel_module * mod ;
} ;
# define to_panel_encoder(x) container_of(x, struct panel_encoder, base)
static void panel_encoder_dpms ( struct drm_encoder * encoder , int mode )
{
struct panel_encoder * panel_encoder = to_panel_encoder ( encoder ) ;
struct backlight_device * backlight = panel_encoder - > mod - > backlight ;
2014-09-02 09:51:22 -03:00
struct gpio_desc * gpio = panel_encoder - > mod - > enable_gpio ;
2012-12-18 17:34:16 -06:00
2014-09-02 09:51:22 -03:00
if ( backlight ) {
backlight - > props . power = mode = = DRM_MODE_DPMS_ON ?
FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN ;
backlight_update_status ( backlight ) ;
}
2012-12-18 17:34:16 -06:00
2014-09-02 09:51:22 -03:00
if ( gpio )
gpiod_set_value_cansleep ( gpio ,
mode = = DRM_MODE_DPMS_ON ? 1 : 0 ) ;
2012-12-18 17:34:16 -06:00
}
static void panel_encoder_prepare ( struct drm_encoder * encoder )
{
panel_encoder_dpms ( encoder , DRM_MODE_DPMS_OFF ) ;
}
static void panel_encoder_commit ( struct drm_encoder * encoder )
{
panel_encoder_dpms ( encoder , DRM_MODE_DPMS_ON ) ;
}
static void panel_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
/* nothing needed */
}
static const struct drm_encoder_funcs panel_encoder_funcs = {
2016-02-23 12:44:27 +02:00
. destroy = drm_encoder_cleanup ,
2012-12-18 17:34:16 -06:00
} ;
static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = {
. dpms = panel_encoder_dpms ,
. prepare = panel_encoder_prepare ,
. commit = panel_encoder_commit ,
. mode_set = panel_encoder_mode_set ,
} ;
static struct drm_encoder * panel_encoder_create ( struct drm_device * dev ,
struct panel_module * mod )
{
struct panel_encoder * panel_encoder ;
struct drm_encoder * encoder ;
int ret ;
2016-02-23 12:44:27 +02:00
panel_encoder = devm_kzalloc ( dev - > dev , sizeof ( * panel_encoder ) ,
GFP_KERNEL ) ;
2012-12-18 17:34:16 -06:00
if ( ! panel_encoder ) {
dev_err ( dev - > dev , " allocation failed \n " ) ;
return NULL ;
}
panel_encoder - > mod = mod ;
encoder = & panel_encoder - > base ;
encoder - > possible_crtcs = 1 ;
ret = drm_encoder_init ( dev , encoder , & panel_encoder_funcs ,
drm: Pass 'name' to drm_encoder_init()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4;
@@
drm_encoder_init(E1, E2, E3, E4
+ ,NULL
)
v2: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670818-2966-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:20:18 +02:00
DRM_MODE_ENCODER_LVDS , NULL ) ;
2012-12-18 17:34:16 -06:00
if ( ret < 0 )
goto fail ;
drm_encoder_helper_add ( encoder , & panel_encoder_helper_funcs ) ;
return encoder ;
fail :
2016-02-23 12:44:27 +02:00
drm_encoder_cleanup ( encoder ) ;
2012-12-18 17:34:16 -06:00
return NULL ;
}
/*
* Connector :
*/
struct panel_connector {
struct drm_connector base ;
struct drm_encoder * encoder ; /* our connected encoder */
struct panel_module * mod ;
} ;
# define to_panel_connector(x) container_of(x, struct panel_connector, base)
static void panel_connector_destroy ( struct drm_connector * connector )
{
2014-07-09 17:12:57 +05:30
drm_connector_unregister ( connector ) ;
2012-12-18 17:34:16 -06:00
drm_connector_cleanup ( connector ) ;
}
static enum drm_connector_status panel_connector_detect (
struct drm_connector * connector ,
bool force )
{
return connector_status_connected ;
}
static int panel_connector_get_modes ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
struct panel_connector * panel_connector = to_panel_connector ( connector ) ;
struct display_timings * timings = panel_connector - > mod - > timings ;
int i ;
for ( i = 0 ; i < timings - > num_timings ; i + + ) {
struct drm_display_mode * mode = drm_mode_create ( dev ) ;
struct videomode vm ;
2013-03-21 14:20:12 +02:00
if ( videomode_from_timings ( timings , & vm , i ) )
2012-12-18 17:34:16 -06:00
break ;
drm_display_mode_from_videomode ( & vm , mode ) ;
mode - > type = DRM_MODE_TYPE_DRIVER ;
if ( timings - > native_mode = = i )
mode - > type | = DRM_MODE_TYPE_PREFERRED ;
drm_mode_set_name ( mode ) ;
drm_mode_probed_add ( connector , mode ) ;
}
return i ;
}
static int panel_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct tilcdc_drm_private * priv = connector - > dev - > dev_private ;
/* our only constraints are what the crtc can generate: */
return tilcdc_crtc_mode_valid ( priv - > crtc , mode ) ;
}
static struct drm_encoder * panel_connector_best_encoder (
struct drm_connector * connector )
{
struct panel_connector * panel_connector = to_panel_connector ( connector ) ;
return panel_connector - > encoder ;
}
static const struct drm_connector_funcs panel_connector_funcs = {
. destroy = panel_connector_destroy ,
2016-04-13 18:49:29 +03:00
. dpms = drm_atomic_helper_connector_dpms ,
2012-12-18 17:34:16 -06:00
. detect = panel_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
2016-04-13 18:49:29 +03:00
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2012-12-18 17:34:16 -06:00
} ;
static const struct drm_connector_helper_funcs panel_connector_helper_funcs = {
. get_modes = panel_connector_get_modes ,
. mode_valid = panel_connector_mode_valid ,
. best_encoder = panel_connector_best_encoder ,
} ;
static struct drm_connector * panel_connector_create ( struct drm_device * dev ,
struct panel_module * mod , struct drm_encoder * encoder )
{
struct panel_connector * panel_connector ;
struct drm_connector * connector ;
int ret ;
2016-02-23 12:44:27 +02:00
panel_connector = devm_kzalloc ( dev - > dev , sizeof ( * panel_connector ) ,
GFP_KERNEL ) ;
2012-12-18 17:34:16 -06:00
if ( ! panel_connector ) {
dev_err ( dev - > dev , " allocation failed \n " ) ;
return NULL ;
}
panel_connector - > encoder = encoder ;
panel_connector - > mod = mod ;
connector = & panel_connector - > base ;
drm_connector_init ( dev , connector , & panel_connector_funcs ,
DRM_MODE_CONNECTOR_LVDS ) ;
drm_connector_helper_add ( connector , & panel_connector_helper_funcs ) ;
connector - > interlace_allowed = 0 ;
connector - > doublescan_allowed = 0 ;
ret = drm_mode_connector_attach_encoder ( connector , encoder ) ;
if ( ret )
goto fail ;
2014-05-29 16:57:41 +01:00
drm_connector_register ( connector ) ;
2012-12-18 17:34:16 -06:00
return connector ;
fail :
panel_connector_destroy ( connector ) ;
return NULL ;
}
/*
* Module :
*/
static int panel_modeset_init ( struct tilcdc_module * mod , struct drm_device * dev )
{
struct panel_module * panel_mod = to_panel_module ( mod ) ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
struct drm_encoder * encoder ;
struct drm_connector * connector ;
encoder = panel_encoder_create ( dev , panel_mod ) ;
if ( ! encoder )
return - ENOMEM ;
connector = panel_connector_create ( dev , panel_mod , encoder ) ;
if ( ! connector )
return - ENOMEM ;
priv - > encoders [ priv - > num_encoders + + ] = encoder ;
priv - > connectors [ priv - > num_connectors + + ] = connector ;
2016-04-13 18:45:29 +03:00
tilcdc_crtc_set_panel_info ( priv - > crtc ,
to_panel_encoder ( encoder ) - > mod - > info ) ;
2012-12-18 17:34:16 -06:00
return 0 ;
}
static const struct tilcdc_module_ops panel_module_ops = {
. modeset_init = panel_modeset_init ,
} ;
/*
* Device :
*/
/* maybe move this somewhere common if it is needed by other outputs? */
2013-03-02 15:53:09 +05:30
static struct tilcdc_panel_info * of_get_panel_info ( struct device_node * np )
2012-12-18 17:34:16 -06:00
{
struct device_node * info_np ;
struct tilcdc_panel_info * info ;
int ret = 0 ;
if ( ! np ) {
pr_err ( " %s: no devicenode given \n " , __func__ ) ;
return NULL ;
}
info_np = of_get_child_by_name ( np , " panel-info " ) ;
if ( ! info_np ) {
pr_err ( " %s: could not find panel-info node \n " , __func__ ) ;
return NULL ;
}
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info ) {
pr_err ( " %s: allocation failed \n " , __func__ ) ;
2014-09-02 09:51:16 -03:00
of_node_put ( info_np ) ;
2012-12-18 17:34:16 -06:00
return NULL ;
}
ret | = of_property_read_u32 ( info_np , " ac-bias " , & info - > ac_bias ) ;
ret | = of_property_read_u32 ( info_np , " ac-bias-intrpt " , & info - > ac_bias_intrpt ) ;
ret | = of_property_read_u32 ( info_np , " dma-burst-sz " , & info - > dma_burst_sz ) ;
ret | = of_property_read_u32 ( info_np , " bpp " , & info - > bpp ) ;
ret | = of_property_read_u32 ( info_np , " fdd " , & info - > fdd ) ;
ret | = of_property_read_u32 ( info_np , " sync-edge " , & info - > sync_edge ) ;
ret | = of_property_read_u32 ( info_np , " sync-ctrl " , & info - > sync_ctrl ) ;
ret | = of_property_read_u32 ( info_np , " raster-order " , & info - > raster_order ) ;
ret | = of_property_read_u32 ( info_np , " fifo-th " , & info - > fifo_th ) ;
/* optional: */
info - > tft_alt_mode = of_property_read_bool ( info_np , " tft-alt-mode " ) ;
info - > invert_pxl_clk = of_property_read_bool ( info_np , " invert-pxl-clk " ) ;
if ( ret ) {
pr_err ( " %s: error reading panel-info properties \n " , __func__ ) ;
kfree ( info ) ;
2014-09-02 09:51:16 -03:00
of_node_put ( info_np ) ;
2012-12-18 17:34:16 -06:00
return NULL ;
}
2014-09-02 09:51:16 -03:00
of_node_put ( info_np ) ;
2012-12-18 17:34:16 -06:00
return info ;
}
static int panel_probe ( struct platform_device * pdev )
{
2014-09-02 09:51:20 -03:00
struct device_node * bl_node , * node = pdev - > dev . of_node ;
2012-12-18 17:34:16 -06:00
struct panel_module * panel_mod ;
struct tilcdc_module * mod ;
struct pinctrl * pinctrl ;
2014-09-02 09:51:21 -03:00
int ret ;
2012-12-18 17:34:16 -06:00
/* bail out early if no DT data: */
if ( ! node ) {
dev_err ( & pdev - > dev , " device-tree data is missing \n " ) ;
return - ENXIO ;
}
2014-09-02 09:51:19 -03:00
panel_mod = devm_kzalloc ( & pdev - > dev , sizeof ( * panel_mod ) , GFP_KERNEL ) ;
2012-12-18 17:34:16 -06:00
if ( ! panel_mod )
return - ENOMEM ;
2014-09-02 09:51:20 -03:00
bl_node = of_parse_phandle ( node , " backlight " , 0 ) ;
if ( bl_node ) {
panel_mod - > backlight = of_find_backlight_by_node ( bl_node ) ;
of_node_put ( bl_node ) ;
if ( ! panel_mod - > backlight )
return - EPROBE_DEFER ;
dev_info ( & pdev - > dev , " found backlight \n " ) ;
}
2015-02-11 17:32:53 +01:00
panel_mod - > enable_gpio = devm_gpiod_get_optional ( & pdev - > dev , " enable " ,
GPIOD_OUT_LOW ) ;
2014-09-02 09:51:22 -03:00
if ( IS_ERR ( panel_mod - > enable_gpio ) ) {
ret = PTR_ERR ( panel_mod - > enable_gpio ) ;
2015-02-11 17:32:53 +01:00
dev_err ( & pdev - > dev , " failed to request enable GPIO \n " ) ;
goto fail_backlight ;
2014-09-02 09:51:22 -03:00
}
2015-02-11 17:32:53 +01:00
if ( panel_mod - > enable_gpio )
dev_info ( & pdev - > dev , " found enable GPIO \n " ) ;
2012-12-18 17:34:16 -06:00
mod = & panel_mod - > base ;
2014-06-17 11:17:10 -03:00
pdev - > dev . platform_data = mod ;
2012-12-18 17:34:16 -06:00
tilcdc_module_init ( mod , " panel " , & panel_module_ops ) ;
pinctrl = devm_pinctrl_get_select_default ( & pdev - > dev ) ;
if ( IS_ERR ( pinctrl ) )
dev_warn ( & pdev - > dev , " pins are not configured \n " ) ;
panel_mod - > timings = of_get_display_timings ( node ) ;
if ( ! panel_mod - > timings ) {
dev_err ( & pdev - > dev , " could not get panel timings \n " ) ;
2014-09-02 09:51:21 -03:00
ret = - EINVAL ;
2014-06-17 11:17:10 -03:00
goto fail_free ;
2012-12-18 17:34:16 -06:00
}
panel_mod - > info = of_get_panel_info ( node ) ;
if ( ! panel_mod - > info ) {
dev_err ( & pdev - > dev , " could not get panel info \n " ) ;
2014-09-02 09:51:21 -03:00
ret = - EINVAL ;
2014-06-17 11:17:10 -03:00
goto fail_timings ;
2012-12-18 17:34:16 -06:00
}
return 0 ;
2014-06-17 11:17:10 -03:00
fail_timings :
display_timings_release ( panel_mod - > timings ) ;
fail_free :
tilcdc_module_cleanup ( mod ) ;
2014-09-02 09:51:22 -03:00
fail_backlight :
2014-09-02 09:51:20 -03:00
if ( panel_mod - > backlight )
put_device ( & panel_mod - > backlight - > dev ) ;
2012-12-18 17:34:16 -06:00
return ret ;
}
static int panel_remove ( struct platform_device * pdev )
{
2014-06-17 11:17:10 -03:00
struct tilcdc_module * mod = dev_get_platdata ( & pdev - > dev ) ;
struct panel_module * panel_mod = to_panel_module ( mod ) ;
2014-09-02 09:51:20 -03:00
struct backlight_device * backlight = panel_mod - > backlight ;
if ( backlight )
put_device ( & backlight - > dev ) ;
2014-06-17 11:17:10 -03:00
display_timings_release ( panel_mod - > timings ) ;
tilcdc_module_cleanup ( mod ) ;
kfree ( panel_mod - > info ) ;
2012-12-18 17:34:16 -06:00
return 0 ;
}
static struct of_device_id panel_of_match [ ] = {
{ . compatible = " ti,tilcdc,panel " , } ,
{ } ,
} ;
struct platform_driver panel_driver = {
. probe = panel_probe ,
. remove = panel_remove ,
. driver = {
. owner = THIS_MODULE ,
. name = " panel " ,
. of_match_table = panel_of_match ,
} ,
} ;
int __init tilcdc_panel_init ( void )
{
return platform_driver_register ( & panel_driver ) ;
}
void __exit tilcdc_panel_fini ( void )
{
platform_driver_unregister ( & panel_driver ) ;
}