add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
obj-$(CONFIG_FB_ATY) += atyfb.o
obj-$(CONFIG_FB_ATY128) += aty128fb.o
obj-$(CONFIG_FB_RADEON) += radeonfb.o
atyfb-y := atyfb_base.o mach64_accel.o mach64_cursor.o
atyfb-$(CONFIG_FB_ATY_GX) += mach64_gx.o
atyfb-$(CONFIG_FB_ATY_CT) += mach64_ct.o
atyfb-objs := $(atyfb-y)
radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o
radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o
radeonfb-$(CONFIG_FB_RADEON_BACKLIGHT) += radeon_backlight.o
radeonfb-objs := $(radeonfb-y)

View File

@@ -0,0 +1,214 @@
/*
* ATI PCI IDs from XFree86, kept here to make sync'ing with
* XFree much simpler. Currently, this list is only used by
* radeonfb
*/
#define PCI_CHIP_RV380_3150 0x3150
#define PCI_CHIP_RV380_3151 0x3151
#define PCI_CHIP_RV380_3152 0x3152
#define PCI_CHIP_RV380_3153 0x3153
#define PCI_CHIP_RV380_3154 0x3154
#define PCI_CHIP_RV380_3156 0x3156
#define PCI_CHIP_RV380_3E50 0x3E50
#define PCI_CHIP_RV380_3E51 0x3E51
#define PCI_CHIP_RV380_3E52 0x3E52
#define PCI_CHIP_RV380_3E53 0x3E53
#define PCI_CHIP_RV380_3E54 0x3E54
#define PCI_CHIP_RV380_3E56 0x3E56
#define PCI_CHIP_RS100_4136 0x4136
#define PCI_CHIP_RS200_4137 0x4137
#define PCI_CHIP_R300_AD 0x4144
#define PCI_CHIP_R300_AE 0x4145
#define PCI_CHIP_R300_AF 0x4146
#define PCI_CHIP_R300_AG 0x4147
#define PCI_CHIP_R350_AH 0x4148
#define PCI_CHIP_R350_AI 0x4149
#define PCI_CHIP_R350_AJ 0x414A
#define PCI_CHIP_R350_AK 0x414B
#define PCI_CHIP_RV350_AP 0x4150
#define PCI_CHIP_RV350_AQ 0x4151
#define PCI_CHIP_RV360_AR 0x4152
#define PCI_CHIP_RV350_AS 0x4153
#define PCI_CHIP_RV350_AT 0x4154
#define PCI_CHIP_RV350_AV 0x4156
#define PCI_CHIP_MACH32 0x4158
#define PCI_CHIP_RS250_4237 0x4237
#define PCI_CHIP_R200_BB 0x4242
#define PCI_CHIP_R200_BC 0x4243
#define PCI_CHIP_RS100_4336 0x4336
#define PCI_CHIP_RS200_4337 0x4337
#define PCI_CHIP_MACH64CT 0x4354
#define PCI_CHIP_MACH64CX 0x4358
#define PCI_CHIP_RS250_4437 0x4437
#define PCI_CHIP_MACH64ET 0x4554
#define PCI_CHIP_MACH64GB 0x4742
#define PCI_CHIP_MACH64GD 0x4744
#define PCI_CHIP_MACH64GI 0x4749
#define PCI_CHIP_MACH64GL 0x474C
#define PCI_CHIP_MACH64GM 0x474D
#define PCI_CHIP_MACH64GN 0x474E
#define PCI_CHIP_MACH64GO 0x474F
#define PCI_CHIP_MACH64GP 0x4750
#define PCI_CHIP_MACH64GQ 0x4751
#define PCI_CHIP_MACH64GR 0x4752
#define PCI_CHIP_MACH64GS 0x4753
#define PCI_CHIP_MACH64GT 0x4754
#define PCI_CHIP_MACH64GU 0x4755
#define PCI_CHIP_MACH64GV 0x4756
#define PCI_CHIP_MACH64GW 0x4757
#define PCI_CHIP_MACH64GX 0x4758
#define PCI_CHIP_MACH64GY 0x4759
#define PCI_CHIP_MACH64GZ 0x475A
#define PCI_CHIP_RV250_Id 0x4964
#define PCI_CHIP_RV250_Ie 0x4965
#define PCI_CHIP_RV250_If 0x4966
#define PCI_CHIP_RV250_Ig 0x4967
#define PCI_CHIP_R420_JH 0x4A48
#define PCI_CHIP_R420_JI 0x4A49
#define PCI_CHIP_R420_JJ 0x4A4A
#define PCI_CHIP_R420_JK 0x4A4B
#define PCI_CHIP_R420_JL 0x4A4C
#define PCI_CHIP_R420_JM 0x4A4D
#define PCI_CHIP_R420_JN 0x4A4E
#define PCI_CHIP_R420_JP 0x4A50
#define PCI_CHIP_MACH64LB 0x4C42
#define PCI_CHIP_MACH64LD 0x4C44
#define PCI_CHIP_RAGE128LE 0x4C45
#define PCI_CHIP_RAGE128LF 0x4C46
#define PCI_CHIP_MACH64LG 0x4C47
#define PCI_CHIP_MACH64LI 0x4C49
#define PCI_CHIP_MACH64LM 0x4C4D
#define PCI_CHIP_MACH64LN 0x4C4E
#define PCI_CHIP_MACH64LP 0x4C50
#define PCI_CHIP_MACH64LQ 0x4C51
#define PCI_CHIP_MACH64LR 0x4C52
#define PCI_CHIP_MACH64LS 0x4C53
#define PCI_CHIP_MACH64LT 0x4C54
#define PCI_CHIP_RADEON_LW 0x4C57
#define PCI_CHIP_RADEON_LX 0x4C58
#define PCI_CHIP_RADEON_LY 0x4C59
#define PCI_CHIP_RADEON_LZ 0x4C5A
#define PCI_CHIP_RV250_Ld 0x4C64
#define PCI_CHIP_RV250_Le 0x4C65
#define PCI_CHIP_RV250_Lf 0x4C66
#define PCI_CHIP_RV250_Lg 0x4C67
#define PCI_CHIP_RV250_Ln 0x4C6E
#define PCI_CHIP_RAGE128MF 0x4D46
#define PCI_CHIP_RAGE128ML 0x4D4C
#define PCI_CHIP_R300_ND 0x4E44
#define PCI_CHIP_R300_NE 0x4E45
#define PCI_CHIP_R300_NF 0x4E46
#define PCI_CHIP_R300_NG 0x4E47
#define PCI_CHIP_R350_NH 0x4E48
#define PCI_CHIP_R350_NI 0x4E49
#define PCI_CHIP_R360_NJ 0x4E4A
#define PCI_CHIP_R350_NK 0x4E4B
#define PCI_CHIP_RV350_NP 0x4E50
#define PCI_CHIP_RV350_NQ 0x4E51
#define PCI_CHIP_RV350_NR 0x4E52
#define PCI_CHIP_RV350_NS 0x4E53
#define PCI_CHIP_RV350_NT 0x4E54
#define PCI_CHIP_RV350_NV 0x4E56
#define PCI_CHIP_RAGE128PA 0x5041
#define PCI_CHIP_RAGE128PB 0x5042
#define PCI_CHIP_RAGE128PC 0x5043
#define PCI_CHIP_RAGE128PD 0x5044
#define PCI_CHIP_RAGE128PE 0x5045
#define PCI_CHIP_RAGE128PF 0x5046
#define PCI_CHIP_RAGE128PG 0x5047
#define PCI_CHIP_RAGE128PH 0x5048
#define PCI_CHIP_RAGE128PI 0x5049
#define PCI_CHIP_RAGE128PJ 0x504A
#define PCI_CHIP_RAGE128PK 0x504B
#define PCI_CHIP_RAGE128PL 0x504C
#define PCI_CHIP_RAGE128PM 0x504D
#define PCI_CHIP_RAGE128PN 0x504E
#define PCI_CHIP_RAGE128PO 0x504F
#define PCI_CHIP_RAGE128PP 0x5050
#define PCI_CHIP_RAGE128PQ 0x5051
#define PCI_CHIP_RAGE128PR 0x5052
#define PCI_CHIP_RAGE128PS 0x5053
#define PCI_CHIP_RAGE128PT 0x5054
#define PCI_CHIP_RAGE128PU 0x5055
#define PCI_CHIP_RAGE128PV 0x5056
#define PCI_CHIP_RAGE128PW 0x5057
#define PCI_CHIP_RAGE128PX 0x5058
#define PCI_CHIP_RADEON_QD 0x5144
#define PCI_CHIP_RADEON_QE 0x5145
#define PCI_CHIP_RADEON_QF 0x5146
#define PCI_CHIP_RADEON_QG 0x5147
#define PCI_CHIP_R200_QH 0x5148
#define PCI_CHIP_R200_QI 0x5149
#define PCI_CHIP_R200_QJ 0x514A
#define PCI_CHIP_R200_QK 0x514B
#define PCI_CHIP_R200_QL 0x514C
#define PCI_CHIP_R200_QM 0x514D
#define PCI_CHIP_R200_QN 0x514E
#define PCI_CHIP_R200_QO 0x514F
#define PCI_CHIP_RV200_QW 0x5157
#define PCI_CHIP_RV200_QX 0x5158
#define PCI_CHIP_RV100_QY 0x5159
#define PCI_CHIP_RV100_QZ 0x515A
#define PCI_CHIP_RN50 0x515E
#define PCI_CHIP_RAGE128RE 0x5245
#define PCI_CHIP_RAGE128RF 0x5246
#define PCI_CHIP_RAGE128RG 0x5247
#define PCI_CHIP_RAGE128RK 0x524B
#define PCI_CHIP_RAGE128RL 0x524C
#define PCI_CHIP_RAGE128SE 0x5345
#define PCI_CHIP_RAGE128SF 0x5346
#define PCI_CHIP_RAGE128SG 0x5347
#define PCI_CHIP_RAGE128SH 0x5348
#define PCI_CHIP_RAGE128SK 0x534B
#define PCI_CHIP_RAGE128SL 0x534C
#define PCI_CHIP_RAGE128SM 0x534D
#define PCI_CHIP_RAGE128SN 0x534E
#define PCI_CHIP_RAGE128TF 0x5446
#define PCI_CHIP_RAGE128TL 0x544C
#define PCI_CHIP_RAGE128TR 0x5452
#define PCI_CHIP_RAGE128TS 0x5453
#define PCI_CHIP_RAGE128TT 0x5454
#define PCI_CHIP_RAGE128TU 0x5455
#define PCI_CHIP_RV370_5460 0x5460
#define PCI_CHIP_RV370_5461 0x5461
#define PCI_CHIP_RV370_5462 0x5462
#define PCI_CHIP_RV370_5463 0x5463
#define PCI_CHIP_RV370_5464 0x5464
#define PCI_CHIP_RV370_5465 0x5465
#define PCI_CHIP_RV370_5466 0x5466
#define PCI_CHIP_RV370_5467 0x5467
#define PCI_CHIP_R423_UH 0x5548
#define PCI_CHIP_R423_UI 0x5549
#define PCI_CHIP_R423_UJ 0x554A
#define PCI_CHIP_R423_UK 0x554B
#define PCI_CHIP_R423_UQ 0x5551
#define PCI_CHIP_R423_UR 0x5552
#define PCI_CHIP_R423_UT 0x5554
#define PCI_CHIP_MACH64VT 0x5654
#define PCI_CHIP_MACH64VU 0x5655
#define PCI_CHIP_MACH64VV 0x5656
#define PCI_CHIP_RC410_5A62 0x5A62
#define PCI_CHIP_RS300_5834 0x5834
#define PCI_CHIP_RS300_5835 0x5835
#define PCI_CHIP_RS300_5836 0x5836
#define PCI_CHIP_RS300_5837 0x5837
#define PCI_CHIP_RS480_5955 0x5955
#define PCI_CHIP_RV280_5960 0x5960
#define PCI_CHIP_RV280_5961 0x5961
#define PCI_CHIP_RV280_5962 0x5962
#define PCI_CHIP_RV280_5964 0x5964
#define PCI_CHIP_RS482_5975 0x5975
#define PCI_CHIP_RV370_5B60 0x5B60
#define PCI_CHIP_RV370_5B61 0x5B61
#define PCI_CHIP_RV370_5B62 0x5B62
#define PCI_CHIP_RV370_5B63 0x5B63
#define PCI_CHIP_RV370_5B64 0x5B64
#define PCI_CHIP_RV370_5B65 0x5B65
#define PCI_CHIP_RV370_5B66 0x5B66
#define PCI_CHIP_RV370_5B67 0x5B67
#define PCI_CHIP_RV280_5C61 0x5C61
#define PCI_CHIP_RV280_5C63 0x5C63
#define PCI_CHIP_R423_5D57 0x5D57
#define PCI_CHIP_RS350_7834 0x7834
#define PCI_CHIP_RS350_7835 0x7835

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,369 @@
/*
* ATI Frame Buffer Device Driver Core Definitions
*/
#include <linux/spinlock.h>
#include <linux/wait.h>
/*
* Elements of the hardware specific atyfb_par structure
*/
struct crtc {
u32 vxres;
u32 vyres;
u32 xoffset;
u32 yoffset;
u32 bpp;
u32 h_tot_disp;
u32 h_sync_strt_wid;
u32 v_tot_disp;
u32 v_sync_strt_wid;
u32 vline_crnt_vline;
u32 off_pitch;
u32 gen_cntl;
u32 dp_pix_width; /* acceleration */
u32 dp_chain_mask; /* acceleration */
#ifdef CONFIG_FB_ATY_GENERIC_LCD
u32 horz_stretching;
u32 vert_stretching;
u32 ext_vert_stretch;
u32 shadow_h_tot_disp;
u32 shadow_h_sync_strt_wid;
u32 shadow_v_tot_disp;
u32 shadow_v_sync_strt_wid;
u32 lcd_gen_cntl;
u32 lcd_config_panel;
u32 lcd_index;
#endif
};
struct aty_interrupt {
wait_queue_head_t wait;
unsigned int count;
int pan_display;
};
struct pll_info {
int pll_max;
int pll_min;
int sclk, mclk, mclk_pm, xclk;
int ref_div;
int ref_clk;
int ecp_max;
};
typedef struct {
u16 unknown1;
u16 PCLK_min_freq;
u16 PCLK_max_freq;
u16 unknown2;
u16 ref_freq;
u16 ref_divider;
u16 unknown3;
u16 MCLK_pwd;
u16 MCLK_max_freq;
u16 XCLK_max_freq;
u16 SCLK_freq;
} __attribute__ ((packed)) PLL_BLOCK_MACH64;
struct pll_514 {
u8 m;
u8 n;
};
struct pll_18818 {
u32 program_bits;
u32 locationAddr;
u32 period_in_ps;
u32 post_divider;
};
struct pll_ct {
u8 pll_ref_div;
u8 pll_gen_cntl;
u8 mclk_fb_div;
u8 mclk_fb_mult; /* 2 ro 4 */
u8 sclk_fb_div;
u8 pll_vclk_cntl;
u8 vclk_post_div;
u8 vclk_fb_div;
u8 pll_ext_cntl;
u8 ext_vpll_cntl;
u8 spll_cntl2;
u32 dsp_config; /* Mach64 GTB DSP */
u32 dsp_on_off; /* Mach64 GTB DSP */
u32 dsp_loop_latency;
u32 fifo_size;
u32 xclkpagefaultdelay;
u32 xclkmaxrasdelay;
u8 xclk_ref_div;
u8 xclk_post_div;
u8 mclk_post_div_real;
u8 xclk_post_div_real;
u8 vclk_post_div_real;
u8 features;
#ifdef CONFIG_FB_ATY_GENERIC_LCD
u32 xres; /* use for LCD stretching/scaling */
#endif
};
/*
for pll_ct.features
*/
#define DONT_USE_SPLL 0x1
#define DONT_USE_XDLL 0x2
#define USE_CPUCLK 0x4
#define POWERDOWN_PLL 0x8
union aty_pll {
struct pll_ct ct;
struct pll_514 ibm514;
struct pll_18818 ics2595;
};
/*
* The hardware parameters for each card
*/
struct atyfb_par {
u32 pseudo_palette[16];
struct { u8 red, green, blue; } palette[256];
const struct aty_dac_ops *dac_ops;
const struct aty_pll_ops *pll_ops;
void __iomem *ati_regbase;
unsigned long clk_wr_offset; /* meaning overloaded, clock id by CT */
struct crtc crtc;
union aty_pll pll;
struct pll_info pll_limits;
u32 features;
u32 ref_clk_per;
u32 pll_per;
u32 mclk_per;
u32 xclk_per;
u8 bus_type;
u8 ram_type;
u8 mem_refresh_rate;
u16 pci_id;
u32 accel_flags;
int blitter_may_be_busy;
int asleep;
int lock_blank;
unsigned long res_start;
unsigned long res_size;
struct pci_dev *pdev;
#ifdef __sparc__
struct pci_mmap_map *mmap_map;
u8 mmaped;
#endif
int open;
#ifdef CONFIG_FB_ATY_GENERIC_LCD
unsigned long bios_base_phys;
unsigned long bios_base;
unsigned long lcd_table;
u16 lcd_width;
u16 lcd_height;
u32 lcd_pixclock;
u16 lcd_refreshrate;
u16 lcd_htotal;
u16 lcd_hdisp;
u16 lcd_hsync_dly;
u16 lcd_hsync_len;
u16 lcd_vtotal;
u16 lcd_vdisp;
u16 lcd_vsync_len;
u16 lcd_right_margin;
u16 lcd_lower_margin;
u16 lcd_hblank_len;
u16 lcd_vblank_len;
#endif
unsigned long aux_start; /* auxiliary aperture */
unsigned long aux_size;
struct aty_interrupt vblank;
unsigned long irq_flags;
unsigned int irq;
spinlock_t int_lock;
#ifdef CONFIG_MTRR
int mtrr_aper;
int mtrr_reg;
#endif
u32 mem_cntl;
struct crtc saved_crtc;
union aty_pll saved_pll;
};
/*
* ATI Mach64 features
*/
#define M64_HAS(feature) ((par)->features & (M64F_##feature))
#define M64F_RESET_3D 0x00000001
#define M64F_MAGIC_FIFO 0x00000002
#define M64F_GTB_DSP 0x00000004
#define M64F_FIFO_32 0x00000008
#define M64F_SDRAM_MAGIC_PLL 0x00000010
#define M64F_MAGIC_POSTDIV 0x00000020
#define M64F_INTEGRATED 0x00000040
#define M64F_CT_BUS 0x00000080
#define M64F_VT_BUS 0x00000100
#define M64F_MOBIL_BUS 0x00000200
#define M64F_GX 0x00000400
#define M64F_CT 0x00000800
#define M64F_VT 0x00001000
#define M64F_GT 0x00002000
#define M64F_MAGIC_VRAM_SIZE 0x00004000
#define M64F_G3_PB_1_1 0x00008000
#define M64F_G3_PB_1024x768 0x00010000
#define M64F_EXTRA_BRIGHT 0x00020000
#define M64F_LT_LCD_REGS 0x00040000
#define M64F_XL_DLL 0x00080000
#define M64F_MFB_FORCE_4 0x00100000
#define M64F_HW_TRIPLE 0x00200000
#define M64F_XL_MEM 0x00400000
/*
* Register access
*/
static inline u32 aty_ld_le32(int regindex, const struct atyfb_par *par)
{
/* Hack for bloc 1, should be cleanly optimized by compiler */
if (regindex >= 0x400)
regindex -= 0x800;
#ifdef CONFIG_ATARI
return in_le32(par->ati_regbase + regindex);
#else
return readl(par->ati_regbase + regindex);
#endif
}
static inline void aty_st_le32(int regindex, u32 val, const struct atyfb_par *par)
{
/* Hack for bloc 1, should be cleanly optimized by compiler */
if (regindex >= 0x400)
regindex -= 0x800;
#ifdef CONFIG_ATARI
out_le32(par->ati_regbase + regindex, val);
#else
writel(val, par->ati_regbase + regindex);
#endif
}
static inline void aty_st_le16(int regindex, u16 val,
const struct atyfb_par *par)
{
/* Hack for bloc 1, should be cleanly optimized by compiler */
if (regindex >= 0x400)
regindex -= 0x800;
#ifdef CONFIG_ATARI
out_le16(par->ati_regbase + regindex, val);
#else
writel(val, par->ati_regbase + regindex);
#endif
}
static inline u8 aty_ld_8(int regindex, const struct atyfb_par *par)
{
/* Hack for bloc 1, should be cleanly optimized by compiler */
if (regindex >= 0x400)
regindex -= 0x800;
#ifdef CONFIG_ATARI
return in_8(par->ati_regbase + regindex);
#else
return readb(par->ati_regbase + regindex);
#endif
}
static inline void aty_st_8(int regindex, u8 val, const struct atyfb_par *par)
{
/* Hack for bloc 1, should be cleanly optimized by compiler */
if (regindex >= 0x400)
regindex -= 0x800;
#ifdef CONFIG_ATARI
out_8(par->ati_regbase + regindex, val);
#else
writeb(val, par->ati_regbase + regindex);
#endif
}
#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \
defined (CONFIG_FB_ATY_GENERIC_LCD) || defined (CONFIG_FB_ATY_BACKLIGHT)
extern void aty_st_lcd(int index, u32 val, const struct atyfb_par *par);
extern u32 aty_ld_lcd(int index, const struct atyfb_par *par);
#endif
/*
* DAC operations
*/
struct aty_dac_ops {
int (*set_dac) (const struct fb_info * info,
const union aty_pll * pll, u32 bpp, u32 accel);
};
extern const struct aty_dac_ops aty_dac_ibm514; /* IBM RGB514 */
extern const struct aty_dac_ops aty_dac_ati68860b; /* ATI 68860-B */
extern const struct aty_dac_ops aty_dac_att21c498; /* AT&T 21C498 */
extern const struct aty_dac_ops aty_dac_unsupported; /* unsupported */
extern const struct aty_dac_ops aty_dac_ct; /* Integrated */
/*
* Clock operations
*/
struct aty_pll_ops {
int (*var_to_pll) (const struct fb_info * info, u32 vclk_per, u32 bpp, union aty_pll * pll);
u32 (*pll_to_var) (const struct fb_info * info, const union aty_pll * pll);
void (*set_pll) (const struct fb_info * info, const union aty_pll * pll);
void (*get_pll) (const struct fb_info *info, union aty_pll * pll);
int (*init_pll) (const struct fb_info * info, union aty_pll * pll);
void (*resume_pll)(const struct fb_info *info, union aty_pll *pll);
};
extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */
extern const struct aty_pll_ops aty_pll_stg1703; /* STG 1703 */
extern const struct aty_pll_ops aty_pll_ch8398; /* Chrontel 8398 */
extern const struct aty_pll_ops aty_pll_att20c408; /* AT&T 20C408 */
extern const struct aty_pll_ops aty_pll_ibm514; /* IBM RGB514 */
extern const struct aty_pll_ops aty_pll_unsupported; /* unsupported */
extern const struct aty_pll_ops aty_pll_ct; /* Integrated */
extern void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll);
extern u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par);
/*
* Hardware cursor support
*/
extern int aty_init_cursor(struct fb_info *info);
/*
* Hardware acceleration
*/
static inline void wait_for_fifo(u16 entries, const struct atyfb_par *par)
{
while ((aty_ld_le32(FIFO_STAT, par) & 0xffff) >
((u32) (0x8000 >> entries)));
}
static inline void wait_for_idle(struct atyfb_par *par)
{
wait_for_fifo(16, par);
while ((aty_ld_le32(GUI_STAT, par) & 1) != 0);
par->blitter_may_be_busy = 0;
}
extern void aty_reset_engine(const struct atyfb_par *par);
extern void aty_init_engine(struct atyfb_par *par, struct fb_info *info);
extern u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par);
void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
void atyfb_imageblit(struct fb_info *info, const struct fb_image *image);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,426 @@
/*
* ATI Mach64 Hardware Acceleration
*/
#include <linux/delay.h>
#include <linux/fb.h>
#include <video/mach64.h>
#include "atyfb.h"
/*
* Generic Mach64 routines
*/
/* this is for DMA GUI engine! work in progress */
typedef struct {
u32 frame_buf_offset;
u32 system_mem_addr;
u32 command;
u32 reserved;
} BM_DESCRIPTOR_ENTRY;
#define LAST_DESCRIPTOR (1 << 31)
#define SYSTEM_TO_FRAME_BUFFER 0
static u32 rotation24bpp(u32 dx, u32 direction)
{
u32 rotation;
if (direction & DST_X_LEFT_TO_RIGHT) {
rotation = (dx / 4) % 6;
} else {
rotation = ((dx + 2) / 4) % 6;
}
return ((rotation << 8) | DST_24_ROTATION_ENABLE);
}
void aty_reset_engine(const struct atyfb_par *par)
{
/* reset engine */
aty_st_le32(GEN_TEST_CNTL,
aty_ld_le32(GEN_TEST_CNTL, par) &
~(GUI_ENGINE_ENABLE | HWCURSOR_ENABLE), par);
/* enable engine */
aty_st_le32(GEN_TEST_CNTL,
aty_ld_le32(GEN_TEST_CNTL, par) | GUI_ENGINE_ENABLE, par);
/* ensure engine is not locked up by clearing any FIFO or */
/* HOST errors */
aty_st_le32(BUS_CNTL,
aty_ld_le32(BUS_CNTL, par) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK, par);
}
static void reset_GTC_3D_engine(const struct atyfb_par *par)
{
aty_st_le32(SCALE_3D_CNTL, 0xc0, par);
mdelay(GTC_3D_RESET_DELAY);
aty_st_le32(SETUP_CNTL, 0x00, par);
mdelay(GTC_3D_RESET_DELAY);
aty_st_le32(SCALE_3D_CNTL, 0x00, par);
mdelay(GTC_3D_RESET_DELAY);
}
void aty_init_engine(struct atyfb_par *par, struct fb_info *info)
{
u32 pitch_value;
u32 vxres;
/* determine modal information from global mode structure */
pitch_value = info->fix.line_length / (info->var.bits_per_pixel / 8);
vxres = info->var.xres_virtual;
if (info->var.bits_per_pixel == 24) {
/* In 24 bpp, the engine is in 8 bpp - this requires that all */
/* horizontal coordinates and widths must be adjusted */
pitch_value *= 3;
vxres *= 3;
}
/* On GTC (RagePro), we need to reset the 3D engine before */
if (M64_HAS(RESET_3D))
reset_GTC_3D_engine(par);
/* Reset engine, enable, and clear any engine errors */
aty_reset_engine(par);
/* Ensure that vga page pointers are set to zero - the upper */
/* page pointers are set to 1 to handle overflows in the */
/* lower page */
aty_st_le32(MEM_VGA_WP_SEL, 0x00010000, par);
aty_st_le32(MEM_VGA_RP_SEL, 0x00010000, par);
/* ---- Setup standard engine context ---- */
/* All GUI registers here are FIFOed - therefore, wait for */
/* the appropriate number of empty FIFO entries */
wait_for_fifo(14, par);
/* enable all registers to be loaded for context loads */
aty_st_le32(CONTEXT_MASK, 0xFFFFFFFF, par);
/* set destination pitch to modal pitch, set offset to zero */
aty_st_le32(DST_OFF_PITCH, (pitch_value / 8) << 22, par);
/* zero these registers (set them to a known state) */
aty_st_le32(DST_Y_X, 0, par);
aty_st_le32(DST_HEIGHT, 0, par);
aty_st_le32(DST_BRES_ERR, 0, par);
aty_st_le32(DST_BRES_INC, 0, par);
aty_st_le32(DST_BRES_DEC, 0, par);
/* set destination drawing attributes */
aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
DST_X_LEFT_TO_RIGHT, par);
/* set source pitch to modal pitch, set offset to zero */
aty_st_le32(SRC_OFF_PITCH, (pitch_value / 8) << 22, par);
/* set these registers to a known state */
aty_st_le32(SRC_Y_X, 0, par);
aty_st_le32(SRC_HEIGHT1_WIDTH1, 1, par);
aty_st_le32(SRC_Y_X_START, 0, par);
aty_st_le32(SRC_HEIGHT2_WIDTH2, 1, par);
/* set source pixel retrieving attributes */
aty_st_le32(SRC_CNTL, SRC_LINE_X_LEFT_TO_RIGHT, par);
/* set host attributes */
wait_for_fifo(13, par);
aty_st_le32(HOST_CNTL, 0, par);
/* set pattern attributes */
aty_st_le32(PAT_REG0, 0, par);
aty_st_le32(PAT_REG1, 0, par);
aty_st_le32(PAT_CNTL, 0, par);
/* set scissors to modal size */
aty_st_le32(SC_LEFT, 0, par);
aty_st_le32(SC_TOP, 0, par);
aty_st_le32(SC_BOTTOM, par->crtc.vyres - 1, par);
aty_st_le32(SC_RIGHT, vxres - 1, par);
/* set background color to minimum value (usually BLACK) */
aty_st_le32(DP_BKGD_CLR, 0, par);
/* set foreground color to maximum value (usually WHITE) */
aty_st_le32(DP_FRGD_CLR, 0xFFFFFFFF, par);
/* set write mask to effect all pixel bits */
aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par);
/* set foreground mix to overpaint and background mix to */
/* no-effect */
aty_st_le32(DP_MIX, FRGD_MIX_S | BKGD_MIX_D, par);
/* set primary source pixel channel to foreground color */
/* register */
aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR, par);
/* set compare functionality to false (no-effect on */
/* destination) */
wait_for_fifo(3, par);
aty_st_le32(CLR_CMP_CLR, 0, par);
aty_st_le32(CLR_CMP_MASK, 0xFFFFFFFF, par);
aty_st_le32(CLR_CMP_CNTL, 0, par);
/* set pixel depth */
wait_for_fifo(2, par);
aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, par);
aty_st_le32(DP_CHAIN_MASK, par->crtc.dp_chain_mask, par);
wait_for_fifo(5, par);
aty_st_le32(SCALE_3D_CNTL, 0, par);
aty_st_le32(Z_CNTL, 0, par);
aty_st_le32(CRTC_INT_CNTL, aty_ld_le32(CRTC_INT_CNTL, par) & ~0x20,
par);
aty_st_le32(GUI_TRAJ_CNTL, 0x100023, par);
/* insure engine is idle before leaving */
wait_for_idle(par);
}
/*
* Accelerated functions
*/
static inline void draw_rect(s16 x, s16 y, u16 width, u16 height,
struct atyfb_par *par)
{
/* perform rectangle fill */
wait_for_fifo(2, par);
aty_st_le32(DST_Y_X, (x << 16) | y, par);
aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | height, par);
par->blitter_may_be_busy = 1;
}
void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 dy = area->dy, sy = area->sy, direction = DST_LAST_PEL;
u32 sx = area->sx, dx = area->dx, width = area->width, rotation = 0;
if (par->asleep)
return;
if (!area->width || !area->height)
return;
if (!par->accel_flags) {
cfb_copyarea(info, area);
return;
}
if (info->var.bits_per_pixel == 24) {
/* In 24 bpp, the engine is in 8 bpp - this requires that all */
/* horizontal coordinates and widths must be adjusted */
sx *= 3;
dx *= 3;
width *= 3;
}
if (area->sy < area->dy) {
dy += area->height - 1;
sy += area->height - 1;
} else
direction |= DST_Y_TOP_TO_BOTTOM;
if (sx < dx) {
dx += width - 1;
sx += width - 1;
} else
direction |= DST_X_LEFT_TO_RIGHT;
if (info->var.bits_per_pixel == 24) {
rotation = rotation24bpp(dx, direction);
}
wait_for_fifo(4, par);
aty_st_le32(DP_SRC, FRGD_SRC_BLIT, par);
aty_st_le32(SRC_Y_X, (sx << 16) | sy, par);
aty_st_le32(SRC_HEIGHT1_WIDTH1, (width << 16) | area->height, par);
aty_st_le32(DST_CNTL, direction | rotation, par);
draw_rect(dx, dy, width, area->height, par);
}
void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 color = rect->color, dx = rect->dx, width = rect->width, rotation = 0;
if (par->asleep)
return;
if (!rect->width || !rect->height)
return;
if (!par->accel_flags) {
cfb_fillrect(info, rect);
return;
}
color |= (rect->color << 8);
color |= (rect->color << 16);
if (info->var.bits_per_pixel == 24) {
/* In 24 bpp, the engine is in 8 bpp - this requires that all */
/* horizontal coordinates and widths must be adjusted */
dx *= 3;
width *= 3;
rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT);
}
wait_for_fifo(3, par);
aty_st_le32(DP_FRGD_CLR, color, par);
aty_st_le32(DP_SRC,
BKGD_SRC_BKGD_CLR | FRGD_SRC_FRGD_CLR | MONO_SRC_ONE,
par);
aty_st_le32(DST_CNTL,
DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
DST_X_LEFT_TO_RIGHT | rotation, par);
draw_rect(dx, rect->dy, width, rect->height, par);
}
void atyfb_imageblit(struct fb_info *info, const struct fb_image *image)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 src_bytes, dx = image->dx, dy = image->dy, width = image->width;
u32 pix_width_save, pix_width, host_cntl, rotation = 0, src, mix;
if (par->asleep)
return;
if (!image->width || !image->height)
return;
if (!par->accel_flags ||
(image->depth != 1 && info->var.bits_per_pixel != image->depth)) {
cfb_imageblit(info, image);
return;
}
pix_width = pix_width_save = aty_ld_le32(DP_PIX_WIDTH, par);
host_cntl = aty_ld_le32(HOST_CNTL, par) | HOST_BYTE_ALIGN;
switch (image->depth) {
case 1:
pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK);
pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_1BPP);
break;
case 4:
pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK);
pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_4BPP);
break;
case 8:
pix_width &= ~HOST_MASK;
pix_width |= HOST_8BPP;
break;
case 15:
pix_width &= ~HOST_MASK;
pix_width |= HOST_15BPP;
break;
case 16:
pix_width &= ~HOST_MASK;
pix_width |= HOST_16BPP;
break;
case 24:
pix_width &= ~HOST_MASK;
pix_width |= HOST_24BPP;
break;
case 32:
pix_width &= ~HOST_MASK;
pix_width |= HOST_32BPP;
break;
}
if (info->var.bits_per_pixel == 24) {
/* In 24 bpp, the engine is in 8 bpp - this requires that all */
/* horizontal coordinates and widths must be adjusted */
dx *= 3;
width *= 3;
rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT);
pix_width &= ~DST_MASK;
pix_width |= DST_8BPP;
/*
* since Rage 3D IIc we have DP_HOST_TRIPLE_EN bit
* this hwaccelerated triple has an issue with not aligned data
*/
if (M64_HAS(HW_TRIPLE) && image->width % 8 == 0)
pix_width |= DP_HOST_TRIPLE_EN;
}
if (image->depth == 1) {
u32 fg, bg;
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fg = ((u32*)(info->pseudo_palette))[image->fg_color];
bg = ((u32*)(info->pseudo_palette))[image->bg_color];
} else {
fg = image->fg_color;
bg = image->bg_color;
}
wait_for_fifo(2, par);
aty_st_le32(DP_BKGD_CLR, bg, par);
aty_st_le32(DP_FRGD_CLR, fg, par);
src = MONO_SRC_HOST | FRGD_SRC_FRGD_CLR | BKGD_SRC_BKGD_CLR;
mix = FRGD_MIX_S | BKGD_MIX_S;
} else {
src = MONO_SRC_ONE | FRGD_SRC_HOST;
mix = FRGD_MIX_D_XOR_S | BKGD_MIX_D;
}
wait_for_fifo(6, par);
aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par);
aty_st_le32(DP_PIX_WIDTH, pix_width, par);
aty_st_le32(DP_MIX, mix, par);
aty_st_le32(DP_SRC, src, par);
aty_st_le32(HOST_CNTL, host_cntl, par);
aty_st_le32(DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT | rotation, par);
draw_rect(dx, dy, width, image->height, par);
src_bytes = (((image->width * image->depth) + 7) / 8) * image->height;
/* manual triple each pixel */
if (info->var.bits_per_pixel == 24 && !(pix_width & DP_HOST_TRIPLE_EN)) {
int inbit, outbit, mult24, byte_id_in_dword, width;
u8 *pbitmapin = (u8*)image->data, *pbitmapout;
u32 hostdword;
for (width = image->width, inbit = 7, mult24 = 0; src_bytes; ) {
for (hostdword = 0, pbitmapout = (u8*)&hostdword, byte_id_in_dword = 0;
byte_id_in_dword < 4 && src_bytes;
byte_id_in_dword++, pbitmapout++) {
for (outbit = 7; outbit >= 0; outbit--) {
*pbitmapout |= (((*pbitmapin >> inbit) & 1) << outbit);
mult24++;
/* next bit */
if (mult24 == 3) {
mult24 = 0;
inbit--;
width--;
}
/* next byte */
if (inbit < 0 || width == 0) {
src_bytes--;
pbitmapin++;
inbit = 7;
if (width == 0) {
width = image->width;
outbit = 0;
}
}
}
}
wait_for_fifo(1, par);
aty_st_le32(HOST_DATA0, hostdword, par);
}
} else {
u32 *pbitmap, dwords = (src_bytes + 3) / 4;
for (pbitmap = (u32*)(image->data); dwords; dwords--, pbitmap++) {
wait_for_fifo(1, par);
aty_st_le32(HOST_DATA0, le32_to_cpup(pbitmap), par);
}
}
/* restore pix_width */
wait_for_fifo(1, par);
aty_st_le32(DP_PIX_WIDTH, pix_width_save, par);
}

