2015-10-29 09:36:23 +01:00
/*
* Copyright ( C ) 2015 Free Electrons
* Copyright ( C ) 2015 NextThing Co
*
* Maxime Ripard < maxime . ripard @ free - electrons . 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 <drm/drmP.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_modes.h>
# include <linux/clk-provider.h>
# include <linux/ioport.h>
# include <linux/of_address.h>
2017-02-23 16:05:34 +08:00
# include <linux/of_graph.h>
2015-10-29 09:36:23 +01:00
# include <linux/of_irq.h>
# include <linux/regmap.h>
# include <video/videomode.h>
# include "sun4i_crtc.h"
# include "sun4i_drv.h"
2017-05-17 22:47:17 +08:00
# include "sunxi_engine.h"
2015-10-29 09:36:23 +01:00
# include "sun4i_tcon.h"
static void sun4i_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
unsigned long flags ;
if ( crtc - > state - > event ) {
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
scrtc - > event = crtc - > state - > event ;
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
crtc - > state - > event = NULL ;
}
}
static void sun4i_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
2016-06-08 14:18:58 +02:00
struct drm_pending_vblank_event * event = crtc - > state - > event ;
2015-10-29 09:36:23 +01:00
DRM_DEBUG_DRIVER ( " Committing plane changes \n " ) ;
2017-05-17 22:47:17 +08:00
sunxi_engine_commit ( scrtc - > engine ) ;
2016-06-08 14:18:58 +02:00
if ( event ) {
crtc - > state - > event = NULL ;
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
if ( drm_crtc_vblank_get ( crtc ) = = 0 )
drm_crtc_arm_vblank_event ( crtc , event ) ;
else
drm_crtc_send_vblank_event ( crtc , event ) ;
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
}
2015-10-29 09:36:23 +01:00
}
static void sun4i_crtc_disable ( struct drm_crtc * crtc )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
DRM_DEBUG_DRIVER ( " Disabling the CRTC \n " ) ;
2017-02-23 16:05:43 +08:00
sun4i_tcon_disable ( scrtc - > tcon ) ;
2016-06-20 12:20:59 +02:00
if ( crtc - > state - > event & & ! crtc - > state - > active ) {
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
crtc - > state - > event = NULL ;
}
2015-10-29 09:36:23 +01:00
}
static void sun4i_crtc_enable ( struct drm_crtc * crtc )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
DRM_DEBUG_DRIVER ( " Enabling the CRTC \n " ) ;
2017-02-23 16:05:43 +08:00
sun4i_tcon_enable ( scrtc - > tcon ) ;
2015-10-29 09:36:23 +01:00
}
static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
. atomic_begin = sun4i_crtc_atomic_begin ,
. atomic_flush = sun4i_crtc_atomic_flush ,
. disable = sun4i_crtc_disable ,
. enable = sun4i_crtc_enable ,
} ;
2017-02-07 17:16:31 +08:00
static int sun4i_crtc_enable_vblank ( struct drm_crtc * crtc )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
DRM_DEBUG_DRIVER ( " Enabling VBLANK on crtc %p \n " , crtc ) ;
2017-02-23 16:05:43 +08:00
sun4i_tcon_enable_vblank ( scrtc - > tcon , true ) ;
2017-02-07 17:16:31 +08:00
return 0 ;
}
static void sun4i_crtc_disable_vblank ( struct drm_crtc * crtc )
{
struct sun4i_crtc * scrtc = drm_crtc_to_sun4i_crtc ( crtc ) ;
DRM_DEBUG_DRIVER ( " Disabling VBLANK on crtc %p \n " , crtc ) ;
2017-02-23 16:05:43 +08:00
sun4i_tcon_enable_vblank ( scrtc - > tcon , false ) ;
2017-02-07 17:16:31 +08:00
}
2015-10-29 09:36:23 +01:00
static const struct drm_crtc_funcs sun4i_crtc_funcs = {
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. destroy = drm_crtc_cleanup ,
. page_flip = drm_atomic_helper_page_flip ,
. reset = drm_atomic_helper_crtc_reset ,
. set_config = drm_atomic_helper_set_config ,
2017-02-07 17:16:31 +08:00
. enable_vblank = sun4i_crtc_enable_vblank ,
. disable_vblank = sun4i_crtc_disable_vblank ,
2015-10-29 09:36:23 +01:00
} ;
2017-03-09 18:05:28 +08:00
struct sun4i_crtc * sun4i_crtc_init ( struct drm_device * drm ,
2017-05-17 22:47:17 +08:00
struct sunxi_engine * engine ,
2017-03-09 18:05:28 +08:00
struct sun4i_tcon * tcon )
2015-10-29 09:36:23 +01:00
{
struct sun4i_crtc * scrtc ;
2017-05-15 00:30:36 +08:00
struct drm_plane * * planes ;
2017-02-23 16:05:38 +08:00
struct drm_plane * primary = NULL , * cursor = NULL ;
int ret , i ;
2015-10-29 09:36:23 +01:00
scrtc = devm_kzalloc ( drm - > dev , sizeof ( * scrtc ) , GFP_KERNEL ) ;
if ( ! scrtc )
2017-02-17 11:13:30 +08:00
return ERR_PTR ( - ENOMEM ) ;
2017-05-17 22:47:17 +08:00
scrtc - > engine = engine ;
2017-03-09 18:05:28 +08:00
scrtc - > tcon = tcon ;
2015-10-29 09:36:23 +01:00
2017-02-23 16:05:36 +08:00
/* Create our layers */
2017-05-17 22:47:17 +08:00
planes = sunxi_engine_layers_init ( drm , engine ) ;
2017-05-15 00:30:36 +08:00
if ( IS_ERR ( planes ) ) {
2017-02-23 16:05:36 +08:00
dev_err ( drm - > dev , " Couldn't create the planes \n " ) ;
2017-02-23 16:05:38 +08:00
return NULL ;
}
/* find primary and cursor planes for drm_crtc_init_with_planes */
2017-05-15 00:30:36 +08:00
for ( i = 0 ; planes [ i ] ; i + + ) {
struct drm_plane * plane = planes [ i ] ;
2017-02-23 16:05:38 +08:00
2017-05-15 00:30:36 +08:00
switch ( plane - > type ) {
2017-02-23 16:05:38 +08:00
case DRM_PLANE_TYPE_PRIMARY :
2017-05-15 00:30:36 +08:00
primary = plane ;
2017-02-23 16:05:38 +08:00
break ;
case DRM_PLANE_TYPE_CURSOR :
2017-05-15 00:30:36 +08:00
cursor = plane ;
2017-02-23 16:05:38 +08:00
break ;
default :
break ;
}
2017-02-23 16:05:36 +08:00
}
2015-10-29 09:36:23 +01:00
ret = drm_crtc_init_with_planes ( drm , & scrtc - > crtc ,
2017-02-23 16:05:38 +08:00
primary ,
cursor ,
2015-10-29 09:36:23 +01:00
& sun4i_crtc_funcs ,
NULL ) ;
if ( ret ) {
dev_err ( drm - > dev , " Couldn't init DRM CRTC \n " ) ;
2017-02-17 11:13:30 +08:00
return ERR_PTR ( ret ) ;
2015-10-29 09:36:23 +01:00
}
drm_crtc_helper_add ( & scrtc - > crtc , & sun4i_crtc_helper_funcs ) ;
2017-02-23 16:05:34 +08:00
/* Set crtc.port to output port node of the tcon */
2017-03-09 18:05:26 +08:00
scrtc - > crtc . port = of_graph_get_port_by_id ( scrtc - > tcon - > dev - > of_node ,
2017-02-23 16:05:34 +08:00
1 ) ;
2017-02-23 16:05:39 +08:00
/* Set possible_crtcs to this crtc for overlay planes */
2017-05-15 00:30:36 +08:00
for ( i = 0 ; planes [ i ] ; i + + ) {
2017-02-23 16:05:39 +08:00
uint32_t possible_crtcs = BIT ( drm_crtc_index ( & scrtc - > crtc ) ) ;
2017-05-15 00:30:36 +08:00
struct drm_plane * plane = planes [ i ] ;
2017-02-23 16:05:39 +08:00
2017-05-15 00:30:36 +08:00
if ( plane - > type = = DRM_PLANE_TYPE_OVERLAY )
plane - > possible_crtcs = possible_crtcs ;
2017-02-23 16:05:39 +08:00
}
2015-10-29 09:36:23 +01:00
return scrtc ;
}