2018-02-05 20:20:13 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Panel driver for the ARM Versatile family reference designs from
* ARM Limited .
*
* Author :
* Linus Walleij < linus . wallei @ linaro . org >
*
* On the Versatile AB , these panels come mounted on daughterboards
* named " IB1 " or " IB2 " ( Interface Board 1 & 2 respectively . ) They
* are documented in ARM DUI 0225 D Appendix C and D . These daughter
* boards support TFT display panels .
*
* - The IB1 is a passive board where the display connector defines a
* few wires for encoding the display type for autodetection ,
* suitable display settings can then be looked up from this setting .
* The magic bits can be read out from the system controller .
*
* - The IB2 is a more complex board intended for GSM phone development
* with some logic and a control register , which needs to be accessed
* and the board display needs to be turned on explicitly .
*
* On the Versatile PB , a special CLCD adaptor board is available
* supporting the same displays as the Versatile AB , plus one more
* Epson QCIF display .
*
*/
# include <linux/bitops.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
2019-05-26 20:05:32 +02:00
# include <linux/mod_devicetable.h>
2018-02-05 20:20:13 +01:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <video/of_videomode.h>
# include <video/videomode.h>
2019-05-26 20:05:32 +02:00
# include <drm/drm_modes.h>
# include <drm/drm_panel.h>
2018-02-05 20:20:13 +01:00
/*
* This configuration register in the Versatile and RealView
* family is uniformly present but appears more and more
* unutilized starting with the RealView series .
*/
# define SYS_CLCD 0x50
/* The Versatile can detect the connected panel type */
# define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
# define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
# define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8)
# define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
# define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
# define SYS_CLCD_ID_VGA (0x1f << 8)
/* IB2 control register for the Versatile daughterboard */
# define IB2_CTRL 0x00
# define IB2_CTRL_LCD_SD BIT(1) /* 1 = shut down LCD */
# define IB2_CTRL_LCD_BL_ON BIT(0)
# define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1))
/**
* struct versatile_panel_type - lookup struct for the supported panels
*/
struct versatile_panel_type {
/**
* @ name : the name of this panel
*/
const char * name ;
/**
* @ magic : the magic value from the detection register
*/
u32 magic ;
/**
* @ mode : the DRM display mode for this panel
*/
struct drm_display_mode mode ;
/**
* @ bus_flags : the DRM bus flags for this panel e . g . inverted clock
*/
u32 bus_flags ;
/**
* @ width_mm : the panel width in mm
*/
u32 width_mm ;
/**
* @ height_mm : the panel height in mm
*/
u32 height_mm ;
/**
* @ ib2 : the panel may be connected on an IB2 daughterboard
*/
bool ib2 ;
} ;
/**
* struct versatile_panel - state container for the Versatile panels
*/
struct versatile_panel {
/**
* @ dev : the container device
*/
struct device * dev ;
/**
* @ panel : the DRM panel instance for this device
*/
struct drm_panel panel ;
/**
* @ panel_type : the Versatile panel type as detected
*/
const struct versatile_panel_type * panel_type ;
/**
* @ map : map to the parent syscon where the main register reside
*/
struct regmap * map ;
/**
* @ ib2_map : map to the IB2 syscon , if applicable
*/
struct regmap * ib2_map ;
} ;
static const struct versatile_panel_type versatile_panels [ ] = {
/*
* Sanyo TM38QV67A02A - 3.8 inch QVGA ( 320 x240 ) Color TFT
* found on the Versatile AB IB1 connector or the Versatile
* PB adaptor board connector .
*/
{
. name = " Sanyo TM38QV67A02A " ,
. magic = SYS_CLCD_ID_SANYO_3_8 ,
. width_mm = 79 ,
. height_mm = 54 ,
. mode = {
2018-02-11 11:38:40 +01:00
. clock = 10000 ,
2018-02-05 20:20:13 +01:00
. hdisplay = 320 ,
. hsync_start = 320 + 6 ,
. hsync_end = 320 + 6 + 6 ,
. htotal = 320 + 6 + 6 + 6 ,
. vdisplay = 240 ,
. vsync_start = 240 + 5 ,
. vsync_end = 240 + 5 + 6 ,
. vtotal = 240 + 5 + 6 + 5 ,
. flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC ,
} ,
} ,
/*
* Sharp LQ084V1DG21 640 x480 VGA Color TFT module
* found on the Versatile AB IB1 connector or the Versatile
* PB adaptor board connector .
*/
{
. name = " Sharp LQ084V1DG21 " ,
. magic = SYS_CLCD_ID_SHARP_8_4 ,
. width_mm = 171 ,
. height_mm = 130 ,
. mode = {
2018-02-11 11:38:40 +01:00
. clock = 25000 ,
2018-02-05 20:20:13 +01:00
. hdisplay = 640 ,
. hsync_start = 640 + 24 ,
. hsync_end = 640 + 24 + 96 ,
. htotal = 640 + 24 + 96 + 24 ,
. vdisplay = 480 ,
. vsync_start = 480 + 11 ,
. vsync_end = 480 + 11 + 2 ,
. vtotal = 480 + 11 + 2 + 32 ,
. flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC ,
} ,
} ,
/*
* Epson L2F50113T00 - 2.2 inch QCIF 176 x220 Color TFT
* found on the Versatile PB adaptor board connector .
*/
{
. name = " Epson L2F50113T00 " ,
. magic = SYS_CLCD_ID_EPSON_2_2 ,
. width_mm = 34 ,
. height_mm = 45 ,
. mode = {
2018-02-11 11:38:40 +01:00
. clock = 62500 ,
2018-02-05 20:20:13 +01:00
. hdisplay = 176 ,
. hsync_start = 176 + 2 ,
. hsync_end = 176 + 2 + 3 ,
. htotal = 176 + 2 + 3 + 3 ,
. vdisplay = 220 ,
. vsync_start = 220 + 0 ,
. vsync_end = 220 + 0 + 2 ,
. vtotal = 220 + 0 + 2 + 1 ,
. flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC ,
} ,
2018-09-22 15:02:42 +03:00
. bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE ,
2018-02-05 20:20:13 +01:00
} ,
/*
* Sanyo ALR252RGT 240 x320 portrait display found on the
* Versatile AB IB2 daughterboard for GSM prototyping .
*/
{
. name = " Sanyo ALR252RGT " ,
. magic = SYS_CLCD_ID_SANYO_2_5 ,
. width_mm = 37 ,
. height_mm = 50 ,
. mode = {
2018-02-11 11:38:40 +01:00
. clock = 5400 ,
2018-02-05 20:20:13 +01:00
. hdisplay = 240 ,
. hsync_start = 240 + 10 ,
. hsync_end = 240 + 10 + 10 ,
. htotal = 240 + 10 + 10 + 20 ,
. vdisplay = 320 ,
. vsync_start = 320 + 2 ,
. vsync_end = 320 + 2 + 2 ,
. vtotal = 320 + 2 + 2 + 2 ,
. flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC ,
} ,
2018-09-22 15:02:42 +03:00
. bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE ,
2018-02-05 20:20:13 +01:00
. ib2 = true ,
} ,
} ;
static inline struct versatile_panel *
to_versatile_panel ( struct drm_panel * panel )
{
return container_of ( panel , struct versatile_panel , panel ) ;
}
static int versatile_panel_disable ( struct drm_panel * panel )
{
struct versatile_panel * vpanel = to_versatile_panel ( panel ) ;
/* If we're on an IB2 daughterboard, turn off display */
if ( vpanel - > ib2_map ) {
dev_dbg ( vpanel - > dev , " disable IB2 display \n " ) ;
regmap_update_bits ( vpanel - > ib2_map ,
IB2_CTRL ,
IB2_CTRL_LCD_MASK ,
IB2_CTRL_LCD_SD ) ;
}
return 0 ;
}
static int versatile_panel_enable ( struct drm_panel * panel )
{
struct versatile_panel * vpanel = to_versatile_panel ( panel ) ;
/* If we're on an IB2 daughterboard, turn on display */
if ( vpanel - > ib2_map ) {
dev_dbg ( vpanel - > dev , " enable IB2 display \n " ) ;
regmap_update_bits ( vpanel - > ib2_map ,
IB2_CTRL ,
IB2_CTRL_LCD_MASK ,
IB2_CTRL_LCD_BL_ON ) ;
}
return 0 ;
}
2019-12-07 15:03:33 +01:00
static int versatile_panel_get_modes ( struct drm_panel * panel ,
struct drm_connector * connector )
2018-02-05 20:20:13 +01:00
{
struct versatile_panel * vpanel = to_versatile_panel ( panel ) ;
struct drm_display_mode * mode ;
connector - > display_info . width_mm = vpanel - > panel_type - > width_mm ;
connector - > display_info . height_mm = vpanel - > panel_type - > height_mm ;
connector - > display_info . bus_flags = vpanel - > panel_type - > bus_flags ;
2019-12-07 15:03:35 +01:00
mode = drm_mode_duplicate ( connector - > dev , & vpanel - > panel_type - > mode ) ;
2018-02-05 20:20:13 +01:00
drm_mode_set_name ( mode ) ;
mode - > type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ;
mode - > width_mm = vpanel - > panel_type - > width_mm ;
mode - > height_mm = vpanel - > panel_type - > height_mm ;
drm_mode_probed_add ( connector , mode ) ;
return 1 ;
}
static const struct drm_panel_funcs versatile_panel_drm_funcs = {
. disable = versatile_panel_disable ,
. enable = versatile_panel_enable ,
. get_modes = versatile_panel_get_modes ,
} ;
static int versatile_panel_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct versatile_panel * vpanel ;
struct device * parent ;
struct regmap * map ;
int ret ;
u32 val ;
int i ;
parent = dev - > parent ;
if ( ! parent ) {
dev_err ( dev , " no parent for versatile panel \n " ) ;
return - ENODEV ;
}
map = syscon_node_to_regmap ( parent - > of_node ) ;
if ( IS_ERR ( map ) ) {
dev_err ( dev , " no regmap for versatile panel parent \n " ) ;
return PTR_ERR ( map ) ;
}
vpanel = devm_kzalloc ( dev , sizeof ( * vpanel ) , GFP_KERNEL ) ;
if ( ! vpanel )
return - ENOMEM ;
ret = regmap_read ( map , SYS_CLCD , & val ) ;
if ( ret ) {
dev_err ( dev , " cannot access syscon regs \n " ) ;
return ret ;
}
val & = SYS_CLCD_CLCDID_MASK ;
for ( i = 0 ; i < ARRAY_SIZE ( versatile_panels ) ; i + + ) {
const struct versatile_panel_type * pt ;
pt = & versatile_panels [ i ] ;
if ( pt - > magic = = val ) {
vpanel - > panel_type = pt ;
break ;
}
}
/* No panel detected or VGA, let's leave this show */
if ( i = = ARRAY_SIZE ( versatile_panels ) ) {
dev_info ( dev , " no panel detected \n " ) ;
return - ENODEV ;
}
dev_info ( dev , " detected: %s \n " , vpanel - > panel_type - > name ) ;
vpanel - > dev = dev ;
vpanel - > map = map ;
/* Check if the panel is mounted on an IB2 daughterboard */
if ( vpanel - > panel_type - > ib2 ) {
vpanel - > ib2_map = syscon_regmap_lookup_by_compatible (
" arm,versatile-ib2-syscon " ) ;
if ( IS_ERR ( vpanel - > ib2_map ) )
vpanel - > ib2_map = NULL ;
else
dev_info ( dev , " panel mounted on IB2 daughterboard \n " ) ;
}
2019-09-04 16:28:03 +03:00
drm_panel_init ( & vpanel - > panel , dev , & versatile_panel_drm_funcs ,
DRM_MODE_CONNECTOR_DPI ) ;
2018-02-05 20:20:13 +01:00
2020-08-01 20:02:13 +08:00
drm_panel_add ( & vpanel - > panel ) ;
return 0 ;
2018-02-05 20:20:13 +01:00
}
static const struct of_device_id versatile_panel_match [ ] = {
{ . compatible = " arm,versatile-tft-panel " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , versatile_panel_match ) ;
static struct platform_driver versatile_panel_driver = {
. probe = versatile_panel_probe ,
. driver = {
. name = " versatile-tft-panel " ,
. of_match_table = versatile_panel_match ,
} ,
} ;
module_platform_driver ( versatile_panel_driver ) ;
MODULE_AUTHOR ( " Linus Walleij <linus.walleij@linaro.org> " ) ;
MODULE_DESCRIPTION ( " ARM Versatile panel driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;