View File

@@ -0,0 +1,651 @@
/*
* ATI Mach64 CT/VT/GT/LT Support
*/
#include <linux/fb.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <video/mach64.h>
#include "atyfb.h"
#ifdef CONFIG_PPC
#include <asm/machdep.h>
#endif
#undef DEBUG
static int aty_valid_pll_ct (const struct fb_info *info, u32 vclk_per, struct pll_ct *pll);
static int aty_dsp_gt (const struct fb_info *info, u32 bpp, struct pll_ct *pll);
static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll);
static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll);
u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par)
{
u8 res;
/* write addr byte */
aty_st_8(CLOCK_CNTL_ADDR, (offset << 2) & PLL_ADDR, par);
/* read the register value */
res = aty_ld_8(CLOCK_CNTL_DATA, par);
return res;
}
static void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par)
{
/* write addr byte */
aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) | PLL_WR_EN, par);
/* write the register value */
aty_st_8(CLOCK_CNTL_DATA, val & PLL_DATA, par);
aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) & ~PLL_WR_EN, par);
}
/*
* by Daniel Mantione
* <daniel.mantione@freepascal.org>
*
*
* ATI Mach64 CT clock synthesis description.
*
* All clocks on the Mach64 can be calculated using the same principle:
*
* XTALIN * x * FB_DIV
* CLK = ----------------------
* PLL_REF_DIV * POST_DIV
*
* XTALIN is a fixed speed clock. Common speeds are 14.31 MHz and 29.50 MHz.
* PLL_REF_DIV can be set by the user, but is the same for all clocks.
* FB_DIV can be set by the user for each clock individually, it should be set
* between 128 and 255, the chip will generate a bad clock signal for too low
* values.
* x depends on the type of clock; usually it is 2, but for the MCLK it can also
* be set to 4.
* POST_DIV can be set by the user for each clock individually, Possible values
* are 1,2,4,8 and for some clocks other values are available too.
* CLK is of course the clock speed that is generated.
*
* The Mach64 has these clocks:
*
* MCLK The clock rate of the chip
* XCLK The clock rate of the on-chip memory
* VCLK0 First pixel clock of first CRT controller
* VCLK1 Second pixel clock of first CRT controller
* VCLK2 Third pixel clock of first CRT controller
* VCLK3 Fourth pixel clock of first CRT controller
* VCLK Selected pixel clock, one of VCLK0, VCLK1, VCLK2, VCLK3
* V2CLK Pixel clock of the second CRT controller.
* SCLK Multi-purpose clock
*
* - MCLK and XCLK use the same FB_DIV
* - VCLK0 .. VCLK3 use the same FB_DIV
* - V2CLK is needed when the second CRTC is used (can be used for dualhead);
* i.e. CRT monitor connected to laptop has different resolution than built
* in LCD monitor.
* - SCLK is not available on all cards; it is know to exist on the Rage LT-PRO,
* Rage XL and Rage Mobility. It is know not to exist on the Mach64 VT.
* - V2CLK is not available on all cards, most likely only the Rage LT-PRO,
* the Rage XL and the Rage Mobility
*
* SCLK can be used to:
* - Clock the chip instead of MCLK
* - Replace XTALIN with a user defined frequency
* - Generate the pixel clock for the LCD monitor (instead of VCLK)
*/
/*
* It can be quite hard to calculate XCLK and MCLK if they don't run at the
* same frequency. Luckily, until now all cards that need asynchrone clock
* speeds seem to have SCLK.
* So this driver uses SCLK to clock the chip and XCLK to clock the memory.
*/
/* ------------------------------------------------------------------------- */
/*
* PLL programming (Mach64 CT family)
*
*
* This procedure sets the display fifo. The display fifo is a buffer that
* contains data read from the video memory that waits to be processed by
* the CRT controller.
*
* On the more modern Mach64 variants, the chip doesn't calculate the
* interval after which the display fifo has to be reloaded from memory
* automatically, the driver has to do it instead.
*/
#define Maximum_DSP_PRECISION 7
static u8 postdividers[] = {1,2,4,8,3};
static int aty_dsp_gt(const struct fb_info *info, u32 bpp, struct pll_ct *pll)
{
u32 dsp_off, dsp_on, dsp_xclks;
u32 multiplier, divider, ras_multiplier, ras_divider, tmp;
u8 vshift, xshift;
s8 dsp_precision;
multiplier = ((u32)pll->mclk_fb_div) * pll->vclk_post_div_real;
divider = ((u32)pll->vclk_fb_div) * pll->xclk_ref_div;
ras_multiplier = pll->xclkmaxrasdelay;
ras_divider = 1;
if (bpp>=8)
divider = divider * (bpp >> 2);
vshift = (6 - 2) - pll->xclk_post_div; /* FIFO is 64 bits wide in accelerator mode ... */
if (bpp == 0)
vshift--; /* ... but only 32 bits in VGA mode. */
#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (pll->xres != 0) {
struct atyfb_par *par = (struct atyfb_par *) info->par;
multiplier = multiplier * par->lcd_width;
divider = divider * pll->xres & ~7;
ras_multiplier = ras_multiplier * par->lcd_width;
ras_divider = ras_divider * pll->xres & ~7;
}
#endif
/* If we don't do this, 32 bits for multiplier & divider won't be
enough in certain situations! */
while (((multiplier | divider) & 1) == 0) {
multiplier = multiplier >> 1;
divider = divider >> 1;
}
/* Determine DSP precision first */
tmp = ((multiplier * pll->fifo_size) << vshift) / divider;
for (dsp_precision = -5; tmp; dsp_precision++)
tmp >>= 1;
if (dsp_precision < 0)
dsp_precision = 0;
else if (dsp_precision > Maximum_DSP_PRECISION)
dsp_precision = Maximum_DSP_PRECISION;
xshift = 6 - dsp_precision;
vshift += xshift;
/* Move on to dsp_off */
dsp_off = ((multiplier * (pll->fifo_size - 1)) << vshift) / divider -
(1 << (vshift - xshift));
/* if (bpp == 0)
dsp_on = ((multiplier * 20 << vshift) + divider) / divider;
else */
{
dsp_on = ((multiplier << vshift) + divider) / divider;
tmp = ((ras_multiplier << xshift) + ras_divider) / ras_divider;
if (dsp_on < tmp)
dsp_on = tmp;
dsp_on = dsp_on + (tmp * 2) + (pll->xclkpagefaultdelay << xshift);
}
/* Calculate rounding factor and apply it to dsp_on */
tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1;
dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1);
if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) {
dsp_on = dsp_off - (multiplier << vshift) / divider;
dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1);
}
/* Last but not least: dsp_xclks */
dsp_xclks = ((multiplier << (vshift + 5)) + divider) / divider;
/* Get register values. */
pll->dsp_on_off = (dsp_on << 16) + dsp_off;
pll->dsp_config = (dsp_precision << 20) | (pll->dsp_loop_latency << 16) | dsp_xclks;
#ifdef DEBUG
printk("atyfb(%s): dsp_config 0x%08x, dsp_on_off 0x%08x\n",
__func__, pll->dsp_config, pll->dsp_on_off);
#endif
return 0;
}
static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per, struct pll_ct *pll)
{
u32 q;
struct atyfb_par *par = (struct atyfb_par *) info->par;
int pllvclk;
/* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
q = par->ref_clk_per * pll->pll_ref_div * 4 / vclk_per;
if (q < 16*8 || q > 255*8) {
printk(KERN_CRIT "atyfb: vclk out of range\n");
return -EINVAL;
} else {
pll->vclk_post_div = (q < 128*8);
pll->vclk_post_div += (q < 64*8);
pll->vclk_post_div += (q < 32*8);
}
pll->vclk_post_div_real = postdividers[pll->vclk_post_div];
// pll->vclk_post_div <<= 6;
pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;
pllvclk = (1000000 * 2 * pll->vclk_fb_div) /
(par->ref_clk_per * pll->pll_ref_div);
#ifdef DEBUG
printk("atyfb(%s): pllvclk=%d MHz, vclk=%d MHz\n",
__func__, pllvclk, pllvclk / pll->vclk_post_div_real);
#endif
pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
/* Set ECP (scaler/overlay clock) divider */
if (par->pll_limits.ecp_max) {
int ecp = pllvclk / pll->vclk_post_div_real;
int ecp_div = 0;
while (ecp > par->pll_limits.ecp_max && ecp_div < 2) {
ecp >>= 1;
ecp_div++;
}
pll->pll_vclk_cntl |= ecp_div << 4;
}
return 0;
}
static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
int err;
if ((err = aty_valid_pll_ct(info, vclk_per, &pll->ct)))
return err;
if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct)))
return err;
/*aty_calc_pll_ct(info, &pll->ct);*/
return 0;
}
static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 ret;
ret = par->ref_clk_per * pll->ct.pll_ref_div * pll->ct.vclk_post_div_real / pll->ct.vclk_fb_div / 2;
#ifdef CONFIG_FB_ATY_GENERIC_LCD
if(pll->ct.xres > 0) {
ret *= par->lcd_width;
ret /= pll->ct.xres;
}
#endif
#ifdef DEBUG
printk("atyfb(%s): calculated 0x%08X(%i)\n", __func__, ret, ret);
#endif
return ret;
}
void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 crtc_gen_cntl, lcd_gen_cntrl;
u8 tmp, tmp2;
lcd_gen_cntrl = 0;
#ifdef DEBUG
printk("atyfb(%s): about to program:\n"
"pll_ext_cntl=0x%02x pll_gen_cntl=0x%02x pll_vclk_cntl=0x%02x\n",
__func__,
pll->ct.pll_ext_cntl, pll->ct.pll_gen_cntl, pll->ct.pll_vclk_cntl);
printk("atyfb(%s): setting clock %lu for FeedBackDivider %i, ReferenceDivider %i, PostDivider %i(%i)\n",
__func__,
par->clk_wr_offset, pll->ct.vclk_fb_div,
pll->ct.pll_ref_div, pll->ct.vclk_post_div, pll->ct.vclk_post_div_real);
#endif
#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (par->lcd_table != 0) {
/* turn off LCD */
lcd_gen_cntrl = aty_ld_lcd(LCD_GEN_CNTL, par);
aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl & ~LCD_ON, par);
}
#endif
aty_st_8(CLOCK_CNTL, par->clk_wr_offset | CLOCK_STROBE, par);
/* Temporarily switch to accelerator mode */
crtc_gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN, par);
/* Reset VCLK generator */
aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par);
/* Set post-divider */
tmp2 = par->clk_wr_offset << 1;
tmp = aty_ld_pll_ct(VCLK_POST_DIV, par);
tmp &= ~(0x03U << tmp2);
tmp |= ((pll->ct.vclk_post_div & 0x03U) << tmp2);
aty_st_pll_ct(VCLK_POST_DIV, tmp, par);
/* Set extended post-divider */
tmp = aty_ld_pll_ct(PLL_EXT_CNTL, par);
tmp &= ~(0x10U << par->clk_wr_offset);
tmp &= 0xF0U;
tmp |= pll->ct.pll_ext_cntl;
aty_st_pll_ct(PLL_EXT_CNTL, tmp, par);
/* Set feedback divider */
tmp = VCLK0_FB_DIV + par->clk_wr_offset;
aty_st_pll_ct(tmp, (pll->ct.vclk_fb_div & 0xFFU), par);
aty_st_pll_ct(PLL_GEN_CNTL, (pll->ct.pll_gen_cntl & (~(PLL_OVERRIDE | PLL_MCLK_RST))) | OSC_EN, par);
/* End VCLK generator reset */
aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl & ~(PLL_VCLK_RST), par);
mdelay(5);
aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par);
mdelay(1);
/* Restore mode register */
if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl, par);
if (M64_HAS(GTB_DSP)) {
u8 dll_cntl;
if (M64_HAS(XL_DLL))
dll_cntl = 0x80;
else if (par->ram_type >= SDRAM)
dll_cntl = 0xa6;
else
dll_cntl = 0xa0;
aty_st_pll_ct(DLL_CNTL, dll_cntl, par);
aty_st_pll_ct(VFC_CNTL, 0x1b, par);
aty_st_le32(DSP_CONFIG, pll->ct.dsp_config, par);
aty_st_le32(DSP_ON_OFF, pll->ct.dsp_on_off, par);
mdelay(10);
aty_st_pll_ct(DLL_CNTL, dll_cntl, par);
mdelay(10);
aty_st_pll_ct(DLL_CNTL, dll_cntl | 0x40, par);
mdelay(10);
aty_st_pll_ct(DLL_CNTL, dll_cntl & ~0x40, par);
}
#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (par->lcd_table != 0) {
/* restore LCD */
aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl, par);
}
#endif
}
static void __devinit aty_get_pll_ct(const struct fb_info *info,
union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u8 tmp, clock;
clock = aty_ld_8(CLOCK_CNTL, par) & 0x03U;
tmp = clock << 1;
pll->ct.vclk_post_div = (aty_ld_pll_ct(VCLK_POST_DIV, par) >> tmp) & 0x03U;
pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par) & 0x0FU;
pll->ct.vclk_fb_div = aty_ld_pll_ct(VCLK0_FB_DIV + clock, par) & 0xFFU;
pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
pll->ct.mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par);
pll->ct.pll_gen_cntl = aty_ld_pll_ct(PLL_GEN_CNTL, par);
pll->ct.pll_vclk_cntl = aty_ld_pll_ct(PLL_VCLK_CNTL, par);
if (M64_HAS(GTB_DSP)) {
pll->ct.dsp_config = aty_ld_le32(DSP_CONFIG, par);
pll->ct.dsp_on_off = aty_ld_le32(DSP_ON_OFF, par);
}
}
static int __devinit aty_init_pll_ct(const struct fb_info *info,
union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u8 mpost_div, xpost_div, sclk_post_div_real;
u32 q, memcntl, trp;
u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off;
#ifdef DEBUG
int pllmclk, pllsclk;
#endif
pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par);
pll->ct.xclk_post_div = pll->ct.pll_ext_cntl & 0x07;
pll->ct.xclk_ref_div = 1;
switch (pll->ct.xclk_post_div) {
case 0: case 1: case 2: case 3:
break;
case 4:
pll->ct.xclk_ref_div = 3;
pll->ct.xclk_post_div = 0;
break;
default:
printk(KERN_CRIT "atyfb: Unsupported xclk source: %d.\n", pll->ct.xclk_post_div);
return -EINVAL;
}
pll->ct.mclk_fb_mult = 2;
if(pll->ct.pll_ext_cntl & PLL_MFB_TIMES_4_2B) {
pll->ct.mclk_fb_mult = 4;
pll->ct.xclk_post_div -= 1;
}
#ifdef DEBUG
printk("atyfb(%s): mclk_fb_mult=%d, xclk_post_div=%d\n",
__func__, pll->ct.mclk_fb_mult, pll->ct.xclk_post_div);
#endif
memcntl = aty_ld_le32(MEM_CNTL, par);
trp = (memcntl & 0x300) >> 8;
pll->ct.xclkpagefaultdelay = ((memcntl & 0xc00) >> 10) + ((memcntl & 0x1000) >> 12) + trp + 2;
pll->ct.xclkmaxrasdelay = ((memcntl & 0x70000) >> 16) + trp + 2;
if (M64_HAS(FIFO_32)) {
pll->ct.fifo_size = 32;
} else {
pll->ct.fifo_size = 24;
pll->ct.xclkpagefaultdelay += 2;
pll->ct.xclkmaxrasdelay += 3;
}
switch (par->ram_type) {
case DRAM:
if (info->fix.smem_len<=ONE_MB) {
pll->ct.dsp_loop_latency = 10;
} else {
pll->ct.dsp_loop_latency = 8;
pll->ct.xclkpagefaultdelay += 2;
}
break;
case EDO:
case PSEUDO_EDO:
if (info->fix.smem_len<=ONE_MB) {
pll->ct.dsp_loop_latency = 9;
} else {
pll->ct.dsp_loop_latency = 8;
pll->ct.xclkpagefaultdelay += 1;
}
break;
case SDRAM:
if (info->fix.smem_len<=ONE_MB) {
pll->ct.dsp_loop_latency = 11;
} else {
pll->ct.dsp_loop_latency = 10;
pll->ct.xclkpagefaultdelay += 1;
}
break;
case SGRAM:
pll->ct.dsp_loop_latency = 8;
pll->ct.xclkpagefaultdelay += 3;
break;
default:
pll->ct.dsp_loop_latency = 11;
pll->ct.xclkpagefaultdelay += 3;
break;
}
if (pll->ct.xclkmaxrasdelay <= pll->ct.xclkpagefaultdelay)
pll->ct.xclkmaxrasdelay = pll->ct.xclkpagefaultdelay + 1;
/* Allow BIOS to override */
dsp_config = aty_ld_le32(DSP_CONFIG, par);
dsp_on_off = aty_ld_le32(DSP_ON_OFF, par);
vga_dsp_config = aty_ld_le32(VGA_DSP_CONFIG, par);
vga_dsp_on_off = aty_ld_le32(VGA_DSP_ON_OFF, par);
if (dsp_config)
pll->ct.dsp_loop_latency = (dsp_config & DSP_LOOP_LATENCY) >> 16;
#if 0
FIXME: is it relevant for us?
if ((!dsp_on_off && !M64_HAS(RESET_3D)) ||
((dsp_on_off == vga_dsp_on_off) &&
(!dsp_config || !((dsp_config ^ vga_dsp_config) & DSP_XCLKS_PER_QW)))) {
vga_dsp_on_off &= VGA_DSP_OFF;
vga_dsp_config &= VGA_DSP_XCLKS_PER_QW;
if (ATIDivide(vga_dsp_on_off, vga_dsp_config, 5, 1) > 24)
pll->ct.fifo_size = 32;
else
pll->ct.fifo_size = 24;
}
#endif
/* Exit if the user does not want us to tamper with the clock
rates of her chip. */
if (par->mclk_per == 0) {
u8 mclk_fb_div, pll_ext_cntl;
pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par);
pll->ct.xclk_post_div_real = postdividers[pll_ext_cntl & 0x07];
mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par);
if (pll_ext_cntl & PLL_MFB_TIMES_4_2B)
mclk_fb_div <<= 1;
pll->ct.mclk_fb_div = mclk_fb_div;
return 0;
}
pll->ct.pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per;
/* FIXME: use the VTB/GTB /3 post divider if it's better suited */
q = par->ref_clk_per * pll->ct.pll_ref_div * 8 /
(pll->ct.mclk_fb_mult * par->xclk_per);
if (q < 16*8 || q > 255*8) {
printk(KERN_CRIT "atxfb: xclk out of range\n");
return -EINVAL;
} else {
xpost_div = (q < 128*8);
xpost_div += (q < 64*8);
xpost_div += (q < 32*8);
}
pll->ct.xclk_post_div_real = postdividers[xpost_div];
pll->ct.mclk_fb_div = q * pll->ct.xclk_post_div_real / 8;
#ifdef CONFIG_PPC
if (machine_is(powermac)) {
/* Override PLL_EXT_CNTL & 0x07. */
pll->ct.xclk_post_div = xpost_div;
pll->ct.xclk_ref_div = 1;
}
#endif
#ifdef DEBUG
pllmclk = (1000000 * pll->ct.mclk_fb_mult * pll->ct.mclk_fb_div) /
(par->ref_clk_per * pll->ct.pll_ref_div);
printk("atyfb(%s): pllmclk=%d MHz, xclk=%d MHz\n",
__func__, pllmclk, pllmclk / pll->ct.xclk_post_div_real);
#endif
if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM))
pll->ct.pll_gen_cntl = OSC_EN;
else
pll->ct.pll_gen_cntl = OSC_EN | DLL_PWDN /* | FORCE_DCLK_TRI_STATE */;
if (M64_HAS(MAGIC_POSTDIV))
pll->ct.pll_ext_cntl = 0;
else
pll->ct.pll_ext_cntl = xpost_div;
if (pll->ct.mclk_fb_mult == 4)
pll->ct.pll_ext_cntl |= PLL_MFB_TIMES_4_2B;
if (par->mclk_per == par->xclk_per) {
pll->ct.pll_gen_cntl |= (xpost_div << 4); /* mclk == xclk */
} else {
/*
* The chip clock is not equal to the memory clock.
* Therefore we will use sclk to clock the chip.
*/
pll->ct.pll_gen_cntl |= (6 << 4); /* mclk == sclk */
q = par->ref_clk_per * pll->ct.pll_ref_div * 4 / par->mclk_per;
if (q < 16*8 || q > 255*8) {
printk(KERN_CRIT "atyfb: mclk out of range\n");
return -EINVAL;
} else {
mpost_div = (q < 128*8);
mpost_div += (q < 64*8);
mpost_div += (q < 32*8);
}
sclk_post_div_real = postdividers[mpost_div];
pll->ct.sclk_fb_div = q * sclk_post_div_real / 8;
pll->ct.spll_cntl2 = mpost_div << 4;
#ifdef DEBUG
pllsclk = (1000000 * 2 * pll->ct.sclk_fb_div) /
(par->ref_clk_per * pll->ct.pll_ref_div);
printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n",
__func__, pllsclk, pllsclk / sclk_post_div_real);
#endif
}
/* Disable the extra precision pixel clock controls since we do not use them. */
pll->ct.ext_vpll_cntl = aty_ld_pll_ct(EXT_VPLL_CNTL, par);
pll->ct.ext_vpll_cntl &= ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC);
return 0;
}
static void aty_resume_pll_ct(const struct fb_info *info,
union aty_pll *pll)
{
struct atyfb_par *par = info->par;
if (par->mclk_per != par->xclk_per) {
/*
* This disables the sclk, crashes the computer as reported:
* aty_st_pll_ct(SPLL_CNTL2, 3, info);
*
* So it seems the sclk must be enabled before it is used;
* so PLL_GEN_CNTL must be programmed *after* the sclk.
*/
aty_st_pll_ct(SCLK_FB_DIV, pll->ct.sclk_fb_div, par);
aty_st_pll_ct(SPLL_CNTL2, pll->ct.spll_cntl2, par);
/*
* SCLK has been started. Wait for the PLL to lock. 5 ms
* should be enough according to mach64 programmer's guide.
*/
mdelay(5);
}
aty_st_pll_ct(PLL_REF_DIV, pll->ct.pll_ref_div, par);
aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par);
aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par);
aty_st_pll_ct(EXT_VPLL_CNTL, pll->ct.ext_vpll_cntl, par);
}
static int dummy(void)
{
return 0;
}
const struct aty_dac_ops aty_dac_ct = {
.set_dac = (void *) dummy,
};
const struct aty_pll_ops aty_pll_ct = {
.var_to_pll = aty_var_to_pll_ct,
.pll_to_var = aty_pll_to_var_ct,
.set_pll = aty_set_pll_ct,
.get_pll = aty_get_pll_ct,
.init_pll = aty_init_pll_ct,
.resume_pll = aty_resume_pll_ct,
};

