2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-01-06 11:13:28 +01:00
/*
* Copyright ( C ) 2014 Traphandler
* Copyright ( C ) 2014 Free Electrons
*
* Author : Jean - Jacques Hiblot < jjhiblot @ traphandler . com >
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*/
# include <linux/clk.h>
2019-06-30 08:19:03 +02:00
# include <linux/mfd/atmel-hlcdc.h>
# include <linux/pinctrl/consumer.h>
2015-01-06 11:13:28 +01:00
# include <linux/pm.h>
# include <linux/pm_runtime.h>
2019-06-30 08:19:03 +02:00
# include <video/videomode.h>
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2015-01-06 11:13:28 +01:00
# include <drm/drm_crtc.h>
2019-06-30 08:19:03 +02:00
# include <drm/drm_modeset_helper_vtables.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2019-06-30 08:19:03 +02:00
# include <drm/drm_vblank.h>
2015-01-06 11:13:28 +01:00
# include "atmel_hlcdc_dc.h"
2016-01-06 11:14:15 +01:00
/**
* Atmel HLCDC CRTC state structure
*
* @ base : base CRTC state
* @ output_mode : RGBXXX output mode
*/
struct atmel_hlcdc_crtc_state {
struct drm_crtc_state base ;
unsigned int output_mode ;
} ;
static inline struct atmel_hlcdc_crtc_state *
drm_crtc_state_to_atmel_hlcdc_crtc_state ( struct drm_crtc_state * state )
{
return container_of ( state , struct atmel_hlcdc_crtc_state , base ) ;
}
2015-01-06 11:13:28 +01:00
/**
* Atmel HLCDC CRTC structure
*
* @ base : base DRM CRTC structure
* @ hlcdc : pointer to the atmel_hlcdc structure provided by the MFD device
* @ event : pointer to the current page flip event
* @ id : CRTC id ( returned by drm_crtc_index )
*/
struct atmel_hlcdc_crtc {
struct drm_crtc base ;
struct atmel_hlcdc_dc * dc ;
struct drm_pending_vblank_event * event ;
int id ;
} ;
static inline struct atmel_hlcdc_crtc *
drm_crtc_to_atmel_hlcdc_crtc ( struct drm_crtc * crtc )
{
return container_of ( crtc , struct atmel_hlcdc_crtc , base ) ;
}
2015-02-05 16:32:33 +01:00
static void atmel_hlcdc_crtc_mode_set_nofb ( struct drm_crtc * c )
2015-01-06 11:13:28 +01:00
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
struct regmap * regmap = crtc - > dc - > hlcdc - > regmap ;
2015-02-05 16:32:33 +01:00
struct drm_display_mode * adj = & c - > state - > adjusted_mode ;
2016-01-06 11:14:15 +01:00
struct atmel_hlcdc_crtc_state * state ;
2015-01-06 11:13:28 +01:00
unsigned long mode_rate ;
struct videomode vm ;
unsigned long prate ;
2019-04-25 12:36:09 +00:00
unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL ;
unsigned int cfg = 0 ;
2019-12-18 14:28:25 +02:00
int div , ret ;
ret = clk_prepare_enable ( crtc - > dc - > hlcdc - > sys_clk ) ;
if ( ret )
return ;
2015-01-06 11:13:28 +01:00
vm . vfront_porch = adj - > crtc_vsync_start - adj - > crtc_vdisplay ;
vm . vback_porch = adj - > crtc_vtotal - adj - > crtc_vsync_end ;
vm . vsync_len = adj - > crtc_vsync_end - adj - > crtc_vsync_start ;
vm . hfront_porch = adj - > crtc_hsync_start - adj - > crtc_hdisplay ;
vm . hback_porch = adj - > crtc_htotal - adj - > crtc_hsync_end ;
vm . hsync_len = adj - > crtc_hsync_end - adj - > crtc_hsync_start ;
regmap_write ( regmap , ATMEL_HLCDC_CFG ( 1 ) ,
( vm . hsync_len - 1 ) | ( ( vm . vsync_len - 1 ) < < 16 ) ) ;
regmap_write ( regmap , ATMEL_HLCDC_CFG ( 2 ) ,
( vm . vfront_porch - 1 ) | ( vm . vback_porch < < 16 ) ) ;
regmap_write ( regmap , ATMEL_HLCDC_CFG ( 3 ) ,
( vm . hfront_porch - 1 ) | ( ( vm . hback_porch - 1 ) < < 16 ) ) ;
regmap_write ( regmap , ATMEL_HLCDC_CFG ( 4 ) ,
( adj - > crtc_hdisplay - 1 ) |
( ( adj - > crtc_vdisplay - 1 ) < < 16 ) ) ;
2019-12-18 14:28:24 +02:00
prate = clk_get_rate ( crtc - > dc - > hlcdc - > sys_clk ) ;
mode_rate = adj - > crtc_clock * 1000 ;
2019-04-25 12:36:09 +00:00
if ( ! crtc - > dc - > desc - > fixed_clksrc ) {
2019-12-18 14:28:24 +02:00
prate * = 2 ;
2019-04-25 12:36:09 +00:00
cfg | = ATMEL_HLCDC_CLKSEL ;
mask | = ATMEL_HLCDC_CLKSEL ;
}
2015-01-06 11:13:28 +01:00
div = DIV_ROUND_UP ( prate , mode_rate ) ;
2018-08-24 11:24:57 +02:00
if ( div < 2 ) {
2015-01-06 11:13:28 +01:00
div = 2 ;
2018-08-24 11:24:57 +02:00
} else if ( ATMEL_HLCDC_CLKDIV ( div ) & ~ ATMEL_HLCDC_CLKDIV_MASK ) {
/* The divider ended up too big, try a lower base rate. */
cfg & = ~ ATMEL_HLCDC_CLKSEL ;
prate / = 2 ;
div = DIV_ROUND_UP ( prate , mode_rate ) ;
if ( ATMEL_HLCDC_CLKDIV ( div ) & ~ ATMEL_HLCDC_CLKDIV_MASK )
div = ATMEL_HLCDC_CLKDIV_MASK ;
2018-08-24 11:24:58 +02:00
} else {
int div_low = prate / mode_rate ;
if ( div_low > = 2 & &
2019-12-18 14:28:28 +02:00
( 10 * ( prate / div_low - mode_rate ) <
( mode_rate - prate / div ) ) )
2018-08-24 11:24:58 +02:00
/*
* At least 10 times better when using a higher
* frequency than requested , instead of a lower .
* So , go with that .
*/
div = div_low ;
2018-08-24 11:24:57 +02:00
}
2015-01-06 11:13:28 +01:00
cfg | = ATMEL_HLCDC_CLKDIV ( div ) ;
2019-04-25 12:36:09 +00:00
regmap_update_bits ( regmap , ATMEL_HLCDC_CFG ( 0 ) , mask , cfg ) ;
2015-01-06 11:13:28 +01:00
2019-04-25 12:36:16 +00:00
state = drm_crtc_state_to_atmel_hlcdc_crtc_state ( c - > state ) ;
cfg = state - > output_mode < < 8 ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( adj - > flags & DRM_MODE_FLAG_NVSYNC )
2015-01-06 11:13:28 +01:00
cfg | = ATMEL_HLCDC_VSPOL ;
2015-02-05 16:32:33 +01:00
if ( adj - > flags & DRM_MODE_FLAG_NHSYNC )
2015-01-06 11:13:28 +01:00
cfg | = ATMEL_HLCDC_HSPOL ;
regmap_update_bits ( regmap , ATMEL_HLCDC_CFG ( 5 ) ,
ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
2016-01-06 11:14:15 +01:00
ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK ,
2015-01-06 11:13:28 +01:00
cfg ) ;
2019-12-18 14:28:25 +02:00
clk_disable_unprepare ( crtc - > dc - > hlcdc - > sys_clk ) ;
2015-01-06 11:13:28 +01:00
}
2017-05-25 15:19:21 +01:00
static enum drm_mode_status
atmel_hlcdc_crtc_mode_valid ( struct drm_crtc * c ,
const struct drm_display_mode * mode )
2016-01-05 18:27:49 +01:00
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
2017-05-25 15:19:21 +01:00
return atmel_hlcdc_dc_mode_valid ( crtc - > dc , mode ) ;
2016-01-05 18:27:49 +01:00
}
2017-06-30 12:36:45 +03:00
static void atmel_hlcdc_crtc_atomic_disable ( struct drm_crtc * c ,
struct drm_crtc_state * old_state )
2015-01-06 11:13:28 +01:00
{
2015-02-05 16:32:33 +01:00
struct drm_device * dev = c - > dev ;
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
struct regmap * regmap = crtc - > dc - > hlcdc - > regmap ;
unsigned int status ;
drm_crtc_vblank_off ( c ) ;
pm_runtime_get_sync ( dev - > dev ) ;
regmap_write ( regmap , ATMEL_HLCDC_DIS , ATMEL_HLCDC_DISP ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
( status & ATMEL_HLCDC_DISP ) )
cpu_relax ( ) ;
regmap_write ( regmap , ATMEL_HLCDC_DIS , ATMEL_HLCDC_SYNC ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
( status & ATMEL_HLCDC_SYNC ) )
cpu_relax ( ) ;
regmap_write ( regmap , ATMEL_HLCDC_DIS , ATMEL_HLCDC_PIXEL_CLK ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
( status & ATMEL_HLCDC_PIXEL_CLK ) )
cpu_relax ( ) ;
clk_disable_unprepare ( crtc - > dc - > hlcdc - > sys_clk ) ;
2015-02-22 18:51:04 +01:00
pinctrl_pm_select_sleep_state ( dev - > dev ) ;
2015-02-05 16:32:33 +01:00
pm_runtime_allow ( dev - > dev ) ;
pm_runtime_put_sync ( dev - > dev ) ;
2015-01-06 11:13:28 +01:00
}
2017-06-30 12:36:44 +03:00
static void atmel_hlcdc_crtc_atomic_enable ( struct drm_crtc * c ,
struct drm_crtc_state * old_state )
2015-01-06 11:13:28 +01:00
{
2015-02-05 16:32:33 +01:00
struct drm_device * dev = c - > dev ;
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
struct regmap * regmap = crtc - > dc - > hlcdc - > regmap ;
unsigned int status ;
pm_runtime_get_sync ( dev - > dev ) ;
pm_runtime_forbid ( dev - > dev ) ;
2015-02-22 18:51:04 +01:00
pinctrl_pm_select_default_state ( dev - > dev ) ;
2015-02-05 16:32:33 +01:00
clk_prepare_enable ( crtc - > dc - > hlcdc - > sys_clk ) ;
regmap_write ( regmap , ATMEL_HLCDC_EN , ATMEL_HLCDC_PIXEL_CLK ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
! ( status & ATMEL_HLCDC_PIXEL_CLK ) )
cpu_relax ( ) ;
regmap_write ( regmap , ATMEL_HLCDC_EN , ATMEL_HLCDC_SYNC ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
! ( status & ATMEL_HLCDC_SYNC ) )
cpu_relax ( ) ;
regmap_write ( regmap , ATMEL_HLCDC_EN , ATMEL_HLCDC_DISP ) ;
while ( ! regmap_read ( regmap , ATMEL_HLCDC_SR , & status ) & &
! ( status & ATMEL_HLCDC_DISP ) )
cpu_relax ( ) ;
pm_runtime_put_sync ( dev - > dev ) ;
drm_crtc_vblank_on ( c ) ;
2015-03-12 19:47:19 +01:00
}
2016-01-06 11:14:15 +01:00
# define ATMEL_HLCDC_RGB444_OUTPUT BIT(0)
# define ATMEL_HLCDC_RGB565_OUTPUT BIT(1)
# define ATMEL_HLCDC_RGB666_OUTPUT BIT(2)
# define ATMEL_HLCDC_RGB888_OUTPUT BIT(3)
# define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0)
2018-08-25 10:56:20 +02:00
static int atmel_hlcdc_connector_output_mode ( struct drm_connector_state * state )
{
struct drm_connector * connector = state - > connector ;
struct drm_display_info * info = & connector - > display_info ;
struct drm_encoder * encoder ;
unsigned int supported_fmts = 0 ;
int j ;
encoder = state - > best_encoder ;
if ( ! encoder )
encoder = connector - > encoder ;
switch ( atmel_hlcdc_encoder_get_bus_fmt ( encoder ) ) {
case 0 :
break ;
case MEDIA_BUS_FMT_RGB444_1X12 :
return ATMEL_HLCDC_RGB444_OUTPUT ;
case MEDIA_BUS_FMT_RGB565_1X16 :
return ATMEL_HLCDC_RGB565_OUTPUT ;
case MEDIA_BUS_FMT_RGB666_1X18 :
return ATMEL_HLCDC_RGB666_OUTPUT ;
case MEDIA_BUS_FMT_RGB888_1X24 :
return ATMEL_HLCDC_RGB888_OUTPUT ;
default :
return - EINVAL ;
}
for ( j = 0 ; j < info - > num_bus_formats ; j + + ) {
switch ( info - > bus_formats [ j ] ) {
case MEDIA_BUS_FMT_RGB444_1X12 :
supported_fmts | = ATMEL_HLCDC_RGB444_OUTPUT ;
break ;
case MEDIA_BUS_FMT_RGB565_1X16 :
supported_fmts | = ATMEL_HLCDC_RGB565_OUTPUT ;
break ;
case MEDIA_BUS_FMT_RGB666_1X18 :
supported_fmts | = ATMEL_HLCDC_RGB666_OUTPUT ;
break ;
case MEDIA_BUS_FMT_RGB888_1X24 :
supported_fmts | = ATMEL_HLCDC_RGB888_OUTPUT ;
break ;
default :
break ;
}
}
return supported_fmts ;
}
2016-01-06 11:14:15 +01:00
static int atmel_hlcdc_crtc_select_output_mode ( struct drm_crtc_state * state )
{
unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK ;
struct atmel_hlcdc_crtc_state * hstate ;
struct drm_connector_state * cstate ;
struct drm_connector * connector ;
struct atmel_hlcdc_crtc * crtc ;
int i ;
crtc = drm_crtc_to_atmel_hlcdc_crtc ( state - > crtc ) ;
2017-07-12 10:13:36 +02:00
for_each_new_connector_in_state ( state - > state , connector , cstate , i ) {
2016-01-06 11:14:15 +01:00
unsigned int supported_fmts = 0 ;
if ( ! cstate - > crtc )
continue ;
2018-08-25 10:56:20 +02:00
supported_fmts = atmel_hlcdc_connector_output_mode ( cstate ) ;
2016-01-06 11:14:15 +01:00
if ( crtc - > dc - > desc - > conflicting_output_formats )
output_fmts & = supported_fmts ;
else
output_fmts | = supported_fmts ;
}
if ( ! output_fmts )
return - EINVAL ;
hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state ( state ) ;
hstate - > output_mode = fls ( output_fmts ) - 1 ;
return 0 ;
}
2015-02-05 16:32:33 +01:00
static int atmel_hlcdc_crtc_atomic_check ( struct drm_crtc * c ,
struct drm_crtc_state * s )
2015-01-06 11:13:28 +01:00
{
2016-01-06 11:14:15 +01:00
int ret ;
2015-02-05 16:32:33 +01:00
2016-01-06 11:14:15 +01:00
ret = atmel_hlcdc_crtc_select_output_mode ( s ) ;
if ( ret )
return ret ;
2016-03-15 18:01:08 +01:00
ret = atmel_hlcdc_plane_prepare_disc_area ( s ) ;
if ( ret )
return ret ;
return atmel_hlcdc_plane_prepare_ahb_routing ( s ) ;
2015-01-06 11:13:28 +01:00
}
2015-07-21 13:28:58 +02:00
static void atmel_hlcdc_crtc_atomic_begin ( struct drm_crtc * c ,
struct drm_crtc_state * old_s )
2015-01-06 11:13:28 +01:00
{
2015-02-05 16:32:33 +01:00
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
if ( c - > state - > event ) {
c - > state - > event - > pipe = drm_crtc_index ( c ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
WARN_ON ( drm_crtc_vblank_get ( c ) ! = 0 ) ;
2015-01-06 11:13:28 +01:00
2015-02-05 16:32:33 +01:00
crtc - > event = c - > state - > event ;
c - > state - > event = NULL ;
2015-01-06 11:13:28 +01:00
}
}
2015-07-21 13:28:58 +02:00
static void atmel_hlcdc_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_s )
2015-02-05 16:32:33 +01:00
{
/* TODO: write common plane control register if available */
}
2015-01-06 11:13:28 +01:00
static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
2017-05-25 15:19:21 +01:00
. mode_valid = atmel_hlcdc_crtc_mode_valid ,
2015-02-05 16:32:33 +01:00
. mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb ,
. atomic_check = atmel_hlcdc_crtc_atomic_check ,
. atomic_begin = atmel_hlcdc_crtc_atomic_begin ,
. atomic_flush = atmel_hlcdc_crtc_atomic_flush ,
2017-06-30 12:36:44 +03:00
. atomic_enable = atmel_hlcdc_crtc_atomic_enable ,
2017-06-30 12:36:45 +03:00
. atomic_disable = atmel_hlcdc_crtc_atomic_disable ,
2015-01-06 11:13:28 +01:00
} ;
static void atmel_hlcdc_crtc_destroy ( struct drm_crtc * c )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
drm_crtc_cleanup ( c ) ;
kfree ( crtc ) ;
}
static void atmel_hlcdc_crtc_finish_page_flip ( struct atmel_hlcdc_crtc * crtc )
{
struct drm_device * dev = crtc - > base . dev ;
unsigned long flags ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
if ( crtc - > event ) {
2016-06-06 11:41:34 -03:00
drm_crtc_send_vblank_event ( & crtc - > base , crtc - > event ) ;
2016-06-06 11:41:41 -03:00
drm_crtc_vblank_put ( & crtc - > base ) ;
2015-01-06 11:13:28 +01:00
crtc - > event = NULL ;
}
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
}
void atmel_hlcdc_crtc_irq ( struct drm_crtc * c )
{
2016-07-04 21:04:49 -03:00
drm_crtc_handle_vblank ( c ) ;
2015-01-06 11:13:28 +01:00
atmel_hlcdc_crtc_finish_page_flip ( drm_crtc_to_atmel_hlcdc_crtc ( c ) ) ;
}
2016-07-11 12:19:40 +02:00
static void atmel_hlcdc_crtc_reset ( struct drm_crtc * crtc )
2016-01-06 11:14:15 +01:00
{
struct atmel_hlcdc_crtc_state * state ;
if ( crtc - > state ) {
2016-04-22 21:28:32 +02:00
__drm_atomic_helper_crtc_destroy_state ( crtc - > state ) ;
2016-01-06 11:14:15 +01:00
state = drm_crtc_state_to_atmel_hlcdc_crtc_state ( crtc - > state ) ;
kfree ( state ) ;
2016-04-22 21:28:32 +02:00
crtc - > state = NULL ;
2016-01-06 11:14:15 +01:00
}
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( state ) {
crtc - > state = & state - > base ;
crtc - > state - > crtc = crtc ;
}
}
static struct drm_crtc_state *
atmel_hlcdc_crtc_duplicate_state ( struct drm_crtc * crtc )
{
struct atmel_hlcdc_crtc_state * state , * cur ;
if ( WARN_ON ( ! crtc - > state ) )
return NULL ;
state = kmalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2016-04-25 12:04:54 +03:00
if ( ! state )
return NULL ;
__drm_atomic_helper_crtc_duplicate_state ( crtc , & state - > base ) ;
2016-01-06 11:14:15 +01:00
cur = drm_crtc_state_to_atmel_hlcdc_crtc_state ( crtc - > state ) ;
state - > output_mode = cur - > output_mode ;
return & state - > base ;
}
static void atmel_hlcdc_crtc_destroy_state ( struct drm_crtc * crtc ,
struct drm_crtc_state * s )
{
struct atmel_hlcdc_crtc_state * state ;
state = drm_crtc_state_to_atmel_hlcdc_crtc_state ( s ) ;
2016-05-09 16:34:09 +02:00
__drm_atomic_helper_crtc_destroy_state ( s ) ;
2016-01-06 11:14:15 +01:00
kfree ( state ) ;
}
2017-02-07 17:16:19 +08:00
static int atmel_hlcdc_crtc_enable_vblank ( struct drm_crtc * c )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
struct regmap * regmap = crtc - > dc - > hlcdc - > regmap ;
/* Enable SOF (Start Of Frame) interrupt for vblank counting */
regmap_write ( regmap , ATMEL_HLCDC_IER , ATMEL_HLCDC_SOF ) ;
return 0 ;
}
static void atmel_hlcdc_crtc_disable_vblank ( struct drm_crtc * c )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
struct regmap * regmap = crtc - > dc - > hlcdc - > regmap ;
regmap_write ( regmap , ATMEL_HLCDC_IDR , ATMEL_HLCDC_SOF ) ;
}
2015-01-06 11:13:28 +01:00
static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
2015-02-05 16:32:33 +01:00
. page_flip = drm_atomic_helper_page_flip ,
. set_config = drm_atomic_helper_set_config ,
2015-01-06 11:13:28 +01:00
. destroy = atmel_hlcdc_crtc_destroy ,
2016-01-06 11:14:15 +01:00
. reset = atmel_hlcdc_crtc_reset ,
. atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state ,
. atomic_destroy_state = atmel_hlcdc_crtc_destroy_state ,
2017-02-07 17:16:19 +08:00
. enable_vblank = atmel_hlcdc_crtc_enable_vblank ,
. disable_vblank = atmel_hlcdc_crtc_disable_vblank ,
2017-06-22 07:03:11 +02:00
. gamma_set = drm_atomic_helper_legacy_gamma_set ,
2015-01-06 11:13:28 +01:00
} ;
int atmel_hlcdc_crtc_create ( struct drm_device * dev )
{
2017-02-06 18:57:19 +01:00
struct atmel_hlcdc_plane * primary = NULL , * cursor = NULL ;
2015-01-06 11:13:28 +01:00
struct atmel_hlcdc_dc * dc = dev - > dev_private ;
struct atmel_hlcdc_crtc * crtc ;
int ret ;
int i ;
crtc = kzalloc ( sizeof ( * crtc ) , GFP_KERNEL ) ;
if ( ! crtc )
return - ENOMEM ;
crtc - > dc = dc ;
2017-02-06 18:57:19 +01:00
for ( i = 0 ; i < ATMEL_HLCDC_MAX_LAYERS ; i + + ) {
if ( ! dc - > layers [ i ] )
continue ;
switch ( dc - > layers [ i ] - > desc - > type ) {
case ATMEL_HLCDC_BASE_LAYER :
primary = atmel_hlcdc_layer_to_plane ( dc - > layers [ i ] ) ;
break ;
case ATMEL_HLCDC_CURSOR_LAYER :
cursor = atmel_hlcdc_layer_to_plane ( dc - > layers [ i ] ) ;
break ;
default :
break ;
}
}
ret = drm_crtc_init_with_planes ( dev , & crtc - > base , & primary - > base ,
& cursor - > base , & atmel_hlcdc_crtc_funcs ,
NULL ) ;
2015-01-06 11:13:28 +01:00
if ( ret < 0 )
goto fail ;
crtc - > id = drm_crtc_index ( & crtc - > base ) ;
2017-02-06 18:57:19 +01:00
for ( i = 0 ; i < ATMEL_HLCDC_MAX_LAYERS ; i + + ) {
struct atmel_hlcdc_plane * overlay ;
2015-01-06 11:13:28 +01:00
2017-02-06 18:57:19 +01:00
if ( dc - > layers [ i ] & &
dc - > layers [ i ] - > desc - > type = = ATMEL_HLCDC_OVERLAY_LAYER ) {
overlay = atmel_hlcdc_layer_to_plane ( dc - > layers [ i ] ) ;
overlay - > base . possible_crtcs = 1 < < crtc - > id ;
}
}
2015-01-06 11:13:28 +01:00
drm_crtc_helper_add ( & crtc - > base , & lcdc_crtc_helper_funcs ) ;
2015-07-16 20:55:34 +02:00
drm_crtc_vblank_reset ( & crtc - > base ) ;
2015-01-06 11:13:28 +01:00
2017-06-22 07:03:11 +02:00
drm_mode_crtc_set_gamma_size ( & crtc - > base , ATMEL_HLCDC_CLUT_SIZE ) ;
drm_crtc_enable_color_mgmt ( & crtc - > base , 0 , false ,
ATMEL_HLCDC_CLUT_SIZE ) ;
2015-01-06 11:13:28 +01:00
dc - > crtc = & crtc - > base ;
return 0 ;
fail :
atmel_hlcdc_crtc_destroy ( & crtc - > base ) ;
return ret ;
}