2012-04-17 14:12:29 +01:00
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details .
*
* Authors : Matthew Garrett
* Dave Airlie
*
* Portions of this code derived from cirrusfb . c :
* drivers / video / cirrusfb . c - driver for Cirrus Logic chipsets
*
* Copyright 1999 - 2001 Jeff Garzik < jgarzik @ pobox . com >
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2012-04-17 14:12:29 +01:00
# include <video/cirrus.h>
# include "cirrus_drv.h"
# define CIRRUS_LUT_SIZE 256
# define PALETTE_INDEX 0x8
# define PALETTE_DATA 0x9
/*
* This file contains setup code for the CRTC .
*/
/*
* The DRM core requires DPMS functions , but they make little sense in our
* case and so are just stubs
*/
static void cirrus_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
struct drm_device * dev = crtc - > dev ;
struct cirrus_device * cdev = dev - > dev_private ;
u8 sr01 , gr0e ;
switch ( mode ) {
case DRM_MODE_DPMS_ON :
sr01 = 0x00 ;
gr0e = 0x00 ;
break ;
case DRM_MODE_DPMS_STANDBY :
sr01 = 0x20 ;
gr0e = 0x02 ;
break ;
case DRM_MODE_DPMS_SUSPEND :
sr01 = 0x20 ;
gr0e = 0x04 ;
break ;
case DRM_MODE_DPMS_OFF :
sr01 = 0x20 ;
gr0e = 0x06 ;
break ;
default :
return ;
}
WREG8 ( SEQ_INDEX , 0x1 ) ;
sr01 | = RREG8 ( SEQ_DATA ) & ~ 0x20 ;
WREG_SEQ ( 0x1 , sr01 ) ;
WREG8 ( GFX_INDEX , 0xe ) ;
gr0e | = RREG8 ( GFX_DATA ) & ~ 0x06 ;
WREG_GFX ( 0xe , gr0e ) ;
}
2014-01-06 20:30:14 +05:30
static void cirrus_set_start_address ( struct drm_crtc * crtc , unsigned offset )
2012-04-17 14:12:29 +01:00
{
struct cirrus_device * cdev = crtc - > dev - > dev_private ;
u32 addr ;
u8 tmp ;
addr = offset > > 2 ;
WREG_CRT ( 0x0c , ( u8 ) ( ( addr > > 8 ) & 0xff ) ) ;
WREG_CRT ( 0x0d , ( u8 ) ( addr & 0xff ) ) ;
WREG8 ( CRT_INDEX , 0x1b ) ;
tmp = RREG8 ( CRT_DATA ) ;
tmp & = 0xf2 ;
tmp | = ( addr > > 16 ) & 0x01 ;
tmp | = ( addr > > 15 ) & 0x0c ;
WREG_CRT ( 0x1b , tmp ) ;
WREG8 ( CRT_INDEX , 0x1d ) ;
tmp = RREG8 ( CRT_DATA ) ;
tmp & = 0x7f ;
tmp | = ( addr > > 12 ) & 0x80 ;
WREG_CRT ( 0x1d , tmp ) ;
}
/* cirrus is different - we will force move buffers out of VRAM */
static int cirrus_crtc_do_set_base ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int x , int y , int atomic )
{
struct cirrus_device * cdev = crtc - > dev - > dev_private ;
struct cirrus_bo * bo ;
int ret ;
u64 gpu_addr ;
/* push the previous fb to system ram */
if ( ! atomic & & fb ) {
2018-03-30 15:11:16 +01:00
bo = gem_to_cirrus_bo ( fb - > obj [ 0 ] ) ;
2012-04-17 14:12:29 +01:00
ret = cirrus_bo_reserve ( bo , false ) ;
if ( ret )
return ret ;
cirrus_bo_push_sysram ( bo ) ;
cirrus_bo_unreserve ( bo ) ;
}
2018-03-30 15:11:16 +01:00
bo = gem_to_cirrus_bo ( crtc - > primary - > fb - > obj [ 0 ] ) ;
2012-04-17 14:12:29 +01:00
ret = cirrus_bo_reserve ( bo , false ) ;
if ( ret )
return ret ;
ret = cirrus_bo_pin ( bo , TTM_PL_FLAG_VRAM , & gpu_addr ) ;
if ( ret ) {
cirrus_bo_unreserve ( bo ) ;
return ret ;
}
2018-07-20 13:27:43 +02:00
if ( cdev - > mode_info . gfbdev - > gfb = = crtc - > primary - > fb ) {
2012-04-17 14:12:29 +01:00
/* if pushing console in kmap it */
ret = ttm_bo_kmap ( & bo - > bo , 0 , bo - > bo . num_pages , & bo - > kmap ) ;
if ( ret )
DRM_ERROR ( " failed to kmap fbcon \n " ) ;
}
cirrus_bo_unreserve ( bo ) ;
cirrus_set_start_address ( crtc , ( u32 ) gpu_addr ) ;
return 0 ;
}
static int cirrus_crtc_mode_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
return cirrus_crtc_do_set_base ( crtc , old_fb , x , y , 0 ) ;
}
/*
* The meat of this driver . The core passes us a mode and we have to program
* it . The modesetting here is the bare minimum required to satisfy the qemu
* emulation of this hardware , and running this against a real device is
* likely to result in an inadequately programmed mode . We ' ve already had
* the opportunity to modify the mode , so whatever we receive here should
* be something that can be correctly programmed and displayed
*/
static int cirrus_crtc_mode_set ( struct drm_crtc * crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode ,
int x , int y , struct drm_framebuffer * old_fb )
{
struct drm_device * dev = crtc - > dev ;
struct cirrus_device * cdev = dev - > dev_private ;
2016-11-18 21:52:43 +02:00
const struct drm_framebuffer * fb = crtc - > primary - > fb ;
2012-04-17 14:12:29 +01:00
int hsyncstart , hsyncend , htotal , hdispend ;
int vtotal , vdispend ;
int tmp ;
int sr07 = 0 , hdr = 0 ;
htotal = mode - > htotal / 8 ;
hsyncend = mode - > hsync_end / 8 ;
hsyncstart = mode - > hsync_start / 8 ;
hdispend = mode - > hdisplay / 8 ;
vtotal = mode - > vtotal ;
vdispend = mode - > vdisplay ;
vdispend - = 1 ;
vtotal - = 2 ;
htotal - = 5 ;
hdispend - = 1 ;
hsyncstart + = 1 ;
hsyncend + = 1 ;
WREG_CRT ( VGA_CRTC_V_SYNC_END , 0x20 ) ;
WREG_CRT ( VGA_CRTC_H_TOTAL , htotal ) ;
WREG_CRT ( VGA_CRTC_H_DISP , hdispend ) ;
WREG_CRT ( VGA_CRTC_H_SYNC_START , hsyncstart ) ;
WREG_CRT ( VGA_CRTC_H_SYNC_END , hsyncend ) ;
WREG_CRT ( VGA_CRTC_V_TOTAL , vtotal & 0xff ) ;
WREG_CRT ( VGA_CRTC_V_DISP_END , vdispend & 0xff ) ;
tmp = 0x40 ;
if ( ( vdispend + 1 ) & 512 )
tmp | = 0x20 ;
WREG_CRT ( VGA_CRTC_MAX_SCAN , tmp ) ;
/*
* Overflow bits for values that don ' t fit in the standard registers
*/
tmp = 16 ;
if ( vtotal & 256 )
tmp | = 1 ;
if ( vdispend & 256 )
tmp | = 2 ;
if ( ( vdispend + 1 ) & 256 )
tmp | = 8 ;
if ( vtotal & 512 )
tmp | = 32 ;
if ( vdispend & 512 )
tmp | = 64 ;
WREG_CRT ( VGA_CRTC_OVERFLOW , tmp ) ;
tmp = 0 ;
/* More overflow bits */
if ( ( htotal + 5 ) & 64 )
tmp | = 16 ;
if ( ( htotal + 5 ) & 128 )
tmp | = 32 ;
if ( vtotal & 256 )
tmp | = 64 ;
if ( vtotal & 512 )
tmp | = 128 ;
WREG_CRT ( CL_CRT1A , tmp ) ;
/* Disable Hercules/CGA compatibility */
WREG_CRT ( VGA_CRTC_MODE , 0x03 ) ;
WREG8 ( SEQ_INDEX , 0x7 ) ;
sr07 = RREG8 ( SEQ_DATA ) ;
sr07 & = 0xe0 ;
hdr = 0 ;
2016-12-14 23:32:20 +02:00
switch ( fb - > format - > cpp [ 0 ] * 8 ) {
2012-04-17 14:12:29 +01:00
case 8 :
sr07 | = 0x11 ;
break ;
case 16 :
2014-01-21 14:34:51 -08:00
sr07 | = 0x17 ;
hdr = 0xc1 ;
2012-04-17 14:12:29 +01:00
break ;
case 24 :
sr07 | = 0x15 ;
hdr = 0xc5 ;
break ;
case 32 :
sr07 | = 0x19 ;
hdr = 0xc5 ;
break ;
default :
return - 1 ;
}
WREG_SEQ ( 0x7 , sr07 ) ;
/* Program the pitch */
2016-11-18 21:52:43 +02:00
tmp = fb - > pitches [ 0 ] / 8 ;
2012-04-17 14:12:29 +01:00
WREG_CRT ( VGA_CRTC_OFFSET , tmp ) ;
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22 ;
2016-11-18 21:52:43 +02:00
tmp | = ( fb - > pitches [ 0 ] > > 7 ) & 0x10 ;
tmp | = ( fb - > pitches [ 0 ] > > 6 ) & 0x40 ;
2012-04-17 14:12:29 +01:00
WREG_CRT ( 0x1b , tmp ) ;
/* Enable high-colour modes */
WREG_GFX ( VGA_GFX_MODE , 0x40 ) ;
/* And set graphics mode */
WREG_GFX ( VGA_GFX_MISC , 0x01 ) ;
WREG_HDR ( hdr ) ;
cirrus_crtc_do_set_base ( crtc , old_fb , x , y , 0 ) ;
2014-04-14 11:34:48 +02:00
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
outb ( 0x20 , 0x3c0 ) ;
2012-04-17 14:12:29 +01:00
return 0 ;
}
/*
* This is called before a mode is programmed . A typical use might be to
* enable DPMS during the programming to avoid seeing intermediate stages ,
* but that ' s not relevant to us
*/
static void cirrus_crtc_prepare ( struct drm_crtc * crtc )
{
}
2018-01-31 12:04:50 +01:00
static void cirrus_crtc_load_lut ( struct drm_crtc * crtc )
2012-04-17 14:12:29 +01:00
{
2017-07-13 18:25:31 +02:00
struct drm_device * dev = crtc - > dev ;
struct cirrus_device * cdev = dev - > dev_private ;
u16 * r , * g , * b ;
2012-04-17 14:12:29 +01:00
int i ;
2017-07-13 18:25:31 +02:00
if ( ! crtc - > enabled )
2018-01-31 12:04:50 +01:00
return ;
2017-07-13 18:25:31 +02:00
r = crtc - > gamma_store ;
g = r + crtc - > gamma_size ;
b = g + crtc - > gamma_size ;
for ( i = 0 ; i < CIRRUS_LUT_SIZE ; i + + ) {
/* VGA registers */
WREG8 ( PALETTE_INDEX , i ) ;
WREG8 ( PALETTE_DATA , * r + + > > 8 ) ;
WREG8 ( PALETTE_DATA , * g + + > > 8 ) ;
WREG8 ( PALETTE_DATA , * b + + > > 8 ) ;
2012-04-17 14:12:29 +01:00
}
2018-01-31 12:04:50 +01:00
}
/*
* This is called after a mode is programmed . It should reverse anything done
* by the prepare function
*/
static void cirrus_crtc_commit ( struct drm_crtc * crtc )
{
cirrus_crtc_load_lut ( crtc ) ;
}
/*
* The core can pass us a set of gamma values to program . We actually only
* use this for 8 - bit mode so can ' t perform smooth fades on deeper modes ,
* but it ' s a requirement that we provide the function
*/
static int cirrus_crtc_gamma_set ( struct drm_crtc * crtc , u16 * red , u16 * green ,
u16 * blue , uint32_t size ,
struct drm_modeset_acquire_ctx * ctx )
{
cirrus_crtc_load_lut ( crtc ) ;
2016-06-07 12:49:30 +02:00
return 0 ;
2012-04-17 14:12:29 +01:00
}
/* Simple cleanup function */
static void cirrus_crtc_destroy ( struct drm_crtc * crtc )
{
struct cirrus_crtc * cirrus_crtc = to_cirrus_crtc ( crtc ) ;
drm_crtc_cleanup ( crtc ) ;
kfree ( cirrus_crtc ) ;
}
/* These provide the minimum set of functions required to handle a CRTC */
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
. gamma_set = cirrus_crtc_gamma_set ,
. set_config = drm_crtc_helper_set_config ,
. destroy = cirrus_crtc_destroy ,
} ;
static const struct drm_crtc_helper_funcs cirrus_helper_funcs = {
. dpms = cirrus_crtc_dpms ,
. mode_set = cirrus_crtc_mode_set ,
. mode_set_base = cirrus_crtc_mode_set_base ,
. prepare = cirrus_crtc_prepare ,
. commit = cirrus_crtc_commit ,
} ;
/* CRTC setup */
static void cirrus_crtc_init ( struct drm_device * dev )
{
struct cirrus_device * cdev = dev - > dev_private ;
struct cirrus_crtc * cirrus_crtc ;
cirrus_crtc = kzalloc ( sizeof ( struct cirrus_crtc ) +
( CIRRUSFB_CONN_LIMIT * sizeof ( struct drm_connector * ) ) ,
GFP_KERNEL ) ;
if ( cirrus_crtc = = NULL )
return ;
drm_crtc_init ( dev , & cirrus_crtc - > base , & cirrus_crtc_funcs ) ;
drm_mode_crtc_set_gamma_size ( & cirrus_crtc - > base , CIRRUS_LUT_SIZE ) ;
cdev - > mode_info . crtc = cirrus_crtc ;
drm_crtc_helper_add ( & cirrus_crtc - > base , & cirrus_helper_funcs ) ;
}
static void cirrus_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
}
static void cirrus_encoder_dpms ( struct drm_encoder * encoder , int state )
{
return ;
}
static void cirrus_encoder_prepare ( struct drm_encoder * encoder )
{
}
static void cirrus_encoder_commit ( struct drm_encoder * encoder )
{
}
2014-01-06 20:30:14 +05:30
static void cirrus_encoder_destroy ( struct drm_encoder * encoder )
2012-04-17 14:12:29 +01:00
{
struct cirrus_encoder * cirrus_encoder = to_cirrus_encoder ( encoder ) ;
drm_encoder_cleanup ( encoder ) ;
kfree ( cirrus_encoder ) ;
}
static const struct drm_encoder_helper_funcs cirrus_encoder_helper_funcs = {
. dpms = cirrus_encoder_dpms ,
. mode_set = cirrus_encoder_mode_set ,
. prepare = cirrus_encoder_prepare ,
. commit = cirrus_encoder_commit ,
} ;
static const struct drm_encoder_funcs cirrus_encoder_encoder_funcs = {
. destroy = cirrus_encoder_destroy ,
} ;
static struct drm_encoder * cirrus_encoder_init ( struct drm_device * dev )
{
struct drm_encoder * encoder ;
struct cirrus_encoder * cirrus_encoder ;
cirrus_encoder = kzalloc ( sizeof ( struct cirrus_encoder ) , GFP_KERNEL ) ;
if ( ! cirrus_encoder )
return NULL ;
encoder = & cirrus_encoder - > base ;
encoder - > possible_crtcs = 0x1 ;
drm_encoder_init ( dev , encoder , & cirrus_encoder_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_DAC , NULL ) ;
2012-04-17 14:12:29 +01:00
drm_encoder_helper_add ( encoder , & cirrus_encoder_helper_funcs ) ;
return encoder ;
}
2014-01-06 20:30:14 +05:30
static int cirrus_vga_get_modes ( struct drm_connector * connector )
2012-04-17 14:12:29 +01:00
{
2013-10-11 10:01:09 +02:00
int count ;
2012-04-17 14:12:29 +01:00
2013-10-11 10:01:09 +02:00
/* Just add a static list of modes */
2015-02-03 17:51:23 +01:00
if ( cirrus_bpp < = 24 ) {
count = drm_add_modes_noedid ( connector , 1280 , 1024 ) ;
drm_set_preferred_mode ( connector , 1024 , 768 ) ;
} else {
count = drm_add_modes_noedid ( connector , 800 , 600 ) ;
drm_set_preferred_mode ( connector , 800 , 600 ) ;
}
2013-10-11 10:01:09 +02:00
return count ;
2012-04-17 14:12:29 +01:00
}
2014-01-06 20:30:14 +05:30
static struct drm_encoder * cirrus_connector_best_encoder ( struct drm_connector
2012-04-17 14:12:29 +01:00
* connector )
{
int enc_id = connector - > encoder_ids [ 0 ] ;
/* pick the encoder ids */
2014-07-17 23:29:58 -04:00
if ( enc_id )
2017-03-14 23:25:07 -07:00
return drm_encoder_find ( connector - > dev , NULL , enc_id ) ;
2012-04-17 14:12:29 +01:00
return NULL ;
}
static void cirrus_connector_destroy ( struct drm_connector * connector )
{
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
2015-12-15 12:21:05 +01:00
static const struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
2012-04-17 14:12:29 +01:00
. get_modes = cirrus_vga_get_modes ,
. best_encoder = cirrus_connector_best_encoder ,
} ;
2015-12-15 12:21:05 +01:00
static const struct drm_connector_funcs cirrus_vga_connector_funcs = {
2012-04-17 14:12:29 +01:00
. dpms = drm_helper_connector_dpms ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = cirrus_connector_destroy ,
} ;
static struct drm_connector * cirrus_vga_init ( struct drm_device * dev )
{
struct drm_connector * connector ;
struct cirrus_connector * cirrus_connector ;
cirrus_connector = kzalloc ( sizeof ( struct cirrus_connector ) , GFP_KERNEL ) ;
if ( ! cirrus_connector )
return NULL ;
connector = & cirrus_connector - > base ;
drm_connector_init ( dev , connector ,
& cirrus_vga_connector_funcs , DRM_MODE_CONNECTOR_VGA ) ;
drm_connector_helper_add ( connector , & cirrus_vga_connector_helper_funcs ) ;
2014-09-19 10:11:36 +02:00
drm_connector_register ( connector ) ;
2012-04-17 14:12:29 +01:00
return connector ;
}
int cirrus_modeset_init ( struct cirrus_device * cdev )
{
struct drm_encoder * encoder ;
struct drm_connector * connector ;
int ret ;
drm_mode_config_init ( cdev - > dev ) ;
cdev - > mode_info . mode_config_initialized = true ;
cdev - > dev - > mode_config . max_width = CIRRUS_MAX_FB_WIDTH ;
cdev - > dev - > mode_config . max_height = CIRRUS_MAX_FB_HEIGHT ;
cdev - > dev - > mode_config . fb_base = cdev - > mc . vram_base ;
2018-08-08 13:13:11 +02:00
cdev - > dev - > mode_config . preferred_depth = cirrus_bpp ;
2012-04-17 14:12:29 +01:00
/* don't prefer a shadow on virt GPU */
cdev - > dev - > mode_config . prefer_shadow = 0 ;
cirrus_crtc_init ( cdev - > dev ) ;
encoder = cirrus_encoder_init ( cdev - > dev ) ;
if ( ! encoder ) {
DRM_ERROR ( " cirrus_encoder_init failed \n " ) ;
return - 1 ;
}
connector = cirrus_vga_init ( cdev - > dev ) ;
if ( ! connector ) {
DRM_ERROR ( " cirrus_vga_init failed \n " ) ;
return - 1 ;
}
2018-07-09 10:40:07 +02:00
drm_connector_attach_encoder ( connector , encoder ) ;
2012-04-17 14:12:29 +01:00
ret = cirrus_fbdev_init ( cdev ) ;
if ( ret ) {
DRM_ERROR ( " cirrus_fbdev_init failed \n " ) ;
return ret ;
}
return 0 ;
}
void cirrus_modeset_fini ( struct cirrus_device * cdev )
{
cirrus_fbdev_fini ( cdev ) ;
if ( cdev - > mode_info . mode_config_initialized ) {
drm_mode_config_cleanup ( cdev - > dev ) ;
cdev - > mode_info . mode_config_initialized = false ;
}
}