2013-06-19 13:54:11 +02:00
/*
* rcar_du_drv . c - - R - Car Display Unit DRM driver
*
2015-09-07 17:34:26 +03:00
* Copyright ( C ) 2013 - 2015 Renesas Electronics Corporation
2013-06-19 13:54:11 +02:00
*
* Contact : Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/mm.h>
# include <linux/module.h>
2014-01-21 15:57:26 +01:00
# include <linux/of_device.h>
2013-06-19 13:54:11 +02:00
# include <linux/platform_device.h>
# include <linux/pm.h>
# include <linux/slab.h>
2015-02-23 01:02:15 +02:00
# include <linux/wait.h>
2013-06-19 13:54:11 +02:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2013-03-14 22:45:22 +01:00
# include <drm/drm_fb_cma_helper.h>
2013-06-19 13:54:11 +02:00
# include <drm/drm_gem_cma_helper.h>
# include "rcar_du_drv.h"
# include "rcar_du_kms.h"
# include "rcar_du_regs.h"
2014-01-21 15:57:26 +01:00
/* -----------------------------------------------------------------------------
* Device Information
*/
static const struct rcar_du_device_info rcar_du_r8a7779_info = {
2015-09-07 17:34:26 +03:00
. gen = 2 ,
2014-01-21 15:57:26 +01:00
. features = 0 ,
. num_crtcs = 2 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
* R8A7779 has two RGB outputs and one ( currently unsupported )
2014-01-21 15:57:26 +01:00
* TCON output .
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 0 ,
} ,
[ RCAR_DU_OUTPUT_DPAD1 ] = {
. possible_crtcs = BIT ( 1 ) | BIT ( 0 ) ,
. port = 1 ,
} ,
} ,
. num_lvds = 0 ,
} ;
static const struct rcar_du_device_info rcar_du_r8a7790_info = {
2015-09-07 17:34:26 +03:00
. gen = 2 ,
2014-12-09 00:21:12 +02:00
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS ,
2014-01-21 15:57:26 +01:00
. quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES ,
. num_crtcs = 3 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
* R8A7790 has one RGB output , two LVDS outputs and one
2014-01-21 15:57:26 +01:00
* ( currently unsupported ) TCON output .
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 2 ) | BIT ( 1 ) | BIT ( 0 ) ,
. port = 0 ,
} ,
[ RCAR_DU_OUTPUT_LVDS0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 1 ,
} ,
[ RCAR_DU_OUTPUT_LVDS1 ] = {
. possible_crtcs = BIT ( 2 ) | BIT ( 1 ) ,
. port = 2 ,
} ,
} ,
. num_lvds = 2 ,
} ;
2015-07-17 10:44:33 +03:00
/* M2-W (r8a7791) and M2-N (r8a7793) are identical */
2014-01-21 15:57:26 +01:00
static const struct rcar_du_device_info rcar_du_r8a7791_info = {
2015-09-07 17:34:26 +03:00
. gen = 2 ,
2014-12-09 00:21:12 +02:00
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS ,
2014-01-21 15:57:26 +01:00
. num_crtcs = 2 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
* R8A779 [ 13 ] has one RGB output , one LVDS output and one
2014-01-21 15:57:26 +01:00
* ( currently unsupported ) TCON output .
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
2015-04-28 15:26:33 +03:00
. possible_crtcs = BIT ( 1 ) | BIT ( 0 ) ,
2014-01-21 15:57:26 +01:00
. port = 0 ,
} ,
[ RCAR_DU_OUTPUT_LVDS0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 1 ,
} ,
} ,
. num_lvds = 1 ,
} ;
2016-08-04 15:01:02 -07:00
static const struct rcar_du_device_info rcar_du_r8a7792_info = {
. gen = 2 ,
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS ,
. num_crtcs = 2 ,
. routes = {
/* R8A7792 has two RGB outputs. */
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 0 ,
} ,
[ RCAR_DU_OUTPUT_DPAD1 ] = {
. possible_crtcs = BIT ( 1 ) ,
. port = 1 ,
} ,
} ,
. num_lvds = 0 ,
} ;
2015-07-17 10:44:33 +03:00
static const struct rcar_du_device_info rcar_du_r8a7794_info = {
2015-09-07 17:34:26 +03:00
. gen = 2 ,
2015-07-17 10:44:33 +03:00
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS ,
. num_crtcs = 2 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
* R8A7794 has two RGB outputs and one ( currently unsupported )
2015-07-17 10:44:33 +03:00
* TCON output .
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 0 ,
} ,
[ RCAR_DU_OUTPUT_DPAD1 ] = {
. possible_crtcs = BIT ( 1 ) ,
. port = 1 ,
} ,
} ,
. num_lvds = 0 ,
} ;
2015-09-07 17:34:26 +03:00
static const struct rcar_du_device_info rcar_du_r8a7795_info = {
. gen = 3 ,
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE ,
. num_crtcs = 4 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
* R8A7795 has one RGB output , two HDMI outputs and one
2016-11-11 18:07:39 +01:00
* LVDS output .
2015-09-07 17:34:26 +03:00
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 3 ) ,
. port = 0 ,
} ,
2016-11-11 18:07:39 +01:00
[ RCAR_DU_OUTPUT_HDMI0 ] = {
. possible_crtcs = BIT ( 1 ) ,
. port = 1 ,
} ,
[ RCAR_DU_OUTPUT_HDMI1 ] = {
. possible_crtcs = BIT ( 2 ) ,
. port = 2 ,
} ,
2015-07-28 20:12:43 +09:00
[ RCAR_DU_OUTPUT_LVDS0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 3 ,
} ,
2015-09-07 17:34:26 +03:00
} ,
2015-07-28 20:12:43 +09:00
. num_lvds = 1 ,
2016-11-11 18:07:41 +01:00
. dpll_ch = BIT ( 1 ) | BIT ( 2 ) ,
2015-09-07 17:34:26 +03:00
} ;
2016-09-06 02:11:43 +03:00
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
. gen = 3 ,
. features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE ,
. num_crtcs = 3 ,
. routes = {
2017-07-11 01:13:20 +03:00
/*
2017-06-19 23:34:40 +03:00
* R8A7796 has one RGB output , one LVDS output and one HDMI
* output .
2016-09-06 02:11:43 +03:00
*/
[ RCAR_DU_OUTPUT_DPAD0 ] = {
. possible_crtcs = BIT ( 2 ) ,
. port = 0 ,
} ,
2017-06-19 23:34:40 +03:00
[ RCAR_DU_OUTPUT_HDMI0 ] = {
. possible_crtcs = BIT ( 1 ) ,
. port = 1 ,
} ,
2016-09-06 02:11:43 +03:00
[ RCAR_DU_OUTPUT_LVDS0 ] = {
. possible_crtcs = BIT ( 0 ) ,
. port = 2 ,
} ,
} ,
. num_lvds = 1 ,
2017-06-19 23:34:40 +03:00
. dpll_ch = BIT ( 1 ) ,
2016-09-06 02:11:43 +03:00
} ;
2014-01-21 15:57:26 +01:00
static const struct of_device_id rcar_du_of_table [ ] = {
{ . compatible = " renesas,du-r8a7779 " , . data = & rcar_du_r8a7779_info } ,
{ . compatible = " renesas,du-r8a7790 " , . data = & rcar_du_r8a7790_info } ,
{ . compatible = " renesas,du-r8a7791 " , . data = & rcar_du_r8a7791_info } ,
2016-08-04 15:01:02 -07:00
{ . compatible = " renesas,du-r8a7792 " , . data = & rcar_du_r8a7792_info } ,
2015-07-17 10:44:33 +03:00
{ . compatible = " renesas,du-r8a7793 " , . data = & rcar_du_r8a7791_info } ,
2015-07-17 10:44:33 +03:00
{ . compatible = " renesas,du-r8a7794 " , . data = & rcar_du_r8a7794_info } ,
2015-09-07 17:34:26 +03:00
{ . compatible = " renesas,du-r8a7795 " , . data = & rcar_du_r8a7795_info } ,
2016-09-06 02:11:43 +03:00
{ . compatible = " renesas,du-r8a7796 " , . data = & rcar_du_r8a7796_info } ,
2014-01-21 15:57:26 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , rcar_du_of_table ) ;
2013-06-19 13:54:11 +02:00
/* -----------------------------------------------------------------------------
* DRM operations
*/
2013-03-14 22:45:22 +01:00
static void rcar_du_lastclose ( struct drm_device * dev )
2013-06-19 13:54:11 +02:00
{
struct rcar_du_device * rcdu = dev - > dev_private ;
2013-03-14 22:45:22 +01:00
drm_fbdev_cma_restore_mode ( rcdu - > fbdev ) ;
2013-06-19 13:54:11 +02:00
}
2017-03-08 15:12:56 +01:00
DEFINE_DRM_GEM_CMA_FOPS ( rcar_du_fops ) ;
2013-06-19 13:54:11 +02:00
static struct drm_driver rcar_du_driver = {
2015-02-26 21:22:10 +02:00
. driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
| DRIVER_ATOMIC ,
2013-03-14 22:45:22 +01:00
. lastclose = rcar_du_lastclose ,
2016-05-30 19:53:02 +02:00
. gem_free_object_unlocked = drm_gem_cma_free_object ,
2013-06-19 13:54:11 +02:00
. gem_vm_ops = & drm_gem_cma_vm_ops ,
. prime_handle_to_fd = drm_gem_prime_handle_to_fd ,
. prime_fd_to_handle = drm_gem_prime_fd_to_handle ,
2013-07-10 15:23:35 +02:00
. gem_prime_import = drm_gem_prime_import ,
. gem_prime_export = drm_gem_prime_export ,
. gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table ,
. gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table ,
. gem_prime_vmap = drm_gem_cma_prime_vmap ,
. gem_prime_vunmap = drm_gem_cma_prime_vunmap ,
. gem_prime_mmap = drm_gem_cma_prime_mmap ,
2013-07-04 20:05:51 +02:00
. dumb_create = rcar_du_dumb_create ,
2013-06-19 13:54:11 +02:00
. fops = & rcar_du_fops ,
. name = " rcar-du " ,
. desc = " Renesas R-Car Display Unit " ,
. date = " 20130110 " ,
. major = 1 ,
. minor = 0 ,
} ;
/* -----------------------------------------------------------------------------
* Power management
*/
2014-07-13 12:18:58 +01:00
# ifdef CONFIG_PM_SLEEP
2013-06-19 13:54:11 +02:00
static int rcar_du_pm_suspend ( struct device * dev )
{
struct rcar_du_device * rcdu = dev_get_drvdata ( dev ) ;
drm_kms_helper_poll_disable ( rcdu - > ddev ) ;
/* TODO Suspend the CRTC */
return 0 ;
}
static int rcar_du_pm_resume ( struct device * dev )
{
struct rcar_du_device * rcdu = dev_get_drvdata ( dev ) ;
/* TODO Resume the CRTC */
drm_kms_helper_poll_enable ( rcdu - > ddev ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops rcar_du_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( rcar_du_pm_suspend , rcar_du_pm_resume )
} ;
/* -----------------------------------------------------------------------------
* Platform driver
*/
2015-09-28 18:39:53 +03:00
static int rcar_du_remove ( struct platform_device * pdev )
2013-06-19 13:54:11 +02:00
{
2015-09-28 18:39:53 +03:00
struct rcar_du_device * rcdu = platform_get_drvdata ( pdev ) ;
struct drm_device * ddev = rcdu - > ddev ;
drm_dev_unregister ( ddev ) ;
if ( rcdu - > fbdev )
drm_fbdev_cma_fini ( rcdu - > fbdev ) ;
drm_kms_helper_poll_fini ( ddev ) ;
drm_mode_config_cleanup ( ddev ) ;
drm_dev_unref ( ddev ) ;
return 0 ;
2013-06-19 13:54:11 +02:00
}
2015-09-28 18:39:53 +03:00
static int rcar_du_probe ( struct platform_device * pdev )
2013-06-19 13:54:11 +02:00
{
2015-09-28 18:39:53 +03:00
struct rcar_du_device * rcdu ;
struct drm_device * ddev ;
struct resource * mem ;
int ret ;
2016-10-19 00:51:35 +03:00
/* Allocate and initialize the R-Car device structure. */
2015-09-28 18:39:53 +03:00
rcdu = devm_kzalloc ( & pdev - > dev , sizeof ( * rcdu ) , GFP_KERNEL ) ;
if ( rcdu = = NULL )
return - ENOMEM ;
rcdu - > dev = & pdev - > dev ;
2016-10-16 10:01:47 +02:00
rcdu - > info = of_device_get_match_data ( rcdu - > dev ) ;
2015-09-28 18:39:53 +03:00
platform_set_drvdata ( pdev , rcdu ) ;
/* I/O resources */
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
rcdu - > mmio = devm_ioremap_resource ( & pdev - > dev , mem ) ;
2016-10-19 00:51:35 +03:00
if ( IS_ERR ( rcdu - > mmio ) )
return PTR_ERR ( rcdu - > mmio ) ;
2015-09-28 18:39:53 +03:00
/* DRM/KMS objects */
2016-10-19 00:51:35 +03:00
ddev = drm_dev_alloc ( & rcar_du_driver , & pdev - > dev ) ;
if ( IS_ERR ( ddev ) )
return PTR_ERR ( ddev ) ;
rcdu - > ddev = ddev ;
ddev - > dev_private = rcdu ;
2015-09-28 18:39:53 +03:00
ret = rcar_du_modeset_init ( rcdu ) ;
if ( ret < 0 ) {
2016-05-25 00:41:18 +00:00
if ( ret ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev ,
" failed to initialize DRM/KMS (%d) \n " , ret ) ;
2015-09-28 18:39:53 +03:00
goto error ;
}
ddev - > irq_enabled = 1 ;
2017-07-11 01:13:20 +03:00
/*
* Register the DRM device with the core and the connectors with
2015-09-28 18:39:53 +03:00
* sysfs .
*/
ret = drm_dev_register ( ddev , 0 ) ;
if ( ret )
goto error ;
DRM_INFO ( " Device %s probed \n " , dev_name ( & pdev - > dev ) ) ;
2013-06-19 13:54:11 +02:00
return 0 ;
2015-09-28 18:39:53 +03:00
error :
rcar_du_remove ( pdev ) ;
return ret ;
2013-06-19 13:54:11 +02:00
}
static struct platform_driver rcar_du_platform_driver = {
. probe = rcar_du_probe ,
. remove = rcar_du_remove ,
. driver = {
. name = " rcar-du " ,
. pm = & rcar_du_pm_ops ,
2014-01-21 15:57:26 +01:00
. of_match_table = rcar_du_of_table ,
2013-06-19 13:54:11 +02:00
} ,
} ;
module_platform_driver ( rcar_du_platform_driver ) ;
MODULE_AUTHOR ( " Laurent Pinchart <laurent.pinchart@ideasonboard.com> " ) ;
MODULE_DESCRIPTION ( " Renesas R-Car Display Unit DRM Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;