View File

@@ -0,0 +1,216 @@
/*
* ATI Mach64 CT/VT/GT/LT Cursor Support
*/
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
#include <asm/io.h>
#ifdef __sparc__
#include <asm/fbio.h>
#endif
#include <video/mach64.h>
#include "atyfb.h"
/*
* The hardware cursor definition requires 2 bits per pixel. The
* Cursor size reguardless of the visible cursor size is 64 pixels
* by 64 lines. The total memory required to define the cursor is
* 16 bytes / line for 64 lines or 1024 bytes of data. The data
* must be in a contigiuos format. The 2 bit cursor code values are
* as follows:
*
* 00 - pixel colour = CURSOR_CLR_0
* 01 - pixel colour = CURSOR_CLR_1
* 10 - pixel colour = transparent (current display pixel)
* 11 - pixel colour = 1's complement of current display pixel
*
* Cursor Offset 64 pixels Actual Displayed Area
* \_________________________/
* | | | |
* |<--------------->| | |
* | CURS_HORZ_OFFSET| | |
* | |_______| | 64 Lines
* | ^ | |
* | | | |
* | CURS_VERT_OFFSET| |
* | | | |
* |____________________|____| |
*
*
* The Screen position of the top left corner of the displayed
* cursor is specificed by CURS_HORZ_VERT_POSN. Care must be taken
* when the cursor hot spot is not the top left corner and the
* physical cursor position becomes negative. It will be be displayed
* if either the horizontal or vertical cursor position is negative
*
* If x becomes negative the cursor manager must adjust the CURS_HORZ_OFFSET
* to a larger number and saturate CUR_HORZ_POSN to zero.
*
* if Y becomes negative, CUR_VERT_OFFSET must be adjusted to a larger number,
* CUR_OFFSET must be adjusted to a point to the appropraite line in the cursor
* definitation and CUR_VERT_POSN must be saturated to zero.
*/
/*
* Hardware Cursor support.
*/
static const u8 cursor_bits_lookup[16] = {
0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
};
static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u16 xoff, yoff;
int x, y, h;
#ifdef __sparc__
if (par->mmaped)
return -EPERM;
#endif
if (par->asleep)
return -EPERM;
wait_for_fifo(1, par);
if (cursor->enable)
aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par)
| HWCURSOR_ENABLE, par);
else
aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par)
& ~HWCURSOR_ENABLE, par);
/* set position */
if (cursor->set & FB_CUR_SETPOS) {
x = cursor->image.dx - cursor->hot.x - info->var.xoffset;
if (x < 0) {
xoff = -x;
x = 0;
} else {
xoff = 0;
}
y = cursor->image.dy - cursor->hot.y - info->var.yoffset;
if (y < 0) {
yoff = -y;
y = 0;
} else {
yoff = 0;
}
h = cursor->image.height;
/*
* In doublescan mode, the cursor location
* and heigh also needs to be doubled.
*/
if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) {
y<<=1;
h<<=1;
}
wait_for_fifo(3, par);
aty_st_le32(CUR_OFFSET, (info->fix.smem_len >> 3) + (yoff << 1), par);
aty_st_le32(CUR_HORZ_VERT_OFF,
((u32) (64 - h + yoff) << 16) | xoff, par);
aty_st_le32(CUR_HORZ_VERT_POSN, ((u32) y << 16) | x, par);
}
/* Set color map */
if (cursor->set & FB_CUR_SETCMAP) {
u32 fg_idx, bg_idx, fg, bg;
fg_idx = cursor->image.fg_color;
bg_idx = cursor->image.bg_color;
fg = ((info->cmap.red[fg_idx] & 0xff) << 24) |
((info->cmap.green[fg_idx] & 0xff) << 16) |
((info->cmap.blue[fg_idx] & 0xff) << 8) | 0xff;
bg = ((info->cmap.red[bg_idx] & 0xff) << 24) |
((info->cmap.green[bg_idx] & 0xff) << 16) |
((info->cmap.blue[bg_idx] & 0xff) << 8);
wait_for_fifo(2, par);
aty_st_le32(CUR_CLR0, bg, par);
aty_st_le32(CUR_CLR1, fg, par);
}
if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
u8 *src = (u8 *)cursor->image.data;
u8 *msk = (u8 *)cursor->mask;
u8 __iomem *dst = (u8 __iomem *)info->sprite.addr;
unsigned int width = (cursor->image.width + 7) >> 3;
unsigned int height = cursor->image.height;
unsigned int align = info->sprite.scan_align;
unsigned int i, j, offset;
u8 m, b;
// Clear cursor image with 1010101010...
fb_memset(dst, 0xaa, 1024);
offset = align - width*2;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
b = *src++;
m = *msk++;
switch (cursor->rop) {
case ROP_XOR:
// Upper 4 bits of mask data
fb_writeb(cursor_bits_lookup[(b ^ m) >> 4], dst++);
// Lower 4 bits of mask
fb_writeb(cursor_bits_lookup[(b ^ m) & 0x0f],
dst++);
break;
case ROP_COPY:
// Upper 4 bits of mask data
fb_writeb(cursor_bits_lookup[(b & m) >> 4], dst++);
// Lower 4 bits of mask
fb_writeb(cursor_bits_lookup[(b & m) & 0x0f],
dst++);
break;
}
}
dst += offset;
}
}
return 0;
}
int __devinit aty_init_cursor(struct fb_info *info)
{
unsigned long addr;
info->fix.smem_len -= PAGE_SIZE;
#ifdef __sparc__
addr = (unsigned long) info->screen_base - 0x800000 + info->fix.smem_len;
info->sprite.addr = (u8 *) addr;
#else
#ifdef __BIG_ENDIAN
addr = info->fix.smem_start - 0x800000 + info->fix.smem_len;
info->sprite.addr = (u8 *) ioremap(addr, 1024);
#else
addr = (unsigned long) info->screen_base + info->fix.smem_len;
info->sprite.addr = (u8 *) addr;
#endif
#endif
if (!info->sprite.addr)
return -ENXIO;
info->sprite.size = PAGE_SIZE;
info->sprite.scan_align = 16; /* Scratch pad 64 bytes wide */
info->sprite.buf_align = 16; /* and 64 lines tall. */
info->sprite.flags = FB_PIXMAP_IO;
info->fbops->fb_cursor = atyfb_cursor;
return 0;
}

