2005-04-17 02:20:36 +04:00
/*
* drivers / video / chipsfb . c - - frame buffer device for
* Chips & Technologies 65550 chip .
*
* Copyright ( C ) 1998 - 2002 Paul Mackerras
*
* This file is derived from the Powermac " chips " driver :
* Copyright ( C ) 1997 Fabio Riccardi .
* And from the frame buffer device for Open Firmware - initialized devices :
* Copyright ( C ) 1997 Geert Uytterhoeven .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/vmalloc.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/fb.h>
2007-07-26 21:41:12 +04:00
# include <linux/pm.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/pci.h>
2005-06-28 01:36:34 +04:00
# include <linux/console.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# ifdef CONFIG_PMAC_BACKLIGHT
# include <asm/backlight.h>
# endif
/*
* Since we access the display with inb / outb to fixed port numbers ,
* we can only handle one 6555 x chip . - - paulus
*/
# define write_ind(num, val, ap, dp) do { \
outb ( ( num ) , ( ap ) ) ; outb ( ( val ) , ( dp ) ) ; \
} while ( 0 )
# define read_ind(num, var, ap, dp) do { \
outb ( ( num ) , ( ap ) ) ; var = inb ( ( dp ) ) ; \
} while ( 0 )
/* extension registers */
# define write_xr(num, val) write_ind(num, val, 0x3d6, 0x3d7)
# define read_xr(num, var) read_ind(num, var, 0x3d6, 0x3d7)
/* flat panel registers */
# define write_fr(num, val) write_ind(num, val, 0x3d0, 0x3d1)
# define read_fr(num, var) read_ind(num, var, 0x3d0, 0x3d1)
/* CRTC registers */
# define write_cr(num, val) write_ind(num, val, 0x3d4, 0x3d5)
# define read_cr(num, var) read_ind(num, var, 0x3d4, 0x3d5)
/* graphics registers */
# define write_gr(num, val) write_ind(num, val, 0x3ce, 0x3cf)
# define read_gr(num, var) read_ind(num, var, 0x3ce, 0x3cf)
/* sequencer registers */
# define write_sr(num, val) write_ind(num, val, 0x3c4, 0x3c5)
# define read_sr(num, var) read_ind(num, var, 0x3c4, 0x3c5)
/* attribute registers - slightly strange */
# define write_ar(num, val) do { \
inb ( 0x3da ) ; write_ind ( num , val , 0x3c0 , 0x3c0 ) ; \
} while ( 0 )
# define read_ar(num, var) do { \
inb ( 0x3da ) ; read_ind ( num , var , 0x3c0 , 0x3c1 ) ; \
} while ( 0 )
/*
* Exported functions
*/
int chips_init ( void ) ;
static int chipsfb_pci_init ( struct pci_dev * dp , const struct pci_device_id * ) ;
static int chipsfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info ) ;
static int chipsfb_set_par ( struct fb_info * info ) ;
static int chipsfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info ) ;
static int chipsfb_blank ( int blank , struct fb_info * info ) ;
static struct fb_ops chipsfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = chipsfb_check_var ,
. fb_set_par = chipsfb_set_par ,
. fb_setcolreg = chipsfb_setcolreg ,
. fb_blank = chipsfb_blank ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
static int chipsfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
if ( var - > xres > 800 | | var - > yres > 600
| | var - > xres_virtual > 800 | | var - > yres_virtual > 600
| | ( var - > bits_per_pixel ! = 8 & & var - > bits_per_pixel ! = 16 )
| | var - > nonstd
| | ( var - > vmode & FB_VMODE_MASK ) ! = FB_VMODE_NONINTERLACED )
return - EINVAL ;
var - > xres = var - > xres_virtual = 800 ;
var - > yres = var - > yres_virtual = 600 ;
return 0 ;
}
static int chipsfb_set_par ( struct fb_info * info )
{
if ( info - > var . bits_per_pixel = = 16 ) {
write_cr ( 0x13 , 200 ) ; // Set line length (doublewords)
write_xr ( 0x81 , 0x14 ) ; // 15 bit (555) color mode
write_xr ( 0x82 , 0x00 ) ; // Disable palettes
write_xr ( 0x20 , 0x10 ) ; // 16 bit blitter mode
info - > fix . line_length = 800 * 2 ;
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
info - > var . red . offset = 10 ;
info - > var . green . offset = 5 ;
info - > var . blue . offset = 0 ;
info - > var . red . length = info - > var . green . length =
info - > var . blue . length = 5 ;
} else {
/* p->var.bits_per_pixel == 8 */
write_cr ( 0x13 , 100 ) ; // Set line length (doublewords)
write_xr ( 0x81 , 0x12 ) ; // 8 bit color mode
write_xr ( 0x82 , 0x08 ) ; // Graphics gamma enable
write_xr ( 0x20 , 0x00 ) ; // 8 bit blitter mode
info - > fix . line_length = 800 ;
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
info - > var . red . offset = info - > var . green . offset =
info - > var . blue . offset = 0 ;
info - > var . red . length = info - > var . green . length =
info - > var . blue . length = 8 ;
}
return 0 ;
}
static int chipsfb_blank ( int blank , struct fb_info * info )
{
return 1 ; /* get fb_blank to set the colormap to all black */
}
static int chipsfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info )
{
if ( regno > 255 )
return 1 ;
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
outb ( regno , 0x3c8 ) ;
udelay ( 1 ) ;
outb ( red , 0x3c9 ) ;
outb ( green , 0x3c9 ) ;
outb ( blue , 0x3c9 ) ;
return 0 ;
}
struct chips_init_reg {
unsigned char addr ;
unsigned char data ;
} ;
static struct chips_init_reg chips_init_sr [ ] = {
{ 0x00 , 0x03 } ,
{ 0x01 , 0x01 } ,
{ 0x02 , 0x0f } ,
{ 0x04 , 0x0e }
} ;
static struct chips_init_reg chips_init_gr [ ] = {
{ 0x05 , 0x00 } ,
{ 0x06 , 0x0d } ,
{ 0x08 , 0xff }
} ;
static struct chips_init_reg chips_init_ar [ ] = {
{ 0x10 , 0x01 } ,
{ 0x12 , 0x0f } ,
{ 0x13 , 0x00 }
} ;
static struct chips_init_reg chips_init_cr [ ] = {
{ 0x00 , 0x7f } ,
{ 0x01 , 0x63 } ,
{ 0x02 , 0x63 } ,
{ 0x03 , 0x83 } ,
{ 0x04 , 0x66 } ,
{ 0x05 , 0x10 } ,
{ 0x06 , 0x72 } ,
{ 0x07 , 0x3e } ,
{ 0x08 , 0x00 } ,
{ 0x09 , 0x40 } ,
{ 0x0c , 0x00 } ,
{ 0x0d , 0x00 } ,
{ 0x10 , 0x59 } ,
{ 0x11 , 0x0d } ,
{ 0x12 , 0x57 } ,
{ 0x13 , 0x64 } ,
{ 0x14 , 0x00 } ,
{ 0x15 , 0x57 } ,
{ 0x16 , 0x73 } ,
{ 0x17 , 0xe3 } ,
{ 0x18 , 0xff } ,
{ 0x30 , 0x02 } ,
{ 0x31 , 0x02 } ,
{ 0x32 , 0x02 } ,
{ 0x33 , 0x02 } ,
{ 0x40 , 0x00 } ,
{ 0x41 , 0x00 } ,
{ 0x40 , 0x80 }
} ;
static struct chips_init_reg chips_init_fr [ ] = {
{ 0x01 , 0x02 } ,
{ 0x03 , 0x08 } ,
{ 0x04 , 0x81 } ,
{ 0x05 , 0x21 } ,
{ 0x08 , 0x0c } ,
{ 0x0a , 0x74 } ,
{ 0x0b , 0x11 } ,
{ 0x10 , 0x0c } ,
{ 0x11 , 0xe0 } ,
/* { 0x12, 0x40 }, -- 3400 needs 40, 2400 needs 48, no way to tell */
{ 0x20 , 0x63 } ,
{ 0x21 , 0x68 } ,
{ 0x22 , 0x19 } ,
{ 0x23 , 0x7f } ,
{ 0x24 , 0x68 } ,
{ 0x26 , 0x00 } ,
{ 0x27 , 0x0f } ,
{ 0x30 , 0x57 } ,
{ 0x31 , 0x58 } ,
{ 0x32 , 0x0d } ,
{ 0x33 , 0x72 } ,
{ 0x34 , 0x02 } ,
{ 0x35 , 0x22 } ,
{ 0x36 , 0x02 } ,
{ 0x37 , 0x00 }
} ;
static struct chips_init_reg chips_init_xr [ ] = {
{ 0xce , 0x00 } , /* set default memory clock */
{ 0xcc , 0x43 } , /* memory clock ratio */
{ 0xcd , 0x18 } ,
{ 0xce , 0xa1 } ,
{ 0xc8 , 0x84 } ,
{ 0xc9 , 0x0a } ,
{ 0xca , 0x00 } ,
{ 0xcb , 0x20 } ,
{ 0xcf , 0x06 } ,
{ 0xd0 , 0x0e } ,
{ 0x09 , 0x01 } ,
{ 0x0a , 0x02 } ,
{ 0x0b , 0x01 } ,
{ 0x20 , 0x00 } ,
{ 0x40 , 0x03 } ,
{ 0x41 , 0x01 } ,
{ 0x42 , 0x00 } ,
{ 0x80 , 0x82 } ,
{ 0x81 , 0x12 } ,
{ 0x82 , 0x08 } ,
{ 0xa0 , 0x00 } ,
{ 0xa8 , 0x00 }
} ;
static void __init chips_hw_init ( void )
{
int i ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_xr ) ; + + i )
2005-04-17 02:20:36 +04:00
write_xr ( chips_init_xr [ i ] . addr , chips_init_xr [ i ] . data ) ;
outb ( 0x29 , 0x3c2 ) ; /* set misc output reg */
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_sr ) ; + + i )
2005-04-17 02:20:36 +04:00
write_sr ( chips_init_sr [ i ] . addr , chips_init_sr [ i ] . data ) ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_gr ) ; + + i )
2005-04-17 02:20:36 +04:00
write_gr ( chips_init_gr [ i ] . addr , chips_init_gr [ i ] . data ) ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_ar ) ; + + i )
2005-04-17 02:20:36 +04:00
write_ar ( chips_init_ar [ i ] . addr , chips_init_ar [ i ] . data ) ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_cr ) ; + + i )
2005-04-17 02:20:36 +04:00
write_cr ( chips_init_cr [ i ] . addr , chips_init_cr [ i ] . data ) ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( chips_init_fr ) ; + + i )
2005-04-17 02:20:36 +04:00
write_fr ( chips_init_fr [ i ] . addr , chips_init_fr [ i ] . data ) ;
}
2012-12-22 01:07:39 +04:00
static struct fb_fix_screeninfo chipsfb_fix = {
2005-04-17 02:20:36 +04:00
. id = " C&T 65550 " ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_PSEUDOCOLOR ,
. accel = FB_ACCEL_NONE ,
. line_length = 800 ,
// FIXME: Assumes 1MB frame buffer, but 65550 supports 1MB or 2MB.
// * "3500" PowerBook G3 (the original PB G3) has 2MB.
// * 2400 has 1MB composed of 2 Mitsubishi M5M4V4265CTP DRAM chips.
// Motherboard actually supports 2MB -- there are two blank locations
// for a second pair of DRAMs. (Thanks, Apple!)
// * 3400 has 1MB (I think). Don't know if it's expandable.
// -- Tim Seufert
. smem_len = 0x100000 , /* 1MB */
} ;
2012-12-22 01:07:39 +04:00
static struct fb_var_screeninfo chipsfb_var = {
2005-04-17 02:20:36 +04:00
. xres = 800 ,
. yres = 600 ,
. xres_virtual = 800 ,
. yres_virtual = 600 ,
. bits_per_pixel = 8 ,
. red = { . length = 8 } ,
. green = { . length = 8 } ,
. blue = { . length = 8 } ,
. height = - 1 ,
. width = - 1 ,
. vmode = FB_VMODE_NONINTERLACED ,
. pixclock = 10000 ,
. left_margin = 16 ,
. right_margin = 16 ,
. upper_margin = 16 ,
. lower_margin = 16 ,
. hsync_len = 8 ,
. vsync_len = 8 ,
} ;
2012-12-22 01:07:39 +04:00
static void init_chips ( struct fb_info * p , unsigned long addr )
2005-04-17 02:20:36 +04:00
{
2005-06-28 01:36:34 +04:00
memset ( p - > screen_base , 0 , 0x100000 ) ;
2005-04-17 02:20:36 +04:00
p - > fix = chipsfb_fix ;
p - > fix . smem_start = addr ;
p - > var = chipsfb_var ;
p - > fbops = & chipsfb_ops ;
p - > flags = FBINFO_DEFAULT ;
fb_alloc_cmap ( & p - > cmap , 256 , 0 ) ;
chips_hw_init ( ) ;
}
2012-12-22 01:07:39 +04:00
static int chipsfb_pci_init ( struct pci_dev * dp , const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
2005-06-28 01:36:34 +04:00
struct fb_info * p ;
2005-04-17 02:20:36 +04:00
unsigned long addr , size ;
unsigned short cmd ;
2005-06-28 01:36:34 +04:00
int rc = - ENODEV ;
if ( pci_enable_device ( dp ) < 0 ) {
dev_err ( & dp - > dev , " Cannot enable PCI device \n " ) ;
goto err_out ;
}
2005-04-17 02:20:36 +04:00
if ( ( dp - > resource [ 0 ] . flags & IORESOURCE_MEM ) = = 0 )
2005-06-28 01:36:34 +04:00
goto err_disable ;
2005-04-17 02:20:36 +04:00
addr = pci_resource_start ( dp , 0 ) ;
size = pci_resource_len ( dp , 0 ) ;
if ( addr = = 0 )
2005-06-28 01:36:34 +04:00
goto err_disable ;
p = framebuffer_alloc ( 0 , & dp - > dev ) ;
if ( p = = NULL ) {
dev_err ( & dp - > dev , " Cannot allocate framebuffer structure \n " ) ;
rc = - ENOMEM ;
goto err_disable ;
}
if ( pci_request_region ( dp , 0 , " chipsfb " ) ! = 0 ) {
dev_err ( & dp - > dev , " Cannot request framebuffer \n " ) ;
rc = - EBUSY ;
goto err_release_fb ;
}
2005-04-17 02:20:36 +04:00
# ifdef __BIG_ENDIAN
addr + = 0x800000 ; // Use big-endian aperture
# endif
/* we should use pci_enable_device here, but,
the device doesn ' t declare its I / O ports in its BARs
so pci_enable_device won ' t turn on I / O responses */
pci_read_config_word ( dp , PCI_COMMAND , & cmd ) ;
cmd | = 3 ; /* enable memory and IO space */
pci_write_config_word ( dp , PCI_COMMAND , cmd ) ;
# ifdef CONFIG_PMAC_BACKLIGHT
/* turn on the backlight */
2006-06-25 16:47:08 +04:00
mutex_lock ( & pmac_backlight_mutex ) ;
if ( pmac_backlight ) {
2007-02-11 02:07:48 +03:00
pmac_backlight - > props . power = FB_BLANK_UNBLANK ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( pmac_backlight ) ;
2006-06-25 16:47:08 +04:00
}
mutex_unlock ( & pmac_backlight_mutex ) ;
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_PMAC_BACKLIGHT */
2005-06-28 01:36:34 +04:00
# ifdef CONFIG_PPC
2005-04-17 02:20:36 +04:00
p - > screen_base = __ioremap ( addr , 0x200000 , _PAGE_NO_CACHE ) ;
2005-06-28 01:36:34 +04:00
# else
p - > screen_base = ioremap ( addr , 0x200000 ) ;
# endif
2005-04-17 02:20:36 +04:00
if ( p - > screen_base = = NULL ) {
2005-06-28 01:36:34 +04:00
dev_err ( & dp - > dev , " Cannot map framebuffer \n " ) ;
rc = - ENOMEM ;
goto err_release_pci ;
2005-04-17 02:20:36 +04:00
}
2005-06-28 01:36:34 +04:00
pci_set_drvdata ( dp , p ) ;
2005-04-17 02:20:36 +04:00
init_chips ( p , addr ) ;
2005-06-28 01:36:34 +04:00
if ( register_framebuffer ( p ) < 0 ) {
dev_err ( & dp - > dev , " C&T 65550 framebuffer failed to register \n " ) ;
goto err_unmap ;
}
dev_info ( & dp - > dev , " fb%d: Chips 65550 frame buffer "
" (%dK RAM detected) \n " ,
p - > node , p - > fix . smem_len / 1024 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-06-28 01:36:34 +04:00
err_unmap :
iounmap ( p - > screen_base ) ;
err_release_pci :
pci_release_region ( dp , 0 ) ;
err_release_fb :
framebuffer_release ( p ) ;
err_disable :
err_out :
return rc ;
2005-04-17 02:20:36 +04:00
}
2012-12-22 01:07:39 +04:00
static void chipsfb_remove ( struct pci_dev * dp )
2005-04-17 02:20:36 +04:00
{
struct fb_info * p = pci_get_drvdata ( dp ) ;
2005-06-28 01:36:34 +04:00
if ( p - > screen_base = = NULL )
2005-04-17 02:20:36 +04:00
return ;
unregister_framebuffer ( p ) ;
iounmap ( p - > screen_base ) ;
p - > screen_base = NULL ;
2005-06-28 01:36:34 +04:00
pci_release_region ( dp , 0 ) ;
}
# ifdef CONFIG_PM
static int chipsfb_pci_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct fb_info * p = pci_get_drvdata ( pdev ) ;
2005-09-04 02:56:57 +04:00
if ( state . event = = pdev - > dev . power . power_state . event )
2005-06-28 01:36:34 +04:00
return 0 ;
2008-02-23 21:13:25 +03:00
if ( ! ( state . event & PM_EVENT_SLEEP ) )
2005-06-28 01:36:34 +04:00
goto done ;
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2005-06-28 01:36:34 +04:00
chipsfb_blank ( 1 , p ) ;
fb_set_suspend ( p , 1 ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2005-06-28 01:36:34 +04:00
done :
pdev - > dev . power . power_state = state ;
return 0 ;
}
static int chipsfb_pci_resume ( struct pci_dev * pdev )
{
struct fb_info * p = pci_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2005-06-28 01:36:34 +04:00
fb_set_suspend ( p , 0 ) ;
chipsfb_blank ( 0 , p ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2005-06-28 01:36:34 +04:00
pdev - > dev . power . power_state = PMSG_ON ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-06-28 01:36:34 +04:00
# endif /* CONFIG_PM */
2005-04-17 02:20:36 +04:00
static struct pci_device_id chipsfb_pci_tbl [ ] = {
{ PCI_VENDOR_ID_CT , PCI_DEVICE_ID_CT_65550 , PCI_ANY_ID , PCI_ANY_ID } ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , chipsfb_pci_tbl ) ;
static struct pci_driver chipsfb_driver = {
. name = " chipsfb " ,
. id_table = chipsfb_pci_tbl ,
. probe = chipsfb_pci_init ,
2012-12-22 01:07:39 +04:00
. remove = chipsfb_remove ,
2005-06-28 01:36:34 +04:00
# ifdef CONFIG_PM
. suspend = chipsfb_pci_suspend ,
. resume = chipsfb_pci_resume ,
# endif
2005-04-17 02:20:36 +04:00
} ;
int __init chips_init ( void )
{
if ( fb_get_options ( " chipsfb " , NULL ) )
return - ENODEV ;
return pci_register_driver ( & chipsfb_driver ) ;
}
module_init ( chips_init ) ;
static void __exit chipsfb_exit ( void )
{
pci_unregister_driver ( & chipsfb_driver ) ;
}
MODULE_LICENSE ( " GPL " ) ;