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 >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
2015-02-22 18:51:04 +01:00
# include <linux/pinctrl/consumer.h>
2015-01-06 11:13:28 +01:00
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drmP.h>
# include <video/videomode.h>
# 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 )
2015-02-05 16:32:33 +01:00
* @ enabled : CRTC state
2015-01-06 11:13:28 +01:00
*/
struct atmel_hlcdc_crtc {
struct drm_crtc base ;
struct atmel_hlcdc_dc * dc ;
struct drm_pending_vblank_event * event ;
int id ;
2015-02-05 16:32:33 +01:00
bool enabled ;
2015-01-06 11:13:28 +01:00
} ;
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 ;
unsigned int cfg ;
int div ;
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 ) ) ;
2015-02-25 18:44:51 +01:00
cfg = 0 ;
2015-01-06 11:13:28 +01:00
prate = clk_get_rate ( crtc - > dc - > hlcdc - > sys_clk ) ;
2015-02-05 16:32:33 +01:00
mode_rate = adj - > crtc_clock * 1000 ;
2015-01-06 11:13:28 +01:00
if ( ( prate / 2 ) < mode_rate ) {
prate * = 2 ;
cfg | = ATMEL_HLCDC_CLKSEL ;
}
div = DIV_ROUND_UP ( prate , mode_rate ) ;
if ( div < 2 )
div = 2 ;
cfg | = ATMEL_HLCDC_CLKDIV ( div ) ;
regmap_update_bits ( regmap , ATMEL_HLCDC_CFG ( 0 ) ,
ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
ATMEL_HLCDC_CLKPOL , cfg ) ;
cfg = 0 ;
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 ;
2016-01-06 11:14:15 +01: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
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 ) ;
}
2016-01-05 18:27:49 +01:00
static bool atmel_hlcdc_crtc_mode_fixup ( struct drm_crtc * c ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
return atmel_hlcdc_dc_mode_valid ( crtc - > dc , adjusted_mode ) = = MODE_OK ;
}
2015-02-05 16:32:33 +01:00
static void atmel_hlcdc_crtc_disable ( struct drm_crtc * c )
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 ;
if ( ! crtc - > enabled )
return ;
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 ) ;
crtc - > enabled = false ;
2015-01-06 11:13:28 +01:00
}
2015-02-05 16:32:33 +01:00
static void atmel_hlcdc_crtc_enable ( struct drm_crtc * c )
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 ;
if ( crtc - > enabled )
return ;
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 ) ;
crtc - > enabled = true ;
2015-01-06 11:13:28 +01:00
}
2015-03-12 19:47:19 +01:00
void atmel_hlcdc_crtc_suspend ( struct drm_crtc * c )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
if ( crtc - > enabled ) {
atmel_hlcdc_crtc_disable ( c ) ;
/* save enable state for resume */
crtc - > enabled = true ;
}
}
void atmel_hlcdc_crtc_resume ( struct drm_crtc * c )
{
struct atmel_hlcdc_crtc * crtc = drm_crtc_to_atmel_hlcdc_crtc ( c ) ;
if ( crtc - > enabled ) {
crtc - > enabled = false ;
atmel_hlcdc_crtc_enable ( c ) ;
}
}
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)
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 ) ;
for_each_connector_in_state ( state - > state , connector , cstate , i ) {
struct drm_display_info * info = & connector - > display_info ;
unsigned int supported_fmts = 0 ;
int j ;
if ( ! cstate - > crtc )
continue ;
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 ;
}
}
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 = {
2016-01-05 18:27:49 +01:00
. mode_fixup = atmel_hlcdc_crtc_mode_fixup ,
2015-02-05 16:32:33 +01:00
. mode_set = drm_helper_crtc_mode_set ,
. mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb ,
. mode_set_base = drm_helper_crtc_mode_set_base ,
2015-01-06 11:13:28 +01:00
. disable = atmel_hlcdc_crtc_disable ,
2015-02-05 16:32:33 +01:00
. enable = atmel_hlcdc_crtc_enable ,
. atomic_check = atmel_hlcdc_crtc_atomic_check ,
. atomic_begin = atmel_hlcdc_crtc_atomic_begin ,
. atomic_flush = atmel_hlcdc_crtc_atomic_flush ,
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 ) ;
}
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 ,
2015-01-06 11:13:28 +01:00
} ;
int atmel_hlcdc_crtc_create ( struct drm_device * dev )
{
struct atmel_hlcdc_dc * dc = dev - > dev_private ;
struct atmel_hlcdc_planes * planes = dc - > planes ;
struct atmel_hlcdc_crtc * crtc ;
int ret ;
int i ;
crtc = kzalloc ( sizeof ( * crtc ) , GFP_KERNEL ) ;
if ( ! crtc )
return - ENOMEM ;
crtc - > dc = dc ;
ret = drm_crtc_init_with_planes ( dev , & crtc - > base ,
& planes - > primary - > base ,
planes - > cursor ? & planes - > cursor - > base : NULL ,
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
& 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 ) ;
if ( planes - > cursor )
planes - > cursor - > base . possible_crtcs = 1 < < crtc - > id ;
for ( i = 0 ; i < planes - > noverlays ; i + + )
planes - > overlays [ i ] - > base . possible_crtcs = 1 < < crtc - > id ;
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
dc - > crtc = & crtc - > base ;
return 0 ;
fail :
atmel_hlcdc_crtc_destroy ( & crtc - > base ) ;
return ret ;
}