View File

@@ -0,0 +1,910 @@
/*
* ATI Mach64 GX Support
*/
#include <linux/delay.h>
#include <linux/fb.h>
#include <asm/io.h>
#include <video/mach64.h>
#include "atyfb.h"
/* Definitions for the ICS 2595 == ATI 18818_1 Clockchip */
#define REF_FREQ_2595 1432 /* 14.33 MHz (exact 14.31818) */
#define REF_DIV_2595 46 /* really 43 on ICS 2595 !!! */
/* ohne Prescaler */
#define MAX_FREQ_2595 15938 /* 159.38 MHz (really 170.486) */
#define MIN_FREQ_2595 8000 /* 80.00 MHz ( 85.565) */
/* mit Prescaler 2, 4, 8 */
#define ABS_MIN_FREQ_2595 1000 /* 10.00 MHz (really 10.697) */
#define N_ADJ_2595 257
#define STOP_BITS_2595 0x1800
#define MIN_N_408 2
#define MIN_N_1703 6
#define MIN_M 2
#define MAX_M 30
#define MIN_N 35
#define MAX_N 255-8
/*
* Support Functions
*/
static void aty_dac_waste4(const struct atyfb_par *par)
{
(void) aty_ld_8(DAC_REGS, par);
(void) aty_ld_8(DAC_REGS + 2, par);
(void) aty_ld_8(DAC_REGS + 2, par);
(void) aty_ld_8(DAC_REGS + 2, par);
(void) aty_ld_8(DAC_REGS + 2, par);
}
static void aty_StrobeClock(const struct atyfb_par *par)
{
u8 tmp;
udelay(26);
tmp = aty_ld_8(CLOCK_CNTL, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, tmp | CLOCK_STROBE, par);
return;
}
/*
* IBM RGB514 DAC and Clock Chip
*/
static void aty_st_514(int offset, u8 val, const struct atyfb_par *par)
{
aty_st_8(DAC_CNTL, 1, par);
/* right addr byte */
aty_st_8(DAC_W_INDEX, offset & 0xff, par);
/* left addr byte */
aty_st_8(DAC_DATA, (offset >> 8) & 0xff, par);
aty_st_8(DAC_MASK, val, par);
aty_st_8(DAC_CNTL, 0, par);
}
static int aty_set_dac_514(const struct fb_info *info,
const union aty_pll *pll, u32 bpp, u32 accel)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
static struct {
u8 pixel_dly;
u8 misc2_cntl;
u8 pixel_rep;
u8 pixel_cntl_index;
u8 pixel_cntl_v1;
} tab[3] = {
{
0, 0x41, 0x03, 0x71, 0x45}, /* 8 bpp */
{
0, 0x45, 0x04, 0x0c, 0x01}, /* 555 */
{
0, 0x45, 0x06, 0x0e, 0x00}, /* XRGB */
};
int i;
switch (bpp) {
case 8:
default:
i = 0;
break;
case 16:
i = 1;
break;
case 32:
i = 2;
break;
}
aty_st_514(0x90, 0x00, par); /* VRAM Mask Low */
aty_st_514(0x04, tab[i].pixel_dly, par); /* Horizontal Sync Control */
aty_st_514(0x05, 0x00, par); /* Power Management */
aty_st_514(0x02, 0x01, par); /* Misc Clock Control */
aty_st_514(0x71, tab[i].misc2_cntl, par); /* Misc Control 2 */
aty_st_514(0x0a, tab[i].pixel_rep, par); /* Pixel Format */
aty_st_514(tab[i].pixel_cntl_index, tab[i].pixel_cntl_v1, par);
/* Misc Control 2 / 16 BPP Control / 32 BPP Control */
return 0;
}
static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per,
u32 bpp, union aty_pll *pll)
{
/*
* FIXME: use real calculations instead of using fixed values from the old
* driver
*/
static struct {
u32 limit; /* pixlock rounding limit (arbitrary) */
u8 m; /* (df<<6) | vco_div_count */
u8 n; /* ref_div_count */
} RGB514_clocks[7] = {
{
8000, (3 << 6) | 20, 9}, /* 7395 ps / 135.2273 MHz */
{
10000, (1 << 6) | 19, 3}, /* 9977 ps / 100.2273 MHz */
{
13000, (1 << 6) | 2, 3}, /* 12509 ps / 79.9432 MHz */
{
14000, (2 << 6) | 8, 7}, /* 13394 ps / 74.6591 MHz */
{
16000, (1 << 6) | 44, 6}, /* 15378 ps / 65.0284 MHz */
{
25000, (1 << 6) | 15, 5}, /* 17460 ps / 57.2727 MHz */
{
50000, (0 << 6) | 53, 7}, /* 33145 ps / 30.1705 MHz */
};
int i;
for (i = 0; i < ARRAY_SIZE(RGB514_clocks); i++)
if (vclk_per <= RGB514_clocks[i].limit) {
pll->ibm514.m = RGB514_clocks[i].m;
pll->ibm514.n = RGB514_clocks[i].n;
return 0;
}
return -EINVAL;
}
static u32 aty_pll_514_to_var(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u8 df, vco_div_count, ref_div_count;
df = pll->ibm514.m >> 6;
vco_div_count = pll->ibm514.m & 0x3f;
ref_div_count = pll->ibm514.n;
return ((par->ref_clk_per * ref_div_count) << (3 - df))/
(vco_div_count + 65);
}
static void aty_set_pll_514(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
aty_st_514(0x06, 0x02, par); /* DAC Operation */
aty_st_514(0x10, 0x01, par); /* PLL Control 1 */
aty_st_514(0x70, 0x01, par); /* Misc Control 1 */
aty_st_514(0x8f, 0x1f, par); /* PLL Ref. Divider Input */
aty_st_514(0x03, 0x00, par); /* Sync Control */
aty_st_514(0x05, 0x00, par); /* Power Management */
aty_st_514(0x20, pll->ibm514.m, par); /* F0 / M0 */
aty_st_514(0x21, pll->ibm514.n, par); /* F1 / N0 */
}
const struct aty_dac_ops aty_dac_ibm514 = {
.set_dac = aty_set_dac_514,
};
const struct aty_pll_ops aty_pll_ibm514 = {
.var_to_pll = aty_var_to_pll_514,
.pll_to_var = aty_pll_514_to_var,
.set_pll = aty_set_pll_514,
};
/*
* ATI 68860-B DAC
*/
static int aty_set_dac_ATI68860_B(const struct fb_info *info,
const union aty_pll *pll, u32 bpp,
u32 accel)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 gModeReg, devSetupRegA, temp, mask;
gModeReg = 0;
devSetupRegA = 0;
switch (bpp) {
case 8:
gModeReg = 0x83;
devSetupRegA =
0x60 | 0x00 /*(info->mach64DAC8Bit ? 0x00 : 0x01) */ ;
break;
case 15:
gModeReg = 0xA0;
devSetupRegA = 0x60;
break;
case 16:
gModeReg = 0xA1;
devSetupRegA = 0x60;
break;
case 24:
gModeReg = 0xC0;
devSetupRegA = 0x60;
break;
case 32:
gModeReg = 0xE3;
devSetupRegA = 0x60;
break;
}
if (!accel) {
gModeReg = 0x80;
devSetupRegA = 0x61;
}
temp = aty_ld_8(DAC_CNTL, par);
aty_st_8(DAC_CNTL, (temp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
par);
aty_st_8(DAC_REGS + 2, 0x1D, par);
aty_st_8(DAC_REGS + 3, gModeReg, par);
aty_st_8(DAC_REGS, 0x02, par);
temp = aty_ld_8(DAC_CNTL, par);
aty_st_8(DAC_CNTL, temp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par);
if (info->fix.smem_len < ONE_MB)
mask = 0x04;
else if (info->fix.smem_len == ONE_MB)
mask = 0x08;
else
mask = 0x0C;
/* The following assumes that the BIOS has correctly set R7 of the
* Device Setup Register A at boot time.
*/
#define A860_DELAY_L 0x80
temp = aty_ld_8(DAC_REGS, par);
aty_st_8(DAC_REGS, (devSetupRegA | mask) | (temp & A860_DELAY_L),
par);
temp = aty_ld_8(DAC_CNTL, par);
aty_st_8(DAC_CNTL, (temp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)),
par);
aty_st_le32(BUS_CNTL, 0x890e20f1, par);
aty_st_le32(DAC_CNTL, 0x47052100, par);
return 0;
}
const struct aty_dac_ops aty_dac_ati68860b = {
.set_dac = aty_set_dac_ATI68860_B,
};
/*
* AT&T 21C498 DAC
*/
static int aty_set_dac_ATT21C498(const struct fb_info *info,
const union aty_pll *pll, u32 bpp,
u32 accel)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 dotClock;
int muxmode = 0;
int DACMask = 0;
dotClock = 100000000 / pll->ics2595.period_in_ps;
switch (bpp) {
case 8:
if (dotClock > 8000) {
DACMask = 0x24;
muxmode = 1;
} else
DACMask = 0x04;
break;
case 15:
DACMask = 0x16;
break;
case 16:
DACMask = 0x36;
break;
case 24:
DACMask = 0xE6;
break;
case 32:
DACMask = 0xE6;
break;
}
if (1 /* info->mach64DAC8Bit */ )
DACMask |= 0x02;
aty_dac_waste4(par);
aty_st_8(DAC_REGS + 2, DACMask, par);
aty_st_le32(BUS_CNTL, 0x890e20f1, par);
aty_st_le32(DAC_CNTL, 0x00072000, par);
return muxmode;
}
const struct aty_dac_ops aty_dac_att21c498 = {
.set_dac = aty_set_dac_ATT21C498,
};
/*
* ATI 18818 / ICS 2595 Clock Chip
*/
static int aty_var_to_pll_18818(const struct fb_info *info, u32 vclk_per,
u32 bpp, union aty_pll *pll)
{
u32 MHz100; /* in 0.01 MHz */
u32 program_bits;
u32 post_divider;
/* Calculate the programming word */
MHz100 = 100000000 / vclk_per;
program_bits = -1;
post_divider = 1;
if (MHz100 > MAX_FREQ_2595) {
MHz100 = MAX_FREQ_2595;
return -EINVAL;
} else if (MHz100 < ABS_MIN_FREQ_2595) {
program_bits = 0; /* MHz100 = 257 */
return -EINVAL;
} else {
while (MHz100 < MIN_FREQ_2595) {
MHz100 *= 2;
post_divider *= 2;
}
}
MHz100 *= 1000;
MHz100 = (REF_DIV_2595 * MHz100) / REF_FREQ_2595;
MHz100 += 500; /* + 0.5 round */
MHz100 /= 1000;
if (program_bits == -1) {
program_bits = MHz100 - N_ADJ_2595;
switch (post_divider) {
case 1:
program_bits |= 0x0600;
break;
case 2:
program_bits |= 0x0400;
break;
case 4:
program_bits |= 0x0200;
break;
case 8:
default:
break;
}
}
program_bits |= STOP_BITS_2595;
pll->ics2595.program_bits = program_bits;
pll->ics2595.locationAddr = 0;
pll->ics2595.post_divider = post_divider;
pll->ics2595.period_in_ps = vclk_per;
return 0;
}
static u32 aty_pll_18818_to_var(const struct fb_info *info,
const union aty_pll *pll)
{
return (pll->ics2595.period_in_ps); /* default for now */
}
static void aty_ICS2595_put1bit(u8 data, const struct atyfb_par *par)
{
u8 tmp;
data &= 0x01;
tmp = aty_ld_8(CLOCK_CNTL, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
(tmp & ~0x04) | (data << 2), par);
tmp = aty_ld_8(CLOCK_CNTL, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (0 << 3),
par);
aty_StrobeClock(par);
tmp = aty_ld_8(CLOCK_CNTL, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (1 << 3),
par);
aty_StrobeClock(par);
return;
}
static void aty_set_pll18818(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 program_bits;
u32 locationAddr;
u32 i;
u8 old_clock_cntl;
u8 old_crtc_ext_disp;
old_clock_cntl = aty_ld_8(CLOCK_CNTL, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par);
old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
aty_st_8(CRTC_GEN_CNTL + 3,
old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
mdelay(15); /* delay for 50 (15) ms */
program_bits = pll->ics2595.program_bits;
locationAddr = pll->ics2595.locationAddr;
/* Program the clock chip */
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par); /* Strobe = 0 */
aty_StrobeClock(par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 1, par); /* Strobe = 0 */
aty_StrobeClock(par);
aty_ICS2595_put1bit(1, par); /* Send start bits */
aty_ICS2595_put1bit(0, par); /* Start bit */
aty_ICS2595_put1bit(0, par); /* Read / ~Write */
for (i = 0; i < 5; i++) { /* Location 0..4 */
aty_ICS2595_put1bit(locationAddr & 1, par);
locationAddr >>= 1;
}
for (i = 0; i < 8 + 1 + 2 + 2; i++) {
aty_ICS2595_put1bit(program_bits & 1, par);
program_bits >>= 1;
}
mdelay(1); /* delay for 1 ms */
(void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
old_clock_cntl | CLOCK_STROBE, par);
mdelay(50); /* delay for 50 (15) ms */
aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
((pll->ics2595.locationAddr & 0x0F) | CLOCK_STROBE), par);
return;
}
const struct aty_pll_ops aty_pll_ati18818_1 = {
.var_to_pll = aty_var_to_pll_18818,
.pll_to_var = aty_pll_18818_to_var,
.set_pll = aty_set_pll18818,
};
/*
* STG 1703 Clock Chip
*/
static int aty_var_to_pll_1703(const struct fb_info *info, u32 vclk_per,
u32 bpp, union aty_pll *pll)
{
u32 mhz100; /* in 0.01 MHz */
u32 program_bits;
/* u32 post_divider; */
u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
u32 temp, tempB;
u16 remainder, preRemainder;
short divider = 0, tempA;
/* Calculate the programming word */
mhz100 = 100000000 / vclk_per;
mach64MinFreq = MIN_FREQ_2595;
mach64MaxFreq = MAX_FREQ_2595;
mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
/* Calculate program word */
if (mhz100 == 0)
program_bits = 0xE0;
else {
if (mhz100 < mach64MinFreq)
mhz100 = mach64MinFreq;
if (mhz100 > mach64MaxFreq)
mhz100 = mach64MaxFreq;
divider = 0;
while (mhz100 < (mach64MinFreq << 3)) {
mhz100 <<= 1;
divider += 0x20;
}
temp = (unsigned int) (mhz100);
temp = (unsigned int) (temp * (MIN_N_1703 + 2));
temp -= (short) (mach64RefFreq << 1);
tempA = MIN_N_1703;
preRemainder = 0xffff;
do {
tempB = temp;
remainder = tempB % mach64RefFreq;
tempB = tempB / mach64RefFreq;
if ((tempB & 0xffff) <= 127
&& (remainder <= preRemainder)) {
preRemainder = remainder;
divider &= ~0x1f;
divider |= tempA;
divider =
(divider & 0x00ff) +
((tempB & 0xff) << 8);
}
temp += mhz100;
tempA++;
} while (tempA <= (MIN_N_1703 << 1));
program_bits = divider;
}
pll->ics2595.program_bits = program_bits;
pll->ics2595.locationAddr = 0;
pll->ics2595.post_divider = divider; /* fuer nix */
pll->ics2595.period_in_ps = vclk_per;
return 0;
}
static u32 aty_pll_1703_to_var(const struct fb_info *info,
const union aty_pll *pll)
{
return (pll->ics2595.period_in_ps); /* default for now */
}
static void aty_set_pll_1703(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 program_bits;
u32 locationAddr;
char old_crtc_ext_disp;
old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
aty_st_8(CRTC_GEN_CNTL + 3,
old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
program_bits = pll->ics2595.program_bits;
locationAddr = pll->ics2595.locationAddr;
/* Program clock */
aty_dac_waste4(par);
(void) aty_ld_8(DAC_REGS + 2, par);
aty_st_8(DAC_REGS + 2, (locationAddr << 1) + 0x20, par);
aty_st_8(DAC_REGS + 2, 0, par);
aty_st_8(DAC_REGS + 2, (program_bits & 0xFF00) >> 8, par);
aty_st_8(DAC_REGS + 2, (program_bits & 0xFF), par);
(void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
return;
}
const struct aty_pll_ops aty_pll_stg1703 = {
.var_to_pll = aty_var_to_pll_1703,
.pll_to_var = aty_pll_1703_to_var,
.set_pll = aty_set_pll_1703,
};
/*
* Chrontel 8398 Clock Chip
*/
static int aty_var_to_pll_8398(const struct fb_info *info, u32 vclk_per,
u32 bpp, union aty_pll *pll)
{
u32 tempA, tempB, fOut, longMHz100, diff, preDiff;
u32 mhz100; /* in 0.01 MHz */
u32 program_bits;
/* u32 post_divider; */
u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
u16 m, n, k = 0, save_m, save_n, twoToKth;
/* Calculate the programming word */
mhz100 = 100000000 / vclk_per;
mach64MinFreq = MIN_FREQ_2595;
mach64MaxFreq = MAX_FREQ_2595;
mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
save_m = 0;
save_n = 0;
/* Calculate program word */
if (mhz100 == 0)
program_bits = 0xE0;
else {
if (mhz100 < mach64MinFreq)
mhz100 = mach64MinFreq;
if (mhz100 > mach64MaxFreq)
mhz100 = mach64MaxFreq;
longMHz100 = mhz100 * 256 / 100; /* 8 bit scale this */
while (mhz100 < (mach64MinFreq << 3)) {
mhz100 <<= 1;
k++;
}
twoToKth = 1 << k;
diff = 0;
preDiff = 0xFFFFFFFF;
for (m = MIN_M; m <= MAX_M; m++) {
for (n = MIN_N; n <= MAX_N; n++) {
tempA = 938356; /* 14.31818 * 65536 */
tempA *= (n + 8); /* 43..256 */
tempB = twoToKth * 256;
tempB *= (m + 2); /* 4..32 */
fOut = tempA / tempB; /* 8 bit scale */
if (longMHz100 > fOut)
diff = longMHz100 - fOut;
else
diff = fOut - longMHz100;
if (diff < preDiff) {
save_m = m;
save_n = n;
preDiff = diff;
}
}
}
program_bits = (k << 6) + (save_m) + (save_n << 8);
}
pll->ics2595.program_bits = program_bits;
pll->ics2595.locationAddr = 0;
pll->ics2595.post_divider = 0;
pll->ics2595.period_in_ps = vclk_per;
return 0;
}
static u32 aty_pll_8398_to_var(const struct fb_info *info,
const union aty_pll *pll)
{
return (pll->ics2595.period_in_ps); /* default for now */
}
static void aty_set_pll_8398(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 program_bits;
u32 locationAddr;
char old_crtc_ext_disp;
char tmp;
old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
aty_st_8(CRTC_GEN_CNTL + 3,
old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
program_bits = pll->ics2595.program_bits;
locationAddr = pll->ics2595.locationAddr;
/* Program clock */
tmp = aty_ld_8(DAC_CNTL, par);
aty_st_8(DAC_CNTL, tmp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par);
aty_st_8(DAC_REGS, locationAddr, par);
aty_st_8(DAC_REGS + 1, (program_bits & 0xff00) >> 8, par);
aty_st_8(DAC_REGS + 1, (program_bits & 0xff), par);
tmp = aty_ld_8(DAC_CNTL, par);
aty_st_8(DAC_CNTL, (tmp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
par);
(void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
return;
}
const struct aty_pll_ops aty_pll_ch8398 = {
.var_to_pll = aty_var_to_pll_8398,
.pll_to_var = aty_pll_8398_to_var,
.set_pll = aty_set_pll_8398,
};
/*
* AT&T 20C408 Clock Chip
*/
static int aty_var_to_pll_408(const struct fb_info *info, u32 vclk_per,
u32 bpp, union aty_pll *pll)
{
u32 mhz100; /* in 0.01 MHz */
u32 program_bits;
/* u32 post_divider; */
u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
u32 temp, tempB;
u16 remainder, preRemainder;
short divider = 0, tempA;
/* Calculate the programming word */
mhz100 = 100000000 / vclk_per;
mach64MinFreq = MIN_FREQ_2595;
mach64MaxFreq = MAX_FREQ_2595;
mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
/* Calculate program word */
if (mhz100 == 0)
program_bits = 0xFF;
else {
if (mhz100 < mach64MinFreq)
mhz100 = mach64MinFreq;
if (mhz100 > mach64MaxFreq)
mhz100 = mach64MaxFreq;
while (mhz100 < (mach64MinFreq << 3)) {
mhz100 <<= 1;
divider += 0x40;
}
temp = (unsigned int) mhz100;
temp = (unsigned int) (temp * (MIN_N_408 + 2));
temp -= ((short) (mach64RefFreq << 1));
tempA = MIN_N_408;
preRemainder = 0xFFFF;
do {
tempB = temp;
remainder = tempB % mach64RefFreq;
tempB = tempB / mach64RefFreq;
if (((tempB & 0xFFFF) <= 255)
&& (remainder <= preRemainder)) {
preRemainder = remainder;
divider &= ~0x3f;
divider |= tempA;
divider =
(divider & 0x00FF) +
((tempB & 0xFF) << 8);
}
temp += mhz100;
tempA++;
} while (tempA <= 32);
program_bits = divider;
}
pll->ics2595.program_bits = program_bits;
pll->ics2595.locationAddr = 0;
pll->ics2595.post_divider = divider; /* fuer nix */
pll->ics2595.period_in_ps = vclk_per;
return 0;
}
static u32 aty_pll_408_to_var(const struct fb_info *info,
const union aty_pll *pll)
{
return (pll->ics2595.period_in_ps); /* default for now */
}
static void aty_set_pll_408(const struct fb_info *info,
const union aty_pll *pll)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 program_bits;
u32 locationAddr;
u8 tmpA, tmpB, tmpC;
char old_crtc_ext_disp;
old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
aty_st_8(CRTC_GEN_CNTL + 3,
old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
program_bits = pll->ics2595.program_bits;
locationAddr = pll->ics2595.locationAddr;
/* Program clock */
aty_dac_waste4(par);
tmpB = aty_ld_8(DAC_REGS + 2, par) | 1;
aty_dac_waste4(par);
aty_st_8(DAC_REGS + 2, tmpB, par);
tmpA = tmpB;
tmpC = tmpA;
tmpA |= 8;
tmpB = 1;
aty_st_8(DAC_REGS, tmpB, par);
aty_st_8(DAC_REGS + 2, tmpA, par);
udelay(400); /* delay for 400 us */
locationAddr = (locationAddr << 2) + 0x40;
tmpB = locationAddr;
tmpA = program_bits >> 8;
aty_st_8(DAC_REGS, tmpB, par);
aty_st_8(DAC_REGS + 2, tmpA, par);
tmpB = locationAddr + 1;
tmpA = (u8) program_bits;
aty_st_8(DAC_REGS, tmpB, par);
aty_st_8(DAC_REGS + 2, tmpA, par);
tmpB = locationAddr + 2;
tmpA = 0x77;
aty_st_8(DAC_REGS, tmpB, par);
aty_st_8(DAC_REGS + 2, tmpA, par);
udelay(400); /* delay for 400 us */
tmpA = tmpC & (~(1 | 8));
tmpB = 1;
aty_st_8(DAC_REGS, tmpB, par);
aty_st_8(DAC_REGS + 2, tmpA, par);
(void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
return;
}
const struct aty_pll_ops aty_pll_att20c408 = {
.var_to_pll = aty_var_to_pll_408,
.pll_to_var = aty_pll_408_to_var,
.set_pll = aty_set_pll_408,
};
/*
* Unsupported DAC and Clock Chip
*/
static int aty_set_dac_unsupported(const struct fb_info *info,
const union aty_pll *pll, u32 bpp,
u32 accel)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
aty_st_le32(BUS_CNTL, 0x890e20f1, par);
aty_st_le32(DAC_CNTL, 0x47052100, par);
/* new in 2.2.3p1 from Geert. ???????? */
aty_st_le32(BUS_CNTL, 0x590e10ff, par);
aty_st_le32(DAC_CNTL, 0x47012100, par);
return 0;
}
static int dummy(void)
{
return 0;
}
const struct aty_dac_ops aty_dac_unsupported = {
.set_dac = aty_set_dac_unsupported,
};
const struct aty_pll_ops aty_pll_unsupported = {
.var_to_pll = (void *) dummy,
.pll_to_var = (void *) dummy,
.set_pll = (void *) dummy,
};

View File

@@ -0,0 +1,328 @@
#include "radeonfb.h"
/* the accelerated functions here are patterned after the
* "ACCEL_MMIO" ifdef branches in XFree86
* --dte
*/
static void radeon_fixup_offset(struct radeonfb_info *rinfo)
{
u32 local_base;
/* *** Ugly workaround *** */
/*
* On some platforms, the video memory is mapped at 0 in radeon chip space
* (like PPCs) by the firmware. X will always move it up so that it's seen
* by the chip to be at the same address as the PCI BAR.
* That means that when switching back from X, there is a mismatch between
* the offsets programmed into the engine. This means that potentially,
* accel operations done before radeonfb has a chance to re-init the engine
* will have incorrect offsets, and potentially trash system memory !
*
* The correct fix is for fbcon to never call any accel op before the engine
* has properly been re-initialized (by a call to set_var), but this is a
* complex fix. This workaround in the meantime, called before every accel
* operation, makes sure the offsets are in sync.
*/
radeon_fifo_wait (1);
local_base = INREG(MC_FB_LOCATION) << 16;
if (local_base == rinfo->fb_local_base)
return;
rinfo->fb_local_base = local_base;
radeon_fifo_wait (3);
OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
(rinfo->fb_local_base >> 10));
OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
}
static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo,
const struct fb_fillrect *region)
{
radeon_fifo_wait(4);
OUTREG(DP_GUI_MASTER_CNTL,
rinfo->dp_gui_master_cntl /* contains, like GMC_DST_32BPP */
| GMC_BRUSH_SOLID_COLOR
| ROP3_P);
if (radeon_get_dstbpp(rinfo->depth) != DST_8BPP)
OUTREG(DP_BRUSH_FRGD_CLR, rinfo->pseudo_palette[region->color]);
else
OUTREG(DP_BRUSH_FRGD_CLR, region->color);
OUTREG(DP_WRITE_MSK, 0xffffffff);
OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
radeon_fifo_wait(2);
OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL);
OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE));
radeon_fifo_wait(2);
OUTREG(DST_Y_X, (region->dy << 16) | region->dx);
OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height);
}
void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region)
{
struct radeonfb_info *rinfo = info->par;
struct fb_fillrect modded;
int vxres, vyres;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_fillrect(info, region);
return;
}
radeon_fixup_offset(rinfo);
vxres = info->var.xres_virtual;
vyres = info->var.yres_virtual;
memcpy(&modded, region, sizeof(struct fb_fillrect));
if(!modded.width || !modded.height ||
modded.dx >= vxres || modded.dy >= vyres)
return;
if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx;
if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
radeonfb_prim_fillrect(rinfo, &modded);
}
static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo,
const struct fb_copyarea *area)
{
int xdir, ydir;
u32 sx, sy, dx, dy, w, h;
w = area->width; h = area->height;
dx = area->dx; dy = area->dy;
sx = area->sx; sy = area->sy;
xdir = sx - dx;
ydir = sy - dy;
if ( xdir < 0 ) { sx += w-1; dx += w-1; }
if ( ydir < 0 ) { sy += h-1; dy += h-1; }
radeon_fifo_wait(3);
OUTREG(DP_GUI_MASTER_CNTL,
rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */
| GMC_BRUSH_NONE
| GMC_SRC_DSTCOLOR
| ROP3_S
| DP_SRC_SOURCE_MEMORY );
OUTREG(DP_WRITE_MSK, 0xffffffff);
OUTREG(DP_CNTL, (xdir>=0 ? DST_X_LEFT_TO_RIGHT : 0)
| (ydir>=0 ? DST_Y_TOP_TO_BOTTOM : 0));
radeon_fifo_wait(2);
OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL);
OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE));
radeon_fifo_wait(3);
OUTREG(SRC_Y_X, (sy << 16) | sx);
OUTREG(DST_Y_X, (dy << 16) | dx);
OUTREG(DST_HEIGHT_WIDTH, (h << 16) | w);
}
void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
{
struct radeonfb_info *rinfo = info->par;
struct fb_copyarea modded;
u32 vxres, vyres;
modded.sx = area->sx;
modded.sy = area->sy;
modded.dx = area->dx;
modded.dy = area->dy;
modded.width = area->width;
modded.height = area->height;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_copyarea(info, area);
return;
}
radeon_fixup_offset(rinfo);
vxres = info->var.xres_virtual;
vyres = info->var.yres_virtual;
if(!modded.width || !modded.height ||
modded.sx >= vxres || modded.sy >= vyres ||
modded.dx >= vxres || modded.dy >= vyres)
return;
if(modded.sx + modded.width > vxres) modded.width = vxres - modded.sx;
if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx;
if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy;
if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
radeonfb_prim_copyarea(rinfo, &modded);
}
void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image)
{
struct radeonfb_info *rinfo = info->par;
if (info->state != FBINFO_STATE_RUNNING)
return;
radeon_engine_idle();
cfb_imageblit(info, image);
}
int radeonfb_sync(struct fb_info *info)
{
struct radeonfb_info *rinfo = info->par;
if (info->state != FBINFO_STATE_RUNNING)
return 0;
radeon_engine_idle();
return 0;
}
void radeonfb_engine_reset(struct radeonfb_info *rinfo)
{
u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
u32 host_path_cntl;
radeon_engine_flush (rinfo);
clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
mclk_cntl = INPLL(MCLK_CNTL);
OUTPLL(MCLK_CNTL, (mclk_cntl |
FORCEON_MCLKA |
FORCEON_MCLKB |
FORCEON_YCLKA |
FORCEON_YCLKB |
FORCEON_MC |
FORCEON_AIC));
host_path_cntl = INREG(HOST_PATH_CNTL);
rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
if (IS_R300_VARIANT(rinfo)) {
u32 tmp;
OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset |
SOFT_RESET_CP |
SOFT_RESET_HI |
SOFT_RESET_E2));
INREG(RBBM_SOFT_RESET);
OUTREG(RBBM_SOFT_RESET, 0);
tmp = INREG(RB2D_DSTCACHE_MODE);
OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */
} else {
OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
SOFT_RESET_CP |
SOFT_RESET_HI |
SOFT_RESET_SE |
SOFT_RESET_RE |
SOFT_RESET_PP |
SOFT_RESET_E2 |
SOFT_RESET_RB);
INREG(RBBM_SOFT_RESET);
OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32)
~(SOFT_RESET_CP |
SOFT_RESET_HI |
SOFT_RESET_SE |
SOFT_RESET_RE |
SOFT_RESET_PP |
SOFT_RESET_E2 |
SOFT_RESET_RB));
INREG(RBBM_SOFT_RESET);
}
OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET);
INREG(HOST_PATH_CNTL);
OUTREG(HOST_PATH_CNTL, host_path_cntl);
if (!IS_R300_VARIANT(rinfo))
OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
OUTPLL(MCLK_CNTL, mclk_cntl);
}
void radeonfb_engine_init (struct radeonfb_info *rinfo)
{
unsigned long temp;
/* disable 3D engine */
OUTREG(RB3D_CNTL, 0);
radeonfb_engine_reset(rinfo);
radeon_fifo_wait (1);
if (IS_R300_VARIANT(rinfo)) {
OUTREG(RB2D_DSTCACHE_MODE, INREG(RB2D_DSTCACHE_MODE) |
RB2D_DC_AUTOFLUSH_ENABLE |
RB2D_DC_DC_DISABLE_IGNORE_PE);
} else {
/* This needs to be double checked with ATI. Latest X driver
* completely "forgets" to set this register on < r3xx, and
* we used to just write 0 there... I'll keep the 0 and update
* that when we have sorted things out on X side.
*/
OUTREG(RB2D_DSTCACHE_MODE, 0);
}
radeon_fifo_wait (3);
/* We re-read MC_FB_LOCATION from card as it can have been
* modified by XFree drivers (ouch !)
*/
rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
(rinfo->fb_local_base >> 10));
OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
radeon_fifo_wait (1);
#if defined(__BIG_ENDIAN)
OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN);
#else
OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
#endif
radeon_fifo_wait (2);
OUTREG(DEFAULT_SC_TOP_LEFT, 0);
OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX |
DEFAULT_SC_BOTTOM_MAX));
temp = radeon_get_dstbpp(rinfo->depth);
rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS);
radeon_fifo_wait (1);
OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl |
GMC_BRUSH_SOLID_COLOR |
GMC_SRC_DATATYPE_COLOR));
radeon_fifo_wait (7);
/* clear line drawing regs */
OUTREG(DST_LINE_START, 0);
OUTREG(DST_LINE_END, 0);
/* set brush color regs */
OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
/* set source color regs */
OUTREG(DP_SRC_FRGD_CLR, 0xffffffff);
OUTREG(DP_SRC_BKGD_CLR, 0x00000000);
/* default write mask */
OUTREG(DP_WRITE_MSK, 0xffffffff);
radeon_engine_idle ();
}

