2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-08-19 22:19:49 -04:00
/*
* Copyright 2015 Freescale Semiconductor , Inc .
*
* Freescale DCU drm device driver
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
2016-02-11 17:31:51 -08:00
# include <linux/console.h>
2015-08-19 22:19:49 -04:00
# include <linux/io.h>
# include <linux/mfd/syscon.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
2016-02-11 17:31:51 -08:00
# include <drm/drm_atomic_helper.h>
2019-06-30 08:18:57 +02:00
# include <drm/drm_drv.h>
2023-03-13 16:51:19 +01:00
# include <drm/drm_fbdev_dma.h>
2022-08-02 02:04:03 +02:00
# include <drm/drm_gem_dma_helper.h>
2017-11-14 22:25:15 +01:00
# include <drm/drm_modeset_helper.h>
2021-12-17 01:37:23 +01:00
# include <drm/drm_module.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2019-06-30 08:18:57 +02:00
# include <drm/drm_vblank.h>
2015-08-19 22:19:49 -04:00
# include "fsl_dcu_drm_crtc.h"
# include "fsl_dcu_drm_drv.h"
2015-12-02 14:39:40 -08:00
# include "fsl_tcon.h"
2015-08-19 22:19:49 -04:00
2016-11-16 17:23:27 -08:00
static int legacyfb_depth = 24 ;
module_param ( legacyfb_depth , int , 0444 ) ;
2015-11-17 18:05:25 -08:00
static bool fsl_dcu_drm_is_volatile_reg ( struct device * dev , unsigned int reg )
{
if ( reg = = DCU_INT_STATUS | | reg = = DCU_UPDATE_MODE )
return true ;
return false ;
}
2015-08-19 22:19:49 -04:00
static const struct regmap_config fsl_dcu_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
2015-11-17 18:05:25 -08:00
. volatile_reg = fsl_dcu_drm_is_volatile_reg ,
2015-08-19 22:19:49 -04:00
} ;
2021-08-03 11:06:54 +02:00
static void fsl_dcu_irq_reset ( struct drm_device * dev )
2015-08-19 22:19:49 -04:00
{
struct fsl_dcu_drm_device * fsl_dev = dev - > dev_private ;
2017-06-01 19:43:30 -07:00
regmap_write ( fsl_dev - > regmap , DCU_INT_STATUS , ~ 0 ) ;
2015-11-18 17:27:04 -08:00
regmap_write ( fsl_dev - > regmap , DCU_INT_MASK , ~ 0 ) ;
2015-08-19 22:19:49 -04:00
}
2021-08-03 11:06:54 +02:00
static irqreturn_t fsl_dcu_drm_irq ( int irq , void * arg )
{
struct drm_device * dev = arg ;
struct fsl_dcu_drm_device * fsl_dev = dev - > dev_private ;
unsigned int int_status ;
int ret ;
ret = regmap_read ( fsl_dev - > regmap , DCU_INT_STATUS , & int_status ) ;
if ( ret ) {
dev_err ( dev - > dev , " read DCU_INT_STATUS failed \n " ) ;
return IRQ_NONE ;
}
if ( int_status & DCU_INT_STATUS_VBLANK )
drm_handle_vblank ( dev , 0 ) ;
regmap_write ( fsl_dev - > regmap , DCU_INT_STATUS , int_status ) ;
return IRQ_HANDLED ;
}
static int fsl_dcu_irq_install ( struct drm_device * dev , unsigned int irq )
{
if ( irq = = IRQ_NOTCONNECTED )
return - ENOTCONN ;
fsl_dcu_irq_reset ( dev ) ;
return request_irq ( irq , fsl_dcu_drm_irq , 0 , dev - > driver - > name , dev ) ;
}
static void fsl_dcu_irq_uninstall ( struct drm_device * dev )
{
struct fsl_dcu_drm_device * fsl_dev = dev - > dev_private ;
fsl_dcu_irq_reset ( dev ) ;
free_irq ( fsl_dev - > irq , dev ) ;
}
2016-04-13 00:14:18 -07:00
static int fsl_dcu_load ( struct drm_device * dev , unsigned long flags )
2015-08-19 22:19:49 -04:00
{
2016-04-13 00:14:18 -07:00
struct fsl_dcu_drm_device * fsl_dev = dev - > dev_private ;
2015-08-19 22:19:49 -04:00
int ret ;
ret = fsl_dcu_drm_modeset_init ( fsl_dev ) ;
if ( ret < 0 ) {
2016-04-13 00:14:18 -07:00
dev_err ( dev - > dev , " failed to initialize mode setting \n " ) ;
2015-08-19 22:19:49 -04:00
return ret ;
}
2016-04-13 00:14:18 -07:00
ret = drm_vblank_init ( dev , dev - > mode_config . num_crtc ) ;
2015-08-19 22:19:49 -04:00
if ( ret < 0 ) {
2016-04-13 00:14:18 -07:00
dev_err ( dev - > dev , " failed to initialize vblank \n " ) ;
2021-08-03 11:06:54 +02:00
goto done_vblank ;
2015-08-19 22:19:49 -04:00
}
2021-08-03 11:06:54 +02:00
ret = fsl_dcu_irq_install ( dev , fsl_dev - > irq ) ;
2017-06-01 19:43:30 -07:00
if ( ret < 0 ) {
dev_err ( dev - > dev , " failed to install IRQ handler \n " ) ;
2021-08-03 11:06:54 +02:00
goto done_irq ;
2017-06-01 19:43:30 -07:00
}
2015-08-19 22:19:49 -04:00
2016-11-16 17:23:27 -08:00
if ( legacyfb_depth ! = 16 & & legacyfb_depth ! = 24 & &
legacyfb_depth ! = 32 ) {
dev_warn ( dev - > dev ,
" Invalid legacyfb_depth. Defaulting to 24bpp \n " ) ;
legacyfb_depth = 24 ;
}
2015-08-19 22:19:49 -04:00
return 0 ;
2021-08-03 11:06:54 +02:00
done_irq :
2016-04-16 21:17:12 -07:00
drm_kms_helper_poll_fini ( dev ) ;
2016-04-13 00:14:18 -07:00
drm_mode_config_cleanup ( dev ) ;
2021-08-03 11:06:54 +02:00
done_vblank :
2016-04-13 00:14:18 -07:00
dev - > dev_private = NULL ;
2015-08-19 22:19:49 -04:00
return ret ;
}
2017-01-06 15:57:31 -02:00
static void fsl_dcu_unload ( struct drm_device * dev )
2015-08-19 22:19:49 -04:00
{
2017-06-01 19:50:21 -07:00
drm_atomic_helper_shutdown ( dev ) ;
2016-04-16 21:17:12 -07:00
drm_kms_helper_poll_fini ( dev ) ;
2015-08-19 22:19:49 -04:00
drm_mode_config_cleanup ( dev ) ;
2021-08-03 11:06:54 +02:00
fsl_dcu_irq_uninstall ( dev ) ;
2015-08-19 22:19:49 -04:00
dev - > dev_private = NULL ;
}
2022-08-02 02:04:03 +02:00
DEFINE_DRM_GEM_DMA_FOPS ( fsl_dcu_drm_fops ) ;
2015-08-19 22:19:49 -04:00
2020-11-04 11:04:24 +01:00
static const struct drm_driver fsl_dcu_drm_driver = {
2019-06-17 17:39:24 +02:00
. driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC ,
2015-08-19 22:19:49 -04:00
. load = fsl_dcu_load ,
. unload = fsl_dcu_unload ,
2022-08-02 02:04:03 +02:00
DRM_GEM_DMA_DRIVER_OPS ,
2015-08-19 22:19:49 -04:00
. fops = & fsl_dcu_drm_fops ,
. name = " fsl-dcu-drm " ,
. desc = " Freescale DCU DRM " ,
2016-04-25 20:47:57 -07:00
. date = " 20160425 " ,
2015-08-19 22:19:49 -04:00
. major = 1 ,
2016-04-25 20:47:57 -07:00
. minor = 1 ,
2015-08-19 22:19:49 -04:00
} ;
# ifdef CONFIG_PM_SLEEP
static int fsl_dcu_drm_pm_suspend ( struct device * dev )
{
struct fsl_dcu_drm_device * fsl_dev = dev_get_drvdata ( dev ) ;
2017-11-14 22:25:15 +01:00
int ret ;
2015-08-19 22:19:49 -04:00
if ( ! fsl_dev )
return 0 ;
2016-02-11 17:31:51 -08:00
disable_irq ( fsl_dev - > irq ) ;
2017-11-14 22:25:15 +01:00
ret = drm_mode_config_helper_suspend ( fsl_dev - > drm ) ;
if ( ret ) {
2016-02-11 17:31:51 -08:00
enable_irq ( fsl_dev - > irq ) ;
2017-11-14 22:25:15 +01:00
return ret ;
2016-02-11 17:31:51 -08:00
}
2016-02-11 16:56:30 -08:00
clk_disable_unprepare ( fsl_dev - > clk ) ;
2015-08-19 22:19:49 -04:00
return 0 ;
}
static int fsl_dcu_drm_pm_resume ( struct device * dev )
{
struct fsl_dcu_drm_device * fsl_dev = dev_get_drvdata ( dev ) ;
int ret ;
if ( ! fsl_dev )
return 0 ;
2016-02-11 16:56:30 -08:00
ret = clk_prepare_enable ( fsl_dev - > clk ) ;
2015-08-19 22:19:49 -04:00
if ( ret < 0 ) {
dev_err ( dev , " failed to enable dcu clk \n " ) ;
return ret ;
}
2016-10-04 16:40:14 -07:00
if ( fsl_dev - > tcon )
fsl_tcon_bypass_enable ( fsl_dev - > tcon ) ;
2016-02-11 17:31:51 -08:00
fsl_dcu_drm_init_planes ( fsl_dev - > drm ) ;
2017-11-10 10:15:28 +01:00
enable_irq ( fsl_dev - > irq ) ;
2016-02-11 17:31:51 -08:00
2017-11-14 22:25:15 +01:00
drm_mode_config_helper_resume ( fsl_dev - > drm ) ;
2015-08-19 22:19:49 -04:00
return 0 ;
}
# endif
static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( fsl_dcu_drm_pm_suspend , fsl_dcu_drm_pm_resume )
} ;
static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
. name = " ls1021a " ,
. total_layer = 16 ,
. max_layer = 4 ,
2016-02-11 16:41:18 -08:00
. layer_regs = LS1021A_LAYER_REG_NUM ,
2015-08-19 22:19:49 -04:00
} ;
static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
. name = " vf610 " ,
. total_layer = 64 ,
. max_layer = 6 ,
2016-02-11 16:41:18 -08:00
. layer_regs = VF610_LAYER_REG_NUM ,
2015-08-19 22:19:49 -04:00
} ;
static const struct of_device_id fsl_dcu_of_match [ ] = {
{
. compatible = " fsl,ls1021a-dcu " ,
. data = & fsl_dcu_ls1021a_data ,
} , {
. compatible = " fsl,vf610-dcu " ,
. data = & fsl_dcu_vf610_data ,
} , {
} ,
} ;
MODULE_DEVICE_TABLE ( of , fsl_dcu_of_match ) ;
static int fsl_dcu_drm_probe ( struct platform_device * pdev )
{
struct fsl_dcu_drm_device * fsl_dev ;
struct drm_device * drm ;
struct device * dev = & pdev - > dev ;
struct resource * res ;
void __iomem * base ;
2016-03-22 18:06:08 -07:00
struct clk * pix_clk_in ;
char pix_clk_name [ 32 ] ;
const char * pix_clk_in_name ;
2015-08-19 22:19:49 -04:00
const struct of_device_id * id ;
int ret ;
2016-09-02 11:23:37 -07:00
u8 div_ratio_shift = 0 ;
2015-08-19 22:19:49 -04:00
fsl_dev = devm_kzalloc ( dev , sizeof ( * fsl_dev ) , GFP_KERNEL ) ;
if ( ! fsl_dev )
return - ENOMEM ;
2016-03-22 15:13:05 -07:00
id = of_match_node ( fsl_dcu_of_match , pdev - > dev . of_node ) ;
if ( ! id )
return - ENODEV ;
fsl_dev - > soc = id - > data ;
2015-08-19 22:19:49 -04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) ) {
ret = PTR_ERR ( base ) ;
return ret ;
}
fsl_dev - > irq = platform_get_irq ( pdev , 0 ) ;
if ( fsl_dev - > irq < 0 ) {
dev_err ( dev , " failed to get irq \n " ) ;
2016-11-16 13:38:33 -02:00
return fsl_dev - > irq ;
2015-08-19 22:19:49 -04:00
}
2016-03-22 15:13:05 -07:00
fsl_dev - > regmap = devm_regmap_init_mmio ( dev , base ,
& fsl_dcu_regmap_config ) ;
if ( IS_ERR ( fsl_dev - > regmap ) ) {
dev_err ( dev , " regmap init failed \n " ) ;
return PTR_ERR ( fsl_dev - > regmap ) ;
}
2015-08-19 22:19:49 -04:00
fsl_dev - > clk = devm_clk_get ( dev , " dcu " ) ;
if ( IS_ERR ( fsl_dev - > clk ) ) {
dev_err ( dev , " failed to get dcu clock \n " ) ;
2016-03-22 15:13:05 -07:00
return PTR_ERR ( fsl_dev - > clk ) ;
2015-08-19 22:19:49 -04:00
}
2016-03-22 15:13:05 -07:00
ret = clk_prepare_enable ( fsl_dev - > clk ) ;
2015-08-19 22:19:49 -04:00
if ( ret < 0 ) {
dev_err ( dev , " failed to enable dcu clk \n " ) ;
return ret ;
}
2016-03-22 18:06:08 -07:00
pix_clk_in = devm_clk_get ( dev , " pix " ) ;
if ( IS_ERR ( pix_clk_in ) ) {
/* legancy binding, use dcu clock as pixel clock input */
pix_clk_in = fsl_dev - > clk ;
}
2016-09-02 11:23:37 -07:00
if ( of_property_read_bool ( dev - > of_node , " big-endian " ) )
div_ratio_shift = 24 ;
2016-03-22 18:06:08 -07:00
pix_clk_in_name = __clk_get_name ( pix_clk_in ) ;
snprintf ( pix_clk_name , sizeof ( pix_clk_name ) , " %s_pix " , pix_clk_in_name ) ;
fsl_dev - > pix_clk = clk_register_divider ( dev , pix_clk_name ,
pix_clk_in_name , 0 , base + DCU_DIV_RATIO ,
2016-09-02 11:23:37 -07:00
div_ratio_shift , 8 , CLK_DIVIDER_ROUND_CLOSEST , NULL ) ;
2016-03-22 15:45:29 -07:00
if ( IS_ERR ( fsl_dev - > pix_clk ) ) {
2016-03-22 18:06:08 -07:00
dev_err ( dev , " failed to register pix clk \n " ) ;
ret = PTR_ERR ( fsl_dev - > pix_clk ) ;
goto disable_clk ;
2016-03-22 15:45:29 -07:00
}
2016-03-22 18:06:08 -07:00
2015-12-02 14:39:40 -08:00
fsl_dev - > tcon = fsl_tcon_init ( dev ) ;
2020-11-04 11:04:24 +01:00
drm = drm_dev_alloc ( & fsl_dcu_drm_driver , dev ) ;
2016-09-21 16:59:19 +02:00
if ( IS_ERR ( drm ) ) {
ret = PTR_ERR ( drm ) ;
2016-10-04 17:40:29 -07:00
goto unregister_pix_clk ;
2016-03-22 15:13:05 -07:00
}
2015-08-19 22:19:49 -04:00
fsl_dev - > dev = dev ;
fsl_dev - > drm = drm ;
fsl_dev - > np = dev - > of_node ;
drm - > dev_private = fsl_dev ;
dev_set_drvdata ( dev , fsl_dev ) ;
ret = drm_dev_register ( drm , 0 ) ;
if ( ret < 0 )
2018-09-26 13:43:12 +02:00
goto put ;
2015-08-19 22:19:49 -04:00
2023-03-13 16:51:19 +01:00
drm_fbdev_dma_setup ( drm , legacyfb_depth ) ;
2018-10-25 22:13:33 +02:00
2015-08-19 22:19:49 -04:00
return 0 ;
2018-09-26 13:43:12 +02:00
put :
drm_dev_put ( drm ) ;
2016-03-22 18:06:08 -07:00
unregister_pix_clk :
clk_unregister ( fsl_dev - > pix_clk ) ;
2016-03-22 15:13:05 -07:00
disable_clk :
clk_disable_unprepare ( fsl_dev - > clk ) ;
2015-08-19 22:19:49 -04:00
return ret ;
}
2023-05-07 18:25:42 +02:00
static void fsl_dcu_drm_remove ( struct platform_device * pdev )
2015-08-19 22:19:49 -04:00
{
struct fsl_dcu_drm_device * fsl_dev = platform_get_drvdata ( pdev ) ;
2016-12-13 18:54:49 +01:00
drm_dev_unregister ( fsl_dev - > drm ) ;
2018-09-26 13:43:12 +02:00
drm_dev_put ( fsl_dev - > drm ) ;
2016-03-22 15:13:05 -07:00
clk_disable_unprepare ( fsl_dev - > clk ) ;
2016-03-22 18:06:08 -07:00
clk_unregister ( fsl_dev - > pix_clk ) ;
2015-08-19 22:19:49 -04:00
}
2023-09-01 16:39:53 -07:00
static void fsl_dcu_drm_shutdown ( struct platform_device * pdev )
{
struct fsl_dcu_drm_device * fsl_dev = platform_get_drvdata ( pdev ) ;
drm_atomic_helper_shutdown ( fsl_dev - > drm ) ;
}
2015-08-19 22:19:49 -04:00
static struct platform_driver fsl_dcu_drm_platform_driver = {
. probe = fsl_dcu_drm_probe ,
2023-05-07 18:25:42 +02:00
. remove_new = fsl_dcu_drm_remove ,
2023-09-01 16:39:53 -07:00
. shutdown = fsl_dcu_drm_shutdown ,
2015-08-19 22:19:49 -04:00
. driver = {
. name = " fsl-dcu " ,
. pm = & fsl_dcu_drm_pm_ops ,
. of_match_table = fsl_dcu_of_match ,
} ,
} ;
2021-12-17 01:37:23 +01:00
drm_module_platform_driver ( fsl_dcu_drm_platform_driver ) ;
2015-08-19 22:19:49 -04:00
MODULE_DESCRIPTION ( " Freescale DCU DRM Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;