2018-10-24 11:43:12 -07:00
// SPDX-License-Identifier: GPL-2.0+
/*
* DRM driver for the HX8357D LCD controller
*
* Copyright 2018 Broadcom
* Copyright 2018 David Lechner < david @ lechnology . com >
* Copyright 2016 Noralf Trønnes
* Copyright ( C ) 2015 Adafruit Industries
* Copyright ( C ) 2013 Christian Vogelgsang
*/
# include <linux/backlight.h>
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <linux/property.h>
# include <linux/spi/spi.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>
2018-11-10 15:56:47 +01:00
# include <drm/drm_gem_cma_helper.h>
2018-10-24 11:43:12 -07:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-07-22 12:43:11 +02:00
# include <drm/drm_mipi_dbi.h>
2018-10-24 11:43:12 -07:00
# include <drm/drm_modeset_helper.h>
# include <video/mipi_display.h>
# define HX8357D_SETOSC 0xb0
# define HX8357D_SETPOWER 0xb1
# define HX8357D_SETRGB 0xb3
# define HX8357D_SETCYC 0xb3
# define HX8357D_SETCOM 0xb6
# define HX8357D_SETEXTC 0xb9
# define HX8357D_SETSTBA 0xc0
# define HX8357D_SETPANEL 0xcc
# define HX8357D_SETGAMMA 0xe0
# define HX8357D_MADCTL_MY 0x80
# define HX8357D_MADCTL_MX 0x40
# define HX8357D_MADCTL_MV 0x20
# define HX8357D_MADCTL_ML 0x10
# define HX8357D_MADCTL_RGB 0x00
# define HX8357D_MADCTL_BGR 0x08
# define HX8357D_MADCTL_MH 0x04
static void yx240qv29_enable ( struct drm_simple_display_pipe * pipe ,
struct drm_crtc_state * crtc_state ,
struct drm_plane_state * plane_state )
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( pipe - > crtc . dev ) ;
struct mipi_dbi * dbi = & dbidev - > dbi ;
2018-10-24 11:43:12 -07:00
u8 addr_mode ;
2019-02-25 15:42:32 +01:00
int ret , idx ;
if ( ! drm_dev_enter ( pipe - > crtc . dev , & idx ) )
return ;
2018-10-24 11:43:12 -07:00
DRM_DEBUG_KMS ( " \n " ) ;
2019-07-22 12:43:06 +02:00
ret = mipi_dbi_poweron_conditional_reset ( dbidev ) ;
2018-10-24 11:43:12 -07:00
if ( ret < 0 )
2019-02-25 15:42:32 +01:00
goto out_exit ;
2018-10-24 11:43:12 -07:00
if ( ret = = 1 )
goto out_enable ;
/* setextc */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETEXTC , 0xFF , 0x83 , 0x57 ) ;
2018-10-24 11:43:12 -07:00
msleep ( 150 ) ;
/* setRGB which also enables SDO */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETRGB , 0x00 , 0x00 , 0x06 , 0x06 ) ;
2018-10-24 11:43:12 -07:00
/* -1.52V */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETCOM , 0x25 ) ;
2018-10-24 11:43:12 -07:00
/* Normal mode 70Hz, Idle mode 55 Hz */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETOSC , 0x68 ) ;
2018-10-24 11:43:12 -07:00
/* Set Panel - BGR, Gate direction swapped */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETPANEL , 0x05 ) ;
2018-10-24 11:43:12 -07:00
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETPOWER ,
2018-10-24 11:43:12 -07:00
0x00 , /* Not deep standby */
0x15 , /* BT */
0x1C , /* VSPR */
0x1C , /* VSNR */
0x83 , /* AP */
0xAA ) ; /* FS */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETSTBA ,
2018-10-24 11:43:12 -07:00
0x50 , /* OPON normal */
0x50 , /* OPON idle */
0x01 , /* STBA */
0x3C , /* STBA */
0x1E , /* STBA */
0x08 ) ; /* GEN */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETCYC ,
2018-10-24 11:43:12 -07:00
0x02 , /* NW 0x02 */
0x40 , /* RTN */
0x00 , /* DIV */
0x2A , /* DUM */
0x2A , /* DUM */
0x0D , /* GDON */
0x78 ) ; /* GDOFF */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , HX8357D_SETGAMMA ,
2018-10-24 11:43:12 -07:00
0x02 ,
0x0A ,
0x11 ,
0x1d ,
0x23 ,
0x35 ,
0x41 ,
0x4b ,
0x4b ,
0x42 ,
0x3A ,
0x27 ,
0x1B ,
0x08 ,
0x09 ,
0x03 ,
0x02 ,
0x0A ,
0x11 ,
0x1d ,
0x23 ,
0x35 ,
0x41 ,
0x4b ,
0x4b ,
0x42 ,
0x3A ,
0x27 ,
0x1B ,
0x08 ,
0x09 ,
0x03 ,
0x00 ,
0x01 ) ;
/* 16 bit */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_PIXEL_FORMAT ,
2018-10-24 11:43:12 -07:00
MIPI_DCS_PIXEL_FMT_16BIT ) ;
/* TE off */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_TEAR_ON , 0x00 ) ;
2018-10-24 11:43:12 -07:00
/* tear line */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_TEAR_SCANLINE , 0x00 , 0x02 ) ;
2018-10-24 11:43:12 -07:00
/* Exit Sleep */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_EXIT_SLEEP_MODE ) ;
2018-10-24 11:43:12 -07:00
msleep ( 150 ) ;
/* display on */
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_DISPLAY_ON ) ;
2018-10-24 11:43:12 -07:00
usleep_range ( 5000 , 7000 ) ;
out_enable :
2019-07-22 12:43:06 +02:00
switch ( dbidev - > rotation ) {
2018-10-24 11:43:12 -07:00
default :
addr_mode = HX8357D_MADCTL_MX | HX8357D_MADCTL_MY ;
break ;
case 90 :
addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MY ;
break ;
case 180 :
addr_mode = 0 ;
break ;
case 270 :
addr_mode = HX8357D_MADCTL_MV | HX8357D_MADCTL_MX ;
break ;
}
2019-07-22 12:43:05 +02:00
mipi_dbi_command ( dbi , MIPI_DCS_SET_ADDRESS_MODE , addr_mode ) ;
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-10-24 11:43:12 -07:00
}
static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
. enable = yx240qv29_enable ,
. disable = mipi_dbi_pipe_disable ,
2019-01-15 05:36:42 +01:00
. update = mipi_dbi_pipe_update ,
2018-10-24 11:43:12 -07:00
. prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb ,
} ;
static const struct drm_display_mode yx350hv15_mode = {
2019-02-10 14:10:31 +01:00
DRM_SIMPLE_MODE ( 320 , 480 , 60 , 75 ) ,
2018-10-24 11:43:12 -07:00
} ;
DEFINE_DRM_GEM_CMA_FOPS ( hx8357d_fops ) ;
static struct drm_driver hx8357d_driver = {
2019-06-17 17:39:24 +02:00
. driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC ,
2018-10-24 11:43:12 -07:00
. fops = & hx8357d_fops ,
2019-02-25 15:42:30 +01:00
. release = mipi_dbi_release ,
2018-11-10 15:56:47 +01:00
DRM_GEM_CMA_VMAP_DRIVER_OPS ,
2018-10-24 11:43:12 -07:00
. debugfs_init = mipi_dbi_debugfs_init ,
. name = " hx8357d " ,
. desc = " HX8357D " ,
. date = " 20181023 " ,
. major = 1 ,
. minor = 0 ,
} ;
static const struct of_device_id hx8357d_of_match [ ] = {
{ . compatible = " adafruit,yx350hv15 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , hx8357d_of_match ) ;
static const struct spi_device_id hx8357d_id [ ] = {
{ " yx350hv15 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , hx8357d_id ) ;
static int hx8357d_probe ( struct spi_device * spi )
{
struct device * dev = & spi - > dev ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev ;
2019-02-25 15:42:30 +01:00
struct drm_device * drm ;
2018-10-24 11:43:12 -07:00
struct gpio_desc * dc ;
u32 rotation = 0 ;
int ret ;
2019-07-22 12:43:06 +02:00
dbidev = kzalloc ( sizeof ( * dbidev ) , GFP_KERNEL ) ;
if ( ! dbidev )
2018-10-24 11:43:12 -07:00
return - ENOMEM ;
2019-07-22 12:43:06 +02:00
drm = & dbidev - > drm ;
2019-02-25 15:42:30 +01:00
ret = devm_drm_dev_init ( dev , drm , & hx8357d_driver ) ;
if ( ret ) {
2019-07-22 12:43:06 +02:00
kfree ( dbidev ) ;
2019-02-25 15:42:30 +01:00
return ret ;
}
drm_mode_config_init ( drm ) ;
2018-10-24 11:43:12 -07:00
dc = devm_gpiod_get ( dev , " dc " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( dc ) ) {
DRM_DEV_ERROR ( dev , " Failed to get gpio 'dc' \n " ) ;
return PTR_ERR ( dc ) ;
}
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-10-24 11:43:12 -07:00
device_property_read_u32 ( dev , " rotation " , & rotation ) ;
2019-07-22 12:43:07 +02:00
ret = mipi_dbi_spi_init ( spi , & dbidev - > dbi , dc ) ;
2018-10-24 11:43:12 -07:00
if ( ret )
return ret ;
2019-07-22 12:43:07 +02:00
ret = mipi_dbi_dev_init ( dbidev , & hx8357d_pipe_funcs , & yx350hv15_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-10-24 11:43:12 -07: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 hx8357d_remove ( struct spi_device * spi )
{
struct drm_device * drm = spi_get_drvdata ( spi ) ;
drm_dev_unplug ( drm ) ;
drm_atomic_helper_shutdown ( drm ) ;
2018-10-24 11:43:12 -07:00
2019-02-25 15:42:30 +01:00
return 0 ;
2018-10-24 11:43:12 -07:00
}
static void hx8357d_shutdown ( struct spi_device * spi )
{
2019-02-10 14:10:33 +01:00
drm_atomic_helper_shutdown ( spi_get_drvdata ( spi ) ) ;
2018-10-24 11:43:12 -07:00
}
static struct spi_driver hx8357d_spi_driver = {
. driver = {
. name = " hx8357d " ,
. of_match_table = hx8357d_of_match ,
} ,
. id_table = hx8357d_id ,
. probe = hx8357d_probe ,
2019-02-25 15:42:30 +01:00
. remove = hx8357d_remove ,
2018-10-24 11:43:12 -07:00
. shutdown = hx8357d_shutdown ,
} ;
module_spi_driver ( hx8357d_spi_driver ) ;
MODULE_DESCRIPTION ( " HX8357D DRM driver " ) ;
MODULE_AUTHOR ( " Eric Anholt <eric@anholt.net> " ) ;
MODULE_LICENSE ( " GPL " ) ;