View File

@@ -0,0 +1,216 @@
/*
* Backlight code for ATI Radeon based graphic cards
*
* Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org>
* Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
*
* 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.
*/
#include "radeonfb.h"
#include <linux/backlight.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#define MAX_RADEON_LEVEL 0xFF
struct radeon_bl_privdata {
struct radeonfb_info *rinfo;
uint8_t negative;
};
static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
int level)
{
int rlevel;
/* Get and convert the value */
/* No locking of bl_curve since we read a single value */
rlevel = pdata->rinfo->info->bl_curve[level] *
FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL;
if (rlevel < 0)
rlevel = 0;
else if (rlevel > MAX_RADEON_LEVEL)
rlevel = MAX_RADEON_LEVEL;
if (pdata->negative)
rlevel = MAX_RADEON_LEVEL - rlevel;
return rlevel;
}
static int radeon_bl_update_status(struct backlight_device *bd)
{
struct radeon_bl_privdata *pdata = bl_get_data(bd);
struct radeonfb_info *rinfo = pdata->rinfo;
u32 lvds_gen_cntl, tmpPixclksCntl;
int level;
if (rinfo->mon1_type != MT_LCD)
return 0;
/* We turn off the LCD completely instead of just dimming the
* backlight. This provides some greater power saving and the display
* is useless without backlight anyway.
*/
if (bd->props.power != FB_BLANK_UNBLANK ||
bd->props.fb_blank != FB_BLANK_UNBLANK)
level = 0;
else
level = bd->props.brightness;
del_timer_sync(&rinfo->lvds_timer);
radeon_engine_idle();
lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
if (level > 0) {
lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |=
(radeon_bl_get_level_brightness(pdata, level) <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_ON;
lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
} else {
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |=
(radeon_bl_get_level_brightness(pdata, level) <<
LVDS_BL_MOD_LEVEL_SHIFT);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
& LVDS_STATE_MASK;
} else {
/* Asic bug, when turning off LVDS_ON, we have to make sure
RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
*/
tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) <<
LVDS_BL_MOD_LEVEL_SHIFT);
lvds_gen_cntl |= LVDS_DISPLAY_DIS;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
udelay(100);
lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
lvds_gen_cntl &= ~(LVDS_DIGON);
rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
mod_timer(&rinfo->lvds_timer,
jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
}
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
return 0;
}
static int radeon_bl_get_brightness(struct backlight_device *bd)
{
return bd->props.brightness;
}
static struct backlight_ops radeon_bl_data = {
.get_brightness = radeon_bl_get_brightness,
.update_status = radeon_bl_update_status,
};
void radeonfb_bl_init(struct radeonfb_info *rinfo)
{
struct backlight_device *bd;
struct radeon_bl_privdata *pdata;
char name[12];
if (rinfo->mon1_type != MT_LCD)
return;
#ifdef CONFIG_PMAC_BACKLIGHT
if (!pmac_has_backlight_type("ati") &&
!pmac_has_backlight_type("mnca"))
return;
#endif
pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL);
if (!pdata) {
printk("radeonfb: Memory allocation failed\n");
goto error;
}
snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
bd = backlight_device_register(name, rinfo->info->dev, pdata, &radeon_bl_data);
if (IS_ERR(bd)) {
rinfo->info->bl_dev = NULL;
printk("radeonfb: Backlight registration failed\n");
goto error;
}
pdata->rinfo = rinfo;
/* Pardon me for that hack... maybe some day we can figure out in what
* direction backlight should work on a given panel?
*/
pdata->negative =
(rinfo->family != CHIP_FAMILY_RV200 &&
rinfo->family != CHIP_FAMILY_RV250 &&
rinfo->family != CHIP_FAMILY_RV280 &&
rinfo->family != CHIP_FAMILY_RV350);
#ifdef CONFIG_PMAC_BACKLIGHT
pdata->negative = pdata->negative ||
machine_is_compatible("PowerBook4,3") ||
machine_is_compatible("PowerBook6,3") ||
machine_is_compatible("PowerBook6,5");
#endif
rinfo->info->bl_dev = bd;
fb_bl_default_curve(rinfo->info, 0,
63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL,
217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL);
bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
bd->props.brightness = bd->props.max_brightness;
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd);
printk("radeonfb: Backlight initialized (%s)\n", name);
return;
error:
kfree(pdata);
return;
}
void radeonfb_bl_exit(struct radeonfb_info *rinfo)
{
struct backlight_device *bd = rinfo->info->bl_dev;
if (bd) {
struct radeon_bl_privdata *pdata;
pdata = bl_get_data(bd);
backlight_device_unregister(bd);
kfree(pdata);
rinfo->info->bl_dev = NULL;
printk("radeonfb: Backlight unloaded\n");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
#include "radeonfb.h"
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
#include <video/radeon.h>
#include "../edid.h"
static void radeon_gpio_setscl(void* data, int state)
{
struct radeon_i2c_chan *chan = data;
struct radeonfb_info *rinfo = chan->rinfo;
u32 val;
val = INREG(chan->ddc_reg) & ~(VGA_DDC_CLK_OUT_EN);
if (!state)
val |= VGA_DDC_CLK_OUT_EN;
OUTREG(chan->ddc_reg, val);
(void)INREG(chan->ddc_reg);
}
static void radeon_gpio_setsda(void* data, int state)
{
struct radeon_i2c_chan *chan = data;
struct radeonfb_info *rinfo = chan->rinfo;
u32 val;
val = INREG(chan->ddc_reg) & ~(VGA_DDC_DATA_OUT_EN);
if (!state)
val |= VGA_DDC_DATA_OUT_EN;
OUTREG(chan->ddc_reg, val);
(void)INREG(chan->ddc_reg);
}
static int radeon_gpio_getscl(void* data)
{
struct radeon_i2c_chan *chan = data;
struct radeonfb_info *rinfo = chan->rinfo;
u32 val;
val = INREG(chan->ddc_reg);
return (val & VGA_DDC_CLK_INPUT) ? 1 : 0;
}
static int radeon_gpio_getsda(void* data)
{
struct radeon_i2c_chan *chan = data;
struct radeonfb_info *rinfo = chan->rinfo;
u32 val;
val = INREG(chan->ddc_reg);
return (val & VGA_DDC_DATA_INPUT) ? 1 : 0;
}
static int radeon_setup_i2c_bus(struct radeon_i2c_chan *chan, const char *name)
{
int rc;
snprintf(chan->adapter.name, sizeof(chan->adapter.name),
"radeonfb %s", name);
chan->adapter.owner = THIS_MODULE;
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = &chan->rinfo->pdev->dev;
chan->algo.setsda = radeon_gpio_setsda;
chan->algo.setscl = radeon_gpio_setscl;
chan->algo.getsda = radeon_gpio_getsda;
chan->algo.getscl = radeon_gpio_getscl;
chan->algo.udelay = 10;
chan->algo.timeout = 20;
chan->algo.data = chan;
i2c_set_adapdata(&chan->adapter, chan);
/* Raise SCL and SDA */
radeon_gpio_setsda(chan, 1);
radeon_gpio_setscl(chan, 1);
udelay(20);
rc = i2c_bit_add_bus(&chan->adapter);
if (rc == 0)
dev_dbg(&chan->rinfo->pdev->dev, "I2C bus %s registered.\n", name);
else
dev_warn(&chan->rinfo->pdev->dev, "Failed to register I2C bus %s.\n", name);
return rc;
}
void radeon_create_i2c_busses(struct radeonfb_info *rinfo)
{
rinfo->i2c[0].rinfo = rinfo;
rinfo->i2c[0].ddc_reg = GPIO_MONID;
radeon_setup_i2c_bus(&rinfo->i2c[0], "monid");
rinfo->i2c[1].rinfo = rinfo;
rinfo->i2c[1].ddc_reg = GPIO_DVI_DDC;
radeon_setup_i2c_bus(&rinfo->i2c[1], "dvi");
rinfo->i2c[2].rinfo = rinfo;
rinfo->i2c[2].ddc_reg = GPIO_VGA_DDC;
radeon_setup_i2c_bus(&rinfo->i2c[2], "vga");
rinfo->i2c[3].rinfo = rinfo;
rinfo->i2c[3].ddc_reg = GPIO_CRT2_DDC;
radeon_setup_i2c_bus(&rinfo->i2c[3], "crt2");
}
void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
{
if (rinfo->i2c[0].rinfo)
i2c_del_adapter(&rinfo->i2c[0].adapter);
rinfo->i2c[0].rinfo = NULL;
if (rinfo->i2c[1].rinfo)
i2c_del_adapter(&rinfo->i2c[1].adapter);
rinfo->i2c[1].rinfo = NULL;
if (rinfo->i2c[2].rinfo)
i2c_del_adapter(&rinfo->i2c[2].adapter);
rinfo->i2c[2].rinfo = NULL;
if (rinfo->i2c[3].rinfo)
i2c_del_adapter(&rinfo->i2c[3].adapter);
rinfo->i2c[3].rinfo = NULL;
}
int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn,
u8 **out_edid)
{
u8 *edid;
edid = fb_ddc_read(&rinfo->i2c[conn-1].adapter);
if (out_edid)
*out_edid = edid;
if (!edid) {
pr_debug("radeonfb: I2C (port %d) ... not found\n", conn);
return MT_NONE;
}
if (edid[0x14] & 0x80) {
/* Fix detection using BIOS tables */
if (rinfo->is_mobility /*&& conn == ddc_dvi*/ &&
(INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
pr_debug("radeonfb: I2C (port %d) ... found LVDS panel\n", conn);
return MT_LCD;
} else {
pr_debug("radeonfb: I2C (port %d) ... found TMDS panel\n", conn);
return MT_DFP;
}
}
pr_debug("radeonfb: I2C (port %d) ... found CRT display\n", conn);
return MT_CRT;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,635 @@
#ifndef __RADEONFB_H__
#define __RADEONFB_H__
#ifdef CONFIG_FB_RADEON_DEBUG
#define DEBUG 1
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/fb.h>
#ifdef CONFIG_FB_RADEON_I2C
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#endif
#include <asm/io.h>
#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
#include <asm/prom.h>
#endif
#include <video/radeon.h>
/***************************************************************
* Most of the definitions here are adapted right from XFree86 *
***************************************************************/
/*
* Chip families. Must fit in the low 16 bits of a long word
*/
enum radeon_family {
CHIP_FAMILY_UNKNOW,
CHIP_FAMILY_LEGACY,
CHIP_FAMILY_RADEON,
CHIP_FAMILY_RV100,
CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/
CHIP_FAMILY_RV200,
CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350),
RS250 (IGP 7000) */
CHIP_FAMILY_R200,
CHIP_FAMILY_RV250,
CHIP_FAMILY_RS300, /* Radeon 9000 IGP */
CHIP_FAMILY_RV280,
CHIP_FAMILY_R300,
CHIP_FAMILY_R350,
CHIP_FAMILY_RV350,
CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */
CHIP_FAMILY_R420, /* R420/R423/M18 */
CHIP_FAMILY_RC410,
CHIP_FAMILY_RS400,
CHIP_FAMILY_RS480,
CHIP_FAMILY_LAST,
};
#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100) || \
((rinfo)->family == CHIP_FAMILY_RV200) || \
((rinfo)->family == CHIP_FAMILY_RS100) || \
((rinfo)->family == CHIP_FAMILY_RS200) || \
((rinfo)->family == CHIP_FAMILY_RV250) || \
((rinfo)->family == CHIP_FAMILY_RV280) || \
((rinfo)->family == CHIP_FAMILY_RS300))
#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300) || \
((rinfo)->family == CHIP_FAMILY_RV350) || \
((rinfo)->family == CHIP_FAMILY_R350) || \
((rinfo)->family == CHIP_FAMILY_RV380) || \
((rinfo)->family == CHIP_FAMILY_R420) || \
((rinfo)->family == CHIP_FAMILY_RC410) || \
((rinfo)->family == CHIP_FAMILY_RS480))
/*
* Chip flags
*/
enum radeon_chip_flags {
CHIP_FAMILY_MASK = 0x0000ffffUL,
CHIP_FLAGS_MASK = 0xffff0000UL,
CHIP_IS_MOBILITY = 0x00010000UL,
CHIP_IS_IGP = 0x00020000UL,
CHIP_HAS_CRTC2 = 0x00040000UL,
};
/*
* Errata workarounds
*/
enum radeon_errata {
CHIP_ERRATA_R300_CG = 0x00000001,
CHIP_ERRATA_PLL_DUMMYREADS = 0x00000002,
CHIP_ERRATA_PLL_DELAY = 0x00000004,
};
/*
* Monitor types
*/
enum radeon_montype {
MT_NONE = 0,
MT_CRT, /* CRT */
MT_LCD, /* LCD */
MT_DFP, /* DVI */
MT_CTV, /* composite TV */
MT_STV /* S-Video out */
};
/*
* DDC i2c ports
*/
enum ddc_type {
ddc_none,
ddc_monid,
ddc_dvi,
ddc_vga,
ddc_crt2,
};
/*
* Connector types
*/
enum conn_type {
conn_none,
conn_proprietary,
conn_crt,
conn_DVI_I,
conn_DVI_D,
};
/*
* PLL infos
*/
struct pll_info {
int ppll_max;
int ppll_min;
int sclk, mclk;
int ref_div;
int ref_clk;
};
/*
* This structure contains the various registers manipulated by this
* driver for setting or restoring a mode. It's mostly copied from
* XFree's RADEONSaveRec structure. A few chip settings might still be
* tweaked without beeing reflected or saved in these registers though
*/
struct radeon_regs {
/* Common registers */
u32 ovr_clr;
u32 ovr_wid_left_right;
u32 ovr_wid_top_bottom;
u32 ov0_scale_cntl;
u32 mpp_tb_config;
u32 mpp_gp_config;
u32 subpic_cntl;
u32 viph_control;
u32 i2c_cntl_1;
u32 gen_int_cntl;
u32 cap0_trig_cntl;
u32 cap1_trig_cntl;
u32 bus_cntl;
u32 surface_cntl;
u32 bios_5_scratch;
/* Other registers to save for VT switches or driver load/unload */
u32 dp_datatype;
u32 rbbm_soft_reset;
u32 clock_cntl_index;
u32 amcgpio_en_reg;
u32 amcgpio_mask;
/* Surface/tiling registers */
u32 surf_lower_bound[8];
u32 surf_upper_bound[8];
u32 surf_info[8];
/* CRTC registers */
u32 crtc_gen_cntl;
u32 crtc_ext_cntl;
u32 dac_cntl;
u32 crtc_h_total_disp;
u32 crtc_h_sync_strt_wid;
u32 crtc_v_total_disp;
u32 crtc_v_sync_strt_wid;
u32 crtc_offset;
u32 crtc_offset_cntl;
u32 crtc_pitch;
u32 disp_merge_cntl;
u32 grph_buffer_cntl;
u32 crtc_more_cntl;
/* CRTC2 registers */
u32 crtc2_gen_cntl;
u32 dac2_cntl;
u32 disp_output_cntl;
u32 disp_hw_debug;
u32 disp2_merge_cntl;
u32 grph2_buffer_cntl;
u32 crtc2_h_total_disp;
u32 crtc2_h_sync_strt_wid;
u32 crtc2_v_total_disp;
u32 crtc2_v_sync_strt_wid;
u32 crtc2_offset;
u32 crtc2_offset_cntl;
u32 crtc2_pitch;
/* Flat panel regs */
u32 fp_crtc_h_total_disp;
u32 fp_crtc_v_total_disp;
u32 fp_gen_cntl;
u32 fp2_gen_cntl;
u32 fp_h_sync_strt_wid;
u32 fp2_h_sync_strt_wid;
u32 fp_horz_stretch;
u32 fp_panel_cntl;
u32 fp_v_sync_strt_wid;
u32 fp2_v_sync_strt_wid;
u32 fp_vert_stretch;
u32 lvds_gen_cntl;
u32 lvds_pll_cntl;
u32 tmds_crc;
u32 tmds_transmitter_cntl;
/* Computed values for PLL */
u32 dot_clock_freq;
int feedback_div;
int post_div;
/* PLL registers */
u32 ppll_div_3;
u32 ppll_ref_div;
u32 vclk_ecp_cntl;
u32 clk_cntl_index;
/* Computed values for PLL2 */
u32 dot_clock_freq_2;
int feedback_div_2;
int post_div_2;
/* PLL2 registers */
u32 p2pll_ref_div;
u32 p2pll_div_0;
u32 htotal_cntl2;
/* Palette */
int palette_valid;
};
struct panel_info {
int xres, yres;
int valid;
int clock;
int hOver_plus, hSync_width, hblank;
int vOver_plus, vSync_width, vblank;
int hAct_high, vAct_high, interlaced;
int pwr_delay;
int use_bios_dividers;
int ref_divider;
int post_divider;
int fbk_divider;
};
struct radeonfb_info;
#ifdef CONFIG_FB_RADEON_I2C
struct radeon_i2c_chan {
struct radeonfb_info *rinfo;
u32 ddc_reg;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo;
};
#endif
enum radeon_pm_mode {
radeon_pm_none = 0, /* Nothing supported */
radeon_pm_d2 = 0x00000001, /* Can do D2 state */
radeon_pm_off = 0x00000002, /* Can resume from D3 cold */
};
typedef void (*reinit_function_ptr)(struct radeonfb_info *rinfo);
struct radeonfb_info {
struct fb_info *info;
struct radeon_regs state;
struct radeon_regs init_state;
char name[50];
unsigned long mmio_base_phys;
unsigned long fb_base_phys;
void __iomem *mmio_base;
void __iomem *fb_base;
unsigned long fb_local_base;
struct pci_dev *pdev;
#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
struct device_node *of_node;
#endif
void __iomem *bios_seg;
int fp_bios_start;
u32 pseudo_palette[16];
struct { u8 red, green, blue, pad; }
palette[256];
int chipset;
u8 family;
u8 rev;
unsigned int errata;
unsigned long video_ram;
unsigned long mapped_vram;
int vram_width;
int vram_ddr;
int pitch, bpp, depth;
int has_CRTC2;
int is_mobility;
int is_IGP;
int reversed_DAC;
int reversed_TMDS;
struct panel_info panel_info;
int mon1_type;
u8 *mon1_EDID;
struct fb_videomode *mon1_modedb;
int mon1_dbsize;
int mon2_type;
u8 *mon2_EDID;
u32 dp_gui_master_cntl;
struct pll_info pll;
int mtrr_hdl;
int pm_reg;
u32 save_regs[100];
int asleep;
int lock_blank;
int dynclk;
int no_schedule;
enum radeon_pm_mode pm_mode;
reinit_function_ptr reinit_func;
/* Lock on register access */
spinlock_t reg_lock;
/* Timer used for delayed LVDS operations */
struct timer_list lvds_timer;
u32 pending_lvds_gen_cntl;
#ifdef CONFIG_FB_RADEON_I2C
struct radeon_i2c_chan i2c[4];
#endif
};
#define PRIMARY_MONITOR(rinfo) (rinfo->mon1_type)
/*
* IO macros
*/
/* Note about this function: we have some rare cases where we must not schedule,
* this typically happen with our special "wake up early" hook which allows us to
* wake up the graphic chip (and thus get the console back) before everything else
* on some machines that support that mechanism. At this point, interrupts are off
* and scheduling is not permitted
*/
static inline void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
{
if (rinfo->no_schedule || oops_in_progress)
mdelay(ms);
else
msleep(ms);
}
#define INREG8(addr) readb((rinfo->mmio_base)+addr)
#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr)
#define INREG16(addr) readw((rinfo->mmio_base)+addr)
#define OUTREG16(addr,val) writew(val, (rinfo->mmio_base)+addr)
#define INREG(addr) readl((rinfo->mmio_base)+addr)
#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr)
static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr,
u32 val, u32 mask)
{
unsigned long flags;
unsigned int tmp;
spin_lock_irqsave(&rinfo->reg_lock, flags);
tmp = INREG(addr);
tmp &= (mask);
tmp |= (val);
OUTREG(addr, tmp);
spin_unlock_irqrestore(&rinfo->reg_lock, flags);
}
#define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask)
/*
* Note about PLL register accesses:
*
* I have removed the spinlock on them on purpose. The driver now
* expects that it will only manipulate the PLL registers in normal
* task environment, where radeon_msleep() will be called, protected
* by a semaphore (currently the console semaphore) so that no conflict
* will happen on the PLL register index.
*
* With the latest changes to the VT layer, this is guaranteed for all
* calls except the actual drawing/blits which aren't supposed to use
* the PLL registers anyway
*
* This is very important for the workarounds to work properly. The only
* possible exception to this rule is the call to unblank(), which may
* be done at irq time if an oops is in progress.
*/
static inline void radeon_pll_errata_after_index(struct radeonfb_info *rinfo)
{
if (!(rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS))
return;
(void)INREG(CLOCK_CNTL_DATA);
(void)INREG(CRTC_GEN_CNTL);
}
static inline void radeon_pll_errata_after_data(struct radeonfb_info *rinfo)
{
if (rinfo->errata & CHIP_ERRATA_PLL_DELAY) {
/* we can't deal with posted writes here ... */
_radeon_msleep(rinfo, 5);
}
if (rinfo->errata & CHIP_ERRATA_R300_CG) {
u32 save, tmp;
save = INREG(CLOCK_CNTL_INDEX);
tmp = save & ~(0x3f | PLL_WR_EN);
OUTREG(CLOCK_CNTL_INDEX, tmp);
tmp = INREG(CLOCK_CNTL_DATA);
OUTREG(CLOCK_CNTL_INDEX, save);
}
}
static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
{
u32 data;
OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
radeon_pll_errata_after_index(rinfo);
data = INREG(CLOCK_CNTL_DATA);
radeon_pll_errata_after_data(rinfo);
return data;
}
static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index,
u32 val)
{
OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
radeon_pll_errata_after_index(rinfo);
OUTREG(CLOCK_CNTL_DATA, val);
radeon_pll_errata_after_data(rinfo);
}
static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
u32 val, u32 mask)
{
unsigned int tmp;
tmp = __INPLL(rinfo, index);
tmp &= (mask);
tmp |= (val);
__OUTPLL(rinfo, index, tmp);
}
#define INPLL(addr) __INPLL(rinfo, addr)
#define OUTPLL(index, val) __OUTPLL(rinfo, index, val)
#define OUTPLLP(index, val, mask) __OUTPLLP(rinfo, index, val, mask)
#define BIOS_IN8(v) (readb(rinfo->bios_seg + (v)))
#define BIOS_IN16(v) (readb(rinfo->bios_seg + (v)) | \
(readb(rinfo->bios_seg + (v) + 1) << 8))
#define BIOS_IN32(v) (readb(rinfo->bios_seg + (v)) | \
(readb(rinfo->bios_seg + (v) + 1) << 8) | \
(readb(rinfo->bios_seg + (v) + 2) << 16) | \
(readb(rinfo->bios_seg + (v) + 3) << 24))
/*
* Inline utilities
*/
static inline int round_div(int num, int den)
{
return (num + (den / 2)) / den;
}
static inline int var_to_depth(const struct fb_var_screeninfo *var)
{
if (var->bits_per_pixel != 16)
return var->bits_per_pixel;
return (var->green.length == 5) ? 15 : 16;
}
static inline u32 radeon_get_dstbpp(u16 depth)
{
switch (depth) {
case 8:
return DST_8BPP;
case 15:
return DST_15BPP;
case 16:
return DST_16BPP;
case 32:
return DST_32BPP;
default:
return 0;
}
}
/*
* 2D Engine helper routines
*/
static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
{
int i;
for (i=0; i<2000000; i++) {
if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
return;
udelay(1);
}
printk(KERN_ERR "radeonfb: FIFO Timeout !\n");
}
static inline void radeon_engine_flush (struct radeonfb_info *rinfo)
{
int i;
/* Initiate flush */
OUTREGP(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
~RB2D_DC_FLUSH_ALL);
/* Ensure FIFO is empty, ie, make sure the flush commands
* has reached the cache
*/
_radeon_fifo_wait (rinfo, 64);
/* Wait for the flush to complete */
for (i=0; i < 2000000; i++) {
if (!(INREG(DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
return;
udelay(1);
}
printk(KERN_ERR "radeonfb: Flush Timeout !\n");
}
static inline void _radeon_engine_idle(struct radeonfb_info *rinfo)
{
int i;
/* ensure FIFO is empty before waiting for idle */
_radeon_fifo_wait (rinfo, 64);
for (i=0; i<2000000; i++) {
if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
radeon_engine_flush (rinfo);
return;
}
udelay(1);
}
printk(KERN_ERR "radeonfb: Idle Timeout !\n");
}
#define radeon_engine_idle() _radeon_engine_idle(rinfo)
#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries)
#define radeon_msleep(ms) _radeon_msleep(rinfo,ms)
/* I2C Functions */
extern void radeon_create_i2c_busses(struct radeonfb_info *rinfo);
extern void radeon_delete_i2c_busses(struct radeonfb_info *rinfo);
extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid);
/* PM Functions */
extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
extern int radeonfb_pci_resume(struct pci_dev *pdev);
extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep);
extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
/* Monitor probe functions */
extern void radeon_probe_screens(struct radeonfb_info *rinfo,
const char *monitor_layout, int ignore_edid);
extern void radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option);
extern int radeon_match_mode(struct radeonfb_info *rinfo,
struct fb_var_screeninfo *dest,
const struct fb_var_screeninfo *src);
/* Accel functions */
extern void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region);
extern void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
extern void radeonfb_imageblit(struct fb_info *p, const struct fb_image *image);
extern int radeonfb_sync(struct fb_info *info);
extern void radeonfb_engine_init (struct radeonfb_info *rinfo);
extern void radeonfb_engine_reset(struct radeonfb_info *rinfo);
/* Other functions */
extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch);
extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
int reg_only);
/* Backlight functions */
#ifdef CONFIG_FB_RADEON_BACKLIGHT
extern void radeonfb_bl_init(struct radeonfb_info *rinfo);
extern void radeonfb_bl_exit(struct radeonfb_info *rinfo);
#else
static inline void radeonfb_bl_init(struct radeonfb_info *rinfo) {}
static inline void radeonfb_bl_exit(struct radeonfb_info *rinfo) {}
#endif
#endif /* __RADEONFB_H__ */