2018-01-01 13:02:16 -06:00
// SPDX-License-Identifier: GPL-2.0+
/*
2020-01-15 13:45:48 +01:00
* DRM driver for display panels connected to a Sitronix ST7715R or ST7735R
* display controller in SPI mode .
2018-01-01 13:02:16 -06:00
*
* Copyright 2017 David Lechner < david @ lechnology . com >
2020-01-15 13:45:47 +01:00
* Copyright ( C ) 2019 Glider bvba
2018-01-01 13:02:16 -06:00
*/
2018-01-24 16:36:09 +00:00
# include <linux/backlight.h>
2018-01-01 13:02:16 -06:00
# include <linux/delay.h>
# include <linux/dma-buf.h>
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <linux/property.h>
# include <linux/spi/spi.h>
# include <video/mipi_display.h>
2019-02-10 14:10:33 +01:00
# include <drm/drm_atomic_helper.h>
2019-01-08 20:29:38 +01:00
# include <drm/drm_drv.h>
2019-02-25 15:42:30 +01:00
# include <drm/drm_fb_helper.h>
2021-02-22 15:17:56 +01:00
# include <drm/drm_gem_atomic_helper.h>
2018-11-10 15:56:47 +01:00
# include <drm/drm_gem_cma_helper.h>
2020-03-23 15:49:04 +01:00
# include <drm/drm_managed.h>
2019-07-22 12:43:11 +02:00
# include <drm/drm_mipi_dbi.h>
2018-01-01 13:02:16 -06:00
# define ST7735R_FRMCTR1 0xb1
# define ST7735R_FRMCTR2 0xb2
# define ST7735R_FRMCTR3 0xb3
# define ST7735R_INVCTR 0xb4
# define ST7735R_PWCTR1 0xc0
# define ST7735R_PWCTR2 0xc1
# define ST7735R_PWCTR3 0xc2
# define ST7735R_PWCTR4 0xc3
# define ST7735R_PWCTR5 0xc4
# define ST7735R_VMCTR1 0xc5
# define ST7735R_GAMCTRP1 0xe0
# define ST7735R_GAMCTRN1 0xe1
# define ST7735R_MY BIT(7)
# define ST7735R_MX BIT(6)
# define ST7735R_MV BIT(5)
2020-01-15 13:45:47 +01:00
# define ST7735R_RGB BIT(3)
struct st7735r_cfg {
const struct drm_display_mode mode ;
unsigned int left_offset ;
unsigned int top_offset ;
unsigned int write_only : 1 ;
unsigned int rgb : 1 ; /* RGB (vs. BGR) */
} ;
struct st7735r_priv {
struct mipi_dbi_dev dbidev ; /* Must be first for .release() */
const struct st7735r_cfg * cfg ;
} ;
2018-01-01 13:02:16 -06:00
2020-01-15 13:45:47 +01:00
static void st7735r_pipe_enable ( struct drm_simple_display_pipe * pipe ,
struct drm_crtc_state * crtc_state ,
struct drm_plane_state * plane_state )
2018-01-01 13:02:16 -06:00
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( pipe - > crtc . dev ) ;
2020-01-15 13:45:47 +01:00
struct st7735r_priv * priv = container_of ( dbidev , struct st7735r_priv ,
dbidev ) ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi * dbi = & dbidev - > dbi ;
2019-02-25 15:42:32 +01:00
int ret , idx ;
2018-01-01 13:02:16 -06:00
u8 addr_mode ;
2019-02-25 15:42:32 +01:00
if ( ! drm_dev_enter ( pipe - > crtc . dev , & idx ) )
return ;
2018-01-01 13:02:16 -06:00
DRM_DEBUG_KMS ( " \n " ) ;
2019-07-22 12:43:06 +02:00
ret = mipi_dbi_poweron_reset ( dbidev ) ;
2018-01-10 19:59:37 +01:00
if ( ret )
2019-02-25 15:42:32 +01:00
goto out_exit ;
2018-01-01 13:02:16 -06:00
msleep ( 150 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_EXIT_SLEEP_MODE ) ;
2018-01-01 13:02:16 -06:00
msleep ( 500 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , ST7735R_FRMCTR1 , 0x01 , 0x2c , 0x2d ) ;
mipi_dbi_command ( dbi , ST7735R_FRMCTR2 , 0x01 , 0x2c , 0x2d ) ;
mipi_dbi_command ( dbi , ST7735R_FRMCTR3 , 0x01 , 0x2c , 0x2d , 0x01 , 0x2c ,
2018-01-01 13:02:16 -06:00
0x2d ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , ST7735R_INVCTR , 0x07 ) ;
mipi_dbi_command ( dbi , ST7735R_PWCTR1 , 0xa2 , 0x02 , 0x84 ) ;
mipi_dbi_command ( dbi , ST7735R_PWCTR2 , 0xc5 ) ;
mipi_dbi_command ( dbi , ST7735R_PWCTR3 , 0x0a , 0x00 ) ;
mipi_dbi_command ( dbi , ST7735R_PWCTR4 , 0x8a , 0x2a ) ;
mipi_dbi_command ( dbi , ST7735R_PWCTR5 , 0x8a , 0xee ) ;
mipi_dbi_command ( dbi , ST7735R_VMCTR1 , 0x0e ) ;
mipi_dbi_command ( dbi , MIPI_DCS_EXIT_INVERT_MODE ) ;
2019-07-22 12:43:06 +02:00
switch ( dbidev - > rotation ) {
2018-01-01 13:02:16 -06:00
default :
addr_mode = ST7735R_MX | ST7735R_MY ;
break ;
case 90 :
addr_mode = ST7735R_MX | ST7735R_MV ;
break ;
case 180 :
addr_mode = 0 ;
break ;
case 270 :
addr_mode = ST7735R_MY | ST7735R_MV ;
break ;
}
2020-01-15 13:45:47 +01:00
if ( priv - > cfg - > rgb )
addr_mode | = ST7735R_RGB ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_ADDRESS_MODE , addr_mode ) ;
mipi_dbi_command ( dbi , MIPI_DCS_SET_PIXEL_FORMAT ,
2018-01-01 13:02:16 -06:00
MIPI_DCS_PIXEL_FMT_16BIT ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , ST7735R_GAMCTRP1 , 0x02 , 0x1c , 0x07 , 0x12 , 0x37 ,
2018-01-01 13:02:16 -06:00
0x32 , 0x29 , 0x2d , 0x29 , 0x25 , 0x2b , 0x39 , 0x00 , 0x01 ,
0x03 , 0x10 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , ST7735R_GAMCTRN1 , 0x03 , 0x1d , 0x07 , 0x06 , 0x2e ,
2018-01-01 13:02:16 -06:00
0x2c , 0x29 , 0x2d , 0x2e , 0x2e , 0x37 , 0x3f , 0x00 , 0x00 ,
0x02 , 0x10 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_DISPLAY_ON ) ;
2018-01-01 13:02:16 -06:00
msleep ( 100 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_ENTER_NORMAL_MODE ) ;
2018-01-01 13:02:16 -06:00
msleep ( 20 ) ;
2019-07-22 12:43:06 +02:00
mipi_dbi_enable_flush ( dbidev , crtc_state , plane_state ) ;
2019-02-25 15:42:32 +01:00
out_exit :
drm_dev_exit ( idx ) ;
2018-01-01 13:02:16 -06:00
}
2020-01-15 13:45:47 +01:00
static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = {
. enable = st7735r_pipe_enable ,
2018-01-01 13:02:16 -06:00
. disable = mipi_dbi_pipe_disable ,
2019-01-15 05:36:42 +01:00
. update = mipi_dbi_pipe_update ,
2018-01-01 13:02:16 -06:00
} ;
2020-01-15 13:45:47 +01:00
static const struct st7735r_cfg jd_t18003_t01_cfg = {
. mode = { DRM_SIMPLE_MODE ( 128 , 160 , 28 , 35 ) } ,
/* Cannot read from Adafruit 1.8" display via SPI */
. write_only = true ,
} ;
2020-01-15 13:45:48 +01:00
static const struct st7735r_cfg rh128128t_cfg = {
. mode = { DRM_SIMPLE_MODE ( 128 , 128 , 25 , 26 ) } ,
. left_offset = 2 ,
. top_offset = 3 ,
. rgb = true ,
2018-01-01 13:02:16 -06:00
} ;
DEFINE_DRM_GEM_CMA_FOPS ( st7735r_fops ) ;
2020-11-04 11:04:24 +01:00
static const struct drm_driver st7735r_driver = {
2019-06-17 17:39:24 +02:00
. driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC ,
2018-01-01 13:02:16 -06:00
. fops = & st7735r_fops ,
2020-06-05 09:32:06 +02:00
DRM_GEM_CMA_DRIVER_OPS_VMAP ,
2018-01-01 13:02:16 -06:00
. debugfs_init = mipi_dbi_debugfs_init ,
. name = " st7735r " ,
. desc = " Sitronix ST7735R " ,
. date = " 20171128 " ,
. major = 1 ,
. minor = 0 ,
} ;
static const struct of_device_id st7735r_of_match [ ] = {
2020-01-15 13:45:47 +01:00
{ . compatible = " jianda,jd-t18003-t01 " , . data = & jd_t18003_t01_cfg } ,
2020-01-15 13:45:48 +01:00
{ . compatible = " okaya,rh128128t " , . data = & rh128128t_cfg } ,
2018-01-01 13:02:16 -06:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , st7735r_of_match ) ;
static const struct spi_device_id st7735r_id [ ] = {
2020-01-15 13:45:47 +01:00
{ " jd-t18003-t01 " , ( uintptr_t ) & jd_t18003_t01_cfg } ,
2018-01-01 13:02:16 -06:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( spi , st7735r_id ) ;
static int st7735r_probe ( struct spi_device * spi )
{
struct device * dev = & spi - > dev ;
2020-01-15 13:45:47 +01:00
const struct st7735r_cfg * cfg ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev ;
2020-01-15 13:45:47 +01:00
struct st7735r_priv * priv ;
2019-02-25 15:42:30 +01:00
struct drm_device * drm ;
2019-07-22 12:43:05 +02:00
struct mipi_dbi * dbi ;
2018-01-01 13:02:16 -06:00
struct gpio_desc * dc ;
u32 rotation = 0 ;
int ret ;
2020-01-31 22:49:26 +02:00
cfg = device_get_match_data ( & spi - > dev ) ;
2020-01-15 13:45:47 +01:00
if ( ! cfg )
cfg = ( void * ) spi_get_device_id ( spi ) - > driver_data ;
2020-04-15 09:39:48 +02:00
priv = devm_drm_dev_alloc ( dev , & st7735r_driver ,
struct st7735r_priv , dbidev . drm ) ;
if ( IS_ERR ( priv ) )
return PTR_ERR ( priv ) ;
2018-01-01 13:02:16 -06:00
2020-01-15 13:45:47 +01:00
dbidev = & priv - > dbidev ;
priv - > cfg = cfg ;
2019-07-22 12:43:07 +02:00
dbi = & dbidev - > dbi ;
2019-07-22 12:43:06 +02:00
drm = & dbidev - > drm ;
2019-02-25 15:42:30 +01:00
2019-07-22 12:43:05 +02:00
dbi - > reset = devm_gpiod_get ( dev , " reset " , GPIOD_OUT_HIGH ) ;
2021-04-21 19:31:51 +03:00
if ( IS_ERR ( dbi - > reset ) )
return dev_err_probe ( dev , PTR_ERR ( dbi - > reset ) , " Failed to get GPIO 'reset' \n " ) ;
2018-01-01 13:02:16 -06:00
dc = devm_gpiod_get ( dev , " dc " , GPIOD_OUT_LOW ) ;
2021-04-21 19:31:51 +03:00
if ( IS_ERR ( dc ) )
return dev_err_probe ( dev , PTR_ERR ( dc ) , " Failed to get GPIO 'dc' \n " ) ;
2018-01-01 13:02:16 -06:00
2019-07-22 12:43:06 +02:00
dbidev - > backlight = devm_of_find_backlight ( dev ) ;
if ( IS_ERR ( dbidev - > backlight ) )
return PTR_ERR ( dbidev - > backlight ) ;
2018-01-01 13:02:16 -06:00
device_property_read_u32 ( dev , " rotation " , & rotation ) ;
2019-07-22 12:43:05 +02:00
ret = mipi_dbi_spi_init ( spi , dbi , dc ) ;
2018-01-01 13:02:16 -06:00
if ( ret )
return ret ;
2020-01-15 13:45:47 +01:00
if ( cfg - > write_only )
dbi - > read_commands = NULL ;
dbidev - > left_offset = cfg - > left_offset ;
dbidev - > top_offset = cfg - > top_offset ;
2018-01-01 13:02:16 -06:00
2020-01-15 13:45:47 +01:00
ret = mipi_dbi_dev_init ( dbidev , & st7735r_pipe_funcs , & cfg - > mode ,
rotation ) ;
2019-02-25 15:42:30 +01:00
if ( ret )
return ret ;
drm_mode_config_reset ( drm ) ;
ret = drm_dev_register ( drm , 0 ) ;
2018-01-01 13:02:16 -06:00
if ( ret )
return ret ;
2019-02-25 15:42:30 +01:00
spi_set_drvdata ( spi , drm ) ;
2019-04-10 14:43:45 +02:00
drm_fbdev_generic_setup ( drm , 0 ) ;
2019-02-25 15:42:30 +01:00
return 0 ;
}
static int st7735r_remove ( struct spi_device * spi )
{
struct drm_device * drm = spi_get_drvdata ( spi ) ;
drm_dev_unplug ( drm ) ;
drm_atomic_helper_shutdown ( drm ) ;
2018-01-01 13:02:16 -06:00
2019-02-25 15:42:30 +01:00
return 0 ;
2018-01-01 13:02:16 -06:00
}
static void st7735r_shutdown ( struct spi_device * spi )
{
2019-02-10 14:10:33 +01:00
drm_atomic_helper_shutdown ( spi_get_drvdata ( spi ) ) ;
2018-01-01 13:02:16 -06:00
}
static struct spi_driver st7735r_spi_driver = {
. driver = {
. name = " st7735r " ,
. of_match_table = st7735r_of_match ,
} ,
. id_table = st7735r_id ,
. probe = st7735r_probe ,
2019-02-25 15:42:30 +01:00
. remove = st7735r_remove ,
2018-01-01 13:02:16 -06:00
. shutdown = st7735r_shutdown ,
} ;
module_spi_driver ( st7735r_spi_driver ) ;
MODULE_DESCRIPTION ( " Sitronix ST7735R DRM driver " ) ;
MODULE_AUTHOR ( " David Lechner <david@lechnology.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;