2013-06-19 13:54:11 +02:00
/*
* rcar_du_crtc . c - - R - Car Display Unit CRTCs
*
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/mutex.h>
# include <drm/drmP.h>
2015-02-20 11:30:59 +02:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2013-06-19 13:54:11 +02:00
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_cma_helper.h>
# include <drm/drm_gem_cma_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2013-06-19 13:54:11 +02:00
# include "rcar_du_crtc.h"
# include "rcar_du_drv.h"
# include "rcar_du_kms.h"
# include "rcar_du_plane.h"
# include "rcar_du_regs.h"
2015-09-07 17:14:58 +03:00
# include "rcar_du_vsp.h"
2013-06-19 13:54:11 +02:00
static u32 rcar_du_crtc_read ( struct rcar_du_crtc * rcrtc , u32 reg )
{
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
return rcar_du_read ( rcdu , rcrtc - > mmio_offset + reg ) ;
}
static void rcar_du_crtc_write ( struct rcar_du_crtc * rcrtc , u32 reg , u32 data )
{
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
rcar_du_write ( rcdu , rcrtc - > mmio_offset + reg , data ) ;
}
static void rcar_du_crtc_clr ( struct rcar_du_crtc * rcrtc , u32 reg , u32 clr )
{
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
rcar_du_write ( rcdu , rcrtc - > mmio_offset + reg ,
rcar_du_read ( rcdu , rcrtc - > mmio_offset + reg ) & ~ clr ) ;
}
static void rcar_du_crtc_set ( struct rcar_du_crtc * rcrtc , u32 reg , u32 set )
{
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
rcar_du_write ( rcdu , rcrtc - > mmio_offset + reg ,
rcar_du_read ( rcdu , rcrtc - > mmio_offset + reg ) | set ) ;
}
static void rcar_du_crtc_clr_set ( struct rcar_du_crtc * rcrtc , u32 reg ,
u32 clr , u32 set )
{
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
u32 value = rcar_du_read ( rcdu , rcrtc - > mmio_offset + reg ) ;
rcar_du_write ( rcdu , rcrtc - > mmio_offset + reg , ( value & ~ clr ) | set ) ;
}
2013-06-14 14:15:01 +02:00
static int rcar_du_crtc_get ( struct rcar_du_crtc * rcrtc )
{
int ret ;
ret = clk_prepare_enable ( rcrtc - > clock ) ;
if ( ret < 0 )
return ret ;
2014-12-09 00:24:49 +02:00
ret = clk_prepare_enable ( rcrtc - > extclock ) ;
if ( ret < 0 )
goto error_clock ;
2013-06-16 21:01:02 +02:00
ret = rcar_du_group_get ( rcrtc - > group ) ;
2013-06-14 14:15:01 +02:00
if ( ret < 0 )
2014-12-09 00:24:49 +02:00
goto error_group ;
return 0 ;
2013-06-14 14:15:01 +02:00
2014-12-09 00:24:49 +02:00
error_group :
clk_disable_unprepare ( rcrtc - > extclock ) ;
error_clock :
clk_disable_unprepare ( rcrtc - > clock ) ;
2013-06-14 14:15:01 +02:00
return ret ;
}
static void rcar_du_crtc_put ( struct rcar_du_crtc * rcrtc )
{
2013-06-16 21:01:02 +02:00
rcar_du_group_put ( rcrtc - > group ) ;
2014-12-09 00:24:49 +02:00
clk_disable_unprepare ( rcrtc - > extclock ) ;
2013-06-14 14:15:01 +02:00
clk_disable_unprepare ( rcrtc - > clock ) ;
}
2015-02-18 13:42:40 +02:00
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
2016-11-11 18:07:41 +01:00
struct dpll_info {
unsigned int output ;
unsigned int fdpll ;
unsigned int n ;
unsigned int m ;
} ;
static void rcar_du_dpll_divider ( struct rcar_du_crtc * rcrtc ,
struct dpll_info * dpll ,
unsigned long input ,
unsigned long target )
{
unsigned long best_diff = ( unsigned long ) - 1 ;
unsigned long diff ;
unsigned int fdpll ;
unsigned int m ;
unsigned int n ;
for ( n = 39 ; n < 120 ; n + + ) {
for ( m = 0 ; m < 4 ; m + + ) {
for ( fdpll = 1 ; fdpll < 32 ; fdpll + + ) {
unsigned long output ;
/* 1/2 (FRQSEL=1) for duty rate 50% */
output = input * ( n + 1 ) / ( m + 1 )
/ ( fdpll + 1 ) / 2 ;
if ( output > = 400000000 )
continue ;
diff = abs ( ( long ) output - ( long ) target ) ;
if ( best_diff > diff ) {
best_diff = diff ;
dpll - > n = n ;
dpll - > m = m ;
dpll - > fdpll = fdpll ;
dpll - > output = output ;
}
if ( diff = = 0 )
goto done ;
}
}
}
done :
dev_dbg ( rcrtc - > group - > dev - > dev ,
" output:%u, fdpll:%u, n:%u, m:%u, diff:%lu \n " ,
dpll - > output , dpll - > fdpll , dpll - > n , dpll - > m ,
best_diff ) ;
}
2013-06-19 13:54:11 +02:00
static void rcar_du_crtc_set_display_timing ( struct rcar_du_crtc * rcrtc )
{
2015-02-18 15:47:27 +02:00
const struct drm_display_mode * mode = & rcrtc - > crtc . state - > adjusted_mode ;
2016-11-11 18:07:41 +01:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2014-12-09 00:24:49 +02:00
unsigned long mode_clock = mode - > clock * 1000 ;
2013-06-19 13:54:11 +02:00
unsigned long clk ;
u32 value ;
2014-12-09 00:24:49 +02:00
u32 escr ;
2013-06-19 13:54:11 +02:00
u32 div ;
2014-12-09 00:24:49 +02:00
/* Compute the clock divisor and select the internal or external dot
* clock based on the requested frequency .
*/
2013-06-14 14:15:01 +02:00
clk = clk_get_rate ( rcrtc - > clock ) ;
2014-12-09 00:24:49 +02:00
div = DIV_ROUND_CLOSEST ( clk , mode_clock ) ;
2013-06-19 13:54:11 +02:00
div = clamp ( div , 1U , 64U ) - 1 ;
2014-12-09 00:24:49 +02:00
escr = div | ESCR_DCLKSEL_CLKS ;
if ( rcrtc - > extclock ) {
2016-11-11 18:07:41 +01:00
struct dpll_info dpll = { 0 } ;
2014-12-09 00:24:49 +02:00
unsigned long extclk ;
unsigned long extrate ;
unsigned long rate ;
u32 extdiv ;
extclk = clk_get_rate ( rcrtc - > extclock ) ;
2016-11-11 18:07:41 +01:00
if ( rcdu - > info - > dpll_ch & ( 1 < < rcrtc - > index ) ) {
rcar_du_dpll_divider ( rcrtc , & dpll , extclk , mode_clock ) ;
extclk = dpll . output ;
}
2014-12-09 00:24:49 +02:00
extdiv = DIV_ROUND_CLOSEST ( extclk , mode_clock ) ;
extdiv = clamp ( extdiv , 1U , 64U ) - 1 ;
rate = clk / ( div + 1 ) ;
extrate = extclk / ( extdiv + 1 ) ;
if ( abs ( ( long ) extrate - ( long ) mode_clock ) <
abs ( ( long ) rate - ( long ) mode_clock ) ) {
dev_dbg ( rcrtc - > group - > dev - > dev ,
" crtc%u: using external clock \n " , rcrtc - > index ) ;
2016-11-11 18:07:41 +01:00
if ( rcdu - > info - > dpll_ch & ( 1 < < rcrtc - > index ) ) {
u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
| DPLLCR_FDPLL ( dpll . fdpll )
| DPLLCR_N ( dpll . n ) | DPLLCR_M ( dpll . m )
| DPLLCR_STBY ;
if ( rcrtc - > index = = 1 )
dpllcr | = DPLLCR_PLCS1
| DPLLCR_INCS_DOTCLKIN1 ;
else
dpllcr | = DPLLCR_PLCS0
| DPLLCR_INCS_DOTCLKIN0 ;
rcar_du_group_write ( rcrtc - > group , DPLLCR ,
dpllcr ) ;
escr = ESCR_DCLKSEL_DCLKIN | 1 ;
} else {
escr = ESCR_DCLKSEL_DCLKIN | extdiv ;
}
2014-12-09 00:24:49 +02:00
}
}
2013-06-19 13:54:11 +02:00
2013-06-17 00:29:25 +02:00
rcar_du_group_write ( rcrtc - > group , rcrtc - > index % 2 ? ESCR2 : ESCR ,
2014-12-09 00:24:49 +02:00
escr ) ;
2013-06-17 00:29:25 +02:00
rcar_du_group_write ( rcrtc - > group , rcrtc - > index % 2 ? OTAR2 : OTAR , 0 ) ;
2013-06-19 13:54:11 +02:00
/* Signal polarities */
2016-05-16 11:28:15 +09:00
value = ( ( mode - > flags & DRM_MODE_FLAG_PVSYNC ) ? DSMR_VSL : 0 )
| ( ( mode - > flags & DRM_MODE_FLAG_PHSYNC ) ? DSMR_HSL : 0 )
2015-09-07 22:02:14 +03:00
| DSMR_DIPM_DISP | DSMR_CSPM ;
2013-06-19 13:54:11 +02:00
rcar_du_crtc_write ( rcrtc , DSMR , value ) ;
/* Display timings */
rcar_du_crtc_write ( rcrtc , HDSR , mode - > htotal - mode - > hsync_start - 19 ) ;
rcar_du_crtc_write ( rcrtc , HDER , mode - > htotal - mode - > hsync_start +
mode - > hdisplay - 19 ) ;
rcar_du_crtc_write ( rcrtc , HSWR , mode - > hsync_end -
mode - > hsync_start - 1 ) ;
rcar_du_crtc_write ( rcrtc , HCR , mode - > htotal - 1 ) ;
2014-12-09 19:11:18 +02:00
rcar_du_crtc_write ( rcrtc , VDSR , mode - > crtc_vtotal -
mode - > crtc_vsync_end - 2 ) ;
rcar_du_crtc_write ( rcrtc , VDER , mode - > crtc_vtotal -
mode - > crtc_vsync_end +
mode - > crtc_vdisplay - 2 ) ;
rcar_du_crtc_write ( rcrtc , VSPR , mode - > crtc_vtotal -
mode - > crtc_vsync_end +
mode - > crtc_vsync_start - 1 ) ;
rcar_du_crtc_write ( rcrtc , VCR , mode - > crtc_vtotal - 1 ) ;
2013-06-19 13:54:11 +02:00
2016-04-18 16:31:30 +09:00
rcar_du_crtc_write ( rcrtc , DESR , mode - > htotal - mode - > hsync_start - 1 ) ;
2013-06-19 13:54:11 +02:00
rcar_du_crtc_write ( rcrtc , DEWR , mode - > hdisplay ) ;
}
2013-06-17 03:13:11 +02:00
void rcar_du_crtc_route_output ( struct drm_crtc * crtc ,
enum rcar_du_output output )
2013-06-19 13:54:11 +02:00
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
2013-06-17 03:13:11 +02:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
/* Store the route from the CRTC output to the DU output. The DU will be
* configured when starting the CRTC .
*/
2013-06-17 03:13:11 +02:00
rcrtc - > outputs | = BIT ( output ) ;
2013-06-17 03:20:08 +02:00
2014-12-09 00:21:12 +02:00
/* Store RGB routing to DPAD0, the hardware will be configured when
* starting the CRTC .
*/
if ( output = = RCAR_DU_OUTPUT_DPAD0 )
2013-06-17 03:20:08 +02:00
rcdu - > dpad0_source = rcrtc - > index ;
2013-06-19 13:54:11 +02:00
}
2015-02-23 02:36:31 +02:00
static unsigned int plane_zpos ( struct rcar_du_plane * plane )
{
2016-07-22 14:28:27 +02:00
return plane - > plane . state - > normalized_zpos ;
2015-02-23 02:36:31 +02:00
}
2015-02-23 02:59:35 +02:00
static const struct rcar_du_format_info *
plane_format ( struct rcar_du_plane * plane )
{
2015-04-29 00:48:17 +03:00
return to_rcar_plane_state ( plane - > plane . state ) - > format ;
2015-02-23 02:59:35 +02:00
}
2015-02-23 01:39:13 +02:00
static void rcar_du_crtc_update_planes ( struct rcar_du_crtc * rcrtc )
2013-06-19 13:54:11 +02:00
{
struct rcar_du_plane * planes [ RCAR_DU_NUM_HW_PLANES ] ;
2015-09-07 17:14:58 +03:00
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
2013-06-19 13:54:11 +02:00
unsigned int num_planes = 0 ;
2015-04-28 18:01:45 +03:00
unsigned int dptsr_planes ;
unsigned int hwplanes = 0 ;
2013-06-19 13:54:11 +02:00
unsigned int prio = 0 ;
unsigned int i ;
u32 dspr = 0 ;
2015-05-25 16:32:45 +03:00
for ( i = 0 ; i < rcrtc - > group - > num_planes ; + + i ) {
2015-04-29 00:05:56 +03:00
struct rcar_du_plane * plane = & rcrtc - > group - > planes [ i ] ;
2013-06-19 13:54:11 +02:00
unsigned int j ;
2015-02-22 19:24:59 +02:00
if ( plane - > plane . state - > crtc ! = & rcrtc - > crtc )
2013-06-19 13:54:11 +02:00
continue ;
/* Insert the plane in the sorted planes array. */
for ( j = num_planes + + ; j > 0 ; - - j ) {
2015-02-23 02:36:31 +02:00
if ( plane_zpos ( planes [ j - 1 ] ) < = plane_zpos ( plane ) )
2013-06-19 13:54:11 +02:00
break ;
planes [ j ] = planes [ j - 1 ] ;
}
planes [ j ] = plane ;
2015-02-23 02:59:35 +02:00
prio + = plane_format ( plane ) - > planes * 4 ;
2013-06-19 13:54:11 +02:00
}
for ( i = 0 ; i < num_planes ; + + i ) {
struct rcar_du_plane * plane = planes [ i ] ;
2015-02-25 18:27:19 +02:00
struct drm_plane_state * state = plane - > plane . state ;
2015-04-29 00:48:17 +03:00
unsigned int index = to_rcar_plane_state ( state ) - > hwindex ;
2013-06-19 13:54:11 +02:00
prio - = 4 ;
dspr | = ( index + 1 ) < < prio ;
2015-04-28 18:01:45 +03:00
hwplanes | = 1 < < index ;
2013-06-19 13:54:11 +02:00
2015-02-23 02:59:35 +02:00
if ( plane_format ( plane ) - > planes = = 2 ) {
2013-06-19 13:54:11 +02:00
index = ( index + 1 ) % 8 ;
prio - = 4 ;
dspr | = ( index + 1 ) < < prio ;
2015-04-28 18:01:45 +03:00
hwplanes | = 1 < < index ;
2013-06-19 13:54:11 +02:00
}
}
2015-09-07 17:14:58 +03:00
/* If VSP+DU integration is enabled the plane assignment is fixed. */
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_VSP1_SOURCE ) ) {
2015-09-07 17:34:26 +03:00
if ( rcdu - > info - > gen < 3 ) {
dspr = ( rcrtc - > index % 2 ) + 1 ;
hwplanes = 1 < < ( rcrtc - > index % 2 ) ;
} else {
dspr = ( rcrtc - > index % 2 ) ? 3 : 1 ;
hwplanes = 1 < < ( ( rcrtc - > index % 2 ) ? 2 : 0 ) ;
}
2015-09-07 17:14:58 +03:00
}
2015-04-28 18:01:45 +03:00
/* Update the planes to display timing and dot clock generator
* associations .
*
* Updating the DPTSR register requires restarting the CRTC group ,
* resulting in visible flicker . To mitigate the issue only update the
* association if needed by enabled planes . Planes being disabled will
* keep their current association .
2013-06-19 13:54:11 +02:00
*/
2015-04-28 18:01:45 +03:00
mutex_lock ( & rcrtc - > group - > lock ) ;
dptsr_planes = rcrtc - > index % 2 ? rcrtc - > group - > dptsr_planes | hwplanes
: rcrtc - > group - > dptsr_planes & ~ hwplanes ;
if ( dptsr_planes ! = rcrtc - > group - > dptsr_planes ) {
rcar_du_group_write ( rcrtc - > group , DPTSR ,
( dptsr_planes < < 16 ) | dptsr_planes ) ;
rcrtc - > group - > dptsr_planes = dptsr_planes ;
if ( rcrtc - > group - > used_crtcs )
rcar_du_group_restart ( rcrtc - > group ) ;
2013-06-19 13:54:11 +02:00
}
2013-08-24 02:17:03 +02:00
/* Restart the group if plane sources have changed. */
if ( rcrtc - > group - > need_restart )
rcar_du_group_restart ( rcrtc - > group ) ;
2015-04-28 18:01:45 +03:00
mutex_unlock ( & rcrtc - > group - > lock ) ;
2013-06-17 00:29:25 +02:00
rcar_du_group_write ( rcrtc - > group , rcrtc - > index % 2 ? DS2PR : DS1PR ,
dspr ) ;
2013-06-19 13:54:11 +02:00
}
2015-02-18 13:42:40 +02:00
/* -----------------------------------------------------------------------------
* Page Flip
*/
static void rcar_du_crtc_finish_page_flip ( struct rcar_du_crtc * rcrtc )
{
struct drm_pending_vblank_event * event ;
struct drm_device * dev = rcrtc - > crtc . dev ;
unsigned long flags ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
event = rcrtc - > event ;
rcrtc - > event = NULL ;
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
if ( event = = NULL )
return ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2016-04-14 10:48:20 -07:00
drm_crtc_send_vblank_event ( & rcrtc - > crtc , event ) ;
2015-02-18 13:21:56 +02:00
wake_up ( & rcrtc - > flip_wait ) ;
2015-02-18 13:42:40 +02:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
2015-02-18 13:14:46 +02:00
drm_crtc_vblank_put ( & rcrtc - > crtc ) ;
2015-02-18 13:42:40 +02:00
}
2015-02-18 13:21:56 +02:00
static bool rcar_du_crtc_page_flip_pending ( struct rcar_du_crtc * rcrtc )
{
struct drm_device * dev = rcrtc - > crtc . dev ;
unsigned long flags ;
bool pending ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
pending = rcrtc - > event ! = NULL ;
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
return pending ;
}
static void rcar_du_crtc_wait_page_flip ( struct rcar_du_crtc * rcrtc )
{
struct rcar_du_device * rcdu = rcrtc - > group - > dev ;
if ( wait_event_timeout ( rcrtc - > flip_wait ,
! rcar_du_crtc_page_flip_pending ( rcrtc ) ,
msecs_to_jiffies ( 50 ) ) )
return ;
dev_warn ( rcdu - > dev , " page flip timeout \n " ) ;
rcar_du_crtc_finish_page_flip ( rcrtc ) ;
}
2015-02-18 13:42:40 +02:00
/* -----------------------------------------------------------------------------
* Start / Stop and Suspend / Resume
*/
2013-06-19 13:54:11 +02:00
static void rcar_du_crtc_start ( struct rcar_du_crtc * rcrtc )
{
struct drm_crtc * crtc = & rcrtc - > crtc ;
2014-12-09 19:11:18 +02:00
bool interlaced ;
2013-06-19 13:54:11 +02:00
if ( rcrtc - > started )
return ;
/* Set display off and background to black */
rcar_du_crtc_write ( rcrtc , DOOR , DOOR_RGB ( 0 , 0 , 0 ) ) ;
rcar_du_crtc_write ( rcrtc , BPOR , BPOR_RGB ( 0 , 0 , 0 ) ) ;
/* Configure display timings and output routing */
rcar_du_crtc_set_display_timing ( rcrtc ) ;
2013-06-17 00:11:05 +02:00
rcar_du_group_set_routing ( rcrtc - > group ) ;
2013-06-19 13:54:11 +02:00
2015-02-23 01:39:13 +02:00
/* Start with all planes disabled. */
rcar_du_group_write ( rcrtc - > group , rcrtc - > index % 2 ? DS2PR : DS1PR , 0 ) ;
2013-06-19 13:54:11 +02:00
/* Select master sync mode. This enables display operation in master
* sync mode ( with the HSYNC and VSYNC signals configured as outputs and
* actively driven ) .
*/
2014-12-09 19:11:18 +02:00
interlaced = rcrtc - > crtc . mode . flags & DRM_MODE_FLAG_INTERLACE ;
rcar_du_crtc_clr_set ( rcrtc , DSYSR , DSYSR_TVM_MASK | DSYSR_SCM_MASK ,
( interlaced ? DSYSR_SCM_INT_VIDEO : 0 ) |
DSYSR_TVM_MASTER ) ;
2013-06-19 13:54:11 +02:00
2013-06-16 21:01:02 +02:00
rcar_du_group_start_stop ( rcrtc - > group , true ) ;
2013-06-19 13:54:11 +02:00
2015-09-07 17:14:58 +03:00
/* Enable the VSP compositor. */
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) )
rcar_du_vsp_enable ( rcrtc ) ;
2015-02-18 13:14:46 +02:00
/* Turn vertical blanking interrupt reporting back on. */
drm_crtc_vblank_on ( crtc ) ;
2013-06-19 13:54:11 +02:00
rcrtc - > started = true ;
}
static void rcar_du_crtc_stop ( struct rcar_du_crtc * rcrtc )
{
struct drm_crtc * crtc = & rcrtc - > crtc ;
if ( ! rcrtc - > started )
return ;
2015-05-14 15:01:47 +03:00
/* Disable all planes and wait for the change to take effect. This is
* required as the DSnPR registers are updated on vblank , and no vblank
* will occur once the CRTC is stopped . Disabling planes when starting
* the CRTC thus wouldn ' t be enough as it would start scanning out
* immediately from old frame buffers until the next vblank .
*
* This increases the CRTC stop delay , especially when multiple CRTCs
* are stopped in one operation as we now wait for one vblank per CRTC .
* Whether this can be improved needs to be researched .
*/
rcar_du_group_write ( rcrtc - > group , rcrtc - > index % 2 ? DS2PR : DS1PR , 0 ) ;
drm_crtc_wait_one_vblank ( crtc ) ;
2015-02-18 13:14:46 +02:00
/* Disable vertical blanking interrupt reporting. We first need to wait
* for page flip completion before stopping the CRTC as userspace
* expects page flips to eventually complete .
2015-02-18 13:21:56 +02:00
*/
rcar_du_crtc_wait_page_flip ( rcrtc ) ;
2015-02-18 13:14:46 +02:00
drm_crtc_vblank_off ( crtc ) ;
2015-02-18 13:21:56 +02:00
2015-09-07 17:14:58 +03:00
/* Disable the VSP compositor. */
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) )
rcar_du_vsp_disable ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
/* Select switch sync mode. This stops display operation and configures
* the HSYNC and VSYNC signals as inputs .
*/
rcar_du_crtc_clr_set ( rcrtc , DSYSR , DSYSR_TVM_MASK , DSYSR_TVM_SWITCH ) ;
2013-06-16 21:01:02 +02:00
rcar_du_group_start_stop ( rcrtc - > group , false ) ;
2013-06-19 13:54:11 +02:00
rcrtc - > started = false ;
}
void rcar_du_crtc_suspend ( struct rcar_du_crtc * rcrtc )
{
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) )
rcar_du_vsp_disable ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
rcar_du_crtc_stop ( rcrtc ) ;
2013-06-14 14:15:01 +02:00
rcar_du_crtc_put ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
}
void rcar_du_crtc_resume ( struct rcar_du_crtc * rcrtc )
{
2015-02-23 01:39:13 +02:00
unsigned int i ;
2016-01-25 00:28:11 +02:00
if ( ! rcrtc - > crtc . state - > active )
2013-06-19 13:54:11 +02:00
return ;
2013-06-14 14:15:01 +02:00
rcar_du_crtc_get ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
rcar_du_crtc_start ( rcrtc ) ;
2015-02-23 01:39:13 +02:00
/* Commit the planes state. */
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) ) {
rcar_du_vsp_enable ( rcrtc ) ;
} else {
for ( i = 0 ; i < rcrtc - > group - > num_planes ; + + i ) {
struct rcar_du_plane * plane = & rcrtc - > group - > planes [ i ] ;
2015-02-23 01:39:13 +02:00
2015-09-07 17:14:58 +03:00
if ( plane - > plane . state - > crtc ! = & rcrtc - > crtc )
continue ;
2015-02-23 01:39:13 +02:00
2015-09-07 17:14:58 +03:00
rcar_du_plane_setup ( plane ) ;
}
2015-02-23 01:39:13 +02:00
}
rcar_du_crtc_update_planes ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
}
2015-02-18 13:42:40 +02:00
/* -----------------------------------------------------------------------------
* CRTC Functions
*/
2015-02-20 14:05:21 +02:00
static void rcar_du_crtc_enable ( struct drm_crtc * crtc )
2013-06-19 13:54:11 +02:00
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
2015-02-20 14:05:21 +02:00
rcar_du_crtc_get ( rcrtc ) ;
rcar_du_crtc_start ( rcrtc ) ;
}
static void rcar_du_crtc_disable ( struct drm_crtc * crtc )
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
2014-12-09 13:19:10 +02:00
2015-02-20 14:05:21 +02:00
rcar_du_crtc_stop ( rcrtc ) ;
rcar_du_crtc_put ( rcrtc ) ;
2013-06-19 13:54:11 +02:00
2017-02-10 13:30:35 +02:00
spin_lock_irq ( & crtc - > dev - > event_lock ) ;
if ( crtc - > state - > event ) {
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
crtc - > state - > event = NULL ;
}
spin_unlock_irq ( & crtc - > dev - > event_lock ) ;
2015-02-20 15:16:55 +02:00
rcrtc - > outputs = 0 ;
2015-02-20 14:05:21 +02:00
}
2015-07-21 13:28:58 +02:00
static void rcar_du_crtc_atomic_begin ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2015-02-18 12:18:05 +02:00
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
2015-02-23 01:04:21 +02:00
struct drm_device * dev = rcrtc - > crtc . dev ;
unsigned long flags ;
2015-02-18 12:18:05 +02:00
2017-02-12 02:45:11 +02:00
if ( crtc - > state - > event ) {
2015-02-23 01:04:21 +02:00
WARN_ON ( drm_crtc_vblank_get ( crtc ) ! = 0 ) ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2017-02-12 02:45:11 +02:00
rcrtc - > event = crtc - > state - > event ;
crtc - > state - > event = NULL ;
2015-02-23 01:04:21 +02:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
}
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) )
rcar_du_vsp_atomic_begin ( rcrtc ) ;
2015-02-18 12:18:05 +02:00
}
2015-07-21 13:28:58 +02:00
static void rcar_du_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2015-02-18 12:18:05 +02:00
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
2015-02-23 01:39:13 +02:00
rcar_du_crtc_update_planes ( rcrtc ) ;
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcrtc - > group - > dev , RCAR_DU_FEATURE_VSP1_SOURCE ) )
rcar_du_vsp_atomic_flush ( rcrtc ) ;
2015-02-18 12:18:05 +02:00
}
2013-06-19 13:54:11 +02:00
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
. disable = rcar_du_crtc_disable ,
2015-02-20 14:05:21 +02:00
. enable = rcar_du_crtc_enable ,
2015-02-18 12:18:05 +02:00
. atomic_begin = rcar_du_crtc_atomic_begin ,
. atomic_flush = rcar_du_crtc_atomic_flush ,
2013-06-19 13:54:11 +02:00
} ;
2017-02-07 17:16:28 +08:00
static int rcar_du_crtc_enable_vblank ( struct drm_crtc * crtc )
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
rcar_du_crtc_write ( rcrtc , DSRCR , DSRCR_VBCL ) ;
rcar_du_crtc_set ( rcrtc , DIER , DIER_VBE ) ;
return 0 ;
}
static void rcar_du_crtc_disable_vblank ( struct drm_crtc * crtc )
{
struct rcar_du_crtc * rcrtc = to_rcar_crtc ( crtc ) ;
rcar_du_crtc_clr ( rcrtc , DIER , DIER_VBE ) ;
}
2013-06-19 13:54:11 +02:00
static const struct drm_crtc_funcs crtc_funcs = {
2015-02-20 11:30:59 +02:00
. reset = drm_atomic_helper_crtc_reset ,
2013-06-19 13:54:11 +02:00
. destroy = drm_crtc_cleanup ,
2015-02-20 15:16:55 +02:00
. set_config = drm_atomic_helper_set_config ,
2015-02-23 01:04:21 +02:00
. page_flip = drm_atomic_helper_page_flip ,
2015-02-20 11:30:59 +02:00
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2017-02-07 17:16:28 +08:00
. enable_vblank = rcar_du_crtc_enable_vblank ,
. disable_vblank = rcar_du_crtc_disable_vblank ,
2013-06-19 13:54:11 +02:00
} ;
2015-02-18 13:42:40 +02:00
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
static irqreturn_t rcar_du_crtc_irq ( int irq , void * arg )
{
struct rcar_du_crtc * rcrtc = arg ;
irqreturn_t ret = IRQ_NONE ;
u32 status ;
status = rcar_du_crtc_read ( rcrtc , DSSR ) ;
rcar_du_crtc_write ( rcrtc , DSRCR , status & DSRCR_MASK ) ;
if ( status & DSSR_FRM ) {
2016-07-04 21:04:51 -03:00
drm_crtc_handle_vblank ( & rcrtc - > crtc ) ;
2015-02-18 13:42:40 +02:00
rcar_du_crtc_finish_page_flip ( rcrtc ) ;
ret = IRQ_HANDLED ;
}
return ret ;
}
/* -----------------------------------------------------------------------------
* Initialization
*/
2013-06-16 21:01:02 +02:00
int rcar_du_crtc_create ( struct rcar_du_group * rgrp , unsigned int index )
2013-06-19 13:54:11 +02:00
{
2013-06-17 00:29:25 +02:00
static const unsigned int mmio_offsets [ ] = {
2015-09-04 19:49:05 +09:00
DU0_REG_OFFSET , DU1_REG_OFFSET , DU2_REG_OFFSET , DU3_REG_OFFSET
2013-06-17 00:29:25 +02:00
} ;
2013-06-16 21:01:02 +02:00
struct rcar_du_device * rcdu = rgrp - > dev ;
2013-06-14 14:15:01 +02:00
struct platform_device * pdev = to_platform_device ( rcdu - > dev ) ;
2013-06-19 13:54:11 +02:00
struct rcar_du_crtc * rcrtc = & rcdu - > crtcs [ index ] ;
struct drm_crtc * crtc = & rcrtc - > crtc ;
2015-09-07 17:14:58 +03:00
struct drm_plane * primary ;
2013-06-14 14:15:01 +02:00
unsigned int irqflags ;
2014-12-09 00:24:49 +02:00
struct clk * clk ;
char clk_name [ 9 ] ;
2013-06-14 14:15:01 +02:00
char * name ;
int irq ;
2013-06-19 13:54:11 +02:00
int ret ;
2014-12-09 00:24:49 +02:00
/* Get the CRTC clock and the optional external clock. */
2013-06-14 14:15:01 +02:00
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_CRTC_IRQ_CLOCK ) ) {
sprintf ( clk_name , " du.%u " , index ) ;
name = clk_name ;
} else {
name = NULL ;
}
rcrtc - > clock = devm_clk_get ( rcdu - > dev , name ) ;
if ( IS_ERR ( rcrtc - > clock ) ) {
dev_err ( rcdu - > dev , " no clock for CRTC %u \n " , index ) ;
return PTR_ERR ( rcrtc - > clock ) ;
}
2014-12-09 00:24:49 +02:00
sprintf ( clk_name , " dclkin.%u " , index ) ;
clk = devm_clk_get ( rcdu - > dev , clk_name ) ;
if ( ! IS_ERR ( clk ) ) {
rcrtc - > extclock = clk ;
} else if ( PTR_ERR ( rcrtc - > clock ) = = - EPROBE_DEFER ) {
dev_info ( rcdu - > dev , " can't get external clock %u \n " , index ) ;
return - EPROBE_DEFER ;
}
2015-02-18 13:21:56 +02:00
init_waitqueue_head ( & rcrtc - > flip_wait ) ;
2013-06-16 21:01:02 +02:00
rcrtc - > group = rgrp ;
2013-06-17 00:29:25 +02:00
rcrtc - > mmio_offset = mmio_offsets [ index ] ;
2013-06-19 13:54:11 +02:00
rcrtc - > index = index ;
2015-09-07 17:14:58 +03:00
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_VSP1_SOURCE ) )
primary = & rcrtc - > vsp - > planes [ 0 ] . plane ;
else
primary = & rgrp - > planes [ index % 2 ] . plane ;
ret = drm_crtc_init_with_planes ( rcdu - > ddev , crtc , primary ,
drm: Pass 'name' to drm_crtc_init_with_planes()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
I didn't convert drm_crtc_init() since passing the varargs through
would mean either cpp macros or va_list, and I figured we don't
care about these legacy functions enough to warrant the extra pain.
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4, E5;
@@
drm_crtc_init_with_planes(E1, E2, E3, E4, E5
+ ,NULL
)
v2: Split crtc and plane changes apart
Pass NULL for no-name instead of ""
Leave drm_crtc_init() alone
v3: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670771-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:19:31 +02:00
NULL , & crtc_funcs , NULL ) ;
2013-06-19 13:54:11 +02:00
if ( ret < 0 )
return ret ;
drm_crtc_helper_add ( crtc , & crtc_helper_funcs ) ;
2015-02-18 13:14:46 +02:00
/* Start with vertical blanking interrupt reporting disabled. */
drm_crtc_vblank_off ( crtc ) ;
2013-06-14 14:15:01 +02:00
/* Register the interrupt handler. */
if ( rcar_du_has ( rcdu , RCAR_DU_FEATURE_CRTC_IRQ_CLOCK ) ) {
irq = platform_get_irq ( pdev , index ) ;
irqflags = 0 ;
} else {
irq = platform_get_irq ( pdev , 0 ) ;
irqflags = IRQF_SHARED ;
}
if ( irq < 0 ) {
dev_err ( rcdu - > dev , " no IRQ for CRTC %u \n " , index ) ;
2014-11-23 14:11:17 +01:00
return irq ;
2013-06-14 14:15:01 +02:00
}
ret = devm_request_irq ( rcdu - > dev , irq , rcar_du_crtc_irq , irqflags ,
dev_name ( rcdu - > dev ) , rcrtc ) ;
if ( ret < 0 ) {
dev_err ( rcdu - > dev ,
" failed to register IRQ for CRTC %u \n " , index ) ;
return ret ;
}
2013-06-19 13:54:11 +02:00
return 0 ;
}