2011-11-03 18:22:37 +00:00
/*
* Copyright © 2006 - 2011 Intel Corporation
*
* 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 ( including the next
* paragraph ) 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 AUTHORS OR COPYRIGHT HOLDERS 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 .
*
* Authors :
* jim liu < jim . liu @ intel . com >
*
* FIXME :
* We should probably make this generic and share it with Medfield
*/
# include <drm/drmP.h>
# include <drm/drm.h>
# include <drm/drm_crtc.h>
# include <drm/drm_edid.h>
# include "psb_intel_drv.h"
# include "psb_drv.h"
# include "psb_intel_reg.h"
2012-03-08 16:10:38 +00:00
# include "cdv_device.h"
2011-11-03 18:22:37 +00:00
# include <linux/pm_runtime.h>
/* hdmi control bits */
# define HDMI_NULL_PACKETS_DURING_VSYNC (1 << 9)
# define HDMI_BORDER_ENABLE (1 << 7)
# define HDMI_AUDIO_ENABLE (1 << 6)
# define HDMI_VSYNC_ACTIVE_HIGH (1 << 4)
# define HDMI_HSYNC_ACTIVE_HIGH (1 << 3)
/* hdmi-b control bits */
# define HDMIB_PIPE_B_SELECT (1 << 30)
struct mid_intel_hdmi_priv {
u32 hdmi_reg ;
u32 save_HDMIB ;
bool has_hdmi_sink ;
bool has_hdmi_audio ;
/* Should set this when detect hotplug */
bool hdmi_device_connected ;
struct mdfld_hdmi_i2c * i2c_bus ;
struct i2c_adapter * hdmi_i2c_adapter ; /* for control functions */
struct drm_device * dev ;
} ;
static void cdv_hdmi_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct drm_device * dev = encoder - > dev ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = to_gma_encoder ( encoder ) ;
struct mid_intel_hdmi_priv * hdmi_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:37 +00:00
u32 hdmib ;
struct drm_crtc * crtc = encoder - > crtc ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
2011-11-03 18:22:37 +00:00
hdmib = ( 2 < < 10 ) ;
if ( adjusted_mode - > flags & DRM_MODE_FLAG_PVSYNC )
hdmib | = HDMI_VSYNC_ACTIVE_HIGH ;
if ( adjusted_mode - > flags & DRM_MODE_FLAG_PHSYNC )
hdmib | = HDMI_HSYNC_ACTIVE_HIGH ;
2013-07-22 01:31:23 +02:00
if ( gma_crtc - > pipe = = 1 )
2011-11-03 18:22:37 +00:00
hdmib | = HDMIB_PIPE_B_SELECT ;
if ( hdmi_priv - > has_hdmi_audio ) {
hdmib | = HDMI_AUDIO_ENABLE ;
hdmib | = HDMI_NULL_PACKETS_DURING_VSYNC ;
}
REG_WRITE ( hdmi_priv - > hdmi_reg , hdmib ) ;
REG_READ ( hdmi_priv - > hdmi_reg ) ;
}
static void cdv_hdmi_dpms ( struct drm_encoder * encoder , int mode )
{
struct drm_device * dev = encoder - > dev ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = to_gma_encoder ( encoder ) ;
struct mid_intel_hdmi_priv * hdmi_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:37 +00:00
u32 hdmib ;
hdmib = REG_READ ( hdmi_priv - > hdmi_reg ) ;
if ( mode ! = DRM_MODE_DPMS_ON )
REG_WRITE ( hdmi_priv - > hdmi_reg , hdmib & ~ HDMIB_PORT_EN ) ;
else
REG_WRITE ( hdmi_priv - > hdmi_reg , hdmib | HDMIB_PORT_EN ) ;
REG_READ ( hdmi_priv - > hdmi_reg ) ;
}
static void cdv_hdmi_save ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
struct mid_intel_hdmi_priv * hdmi_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:37 +00:00
hdmi_priv - > save_HDMIB = REG_READ ( hdmi_priv - > hdmi_reg ) ;
}
static void cdv_hdmi_restore ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
struct mid_intel_hdmi_priv * hdmi_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:37 +00:00
REG_WRITE ( hdmi_priv - > hdmi_reg , hdmi_priv - > save_HDMIB ) ;
REG_READ ( hdmi_priv - > hdmi_reg ) ;
}
static enum drm_connector_status cdv_hdmi_detect (
struct drm_connector * connector , bool force )
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
struct mid_intel_hdmi_priv * hdmi_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:37 +00:00
struct edid * edid = NULL ;
enum drm_connector_status status = connector_status_disconnected ;
2013-07-22 17:45:26 +02:00
edid = drm_get_edid ( connector , & gma_encoder - > i2c_bus - > adapter ) ;
2011-11-03 18:22:37 +00:00
hdmi_priv - > has_hdmi_sink = false ;
hdmi_priv - > has_hdmi_audio = false ;
if ( edid ) {
if ( edid - > input & DRM_EDID_INPUT_DIGITAL ) {
status = connector_status_connected ;
hdmi_priv - > has_hdmi_sink =
drm_detect_hdmi_monitor ( edid ) ;
hdmi_priv - > has_hdmi_audio =
drm_detect_monitor_audio ( edid ) ;
}
kfree ( edid ) ;
}
return status ;
}
static int cdv_hdmi_set_property ( struct drm_connector * connector ,
struct drm_property * property ,
uint64_t value )
{
struct drm_encoder * encoder = connector - > encoder ;
if ( ! strcmp ( property - > name , " scaling mode " ) & & encoder ) {
2013-07-22 01:31:23 +02:00
struct gma_crtc * crtc = to_gma_crtc ( encoder - > crtc ) ;
2011-11-03 18:22:37 +00:00
bool centre ;
uint64_t curValue ;
if ( ! crtc )
return - 1 ;
switch ( value ) {
case DRM_MODE_SCALE_FULLSCREEN :
break ;
case DRM_MODE_SCALE_NO_SCALE :
break ;
case DRM_MODE_SCALE_ASPECT :
break ;
default :
return - 1 ;
}
2012-10-11 20:38:23 -05:00
if ( drm_object_property_get_value ( & connector - > base ,
2011-11-03 18:22:37 +00:00
property , & curValue ) )
return - 1 ;
if ( curValue = = value )
return 0 ;
2012-10-11 20:38:23 -05:00
if ( drm_object_property_set_value ( & connector - > base ,
2011-11-03 18:22:37 +00:00
property , value ) )
return - 1 ;
centre = ( curValue = = DRM_MODE_SCALE_NO_SCALE ) | |
( value = = DRM_MODE_SCALE_NO_SCALE ) ;
if ( crtc - > saved_mode . hdisplay ! = 0 & &
crtc - > saved_mode . vdisplay ! = 0 ) {
if ( centre ) {
if ( ! drm_crtc_helper_set_mode ( encoder - > crtc , & crtc - > saved_mode ,
2014-04-01 15:22:40 -07:00
encoder - > crtc - > x , encoder - > crtc - > y , encoder - > crtc - > primary - > fb ) )
2011-11-03 18:22:37 +00:00
return - 1 ;
} else {
2015-03-11 11:51:01 +02:00
const struct drm_encoder_helper_funcs * helpers
2011-11-03 18:22:37 +00:00
= encoder - > helper_private ;
helpers - > mode_set ( encoder , & crtc - > saved_mode ,
& crtc - > saved_adjusted_mode ) ;
}
}
}
return 0 ;
}
/*
* Return the list of HDMI DDC modes if available .
*/
static int cdv_hdmi_get_modes ( struct drm_connector * connector )
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
2011-11-03 18:22:37 +00:00
struct edid * edid = NULL ;
int ret = 0 ;
2013-07-22 17:45:26 +02:00
edid = drm_get_edid ( connector , & gma_encoder - > i2c_bus - > adapter ) ;
2011-11-03 18:22:37 +00:00
if ( edid ) {
2011-12-19 21:41:22 +00:00
drm_mode_connector_update_edid_property ( connector , edid ) ;
ret = drm_add_edid_modes ( connector , edid ) ;
2011-11-03 18:22:37 +00:00
kfree ( edid ) ;
}
return ret ;
}
2018-04-24 15:14:54 +02:00
static enum drm_mode_status cdv_hdmi_mode_valid ( struct drm_connector * connector ,
2011-11-03 18:22:37 +00:00
struct drm_display_mode * mode )
{
if ( mode - > clock > 165000 )
return MODE_CLOCK_HIGH ;
if ( mode - > clock < 20000 )
return MODE_CLOCK_HIGH ;
/* just in case */
if ( mode - > flags & DRM_MODE_FLAG_DBLSCAN )
return MODE_NO_DBLESCAN ;
/* just in case */
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
return MODE_NO_INTERLACE ;
return MODE_OK ;
}
static void cdv_hdmi_destroy ( struct drm_connector * connector )
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
2011-11-03 18:22:37 +00:00
2016-07-22 10:30:30 +02:00
psb_intel_i2c_destroy ( gma_encoder - > i2c_bus ) ;
2014-05-29 16:57:41 +01:00
drm_connector_unregister ( connector ) ;
2011-11-03 18:22:37 +00:00
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = {
. dpms = cdv_hdmi_dpms ,
2013-07-11 01:02:01 +02:00
. prepare = gma_encoder_prepare ,
2011-11-03 18:22:37 +00:00
. mode_set = cdv_hdmi_mode_set ,
2013-07-11 01:02:01 +02:00
. commit = gma_encoder_commit ,
2011-11-03 18:22:37 +00:00
} ;
static const struct drm_connector_helper_funcs
cdv_hdmi_connector_helper_funcs = {
. get_modes = cdv_hdmi_get_modes ,
. mode_valid = cdv_hdmi_mode_valid ,
2013-07-11 01:02:01 +02:00
. best_encoder = gma_best_encoder ,
2011-11-03 18:22:37 +00:00
} ;
static const struct drm_connector_funcs cdv_hdmi_connector_funcs = {
. dpms = drm_helper_connector_dpms ,
. detect = cdv_hdmi_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = cdv_hdmi_set_property ,
. destroy = cdv_hdmi_destroy ,
} ;
void cdv_hdmi_init ( struct drm_device * dev ,
struct psb_intel_mode_device * mode_dev , int reg )
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder ;
2013-07-22 17:05:25 +02:00
struct gma_connector * gma_connector ;
2011-11-03 18:22:37 +00:00
struct drm_connector * connector ;
struct drm_encoder * encoder ;
struct mid_intel_hdmi_priv * hdmi_priv ;
int ddc_bus ;
2013-07-22 17:45:26 +02:00
gma_encoder = kzalloc ( sizeof ( struct gma_encoder ) , GFP_KERNEL ) ;
2011-12-19 21:41:22 +00:00
2013-07-22 17:45:26 +02:00
if ( ! gma_encoder )
2011-11-03 18:22:37 +00:00
return ;
2013-07-22 17:05:25 +02:00
gma_connector = kzalloc ( sizeof ( struct gma_connector ) ,
2011-12-19 21:41:22 +00:00
GFP_KERNEL ) ;
2013-07-22 17:05:25 +02:00
if ( ! gma_connector )
2011-12-19 21:41:22 +00:00
goto err_connector ;
hdmi_priv = kzalloc ( sizeof ( struct mid_intel_hdmi_priv ) , GFP_KERNEL ) ;
if ( ! hdmi_priv )
goto err_priv ;
2013-07-22 17:05:25 +02:00
connector = & gma_connector - > base ;
2013-03-31 13:38:44 +02:00
connector - > polled = DRM_CONNECTOR_POLL_HPD ;
2015-12-04 09:45:53 +01:00
gma_connector - > save = cdv_hdmi_save ;
gma_connector - > restore = cdv_hdmi_restore ;
2013-07-22 17:45:26 +02:00
encoder = & gma_encoder - > base ;
2011-12-19 21:41:22 +00:00
drm_connector_init ( dev , connector ,
2011-11-03 18:22:37 +00:00
& cdv_hdmi_connector_funcs ,
DRM_MODE_CONNECTOR_DVID ) ;
2011-12-19 21:41:22 +00:00
drm_encoder_init ( dev , encoder , & psb_intel_lvds_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_TMDS , NULL ) ;
2011-11-03 18:22:37 +00:00
2013-07-22 17:45:26 +02:00
gma_connector_attach_encoder ( gma_connector , gma_encoder ) ;
gma_encoder - > type = INTEL_OUTPUT_HDMI ;
2011-11-03 18:22:37 +00:00
hdmi_priv - > hdmi_reg = reg ;
hdmi_priv - > has_hdmi_sink = false ;
2013-07-22 17:45:26 +02:00
gma_encoder - > dev_priv = hdmi_priv ;
2011-11-03 18:22:37 +00:00
drm_encoder_helper_add ( encoder , & cdv_hdmi_helper_funcs ) ;
drm_connector_helper_add ( connector ,
& cdv_hdmi_connector_helper_funcs ) ;
connector - > display_info . subpixel_order = SubPixelHorizontalRGB ;
connector - > interlace_allowed = false ;
connector - > doublescan_allowed = false ;
2012-10-11 20:38:23 -05:00
drm_object_attach_property ( & connector - > base ,
2011-12-19 21:41:22 +00:00
dev - > mode_config . scaling_mode_property ,
DRM_MODE_SCALE_FULLSCREEN ) ;
2011-11-03 18:22:37 +00:00
switch ( reg ) {
case SDVOB :
ddc_bus = GPIOE ;
2013-07-22 17:45:26 +02:00
gma_encoder - > ddi_select = DDI0_SELECT ;
2011-11-03 18:22:37 +00:00
break ;
case SDVOC :
ddc_bus = GPIOD ;
2013-07-22 17:45:26 +02:00
gma_encoder - > ddi_select = DDI1_SELECT ;
2011-11-03 18:22:37 +00:00
break ;
default :
DRM_ERROR ( " unknown reg 0x%x for HDMI \n " , reg ) ;
goto failed_ddc ;
break ;
}
2013-07-22 17:45:26 +02:00
gma_encoder - > i2c_bus = psb_intel_i2c_create ( dev ,
2011-11-03 18:22:37 +00:00
ddc_bus , ( reg = = SDVOB ) ? " HDMIB " : " HDMIC " ) ;
2013-07-22 17:45:26 +02:00
if ( ! gma_encoder - > i2c_bus ) {
2011-11-03 18:22:37 +00:00
dev_err ( dev - > dev , " No ddc adapter available! \n " ) ;
goto failed_ddc ;
}
2011-12-19 21:41:22 +00:00
2013-07-22 17:45:26 +02:00
hdmi_priv - > hdmi_i2c_adapter = & ( gma_encoder - > i2c_bus - > adapter ) ;
2011-11-03 18:22:37 +00:00
hdmi_priv - > dev = dev ;
2014-05-29 16:57:41 +01:00
drm_connector_register ( connector ) ;
2011-11-03 18:22:37 +00:00
return ;
failed_ddc :
2011-12-19 21:41:22 +00:00
drm_encoder_cleanup ( encoder ) ;
drm_connector_cleanup ( connector ) ;
err_priv :
2013-07-22 17:05:25 +02:00
kfree ( gma_connector ) ;
2011-12-19 21:41:22 +00:00
err_connector :
2013-07-22 17:45:26 +02:00
kfree ( gma_encoder ) ;
2011-11-03 18:22:37 +00:00
}