2016-06-10 16:55:59 +02:00
/*
* Copyright ( C ) 2016 Noralf Trønnes
*
* 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.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_plane_helper.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2016-06-10 16:55:59 +02:00
# include <drm/drm_simple_kms_helper.h>
# include <linux/slab.h>
/**
* DOC : overview
*
* This helper library provides helpers for drivers for simple display
* hardware .
*
* drm_simple_display_pipe_init ( ) initializes a simple display pipeline
* which has only one full - screen scanout buffer feeding one output . The
2016-12-29 21:48:26 +01:00
* pipeline is represented by & struct drm_simple_display_pipe and binds
2016-06-10 16:55:59 +02:00
* together & drm_plane , & drm_crtc and & drm_encoder structures into one fixed
* entity . Some flexibility for code reuse is provided through a separately
* allocated & drm_connector object and supporting optional & drm_bridge
* encoder drivers .
*/
static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = {
. destroy = drm_encoder_cleanup ,
} ;
2018-02-20 08:28:59 +01:00
static enum drm_mode_status
drm_simple_kms_crtc_mode_valid ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode )
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( crtc , struct drm_simple_display_pipe , crtc ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > mode_valid )
/* Anything goes */
return MODE_OK ;
return pipe - > funcs - > mode_valid ( crtc , mode ) ;
}
2016-08-23 08:25:40 +02:00
static int drm_simple_kms_crtc_check ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
2017-07-12 10:13:29 +02:00
bool has_primary = state - > plane_mask &
2018-06-26 22:47:07 +03:00
drm_plane_mask ( crtc - > primary ) ;
2017-07-12 10:13:29 +02:00
/* We always want to have an active plane with an active CRTC */
if ( has_primary ! = state - > enable )
return - EINVAL ;
2016-08-23 08:25:40 +02:00
return drm_atomic_add_affected_planes ( state - > state , crtc ) ;
}
2017-06-30 12:36:44 +03:00
static void drm_simple_kms_crtc_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2016-06-10 16:55:59 +02:00
{
2018-03-22 22:27:37 +02:00
struct drm_plane * plane ;
2016-06-10 16:55:59 +02:00
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( crtc , struct drm_simple_display_pipe , crtc ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > enable )
return ;
2018-03-22 22:27:37 +02:00
plane = & pipe - > plane ;
pipe - > funcs - > enable ( pipe , crtc - > state , plane - > state ) ;
2016-06-10 16:55:59 +02:00
}
2017-06-30 12:36:45 +03:00
static void drm_simple_kms_crtc_disable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2016-06-10 16:55:59 +02:00
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( crtc , struct drm_simple_display_pipe , crtc ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > disable )
return ;
pipe - > funcs - > disable ( pipe ) ;
}
static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
2018-02-20 08:28:59 +01:00
. mode_valid = drm_simple_kms_crtc_mode_valid ,
2016-08-23 08:25:40 +02:00
. atomic_check = drm_simple_kms_crtc_check ,
2017-06-30 12:36:44 +03:00
. atomic_enable = drm_simple_kms_crtc_enable ,
2017-06-30 12:36:45 +03:00
. atomic_disable = drm_simple_kms_crtc_disable ,
2016-06-10 16:55:59 +02:00
} ;
2018-02-12 10:52:51 +02:00
static int drm_simple_kms_crtc_enable_vblank ( struct drm_crtc * crtc )
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( crtc , struct drm_simple_display_pipe , crtc ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > enable_vblank )
return 0 ;
return pipe - > funcs - > enable_vblank ( pipe ) ;
}
static void drm_simple_kms_crtc_disable_vblank ( struct drm_crtc * crtc )
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( crtc , struct drm_simple_display_pipe , crtc ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > disable_vblank )
return ;
pipe - > funcs - > disable_vblank ( pipe ) ;
}
2016-06-10 16:55:59 +02:00
static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
. reset = drm_atomic_helper_crtc_reset ,
. destroy = drm_crtc_cleanup ,
. set_config = drm_atomic_helper_set_config ,
. page_flip = drm_atomic_helper_page_flip ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2018-02-12 10:52:51 +02:00
. enable_vblank = drm_simple_kms_crtc_enable_vblank ,
. disable_vblank = drm_simple_kms_crtc_disable_vblank ,
2016-06-10 16:55:59 +02:00
} ;
static int drm_simple_kms_plane_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * plane_state )
{
struct drm_simple_display_pipe * pipe ;
struct drm_crtc_state * crtc_state ;
int ret ;
pipe = container_of ( plane , struct drm_simple_display_pipe , plane ) ;
2017-03-01 10:22:10 +01:00
crtc_state = drm_atomic_get_new_crtc_state ( plane_state - > state ,
& pipe - > crtc ) ;
2016-07-26 19:07:04 +03:00
2017-11-01 22:16:19 +02:00
ret = drm_atomic_helper_check_plane_state ( plane_state , crtc_state ,
DRM_PLANE_HELPER_NO_SCALING ,
DRM_PLANE_HELPER_NO_SCALING ,
false , true ) ;
2016-06-10 16:55:59 +02:00
if ( ret )
return ret ;
2016-07-26 19:07:04 +03:00
if ( ! plane_state - > visible )
2018-02-22 08:09:19 +02:00
return 0 ;
2016-06-10 16:55:59 +02:00
if ( ! pipe - > funcs | | ! pipe - > funcs - > check )
return 0 ;
return pipe - > funcs - > check ( pipe , plane_state , crtc_state ) ;
}
static void drm_simple_kms_plane_atomic_update ( struct drm_plane * plane ,
2017-03-20 16:36:15 -07:00
struct drm_plane_state * old_pstate )
2016-06-10 16:55:59 +02:00
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( plane , struct drm_simple_display_pipe , plane ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > update )
return ;
2017-03-20 16:36:15 -07:00
pipe - > funcs - > update ( pipe , old_pstate ) ;
2016-06-10 16:55:59 +02:00
}
2016-10-02 19:01:24 +02:00
static int drm_simple_kms_plane_prepare_fb ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( plane , struct drm_simple_display_pipe , plane ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > prepare_fb )
return 0 ;
return pipe - > funcs - > prepare_fb ( pipe , state ) ;
}
static void drm_simple_kms_plane_cleanup_fb ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
struct drm_simple_display_pipe * pipe ;
pipe = container_of ( plane , struct drm_simple_display_pipe , plane ) ;
if ( ! pipe - > funcs | | ! pipe - > funcs - > cleanup_fb )
return ;
pipe - > funcs - > cleanup_fb ( pipe , state ) ;
}
2018-10-25 09:26:35 -07:00
static bool drm_simple_kms_format_mod_supported ( struct drm_plane * plane ,
uint32_t format ,
uint64_t modifier )
{
return modifier = = DRM_FORMAT_MOD_LINEAR ;
}
2016-06-10 16:55:59 +02:00
static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
2016-10-02 19:01:24 +02:00
. prepare_fb = drm_simple_kms_plane_prepare_fb ,
. cleanup_fb = drm_simple_kms_plane_cleanup_fb ,
2016-06-10 16:55:59 +02:00
. atomic_check = drm_simple_kms_plane_atomic_check ,
. atomic_update = drm_simple_kms_plane_atomic_update ,
} ;
static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
. destroy = drm_plane_cleanup ,
. reset = drm_atomic_helper_plane_reset ,
. atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_plane_destroy_state ,
2018-10-25 09:26:35 -07:00
. format_mod_supported = drm_simple_kms_format_mod_supported ,
2016-06-10 16:55:59 +02:00
} ;
2016-08-25 11:04:34 +02:00
/**
* drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
* @ pipe : simple display pipe object
* @ bridge : bridge to attach
*
* Makes it possible to still use the drm_simple_display_pipe helpers when
* a DRM bridge has to be used .
*
* Note that you probably want to initialize the pipe by passing a NULL
* connector to drm_simple_display_pipe_init ( ) .
*
* Returns :
* Zero on success , negative error code on failure .
*/
int drm_simple_display_pipe_attach_bridge ( struct drm_simple_display_pipe * pipe ,
struct drm_bridge * bridge )
{
2016-11-28 17:59:08 +02:00
return drm_bridge_attach ( & pipe - > encoder , bridge , NULL ) ;
2016-08-25 11:04:34 +02:00
}
EXPORT_SYMBOL ( drm_simple_display_pipe_attach_bridge ) ;
2016-06-10 16:55:59 +02:00
/**
* drm_simple_display_pipe_init - Initialize a simple display pipeline
* @ dev : DRM device
* @ pipe : simple display pipe object to initialize
* @ funcs : callbacks for the display pipe ( optional )
2016-08-12 22:48:37 +02:00
* @ formats : array of supported formats ( DRM_FORMAT \ _ \ * )
2016-06-10 16:55:59 +02:00
* @ format_count : number of elements in @ formats
2017-07-23 20:46:38 -07:00
* @ format_modifiers : array of formats modifiers
2016-08-25 11:04:33 +02:00
* @ connector : connector to attach and register ( optional )
2016-06-10 16:55:59 +02:00
*
* Sets up a display pipeline which consist of a really simple
2016-08-25 11:04:33 +02:00
* plane - crtc - encoder pipe .
*
* If a connector is supplied , the pipe will be coupled with the provided
* connector . You may supply a NULL connector when using drm bridges , that
* handle connectors themselves ( see drm_simple_display_pipe_attach_bridge ( ) ) .
*
2016-06-10 16:55:59 +02:00
* Teardown of a simple display pipe is all handled automatically by the drm
* core through calling drm_mode_config_cleanup ( ) . Drivers afterwards need to
* release the memory for the structure themselves .
*
* Returns :
* Zero on success , negative error code on failure .
*/
int drm_simple_display_pipe_init ( struct drm_device * dev ,
struct drm_simple_display_pipe * pipe ,
const struct drm_simple_display_pipe_funcs * funcs ,
const uint32_t * formats , unsigned int format_count ,
2017-07-23 20:46:38 -07:00
const uint64_t * format_modifiers ,
2016-06-10 16:55:59 +02:00
struct drm_connector * connector )
{
struct drm_encoder * encoder = & pipe - > encoder ;
struct drm_plane * plane = & pipe - > plane ;
struct drm_crtc * crtc = & pipe - > crtc ;
int ret ;
pipe - > connector = connector ;
pipe - > funcs = funcs ;
drm_plane_helper_add ( plane , & drm_simple_kms_plane_helper_funcs ) ;
ret = drm_universal_plane_init ( dev , plane , 0 ,
& drm_simple_kms_plane_funcs ,
formats , format_count ,
2017-07-23 20:46:38 -07:00
format_modifiers ,
2016-06-10 16:55:59 +02:00
DRM_PLANE_TYPE_PRIMARY , NULL ) ;
if ( ret )
return ret ;
drm_crtc_helper_add ( crtc , & drm_simple_kms_crtc_helper_funcs ) ;
ret = drm_crtc_init_with_planes ( dev , crtc , plane , NULL ,
& drm_simple_kms_crtc_funcs , NULL ) ;
if ( ret )
return ret ;
2018-06-26 22:47:08 +03:00
encoder - > possible_crtcs = drm_crtc_mask ( crtc ) ;
2016-06-10 16:55:59 +02:00
ret = drm_encoder_init ( dev , encoder , & drm_simple_kms_encoder_funcs ,
DRM_MODE_ENCODER_NONE , NULL ) ;
2016-08-25 11:04:33 +02:00
if ( ret | | ! connector )
2016-06-10 16:55:59 +02:00
return ret ;
2018-07-09 10:40:07 +02:00
return drm_connector_attach_encoder ( connector , encoder ) ;
2016-06-10 16:55:59 +02:00
}
EXPORT_SYMBOL ( drm_simple_display_pipe_init ) ;
MODULE_LICENSE ( " GPL " ) ;