2012-09-21 10:07:48 +02:00
/*
* i . MX drm driver - parallel display implementation
*
* Copyright ( C ) 2012 Sascha Hauer , Pengutronix
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
* 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 .
*/
2013-11-03 11:23:34 +00:00
# include <linux/component.h>
2012-09-21 10:07:48 +02:00
# include <linux/module.h>
# include <drm/drmP.h>
# include <drm/drm_fb_helper.h>
# include <drm/drm_crtc_helper.h>
2014-02-25 11:55:04 +01:00
# include <drm/drm_panel.h>
2012-09-21 10:07:48 +02:00
# include <linux/videodev2.h>
2014-01-13 11:24:33 +01:00
# include <video/of_display_timing.h>
2012-09-21 10:07:48 +02:00
# include "imx-drm.h"
# define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
# define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder)
struct imx_parallel_display {
struct drm_connector connector ;
struct drm_encoder encoder ;
struct device * dev ;
void * edid ;
int edid_len ;
u32 interface_pix_fmt ;
int mode_valid ;
struct drm_display_mode mode ;
2014-02-25 11:55:04 +01:00
struct drm_panel * panel ;
2012-09-21 10:07:48 +02:00
} ;
static enum drm_connector_status imx_pd_connector_detect (
struct drm_connector * connector , bool force )
{
return connector_status_connected ;
}
static int imx_pd_connector_get_modes ( struct drm_connector * connector )
{
struct imx_parallel_display * imxpd = con_to_imxpd ( connector ) ;
2013-04-03 12:36:28 -03:00
struct device_node * np = imxpd - > dev - > of_node ;
2012-09-21 10:07:48 +02:00
int num_modes = 0 ;
2014-02-25 11:55:04 +01:00
if ( imxpd - > panel & & imxpd - > panel - > funcs & &
imxpd - > panel - > funcs - > get_modes ) {
num_modes = imxpd - > panel - > funcs - > get_modes ( imxpd - > panel ) ;
if ( num_modes > 0 )
return num_modes ;
}
2012-09-21 10:07:48 +02:00
if ( imxpd - > edid ) {
drm_mode_connector_update_edid_property ( connector , imxpd - > edid ) ;
num_modes = drm_add_edid_modes ( connector , imxpd - > edid ) ;
}
if ( imxpd - > mode_valid ) {
struct drm_display_mode * mode = drm_mode_create ( connector - > dev ) ;
2014-08-04 21:07:07 +02:00
2014-02-26 20:53:42 -03:00
if ( ! mode )
return - EINVAL ;
2012-09-21 10:07:48 +02:00
drm_mode_copy ( mode , & imxpd - > mode ) ;
mode - > type | = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ,
2013-04-03 12:36:28 -03:00
drm_mode_probed_add ( connector , mode ) ;
num_modes + + ;
}
if ( np ) {
struct drm_display_mode * mode = drm_mode_create ( connector - > dev ) ;
2014-08-04 21:07:07 +02:00
2014-02-26 20:53:42 -03:00
if ( ! mode )
return - EINVAL ;
2014-01-13 11:24:33 +01:00
of_get_drm_display_mode ( np , & imxpd - > mode , OF_USE_NATIVE_MODE ) ;
2013-04-03 12:36:28 -03:00
drm_mode_copy ( mode , & imxpd - > mode ) ;
mode - > type | = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ,
2012-09-21 10:07:48 +02:00
drm_mode_probed_add ( connector , mode ) ;
num_modes + + ;
}
return num_modes ;
}
static struct drm_encoder * imx_pd_connector_best_encoder (
struct drm_connector * connector )
{
struct imx_parallel_display * imxpd = con_to_imxpd ( connector ) ;
return & imxpd - > encoder ;
}
static void imx_pd_encoder_dpms ( struct drm_encoder * encoder , int mode )
{
2014-02-25 11:55:04 +01:00
struct imx_parallel_display * imxpd = enc_to_imxpd ( encoder ) ;
if ( mode ! = DRM_MODE_DPMS_ON )
drm_panel_disable ( imxpd - > panel ) ;
else
drm_panel_enable ( imxpd - > panel ) ;
2012-09-21 10:07:48 +02:00
}
static bool imx_pd_encoder_mode_fixup ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
return true ;
}
static void imx_pd_encoder_prepare ( struct drm_encoder * encoder )
{
struct imx_parallel_display * imxpd = enc_to_imxpd ( encoder ) ;
2013-11-03 15:52:16 +00:00
imx_drm_panel_format ( encoder , imxpd - > interface_pix_fmt ) ;
2012-09-21 10:07:48 +02:00
}
static void imx_pd_encoder_commit ( struct drm_encoder * encoder )
{
2014-11-03 15:54:47 +01:00
struct imx_parallel_display * imxpd = enc_to_imxpd ( encoder ) ;
drm_panel_prepare ( imxpd - > panel ) ;
drm_panel_enable ( imxpd - > panel ) ;
2012-09-21 10:07:48 +02:00
}
static void imx_pd_encoder_mode_set ( struct drm_encoder * encoder ,
2014-12-18 18:00:24 -08:00
struct drm_display_mode * orig_mode ,
struct drm_display_mode * mode )
2012-09-21 10:07:48 +02:00
{
}
static void imx_pd_encoder_disable ( struct drm_encoder * encoder )
{
2014-11-03 15:54:47 +01:00
struct imx_parallel_display * imxpd = enc_to_imxpd ( encoder ) ;
drm_panel_disable ( imxpd - > panel ) ;
drm_panel_unprepare ( imxpd - > panel ) ;
2012-09-21 10:07:48 +02:00
}
static struct drm_connector_funcs imx_pd_connector_funcs = {
. dpms = drm_helper_connector_dpms ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. detect = imx_pd_connector_detect ,
2013-11-03 13:30:48 +00:00
. destroy = imx_drm_connector_destroy ,
2012-09-21 10:07:48 +02:00
} ;
static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
. get_modes = imx_pd_connector_get_modes ,
. best_encoder = imx_pd_connector_best_encoder ,
} ;
static struct drm_encoder_funcs imx_pd_encoder_funcs = {
2013-11-03 13:30:48 +00:00
. destroy = imx_drm_encoder_destroy ,
2012-09-21 10:07:48 +02:00
} ;
static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
. dpms = imx_pd_encoder_dpms ,
. mode_fixup = imx_pd_encoder_mode_fixup ,
. prepare = imx_pd_encoder_prepare ,
. commit = imx_pd_encoder_commit ,
. mode_set = imx_pd_encoder_mode_set ,
. disable = imx_pd_encoder_disable ,
} ;
2013-11-03 13:30:48 +00:00
static int imx_pd_register ( struct drm_device * drm ,
struct imx_parallel_display * imxpd )
2012-09-21 10:07:48 +02:00
{
int ret ;
2013-11-03 13:30:48 +00:00
ret = imx_drm_encoder_parse_of ( drm , & imxpd - > encoder ,
imxpd - > dev - > of_node ) ;
if ( ret )
return ret ;
2012-09-21 10:07:48 +02:00
2014-06-17 15:34:51 +02:00
/* set the connector's dpms to OFF so that
* drm_helper_connector_dpms ( ) won ' t return
* immediately since the current state is ON
* at this point .
*/
imxpd - > connector . dpms = DRM_MODE_DPMS_OFF ;
2012-09-21 10:07:48 +02:00
drm_encoder_helper_add ( & imxpd - > encoder , & imx_pd_encoder_helper_funcs ) ;
2013-11-03 13:30:48 +00:00
drm_encoder_init ( drm , & imxpd - > encoder , & imx_pd_encoder_funcs ,
DRM_MODE_ENCODER_NONE ) ;
2012-09-21 10:07:48 +02:00
drm_connector_helper_add ( & imxpd - > connector ,
& imx_pd_connector_helper_funcs ) ;
2013-11-03 13:30:48 +00:00
drm_connector_init ( drm , & imxpd - > connector , & imx_pd_connector_funcs ,
DRM_MODE_CONNECTOR_VGA ) ;
2012-09-21 10:07:48 +02:00
2014-02-25 11:55:04 +01:00
if ( imxpd - > panel )
drm_panel_attach ( imxpd - > panel , & imxpd - > connector ) ;
2013-11-03 13:30:48 +00:00
drm_mode_connector_attach_encoder ( & imxpd - > connector , & imxpd - > encoder ) ;
2012-09-21 10:07:48 +02:00
imxpd - > connector . encoder = & imxpd - > encoder ;
return 0 ;
}
2013-11-03 11:23:34 +00:00
static int imx_pd_bind ( struct device * dev , struct device * master , void * data )
2012-09-21 10:07:48 +02:00
{
2013-11-03 13:30:48 +00:00
struct drm_device * drm = data ;
2013-11-03 11:23:34 +00:00
struct device_node * np = dev - > of_node ;
2014-02-25 11:55:04 +01:00
struct device_node * panel_node ;
2012-09-21 10:07:48 +02:00
const u8 * edidp ;
struct imx_parallel_display * imxpd ;
int ret ;
const char * fmt ;
2013-11-03 11:23:34 +00:00
imxpd = devm_kzalloc ( dev , sizeof ( * imxpd ) , GFP_KERNEL ) ;
2012-09-21 10:07:48 +02:00
if ( ! imxpd )
return - ENOMEM ;
edidp = of_get_property ( np , " edid " , & imxpd - > edid_len ) ;
if ( edidp )
imxpd - > edid = kmemdup ( edidp , imxpd - > edid_len , GFP_KERNEL ) ;
ret = of_property_read_string ( np , " interface-pix-fmt " , & fmt ) ;
if ( ! ret ) {
if ( ! strcmp ( fmt , " rgb24 " ) )
imxpd - > interface_pix_fmt = V4L2_PIX_FMT_RGB24 ;
else if ( ! strcmp ( fmt , " rgb565 " ) )
imxpd - > interface_pix_fmt = V4L2_PIX_FMT_RGB565 ;
2013-04-22 23:19:31 +02:00
else if ( ! strcmp ( fmt , " bgr666 " ) )
imxpd - > interface_pix_fmt = V4L2_PIX_FMT_BGR666 ;
2014-03-29 13:57:44 +01:00
else if ( ! strcmp ( fmt , " lvds666 " ) )
2014-09-21 19:40:21 +03:00
imxpd - > interface_pix_fmt =
v4l2_fourcc ( ' L ' , ' V ' , ' D ' , ' 6 ' ) ;
2012-09-21 10:07:48 +02:00
}
2014-02-25 11:55:04 +01:00
panel_node = of_parse_phandle ( np , " fsl,panel " , 0 ) ;
if ( panel_node )
imxpd - > panel = of_drm_find_panel ( panel_node ) ;
2013-11-03 11:23:34 +00:00
imxpd - > dev = dev ;
2012-09-21 10:07:48 +02:00
2013-11-03 13:30:48 +00:00
ret = imx_pd_register ( drm , imxpd ) ;
2012-09-21 10:07:48 +02:00
if ( ret )
return ret ;
2013-11-03 11:23:34 +00:00
dev_set_drvdata ( dev , imxpd ) ;
2012-09-21 10:07:48 +02:00
return 0 ;
}
2013-11-03 11:23:34 +00:00
static void imx_pd_unbind ( struct device * dev , struct device * master ,
void * data )
2012-09-21 10:07:48 +02:00
{
2013-11-03 11:23:34 +00:00
struct imx_parallel_display * imxpd = dev_get_drvdata ( dev ) ;
2012-09-21 10:07:48 +02:00
2013-11-03 13:30:48 +00:00
imxpd - > encoder . funcs - > destroy ( & imxpd - > encoder ) ;
imxpd - > connector . funcs - > destroy ( & imxpd - > connector ) ;
2013-12-08 22:03:58 +01:00
kfree ( imxpd - > edid ) ;
2013-11-03 11:23:34 +00:00
}
2012-09-21 10:07:48 +02:00
2013-11-03 11:23:34 +00:00
static const struct component_ops imx_pd_ops = {
. bind = imx_pd_bind ,
. unbind = imx_pd_unbind ,
} ;
static int imx_pd_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & imx_pd_ops ) ;
}
static int imx_pd_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & imx_pd_ops ) ;
2013-12-08 22:03:58 +01:00
2012-09-21 10:07:48 +02:00
return 0 ;
}
static const struct of_device_id imx_pd_dt_ids [ ] = {
{ . compatible = " fsl,imx-parallel-display " , } ,
{ /* sentinel */ }
} ;
2013-06-07 18:36:46 +09:00
MODULE_DEVICE_TABLE ( of , imx_pd_dt_ids ) ;
2012-09-21 10:07:48 +02:00
static struct platform_driver imx_pd_driver = {
. probe = imx_pd_probe ,
2012-11-19 13:20:51 -05:00
. remove = imx_pd_remove ,
2012-09-21 10:07:48 +02:00
. driver = {
. of_match_table = imx_pd_dt_ids ,
. name = " imx-parallel-display " ,
} ,
} ;
module_platform_driver ( imx_pd_driver ) ;
MODULE_DESCRIPTION ( " i.MX parallel display driver " ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;
2013-08-18 21:40:05 -03:00
MODULE_ALIAS ( " platform:imx-parallel-display " ) ;