2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-02-10 11:42:32 -08:00
/*
* Copyright ( C ) 2016 Broadcom Limited
*/
/**
* DOC : VC4 DPI module
*
* The VC4 DPI hardware supports MIPI DPI type 4 and Nokia ViSSI
2017-02-27 12:11:43 -08:00
* signals . On BCM2835 , these can be routed out to GPIO0 - 27 with the
* ALT2 function .
2016-02-10 11:42:32 -08:00
*/
2017-05-18 13:29:38 +09:00
# include <drm/drm_atomic_helper.h>
2017-05-11 11:31:24 -07:00
# include <drm/drm_bridge.h>
2022-07-11 19:39:01 +02:00
# include <drm/drm_drv.h>
2017-05-18 13:29:38 +09:00
# include <drm/drm_edid.h>
2017-05-11 11:31:24 -07:00
# include <drm/drm_of.h>
2017-05-18 13:29:38 +09:00
# include <drm/drm_panel.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2020-03-05 16:59:46 +01:00
# include <drm/drm_simple_kms_helper.h>
2017-05-18 13:29:38 +09:00
# include <linux/clk.h>
# include <linux/component.h>
2022-06-30 22:51:13 +03:00
# include <linux/media-bus-format.h>
2023-07-14 11:45:34 -06:00
# include <linux/mod_devicetable.h>
# include <linux/platform_device.h>
2016-02-10 11:42:32 -08:00
# include "vc4_drv.h"
# include "vc4_regs.h"
# define DPI_C 0x00
# define DPI_OUTPUT_ENABLE_MODE BIT(16)
/* The order field takes the incoming 24 bit RGB from the pixel valve
* and shuffles the 3 channels .
*/
# define DPI_ORDER_MASK VC4_MASK(15, 14)
# define DPI_ORDER_SHIFT 14
# define DPI_ORDER_RGB 0
# define DPI_ORDER_BGR 1
# define DPI_ORDER_GRB 2
# define DPI_ORDER_BRG 3
/* The format field takes the ORDER-shuffled pixel valve data and
* formats it onto the output lines .
*/
# define DPI_FORMAT_MASK VC4_MASK(13, 11)
# define DPI_FORMAT_SHIFT 11
/* This define is named in the hardware, but actually just outputs 0. */
# define DPI_FORMAT_9BIT_666_RGB 0
/* Outputs 00000000rrrrrggggggbbbbb */
# define DPI_FORMAT_16BIT_565_RGB_1 1
/* Outputs 000rrrrr00gggggg000bbbbb */
# define DPI_FORMAT_16BIT_565_RGB_2 2
/* Outputs 00rrrrr000gggggg00bbbbb0 */
# define DPI_FORMAT_16BIT_565_RGB_3 3
/* Outputs 000000rrrrrrggggggbbbbbb */
# define DPI_FORMAT_18BIT_666_RGB_1 4
/* Outputs 00rrrrrr00gggggg00bbbbbb */
# define DPI_FORMAT_18BIT_666_RGB_2 5
/* Outputs rrrrrrrrggggggggbbbbbbbb */
# define DPI_FORMAT_24BIT_888_RGB 6
/* Reverses the polarity of the corresponding signal */
# define DPI_PIXEL_CLK_INVERT BIT(10)
# define DPI_HSYNC_INVERT BIT(9)
# define DPI_VSYNC_INVERT BIT(8)
# define DPI_OUTPUT_ENABLE_INVERT BIT(7)
/* Outputs the signal the falling clock edge instead of rising. */
# define DPI_HSYNC_NEGATE BIT(6)
# define DPI_VSYNC_NEGATE BIT(5)
# define DPI_OUTPUT_ENABLE_NEGATE BIT(4)
/* Disables the signal */
# define DPI_HSYNC_DISABLE BIT(3)
# define DPI_VSYNC_DISABLE BIT(2)
# define DPI_OUTPUT_ENABLE_DISABLE BIT(1)
/* Power gate to the device, full reset at 0 -> 1 transition */
# define DPI_ENABLE BIT(0)
/* All other registers besides DPI_C return the ID */
# define DPI_ID 0x04
# define DPI_ID_VALUE 0x00647069
/* General DPI hardware state. */
struct vc4_dpi {
2022-07-11 19:38:54 +02:00
struct vc4_encoder encoder ;
2016-02-10 11:42:32 -08:00
2022-07-11 19:38:54 +02:00
struct platform_device * pdev ;
2016-02-10 11:42:32 -08:00
void __iomem * regs ;
struct clk * pixel_clock ;
struct clk * core_clock ;
2019-02-20 13:03:38 -08:00
struct debugfs_regset32 regset ;
2016-02-10 11:42:32 -08:00
} ;
2023-03-06 11:46:42 +01:00
# define to_vc4_dpi(_encoder) \
container_of_const ( _encoder , struct vc4_dpi , encoder . base )
2016-02-10 11:42:32 -08:00
2022-12-01 16:11:49 +01:00
# define DPI_READ(offset) \
( { \
kunit_fail_current_test ( " Accessing a register in a unit test! \n " ) ; \
readl ( dpi - > regs + ( offset ) ) ; \
} )
# define DPI_WRITE(offset, val) \
do { \
kunit_fail_current_test ( " Accessing a register in a unit test! \n " ) ; \
writel ( val , dpi - > regs + ( offset ) ) ; \
} while ( 0 )
2022-07-11 19:38:54 +02:00
2019-02-20 13:03:38 -08:00
static const struct debugfs_reg32 dpi_regs [ ] = {
VC4_REG32 ( DPI_C ) ,
VC4_REG32 ( DPI_ID ) ,
2016-02-10 11:42:32 -08:00
} ;
static void vc4_dpi_encoder_disable ( struct drm_encoder * encoder )
{
2022-07-11 19:39:01 +02:00
struct drm_device * dev = encoder - > dev ;
2022-07-11 19:38:54 +02:00
struct vc4_dpi * dpi = to_vc4_dpi ( encoder ) ;
2022-07-11 19:39:01 +02:00
int idx ;
if ( ! drm_dev_enter ( dev , & idx ) )
return ;
2016-02-10 11:42:32 -08:00
clk_disable_unprepare ( dpi - > pixel_clock ) ;
2022-07-11 19:39:01 +02:00
drm_dev_exit ( idx ) ;
2016-02-10 11:42:32 -08:00
}
static void vc4_dpi_encoder_enable ( struct drm_encoder * encoder )
{
2018-03-09 15:32:56 -08:00
struct drm_device * dev = encoder - > dev ;
2016-02-10 11:42:32 -08:00
struct drm_display_mode * mode = & encoder - > crtc - > mode ;
2022-07-11 19:38:54 +02:00
struct vc4_dpi * dpi = to_vc4_dpi ( encoder ) ;
2018-03-09 15:32:56 -08:00
struct drm_connector_list_iter conn_iter ;
struct drm_connector * connector = NULL , * connector_scan ;
2022-06-13 16:47:35 +02:00
u32 dpi_c = DPI_ENABLE ;
2022-07-11 19:39:01 +02:00
int idx ;
2016-02-10 11:42:32 -08:00
int ret ;
2018-03-09 15:32:56 -08:00
/* Look up the connector attached to DPI so we can get the
* bus_format . Ideally the bridge would tell us the
* bus_format we want , but it doesn ' t yet , so assume that it ' s
* uniform throughout the bridge chain .
*/
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
drm_for_each_connector_iter ( connector_scan , & conn_iter ) {
if ( connector_scan - > encoder = = encoder ) {
connector = connector_scan ;
break ;
}
}
drm_connector_list_iter_end ( & conn_iter ) ;
2022-12-01 09:42:51 +01:00
/* Default to 18bit if no connector or format found. */
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_18BIT_666_RGB_1 , DPI_FORMAT ) ;
2022-06-13 16:47:37 +02:00
2022-06-13 16:47:36 +02:00
if ( connector ) {
if ( connector - > display_info . num_bus_formats ) {
u32 bus_format = connector - > display_info . bus_formats [ 0 ] ;
2022-06-13 16:47:37 +02:00
dpi_c & = ~ DPI_FORMAT_MASK ;
2022-06-13 16:47:36 +02:00
switch ( bus_format ) {
case MEDIA_BUS_FMT_RGB888_1X24 :
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_24BIT_888_RGB ,
DPI_FORMAT ) ;
break ;
case MEDIA_BUS_FMT_BGR888_1X24 :
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_24BIT_888_RGB ,
DPI_FORMAT ) ;
dpi_c | = VC4_SET_FIELD ( DPI_ORDER_BGR ,
DPI_ORDER ) ;
break ;
2022-12-01 09:42:50 +01:00
case MEDIA_BUS_FMT_BGR666_1X24_CPADHI :
dpi_c | = VC4_SET_FIELD ( DPI_ORDER_BGR , DPI_ORDER ) ;
fallthrough ;
2022-06-13 16:47:36 +02:00
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI :
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_18BIT_666_RGB_2 ,
DPI_FORMAT ) ;
break ;
2022-12-01 09:42:50 +01:00
case MEDIA_BUS_FMT_BGR666_1X18 :
dpi_c | = VC4_SET_FIELD ( DPI_ORDER_BGR , DPI_ORDER ) ;
fallthrough ;
2022-06-13 16:47:36 +02:00
case MEDIA_BUS_FMT_RGB666_1X18 :
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_18BIT_666_RGB_1 ,
DPI_FORMAT ) ;
break ;
case MEDIA_BUS_FMT_RGB565_1X16 :
2022-12-01 09:42:52 +01:00
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_16BIT_565_RGB_1 ,
2022-06-13 16:47:36 +02:00
DPI_FORMAT ) ;
break ;
2022-12-01 09:42:49 +01:00
case MEDIA_BUS_FMT_RGB565_1X24_CPADHI :
dpi_c | = VC4_SET_FIELD ( DPI_FORMAT_16BIT_565_RGB_2 ,
DPI_FORMAT ) ;
break ;
2022-06-13 16:47:36 +02:00
default :
DRM_ERROR ( " Unknown media bus format %d \n " ,
bus_format ) ;
break ;
}
2016-02-10 11:42:32 -08:00
}
2022-06-13 16:47:36 +02:00
if ( connector - > display_info . bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE )
dpi_c | = DPI_PIXEL_CLK_INVERT ;
if ( connector - > display_info . bus_flags & DRM_BUS_FLAG_DE_LOW )
dpi_c | = DPI_OUTPUT_ENABLE_INVERT ;
2016-02-10 11:42:32 -08:00
}
2022-06-13 16:47:35 +02:00
if ( mode - > flags & DRM_MODE_FLAG_CSYNC ) {
if ( mode - > flags & DRM_MODE_FLAG_NCSYNC )
dpi_c | = DPI_OUTPUT_ENABLE_INVERT ;
} else {
dpi_c | = DPI_OUTPUT_ENABLE_MODE ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC )
dpi_c | = DPI_HSYNC_INVERT ;
else if ( ! ( mode - > flags & DRM_MODE_FLAG_PHSYNC ) )
dpi_c | = DPI_HSYNC_DISABLE ;
2016-02-10 11:42:32 -08:00
2022-06-13 16:47:35 +02:00
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
dpi_c | = DPI_VSYNC_INVERT ;
else if ( ! ( mode - > flags & DRM_MODE_FLAG_PVSYNC ) )
dpi_c | = DPI_VSYNC_DISABLE ;
}
2016-02-10 11:42:32 -08:00
2022-07-11 19:39:01 +02:00
if ( ! drm_dev_enter ( dev , & idx ) )
return ;
2016-02-10 11:42:32 -08:00
DPI_WRITE ( DPI_C , dpi_c ) ;
ret = clk_set_rate ( dpi - > pixel_clock , mode - > clock * 1000 ) ;
if ( ret )
DRM_ERROR ( " Failed to set clock rate: %d \n " , ret ) ;
ret = clk_prepare_enable ( dpi - > pixel_clock ) ;
if ( ret )
DRM_ERROR ( " Failed to set clock rate: %d \n " , ret ) ;
2022-07-11 19:39:01 +02:00
drm_dev_exit ( idx ) ;
2016-02-10 11:42:32 -08:00
}
2017-05-25 15:19:22 +01:00
static enum drm_mode_status vc4_dpi_encoder_mode_valid ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode )
2016-07-19 20:58:57 +02:00
{
2017-05-25 15:19:22 +01:00
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
return MODE_NO_INTERLACE ;
2016-07-19 20:58:57 +02:00
2017-05-25 15:19:22 +01:00
return MODE_OK ;
2016-07-19 20:58:57 +02:00
}
2016-02-10 11:42:32 -08:00
static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {
. disable = vc4_dpi_encoder_disable ,
. enable = vc4_dpi_encoder_enable ,
2017-05-25 15:19:22 +01:00
. mode_valid = vc4_dpi_encoder_mode_valid ,
2016-02-10 11:42:32 -08:00
} ;
2022-07-11 19:39:34 +02:00
static int vc4_dpi_late_register ( struct drm_encoder * encoder )
{
struct drm_device * drm = encoder - > dev ;
struct vc4_dpi * dpi = to_vc4_dpi ( encoder ) ;
2022-12-19 09:06:18 -03:00
vc4_debugfs_add_regset32 ( drm , " dpi_regs " , & dpi - > regset ) ;
2022-07-11 19:39:34 +02:00
return 0 ;
}
static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
. late_register = vc4_dpi_late_register ,
} ;
2016-02-10 11:42:32 -08:00
static const struct of_device_id vc4_dpi_dt_match [ ] = {
{ . compatible = " brcm,bcm2835-dpi " , . data = NULL } ,
{ }
} ;
2017-05-11 11:31:24 -07:00
/* Sets up the next link in the display chain, whether it's a panel or
* a bridge .
2016-02-10 11:42:32 -08:00
*/
2017-05-11 11:31:24 -07:00
static int vc4_dpi_init_bridge ( struct vc4_dpi * dpi )
2016-02-10 11:42:32 -08:00
{
2022-07-11 19:39:00 +02:00
struct drm_device * drm = dpi - > encoder . base . dev ;
2017-05-11 11:31:24 -07:00
struct device * dev = & dpi - > pdev - > dev ;
2017-10-02 11:34:47 +02:00
struct drm_bridge * bridge ;
2016-02-10 11:42:32 -08:00
2022-07-11 19:39:00 +02:00
bridge = drmm_of_get_bridge ( drm , dev - > of_node , 0 , 0 ) ;
2021-09-10 15:09:40 +02:00
if ( IS_ERR ( bridge ) ) {
2017-05-11 11:31:24 -07:00
/* If nothing was connected in the DT, that's not an
* error .
*/
2021-09-10 15:09:40 +02:00
if ( PTR_ERR ( bridge ) = = - ENODEV )
2017-05-11 11:31:24 -07:00
return 0 ;
else
2021-09-10 15:09:40 +02:00
return PTR_ERR ( bridge ) ;
2017-05-11 11:31:24 -07:00
}
2016-02-10 11:42:32 -08:00
2022-07-11 19:38:54 +02:00
return drm_bridge_attach ( & dpi - > encoder . base , bridge , NULL , 0 ) ;
2016-02-10 11:42:32 -08:00
}
2022-07-11 19:38:58 +02:00
static void vc4_dpi_disable_clock ( void * ptr )
{
struct vc4_dpi * dpi = ptr ;
clk_disable_unprepare ( dpi - > core_clock ) ;
}
2016-02-10 11:42:32 -08:00
static int vc4_dpi_bind ( struct device * dev , struct device * master , void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct drm_device * drm = dev_get_drvdata ( master ) ;
struct vc4_dpi * dpi ;
int ret ;
2022-07-11 19:38:55 +02:00
dpi = drmm_kzalloc ( drm , sizeof ( * dpi ) , GFP_KERNEL ) ;
2016-02-10 11:42:32 -08:00
if ( ! dpi )
return - ENOMEM ;
2022-07-11 19:38:55 +02:00
2022-07-11 19:38:54 +02:00
dpi - > encoder . type = VC4_ENCODER_TYPE_DPI ;
2016-02-10 11:42:32 -08:00
dpi - > pdev = pdev ;
dpi - > regs = vc4_ioremap_regs ( pdev , 0 ) ;
if ( IS_ERR ( dpi - > regs ) )
return PTR_ERR ( dpi - > regs ) ;
2019-02-20 13:03:38 -08:00
dpi - > regset . base = dpi - > regs ;
dpi - > regset . regs = dpi_regs ;
dpi - > regset . nregs = ARRAY_SIZE ( dpi_regs ) ;
2016-02-10 11:42:32 -08:00
if ( DPI_READ ( DPI_ID ) ! = DPI_ID_VALUE ) {
dev_err ( dev , " Port returned 0x%08x for ID instead of 0x%08x \n " ,
DPI_READ ( DPI_ID ) , DPI_ID_VALUE ) ;
return - ENODEV ;
}
dpi - > core_clock = devm_clk_get ( dev , " core " ) ;
if ( IS_ERR ( dpi - > core_clock ) ) {
ret = PTR_ERR ( dpi - > core_clock ) ;
if ( ret ! = - EPROBE_DEFER )
DRM_ERROR ( " Failed to get core clock: %d \n " , ret ) ;
return ret ;
}
2022-07-11 19:38:56 +02:00
2016-02-10 11:42:32 -08:00
dpi - > pixel_clock = devm_clk_get ( dev , " pixel " ) ;
if ( IS_ERR ( dpi - > pixel_clock ) ) {
ret = PTR_ERR ( dpi - > pixel_clock ) ;
if ( ret ! = - EPROBE_DEFER )
DRM_ERROR ( " Failed to get pixel clock: %d \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( dpi - > core_clock ) ;
2022-07-11 19:38:56 +02:00
if ( ret ) {
2016-02-10 11:42:32 -08:00
DRM_ERROR ( " Failed to turn on core clock: %d \n " , ret ) ;
2022-07-11 19:38:56 +02:00
return ret ;
}
2016-02-10 11:42:32 -08:00
2022-07-11 19:38:58 +02:00
ret = devm_add_action_or_reset ( dev , vc4_dpi_disable_clock , dpi ) ;
if ( ret )
return ret ;
2022-07-11 19:38:59 +02:00
ret = drmm_encoder_init ( drm , & dpi - > encoder . base ,
2022-07-11 19:39:34 +02:00
& vc4_dpi_encoder_funcs ,
2022-07-11 19:38:59 +02:00
DRM_MODE_ENCODER_DPI ,
NULL ) ;
if ( ret )
return ret ;
2022-07-11 19:38:54 +02:00
drm_encoder_helper_add ( & dpi - > encoder . base , & vc4_dpi_encoder_helper_funcs ) ;
2016-02-10 11:42:32 -08:00
2017-05-11 11:31:24 -07:00
ret = vc4_dpi_init_bridge ( dpi ) ;
if ( ret )
2022-07-11 19:38:59 +02:00
return ret ;
2016-02-10 11:42:32 -08:00
dev_set_drvdata ( dev , dpi ) ;
return 0 ;
}
static const struct component_ops vc4_dpi_ops = {
. bind = vc4_dpi_bind ,
} ;
static int vc4_dpi_dev_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & vc4_dpi_ops ) ;
}
2023-05-07 18:26:15 +02:00
static void vc4_dpi_dev_remove ( struct platform_device * pdev )
2016-02-10 11:42:32 -08:00
{
component_del ( & pdev - > dev , & vc4_dpi_ops ) ;
}
struct platform_driver vc4_dpi_driver = {
. probe = vc4_dpi_dev_probe ,
2023-05-07 18:26:15 +02:00
. remove_new = vc4_dpi_dev_remove ,
2016-02-10 11:42:32 -08:00
. driver = {
. name = " vc4_dpi " ,
. of_match_table = vc4_dpi_dt_match ,
} ,
} ;