2019-02-20 09:21:56 +01:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright ( C ) 2018 - 2019 , Bridge Systems BV
* Copyright ( C ) 2018 - 2019 , Bootlin
* Copyright ( C ) 2017 , Free Electrons
*
* This file based on panel - ilitek - ili9881c . c
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/media-bus-format.h>
# include <linux/module.h>
2023-01-11 14:01:58 +01:00
# include <linux/of.h>
2019-02-20 09:21:56 +01:00
# include <linux/gpio/consumer.h>
# include <linux/regulator/consumer.h>
# include <drm/drm_connector.h>
# include <drm/drm_mipi_dsi.h>
# include <drm/drm_modes.h>
# include <drm/drm_panel.h>
struct rb070d30_panel {
struct drm_panel panel ;
struct mipi_dsi_device * dsi ;
struct regulator * supply ;
struct {
struct gpio_desc * power ;
struct gpio_desc * reset ;
struct gpio_desc * updn ;
struct gpio_desc * shlr ;
} gpios ;
} ;
static inline struct rb070d30_panel * panel_to_rb070d30_panel ( struct drm_panel * panel )
{
return container_of ( panel , struct rb070d30_panel , panel ) ;
}
static int rb070d30_panel_prepare ( struct drm_panel * panel )
{
struct rb070d30_panel * ctx = panel_to_rb070d30_panel ( panel ) ;
int ret ;
ret = regulator_enable ( ctx - > supply ) ;
if ( ret < 0 ) {
2020-08-15 14:54:06 +02:00
dev_err ( & ctx - > dsi - > dev , " Failed to enable supply: %d \n " , ret ) ;
2019-02-20 09:21:56 +01:00
return ret ;
}
msleep ( 20 ) ;
gpiod_set_value ( ctx - > gpios . power , 1 ) ;
msleep ( 20 ) ;
gpiod_set_value ( ctx - > gpios . reset , 1 ) ;
msleep ( 20 ) ;
return 0 ;
}
static int rb070d30_panel_unprepare ( struct drm_panel * panel )
{
struct rb070d30_panel * ctx = panel_to_rb070d30_panel ( panel ) ;
gpiod_set_value ( ctx - > gpios . reset , 0 ) ;
gpiod_set_value ( ctx - > gpios . power , 0 ) ;
regulator_disable ( ctx - > supply ) ;
return 0 ;
}
static int rb070d30_panel_enable ( struct drm_panel * panel )
{
struct rb070d30_panel * ctx = panel_to_rb070d30_panel ( panel ) ;
2020-09-15 11:26:23 +08:00
return mipi_dsi_dcs_exit_sleep_mode ( ctx - > dsi ) ;
2019-02-20 09:21:56 +01:00
}
static int rb070d30_panel_disable ( struct drm_panel * panel )
{
struct rb070d30_panel * ctx = panel_to_rb070d30_panel ( panel ) ;
return mipi_dsi_dcs_enter_sleep_mode ( ctx - > dsi ) ;
}
/* Default timings */
static const struct drm_display_mode default_mode = {
. clock = 51206 ,
. hdisplay = 1024 ,
. hsync_start = 1024 + 160 ,
. hsync_end = 1024 + 160 + 80 ,
. htotal = 1024 + 160 + 80 + 80 ,
. vdisplay = 600 ,
. vsync_start = 600 + 12 ,
. vsync_end = 600 + 12 + 10 ,
. vtotal = 600 + 12 + 10 + 13 ,
. width_mm = 154 ,
. height_mm = 85 ,
} ;
2019-12-07 15:03:33 +01:00
static int rb070d30_panel_get_modes ( struct drm_panel * panel ,
struct drm_connector * connector )
2019-02-20 09:21:56 +01:00
{
struct rb070d30_panel * ctx = panel_to_rb070d30_panel ( panel ) ;
struct drm_display_mode * mode ;
static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24 ;
2019-12-07 15:03:35 +01:00
mode = drm_mode_duplicate ( connector - > dev , & default_mode ) ;
2019-02-20 09:21:56 +01:00
if ( ! mode ) {
2020-08-15 14:54:06 +02:00
dev_err ( & ctx - > dsi - > dev , " Failed to add mode " DRM_MODE_FMT " \n " ,
DRM_MODE_ARG ( & default_mode ) ) ;
2019-02-20 09:21:56 +01:00
return - EINVAL ;
}
drm_mode_set_name ( mode ) ;
mode - > type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
2019-12-07 15:03:33 +01:00
connector - > display_info . bpc = 8 ;
connector - > display_info . width_mm = mode - > width_mm ;
connector - > display_info . height_mm = mode - > height_mm ;
2019-02-20 09:21:56 +01:00
drm_display_info_set_bus_formats ( & connector - > display_info ,
& bus_format , 1 ) ;
return 1 ;
}
static const struct drm_panel_funcs rb070d30_panel_funcs = {
. get_modes = rb070d30_panel_get_modes ,
. prepare = rb070d30_panel_prepare ,
. enable = rb070d30_panel_enable ,
. disable = rb070d30_panel_disable ,
. unprepare = rb070d30_panel_unprepare ,
} ;
static int rb070d30_panel_dsi_probe ( struct mipi_dsi_device * dsi )
{
struct rb070d30_panel * ctx ;
int ret ;
ctx = devm_kzalloc ( & dsi - > dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
ctx - > supply = devm_regulator_get ( & dsi - > dev , " vcc-lcd " ) ;
if ( IS_ERR ( ctx - > supply ) )
return PTR_ERR ( ctx - > supply ) ;
mipi_dsi_set_drvdata ( dsi , ctx ) ;
ctx - > dsi = dsi ;
2019-09-04 16:28:03 +03:00
drm_panel_init ( & ctx - > panel , & dsi - > dev , & rb070d30_panel_funcs ,
DRM_MODE_CONNECTOR_DSI ) ;
2019-02-20 09:21:56 +01:00
ctx - > gpios . reset = devm_gpiod_get ( & dsi - > dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ctx - > gpios . reset ) ) {
2020-08-15 14:54:06 +02:00
dev_err ( & dsi - > dev , " Couldn't get our reset GPIO \n " ) ;
2019-02-20 09:21:56 +01:00
return PTR_ERR ( ctx - > gpios . reset ) ;
}
ctx - > gpios . power = devm_gpiod_get ( & dsi - > dev , " power " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ctx - > gpios . power ) ) {
2020-08-15 14:54:06 +02:00
dev_err ( & dsi - > dev , " Couldn't get our power GPIO \n " ) ;
2019-02-20 09:21:56 +01:00
return PTR_ERR ( ctx - > gpios . power ) ;
}
/*
* We don ' t change the state of that GPIO later on but we need
* to force it into a low state .
*/
ctx - > gpios . updn = devm_gpiod_get ( & dsi - > dev , " updn " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ctx - > gpios . updn ) ) {
2020-08-15 14:54:06 +02:00
dev_err ( & dsi - > dev , " Couldn't get our updn GPIO \n " ) ;
2019-02-20 09:21:56 +01:00
return PTR_ERR ( ctx - > gpios . updn ) ;
}
/*
* We don ' t change the state of that GPIO later on but we need
* to force it into a low state .
*/
ctx - > gpios . shlr = devm_gpiod_get ( & dsi - > dev , " shlr " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ctx - > gpios . shlr ) ) {
2020-08-15 14:54:06 +02:00
dev_err ( & dsi - > dev , " Couldn't get our shlr GPIO \n " ) ;
2019-02-20 09:21:56 +01:00
return PTR_ERR ( ctx - > gpios . shlr ) ;
}
2019-12-07 15:03:46 +01:00
ret = drm_panel_of_backlight ( & ctx - > panel ) ;
if ( ret )
return ret ;
2019-02-20 09:21:56 +01:00
2020-08-01 20:02:13 +08:00
drm_panel_add ( & ctx - > panel ) ;
2019-02-20 09:21:56 +01:00
dsi - > mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM ;
dsi - > format = MIPI_DSI_FMT_RGB888 ;
dsi - > lanes = 4 ;
2021-09-23 17:33:55 -07:00
ret = mipi_dsi_attach ( dsi ) ;
if ( ret < 0 ) {
drm_panel_remove ( & ctx - > panel ) ;
return ret ;
}
return 0 ;
2019-02-20 09:21:56 +01:00
}
2022-07-08 11:49:22 +02:00
static void rb070d30_panel_dsi_remove ( struct mipi_dsi_device * dsi )
2019-02-20 09:21:56 +01:00
{
struct rb070d30_panel * ctx = mipi_dsi_get_drvdata ( dsi ) ;
mipi_dsi_detach ( dsi ) ;
drm_panel_remove ( & ctx - > panel ) ;
}
static const struct of_device_id rb070d30_panel_of_match [ ] = {
{ . compatible = " ronbo,rb070d30 " } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , rb070d30_panel_of_match ) ;
static struct mipi_dsi_driver rb070d30_panel_driver = {
. probe = rb070d30_panel_dsi_probe ,
. remove = rb070d30_panel_dsi_remove ,
. driver = {
. name = " panel-ronbo-rb070d30 " ,
. of_match_table = rb070d30_panel_of_match ,
} ,
} ;
module_mipi_dsi_driver ( rb070d30_panel_driver ) ;
MODULE_AUTHOR ( " Boris Brezillon <boris.brezillon@bootlin.com> " ) ;
MODULE_AUTHOR ( " Konstantin Sudakov <k.sudakov@integrasources.com> " ) ;
MODULE_DESCRIPTION ( " Ronbo RB070D30 Panel Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;