2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-10-30 15:34:30 -07:00
/*
* Copyright ( C ) 2015 Red Hat
* Copyright ( C ) 2015 Sony Mobile Communications Inc .
* Author : Werner Johansson < werner . johansson @ sonymobile . com >
*
* Based on AUO panel driver by Rob Clark < robdclark @ gmail . com >
*/
2019-05-26 20:05:32 +02:00
# include <linux/delay.h>
2015-10-30 15:34:30 -07:00
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/regulator/consumer.h>
2019-05-26 20:05:32 +02:00
# include <video/mipi_display.h>
2015-10-30 15:34:30 -07:00
# include <drm/drm_crtc.h>
2019-05-26 20:05:32 +02:00
# include <drm/drm_device.h>
2015-10-30 15:34:30 -07:00
# include <drm/drm_mipi_dsi.h>
# include <drm/drm_panel.h>
struct sharp_nt_panel {
struct drm_panel base ;
struct mipi_dsi_device * dsi ;
struct regulator * supply ;
struct gpio_desc * reset_gpio ;
bool prepared ;
bool enabled ;
const struct drm_display_mode * mode ;
} ;
static inline struct sharp_nt_panel * to_sharp_nt_panel ( struct drm_panel * panel )
{
return container_of ( panel , struct sharp_nt_panel , base ) ;
}
static int sharp_nt_panel_init ( struct sharp_nt_panel * sharp_nt )
{
struct mipi_dsi_device * dsi = sharp_nt - > dsi ;
int ret ;
dsi - > mode_flags | = MIPI_DSI_MODE_LPM ;
ret = mipi_dsi_dcs_exit_sleep_mode ( dsi ) ;
if ( ret < 0 )
return ret ;
msleep ( 120 ) ;
/* Novatek two-lane operation */
ret = mipi_dsi_dcs_write ( dsi , 0xae , ( u8 [ ] ) { 0x03 } , 1 ) ;
if ( ret < 0 )
return ret ;
/* Set both MCU and RGB I/F to 24bpp */
ret = mipi_dsi_dcs_set_pixel_format ( dsi , MIPI_DCS_PIXEL_FMT_24BIT |
( MIPI_DCS_PIXEL_FMT_24BIT < < 4 ) ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int sharp_nt_panel_on ( struct sharp_nt_panel * sharp_nt )
{
struct mipi_dsi_device * dsi = sharp_nt - > dsi ;
int ret ;
dsi - > mode_flags | = MIPI_DSI_MODE_LPM ;
ret = mipi_dsi_dcs_set_display_on ( dsi ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int sharp_nt_panel_off ( struct sharp_nt_panel * sharp_nt )
{
struct mipi_dsi_device * dsi = sharp_nt - > dsi ;
int ret ;
dsi - > mode_flags & = ~ MIPI_DSI_MODE_LPM ;
ret = mipi_dsi_dcs_set_display_off ( dsi ) ;
if ( ret < 0 )
return ret ;
ret = mipi_dsi_dcs_enter_sleep_mode ( dsi ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int sharp_nt_panel_disable ( struct drm_panel * panel )
{
struct sharp_nt_panel * sharp_nt = to_sharp_nt_panel ( panel ) ;
if ( ! sharp_nt - > enabled )
return 0 ;
sharp_nt - > enabled = false ;
return 0 ;
}
static int sharp_nt_panel_unprepare ( struct drm_panel * panel )
{
struct sharp_nt_panel * sharp_nt = to_sharp_nt_panel ( panel ) ;
int ret ;
if ( ! sharp_nt - > prepared )
return 0 ;
ret = sharp_nt_panel_off ( sharp_nt ) ;
if ( ret < 0 ) {
dev_err ( panel - > dev , " failed to set panel off: %d \n " , ret ) ;
return ret ;
}
regulator_disable ( sharp_nt - > supply ) ;
if ( sharp_nt - > reset_gpio )
gpiod_set_value ( sharp_nt - > reset_gpio , 0 ) ;
sharp_nt - > prepared = false ;
return 0 ;
}
static int sharp_nt_panel_prepare ( struct drm_panel * panel )
{
struct sharp_nt_panel * sharp_nt = to_sharp_nt_panel ( panel ) ;
int ret ;
if ( sharp_nt - > prepared )
return 0 ;
ret = regulator_enable ( sharp_nt - > supply ) ;
if ( ret < 0 )
return ret ;
msleep ( 20 ) ;
if ( sharp_nt - > reset_gpio ) {
gpiod_set_value ( sharp_nt - > reset_gpio , 1 ) ;
msleep ( 1 ) ;
gpiod_set_value ( sharp_nt - > reset_gpio , 0 ) ;
msleep ( 1 ) ;
gpiod_set_value ( sharp_nt - > reset_gpio , 1 ) ;
msleep ( 10 ) ;
}
ret = sharp_nt_panel_init ( sharp_nt ) ;
if ( ret < 0 ) {
dev_err ( panel - > dev , " failed to init panel: %d \n " , ret ) ;
goto poweroff ;
}
ret = sharp_nt_panel_on ( sharp_nt ) ;
if ( ret < 0 ) {
dev_err ( panel - > dev , " failed to set panel on: %d \n " , ret ) ;
goto poweroff ;
}
sharp_nt - > prepared = true ;
return 0 ;
poweroff :
regulator_disable ( sharp_nt - > supply ) ;
if ( sharp_nt - > reset_gpio )
gpiod_set_value ( sharp_nt - > reset_gpio , 0 ) ;
return ret ;
}
static int sharp_nt_panel_enable ( struct drm_panel * panel )
{
struct sharp_nt_panel * sharp_nt = to_sharp_nt_panel ( panel ) ;
if ( sharp_nt - > enabled )
return 0 ;
sharp_nt - > enabled = true ;
return 0 ;
}
static const struct drm_display_mode default_mode = {
. clock = 41118 ,
. hdisplay = 540 ,
. hsync_start = 540 + 48 ,
. hsync_end = 540 + 48 + 80 ,
. htotal = 540 + 48 + 80 + 32 ,
. vdisplay = 960 ,
. vsync_start = 960 + 3 ,
. vsync_end = 960 + 3 + 15 ,
. vtotal = 960 + 3 + 15 + 1 ,
. vrefresh = 60 ,
} ;
2019-12-07 15:03:33 +01:00
static int sharp_nt_panel_get_modes ( struct drm_panel * panel ,
struct drm_connector * connector )
2015-10-30 15:34:30 -07:00
{
struct drm_display_mode * mode ;
2019-12-07 15:03:35 +01:00
mode = drm_mode_duplicate ( connector - > dev , & default_mode ) ;
2015-10-30 15:34:30 -07:00
if ( ! mode ) {
2019-12-07 15:03:35 +01:00
dev_err ( panel - > dev , " failed to add mode %ux%u@%u \n " ,
default_mode . hdisplay , default_mode . vdisplay ,
default_mode . vrefresh ) ;
2015-10-30 15:34:30 -07:00
return - ENOMEM ;
}
drm_mode_set_name ( mode ) ;
2019-12-07 15:03:33 +01:00
drm_mode_probed_add ( connector , mode ) ;
2015-10-30 15:34:30 -07:00
2019-12-07 15:03:33 +01:00
connector - > display_info . width_mm = 54 ;
connector - > display_info . height_mm = 95 ;
2015-10-30 15:34:30 -07:00
return 1 ;
}
static const struct drm_panel_funcs sharp_nt_panel_funcs = {
. disable = sharp_nt_panel_disable ,
. unprepare = sharp_nt_panel_unprepare ,
. prepare = sharp_nt_panel_prepare ,
. enable = sharp_nt_panel_enable ,
. get_modes = sharp_nt_panel_get_modes ,
} ;
static int sharp_nt_panel_add ( struct sharp_nt_panel * sharp_nt )
{
struct device * dev = & sharp_nt - > dsi - > dev ;
2019-12-07 15:03:49 +01:00
int ret ;
2015-10-30 15:34:30 -07:00
sharp_nt - > mode = & default_mode ;
sharp_nt - > supply = devm_regulator_get ( dev , " avdd " ) ;
if ( IS_ERR ( sharp_nt - > supply ) )
return PTR_ERR ( sharp_nt - > supply ) ;
sharp_nt - > reset_gpio = devm_gpiod_get ( dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( sharp_nt - > reset_gpio ) ) {
dev_err ( dev , " cannot get reset-gpios %ld \n " ,
PTR_ERR ( sharp_nt - > reset_gpio ) ) ;
sharp_nt - > reset_gpio = NULL ;
} else {
gpiod_set_value ( sharp_nt - > reset_gpio , 0 ) ;
}
2019-08-23 22:32:43 +03:00
drm_panel_init ( & sharp_nt - > base , & sharp_nt - > dsi - > dev ,
2019-09-04 16:28:03 +03:00
& sharp_nt_panel_funcs , DRM_MODE_CONNECTOR_DSI ) ;
2015-10-30 15:34:30 -07:00
2019-12-07 15:03:49 +01:00
ret = drm_panel_of_backlight ( & sharp_nt - > base ) ;
if ( ret )
return ret ;
2018-01-24 16:40:51 +00:00
return drm_panel_add ( & sharp_nt - > base ) ;
2015-10-30 15:34:30 -07:00
}
static void sharp_nt_panel_del ( struct sharp_nt_panel * sharp_nt )
{
if ( sharp_nt - > base . dev )
drm_panel_remove ( & sharp_nt - > base ) ;
}
static int sharp_nt_panel_probe ( struct mipi_dsi_device * dsi )
{
struct sharp_nt_panel * sharp_nt ;
int ret ;
dsi - > lanes = 2 ;
dsi - > format = MIPI_DSI_FMT_RGB888 ;
dsi - > mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_EOT_PACKET ;
sharp_nt = devm_kzalloc ( & dsi - > dev , sizeof ( * sharp_nt ) , GFP_KERNEL ) ;
if ( ! sharp_nt )
return - ENOMEM ;
mipi_dsi_set_drvdata ( dsi , sharp_nt ) ;
sharp_nt - > dsi = dsi ;
ret = sharp_nt_panel_add ( sharp_nt ) ;
if ( ret < 0 )
return ret ;
return mipi_dsi_attach ( dsi ) ;
}
static int sharp_nt_panel_remove ( struct mipi_dsi_device * dsi )
{
struct sharp_nt_panel * sharp_nt = mipi_dsi_get_drvdata ( dsi ) ;
int ret ;
2019-12-07 15:03:49 +01:00
ret = drm_panel_disable ( & sharp_nt - > base ) ;
2015-10-30 15:34:30 -07:00
if ( ret < 0 )
dev_err ( & dsi - > dev , " failed to disable panel: %d \n " , ret ) ;
ret = mipi_dsi_detach ( dsi ) ;
if ( ret < 0 )
dev_err ( & dsi - > dev , " failed to detach from DSI host: %d \n " , ret ) ;
sharp_nt_panel_del ( sharp_nt ) ;
return 0 ;
}
static void sharp_nt_panel_shutdown ( struct mipi_dsi_device * dsi )
{
struct sharp_nt_panel * sharp_nt = mipi_dsi_get_drvdata ( dsi ) ;
2019-12-07 15:03:49 +01:00
drm_panel_disable ( & sharp_nt - > base ) ;
2015-10-30 15:34:30 -07:00
}
static const struct of_device_id sharp_nt_of_match [ ] = {
{ . compatible = " sharp,ls043t1le01-qhd " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sharp_nt_of_match ) ;
static struct mipi_dsi_driver sharp_nt_panel_driver = {
. driver = {
. name = " panel-sharp-ls043t1le01-qhd " ,
. of_match_table = sharp_nt_of_match ,
} ,
. probe = sharp_nt_panel_probe ,
. remove = sharp_nt_panel_remove ,
. shutdown = sharp_nt_panel_shutdown ,
} ;
module_mipi_dsi_driver ( sharp_nt_panel_driver ) ;
MODULE_AUTHOR ( " Werner Johansson <werner.johansson@sonymobile.com> " ) ;
MODULE_DESCRIPTION ( " Sharp LS043T1LE01 NT35565-based qHD (540x960) video mode panel driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;