2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-01-22 00:30:47 +01:00
/*
* MIPI Display Bus Interface ( DBI ) LCD controller support
*
* Copyright 2016 Noralf Trønnes
*/
# include <linux/debugfs.h>
2019-01-08 20:29:38 +01:00
# include <linux/delay.h>
2017-01-22 00:30:47 +01:00
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <linux/regulator/consumer.h>
# include <linux/spi/spi.h>
2018-11-10 15:56:47 +01:00
2019-07-19 17:59:16 +02:00
# include <drm/drm_connector.h>
2019-01-15 05:36:42 +01:00
# include <drm/drm_damage_helper.h>
2019-01-08 20:29:38 +01:00
# include <drm/drm_drv.h>
2018-11-10 15:56:47 +01:00
# include <drm/drm_gem_cma_helper.h>
2019-04-05 11:52:15 +02:00
# include <drm/drm_format_helper.h>
2019-01-08 20:29:38 +01:00
# include <drm/drm_fourcc.h>
2018-11-10 15:56:47 +01:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-07-22 12:43:11 +02:00
# include <drm/drm_mipi_dbi.h>
2019-07-19 17:59:16 +02:00
# include <drm/drm_modes.h>
# include <drm/drm_probe_helper.h>
2019-01-15 05:36:41 +01:00
# include <drm/drm_rect.h>
2017-01-22 00:30:47 +01:00
# include <video/mipi_display.h>
# define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
# define DCS_POWER_MODE_DISPLAY BIT(2)
# define DCS_POWER_MODE_DISPLAY_NORMAL_MODE BIT(3)
# define DCS_POWER_MODE_SLEEP_MODE BIT(4)
# define DCS_POWER_MODE_PARTIAL_MODE BIT(5)
# define DCS_POWER_MODE_IDLE_MODE BIT(6)
# define DCS_POWER_MODE_RESERVED_MASK (BIT(0) | BIT(1) | BIT(7))
/**
* DOC : overview
*
* This library provides helpers for MIPI Display Bus Interface ( DBI )
* compatible display controllers .
*
* Many controllers for tiny lcd displays are MIPI compliant and can use this
* library . If a controller uses registers 0x2A and 0x2B to set the area to
* update and uses register 0x2C to write to frame memory , it is most likely
* MIPI compliant .
*
* Only MIPI Type 1 displays are supported since a full frame memory is needed .
*
* There are 3 MIPI DBI implementation types :
*
* A . Motorola 6800 type parallel bus
*
* B . Intel 8080 type parallel bus
*
* C . SPI type with 3 options :
*
* 1. 9 - bit with the Data / Command signal as the ninth bit
* 2. Same as above except it ' s sent as 16 bits
* 3. 8 - bit with the Data / Command signal as a separate D / CX pin
*
* Currently mipi_dbi only supports Type C options 1 and 3 with
* mipi_dbi_spi_init ( ) .
*/
# define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
( { \
if ( ! len ) \
DRM_DEBUG_DRIVER ( " cmd=%02x \n " , cmd ) ; \
else if ( len < = 32 ) \
drm/tinydrm: mipi-dbi: Fix field width specifier warning
This warning is seen on 64-bit builds in functions:
'mipi_dbi_typec1_command':
'mipi_dbi_typec3_command_read':
'mipi_dbi_typec3_command':
>> drivers/gpu/drm/tinydrm/mipi-dbi.c:65:20: warning: field width specifier '*' expects argument of type 'int', but argument 5 has type 'size_t {aka long unsigned int}' [-Wformat=]
DRM_DEBUG_DRIVER("cmd=%02x, par=%*ph\n", cmd, len, data); \
^
include/drm/drmP.h:228:40: note: in definition of macro 'DRM_DEBUG_DRIVER'
drm_printk(KERN_DEBUG, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
^~~
>> drivers/gpu/drm/tinydrm/mipi-dbi.c:671:2: note: in expansion of macro 'MIPI_DBI_DEBUG_COMMAND'
MIPI_DBI_DEBUG_COMMAND(cmd, parameters, num);
^~~~~~~~~~~~~~~~~~~~~~
Fix by casting 'len' to int in the macro MIPI_DBI_DEBUG_COMMAND().
There is no chance of overflow.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Signed-off-by: Dave Airlie <airlied@redhat.com>
2017-02-23 14:29:56 +01:00
DRM_DEBUG_DRIVER ( " cmd=%02x, par=%*ph \n " , cmd , ( int ) len , data ) ; \
2017-01-22 00:30:47 +01:00
else \
2017-02-23 18:58:18 -08:00
DRM_DEBUG_DRIVER ( " cmd=%02x, len=%zu \n " , cmd , len ) ; \
2017-01-22 00:30:47 +01:00
} )
static const u8 mipi_dbi_dcs_read_commands [ ] = {
MIPI_DCS_GET_DISPLAY_ID ,
MIPI_DCS_GET_RED_CHANNEL ,
MIPI_DCS_GET_GREEN_CHANNEL ,
MIPI_DCS_GET_BLUE_CHANNEL ,
MIPI_DCS_GET_DISPLAY_STATUS ,
MIPI_DCS_GET_POWER_MODE ,
MIPI_DCS_GET_ADDRESS_MODE ,
MIPI_DCS_GET_PIXEL_FORMAT ,
MIPI_DCS_GET_DISPLAY_MODE ,
MIPI_DCS_GET_SIGNAL_MODE ,
MIPI_DCS_GET_DIAGNOSTIC_RESULT ,
MIPI_DCS_READ_MEMORY_START ,
MIPI_DCS_READ_MEMORY_CONTINUE ,
MIPI_DCS_GET_SCANLINE ,
MIPI_DCS_GET_DISPLAY_BRIGHTNESS ,
MIPI_DCS_GET_CONTROL_DISPLAY ,
MIPI_DCS_GET_POWER_SAVE ,
MIPI_DCS_GET_CABC_MIN_BRIGHTNESS ,
MIPI_DCS_READ_DDB_START ,
MIPI_DCS_READ_DDB_CONTINUE ,
0 , /* sentinel */
} ;
2019-07-22 12:43:05 +02:00
static bool mipi_dbi_command_is_read ( struct mipi_dbi * dbi , u8 cmd )
2017-01-22 00:30:47 +01:00
{
unsigned int i ;
2019-07-22 12:43:05 +02:00
if ( ! dbi - > read_commands )
2017-01-22 00:30:47 +01:00
return false ;
for ( i = 0 ; i < 0xff ; i + + ) {
2019-07-22 12:43:05 +02:00
if ( ! dbi - > read_commands [ i ] )
2017-01-22 00:30:47 +01:00
return false ;
2019-07-22 12:43:05 +02:00
if ( cmd = = dbi - > read_commands [ i ] )
2017-01-22 00:30:47 +01:00
return true ;
}
return false ;
}
/**
* mipi_dbi_command_read - MIPI DCS read command
2019-07-22 12:43:05 +02:00
* @ dbi : MIPI DBI structure
2017-01-22 00:30:47 +01:00
* @ cmd : Command
* @ val : Value read
*
* Send MIPI DCS read command to the controller .
*
* Returns :
* Zero on success , negative error code on failure .
*/
2019-07-22 12:43:05 +02:00
int mipi_dbi_command_read ( struct mipi_dbi * dbi , u8 cmd , u8 * val )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:05 +02:00
if ( ! dbi - > read_commands )
2017-01-22 00:30:47 +01:00
return - EACCES ;
2019-07-22 12:43:05 +02:00
if ( ! mipi_dbi_command_is_read ( dbi , cmd ) )
2017-01-22 00:30:47 +01:00
return - EINVAL ;
2019-07-22 12:43:05 +02:00
return mipi_dbi_command_buf ( dbi , cmd , val , 1 ) ;
2017-01-22 00:30:47 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_command_read ) ;
/**
* mipi_dbi_command_buf - MIPI DCS command with parameter ( s ) in an array
2019-07-22 12:43:05 +02:00
* @ dbi : MIPI DBI structure
2017-01-22 00:30:47 +01:00
* @ cmd : Command
* @ data : Parameter buffer
* @ len : Buffer length
*
* Returns :
* Zero on success , negative error code on failure .
*/
2019-07-22 12:43:05 +02:00
int mipi_dbi_command_buf ( struct mipi_dbi * dbi , u8 cmd , u8 * data , size_t len )
2017-01-22 00:30:47 +01:00
{
2019-02-22 13:43:29 +01:00
u8 * cmdbuf ;
2017-01-22 00:30:47 +01:00
int ret ;
2019-02-22 13:43:29 +01:00
/* SPI requires dma-safe buffers */
cmdbuf = kmemdup ( & cmd , 1 , GFP_KERNEL ) ;
if ( ! cmdbuf )
return - ENOMEM ;
2019-07-22 12:43:05 +02:00
mutex_lock ( & dbi - > cmdlock ) ;
ret = dbi - > command ( dbi , cmdbuf , data , len ) ;
mutex_unlock ( & dbi - > cmdlock ) ;
2017-01-22 00:30:47 +01:00
2019-02-22 13:43:29 +01:00
kfree ( cmdbuf ) ;
2017-01-22 00:30:47 +01:00
return ret ;
}
EXPORT_SYMBOL ( mipi_dbi_command_buf ) ;
2019-02-22 13:43:29 +01:00
/* This should only be used by mipi_dbi_command() */
2020-03-16 17:42:49 +01:00
int mipi_dbi_command_stackbuf ( struct mipi_dbi * dbi , u8 cmd , const u8 * data ,
size_t len )
2019-02-22 13:43:29 +01:00
{
u8 * buf ;
int ret ;
buf = kmemdup ( data , len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
2019-07-22 12:43:05 +02:00
ret = mipi_dbi_command_buf ( dbi , cmd , buf , len ) ;
2019-02-22 13:43:29 +01:00
kfree ( buf ) ;
return ret ;
}
EXPORT_SYMBOL ( mipi_dbi_command_stackbuf ) ;
2017-11-19 14:12:07 -06:00
/**
* mipi_dbi_buf_copy - Copy a framebuffer , transforming it if necessary
* @ dst : The destination buffer
* @ fb : The source framebuffer
* @ clip : Clipping rectangle of the area to be copied
* @ swap : When true , swap MSB / LSB of 16 - bit values
*
* Returns :
* Zero on success , negative error code on failure .
*/
int mipi_dbi_buf_copy ( void * dst , struct drm_framebuffer * fb ,
2019-01-15 05:36:41 +01:00
struct drm_rect * clip , bool swap )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:08 +02:00
struct drm_gem_object * gem = drm_gem_fb_get_obj ( fb , 0 ) ;
struct drm_gem_cma_object * cma_obj = to_drm_gem_cma_obj ( gem ) ;
2017-01-22 00:30:47 +01:00
void * src = cma_obj - > vaddr ;
2021-07-16 16:07:57 +02:00
int ret ;
2017-01-22 00:30:47 +01:00
2021-07-16 16:07:57 +02:00
ret = drm_gem_fb_begin_cpu_access ( fb , DMA_FROM_DEVICE ) ;
if ( ret )
return ret ;
2017-01-22 00:30:47 +01:00
switch ( fb - > format - > format ) {
case DRM_FORMAT_RGB565 :
if ( swap )
2021-07-16 16:07:57 +02:00
drm_fb_swab ( dst , src , fb , clip , ! gem - > import_attach ) ;
2017-01-22 00:30:47 +01:00
else
2019-04-05 11:52:15 +02:00
drm_fb_memcpy ( dst , src , fb , clip ) ;
2017-01-22 00:30:47 +01:00
break ;
case DRM_FORMAT_XRGB8888 :
2019-04-05 11:52:15 +02:00
drm_fb_xrgb8888_to_rgb565 ( dst , src , fb , clip , swap ) ;
2017-01-22 00:30:47 +01:00
break ;
default :
2021-02-16 17:57:22 +02:00
drm_err_once ( fb - > dev , " Format is not supported: %p4cc \n " ,
& fb - > format - > format ) ;
2017-01-22 00:30:47 +01:00
return - EINVAL ;
}
2021-07-16 16:07:57 +02:00
drm_gem_fb_end_cpu_access ( fb , DMA_FROM_DEVICE ) ;
2017-01-22 00:30:47 +01:00
return ret ;
}
2017-11-19 14:12:07 -06:00
EXPORT_SYMBOL ( mipi_dbi_buf_copy ) ;
2017-01-22 00:30:47 +01:00
2020-01-15 13:45:46 +01:00
static void mipi_dbi_set_window_address ( struct mipi_dbi_dev * dbidev ,
unsigned int xs , unsigned int xe ,
unsigned int ys , unsigned int ye )
{
struct mipi_dbi * dbi = & dbidev - > dbi ;
xs + = dbidev - > left_offset ;
xe + = dbidev - > left_offset ;
ys + = dbidev - > top_offset ;
ye + = dbidev - > top_offset ;
mipi_dbi_command ( dbi , MIPI_DCS_SET_COLUMN_ADDRESS , ( xs > > 8 ) & 0xff ,
xs & 0xff , ( xe > > 8 ) & 0xff , xe & 0xff ) ;
mipi_dbi_command ( dbi , MIPI_DCS_SET_PAGE_ADDRESS , ( ys > > 8 ) & 0xff ,
ys & 0xff , ( ye > > 8 ) & 0xff , ye & 0xff ) ;
}
2019-01-15 05:36:42 +01:00
static void mipi_dbi_fb_dirty ( struct drm_framebuffer * fb , struct drm_rect * rect )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:08 +02:00
struct drm_gem_object * gem = drm_gem_fb_get_obj ( fb , 0 ) ;
struct drm_gem_cma_object * cma_obj = to_drm_gem_cma_obj ( gem ) ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( fb - > dev ) ;
2019-01-15 05:36:42 +01:00
unsigned int height = rect - > y2 - rect - > y1 ;
unsigned int width = rect - > x2 - rect - > x1 ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi * dbi = & dbidev - > dbi ;
2019-07-22 12:43:05 +02:00
bool swap = dbi - > swap_bytes ;
2019-02-25 15:42:32 +01:00
int idx , ret = 0 ;
2017-01-22 00:30:47 +01:00
bool full ;
void * tr ;
2020-06-12 18:00:55 +02:00
if ( WARN_ON ( ! fb ) )
2019-01-15 05:36:42 +01:00
return ;
2017-01-22 00:30:47 +01:00
2019-02-25 15:42:32 +01:00
if ( ! drm_dev_enter ( fb - > dev , & idx ) )
return ;
2019-01-15 05:36:42 +01:00
full = width = = fb - > width & & height = = fb - > height ;
2017-01-22 00:30:47 +01:00
2019-01-15 05:36:41 +01:00
DRM_DEBUG_KMS ( " Flushing [FB:%d] " DRM_RECT_FMT " \n " , fb - > base . id , DRM_RECT_ARG ( rect ) ) ;
2017-01-22 00:30:47 +01:00
2019-07-22 12:43:05 +02:00
if ( ! dbi - > dc | | ! full | | swap | |
2017-01-22 00:30:47 +01:00
fb - > format - > format = = DRM_FORMAT_XRGB8888 ) {
2019-07-22 12:43:06 +02:00
tr = dbidev - > tx_buf ;
ret = mipi_dbi_buf_copy ( dbidev - > tx_buf , fb , rect , swap ) ;
2017-01-22 00:30:47 +01:00
if ( ret )
2019-01-15 05:36:42 +01:00
goto err_msg ;
2017-01-22 00:30:47 +01:00
} else {
tr = cma_obj - > vaddr ;
}
2020-01-15 13:45:46 +01:00
mipi_dbi_set_window_address ( dbidev , rect - > x1 , rect - > x2 - 1 , rect - > y1 ,
rect - > y2 - 1 ) ;
2017-01-22 00:30:47 +01:00
2019-07-22 12:43:05 +02:00
ret = mipi_dbi_command_buf ( dbi , MIPI_DCS_WRITE_MEMORY_START , tr ,
2019-01-15 05:36:42 +01:00
width * height * 2 ) ;
err_msg :
if ( ret )
drm: core: Convert device logging to drm_* functions.
Convert device logging with dev_* functions into drm_* functions.
The patch has been generated with the coccinelle script below.
The script focuses on instances of dev_* functions where the drm device
context is clearly visible in its arguments.
@@expression E1; expression list E2; @@
-dev_warn(E1->dev, E2)
+drm_warn(E1, E2)
@@expression E1; expression list E2; @@
-dev_info(E1->dev, E2)
+drm_info(E1, E2)
@@expression E1; expression list E2; @@
-dev_err(E1->dev, E2)
+drm_err(E1, E2)
@@expression E1; expression list E2; @@
-dev_info_once(E1->dev, E2)
+drm_info_once(E1, E2)
@@expression E1; expression list E2; @@
-dev_notice_once(E1->dev, E2)
+drm_notice_once(E1, E2)
@@expression E1; expression list E2; @@
-dev_warn_once(E1->dev, E2)
+drm_warn_once(E1, E2)
@@expression E1; expression list E2; @@
-dev_err_once(E1->dev, E2)
+drm_err_once(E1, E2)
@@expression E1; expression list E2; @@
-dev_err_ratelimited(E1->dev, E2)
+drm_err_ratelimited(E1, E2)
@@expression E1; expression list E2; @@
-dev_dbg(E1->dev, E2)
+drm_dbg(E1, E2)
Signed-off-by: Suraj Upadhyay <usuraj35@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20200718150955.GA23103@blackclown
2020-07-18 20:39:55 +05:30
drm_err_once ( fb - > dev , " Failed to update display %d \n " , ret ) ;
2019-02-25 15:42:32 +01:00
drm_dev_exit ( idx ) ;
2017-01-22 00:30:47 +01:00
}
2019-01-15 05:36:42 +01:00
/**
* mipi_dbi_pipe_update - Display pipe update helper
* @ pipe : Simple display pipe
* @ old_state : Old plane state
*
* This function handles framebuffer flushing and vblank events . Drivers can use
* this as their & drm_simple_display_pipe_funcs - > update callback .
*/
void mipi_dbi_pipe_update ( struct drm_simple_display_pipe * pipe ,
struct drm_plane_state * old_state )
{
struct drm_plane_state * state = pipe - > plane . state ;
struct drm_rect rect ;
2020-06-12 18:00:55 +02:00
if ( ! pipe - > crtc . state - > active )
return ;
2019-01-15 05:36:42 +01:00
if ( drm_atomic_helper_damage_merged ( old_state , state , & rect ) )
mipi_dbi_fb_dirty ( state - > fb , & rect ) ;
}
EXPORT_SYMBOL ( mipi_dbi_pipe_update ) ;
2017-01-22 00:30:47 +01:00
2018-01-10 19:59:36 +01:00
/**
* mipi_dbi_enable_flush - MIPI DBI enable helper
2019-07-22 12:43:06 +02:00
* @ dbidev : MIPI DBI device structure
2018-07-10 17:05:18 +02:00
* @ crtc_state : CRTC state
* @ plane_state : Plane state
2018-01-10 19:59:36 +01:00
*
2020-06-12 18:00:55 +02:00
* Flushes the whole framebuffer and enables the backlight . Drivers can use this
* in their & drm_simple_display_pipe_funcs - > enable callback .
2019-01-15 05:36:42 +01:00
*
* Note : Drivers which don ' t use mipi_dbi_pipe_update ( ) because they have custom
* framebuffer flushing , can ' t use this function since they both use the same
* flushing code .
2018-01-10 19:59:36 +01:00
*/
2019-07-22 12:43:07 +02:00
void mipi_dbi_enable_flush ( struct mipi_dbi_dev * dbidev ,
2018-03-23 17:35:09 +02:00
struct drm_crtc_state * crtc_state ,
struct drm_plane_state * plane_state )
2018-01-10 19:59:36 +01:00
{
2018-03-23 17:35:09 +02:00
struct drm_framebuffer * fb = plane_state - > fb ;
2019-01-15 05:36:42 +01:00
struct drm_rect rect = {
. x1 = 0 ,
. x2 = fb - > width ,
. y1 = 0 ,
. y2 = fb - > height ,
} ;
2019-02-25 15:42:32 +01:00
int idx ;
2019-07-22 12:43:06 +02:00
if ( ! drm_dev_enter ( & dbidev - > drm , & idx ) )
2019-02-25 15:42:32 +01:00
return ;
2018-01-10 19:59:36 +01:00
2019-01-15 05:36:42 +01:00
mipi_dbi_fb_dirty ( fb , & rect ) ;
2019-07-22 12:43:06 +02:00
backlight_enable ( dbidev - > backlight ) ;
2019-02-25 15:42:32 +01:00
drm_dev_exit ( idx ) ;
2018-01-10 19:59:36 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_enable_flush ) ;
2019-07-22 12:43:07 +02:00
static void mipi_dbi_blank ( struct mipi_dbi_dev * dbidev )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:06 +02:00
struct drm_device * drm = & dbidev - > drm ;
2017-01-22 00:30:47 +01:00
u16 height = drm - > mode_config . min_height ;
u16 width = drm - > mode_config . min_width ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi * dbi = & dbidev - > dbi ;
2017-01-22 00:30:47 +01:00
size_t len = width * height * 2 ;
2019-02-25 15:42:32 +01:00
int idx ;
if ( ! drm_dev_enter ( drm , & idx ) )
return ;
2017-01-22 00:30:47 +01:00
2019-07-22 12:43:06 +02:00
memset ( dbidev - > tx_buf , 0 , len ) ;
2017-01-22 00:30:47 +01:00
2020-01-15 13:45:46 +01:00
mipi_dbi_set_window_address ( dbidev , 0 , width - 1 , 0 , height - 1 ) ;
2019-07-22 12:43:05 +02:00
mipi_dbi_command_buf ( dbi , MIPI_DCS_WRITE_MEMORY_START ,
2019-07-22 12:43:06 +02:00
( u8 * ) dbidev - > tx_buf , len ) ;
2019-02-25 15:42:32 +01:00
drm_dev_exit ( idx ) ;
2017-01-22 00:30:47 +01:00
}
/**
* mipi_dbi_pipe_disable - MIPI DBI pipe disable helper
* @ pipe : Display pipe
*
2018-01-10 19:59:38 +01:00
* This function disables backlight if present , if not the display memory is
* blanked . The regulator is disabled if in use . Drivers can use this as their
2017-01-22 00:30:47 +01:00
* & drm_simple_display_pipe_funcs - > disable callback .
*/
void mipi_dbi_pipe_disable ( struct drm_simple_display_pipe * pipe )
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( pipe - > crtc . dev ) ;
2017-01-22 00:30:47 +01:00
DRM_DEBUG_KMS ( " \n " ) ;
2019-07-22 12:43:06 +02:00
if ( dbidev - > backlight )
backlight_disable ( dbidev - > backlight ) ;
2017-01-22 00:30:47 +01:00
else
2019-07-22 12:43:06 +02:00
mipi_dbi_blank ( dbidev ) ;
2018-01-10 19:59:38 +01:00
2019-07-22 12:43:06 +02:00
if ( dbidev - > regulator )
regulator_disable ( dbidev - > regulator ) ;
2017-01-22 00:30:47 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_pipe_disable ) ;
2019-07-19 17:59:16 +02:00
static int mipi_dbi_connector_get_modes ( struct drm_connector * connector )
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( connector - > dev ) ;
2019-07-19 17:59:16 +02:00
struct drm_display_mode * mode ;
2019-07-22 12:43:06 +02:00
mode = drm_mode_duplicate ( connector - > dev , & dbidev - > mode ) ;
2019-07-19 17:59:16 +02:00
if ( ! mode ) {
DRM_ERROR ( " Failed to duplicate mode \n " ) ;
return 0 ;
}
if ( mode - > name [ 0 ] = = ' \0 ' )
drm_mode_set_name ( mode ) ;
mode - > type | = DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
if ( mode - > width_mm ) {
connector - > display_info . width_mm = mode - > width_mm ;
connector - > display_info . height_mm = mode - > height_mm ;
}
return 1 ;
}
static const struct drm_connector_helper_funcs mipi_dbi_connector_hfuncs = {
. get_modes = mipi_dbi_connector_get_modes ,
} ;
static const struct drm_connector_funcs mipi_dbi_connector_funcs = {
. reset = drm_atomic_helper_connector_reset ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = drm_connector_cleanup ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
} ;
static int mipi_dbi_rotate_mode ( struct drm_display_mode * mode ,
unsigned int rotation )
{
if ( rotation = = 0 | | rotation = = 180 ) {
return 0 ;
} else if ( rotation = = 90 | | rotation = = 270 ) {
swap ( mode - > hdisplay , mode - > vdisplay ) ;
swap ( mode - > hsync_start , mode - > vsync_start ) ;
swap ( mode - > hsync_end , mode - > vsync_end ) ;
swap ( mode - > htotal , mode - > vtotal ) ;
swap ( mode - > width_mm , mode - > height_mm ) ;
return 0 ;
} else {
return - EINVAL ;
}
}
2019-02-25 15:42:30 +01:00
static const struct drm_mode_config_funcs mipi_dbi_mode_config_funcs = {
. fb_create = drm_gem_fb_create_with_dirty ,
. atomic_check = drm_atomic_helper_check ,
. atomic_commit = drm_atomic_helper_commit ,
} ;
2017-01-22 00:30:47 +01:00
static const uint32_t mipi_dbi_formats [ ] = {
DRM_FORMAT_RGB565 ,
DRM_FORMAT_XRGB8888 ,
} ;
/**
2019-07-22 12:43:07 +02:00
* mipi_dbi_dev_init_with_formats - MIPI DBI device initialization with custom formats
2019-07-22 12:43:06 +02:00
* @ dbidev : MIPI DBI device structure to initialize
2019-02-25 15:42:30 +01:00
* @ funcs : Display pipe functions
2019-07-19 17:59:15 +02:00
* @ formats : Array of supported formats ( DRM_FORMAT \ _ \ * ) .
* @ format_count : Number of elements in @ formats
2017-01-22 00:30:47 +01:00
* @ mode : Display mode
* @ rotation : Initial rotation in degrees Counter Clock Wise
2019-07-19 17:59:15 +02:00
* @ tx_buf_size : Allocate a transmit buffer of this size .
2017-01-22 00:30:47 +01:00
*
2019-02-25 15:42:30 +01:00
* This function sets up a & drm_simple_display_pipe with a & drm_connector that
* has one fixed & drm_display_mode which is rotated according to @ rotation .
* This mode is used to set the mode config min / max width / height properties .
2017-01-22 00:30:47 +01:00
*
2019-07-22 12:43:07 +02:00
* Use mipi_dbi_dev_init ( ) if you don ' t need custom formats .
2019-07-19 17:59:15 +02:00
*
* Note :
* Some of the helper functions expects RGB565 to be the default format and the
* transmit buffer sized to fit that .
2017-01-22 00:30:47 +01:00
*
* Returns :
* Zero on success , negative error code on failure .
*/
2019-07-22 12:43:07 +02:00
int mipi_dbi_dev_init_with_formats ( struct mipi_dbi_dev * dbidev ,
const struct drm_simple_display_pipe_funcs * funcs ,
const uint32_t * formats , unsigned int format_count ,
const struct drm_display_mode * mode ,
unsigned int rotation , size_t tx_buf_size )
2017-01-22 00:30:47 +01:00
{
2019-07-19 17:59:16 +02:00
static const uint64_t modifiers [ ] = {
DRM_FORMAT_MOD_LINEAR ,
DRM_FORMAT_MOD_INVALID
} ;
2019-07-22 12:43:06 +02:00
struct drm_device * drm = & dbidev - > drm ;
2017-01-22 00:30:47 +01:00
int ret ;
2019-07-22 12:43:07 +02:00
if ( ! dbidev - > dbi . command )
2017-01-22 00:30:47 +01:00
return - EINVAL ;
2020-03-23 15:49:47 +01:00
ret = drmm_mode_config_init ( drm ) ;
2020-03-23 15:49:46 +01:00
if ( ret )
return ret ;
2019-07-22 12:43:06 +02:00
dbidev - > tx_buf = devm_kmalloc ( drm - > dev , tx_buf_size , GFP_KERNEL ) ;
if ( ! dbidev - > tx_buf )
2017-01-22 00:30:47 +01:00
return - ENOMEM ;
2019-07-22 12:43:06 +02:00
drm_mode_copy ( & dbidev - > mode , mode ) ;
ret = mipi_dbi_rotate_mode ( & dbidev - > mode , rotation ) ;
2019-07-19 17:59:16 +02:00
if ( ret ) {
DRM_ERROR ( " Illegal rotation value %u \n " , rotation ) ;
return - EINVAL ;
}
2019-07-22 12:43:06 +02:00
drm_connector_helper_add ( & dbidev - > connector , & mipi_dbi_connector_hfuncs ) ;
ret = drm_connector_init ( drm , & dbidev - > connector , & mipi_dbi_connector_funcs ,
2019-07-19 17:59:16 +02:00
DRM_MODE_CONNECTOR_SPI ) ;
if ( ret )
return ret ;
2019-07-22 12:43:06 +02:00
ret = drm_simple_display_pipe_init ( drm , & dbidev - > pipe , funcs , formats , format_count ,
modifiers , & dbidev - > connector ) ;
2017-01-22 00:30:47 +01:00
if ( ret )
return ret ;
2019-07-22 12:43:06 +02:00
drm_plane_enable_fb_damage_clips ( & dbidev - > pipe . plane ) ;
2019-01-15 05:36:42 +01:00
2019-02-25 15:42:30 +01:00
drm - > mode_config . funcs = & mipi_dbi_mode_config_funcs ;
2019-07-22 12:43:06 +02:00
drm - > mode_config . min_width = dbidev - > mode . hdisplay ;
drm - > mode_config . max_width = dbidev - > mode . hdisplay ;
drm - > mode_config . min_height = dbidev - > mode . vdisplay ;
drm - > mode_config . max_height = dbidev - > mode . vdisplay ;
dbidev - > rotation = rotation ;
2017-01-22 00:30:47 +01:00
2019-07-19 17:59:15 +02:00
DRM_DEBUG_KMS ( " rotation = %u \n " , rotation ) ;
2017-01-22 00:30:47 +01:00
return 0 ;
}
2019-07-22 12:43:07 +02:00
EXPORT_SYMBOL ( mipi_dbi_dev_init_with_formats ) ;
2019-07-19 17:59:15 +02:00
/**
2019-07-22 12:43:07 +02:00
* mipi_dbi_dev_init - MIPI DBI device initialization
2019-07-22 12:43:06 +02:00
* @ dbidev : MIPI DBI device structure to initialize
2019-07-19 17:59:15 +02:00
* @ funcs : Display pipe functions
* @ mode : Display mode
* @ rotation : Initial rotation in degrees Counter Clock Wise
*
* This function sets up a & drm_simple_display_pipe with a & drm_connector that
* has one fixed & drm_display_mode which is rotated according to @ rotation .
* This mode is used to set the mode config min / max width / height properties .
* Additionally & mipi_dbi . tx_buf is allocated .
*
* Supported formats : Native RGB565 and emulated XRGB8888 .
*
* Returns :
* Zero on success , negative error code on failure .
*/
2019-07-22 12:43:07 +02:00
int mipi_dbi_dev_init ( struct mipi_dbi_dev * dbidev ,
const struct drm_simple_display_pipe_funcs * funcs ,
const struct drm_display_mode * mode , unsigned int rotation )
2019-07-19 17:59:15 +02:00
{
size_t bufsize = mode - > vdisplay * mode - > hdisplay * sizeof ( u16 ) ;
2019-07-22 12:43:06 +02:00
dbidev - > drm . mode_config . preferred_depth = 16 ;
2019-07-19 17:59:15 +02:00
2019-07-22 12:43:07 +02:00
return mipi_dbi_dev_init_with_formats ( dbidev , funcs , mipi_dbi_formats ,
ARRAY_SIZE ( mipi_dbi_formats ) , mode ,
rotation , bufsize ) ;
2019-07-19 17:59:15 +02:00
}
2019-07-22 12:43:07 +02:00
EXPORT_SYMBOL ( mipi_dbi_dev_init ) ;
2017-01-22 00:30:47 +01:00
/**
* mipi_dbi_hw_reset - Hardware reset of controller
2019-07-22 12:43:05 +02:00
* @ dbi : MIPI DBI structure
2017-01-22 00:30:47 +01:00
*
* Reset controller if the & mipi_dbi - > reset gpio is set .
*/
2019-07-22 12:43:05 +02:00
void mipi_dbi_hw_reset ( struct mipi_dbi * dbi )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:05 +02:00
if ( ! dbi - > reset )
2017-01-22 00:30:47 +01:00
return ;
2019-07-22 12:43:05 +02:00
gpiod_set_value_cansleep ( dbi - > reset , 0 ) ;
2018-01-10 19:59:40 +01:00
usleep_range ( 20 , 1000 ) ;
2019-07-22 12:43:05 +02:00
gpiod_set_value_cansleep ( dbi - > reset , 1 ) ;
2017-01-22 00:30:47 +01:00
msleep ( 120 ) ;
}
EXPORT_SYMBOL ( mipi_dbi_hw_reset ) ;
/**
* mipi_dbi_display_is_on - Check if display is on
2019-07-22 12:43:05 +02:00
* @ dbi : MIPI DBI structure
2017-01-22 00:30:47 +01:00
*
* This function checks the Power Mode register ( if readable ) to see if
* display output is turned on . This can be used to see if the bootloader
* has already turned on the display avoiding flicker when the pipeline is
* enabled .
*
* Returns :
* true if the display can be verified to be on , false otherwise .
*/
2019-07-22 12:43:05 +02:00
bool mipi_dbi_display_is_on ( struct mipi_dbi * dbi )
2017-01-22 00:30:47 +01:00
{
u8 val ;
2019-07-22 12:43:05 +02:00
if ( mipi_dbi_command_read ( dbi , MIPI_DCS_GET_POWER_MODE , & val ) )
2017-01-22 00:30:47 +01:00
return false ;
val & = ~ DCS_POWER_MODE_RESERVED_MASK ;
2018-01-10 19:59:37 +01:00
/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
2017-01-22 00:30:47 +01:00
if ( val ! = ( DCS_POWER_MODE_DISPLAY |
DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE ) )
return false ;
DRM_DEBUG_DRIVER ( " Display is ON \n " ) ;
return true ;
}
EXPORT_SYMBOL ( mipi_dbi_display_is_on ) ;
2019-07-22 12:43:07 +02:00
static int mipi_dbi_poweron_reset_conditional ( struct mipi_dbi_dev * dbidev , bool cond )
2018-01-10 19:59:37 +01:00
{
2019-07-22 12:43:06 +02:00
struct device * dev = dbidev - > drm . dev ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi * dbi = & dbidev - > dbi ;
2018-01-10 19:59:37 +01:00
int ret ;
2019-07-22 12:43:06 +02:00
if ( dbidev - > regulator ) {
ret = regulator_enable ( dbidev - > regulator ) ;
2018-01-10 19:59:37 +01:00
if ( ret ) {
DRM_DEV_ERROR ( dev , " Failed to enable regulator (%d) \n " , ret ) ;
return ret ;
}
}
2019-07-22 12:43:05 +02:00
if ( cond & & mipi_dbi_display_is_on ( dbi ) )
2018-01-10 19:59:37 +01:00
return 1 ;
2019-07-22 12:43:05 +02:00
mipi_dbi_hw_reset ( dbi ) ;
ret = mipi_dbi_command ( dbi , MIPI_DCS_SOFT_RESET ) ;
2018-01-10 19:59:37 +01:00
if ( ret ) {
DRM_DEV_ERROR ( dev , " Failed to send reset command (%d) \n " , ret ) ;
2019-07-22 12:43:06 +02:00
if ( dbidev - > regulator )
regulator_disable ( dbidev - > regulator ) ;
2018-01-10 19:59:37 +01:00
return ret ;
}
/*
* If we did a hw reset , we know the controller is in Sleep mode and
* per MIPI DSC spec should wait 5 ms after soft reset . If we didn ' t ,
* we assume worst case and wait 120 ms .
*/
2019-07-22 12:43:05 +02:00
if ( dbi - > reset )
2018-01-10 19:59:37 +01:00
usleep_range ( 5000 , 20000 ) ;
else
msleep ( 120 ) ;
return 0 ;
}
/**
* mipi_dbi_poweron_reset - MIPI DBI poweron and reset
2019-07-22 12:43:06 +02:00
* @ dbidev : MIPI DBI device structure
2018-01-10 19:59:37 +01:00
*
* This function enables the regulator if used and does a hardware and software
* reset .
*
* Returns :
* Zero on success , or a negative error code .
*/
2019-07-22 12:43:07 +02:00
int mipi_dbi_poweron_reset ( struct mipi_dbi_dev * dbidev )
2018-01-10 19:59:37 +01:00
{
2019-07-22 12:43:06 +02:00
return mipi_dbi_poweron_reset_conditional ( dbidev , false ) ;
2018-01-10 19:59:37 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_poweron_reset ) ;
/**
* mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset
2019-07-22 12:43:06 +02:00
* @ dbidev : MIPI DBI device structure
2018-01-10 19:59:37 +01:00
*
* This function enables the regulator if used and if the display is off , it
* does a hardware and software reset . If mipi_dbi_display_is_on ( ) determines
* that the display is on , no reset is performed .
*
* Returns :
* Zero if the controller was reset , 1 if the display was already on , or a
* negative error code .
*/
2019-07-22 12:43:07 +02:00
int mipi_dbi_poweron_conditional_reset ( struct mipi_dbi_dev * dbidev )
2018-01-10 19:59:37 +01:00
{
2019-07-22 12:43:06 +02:00
return mipi_dbi_poweron_reset_conditional ( dbidev , true ) ;
2018-01-10 19:59:37 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_poweron_conditional_reset ) ;
2017-01-22 00:30:47 +01:00
# if IS_ENABLED(CONFIG_SPI)
2017-11-19 14:12:07 -06:00
/**
* mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
* @ spi : SPI device
* @ len : The transfer buffer length .
*
2017-01-22 00:30:47 +01:00
* Many controllers have a max speed of 10 MHz , but can be pushed way beyond
* that . Increase reliability by running pixel data at max speed and the rest
* at 10 MHz , preventing transfer glitches from messing up the init settings .
*/
2017-11-19 14:12:07 -06:00
u32 mipi_dbi_spi_cmd_max_speed ( struct spi_device * spi , size_t len )
2017-01-22 00:30:47 +01:00
{
if ( len > 64 )
return 0 ; /* use default */
return min_t ( u32 , 10000000 , spi - > max_speed_hz ) ;
}
2017-11-19 14:12:07 -06:00
EXPORT_SYMBOL ( mipi_dbi_spi_cmd_max_speed ) ;
2017-01-22 00:30:47 +01:00
2019-07-19 17:59:13 +02:00
static bool mipi_dbi_machine_little_endian ( void )
{
# if defined(__LITTLE_ENDIAN)
return true ;
# else
return false ;
# endif
}
2017-01-22 00:30:47 +01:00
/*
* MIPI DBI Type C Option 1
*
* If the SPI controller doesn ' t have 9 bits per word support ,
* use blocks of 9 bytes to send 8 x 9 - bit words using a 8 - bit SPI transfer .
* Pad partial blocks with MIPI_DCS_NOP ( zero ) .
* This is how the D / C bit ( x ) is added :
* x7654321
* 0x765432
* 10 x76543
* 210 x7654
* 3210 x765
* 43210 x76
* 543210 x7
* 6543210 x
* 76543210
*/
2019-07-22 12:43:05 +02:00
static int mipi_dbi_spi1e_transfer ( struct mipi_dbi * dbi , int dc ,
2017-01-22 00:30:47 +01:00
const void * buf , size_t len ,
unsigned int bpw )
{
2019-07-19 17:59:13 +02:00
bool swap_bytes = ( bpw = = 16 & & mipi_dbi_machine_little_endian ( ) ) ;
2019-07-22 12:43:05 +02:00
size_t chunk , max_chunk = dbi - > tx_buf9_len ;
struct spi_device * spi = dbi - > spi ;
2017-01-22 00:30:47 +01:00
struct spi_transfer tr = {
2019-07-22 12:43:05 +02:00
. tx_buf = dbi - > tx_buf9 ,
2017-01-22 00:30:47 +01:00
. bits_per_word = 8 ,
} ;
struct spi_message m ;
const u8 * src = buf ;
int i , ret ;
u8 * dst ;
2019-10-01 17:06:14 +03:00
if ( drm_debug_enabled ( DRM_UT_DRIVER ) )
2017-01-22 00:30:47 +01:00
pr_debug ( " [drm:%s] dc=%d, max_chunk=%zu, transfers: \n " ,
__func__ , dc , max_chunk ) ;
tr . speed_hz = mipi_dbi_spi_cmd_max_speed ( spi , len ) ;
spi_message_init_with_transfers ( & m , & tr , 1 ) ;
if ( ! dc ) {
if ( WARN_ON_ONCE ( len ! = 1 ) )
return - EINVAL ;
/* Command: pad no-op's (zeroes) at beginning of block */
2019-07-22 12:43:05 +02:00
dst = dbi - > tx_buf9 ;
2017-01-22 00:30:47 +01:00
memset ( dst , 0 , 9 ) ;
dst [ 8 ] = * src ;
tr . len = 9 ;
return spi_sync ( spi , & m ) ;
}
/* max with room for adding one bit per byte */
max_chunk = max_chunk / 9 * 8 ;
/* but no bigger than len */
max_chunk = min ( max_chunk , len ) ;
/* 8 byte blocks */
max_chunk = max_t ( size_t , 8 , max_chunk & ~ 0x7 ) ;
while ( len ) {
size_t added = 0 ;
chunk = min ( len , max_chunk ) ;
len - = chunk ;
2019-07-22 12:43:05 +02:00
dst = dbi - > tx_buf9 ;
2017-01-22 00:30:47 +01:00
if ( chunk < 8 ) {
u8 val , carry = 0 ;
/* Data: pad no-op's (zeroes) at end of block */
memset ( dst , 0 , 9 ) ;
if ( swap_bytes ) {
for ( i = 1 ; i < ( chunk + 1 ) ; i + + ) {
val = src [ 1 ] ;
* dst + + = carry | BIT ( 8 - i ) | ( val > > i ) ;
carry = val < < ( 8 - i ) ;
i + + ;
val = src [ 0 ] ;
* dst + + = carry | BIT ( 8 - i ) | ( val > > i ) ;
carry = val < < ( 8 - i ) ;
src + = 2 ;
}
* dst + + = carry ;
} else {
for ( i = 1 ; i < ( chunk + 1 ) ; i + + ) {
val = * src + + ;
* dst + + = carry | BIT ( 8 - i ) | ( val > > i ) ;
carry = val < < ( 8 - i ) ;
}
* dst + + = carry ;
}
chunk = 8 ;
added = 1 ;
} else {
for ( i = 0 ; i < chunk ; i + = 8 ) {
if ( swap_bytes ) {
* dst + + = BIT ( 7 ) | ( src [ 1 ] > > 1 ) ;
* dst + + = ( src [ 1 ] < < 7 ) | BIT ( 6 ) | ( src [ 0 ] > > 2 ) ;
* dst + + = ( src [ 0 ] < < 6 ) | BIT ( 5 ) | ( src [ 3 ] > > 3 ) ;
* dst + + = ( src [ 3 ] < < 5 ) | BIT ( 4 ) | ( src [ 2 ] > > 4 ) ;
* dst + + = ( src [ 2 ] < < 4 ) | BIT ( 3 ) | ( src [ 5 ] > > 5 ) ;
* dst + + = ( src [ 5 ] < < 3 ) | BIT ( 2 ) | ( src [ 4 ] > > 6 ) ;
* dst + + = ( src [ 4 ] < < 2 ) | BIT ( 1 ) | ( src [ 7 ] > > 7 ) ;
* dst + + = ( src [ 7 ] < < 1 ) | BIT ( 0 ) ;
* dst + + = src [ 6 ] ;
} else {
* dst + + = BIT ( 7 ) | ( src [ 0 ] > > 1 ) ;
* dst + + = ( src [ 0 ] < < 7 ) | BIT ( 6 ) | ( src [ 1 ] > > 2 ) ;
* dst + + = ( src [ 1 ] < < 6 ) | BIT ( 5 ) | ( src [ 2 ] > > 3 ) ;
* dst + + = ( src [ 2 ] < < 5 ) | BIT ( 4 ) | ( src [ 3 ] > > 4 ) ;
* dst + + = ( src [ 3 ] < < 4 ) | BIT ( 3 ) | ( src [ 4 ] > > 5 ) ;
* dst + + = ( src [ 4 ] < < 3 ) | BIT ( 2 ) | ( src [ 5 ] > > 6 ) ;
* dst + + = ( src [ 5 ] < < 2 ) | BIT ( 1 ) | ( src [ 6 ] > > 7 ) ;
* dst + + = ( src [ 6 ] < < 1 ) | BIT ( 0 ) ;
* dst + + = src [ 7 ] ;
}
src + = 8 ;
added + + ;
}
}
tr . len = chunk + added ;
ret = spi_sync ( spi , & m ) ;
if ( ret )
return ret ;
2017-03-12 22:46:36 +08:00
}
2017-01-22 00:30:47 +01:00
return 0 ;
}
2019-07-22 12:43:05 +02:00
static int mipi_dbi_spi1_transfer ( struct mipi_dbi * dbi , int dc ,
2017-01-22 00:30:47 +01:00
const void * buf , size_t len ,
unsigned int bpw )
{
2019-07-22 12:43:05 +02:00
struct spi_device * spi = dbi - > spi ;
2017-01-22 00:30:47 +01:00
struct spi_transfer tr = {
. bits_per_word = 9 ,
} ;
const u16 * src16 = buf ;
const u8 * src8 = buf ;
struct spi_message m ;
size_t max_chunk ;
u16 * dst16 ;
int ret ;
2019-07-19 17:59:08 +02:00
if ( ! spi_is_bpw_supported ( spi , 9 ) )
2019-07-22 12:43:05 +02:00
return mipi_dbi_spi1e_transfer ( dbi , dc , buf , len , bpw ) ;
2017-01-22 00:30:47 +01:00
tr . speed_hz = mipi_dbi_spi_cmd_max_speed ( spi , len ) ;
2019-07-22 12:43:05 +02:00
max_chunk = dbi - > tx_buf9_len ;
dst16 = dbi - > tx_buf9 ;
2017-01-22 00:30:47 +01:00
2019-10-01 17:06:14 +03:00
if ( drm_debug_enabled ( DRM_UT_DRIVER ) )
2017-01-22 00:30:47 +01:00
pr_debug ( " [drm:%s] dc=%d, max_chunk=%zu, transfers: \n " ,
__func__ , dc , max_chunk ) ;
max_chunk = min ( max_chunk / 2 , len ) ;
spi_message_init_with_transfers ( & m , & tr , 1 ) ;
tr . tx_buf = dst16 ;
while ( len ) {
size_t chunk = min ( len , max_chunk ) ;
unsigned int i ;
2019-07-19 17:59:13 +02:00
if ( bpw = = 16 & & mipi_dbi_machine_little_endian ( ) ) {
2017-01-22 00:30:47 +01:00
for ( i = 0 ; i < ( chunk * 2 ) ; i + = 2 ) {
dst16 [ i ] = * src16 > > 8 ;
dst16 [ i + 1 ] = * src16 + + & 0xFF ;
if ( dc ) {
dst16 [ i ] | = 0x0100 ;
dst16 [ i + 1 ] | = 0x0100 ;
}
}
} else {
for ( i = 0 ; i < chunk ; i + + ) {
dst16 [ i ] = * src8 + + ;
if ( dc )
dst16 [ i ] | = 0x0100 ;
}
}
2020-07-03 16:13:41 +02:00
tr . len = chunk * 2 ;
2017-01-22 00:30:47 +01:00
len - = chunk ;
ret = spi_sync ( spi , & m ) ;
if ( ret )
return ret ;
2017-03-12 22:46:36 +08:00
}
2017-01-22 00:30:47 +01:00
return 0 ;
}
2021-06-14 20:11:34 +02:00
static int mipi_dbi_typec1_command_read ( struct mipi_dbi * dbi , u8 * cmd ,
u8 * data , size_t len )
{
struct spi_device * spi = dbi - > spi ;
u32 speed_hz = min_t ( u32 , MIPI_DBI_MAX_SPI_READ_SPEED ,
spi - > max_speed_hz / 2 ) ;
struct spi_transfer tr [ 2 ] = {
{
. speed_hz = speed_hz ,
. bits_per_word = 9 ,
. tx_buf = dbi - > tx_buf9 ,
. len = 2 ,
} , {
. speed_hz = speed_hz ,
. bits_per_word = 8 ,
. len = len ,
. rx_buf = data ,
} ,
} ;
struct spi_message m ;
u16 * dst16 ;
int ret ;
if ( ! len )
return - EINVAL ;
if ( ! spi_is_bpw_supported ( spi , 9 ) ) {
/*
* FIXME : implement something like mipi_dbi_spi1e_transfer ( ) but
* for reads using emulation .
*/
dev_err ( & spi - > dev ,
" reading on host not supporting 9 bpw not yet implemented \n " ) ;
return - EOPNOTSUPP ;
}
/*
* Turn the 8 bit command into a 16 bit version of the command in the
* buffer . Only 9 bits of this will be used when executing the actual
* transfer .
*/
dst16 = dbi - > tx_buf9 ;
dst16 [ 0 ] = * cmd ;
spi_message_init_with_transfers ( & m , tr , ARRAY_SIZE ( tr ) ) ;
ret = spi_sync ( spi , & m ) ;
if ( ! ret )
MIPI_DBI_DEBUG_COMMAND ( * cmd , data , len ) ;
return ret ;
}
2019-07-22 12:43:05 +02:00
static int mipi_dbi_typec1_command ( struct mipi_dbi * dbi , u8 * cmd ,
2017-01-22 00:30:47 +01:00
u8 * parameters , size_t num )
{
2019-02-22 13:43:29 +01:00
unsigned int bpw = ( * cmd = = MIPI_DCS_WRITE_MEMORY_START ) ? 16 : 8 ;
2017-01-22 00:30:47 +01:00
int ret ;
2019-07-22 12:43:05 +02:00
if ( mipi_dbi_command_is_read ( dbi , * cmd ) )
2021-06-14 20:11:34 +02:00
return mipi_dbi_typec1_command_read ( dbi , cmd , parameters , num ) ;
2017-01-22 00:30:47 +01:00
2019-02-22 13:43:29 +01:00
MIPI_DBI_DEBUG_COMMAND ( * cmd , parameters , num ) ;
2017-01-22 00:30:47 +01:00
2019-07-22 12:43:05 +02:00
ret = mipi_dbi_spi1_transfer ( dbi , 0 , cmd , 1 , 8 ) ;
2017-01-22 00:30:47 +01:00
if ( ret | | ! num )
return ret ;
2019-07-22 12:43:05 +02:00
return mipi_dbi_spi1_transfer ( dbi , 1 , parameters , num , bpw ) ;
2017-01-22 00:30:47 +01:00
}
/* MIPI DBI Type C Option 3 */
2019-07-22 12:43:05 +02:00
static int mipi_dbi_typec3_command_read ( struct mipi_dbi * dbi , u8 * cmd ,
2017-01-22 00:30:47 +01:00
u8 * data , size_t len )
{
2019-07-22 12:43:05 +02:00
struct spi_device * spi = dbi - > spi ;
2017-01-22 00:30:47 +01:00
u32 speed_hz = min_t ( u32 , MIPI_DBI_MAX_SPI_READ_SPEED ,
spi - > max_speed_hz / 2 ) ;
struct spi_transfer tr [ 2 ] = {
{
. speed_hz = speed_hz ,
2019-02-22 13:43:29 +01:00
. tx_buf = cmd ,
2017-01-22 00:30:47 +01:00
. len = 1 ,
} , {
. speed_hz = speed_hz ,
. len = len ,
} ,
} ;
struct spi_message m ;
u8 * buf ;
int ret ;
if ( ! len )
return - EINVAL ;
/*
* Support non - standard 24 - bit and 32 - bit Nokia read commands which
* start with a dummy clock , so we need to read an extra byte .
*/
2019-02-22 13:43:29 +01:00
if ( * cmd = = MIPI_DCS_GET_DISPLAY_ID | |
* cmd = = MIPI_DCS_GET_DISPLAY_STATUS ) {
2017-01-22 00:30:47 +01:00
if ( ! ( len = = 3 | | len = = 4 ) )
return - EINVAL ;
tr [ 1 ] . len = len + 1 ;
}
buf = kmalloc ( tr [ 1 ] . len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
tr [ 1 ] . rx_buf = buf ;
2019-07-22 12:43:05 +02:00
gpiod_set_value_cansleep ( dbi - > dc , 0 ) ;
2017-01-22 00:30:47 +01:00
spi_message_init_with_transfers ( & m , tr , ARRAY_SIZE ( tr ) ) ;
ret = spi_sync ( spi , & m ) ;
if ( ret )
goto err_free ;
if ( tr [ 1 ] . len = = len ) {
memcpy ( data , buf , len ) ;
} else {
unsigned int i ;
for ( i = 0 ; i < len ; i + + )
2019-10-17 14:49:12 +03:00
data [ i ] = ( buf [ i ] < < 1 ) | ( buf [ i + 1 ] > > 7 ) ;
2017-01-22 00:30:47 +01:00
}
2019-02-22 13:43:29 +01:00
MIPI_DBI_DEBUG_COMMAND ( * cmd , data , len ) ;
2017-01-22 00:30:47 +01:00
err_free :
kfree ( buf ) ;
return ret ;
}
2019-07-22 12:43:05 +02:00
static int mipi_dbi_typec3_command ( struct mipi_dbi * dbi , u8 * cmd ,
2017-01-22 00:30:47 +01:00
u8 * par , size_t num )
{
2019-07-22 12:43:05 +02:00
struct spi_device * spi = dbi - > spi ;
2017-01-22 00:30:47 +01:00
unsigned int bpw = 8 ;
u32 speed_hz ;
int ret ;
2019-07-22 12:43:05 +02:00
if ( mipi_dbi_command_is_read ( dbi , * cmd ) )
return mipi_dbi_typec3_command_read ( dbi , cmd , par , num ) ;
2017-01-22 00:30:47 +01:00
2019-02-22 13:43:29 +01:00
MIPI_DBI_DEBUG_COMMAND ( * cmd , par , num ) ;
2017-01-22 00:30:47 +01:00
2019-07-22 12:43:05 +02:00
gpiod_set_value_cansleep ( dbi - > dc , 0 ) ;
2017-01-22 00:30:47 +01:00
speed_hz = mipi_dbi_spi_cmd_max_speed ( spi , 1 ) ;
2019-07-19 17:59:12 +02:00
ret = mipi_dbi_spi_transfer ( spi , speed_hz , 8 , cmd , 1 ) ;
2017-01-22 00:30:47 +01:00
if ( ret | | ! num )
return ret ;
2019-07-22 12:43:05 +02:00
if ( * cmd = = MIPI_DCS_WRITE_MEMORY_START & & ! dbi - > swap_bytes )
2017-01-22 00:30:47 +01:00
bpw = 16 ;
2019-07-22 12:43:05 +02:00
gpiod_set_value_cansleep ( dbi - > dc , 1 ) ;
2017-01-22 00:30:47 +01:00
speed_hz = mipi_dbi_spi_cmd_max_speed ( spi , num ) ;
2019-07-19 17:59:12 +02:00
return mipi_dbi_spi_transfer ( spi , speed_hz , bpw , par , num ) ;
2017-01-22 00:30:47 +01:00
}
/**
2019-07-22 12:43:05 +02:00
* mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
2017-01-22 00:30:47 +01:00
* @ spi : SPI device
2019-07-22 12:43:05 +02:00
* @ dbi : MIPI DBI structure to initialize
2017-08-03 17:33:45 -05:00
* @ dc : D / C gpio ( optional )
2017-01-22 00:30:47 +01:00
*
2019-07-22 12:43:05 +02:00
* This function sets & mipi_dbi - > command , enables & mipi_dbi - > read_commands for the
2019-07-22 12:43:07 +02:00
* usual read commands . It should be followed by a call to mipi_dbi_dev_init ( ) or
2017-08-03 17:33:45 -05:00
* a driver - specific init .
2017-01-22 00:30:47 +01:00
*
* If @ dc is set , a Type C Option 3 interface is assumed , if not
* Type C Option 1.
*
* If the SPI master driver doesn ' t support the necessary bits per word ,
* the following transformation is used :
*
* - 9 - bit : reorder buffer as 9 x 8 - bit words , padded with no - op command .
* - 16 - bit : if big endian send as 8 - bit , if little endian swap bytes
*
* Returns :
* Zero on success , negative error code on failure .
*/
2019-07-22 12:43:05 +02:00
int mipi_dbi_spi_init ( struct spi_device * spi , struct mipi_dbi * dbi ,
2017-08-03 17:33:45 -05:00
struct gpio_desc * dc )
2017-01-22 00:30:47 +01:00
{
struct device * dev = & spi - > dev ;
int ret ;
/*
* Even though it ' s not the SPI device that does DMA ( the master does ) ,
* the dma mask is necessary for the dma_alloc_wc ( ) in
* drm_gem_cma_create ( ) . The dma_addr returned will be a physical
2019-02-19 21:13:31 +03:00
* address which might be different from the bus address , but this is
2017-01-22 00:30:47 +01:00
* not a problem since the address will not be used .
* The virtual address is used in the transfer and the SPI core
* re - maps it on the SPI master device using the DMA streaming API
* ( spi_map_buf ( ) ) .
*/
if ( ! dev - > coherent_dma_mask ) {
ret = dma_coerce_mask_and_coherent ( dev , DMA_BIT_MASK ( 32 ) ) ;
if ( ret ) {
dev_warn ( dev , " Failed to set dma mask %d \n " , ret ) ;
return ret ;
}
}
2019-07-22 12:43:05 +02:00
dbi - > spi = spi ;
dbi - > read_commands = mipi_dbi_dcs_read_commands ;
2017-01-22 00:30:47 +01:00
if ( dc ) {
2019-07-22 12:43:05 +02:00
dbi - > command = mipi_dbi_typec3_command ;
dbi - > dc = dc ;
2019-07-19 17:59:13 +02:00
if ( mipi_dbi_machine_little_endian ( ) & & ! spi_is_bpw_supported ( spi , 16 ) )
2019-07-22 12:43:05 +02:00
dbi - > swap_bytes = true ;
2017-01-22 00:30:47 +01:00
} else {
2019-07-22 12:43:05 +02:00
dbi - > command = mipi_dbi_typec1_command ;
dbi - > tx_buf9_len = SZ_16K ;
dbi - > tx_buf9 = devm_kmalloc ( dev , dbi - > tx_buf9_len , GFP_KERNEL ) ;
if ( ! dbi - > tx_buf9 )
2017-01-22 00:30:47 +01:00
return - ENOMEM ;
}
2019-07-22 12:43:05 +02:00
mutex_init ( & dbi - > cmdlock ) ;
2019-07-22 12:43:04 +02:00
2017-09-08 17:07:26 +02:00
DRM_DEBUG_DRIVER ( " SPI speed: %uMHz \n " , spi - > max_speed_hz / 1000000 ) ;
2017-08-03 17:33:45 -05:00
return 0 ;
2017-01-22 00:30:47 +01:00
}
EXPORT_SYMBOL ( mipi_dbi_spi_init ) ;
2019-07-19 17:59:12 +02:00
/**
* mipi_dbi_spi_transfer - SPI transfer helper
* @ spi : SPI device
* @ speed_hz : Override speed ( optional )
* @ bpw : Bits per word
* @ buf : Buffer to transfer
* @ len : Buffer length
*
* This SPI transfer helper breaks up the transfer of @ buf into chunks which
* the SPI controller driver can handle .
*
* Returns :
* Zero on success , negative error code on failure .
*/
int mipi_dbi_spi_transfer ( struct spi_device * spi , u32 speed_hz ,
u8 bpw , const void * buf , size_t len )
{
size_t max_chunk = spi_max_transfer_size ( spi ) ;
struct spi_transfer tr = {
. bits_per_word = bpw ,
. speed_hz = speed_hz ,
} ;
struct spi_message m ;
size_t chunk ;
int ret ;
spi_message_init_with_transfers ( & m , & tr , 1 ) ;
while ( len ) {
chunk = min ( len , max_chunk ) ;
tr . tx_buf = buf ;
tr . len = chunk ;
buf + = chunk ;
len - = chunk ;
ret = spi_sync ( spi , & m ) ;
if ( ret )
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL ( mipi_dbi_spi_transfer ) ;
2017-01-22 00:30:47 +01:00
# endif /* CONFIG_SPI */
# ifdef CONFIG_DEBUG_FS
static ssize_t mipi_dbi_debugfs_command_write ( struct file * file ,
const char __user * ubuf ,
size_t count , loff_t * ppos )
{
struct seq_file * m = file - > private_data ;
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = m - > private ;
2017-02-23 14:29:55 +01:00
u8 val , cmd = 0 , parameters [ 64 ] ;
2017-01-22 00:30:47 +01:00
char * buf , * pos , * token ;
2019-08-21 10:24:56 +03:00
int i , ret , idx ;
2019-02-25 15:42:32 +01:00
2019-07-22 12:43:07 +02:00
if ( ! drm_dev_enter ( & dbidev - > drm , & idx ) )
2019-02-25 15:42:32 +01:00
return - ENODEV ;
2017-01-22 00:30:47 +01:00
buf = memdup_user_nul ( ubuf , count ) ;
2019-02-25 15:42:32 +01:00
if ( IS_ERR ( buf ) ) {
ret = PTR_ERR ( buf ) ;
goto err_exit ;
}
2017-01-22 00:30:47 +01:00
/* strip trailing whitespace */
for ( i = count - 1 ; i > 0 ; i - - )
if ( isspace ( buf [ i ] ) )
buf [ i ] = ' \0 ' ;
else
break ;
i = 0 ;
pos = buf ;
while ( pos ) {
token = strsep ( & pos , " " ) ;
if ( ! token ) {
ret = - EINVAL ;
goto err_free ;
}
ret = kstrtou8 ( token , 16 , & val ) ;
if ( ret < 0 )
goto err_free ;
if ( token = = buf )
cmd = val ;
else
parameters [ i + + ] = val ;
if ( i = = 64 ) {
ret = - E2BIG ;
goto err_free ;
}
}
2019-07-22 12:43:07 +02:00
ret = mipi_dbi_command_buf ( & dbidev - > dbi , cmd , parameters , i ) ;
2017-01-22 00:30:47 +01:00
err_free :
kfree ( buf ) ;
2019-02-25 15:42:32 +01:00
err_exit :
drm_dev_exit ( idx ) ;
2017-01-22 00:30:47 +01:00
return ret < 0 ? ret : count ;
}
static int mipi_dbi_debugfs_command_show ( struct seq_file * m , void * unused )
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = m - > private ;
struct mipi_dbi * dbi = & dbidev - > dbi ;
2017-01-22 00:30:47 +01:00
u8 cmd , val [ 4 ] ;
2019-02-25 15:42:32 +01:00
int ret , idx ;
2017-05-30 16:35:37 -07:00
size_t len ;
2019-02-25 15:42:32 +01:00
2019-07-22 12:43:07 +02:00
if ( ! drm_dev_enter ( & dbidev - > drm , & idx ) )
2019-02-25 15:42:32 +01:00
return - ENODEV ;
2017-01-22 00:30:47 +01:00
for ( cmd = 0 ; cmd < 255 ; cmd + + ) {
2019-07-22 12:43:07 +02:00
if ( ! mipi_dbi_command_is_read ( dbi , cmd ) )
2017-01-22 00:30:47 +01:00
continue ;
switch ( cmd ) {
case MIPI_DCS_READ_MEMORY_START :
case MIPI_DCS_READ_MEMORY_CONTINUE :
len = 2 ;
break ;
case MIPI_DCS_GET_DISPLAY_ID :
len = 3 ;
break ;
case MIPI_DCS_GET_DISPLAY_STATUS :
len = 4 ;
break ;
default :
len = 1 ;
break ;
}
seq_printf ( m , " %02x: " , cmd ) ;
2019-07-22 12:43:07 +02:00
ret = mipi_dbi_command_buf ( dbi , cmd , val , len ) ;
2017-01-22 00:30:47 +01:00
if ( ret ) {
seq_puts ( m , " XX \n " ) ;
continue ;
}
2017-05-30 16:35:37 -07:00
seq_printf ( m , " %*phN \n " , ( int ) len , val ) ;
2017-01-22 00:30:47 +01:00
}
2019-02-25 15:42:32 +01:00
drm_dev_exit ( idx ) ;
2017-01-22 00:30:47 +01:00
return 0 ;
}
static int mipi_dbi_debugfs_command_open ( struct inode * inode ,
struct file * file )
{
return single_open ( file , mipi_dbi_debugfs_command_show ,
inode - > i_private ) ;
}
static const struct file_operations mipi_dbi_debugfs_command_fops = {
. owner = THIS_MODULE ,
. open = mipi_dbi_debugfs_command_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. write = mipi_dbi_debugfs_command_write ,
} ;
/**
* mipi_dbi_debugfs_init - Create debugfs entries
* @ minor : DRM minor
*
* This function creates a ' command ' debugfs file for sending commands to the
* controller or getting the read command values .
* Drivers can use this as their & drm_driver - > debugfs_init callback .
*
*/
2020-03-10 16:31:21 +03:00
void mipi_dbi_debugfs_init ( struct drm_minor * minor )
2017-01-22 00:30:47 +01:00
{
2019-07-22 12:43:07 +02:00
struct mipi_dbi_dev * dbidev = drm_to_mipi_dbi_dev ( minor - > dev ) ;
2017-01-22 00:30:47 +01:00
umode_t mode = S_IFREG | S_IWUSR ;
2019-07-22 12:43:07 +02:00
if ( dbidev - > dbi . read_commands )
2017-01-22 00:30:47 +01:00
mode | = S_IRUGO ;
2019-07-22 12:43:07 +02:00
debugfs_create_file ( " command " , mode , minor - > debugfs_root , dbidev ,
2017-01-22 00:30:47 +01:00
& mipi_dbi_debugfs_command_fops ) ;
}
EXPORT_SYMBOL ( mipi_dbi_debugfs_init ) ;
# endif
MODULE_LICENSE ( " GPL " ) ;