2012-03-21 05:55:26 +04:00
/* exynos_drm_vidi.c
*
* Copyright ( C ) 2012 Samsung Electronics Co . Ltd
* Authors :
* Inki Dae < inki . dae @ samsung . 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 .
*
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
2012-03-21 05:55:26 +04:00
# include <linux/kernel.h>
# include <linux/platform_device.h>
2014-11-24 08:55:41 +03:00
# include <linux/component.h>
2012-03-21 05:55:26 +04:00
# include <drm/exynos_drm.h>
2012-10-02 21:01:07 +04:00
# include <drm/drm_edid.h>
# include <drm/drm_crtc_helper.h>
2012-03-21 05:55:26 +04:00
# include "exynos_drm_drv.h"
# include "exynos_drm_crtc.h"
# include "exynos_drm_encoder.h"
2013-08-13 03:46:40 +04:00
# include "exynos_drm_vidi.h"
2012-03-21 05:55:26 +04:00
/* vidi has totally three virtual windows. */
# define WINDOWS_NR 3
2014-01-31 01:38:07 +04:00
# define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector )
2012-03-21 05:55:26 +04:00
struct vidi_win_data {
unsigned int offset_x ;
unsigned int offset_y ;
unsigned int ovl_width ;
unsigned int ovl_height ;
unsigned int fb_width ;
unsigned int fb_height ;
unsigned int bpp ;
dma_addr_t dma_addr ;
unsigned int buf_offsize ;
unsigned int line_size ; /* bytes */
bool enabled ;
} ;
struct vidi_context {
2014-11-17 11:54:22 +03:00
struct exynos_drm_display display ;
2014-11-24 08:55:41 +03:00
struct platform_device * pdev ;
2014-02-19 16:02:55 +04:00
struct drm_device * drm_dev ;
2015-01-18 12:16:23 +03:00
struct exynos_drm_crtc * crtc ;
2014-01-31 01:38:07 +04:00
struct drm_encoder * encoder ;
struct drm_connector connector ;
2012-03-21 05:55:26 +04:00
struct vidi_win_data win_data [ WINDOWS_NR ] ;
struct edid * raw_edid ;
unsigned int clkdiv ;
unsigned int default_win ;
unsigned long irq_flags ;
unsigned int connected ;
bool vblank_on ;
bool suspended ;
2012-09-19 06:02:43 +04:00
bool direct_vblank ;
2012-03-21 05:55:26 +04:00
struct work_struct work ;
struct mutex lock ;
2014-02-19 16:02:55 +04:00
int pipe ;
2012-03-21 05:55:26 +04:00
} ;
2014-11-17 11:54:23 +03:00
static inline struct vidi_context * display_to_vidi ( struct exynos_drm_display * d )
{
return container_of ( d , struct vidi_context , display ) ;
}
2012-03-21 05:55:26 +04:00
static const char fake_edid_info [ ] = {
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 , 0x4c , 0x2d , 0x05 , 0x05 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x30 , 0x12 , 0x01 , 0x03 , 0x80 , 0x10 , 0x09 , 0x78 ,
0x0a , 0xee , 0x91 , 0xa3 , 0x54 , 0x4c , 0x99 , 0x26 , 0x0f , 0x50 , 0x54 , 0xbd ,
0xee , 0x00 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x66 , 0x21 , 0x50 , 0xb0 , 0x51 , 0x00 ,
0x1b , 0x30 , 0x40 , 0x70 , 0x36 , 0x00 , 0xa0 , 0x5a , 0x00 , 0x00 , 0x00 , 0x1e ,
0x01 , 0x1d , 0x00 , 0x72 , 0x51 , 0xd0 , 0x1e , 0x20 , 0x6e , 0x28 , 0x55 , 0x00 ,
0xa0 , 0x5a , 0x00 , 0x00 , 0x00 , 0x1e , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x18 ,
0x4b , 0x1a , 0x44 , 0x17 , 0x00 , 0x0a , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 ,
0x00 , 0x00 , 0x00 , 0xfc , 0x00 , 0x53 , 0x41 , 0x4d , 0x53 , 0x55 , 0x4e , 0x47 ,
0x0a , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x01 , 0xbc , 0x02 , 0x03 , 0x1e , 0xf1 ,
0x46 , 0x84 , 0x05 , 0x03 , 0x10 , 0x20 , 0x22 , 0x23 , 0x09 , 0x07 , 0x07 , 0x83 ,
0x01 , 0x00 , 0x00 , 0xe2 , 0x00 , 0x0f , 0x67 , 0x03 , 0x0c , 0x00 , 0x10 , 0x00 ,
0xb8 , 0x2d , 0x01 , 0x1d , 0x80 , 0x18 , 0x71 , 0x1c , 0x16 , 0x20 , 0x58 , 0x2c ,
0x25 , 0x00 , 0xa0 , 0x5a , 0x00 , 0x00 , 0x00 , 0x9e , 0x8c , 0x0a , 0xd0 , 0x8a ,
0x20 , 0xe0 , 0x2d , 0x10 , 0x10 , 0x3e , 0x96 , 0x00 , 0xa0 , 0x5a , 0x00 , 0x00 ,
0x00 , 0x18 , 0x02 , 0x3a , 0x80 , 0x18 , 0x71 , 0x38 , 0x2d , 0x40 , 0x58 , 0x2c ,
0x45 , 0x00 , 0xa0 , 0x5a , 0x00 , 0x00 , 0x00 , 0x1e , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x06
} ;
2015-01-18 12:16:23 +03:00
static void vidi_apply ( struct exynos_drm_crtc * crtc )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
struct exynos_drm_crtc_ops * crtc_ops = crtc - > ops ;
2012-03-21 05:55:26 +04:00
struct vidi_win_data * win_data ;
int i ;
for ( i = 0 ; i < WINDOWS_NR ; i + + ) {
win_data = & ctx - > win_data [ i ] ;
2015-01-18 12:16:23 +03:00
if ( win_data - > enabled & & ( crtc_ops & & crtc_ops - > win_commit ) )
crtc_ops - > win_commit ( crtc , i ) ;
2012-03-21 05:55:26 +04:00
}
}
2015-01-18 12:16:23 +03:00
static int vidi_enable_vblank ( struct exynos_drm_crtc * crtc )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2012-03-21 05:55:26 +04:00
if ( ctx - > suspended )
return - EPERM ;
if ( ! test_and_set_bit ( 0 , & ctx - > irq_flags ) )
ctx - > vblank_on = true ;
2012-09-19 06:02:43 +04:00
ctx - > direct_vblank = true ;
/*
* in case of page flip request , vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
2015-01-18 12:16:23 +03:00
* that function will be called by crtc_ops - > win_commit callback
2012-09-19 06:02:43 +04:00
*/
schedule_work ( & ctx - > work ) ;
2012-03-21 05:55:26 +04:00
return 0 ;
}
2015-01-18 12:16:23 +03:00
static void vidi_disable_vblank ( struct exynos_drm_crtc * crtc )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2012-03-21 05:55:26 +04:00
if ( ctx - > suspended )
return ;
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) )
ctx - > vblank_on = false ;
}
2015-01-18 12:16:23 +03:00
static void vidi_win_mode_set ( struct exynos_drm_crtc * crtc ,
2014-11-03 23:13:27 +03:00
struct exynos_drm_plane * plane )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2012-03-21 05:55:26 +04:00
struct vidi_win_data * win_data ;
int win ;
unsigned long offset ;
2014-11-03 23:13:27 +03:00
if ( ! plane ) {
DRM_ERROR ( " plane is NULL \n " ) ;
2012-03-21 05:55:26 +04:00
return ;
}
2014-11-03 23:13:27 +03:00
win = plane - > zpos ;
2012-03-21 05:55:26 +04:00
if ( win = = DEFAULT_ZPOS )
win = ctx - > default_win ;
2013-05-28 11:01:21 +04:00
if ( win < 0 | | win > = WINDOWS_NR )
2012-03-21 05:55:26 +04:00
return ;
2014-11-03 23:13:27 +03:00
offset = plane - > fb_x * ( plane - > bpp > > 3 ) ;
offset + = plane - > fb_y * plane - > pitch ;
2012-03-21 05:55:26 +04:00
2014-11-03 23:13:27 +03:00
DRM_DEBUG_KMS ( " offset = 0x%lx, pitch = %x \n " , offset , plane - > pitch ) ;
2012-03-21 05:55:26 +04:00
win_data = & ctx - > win_data [ win ] ;
2014-11-03 23:13:27 +03:00
win_data - > offset_x = plane - > crtc_x ;
win_data - > offset_y = plane - > crtc_y ;
win_data - > ovl_width = plane - > crtc_width ;
win_data - > ovl_height = plane - > crtc_height ;
win_data - > fb_width = plane - > fb_width ;
win_data - > fb_height = plane - > fb_height ;
win_data - > dma_addr = plane - > dma_addr [ 0 ] + offset ;
win_data - > bpp = plane - > bpp ;
win_data - > buf_offsize = ( plane - > fb_width - plane - > crtc_width ) *
( plane - > bpp > > 3 ) ;
win_data - > line_size = plane - > crtc_width * ( plane - > bpp > > 3 ) ;
2012-03-21 05:55:26 +04:00
/*
* some parts of win_data should be transferred to user side
* through specific ioctl .
*/
DRM_DEBUG_KMS ( " offset_x = %d, offset_y = %d \n " ,
win_data - > offset_x , win_data - > offset_y ) ;
DRM_DEBUG_KMS ( " ovl_width = %d, ovl_height = %d \n " ,
win_data - > ovl_width , win_data - > ovl_height ) ;
2012-12-10 10:44:58 +04:00
DRM_DEBUG_KMS ( " paddr = 0x%lx \n " , ( unsigned long ) win_data - > dma_addr ) ;
2012-03-21 05:55:26 +04:00
DRM_DEBUG_KMS ( " fb_width = %d, crtc_width = %d \n " ,
2014-11-03 23:13:27 +03:00
plane - > fb_width , plane - > crtc_width ) ;
2012-03-21 05:55:26 +04:00
}
2015-01-18 12:16:23 +03:00
static void vidi_win_commit ( struct exynos_drm_crtc * crtc , int zpos )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2012-03-21 05:55:26 +04:00
struct vidi_win_data * win_data ;
int win = zpos ;
if ( ctx - > suspended )
return ;
if ( win = = DEFAULT_ZPOS )
win = ctx - > default_win ;
2013-05-28 11:01:21 +04:00
if ( win < 0 | | win > = WINDOWS_NR )
2012-03-21 05:55:26 +04:00
return ;
win_data = & ctx - > win_data [ win ] ;
win_data - > enabled = true ;
2014-04-22 09:45:32 +04:00
DRM_DEBUG_KMS ( " dma_addr = %pad \n " , & win_data - > dma_addr ) ;
2012-03-21 05:55:26 +04:00
if ( ctx - > vblank_on )
schedule_work ( & ctx - > work ) ;
}
2015-01-18 12:16:23 +03:00
static void vidi_win_disable ( struct exynos_drm_crtc * crtc , int zpos )
2012-03-21 05:55:26 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2012-03-21 05:55:26 +04:00
struct vidi_win_data * win_data ;
int win = zpos ;
if ( win = = DEFAULT_ZPOS )
win = ctx - > default_win ;
2013-05-28 11:01:21 +04:00
if ( win < 0 | | win > = WINDOWS_NR )
2012-03-21 05:55:26 +04:00
return ;
win_data = & ctx - > win_data [ win ] ;
win_data - > enabled = false ;
/* TODO. */
}
2015-01-18 12:16:23 +03:00
static int vidi_power_on ( struct exynos_drm_crtc * crtc , bool enable )
2014-01-31 01:19:27 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2014-01-31 01:19:27 +04:00
DRM_DEBUG_KMS ( " %s \n " , __FILE__ ) ;
if ( enable ! = false & & enable ! = true )
return - EINVAL ;
if ( enable ) {
ctx - > suspended = false ;
/* if vblank was enabled status, enable it again. */
if ( test_and_clear_bit ( 0 , & ctx - > irq_flags ) )
2015-01-18 12:16:23 +03:00
vidi_enable_vblank ( crtc ) ;
2014-01-31 01:19:27 +04:00
2015-01-18 12:16:23 +03:00
vidi_apply ( crtc ) ;
2014-01-31 01:19:27 +04:00
} else {
ctx - > suspended = true ;
}
return 0 ;
}
2015-01-18 12:16:23 +03:00
static void vidi_dpms ( struct exynos_drm_crtc * crtc , int mode )
2014-01-31 01:19:27 +04:00
{
2015-01-18 12:16:23 +03:00
struct vidi_context * ctx = crtc - > ctx ;
2014-01-31 01:19:27 +04:00
DRM_DEBUG_KMS ( " %d \n " , mode ) ;
mutex_lock ( & ctx - > lock ) ;
switch ( mode ) {
case DRM_MODE_DPMS_ON :
2015-01-18 12:16:23 +03:00
vidi_power_on ( crtc , true ) ;
2014-01-31 01:19:27 +04:00
break ;
case DRM_MODE_DPMS_STANDBY :
case DRM_MODE_DPMS_SUSPEND :
case DRM_MODE_DPMS_OFF :
2015-01-18 12:16:23 +03:00
vidi_power_on ( crtc , false ) ;
2014-01-31 01:19:27 +04:00
break ;
default :
DRM_DEBUG_KMS ( " unspecified mode %d \n " , mode ) ;
break ;
}
mutex_unlock ( & ctx - > lock ) ;
}
2015-01-18 12:16:23 +03:00
static int vidi_ctx_initialize ( struct vidi_context * ctx ,
2014-05-09 09:25:20 +04:00
struct drm_device * drm_dev )
2014-02-19 16:02:55 +04:00
{
2014-05-09 09:25:20 +04:00
struct exynos_drm_private * priv = drm_dev - > dev_private ;
2014-02-19 16:02:55 +04:00
2014-11-26 21:43:27 +03:00
ctx - > drm_dev = drm_dev ;
2014-11-04 23:44:47 +03:00
ctx - > pipe = priv - > pipe + + ;
2014-02-19 16:02:55 +04:00
return 0 ;
}
2015-01-18 12:16:23 +03:00
static struct exynos_drm_crtc_ops vidi_crtc_ops = {
2014-01-31 01:19:02 +04:00
. dpms = vidi_dpms ,
. enable_vblank = vidi_enable_vblank ,
. disable_vblank = vidi_disable_vblank ,
. win_mode_set = vidi_win_mode_set ,
. win_commit = vidi_win_commit ,
. win_disable = vidi_win_disable ,
2012-03-21 05:55:26 +04:00
} ;
static void vidi_fake_vblank_handler ( struct work_struct * work )
{
struct vidi_context * ctx = container_of ( work , struct vidi_context ,
work ) ;
2014-02-19 16:02:55 +04:00
if ( ctx - > pipe < 0 )
2012-03-21 05:55:26 +04:00
return ;
/* refresh rate is about 50Hz. */
usleep_range ( 16000 , 20000 ) ;
2012-09-19 06:02:43 +04:00
mutex_lock ( & ctx - > lock ) ;
if ( ctx - > direct_vblank ) {
2014-02-19 16:02:55 +04:00
drm_handle_vblank ( ctx - > drm_dev , ctx - > pipe ) ;
2012-09-19 06:02:43 +04:00
ctx - > direct_vblank = false ;
mutex_unlock ( & ctx - > lock ) ;
return ;
}
mutex_unlock ( & ctx - > lock ) ;
2014-02-19 16:02:55 +04:00
exynos_drm_crtc_finish_pageflip ( ctx - > drm_dev , ctx - > pipe ) ;
2012-03-21 05:55:26 +04:00
}
static int vidi_show_connection ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2014-11-17 11:54:16 +03:00
struct vidi_context * ctx = dev_get_drvdata ( dev ) ;
2012-03-21 05:55:26 +04:00
int rc ;
mutex_lock ( & ctx - > lock ) ;
rc = sprintf ( buf , " %d \n " , ctx - > connected ) ;
mutex_unlock ( & ctx - > lock ) ;
return rc ;
}
static int vidi_store_connection ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
2014-11-17 11:54:16 +03:00
struct vidi_context * ctx = dev_get_drvdata ( dev ) ;
2012-03-21 05:55:26 +04:00
int ret ;
ret = kstrtoint ( buf , 0 , & ctx - > connected ) ;
if ( ret )
return ret ;
if ( ctx - > connected > 1 )
return - EINVAL ;
2012-06-27 11:00:56 +04:00
/* use fake edid data for test. */
if ( ! ctx - > raw_edid )
ctx - > raw_edid = ( struct edid * ) fake_edid_info ;
2012-06-27 11:16:26 +04:00
/* if raw_edid isn't same as fake data then it can't be tested. */
if ( ctx - > raw_edid ! = ( struct edid * ) fake_edid_info ) {
DRM_DEBUG_KMS ( " edid data is not fake data. \n " ) ;
return - EINVAL ;
}
2012-03-21 05:55:26 +04:00
DRM_DEBUG_KMS ( " requested connection. \n " ) ;
2014-02-19 16:02:55 +04:00
drm_helper_hpd_irq_event ( ctx - > drm_dev ) ;
2012-03-21 05:55:26 +04:00
return len ;
}
static DEVICE_ATTR ( connection , 0644 , vidi_show_connection ,
vidi_store_connection ) ;
int vidi_connection_ioctl ( struct drm_device * drm_dev , void * data ,
struct drm_file * file_priv )
{
struct vidi_context * ctx = NULL ;
struct drm_encoder * encoder ;
2014-02-19 16:02:55 +04:00
struct exynos_drm_display * display ;
2012-03-21 05:55:26 +04:00
struct drm_exynos_vidi_connection * vidi = data ;
if ( ! vidi ) {
DRM_DEBUG_KMS ( " user data for vidi is null. \n " ) ;
return - EINVAL ;
}
if ( vidi - > connection > 1 ) {
DRM_DEBUG_KMS ( " connection should be 0 or 1. \n " ) ;
return - EINVAL ;
}
list_for_each_entry ( encoder , & drm_dev - > mode_config . encoder_list ,
head ) {
2014-02-19 16:02:55 +04:00
display = exynos_drm_get_display ( encoder ) ;
2012-03-21 05:55:26 +04:00
2014-02-19 16:02:55 +04:00
if ( display - > type = = EXYNOS_DISPLAY_TYPE_VIDI ) {
2014-11-17 11:54:23 +03:00
ctx = display_to_vidi ( display ) ;
2012-03-21 05:55:26 +04:00
break ;
}
}
if ( ! ctx ) {
DRM_DEBUG_KMS ( " not found virtual device type encoder. \n " ) ;
return - EINVAL ;
}
if ( ctx - > connected = = vidi - > connection ) {
DRM_DEBUG_KMS ( " same connection request. \n " ) ;
return - EINVAL ;
}
2012-06-27 11:36:12 +04:00
if ( vidi - > connection ) {
2013-01-10 14:35:06 +04:00
struct edid * raw_edid = ( struct edid * ) ( uint32_t ) vidi - > edid ;
if ( ! drm_edid_is_valid ( raw_edid ) ) {
DRM_DEBUG_KMS ( " edid data is invalid. \n " ) ;
2012-06-27 11:36:12 +04:00
return - EINVAL ;
}
2013-09-27 16:08:29 +04:00
ctx - > raw_edid = drm_edid_duplicate ( raw_edid ) ;
2012-06-27 11:36:12 +04:00
if ( ! ctx - > raw_edid ) {
DRM_DEBUG_KMS ( " failed to allocate raw_edid. \n " ) ;
return - ENOMEM ;
}
} else {
/*
* with connection = 0 , free raw_edid
* only if raw edid data isn ' t same as fake data .
*/
if ( ctx - > raw_edid & & ctx - > raw_edid ! =
( struct edid * ) fake_edid_info ) {
kfree ( ctx - > raw_edid ) ;
ctx - > raw_edid = NULL ;
}
}
2012-03-21 05:55:26 +04:00
ctx - > connected = vidi - > connection ;
2014-02-19 16:02:55 +04:00
drm_helper_hpd_irq_event ( ctx - > drm_dev ) ;
2012-03-21 05:55:26 +04:00
return 0 ;
}
2014-01-31 01:38:07 +04:00
static enum drm_connector_status vidi_detect ( struct drm_connector * connector ,
bool force )
{
struct vidi_context * ctx = ctx_from_connector ( connector ) ;
/*
* connection request would come from user side
* to do hotplug through specific ioctl .
*/
return ctx - > connected ? connector_status_connected :
connector_status_disconnected ;
}
static void vidi_connector_destroy ( struct drm_connector * connector )
{
}
static struct drm_connector_funcs vidi_connector_funcs = {
. dpms = drm_helper_connector_dpms ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. detect = vidi_detect ,
. destroy = vidi_connector_destroy ,
} ;
static int vidi_get_modes ( struct drm_connector * connector )
{
struct vidi_context * ctx = ctx_from_connector ( connector ) ;
struct edid * edid ;
int edid_len ;
/*
* the edid data comes from user side and it would be set
* to ctx - > raw_edid through specific ioctl .
*/
if ( ! ctx - > raw_edid ) {
DRM_DEBUG_KMS ( " raw_edid is null. \n " ) ;
return - EFAULT ;
}
edid_len = ( 1 + ctx - > raw_edid - > extensions ) * EDID_LENGTH ;
edid = kmemdup ( ctx - > raw_edid , edid_len , GFP_KERNEL ) ;
if ( ! edid ) {
DRM_DEBUG_KMS ( " failed to allocate edid \n " ) ;
return - ENOMEM ;
}
drm_mode_connector_update_edid_property ( connector , edid ) ;
return drm_add_edid_modes ( connector , edid ) ;
}
static struct drm_encoder * vidi_best_encoder ( struct drm_connector * connector )
{
struct vidi_context * ctx = ctx_from_connector ( connector ) ;
return ctx - > encoder ;
}
static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
. get_modes = vidi_get_modes ,
. best_encoder = vidi_best_encoder ,
} ;
static int vidi_create_connector ( struct exynos_drm_display * display ,
struct drm_encoder * encoder )
{
2014-11-17 11:54:23 +03:00
struct vidi_context * ctx = display_to_vidi ( display ) ;
2014-01-31 01:38:07 +04:00
struct drm_connector * connector = & ctx - > connector ;
int ret ;
ctx - > encoder = encoder ;
connector - > polled = DRM_CONNECTOR_POLL_HPD ;
ret = drm_connector_init ( ctx - > drm_dev , connector ,
& vidi_connector_funcs , DRM_MODE_CONNECTOR_VIRTUAL ) ;
if ( ret ) {
DRM_ERROR ( " Failed to initialize connector with drm \n " ) ;
return ret ;
}
drm_connector_helper_add ( connector , & vidi_connector_helper_funcs ) ;
2014-05-29 19:57:41 +04:00
drm_connector_register ( connector ) ;
2014-01-31 01:38:07 +04:00
drm_mode_connector_attach_encoder ( connector , encoder ) ;
return 0 ;
}
static struct exynos_drm_display_ops vidi_display_ops = {
. create_connector = vidi_create_connector ,
} ;
2014-11-24 08:55:41 +03:00
static int vidi_bind ( struct device * dev , struct device * master , void * data )
2014-05-09 09:25:20 +04:00
{
2014-11-17 11:54:16 +03:00
struct vidi_context * ctx = dev_get_drvdata ( dev ) ;
2014-11-24 08:55:41 +03:00
struct drm_device * drm_dev = data ;
2014-05-09 09:25:20 +04:00
int ret ;
2015-01-18 12:16:23 +03:00
ctx - > crtc = exynos_drm_crtc_create ( drm_dev , ctx - > pipe ,
EXYNOS_DISPLAY_TYPE_VIDI ,
& vidi_crtc_ops , ctx ) ;
if ( IS_ERR ( ctx - > crtc ) ) {
2014-05-09 09:25:20 +04:00
DRM_ERROR ( " failed to create crtc. \n " ) ;
2015-01-18 12:16:23 +03:00
return PTR_ERR ( ctx - > crtc ) ;
2014-05-09 09:25:20 +04:00
}
2015-01-18 12:16:23 +03:00
vidi_ctx_initialize ( ctx , drm_dev ) ;
2014-11-17 11:54:22 +03:00
ret = exynos_drm_create_enc_conn ( drm_dev , & ctx - > display ) ;
2014-05-09 09:25:20 +04:00
if ( ret ) {
2015-01-18 12:16:23 +03:00
ctx - > crtc - > base . funcs - > destroy ( & ctx - > crtc - > base ) ;
2014-05-09 09:25:20 +04:00
return ret ;
}
return 0 ;
}
2014-11-24 08:55:41 +03:00
static void vidi_unbind ( struct device * dev , struct device * master , void * data )
{
}
static const struct component_ops vidi_component_ops = {
. bind = vidi_bind ,
. unbind = vidi_unbind ,
} ;
2012-12-22 03:09:25 +04:00
static int vidi_probe ( struct platform_device * pdev )
2012-03-21 05:55:26 +04:00
{
struct vidi_context * ctx ;
int ret ;
2014-05-09 09:25:20 +04:00
ctx = devm_kzalloc ( & pdev - > dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
2012-03-21 05:55:26 +04:00
if ( ! ctx )
return - ENOMEM ;
2014-11-17 11:54:22 +03:00
ctx - > display . type = EXYNOS_DISPLAY_TYPE_VIDI ;
ctx - > display . ops = & vidi_display_ops ;
2012-03-21 05:55:26 +04:00
ctx - > default_win = 0 ;
2014-11-24 08:55:41 +03:00
ctx - > pdev = pdev ;
ret = exynos_drm_component_add ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CRTC ,
2014-11-06 00:51:35 +03:00
EXYNOS_DISPLAY_TYPE_VIDI ) ;
2014-11-24 08:55:41 +03:00
if ( ret )
return ret ;
ret = exynos_drm_component_add ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CONNECTOR ,
ctx - > display . type ) ;
if ( ret )
goto err_del_crtc_component ;
2012-03-21 05:55:26 +04:00
INIT_WORK ( & ctx - > work , vidi_fake_vblank_handler ) ;
mutex_init ( & ctx - > lock ) ;
2014-11-17 11:54:16 +03:00
platform_set_drvdata ( pdev , ctx ) ;
2012-03-21 05:55:26 +04:00
2014-05-09 09:25:20 +04:00
ret = device_create_file ( & pdev - > dev , & dev_attr_connection ) ;
if ( ret < 0 ) {
2014-11-24 08:55:41 +03:00
DRM_ERROR ( " failed to create connection sysfs. \n " ) ;
goto err_del_conn_component ;
2014-05-09 09:25:20 +04:00
}
2012-03-21 05:55:26 +04:00
2014-11-24 08:55:41 +03:00
ret = component_add ( & pdev - > dev , & vidi_component_ops ) ;
if ( ret )
goto err_remove_file ;
return ret ;
err_remove_file :
device_remove_file ( & pdev - > dev , & dev_attr_connection ) ;
err_del_conn_component :
exynos_drm_component_del ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CONNECTOR ) ;
err_del_crtc_component :
exynos_drm_component_del ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CRTC ) ;
return ret ;
2012-03-21 05:55:26 +04:00
}
2012-12-22 03:09:25 +04:00
static int vidi_remove ( struct platform_device * pdev )
2012-03-21 05:55:26 +04:00
{
2014-11-17 11:54:16 +03:00
struct vidi_context * ctx = platform_get_drvdata ( pdev ) ;
2012-03-21 05:55:26 +04:00
2012-06-27 11:36:12 +04:00
if ( ctx - > raw_edid ! = ( struct edid * ) fake_edid_info ) {
kfree ( ctx - > raw_edid ) ;
ctx - > raw_edid = NULL ;
2014-05-09 09:25:20 +04:00
return - EINVAL ;
2012-06-27 11:36:12 +04:00
}
2014-11-24 08:55:41 +03:00
component_del ( & pdev - > dev , & vidi_component_ops ) ;
exynos_drm_component_del ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CONNECTOR ) ;
exynos_drm_component_del ( & pdev - > dev , EXYNOS_DEVICE_TYPE_CRTC ) ;
2012-03-21 05:55:26 +04:00
return 0 ;
}
struct platform_driver vidi_driver = {
. probe = vidi_probe ,
2012-12-22 03:09:25 +04:00
. remove = vidi_remove ,
2012-03-21 05:55:26 +04:00
. driver = {
. name = " exynos-drm-vidi " ,
. owner = THIS_MODULE ,
} ,
} ;
2014-05-09 09:25:20 +04:00
int exynos_drm_probe_vidi ( void )
{
struct platform_device * pdev ;
int ret ;
pdev = platform_device_register_simple ( " exynos-drm-vidi " , - 1 , NULL , 0 ) ;
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
ret = platform_driver_register ( & vidi_driver ) ;
if ( ret ) {
platform_device_unregister ( pdev ) ;
return ret ;
}
return ret ;
}
2014-11-17 11:54:16 +03:00
static int exynos_drm_remove_vidi_device ( struct device * dev , void * data )
{
platform_device_unregister ( to_platform_device ( dev ) ) ;
return 0 ;
}
2014-05-09 09:25:20 +04:00
void exynos_drm_remove_vidi ( void )
{
2014-11-17 11:54:16 +03:00
int ret = driver_for_each_device ( & vidi_driver . driver , NULL , NULL ,
exynos_drm_remove_vidi_device ) ;
/* silence compiler warning */
( void ) ret ;
2014-05-09 09:25:20 +04:00
platform_driver_unregister ( & vidi_driver ) ;
}