2015-03-02 13:01:12 -08:00
/*
* Copyright ( C ) 2015 Broadcom
* Copyright ( c ) 2014 The Linux Foundation . All rights reserved .
* Copyright ( C ) 2013 Red Hat
* Author : Rob Clark < robdclark @ gmail . 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/>.
*/
/**
* DOC : VC4 Falcon HDMI module
*
2017-02-27 12:11:43 -08:00
* The HDMI core has a state machine and a PHY . On BCM2835 , most of
* the unit operates off of the HSM clock from CPRMAN . It also
* internally uses the PLLH_PIX clock for the PHY .
*
* HDMI infoframes are kept within a small packet ram , where each
* packet can be individually enabled for including in a frame .
*
* HDMI audio is implemented entirely within the HDMI IP block . A
* register in the HDMI encoder takes SPDIF frames from the DMA engine
* and transfers them over an internal MAI ( multi - channel audio
* interconnect ) bus to the encoder side for insertion into the video
* blank regions .
*
* The driver ' s HDMI encoder does not yet support power management .
* The HDMI encoder ' s power domain and the HSM / pixel clocks are kept
* continuously running , and only the HDMI logic and packet ram are
* powered off / on at disable / enable time .
*
* The driver does not yet support CEC control , though the HDMI
* encoder block has CEC support .
2015-03-02 13:01:12 -08:00
*/
# include "drm_atomic_helper.h"
# include "drm_crtc_helper.h"
# include "drm_edid.h"
# include "linux/clk.h"
# include "linux/component.h"
# include "linux/i2c.h"
# include "linux/of_gpio.h"
# include "linux/of_platform.h"
# include "vc4_drv.h"
# include "vc4_regs.h"
/* General HDMI hardware state. */
struct vc4_hdmi {
struct platform_device * pdev ;
struct drm_encoder * encoder ;
struct drm_connector * connector ;
struct i2c_adapter * ddc ;
void __iomem * hdmicore_regs ;
void __iomem * hd_regs ;
int hpd_gpio ;
2016-02-29 17:53:01 -08:00
bool hpd_active_low ;
2015-03-02 13:01:12 -08:00
struct clk * pixel_clock ;
struct clk * hsm_clock ;
} ;
# define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
# define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
# define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
# define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
/* VC4 HDMI encoder KMS struct */
struct vc4_hdmi_encoder {
struct vc4_encoder base ;
bool hdmi_monitor ;
2016-09-29 15:34:43 -07:00
bool limited_rgb_range ;
bool rgb_range_selectable ;
2015-03-02 13:01:12 -08:00
} ;
static inline struct vc4_hdmi_encoder *
to_vc4_hdmi_encoder ( struct drm_encoder * encoder )
{
return container_of ( encoder , struct vc4_hdmi_encoder , base . base ) ;
}
/* VC4 HDMI connector KMS struct */
struct vc4_hdmi_connector {
struct drm_connector base ;
/* Since the connector is attached to just the one encoder,
* this is the reference to it so we can do the best_encoder ( )
* hook .
*/
struct drm_encoder * encoder ;
} ;
static inline struct vc4_hdmi_connector *
to_vc4_hdmi_connector ( struct drm_connector * connector )
{
return container_of ( connector , struct vc4_hdmi_connector , base ) ;
}
# define HDMI_REG(reg) { reg, #reg }
static const struct {
u32 reg ;
const char * name ;
} hdmi_regs [ ] = {
HDMI_REG ( VC4_HDMI_CORE_REV ) ,
HDMI_REG ( VC4_HDMI_SW_RESET_CONTROL ) ,
HDMI_REG ( VC4_HDMI_HOTPLUG_INT ) ,
HDMI_REG ( VC4_HDMI_HOTPLUG ) ,
2016-02-12 15:16:56 -08:00
HDMI_REG ( VC4_HDMI_RAM_PACKET_CONFIG ) ,
2015-03-02 13:01:12 -08:00
HDMI_REG ( VC4_HDMI_HORZA ) ,
HDMI_REG ( VC4_HDMI_HORZB ) ,
HDMI_REG ( VC4_HDMI_FIFO_CTL ) ,
HDMI_REG ( VC4_HDMI_SCHEDULER_CONTROL ) ,
HDMI_REG ( VC4_HDMI_VERTA0 ) ,
HDMI_REG ( VC4_HDMI_VERTA1 ) ,
HDMI_REG ( VC4_HDMI_VERTB0 ) ,
HDMI_REG ( VC4_HDMI_VERTB1 ) ,
HDMI_REG ( VC4_HDMI_TX_PHY_RESET_CTL ) ,
} ;
static const struct {
u32 reg ;
const char * name ;
} hd_regs [ ] = {
HDMI_REG ( VC4_HD_M_CTL ) ,
HDMI_REG ( VC4_HD_MAI_CTL ) ,
HDMI_REG ( VC4_HD_VID_CTL ) ,
HDMI_REG ( VC4_HD_CSC_CTL ) ,
HDMI_REG ( VC4_HD_FRAME_COUNT ) ,
} ;
# ifdef CONFIG_DEBUG_FS
int vc4_hdmi_debugfs_regs ( struct seq_file * m , void * unused )
{
struct drm_info_node * node = ( struct drm_info_node * ) m - > private ;
struct drm_device * dev = node - > minor - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hdmi_regs ) ; i + + ) {
seq_printf ( m , " %s (0x%04x): 0x%08x \n " ,
hdmi_regs [ i ] . name , hdmi_regs [ i ] . reg ,
HDMI_READ ( hdmi_regs [ i ] . reg ) ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( hd_regs ) ; i + + ) {
seq_printf ( m , " %s (0x%04x): 0x%08x \n " ,
hd_regs [ i ] . name , hd_regs [ i ] . reg ,
HD_READ ( hd_regs [ i ] . reg ) ) ;
}
return 0 ;
}
# endif /* CONFIG_DEBUG_FS */
static void vc4_hdmi_dump_regs ( struct drm_device * dev )
{
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hdmi_regs ) ; i + + ) {
DRM_INFO ( " 0x%04x (%s): 0x%08x \n " ,
hdmi_regs [ i ] . reg , hdmi_regs [ i ] . name ,
HDMI_READ ( hdmi_regs [ i ] . reg ) ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( hd_regs ) ; i + + ) {
DRM_INFO ( " 0x%04x (%s): 0x%08x \n " ,
hd_regs [ i ] . reg , hd_regs [ i ] . name ,
HD_READ ( hd_regs [ i ] . reg ) ) ;
}
}
static enum drm_connector_status
vc4_hdmi_connector_detect ( struct drm_connector * connector , bool force )
{
struct drm_device * dev = connector - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
if ( vc4 - > hdmi - > hpd_gpio ) {
2016-02-29 17:53:01 -08:00
if ( gpio_get_value_cansleep ( vc4 - > hdmi - > hpd_gpio ) ^
vc4 - > hdmi - > hpd_active_low )
2015-03-02 13:01:12 -08:00
return connector_status_connected ;
else
return connector_status_disconnected ;
}
2016-09-14 19:21:29 +01:00
if ( drm_probe_ddc ( vc4 - > hdmi - > ddc ) )
return connector_status_connected ;
2015-03-02 13:01:12 -08:00
if ( HDMI_READ ( VC4_HDMI_HOTPLUG ) & VC4_HDMI_HOTPLUG_CONNECTED )
return connector_status_connected ;
else
return connector_status_disconnected ;
}
static void vc4_hdmi_connector_destroy ( struct drm_connector * connector )
{
drm_connector_unregister ( connector ) ;
drm_connector_cleanup ( connector ) ;
}
static int vc4_hdmi_connector_get_modes ( struct drm_connector * connector )
{
struct vc4_hdmi_connector * vc4_connector =
to_vc4_hdmi_connector ( connector ) ;
struct drm_encoder * encoder = vc4_connector - > encoder ;
struct vc4_hdmi_encoder * vc4_encoder = to_vc4_hdmi_encoder ( encoder ) ;
struct drm_device * dev = connector - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
int ret = 0 ;
struct edid * edid ;
edid = drm_get_edid ( connector , vc4 - > hdmi - > ddc ) ;
if ( ! edid )
return - ENODEV ;
vc4_encoder - > hdmi_monitor = drm_detect_hdmi_monitor ( edid ) ;
2016-09-29 15:34:43 -07:00
if ( edid & & edid - > input & DRM_EDID_INPUT_DIGITAL ) {
vc4_encoder - > rgb_range_selectable =
drm_rgb_quant_range_selectable ( edid ) ;
}
2015-03-02 13:01:12 -08:00
drm_mode_connector_update_edid_property ( connector , edid ) ;
ret = drm_add_edid_modes ( connector , edid ) ;
return ret ;
}
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
. dpms = drm_atomic_helper_connector_dpms ,
. detect = vc4_hdmi_connector_detect ,
2016-09-28 17:30:25 -07:00
. fill_modes = drm_helper_probe_single_connector_modes ,
2015-03-02 13:01:12 -08:00
. destroy = vc4_hdmi_connector_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_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
. get_modes = vc4_hdmi_connector_get_modes ,
} ;
static struct drm_connector * vc4_hdmi_connector_init ( struct drm_device * dev ,
struct drm_encoder * encoder )
{
struct drm_connector * connector = NULL ;
struct vc4_hdmi_connector * hdmi_connector ;
int ret = 0 ;
hdmi_connector = devm_kzalloc ( dev - > dev , sizeof ( * hdmi_connector ) ,
GFP_KERNEL ) ;
if ( ! hdmi_connector ) {
ret = - ENOMEM ;
goto fail ;
}
connector = & hdmi_connector - > base ;
hdmi_connector - > encoder = encoder ;
drm_connector_init ( dev , connector , & vc4_hdmi_connector_funcs ,
DRM_MODE_CONNECTOR_HDMIA ) ;
drm_connector_helper_add ( connector , & vc4_hdmi_connector_helper_funcs ) ;
connector - > polled = ( DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT ) ;
2016-07-19 20:58:58 +02:00
connector - > interlace_allowed = 1 ;
2015-03-02 13:01:12 -08:00
connector - > doublescan_allowed = 0 ;
drm_mode_connector_attach_encoder ( connector , encoder ) ;
return connector ;
fail :
if ( connector )
vc4_hdmi_connector_destroy ( connector ) ;
return ERR_PTR ( ret ) ;
}
static void vc4_hdmi_encoder_destroy ( struct drm_encoder * encoder )
{
drm_encoder_cleanup ( encoder ) ;
}
static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
. destroy = vc4_hdmi_encoder_destroy ,
} ;
2016-09-29 15:34:43 -07:00
static int vc4_hdmi_stop_packet ( struct drm_encoder * encoder ,
enum hdmi_infoframe_type type )
{
struct drm_device * dev = encoder - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
u32 packet_id = type - 0x80 ;
HDMI_WRITE ( VC4_HDMI_RAM_PACKET_CONFIG ,
HDMI_READ ( VC4_HDMI_RAM_PACKET_CONFIG ) & ~ BIT ( packet_id ) ) ;
return wait_for ( ! ( HDMI_READ ( VC4_HDMI_RAM_PACKET_STATUS ) &
BIT ( packet_id ) ) , 100 ) ;
}
static void vc4_hdmi_write_infoframe ( struct drm_encoder * encoder ,
union hdmi_infoframe * frame )
{
struct drm_device * dev = encoder - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
u32 packet_id = frame - > any . type - 0x80 ;
u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id ;
uint8_t buffer [ VC4_HDMI_PACKET_STRIDE ] ;
ssize_t len , i ;
int ret ;
WARN_ONCE ( ! ( HDMI_READ ( VC4_HDMI_RAM_PACKET_CONFIG ) &
VC4_HDMI_RAM_PACKET_ENABLE ) ,
" Packet RAM has to be on to store the packet. " ) ;
len = hdmi_infoframe_pack ( frame , buffer , sizeof ( buffer ) ) ;
if ( len < 0 )
return ;
ret = vc4_hdmi_stop_packet ( encoder , frame - > any . type ) ;
if ( ret ) {
DRM_ERROR ( " Failed to wait for infoframe to go idle: %d \n " , ret ) ;
return ;
}
for ( i = 0 ; i < len ; i + = 7 ) {
HDMI_WRITE ( packet_reg ,
buffer [ i + 0 ] < < 0 |
buffer [ i + 1 ] < < 8 |
buffer [ i + 2 ] < < 16 ) ;
packet_reg + = 4 ;
HDMI_WRITE ( packet_reg ,
buffer [ i + 3 ] < < 0 |
buffer [ i + 4 ] < < 8 |
buffer [ i + 5 ] < < 16 |
buffer [ i + 6 ] < < 24 ) ;
packet_reg + = 4 ;
}
HDMI_WRITE ( VC4_HDMI_RAM_PACKET_CONFIG ,
HDMI_READ ( VC4_HDMI_RAM_PACKET_CONFIG ) | BIT ( packet_id ) ) ;
ret = wait_for ( ( HDMI_READ ( VC4_HDMI_RAM_PACKET_STATUS ) &
BIT ( packet_id ) ) , 100 ) ;
if ( ret )
DRM_ERROR ( " Failed to wait for infoframe to start: %d \n " , ret ) ;
}
static void vc4_hdmi_set_avi_infoframe ( struct drm_encoder * encoder )
{
struct vc4_hdmi_encoder * vc4_encoder = to_vc4_hdmi_encoder ( encoder ) ;
struct drm_crtc * crtc = encoder - > crtc ;
const struct drm_display_mode * mode = & crtc - > state - > adjusted_mode ;
union hdmi_infoframe frame ;
int ret ;
ret = drm_hdmi_avi_infoframe_from_display_mode ( & frame . avi , mode ) ;
if ( ret < 0 ) {
DRM_ERROR ( " couldn't fill AVI infoframe \n " ) ;
return ;
}
2017-01-11 14:57:24 +02:00
drm_hdmi_avi_infoframe_quant_range ( & frame . avi , mode ,
2017-01-11 14:57:23 +02:00
vc4_encoder - > limited_rgb_range ?
HDMI_QUANTIZATION_RANGE_LIMITED :
HDMI_QUANTIZATION_RANGE_FULL ,
vc4_encoder - > rgb_range_selectable ) ;
2016-09-29 15:34:43 -07:00
vc4_hdmi_write_infoframe ( encoder , & frame ) ;
}
static void vc4_hdmi_set_spd_infoframe ( struct drm_encoder * encoder )
{
union hdmi_infoframe frame ;
int ret ;
ret = hdmi_spd_infoframe_init ( & frame . spd , " Broadcom " , " Videocore " ) ;
if ( ret < 0 ) {
DRM_ERROR ( " couldn't fill SPD infoframe \n " ) ;
return ;
}
frame . spd . sdi = HDMI_SPD_SDI_PC ;
vc4_hdmi_write_infoframe ( encoder , & frame ) ;
}
static void vc4_hdmi_set_infoframes ( struct drm_encoder * encoder )
{
vc4_hdmi_set_avi_infoframe ( encoder ) ;
vc4_hdmi_set_spd_infoframe ( encoder ) ;
}
2015-03-02 13:01:12 -08:00
static void vc4_hdmi_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * unadjusted_mode ,
struct drm_display_mode * mode )
{
2016-09-16 10:59:45 +01:00
struct vc4_hdmi_encoder * vc4_encoder = to_vc4_hdmi_encoder ( encoder ) ;
2015-03-02 13:01:12 -08:00
struct drm_device * dev = encoder - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
bool debug_dump_regs = false ;
bool hsync_pos = mode - > flags & DRM_MODE_FLAG_PHSYNC ;
bool vsync_pos = mode - > flags & DRM_MODE_FLAG_PVSYNC ;
2016-09-28 17:30:25 -07:00
bool interlaced = mode - > flags & DRM_MODE_FLAG_INTERLACE ;
2016-09-29 15:34:44 -07:00
u32 pixel_rep = ( mode - > flags & DRM_MODE_FLAG_DBLCLK ) ? 2 : 1 ;
2016-09-28 17:30:25 -07:00
u32 verta = ( VC4_SET_FIELD ( mode - > crtc_vsync_end - mode - > crtc_vsync_start ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_VERTA_VSP ) |
2016-09-28 17:30:25 -07:00
VC4_SET_FIELD ( mode - > crtc_vsync_start - mode - > crtc_vdisplay ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_VERTA_VFP ) |
2016-09-28 17:30:25 -07:00
VC4_SET_FIELD ( mode - > crtc_vdisplay , VC4_HDMI_VERTA_VAL ) ) ;
2015-03-02 13:01:12 -08:00
u32 vertb = ( VC4_SET_FIELD ( 0 , VC4_HDMI_VERTB_VSPO ) |
2016-09-28 17:30:25 -07:00
VC4_SET_FIELD ( mode - > crtc_vtotal - mode - > crtc_vsync_end ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_VERTB_VBP ) ) ;
2016-09-28 17:30:25 -07:00
u32 vertb_even = ( VC4_SET_FIELD ( 0 , VC4_HDMI_VERTB_VSPO ) |
VC4_SET_FIELD ( mode - > crtc_vtotal -
mode - > crtc_vsync_end -
interlaced ,
VC4_HDMI_VERTB_VBP ) ) ;
2016-09-16 10:59:45 +01:00
u32 csc_ctl ;
2015-03-02 13:01:12 -08:00
if ( debug_dump_regs ) {
DRM_INFO ( " HDMI regs before: \n " ) ;
vc4_hdmi_dump_regs ( dev ) ;
}
HD_WRITE ( VC4_HD_VID_CTL , 0 ) ;
2016-09-29 15:34:44 -07:00
clk_set_rate ( vc4 - > hdmi - > pixel_clock , mode - > clock * 1000 *
( ( mode - > flags & DRM_MODE_FLAG_DBLCLK ) ? 2 : 1 ) ) ;
2015-03-02 13:01:12 -08:00
HDMI_WRITE ( VC4_HDMI_SCHEDULER_CONTROL ,
HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) |
VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS ) ;
HDMI_WRITE ( VC4_HDMI_HORZA ,
( vsync_pos ? VC4_HDMI_HORZA_VPOS : 0 ) |
( hsync_pos ? VC4_HDMI_HORZA_HPOS : 0 ) |
2016-09-29 15:34:44 -07:00
VC4_SET_FIELD ( mode - > hdisplay * pixel_rep ,
VC4_HDMI_HORZA_HAP ) ) ;
2015-03-02 13:01:12 -08:00
HDMI_WRITE ( VC4_HDMI_HORZB ,
2016-09-29 15:34:44 -07:00
VC4_SET_FIELD ( ( mode - > htotal -
mode - > hsync_end ) * pixel_rep ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_HORZB_HBP ) |
2016-09-29 15:34:44 -07:00
VC4_SET_FIELD ( ( mode - > hsync_end -
mode - > hsync_start ) * pixel_rep ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_HORZB_HSP ) |
2016-09-29 15:34:44 -07:00
VC4_SET_FIELD ( ( mode - > hsync_start -
mode - > hdisplay ) * pixel_rep ,
2015-03-02 13:01:12 -08:00
VC4_HDMI_HORZB_HFP ) ) ;
HDMI_WRITE ( VC4_HDMI_VERTA0 , verta ) ;
HDMI_WRITE ( VC4_HDMI_VERTA1 , verta ) ;
2016-09-28 17:30:25 -07:00
HDMI_WRITE ( VC4_HDMI_VERTB0 , vertb_even ) ;
2015-03-02 13:01:12 -08:00
HDMI_WRITE ( VC4_HDMI_VERTB1 , vertb ) ;
HD_WRITE ( VC4_HD_VID_CTL ,
( vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW ) |
( hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW ) ) ;
2016-09-16 10:59:45 +01:00
csc_ctl = VC4_SET_FIELD ( VC4_HD_CSC_CTL_ORDER_BGR ,
VC4_HD_CSC_CTL_ORDER ) ;
2017-01-11 16:18:35 +02:00
if ( vc4_encoder - > hdmi_monitor & &
drm_default_rgb_quant_range ( mode ) = =
HDMI_QUANTIZATION_RANGE_LIMITED ) {
2016-09-16 10:59:45 +01:00
/* CEA VICs other than #1 requre limited range RGB
2016-09-29 15:34:43 -07:00
* output unless overridden by an AVI infoframe .
* Apply a colorspace conversion to squash 0 - 255 down
* to 16 - 235. The matrix here is :
2016-09-16 10:59:45 +01:00
*
* [ 0 0 0.8594 16 ]
* [ 0 0.8594 0 16 ]
* [ 0.8594 0 0 16 ]
* [ 0 0 0 1 ]
*/
csc_ctl | = VC4_HD_CSC_CTL_ENABLE ;
csc_ctl | = VC4_HD_CSC_CTL_RGB2YCC ;
csc_ctl | = VC4_SET_FIELD ( VC4_HD_CSC_CTL_MODE_CUSTOM ,
VC4_HD_CSC_CTL_MODE ) ;
HD_WRITE ( VC4_HD_CSC_12_11 , ( 0x000 < < 16 ) | 0x000 ) ;
HD_WRITE ( VC4_HD_CSC_14_13 , ( 0x100 < < 16 ) | 0x6e0 ) ;
HD_WRITE ( VC4_HD_CSC_22_21 , ( 0x6e0 < < 16 ) | 0x000 ) ;
HD_WRITE ( VC4_HD_CSC_24_23 , ( 0x100 < < 16 ) | 0x000 ) ;
HD_WRITE ( VC4_HD_CSC_32_31 , ( 0x000 < < 16 ) | 0x6e0 ) ;
HD_WRITE ( VC4_HD_CSC_34_33 , ( 0x100 < < 16 ) | 0x000 ) ;
2016-09-29 15:34:43 -07:00
vc4_encoder - > limited_rgb_range = true ;
} else {
vc4_encoder - > limited_rgb_range = false ;
2016-09-16 10:59:45 +01:00
}
2015-03-02 13:01:12 -08:00
/* The RGB order applies even when CSC is disabled. */
2016-09-16 10:59:45 +01:00
HD_WRITE ( VC4_HD_CSC_CTL , csc_ctl ) ;
2015-03-02 13:01:12 -08:00
HDMI_WRITE ( VC4_HDMI_FIFO_CTL , VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N ) ;
if ( debug_dump_regs ) {
DRM_INFO ( " HDMI regs after: \n " ) ;
vc4_hdmi_dump_regs ( dev ) ;
}
}
static void vc4_hdmi_encoder_disable ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2016-09-29 15:34:43 -07:00
HDMI_WRITE ( VC4_HDMI_RAM_PACKET_CONFIG , 0 ) ;
2015-03-02 13:01:12 -08:00
HDMI_WRITE ( VC4_HDMI_TX_PHY_RESET_CTL , 0xf < < 16 ) ;
HD_WRITE ( VC4_HD_VID_CTL ,
HD_READ ( VC4_HD_VID_CTL ) & ~ VC4_HD_VID_CTL_ENABLE ) ;
}
static void vc4_hdmi_encoder_enable ( struct drm_encoder * encoder )
{
struct vc4_hdmi_encoder * vc4_encoder = to_vc4_hdmi_encoder ( encoder ) ;
struct drm_device * dev = encoder - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
int ret ;
HDMI_WRITE ( VC4_HDMI_TX_PHY_RESET_CTL , 0 ) ;
HD_WRITE ( VC4_HD_VID_CTL ,
HD_READ ( VC4_HD_VID_CTL ) |
VC4_HD_VID_CTL_ENABLE |
VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
VC4_HD_VID_CTL_FRAME_COUNTER_RESET ) ;
if ( vc4_encoder - > hdmi_monitor ) {
HDMI_WRITE ( VC4_HDMI_SCHEDULER_CONTROL ,
HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) |
VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI ) ;
ret = wait_for ( HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) &
2016-09-28 17:21:05 -07:00
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE , 1000 ) ;
2015-03-02 13:01:12 -08:00
WARN_ONCE ( ret , " Timeout waiting for "
" VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE \n " ) ;
} else {
HDMI_WRITE ( VC4_HDMI_RAM_PACKET_CONFIG ,
HDMI_READ ( VC4_HDMI_RAM_PACKET_CONFIG ) &
~ ( VC4_HDMI_RAM_PACKET_ENABLE ) ) ;
HDMI_WRITE ( VC4_HDMI_SCHEDULER_CONTROL ,
HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) &
~ VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI ) ;
ret = wait_for ( ! ( HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) &
2016-09-28 17:21:05 -07:00
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE ) , 1000 ) ;
2015-03-02 13:01:12 -08:00
WARN_ONCE ( ret , " Timeout waiting for "
" !VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE \n " ) ;
}
if ( vc4_encoder - > hdmi_monitor ) {
u32 drift ;
WARN_ON ( ! ( HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) &
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE ) ) ;
HDMI_WRITE ( VC4_HDMI_SCHEDULER_CONTROL ,
HDMI_READ ( VC4_HDMI_SCHEDULER_CONTROL ) |
VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT ) ;
2016-09-29 15:34:43 -07:00
HDMI_WRITE ( VC4_HDMI_RAM_PACKET_CONFIG ,
VC4_HDMI_RAM_PACKET_ENABLE ) ;
vc4_hdmi_set_infoframes ( encoder ) ;
2015-03-02 13:01:12 -08:00
drift = HDMI_READ ( VC4_HDMI_FIFO_CTL ) ;
drift & = VC4_HDMI_FIFO_VALID_WRITE_MASK ;
HDMI_WRITE ( VC4_HDMI_FIFO_CTL ,
drift & ~ VC4_HDMI_FIFO_CTL_RECENTER ) ;
HDMI_WRITE ( VC4_HDMI_FIFO_CTL ,
drift | VC4_HDMI_FIFO_CTL_RECENTER ) ;
udelay ( 1000 ) ;
HDMI_WRITE ( VC4_HDMI_FIFO_CTL ,
drift & ~ VC4_HDMI_FIFO_CTL_RECENTER ) ;
HDMI_WRITE ( VC4_HDMI_FIFO_CTL ,
drift | VC4_HDMI_FIFO_CTL_RECENTER ) ;
ret = wait_for ( HDMI_READ ( VC4_HDMI_FIFO_CTL ) &
VC4_HDMI_FIFO_CTL_RECENTER_DONE , 1 ) ;
WARN_ONCE ( ret , " Timeout waiting for "
" VC4_HDMI_FIFO_CTL_RECENTER_DONE " ) ;
}
}
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
. mode_set = vc4_hdmi_encoder_mode_set ,
. disable = vc4_hdmi_encoder_disable ,
. enable = vc4_hdmi_encoder_enable ,
} ;
static int vc4_hdmi_bind ( struct device * dev , struct device * master , void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct drm_device * drm = dev_get_drvdata ( master ) ;
struct vc4_dev * vc4 = drm - > dev_private ;
struct vc4_hdmi * hdmi ;
struct vc4_hdmi_encoder * vc4_hdmi_encoder ;
struct device_node * ddc_node ;
u32 value ;
int ret ;
hdmi = devm_kzalloc ( dev , sizeof ( * hdmi ) , GFP_KERNEL ) ;
if ( ! hdmi )
return - ENOMEM ;
vc4_hdmi_encoder = devm_kzalloc ( dev , sizeof ( * vc4_hdmi_encoder ) ,
GFP_KERNEL ) ;
if ( ! vc4_hdmi_encoder )
return - ENOMEM ;
vc4_hdmi_encoder - > base . type = VC4_ENCODER_TYPE_HDMI ;
hdmi - > encoder = & vc4_hdmi_encoder - > base . base ;
hdmi - > pdev = pdev ;
hdmi - > hdmicore_regs = vc4_ioremap_regs ( pdev , 0 ) ;
if ( IS_ERR ( hdmi - > hdmicore_regs ) )
return PTR_ERR ( hdmi - > hdmicore_regs ) ;
hdmi - > hd_regs = vc4_ioremap_regs ( pdev , 1 ) ;
if ( IS_ERR ( hdmi - > hd_regs ) )
return PTR_ERR ( hdmi - > hd_regs ) ;
hdmi - > pixel_clock = devm_clk_get ( dev , " pixel " ) ;
if ( IS_ERR ( hdmi - > pixel_clock ) ) {
DRM_ERROR ( " Failed to get pixel clock \n " ) ;
return PTR_ERR ( hdmi - > pixel_clock ) ;
}
hdmi - > hsm_clock = devm_clk_get ( dev , " hdmi " ) ;
if ( IS_ERR ( hdmi - > hsm_clock ) ) {
DRM_ERROR ( " Failed to get HDMI state machine clock \n " ) ;
return PTR_ERR ( hdmi - > hsm_clock ) ;
}
2016-07-05 10:04:54 +08:00
ddc_node = of_parse_phandle ( dev - > of_node , " ddc " , 0 ) ;
if ( ! ddc_node ) {
DRM_ERROR ( " Failed to find ddc node in device tree \n " ) ;
return - ENODEV ;
}
2015-03-02 13:01:12 -08:00
hdmi - > ddc = of_find_i2c_adapter_by_node ( ddc_node ) ;
2016-07-05 10:04:54 +08:00
of_node_put ( ddc_node ) ;
2015-03-02 13:01:12 -08:00
if ( ! hdmi - > ddc ) {
DRM_DEBUG ( " Failed to get ddc i2c adapter by node \n " ) ;
return - EPROBE_DEFER ;
}
/* Enable the clocks at startup. We can't quite recover from
* turning off the pixel clock during disable / enables yet , so
* it ' s always running .
*/
ret = clk_prepare_enable ( hdmi - > pixel_clock ) ;
if ( ret ) {
DRM_ERROR ( " Failed to turn on pixel clock: %d \n " , ret ) ;
goto err_put_i2c ;
}
2016-02-12 14:15:14 -08:00
/* This is the rate that is set by the firmware. The number
* needs to be a bit higher than the pixel clock rate
* ( generally 148.5 Mhz ) .
*/
ret = clk_set_rate ( hdmi - > hsm_clock , 163682864 ) ;
if ( ret ) {
DRM_ERROR ( " Failed to set HSM clock rate: %d \n " , ret ) ;
goto err_unprepare_pix ;
}
2015-03-02 13:01:12 -08:00
ret = clk_prepare_enable ( hdmi - > hsm_clock ) ;
if ( ret ) {
DRM_ERROR ( " Failed to turn on HDMI state machine clock: %d \n " ,
ret ) ;
goto err_unprepare_pix ;
}
/* Only use the GPIO HPD pin if present in the DT, otherwise
* we ' ll use the HDMI core ' s register .
*/
if ( of_find_property ( dev - > of_node , " hpd-gpios " , & value ) ) {
2016-02-29 17:53:01 -08:00
enum of_gpio_flags hpd_gpio_flags ;
hdmi - > hpd_gpio = of_get_named_gpio_flags ( dev - > of_node ,
" hpd-gpios " , 0 ,
& hpd_gpio_flags ) ;
2015-03-02 13:01:12 -08:00
if ( hdmi - > hpd_gpio < 0 ) {
ret = hdmi - > hpd_gpio ;
goto err_unprepare_hsm ;
}
2016-02-29 17:53:01 -08:00
hdmi - > hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW ;
2015-03-02 13:01:12 -08:00
}
vc4 - > hdmi = hdmi ;
/* HDMI core must be enabled. */
2016-02-12 14:15:14 -08:00
if ( ! ( HD_READ ( VC4_HD_M_CTL ) & VC4_HD_M_ENABLE ) ) {
HD_WRITE ( VC4_HD_M_CTL , VC4_HD_M_SW_RST ) ;
udelay ( 1 ) ;
HD_WRITE ( VC4_HD_M_CTL , 0 ) ;
HD_WRITE ( VC4_HD_M_CTL , VC4_HD_M_ENABLE ) ;
HDMI_WRITE ( VC4_HDMI_SW_RESET_CONTROL ,
VC4_HDMI_SW_RESET_HDMI |
VC4_HDMI_SW_RESET_FORMAT_DETECT ) ;
HDMI_WRITE ( VC4_HDMI_SW_RESET_CONTROL , 0 ) ;
/* PHY should be in reset, like
* vc4_hdmi_encoder_disable ( ) does .
*/
HDMI_WRITE ( VC4_HDMI_TX_PHY_RESET_CTL , 0xf < < 16 ) ;
}
2015-03-02 13:01:12 -08:00
drm_encoder_init ( drm , hdmi - > encoder , & vc4_hdmi_encoder_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 ) ;
2015-03-02 13:01:12 -08:00
drm_encoder_helper_add ( hdmi - > encoder , & vc4_hdmi_encoder_helper_funcs ) ;
hdmi - > connector = vc4_hdmi_connector_init ( drm , hdmi - > encoder ) ;
if ( IS_ERR ( hdmi - > connector ) ) {
ret = PTR_ERR ( hdmi - > connector ) ;
goto err_destroy_encoder ;
}
return 0 ;
err_destroy_encoder :
vc4_hdmi_encoder_destroy ( hdmi - > encoder ) ;
err_unprepare_hsm :
clk_disable_unprepare ( hdmi - > hsm_clock ) ;
err_unprepare_pix :
clk_disable_unprepare ( hdmi - > pixel_clock ) ;
err_put_i2c :
2016-04-04 14:25:59 -07:00
put_device ( & hdmi - > ddc - > dev ) ;
2015-03-02 13:01:12 -08:00
return ret ;
}
static void vc4_hdmi_unbind ( struct device * dev , struct device * master ,
void * data )
{
struct drm_device * drm = dev_get_drvdata ( master ) ;
struct vc4_dev * vc4 = drm - > dev_private ;
struct vc4_hdmi * hdmi = vc4 - > hdmi ;
vc4_hdmi_connector_destroy ( hdmi - > connector ) ;
vc4_hdmi_encoder_destroy ( hdmi - > encoder ) ;
clk_disable_unprepare ( hdmi - > pixel_clock ) ;
clk_disable_unprepare ( hdmi - > hsm_clock ) ;
put_device ( & hdmi - > ddc - > dev ) ;
vc4 - > hdmi = NULL ;
}
static const struct component_ops vc4_hdmi_ops = {
. bind = vc4_hdmi_bind ,
. unbind = vc4_hdmi_unbind ,
} ;
static int vc4_hdmi_dev_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & vc4_hdmi_ops ) ;
}
static int vc4_hdmi_dev_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & vc4_hdmi_ops ) ;
return 0 ;
}
static const struct of_device_id vc4_hdmi_dt_match [ ] = {
{ . compatible = " brcm,bcm2835-hdmi " } ,
{ }
} ;
struct platform_driver vc4_hdmi_driver = {
. probe = vc4_hdmi_dev_probe ,
. remove = vc4_hdmi_dev_remove ,
. driver = {
. name = " vc4_hdmi " ,
. of_match_table = vc4_hdmi_dt_match ,
} ,
} ;