2013-09-09 10:02:56 +10:00
/*
* Copyright ( C ) 2015 Red Hat , Inc .
* All Rights Reserved .
*
* Authors :
* Dave Airlie
* Alon Levy
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*/
# include "virtgpu_drv.h"
# include <drm/drm_atomic_helper.h>
2018-03-30 15:11:17 +01:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2013-09-09 10:02:56 +10:00
2016-05-26 11:42:52 +02:00
# define XRES_MIN 32
# define YRES_MIN 32
2013-09-09 10:02:56 +10:00
# define XRES_DEF 1024
# define YRES_DEF 768
# define XRES_MAX 8192
# define YRES_MAX 8192
static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
. set_config = drm_atomic_helper_set_config ,
. destroy = drm_crtc_cleanup ,
2016-06-10 00:07:53 +02:00
. page_flip = drm_atomic_helper_page_flip ,
2013-09-09 10:02:56 +10:00
. reset = drm_atomic_helper_crtc_reset ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
} ;
static int
virtio_gpu_framebuffer_surface_dirty ( struct drm_framebuffer * fb ,
struct drm_file * file_priv ,
2018-02-22 21:00:45 -03:00
unsigned int flags , unsigned int color ,
2013-09-09 10:02:56 +10:00
struct drm_clip_rect * clips ,
2018-02-22 21:00:45 -03:00
unsigned int num_clips )
2013-09-09 10:02:56 +10:00
{
struct virtio_gpu_framebuffer * virtio_gpu_fb
= to_virtio_gpu_framebuffer ( fb ) ;
return virtio_gpu_surface_dirty ( virtio_gpu_fb , clips , num_clips ) ;
}
static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
2018-03-30 15:11:17 +01:00
. create_handle = drm_gem_fb_create_handle ,
. destroy = drm_gem_fb_destroy ,
2013-09-09 10:02:56 +10:00
. dirty = virtio_gpu_framebuffer_surface_dirty ,
} ;
int
virtio_gpu_framebuffer_init ( struct drm_device * dev ,
struct virtio_gpu_framebuffer * vgfb ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd ,
2013-09-09 10:02:56 +10:00
struct drm_gem_object * obj )
{
int ret ;
2018-02-22 21:00:00 -03:00
2018-03-30 15:11:17 +01:00
vgfb - > base . obj [ 0 ] = obj ;
2013-09-09 10:02:56 +10:00
2016-11-18 21:52:52 +02:00
drm_helper_mode_fill_fb_struct ( dev , & vgfb - > base , mode_cmd ) ;
2013-09-09 10:02:56 +10:00
ret = drm_framebuffer_init ( dev , & vgfb - > base , & virtio_gpu_fb_funcs ) ;
if ( ret ) {
2018-03-30 15:11:17 +01:00
vgfb - > base . obj [ 0 ] = NULL ;
2013-09-09 10:02:56 +10:00
return ret ;
}
spin_lock_init ( & vgfb - > dirty_lock ) ;
vgfb - > x1 = vgfb - > y1 = INT_MAX ;
vgfb - > x2 = vgfb - > y2 = 0 ;
return 0 ;
}
static void virtio_gpu_crtc_mode_set_nofb ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct virtio_gpu_device * vgdev = dev - > dev_private ;
struct virtio_gpu_output * output = drm_crtc_to_virtio_gpu_output ( crtc ) ;
virtio_gpu_cmd_set_scanout ( vgdev , output - > index , 0 ,
crtc - > mode . hdisplay ,
crtc - > mode . vdisplay , 0 , 0 ) ;
}
2017-06-30 12:36:44 +03:00
static void virtio_gpu_crtc_atomic_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2013-09-09 10:02:56 +10:00
{
2018-08-13 17:28:55 +02:00
struct virtio_gpu_output * output = drm_crtc_to_virtio_gpu_output ( crtc ) ;
output - > enabled = true ;
2013-09-09 10:02:56 +10:00
}
2017-06-30 12:36:45 +03:00
static void virtio_gpu_crtc_atomic_disable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
2013-09-09 10:02:56 +10:00
{
struct drm_device * dev = crtc - > dev ;
struct virtio_gpu_device * vgdev = dev - > dev_private ;
struct virtio_gpu_output * output = drm_crtc_to_virtio_gpu_output ( crtc ) ;
virtio_gpu_cmd_set_scanout ( vgdev , output - > index , 0 , 0 , 0 , 0 , 0 ) ;
2018-08-13 17:28:55 +02:00
output - > enabled = false ;
2013-09-09 10:02:56 +10:00
}
static int virtio_gpu_crtc_atomic_check ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
return 0 ;
}
2016-04-14 10:58:54 -07:00
static void virtio_gpu_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_state )
{
unsigned long flags ;
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
if ( crtc - > state - > event )
drm_crtc_send_vblank_event ( crtc , crtc - > state - > event ) ;
2016-06-10 00:07:53 +02:00
crtc - > state - > event = NULL ;
2016-04-14 10:58:54 -07:00
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
}
2013-09-09 10:02:56 +10:00
static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
. mode_set_nofb = virtio_gpu_crtc_mode_set_nofb ,
. atomic_check = virtio_gpu_crtc_atomic_check ,
2016-04-14 10:58:54 -07:00
. atomic_flush = virtio_gpu_crtc_atomic_flush ,
2017-06-30 12:36:44 +03:00
. atomic_enable = virtio_gpu_crtc_atomic_enable ,
2017-06-30 12:36:45 +03:00
. atomic_disable = virtio_gpu_crtc_atomic_disable ,
2013-09-09 10:02:56 +10:00
} ;
static void virtio_gpu_enc_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
}
static void virtio_gpu_enc_enable ( struct drm_encoder * encoder )
{
}
static void virtio_gpu_enc_disable ( struct drm_encoder * encoder )
{
}
static int virtio_gpu_conn_get_modes ( struct drm_connector * connector )
{
struct virtio_gpu_output * output =
drm_connector_to_virtio_gpu_output ( connector ) ;
struct drm_display_mode * mode = NULL ;
int count , width , height ;
2018-10-30 07:32:06 +01:00
if ( output - > edid ) {
count = drm_add_edid_modes ( connector , output - > edid ) ;
if ( count )
return count ;
}
2013-09-09 10:02:56 +10:00
width = le32_to_cpu ( output - > info . r . width ) ;
height = le32_to_cpu ( output - > info . r . height ) ;
count = drm_add_modes_noedid ( connector , XRES_MAX , YRES_MAX ) ;
if ( width = = 0 | | height = = 0 ) {
width = XRES_DEF ;
height = YRES_DEF ;
drm_set_preferred_mode ( connector , XRES_DEF , YRES_DEF ) ;
} else {
DRM_DEBUG ( " add mode: %dx%d \n " , width , height ) ;
mode = drm_cvt_mode ( connector - > dev , width , height , 60 ,
false , false , false ) ;
mode - > type | = DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
count + + ;
}
return count ;
}
2018-04-24 15:15:24 +02:00
static enum drm_mode_status virtio_gpu_conn_mode_valid ( struct drm_connector * connector ,
2013-09-09 10:02:56 +10:00
struct drm_display_mode * mode )
{
struct virtio_gpu_output * output =
drm_connector_to_virtio_gpu_output ( connector ) ;
int width , height ;
width = le32_to_cpu ( output - > info . r . width ) ;
height = le32_to_cpu ( output - > info . r . height ) ;
if ( ! ( mode - > type & DRM_MODE_TYPE_PREFERRED ) )
return MODE_OK ;
if ( mode - > hdisplay = = XRES_DEF & & mode - > vdisplay = = YRES_DEF )
return MODE_OK ;
if ( mode - > hdisplay < = width & & mode - > hdisplay > = width - 16 & &
mode - > vdisplay < = height & & mode - > vdisplay > = height - 16 )
return MODE_OK ;
DRM_DEBUG ( " del mode: %dx%d \n " , mode - > hdisplay , mode - > vdisplay ) ;
return MODE_BAD ;
}
static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
. mode_set = virtio_gpu_enc_mode_set ,
. enable = virtio_gpu_enc_enable ,
. disable = virtio_gpu_enc_disable ,
} ;
static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
. get_modes = virtio_gpu_conn_get_modes ,
. mode_valid = virtio_gpu_conn_mode_valid ,
} ;
static enum drm_connector_status virtio_gpu_conn_detect (
struct drm_connector * connector ,
bool force )
{
struct virtio_gpu_output * output =
drm_connector_to_virtio_gpu_output ( connector ) ;
if ( output - > info . enabled )
return connector_status_connected ;
else
return connector_status_disconnected ;
}
static void virtio_gpu_conn_destroy ( struct drm_connector * connector )
{
drm_connector_unregister ( connector ) ;
drm_connector_cleanup ( connector ) ;
}
static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
. detect = virtio_gpu_conn_detect ,
2015-12-03 23:14:14 +02:00
. fill_modes = drm_helper_probe_single_connector_modes ,
2013-09-09 10:02:56 +10:00
. destroy = virtio_gpu_conn_destroy ,
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
} ;
static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
. destroy = drm_encoder_cleanup ,
} ;
static int vgdev_output_init ( struct virtio_gpu_device * vgdev , int index )
{
struct drm_device * dev = vgdev - > ddev ;
struct virtio_gpu_output * output = vgdev - > outputs + index ;
struct drm_connector * connector = & output - > conn ;
struct drm_encoder * encoder = & output - > enc ;
struct drm_crtc * crtc = & output - > crtc ;
2016-05-26 11:42:52 +02:00
struct drm_plane * primary , * cursor ;
2013-09-09 10:02:56 +10:00
output - > index = index ;
if ( index = = 0 ) {
output - > info . enabled = cpu_to_le32 ( true ) ;
output - > info . r . width = cpu_to_le32 ( XRES_DEF ) ;
output - > info . r . height = cpu_to_le32 ( YRES_DEF ) ;
}
2016-05-26 11:42:52 +02:00
primary = virtio_gpu_plane_init ( vgdev , DRM_PLANE_TYPE_PRIMARY , index ) ;
if ( IS_ERR ( primary ) )
return PTR_ERR ( primary ) ;
cursor = virtio_gpu_plane_init ( vgdev , DRM_PLANE_TYPE_CURSOR , index ) ;
if ( IS_ERR ( cursor ) )
return PTR_ERR ( cursor ) ;
drm_crtc_init_with_planes ( dev , crtc , primary , cursor ,
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
& virtio_gpu_crtc_funcs , NULL ) ;
2013-09-09 10:02:56 +10:00
drm_crtc_helper_add ( crtc , & virtio_gpu_crtc_helper_funcs ) ;
drm_connector_init ( dev , connector , & virtio_gpu_connector_funcs ,
DRM_MODE_CONNECTOR_VIRTUAL ) ;
drm_connector_helper_add ( connector , & virtio_gpu_conn_helper_funcs ) ;
2018-10-30 07:32:06 +01:00
if ( vgdev - > has_edid )
drm_connector_attach_edid_property ( connector ) ;
2013-09-09 10:02:56 +10:00
drm_encoder_init ( dev , encoder , & virtio_gpu_enc_funcs ,
drm: Pass 'name' to drm_encoder_init()
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.
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, encoder, funcs;
@@
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4;
@@
drm_encoder_init(E1, E2, E3, E4
+ ,NULL
)
v2: 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/1449670818-2966-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 16:20:18 +02:00
DRM_MODE_ENCODER_VIRTUAL , NULL ) ;
2013-09-09 10:02:56 +10:00
drm_encoder_helper_add ( encoder , & virtio_gpu_enc_helper_funcs ) ;
encoder - > possible_crtcs = 1 < < index ;
2018-07-09 10:40:07 +02:00
drm_connector_attach_encoder ( connector , encoder ) ;
2013-09-09 10:02:56 +10:00
drm_connector_register ( connector ) ;
return 0 ;
}
static struct drm_framebuffer *
virtio_gpu_user_framebuffer_create ( struct drm_device * dev ,
struct drm_file * file_priv ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd )
2013-09-09 10:02:56 +10:00
{
struct drm_gem_object * obj = NULL ;
struct virtio_gpu_framebuffer * virtio_gpu_fb ;
int ret ;
2018-09-21 15:47:03 +02:00
if ( mode_cmd - > pixel_format ! = DRM_FORMAT_HOST_XRGB8888 & &
mode_cmd - > pixel_format ! = DRM_FORMAT_HOST_ARGB8888 )
return ERR_PTR ( - ENOENT ) ;
2013-09-09 10:02:56 +10:00
/* lookup object associated with res handle */
2016-05-09 11:04:54 +01:00
obj = drm_gem_object_lookup ( file_priv , mode_cmd - > handles [ 0 ] ) ;
2013-09-09 10:02:56 +10:00
if ( ! obj )
return ERR_PTR ( - EINVAL ) ;
virtio_gpu_fb = kzalloc ( sizeof ( * virtio_gpu_fb ) , GFP_KERNEL ) ;
if ( virtio_gpu_fb = = NULL )
return ERR_PTR ( - ENOMEM ) ;
ret = virtio_gpu_framebuffer_init ( dev , virtio_gpu_fb , mode_cmd , obj ) ;
if ( ret ) {
kfree ( virtio_gpu_fb ) ;
2017-09-29 15:33:39 +05:30
drm_gem_object_put_unlocked ( obj ) ;
2013-09-09 10:02:56 +10:00
return NULL ;
}
return & virtio_gpu_fb - > base ;
}
2016-06-10 00:07:53 +02:00
static void vgdev_atomic_commit_tail ( struct drm_atomic_state * state )
2016-05-31 08:50:47 +02:00
{
2016-06-10 00:07:53 +02:00
struct drm_device * dev = state - > dev ;
2016-05-31 08:50:47 +02:00
drm_atomic_helper_commit_modeset_disables ( dev , state ) ;
drm_atomic_helper_commit_modeset_enables ( dev , state ) ;
2016-10-18 15:32:30 -02:00
drm_atomic_helper_commit_planes ( dev , state , 0 ) ;
2016-05-31 08:50:47 +02:00
2016-06-10 00:07:53 +02:00
drm_atomic_helper_commit_hw_done ( state ) ;
2016-05-31 08:50:47 +02:00
drm_atomic_helper_wait_for_vblanks ( dev , state ) ;
drm_atomic_helper_cleanup_planes ( dev , state ) ;
}
2017-01-02 11:16:13 +02:00
static const struct drm_mode_config_helper_funcs virtio_mode_config_helpers = {
2016-06-10 00:07:53 +02:00
. atomic_commit_tail = vgdev_atomic_commit_tail ,
} ;
2013-09-09 10:02:56 +10:00
static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
. fb_create = virtio_gpu_user_framebuffer_create ,
. atomic_check = drm_atomic_helper_check ,
2016-06-10 00:07:53 +02:00
. atomic_commit = drm_atomic_helper_commit ,
2013-09-09 10:02:56 +10:00
} ;
2019-01-08 11:59:30 -03:00
void virtio_gpu_modeset_init ( struct virtio_gpu_device * vgdev )
2013-09-09 10:02:56 +10:00
{
int i ;
drm_mode_config_init ( vgdev - > ddev ) ;
2018-09-21 15:47:03 +02:00
vgdev - > ddev - > mode_config . quirk_addfb_prefer_host_byte_order = true ;
2016-06-10 00:07:53 +02:00
vgdev - > ddev - > mode_config . funcs = & virtio_gpu_mode_funcs ;
vgdev - > ddev - > mode_config . helper_private = & virtio_mode_config_helpers ;
2013-09-09 10:02:56 +10:00
/* modes will be validated against the framebuffer size */
vgdev - > ddev - > mode_config . min_width = XRES_MIN ;
vgdev - > ddev - > mode_config . min_height = YRES_MIN ;
vgdev - > ddev - > mode_config . max_width = XRES_MAX ;
vgdev - > ddev - > mode_config . max_height = YRES_MAX ;
for ( i = 0 ; i < vgdev - > num_scanouts ; + + i )
vgdev_output_init ( vgdev , i ) ;
2018-02-22 20:59:47 -03:00
drm_mode_config_reset ( vgdev - > ddev ) ;
2013-09-09 10:02:56 +10:00
}
void virtio_gpu_modeset_fini ( struct virtio_gpu_device * vgdev )
{
2018-10-30 07:32:06 +01:00
int i ;
for ( i = 0 ; i < vgdev - > num_scanouts ; + + i )
kfree ( vgdev - > outputs [ i ] . edid ) ;
2013-09-09 10:02:56 +10:00
drm_mode_config_cleanup ( vgdev - > ddev ) ;
}