2017-12-06 12:29:47 +01:00
// SPDX-License-Identifier: GPL-2.0
2014-07-28 10:30:18 +02:00
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Authors : Benjamin Gaignard < benjamin . gaignard @ st . com >
* Fabien Dessenne < fabien . dessenne @ st . com >
* for STMicroelectronics .
*/
2019-06-05 15:48:35 +02:00
# include <linux/moduleparam.h>
2016-05-09 23:51:28 +02:00
# include <linux/seq_file.h>
2014-07-28 10:30:18 +02:00
2019-06-05 15:48:35 +02:00
# include <drm/drm_print.h>
2014-07-30 19:28:27 +02:00
# include "sti_compositor.h"
2014-07-28 10:30:18 +02:00
# include "sti_mixer.h"
# include "sti_vtg.h"
2015-10-22 10:35:50 +02:00
/* Module parameter to set the background color of the mixer */
static unsigned int bkg_color = 0x000000 ;
MODULE_PARM_DESC ( bkgcolor , " Value of the background color 0xRRGGBB " ) ;
module_param_named ( bkgcolor , bkg_color , int , 0644 ) ;
2014-07-28 10:30:18 +02:00
/* regs offset */
# define GAM_MIXER_CTL 0x00
# define GAM_MIXER_BKC 0x04
# define GAM_MIXER_BCO 0x0C
# define GAM_MIXER_BCS 0x10
# define GAM_MIXER_AVO 0x28
# define GAM_MIXER_AVS 0x2C
# define GAM_MIXER_CRB 0x34
# define GAM_MIXER_ACT 0x38
# define GAM_MIXER_MBP 0x3C
# define GAM_MIXER_MX0 0x80
/* id for depth of CRB reg */
# define GAM_DEPTH_VID0_ID 1
# define GAM_DEPTH_VID1_ID 2
# define GAM_DEPTH_GDP0_ID 3
# define GAM_DEPTH_GDP1_ID 4
# define GAM_DEPTH_GDP2_ID 5
# define GAM_DEPTH_GDP3_ID 6
# define GAM_DEPTH_MASK_ID 7
/* mask in CTL reg */
# define GAM_CTL_BACK_MASK BIT(0)
# define GAM_CTL_VID0_MASK BIT(1)
# define GAM_CTL_VID1_MASK BIT(2)
# define GAM_CTL_GDP0_MASK BIT(3)
# define GAM_CTL_GDP1_MASK BIT(4)
# define GAM_CTL_GDP2_MASK BIT(5)
# define GAM_CTL_GDP3_MASK BIT(6)
2014-12-11 13:34:42 +01:00
# define GAM_CTL_CURSOR_MASK BIT(9)
2014-07-28 10:30:18 +02:00
const char * sti_mixer_to_str ( struct sti_mixer * mixer )
{
switch ( mixer - > id ) {
case STI_MIXER_MAIN :
return " MAIN_MIXER " ;
case STI_MIXER_AUX :
return " AUX_MIXER " ;
default :
return " <UNKNOWN MIXER> " ;
}
}
static inline u32 sti_mixer_reg_read ( struct sti_mixer * mixer , u32 reg_id )
{
return readl ( mixer - > regs + reg_id ) ;
}
static inline void sti_mixer_reg_write ( struct sti_mixer * mixer ,
u32 reg_id , u32 val )
{
writel ( val , mixer - > regs + reg_id ) ;
}
2016-02-04 17:44:50 +01:00
# define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
sti_mixer_reg_read ( mixer , reg ) )
static void mixer_dbg_ctl ( struct seq_file * s , int val )
{
unsigned int i ;
int count = 0 ;
char * const disp_layer [ ] = { " BKG " , " VID0 " , " VID1 " , " GDP0 " ,
" GDP1 " , " GDP2 " , " GDP3 " } ;
seq_puts ( s , " \t Enabled: " ) ;
for ( i = 0 ; i < 7 ; i + + ) {
if ( val & 1 ) {
seq_printf ( s , " %s " , disp_layer [ i ] ) ;
count + + ;
}
val = val > > 1 ;
}
val = val > > 2 ;
if ( val & 1 ) {
seq_puts ( s , " CURS " ) ;
count + + ;
}
if ( ! count )
seq_puts ( s , " Nothing " ) ;
}
static void mixer_dbg_crb ( struct seq_file * s , int val )
{
int i ;
seq_puts ( s , " \t Depth: " ) ;
for ( i = 0 ; i < GAM_MIXER_NB_DEPTH_LEVEL ; i + + ) {
switch ( val & GAM_DEPTH_MASK_ID ) {
case GAM_DEPTH_VID0_ID :
seq_puts ( s , " VID0 " ) ;
break ;
case GAM_DEPTH_VID1_ID :
seq_puts ( s , " VID1 " ) ;
break ;
case GAM_DEPTH_GDP0_ID :
seq_puts ( s , " GDP0 " ) ;
break ;
case GAM_DEPTH_GDP1_ID :
seq_puts ( s , " GDP1 " ) ;
break ;
case GAM_DEPTH_GDP2_ID :
seq_puts ( s , " GDP2 " ) ;
break ;
case GAM_DEPTH_GDP3_ID :
seq_puts ( s , " GDP3 " ) ;
break ;
default :
seq_puts ( s , " --- " ) ;
}
if ( i < GAM_MIXER_NB_DEPTH_LEVEL - 1 )
seq_puts ( s , " < " ) ;
val = val > > 3 ;
}
}
static void mixer_dbg_mxn ( struct seq_file * s , void * addr )
{
int i ;
for ( i = 1 ; i < 8 ; i + + )
seq_printf ( s , " -0x%08X " , ( int ) readl ( addr + i * 4 ) ) ;
}
static int mixer_dbg_show ( struct seq_file * s , void * arg )
{
struct drm_info_node * node = s - > private ;
struct sti_mixer * mixer = ( struct sti_mixer * ) node - > info_ent - > data ;
seq_printf ( s , " %s: (vaddr = 0x%p) " ,
sti_mixer_to_str ( mixer ) , mixer - > regs ) ;
DBGFS_DUMP ( GAM_MIXER_CTL ) ;
mixer_dbg_ctl ( s , sti_mixer_reg_read ( mixer , GAM_MIXER_CTL ) ) ;
DBGFS_DUMP ( GAM_MIXER_BKC ) ;
DBGFS_DUMP ( GAM_MIXER_BCO ) ;
DBGFS_DUMP ( GAM_MIXER_BCS ) ;
DBGFS_DUMP ( GAM_MIXER_AVO ) ;
DBGFS_DUMP ( GAM_MIXER_AVS ) ;
DBGFS_DUMP ( GAM_MIXER_CRB ) ;
mixer_dbg_crb ( s , sti_mixer_reg_read ( mixer , GAM_MIXER_CRB ) ) ;
DBGFS_DUMP ( GAM_MIXER_ACT ) ;
DBGFS_DUMP ( GAM_MIXER_MBP ) ;
DBGFS_DUMP ( GAM_MIXER_MX0 ) ;
mixer_dbg_mxn ( s , mixer - > regs + GAM_MIXER_MX0 ) ;
2017-05-05 15:00:46 +02:00
seq_putc ( s , ' \n ' ) ;
2016-02-04 17:44:50 +01:00
return 0 ;
}
static struct drm_info_list mixer0_debugfs_files [ ] = {
{ " mixer_main " , mixer_dbg_show , 0 , NULL } ,
} ;
static struct drm_info_list mixer1_debugfs_files [ ] = {
{ " mixer_aux " , mixer_dbg_show , 0 , NULL } ,
} ;
2020-03-10 16:31:13 +03:00
void sti_mixer_debugfs_init ( struct sti_mixer * mixer , struct drm_minor * minor )
2016-02-04 17:44:50 +01:00
{
unsigned int i ;
struct drm_info_list * mixer_debugfs_files ;
int nb_files ;
switch ( mixer - > id ) {
case STI_MIXER_MAIN :
mixer_debugfs_files = mixer0_debugfs_files ;
nb_files = ARRAY_SIZE ( mixer0_debugfs_files ) ;
break ;
case STI_MIXER_AUX :
mixer_debugfs_files = mixer1_debugfs_files ;
nb_files = ARRAY_SIZE ( mixer1_debugfs_files ) ;
break ;
default :
2020-03-10 16:31:13 +03:00
return ;
2016-02-04 17:44:50 +01:00
}
for ( i = 0 ; i < nb_files ; i + + )
mixer_debugfs_files [ i ] . data = mixer ;
2020-03-10 16:31:13 +03:00
drm_debugfs_create_files ( mixer_debugfs_files ,
nb_files ,
minor - > debugfs_root , minor ) ;
2016-02-04 17:44:50 +01:00
}
2014-07-28 10:30:18 +02:00
void sti_mixer_set_background_status ( struct sti_mixer * mixer , bool enable )
{
u32 val = sti_mixer_reg_read ( mixer , GAM_MIXER_CTL ) ;
val & = ~ GAM_CTL_BACK_MASK ;
val | = enable ;
sti_mixer_reg_write ( mixer , GAM_MIXER_CTL , val ) ;
}
static void sti_mixer_set_background_color ( struct sti_mixer * mixer ,
2015-10-22 10:35:50 +02:00
unsigned int rgb )
2014-07-28 10:30:18 +02:00
{
2015-10-22 10:35:50 +02:00
sti_mixer_reg_write ( mixer , GAM_MIXER_BKC , rgb ) ;
2014-07-28 10:30:18 +02:00
}
static void sti_mixer_set_background_area ( struct sti_mixer * mixer ,
struct drm_display_mode * mode )
{
u32 ydo , xdo , yds , xds ;
ydo = sti_vtg_get_line_number ( * mode , 0 ) ;
yds = sti_vtg_get_line_number ( * mode , mode - > vdisplay - 1 ) ;
xdo = sti_vtg_get_pixel_number ( * mode , 0 ) ;
xds = sti_vtg_get_pixel_number ( * mode , mode - > hdisplay - 1 ) ;
sti_mixer_reg_write ( mixer , GAM_MIXER_BCO , ydo < < 16 | xdo ) ;
sti_mixer_reg_write ( mixer , GAM_MIXER_BCS , yds < < 16 | xds ) ;
}
2015-07-31 11:32:13 +02:00
int sti_mixer_set_plane_depth ( struct sti_mixer * mixer , struct sti_plane * plane )
2014-07-28 10:30:18 +02:00
{
2016-03-24 17:18:20 +01:00
int plane_id , depth = plane - > drm_plane . state - > normalized_zpos ;
2015-07-31 11:31:38 +02:00
unsigned int i ;
2014-07-28 10:30:18 +02:00
u32 mask , val ;
2015-07-31 11:32:13 +02:00
switch ( plane - > desc ) {
2014-07-28 10:30:18 +02:00
case STI_GDP_0 :
2015-07-31 11:32:13 +02:00
plane_id = GAM_DEPTH_GDP0_ID ;
2014-07-28 10:30:18 +02:00
break ;
case STI_GDP_1 :
2015-07-31 11:32:13 +02:00
plane_id = GAM_DEPTH_GDP1_ID ;
2014-07-28 10:30:18 +02:00
break ;
case STI_GDP_2 :
2015-07-31 11:32:13 +02:00
plane_id = GAM_DEPTH_GDP2_ID ;
2014-07-28 10:30:18 +02:00
break ;
case STI_GDP_3 :
2015-07-31 11:32:13 +02:00
plane_id = GAM_DEPTH_GDP3_ID ;
2014-07-28 10:30:18 +02:00
break ;
2014-12-11 11:38:59 +01:00
case STI_HQVDP_0 :
2015-07-31 11:32:13 +02:00
plane_id = GAM_DEPTH_VID0_ID ;
2014-07-28 10:30:18 +02:00
break ;
2014-12-11 13:34:42 +01:00
case STI_CURSOR :
/* no need to set depth for cursor */
return 0 ;
2014-07-28 10:30:18 +02:00
default :
2015-07-31 11:32:13 +02:00
DRM_ERROR ( " Unknown plane %d \n " , plane - > desc ) ;
2014-07-28 10:30:18 +02:00
return 1 ;
}
2015-07-31 11:31:38 +02:00
2015-07-31 11:32:13 +02:00
/* Search if a previous depth was already assigned to the plane */
2015-07-31 11:31:38 +02:00
val = sti_mixer_reg_read ( mixer , GAM_MIXER_CRB ) ;
for ( i = 0 ; i < GAM_MIXER_NB_DEPTH_LEVEL ; i + + ) {
mask = GAM_DEPTH_MASK_ID < < ( 3 * i ) ;
2015-07-31 11:32:13 +02:00
if ( ( val & mask ) = = plane_id < < ( 3 * i ) )
2015-07-31 11:31:38 +02:00
break ;
}
2016-03-24 17:18:20 +01:00
mask | = GAM_DEPTH_MASK_ID < < ( 3 * depth ) ;
plane_id = plane_id < < ( 3 * depth ) ;
2014-07-28 10:30:18 +02:00
2014-07-30 19:28:27 +02:00
DRM_DEBUG_DRIVER ( " %s %s depth=%d \n " , sti_mixer_to_str ( mixer ) ,
2015-07-31 11:32:13 +02:00
sti_plane_to_str ( plane ) , depth ) ;
2014-07-28 10:30:18 +02:00
dev_dbg ( mixer - > dev , " GAM_MIXER_CRB val 0x%x mask 0x%x \n " ,
2015-07-31 11:32:13 +02:00
plane_id , mask ) ;
2014-07-28 10:30:18 +02:00
val & = ~ mask ;
2015-07-31 11:32:13 +02:00
val | = plane_id ;
2014-07-28 10:30:18 +02:00
sti_mixer_reg_write ( mixer , GAM_MIXER_CRB , val ) ;
dev_dbg ( mixer - > dev , " Read GAM_MIXER_CRB 0x%x \n " ,
sti_mixer_reg_read ( mixer , GAM_MIXER_CRB ) ) ;
return 0 ;
}
int sti_mixer_active_video_area ( struct sti_mixer * mixer ,
struct drm_display_mode * mode )
{
u32 ydo , xdo , yds , xds ;
ydo = sti_vtg_get_line_number ( * mode , 0 ) ;
yds = sti_vtg_get_line_number ( * mode , mode - > vdisplay - 1 ) ;
xdo = sti_vtg_get_pixel_number ( * mode , 0 ) ;
xds = sti_vtg_get_pixel_number ( * mode , mode - > hdisplay - 1 ) ;
DRM_DEBUG_DRIVER ( " %s active video area xdo:%d ydo:%d xds:%d yds:%d \n " ,
sti_mixer_to_str ( mixer ) , xdo , ydo , xds , yds ) ;
sti_mixer_reg_write ( mixer , GAM_MIXER_AVO , ydo < < 16 | xdo ) ;
sti_mixer_reg_write ( mixer , GAM_MIXER_AVS , yds < < 16 | xds ) ;
2015-10-22 10:35:50 +02:00
sti_mixer_set_background_color ( mixer , bkg_color ) ;
2014-07-28 10:30:18 +02:00
sti_mixer_set_background_area ( mixer , mode ) ;
sti_mixer_set_background_status ( mixer , true ) ;
return 0 ;
}
2015-07-31 11:32:13 +02:00
static u32 sti_mixer_get_plane_mask ( struct sti_plane * plane )
2014-07-28 10:30:18 +02:00
{
2015-07-31 11:32:13 +02:00
switch ( plane - > desc ) {
2014-07-28 10:30:18 +02:00
case STI_BACK :
return GAM_CTL_BACK_MASK ;
case STI_GDP_0 :
return GAM_CTL_GDP0_MASK ;
case STI_GDP_1 :
return GAM_CTL_GDP1_MASK ;
case STI_GDP_2 :
return GAM_CTL_GDP2_MASK ;
case STI_GDP_3 :
return GAM_CTL_GDP3_MASK ;
2014-12-11 11:38:59 +01:00
case STI_HQVDP_0 :
2014-07-28 10:30:18 +02:00
return GAM_CTL_VID0_MASK ;
2014-12-11 13:34:42 +01:00
case STI_CURSOR :
return GAM_CTL_CURSOR_MASK ;
2014-07-28 10:30:18 +02:00
default :
return 0 ;
}
}
2015-07-31 11:32:13 +02:00
int sti_mixer_set_plane_status ( struct sti_mixer * mixer ,
struct sti_plane * plane , bool status )
2014-07-28 10:30:18 +02:00
{
u32 mask , val ;
2014-07-30 19:28:27 +02:00
DRM_DEBUG_DRIVER ( " %s %s %s \n " , status ? " enable " : " disable " ,
2015-07-31 11:32:13 +02:00
sti_mixer_to_str ( mixer ) , sti_plane_to_str ( plane ) ) ;
2014-07-30 19:28:27 +02:00
2015-07-31 11:32:13 +02:00
mask = sti_mixer_get_plane_mask ( plane ) ;
2014-07-28 10:30:18 +02:00
if ( ! mask ) {
2015-07-31 11:32:13 +02:00
DRM_ERROR ( " Can't find layer mask \n " ) ;
2014-07-28 10:30:18 +02:00
return - EINVAL ;
}
val = sti_mixer_reg_read ( mixer , GAM_MIXER_CTL ) ;
val & = ~ mask ;
val | = status ? mask : 0 ;
sti_mixer_reg_write ( mixer , GAM_MIXER_CTL , val ) ;
return 0 ;
}
2016-02-04 17:44:50 +01:00
struct sti_mixer * sti_mixer_create ( struct device * dev ,
struct drm_device * drm_dev ,
int id ,
2014-07-28 10:30:18 +02:00
void __iomem * baseaddr )
{
struct sti_mixer * mixer = devm_kzalloc ( dev , sizeof ( * mixer ) , GFP_KERNEL ) ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
if ( ! mixer ) {
DRM_ERROR ( " Failed to allocated memory for mixer \n " ) ;
return NULL ;
}
mixer - > regs = baseaddr ;
mixer - > dev = dev ;
mixer - > id = id ;
DRM_DEBUG_DRIVER ( " %s created. Regs=%p \n " ,
sti_mixer_to_str ( mixer ) , mixer - > regs ) ;
return mixer ;
}