/*
* IBMRGB52x.c:
*
* RAMDAC definitions for IBM's RGB52x PaletteDAC.
*
* Portion of this file is derived from XFree86's source code.
* [insert XFree86's copyright here].
*/
#include <stdio.h>
#include "libvga.h"
#include "timing.h"
#include "vgaregs.h"
#include "driver.h" /* for __svgalib_driver_report */
#include "ramdac.h"
#include "IBMRGB52x.h"
#define IBMRGB52x_STATESIZE 0x101
static int IBMRGB52x_dacspeed
= 170000; /* assuming 170MHz DAC */
static int IBMRGB52x_fref
= 16000; /* assuming 16MHz refclock */
static int IBMRGB52x_clk
= 2; /* use clock 2 */
#ifdef INCLUDE_IBMRGB52x_DAC_TEST
/*
* s3IBMRGB_Probe() from XFree86.
*
* returns 0x01xx for 525, 0x02xx for 524/528, where xx = revision.
*/
static int IBMRGB52x_probe
(void)
{
unsigned char CR43
, CR55
, dac
[3], lut
[6];
unsigned char ilow
, ihigh
, id
, rev
, id2
, rev2
;
int i
, j
;
int ret
= 0;
port_out
(0x43, 0x3D4);
CR43
= port_in
(0x3D5);
port_out
(CR43
& ~
0x02, 0x3D5);
port_out
(0x55, 0x3D4);
CR55
= port_in
(0x3D5);
port_out
(CR55
& ~
0x03, 0x3D5);
/* save DAC and first LUT entries */
for (i
= 0; i
< 3; i
++)
dac
[i
] = port_in
(IBMRGB_PIXEL_MASK
+ i
);
for (i
= j
= 0; i
< 2; i
++) {
port_out
(i
, IBMRGB_READ_ADDR
);
lut
[j
++] = port_in
(IBMRGB_RAMDAC_DATA
);
lut
[j
++] = port_in
(IBMRGB_RAMDAC_DATA
);
lut
[j
++] = port_in
(IBMRGB_RAMDAC_DATA
);
}
port_out
(0x55, 0x3D4);
port_out
((CR55
& ~
0x03) | 0x01, 0x3D5); /* set RS2 */
/* read ID and revision */
ilow
= port_in
(IBMRGB_INDEX_LOW
);
ihigh
= port_in
(IBMRGB_INDEX_HIGH
);
port_out
(0, IBMRGB_INDEX_HIGH
); /* index high */
port_out
(IBMRGB_rev
, IBMRGB_INDEX_LOW
);
rev
= port_in
(IBMRGB_INDEX_DATA
);
port_out
(IBMRGB_id
, IBMRGB_INDEX_LOW
);
id
= port_in
(IBMRGB_INDEX_DATA
);
/* known IDs:
1 = RGB525
2 = RGB524, RGB528
*/
if (id
>= 1 && id
<= 2) {
/* check if ID and revision are read only */
port_out
(IBMRGB_rev
, IBMRGB_INDEX_LOW
);
port_out
(~rev
, IBMRGB_INDEX_DATA
);
port_out
(IBMRGB_id
, IBMRGB_INDEX_LOW
);
port_out
(~id
, IBMRGB_INDEX_DATA
);
port_out
(IBMRGB_rev
, IBMRGB_INDEX_LOW
);
rev2
= port_in
(IBMRGB_INDEX_DATA
);
port_out
(IBMRGB_id
, IBMRGB_INDEX_LOW
);
id2
= port_in
(IBMRGB_INDEX_DATA
);
if (id
== id2
&& rev
== rev2
) { /* IBM RGB52x found */
ret
= (id
<< 8) | rev
;
} else {
port_out
(IBMRGB_rev
, IBMRGB_INDEX_LOW
);
port_out
(rev
, IBMRGB_INDEX_DATA
);
port_out
(IBMRGB_id
, IBMRGB_INDEX_LOW
);
port_out
(id
, IBMRGB_INDEX_DATA
);
}
}
port_out
(ilow
, IBMRGB_INDEX_LOW
);
port_out
(ihigh
, IBMRGB_INDEX_HIGH
);
port_out
(0x55, 0x3D4);
port_out
(CR55
& ~
0x03, 0x3D5); /* reset RS2 */
/* restore DAC and first LUT entries */
for (i
= j
= 0; i
< 2; i
++) {
port_out
(i
, IBMRGB_WRITE_ADDR
);
port_out
(lut
[j
++], IBMRGB_RAMDAC_DATA
);
port_out
(lut
[j
++], IBMRGB_RAMDAC_DATA
);
port_out
(lut
[j
++], IBMRGB_RAMDAC_DATA
);
}
for (i
= 0; i
< 3; i
++)
port_out
(dac
[i
], IBMRGB_PIXEL_MASK
+ i
);
port_out
(0x43, 0x3D4);
port_out
(CR43
, 0x3D5);
port_out
(0x55, 0x3D4);
port_out
(CR55
, 0x3D5);
return ret
;
}
#else
#define IBMRGB52x_probe 0
#endif
#ifdef INCLUDE_IBMRGB52x_DAC
static void IBMRGBSetClock
(long freq
, int clk
, long dacspeed
, long fref
,
int *best_m_out
, int *best_n_out
, int *best_df_out
)
{
volatile double ffreq
, fdacspeed
, ffref
;
volatile int df
, n
, m
, max_n
, min_df
;
volatile int best_m
= 69, best_n
= 17, best_df
= 0;
volatile double diff
, mindiff
;
#define FREQ_MIN 16250 /* 1000 * (0+65) / 4 */
#define FREQ_MAX dacspeed
if (freq
< FREQ_MIN
)
ffreq
= FREQ_MIN
/ 1000.0;
else if (freq
> FREQ_MAX
)
ffreq
= FREQ_MAX
/ 1000.0;
else
ffreq
= freq
/ 1000.0;
fdacspeed
= dacspeed
/ 1e3;
ffref
= fref
/ 1e3;
ffreq
/= ffref
;
ffreq
*= 16;
mindiff
= ffreq
;
if (freq
<= dacspeed
/ 4)
min_df
= 0;
else if (freq
<= dacspeed
/ 2)
min_df
= 1;
else
min_df
= 2;
for (df
= 0; df
< 4; df
++) {
ffreq
/= 2;
mindiff
/= 2;
if (df
< min_df
)
continue;
/* the remaining formula is ffreq = (m+65) / n */
if (df
< 3)
max_n
= fref
/ 1000 / 2;
else
max_n
= fref
/ 1000;
if (max_n
> 31)
max_n
= 31;
for (n
= 2; n
<= max_n
; n
++) {
m
= (int) (ffreq
* n
+ 0.5) - 65;
if (m
< 0)
m
= 0;
else if (m
> 63)
m
= 63;
diff
= (m
+ 65.0) / n
- ffreq
;
if (diff
< 0)
diff
= -diff
;
if (diff
< mindiff
) {
mindiff
= diff
;
best_n
= n
;
best_m
= m
;
best_df
= df
;
}
}
}
#ifdef DEBUG
fprintf(stderr
,"clk %d, setting to %f, m 0x%02x %d, n 0x%02x %d, df %d\n", clk
,
((best_m
+ 65.0) / best_n
) / (8 >> best_df
) * ffref
,
best_m
, best_m
, best_n
, best_n
, best_df
);
#endif
*best_m_out
= best_m
;
*best_n_out
= best_n
;
*best_df_out
= best_df
;
}
static void IBMRGB52x_init
(void)
{
unsigned char tmp
, CR55
;
#ifdef INCLUDE_IBMRGB52x_DAC_TEST
int idrev
;
idrev
= IBMRGB52x_probe
();
if (__svgalib_driver_report
)
fprintf(stderr
,"svgalib: Using IBM RGB 52%d PaletteDAC, revision %d.\n",
(idrev
>> 8) == 1 ? 5 : 4,
idrev
& 0xff);
#else
if (__svgalib_driver_report
)
fprintf(stderr
,"svgalib: Using IBM RGB 52x PaletteDAC.\n");
#endif
/* set RS2 */
port_out
(0x55, 0x3D4);
CR55
= port_in
(0x3D5) & 0xFC;
port_out
(CR55
| 0x01, 0x3D5);
tmp
= port_in
(IBMRGB_INDEX_CONTROL
);
port_out
(tmp
& ~
0x01, IBMRGB_INDEX_CONTROL
); /* turn off auto-increment */
port_out
(0, IBMRGB_INDEX_HIGH
); /* reset index high */
__svgalib_outCR
(0x55, CR55
);
}
static int IBMRGB52x_match_programmable_clock
(int desiredclock
)
{
int m
, n
, df
;
IBMRGBSetClock
(desiredclock
, IBMRGB52x_clk
, IBMRGB52x_dacspeed
,
IBMRGB52x_fref
, &m
, &n
, &df
);
return ((m
+ 65.0) / n
) / (8 >> df
) * IBMRGB52x_fref
;
}
static void IBMRGB52x_initialize_clock_state
(unsigned char *regs
, int freq
)
{
int m
, n
, df
;
IBMRGBSetClock
(freq
, IBMRGB52x_clk
, IBMRGB52x_dacspeed
,
IBMRGB52x_fref
, &m
, &n
, &df
);
if (__svgalib_driver_report
)
fprintf(stderr
,"clk %d, setting to %.3f, m 0x%02x %d, n 0x%02x %d, df %d\n",
IBMRGB52x_clk
, ((m
+ 65.0) / n
) / (8 >> df
) * IBMRGB52x_fref
/ 1000,
m
, m
, n
, n
, df
);
regs
[IBMRGB_misc_clock
] |= 0x01;
regs
[IBMRGB_m0
+ 2 * IBMRGB52x_clk
] = (df
<< 6) | (m
& 0x3f);
regs
[IBMRGB_n0
+ 2 * IBMRGB52x_clk
] = n
;
regs
[IBMRGB_pll_ctrl2
] &= 0xf0;
regs
[IBMRGB_pll_ctrl2
] |= IBMRGB52x_clk
;
regs
[IBMRGB_pll_ctrl1
] &= 0xf8;
regs
[IBMRGB_pll_ctrl1
] |= 0x03;
}
static void IBMRGB52x_savestate
(unsigned char *regs
)
{
int i
;
unsigned char tmp
;
/* set RS2 */
port_out
(0x55, 0x3D4);
tmp
= port_in
(0x3D5) & 0xFC;
port_out
(tmp
| 0x01, 0x3D5);
for (i
= 0; i
< 0x100; i
++) {
port_out
(i
, IBMRGB_INDEX_LOW
); /* high index is set to 0 */
regs
[i
] = port_in
(IBMRGB_INDEX_DATA
);
}
regs
[0x100] = __svgalib_inCR
(0x22);
__svgalib_outCR
(0x55, tmp
);
}
/* SL: not complete, need more work for 525. */
static void IBMRGB52x_restorestate
(const unsigned char *regs
)
{
int i
;
unsigned char tmp
;
/* set RS2 */
port_out
(0x55, 0x3D4);
tmp
= port_in
(0x3D5) & 0xFC;
port_out
(tmp
| 0x01, 0x3D5);
for (i
= 0; i
< 0x100; i
++) {
port_out
(i
, IBMRGB_INDEX_LOW
); /* high index is set to 0 */
port_out
(regs
[i
], IBMRGB_INDEX_DATA
);
}
__svgalib_outCR
(0x22, regs
[0x100]);
__svgalib_outCR
(0x55, tmp
);
}
static int IBMRGB52x_map_clock
(int bpp
, int pixelclock
)
{
return pixelclock
;
}
static int IBMRGB52x_map_horizontal_crtc
(int bpp
, int pixelclock
, int htiming
)
{
#ifdef PIXEL_MULTIPLEXING
switch (bpp
) {
case 4:
break;
case 8:
return htiming
/ 2;
case 16:
break;
case 24:
return htiming
* 3 / 2;
case 32:
return htiming
* 2;
}
#endif
return htiming
;
}
static void IBMRGB52x_initializestate
(unsigned char *regs
, int bpp
, int colormode
,
int pixelclock
)
{
unsigned char tmp
;
regs
[IBMRGB_misc_clock
] = (regs
[IBMRGB_misc_clock
] & 0xf0) | 0x03;
regs
[IBMRGB_sync
] = 0;
regs
[IBMRGB_hsync_pos
] = 0;
regs
[IBMRGB_pwr_mgmt
] = 0;
regs
[IBMRGB_dac_op
] &= ~
0x08; /* no sync on green */
regs
[IBMRGB_dac_op
] |= 0x02; /* fast slew */
regs
[IBMRGB_pal_ctrl
] = 0;
regs
[IBMRGB_misc1
] &= ~
0x43;
regs
[IBMRGB_misc1
] |= 1;
#ifdef PIXEL_MULTIPLEXING
if (bpp
>= 8)
regs
[IBMRGB_misc2
] = 0x43; /* use SID bus? 0x47 for DAC_8_BIT */
#endif
tmp
= __svgalib_inCR
(0x22);
if (bpp
<= 8) /* and 968 */
__svgalib_outCR
(0x22, tmp
| 0x08);
else
__svgalib_outCR
(0x22, tmp
& ~
0x08);
regs
[IBMRGB_pix_fmt
] &= ~
0x07;
switch (bpp
) {
case 4:
case 8:
regs
[IBMRGB_pix_fmt
] |= 0x03;
regs
[IBMRGB_8bpp
] = 0x00;
break;
case 15:
regs
[IBMRGB_pix_fmt
] |= 0x04;
regs
[IBMRGB_16bpp
] = 0x02;
break;
case 16:
regs
[IBMRGB_pix_fmt
] |= 0x04;
regs
[IBMRGB_16bpp
] = 0x00;
break;
case 24:
regs
[IBMRGB_pix_fmt
] |= 0x05; /* SL: guess */
regs
[IBMRGB_24bpp
] = 0x00;
break;
case 32:
regs
[IBMRGB_pix_fmt
] |= 0x06;
regs
[IBMRGB_32bpp
] = 0x00;
break;
}
IBMRGB52x_initialize_clock_state
(regs
,
IBMRGB52x_map_clock
(bpp
, pixelclock
));
}
static void IBMRGB52x_qualify_cardspecs
(CardSpecs
* cardspecs
, int dacspeed
)
{
IBMRGB52x_dacspeed
= __svgalib_setDacSpeed
(dacspeed
, 170000); /* 220 MHz version exist also */
cardspecs
->maxPixelClock4bpp
= IBMRGB52x_dacspeed
;
cardspecs
->maxPixelClock8bpp
= IBMRGB52x_dacspeed
;
#ifdef PIXEL_MULTIPLEXING
cardspecs
->maxPixelClock16bpp
= IBMRGB52x_dacspeed
;
cardspecs
->maxPixelClock24bpp
= IBMRGB52x_dacspeed
* 3 / 2;
cardspecs
->maxPixelClock32bpp
= IBMRGB52x_dacspeed
/ 2;
#else
cardspecs
->maxPixelClock16bpp
= 0;
cardspecs
->maxPixelClock24bpp
= 0;
cardspecs
->maxPixelClock32bpp
= 0;
#endif
cardspecs
->mapClock
= IBMRGB52x_map_clock
;
cardspecs
->matchProgrammableClock
= IBMRGB52x_match_programmable_clock
;
cardspecs
->mapHorizontalCrtc
= IBMRGB52x_map_horizontal_crtc
;
cardspecs
->flags
|= CLOCK_PROGRAMMABLE
;
}
DacMethods __svgalib_IBMRGB52x_methods
=
{
IBMRGB52x
,
"IBM RGB 52x PaletteDAC",
DAC_HAS_PROGRAMMABLE_CLOCKS
,
IBMRGB52x_probe
,
IBMRGB52x_init
,
IBMRGB52x_qualify_cardspecs
,
IBMRGB52x_savestate
,
IBMRGB52x_restorestate
,
IBMRGB52x_initializestate
,
IBMRGB52x_STATESIZE
};
#endif