Support linux FB over VGA/Direct Video.

This commit is contained in:
sorgelig
2020-01-04 14:57:47 +08:00
parent 7d27132de9
commit 8f0c9bd8cc
4 changed files with 108 additions and 24 deletions

114
video.cpp
View File

@@ -42,6 +42,11 @@
#define FB_FMT_RxB 0b10000
#define FB_EN 0x8000
#define FB_DV_LBRD 3
#define FB_DV_RBRD 6
#define FB_DV_UBRD 2
#define FB_DV_BBRD 2
static volatile uint32_t *fb_base = 0;
static int fb_enabled = 0;
@@ -77,6 +82,14 @@ vmode_t vmodes[] =
};
#define VMODES_NUM (sizeof(vmodes) / sizeof(vmodes[0]))
vmode_t tvmodes[] =
{
{{ 640, 16, 96, 48, 240, 4, 4, 14 }, 12.587 }, //NTSC 15K
{{ 640, 16, 96, 48, 480, 8, 4, 33 }, 25.175 }, //NTSC 31K
{{ 640, 16, 96, 48, 288, 6, 4, 14 }, 12.587 }, //PAL 15K
{{ 640, 16, 96, 48, 576, 2, 4, 42 }, 25.175 }, //PAL 31K
};
struct vmode_custom_t
{
uint32_t item[32];
@@ -404,19 +417,31 @@ static void set_video(vmode_custom_t *v, double Fpix)
loadScalerCfg();
setScaler();
v_cur = *v;
vmode_custom_t v_fix = v_cur;
if (cfg.direct_video)
{
v_fix.item[2] = FB_DV_RBRD;
v_fix.item[4] = FB_DV_LBRD;
v_fix.item[1] += v_cur.item[2] - v_fix.item[2];
v_fix.item[1] += v_cur.item[4] - v_fix.item[4];
v_fix.item[6] = FB_DV_BBRD;
v_fix.item[8] = FB_DV_UBRD;;
v_fix.item[5] += v_cur.item[6] - v_fix.item[6];
v_fix.item[5] += v_cur.item[8] - v_fix.item[8];
}
printf("Send HDMI parameters:\n");
spi_uio_cmd_cont(UIO_SET_VIDEO);
printf("video: ");
for (int i = 1; i <= 8; i++)
{
v_cur.item[i] = v->item[i];
spi_w(v_cur.item[i]);
printf("%d, ", v_cur.item[i]);
spi_w(v_fix.item[i]);
printf("%d(%d), ", v_cur.item[i], v_fix.item[i]);
}
for (int i = 9; i < 21; i++) v_cur.item[i] = v->item[i];
v_cur.Fpix = v->Fpix;
if(Fpix) setPLL(Fpix, &v_cur);
printf("\nPLL: ");
@@ -548,9 +573,26 @@ void video_mode_load()
printf("Disabling vsync_adjust because of enabled direct video.\n");
cfg.vsync_adjust = 0;
}
vmode_def = store_custom_video_mode(cfg.video_conf, &v_def);
vmode_pal = store_custom_video_mode(cfg.video_conf_pal, &v_pal);
vmode_ntsc = store_custom_video_mode(cfg.video_conf_ntsc, &v_ntsc);
if (cfg.direct_video)
{
int mode = cfg.menu_pal ? 2 : 0;
if (cfg.forced_scandoubler) mode++;
v_def.item[0] = mode;
for (int i = 0; i < 8; i++) v_def.item[i + 1] = tvmodes[mode].vpar[i];
setPLL(tvmodes[mode].Fpix, &v_def);
vmode_def = 1;
vmode_pal = 0;
vmode_ntsc = 0;
}
else
{
vmode_def = store_custom_video_mode(cfg.video_conf, &v_def);
vmode_pal = store_custom_video_mode(cfg.video_conf_pal, &v_pal);
vmode_ntsc = store_custom_video_mode(cfg.video_conf_ntsc, &v_ntsc);
}
set_video(&v_def, 0);
}
@@ -708,6 +750,7 @@ void video_fb_enable(int enable, int n)
{
if (fb_base)
{
if (cfg.direct_video) set_vga_fb(enable);
int res = spi_uio_cmd_cont(UIO_SET_FBUF);
if (res)
{
@@ -722,16 +765,23 @@ void video_fb_enable(int enable, int n)
uint32_t fb_addr = FB_ADDR + (FB_SIZE * 4 * n) + (n ? 0 : 4096);
fb_num = n;
int xoff = 0, yoff = 0;
if (cfg.direct_video)
{
xoff = v_cur.item[4] - FB_DV_LBRD;
yoff = v_cur.item[8] - FB_DV_UBRD;
}
printf("Switch to HPS frame buffer\n");
spi_w((uint16_t)(FB_EN | FB_FMT_RxB | FB_FMT_8888)); // format, enable flag
spi_w((uint16_t)fb_addr); // base address low word
spi_w(fb_addr >> 16); // base address high word
spi_w(fb_width); // frame width
spi_w(fb_height); // frame height
spi_w(0); // scaled left
spi_w(v_cur.item[1] - 1); // scaled right
spi_w(0); // scaled top
spi_w(v_cur.item[5] - 1); // scaled bottom
spi_w(xoff); // scaled left
spi_w(xoff + v_cur.item[1] - 1); // scaled right
spi_w(yoff); // scaled top
spi_w(yoff + v_cur.item[5] - 1); // scaled bottom
printf("HPS frame buffer: %dx%d, stride = %d bytes\n", fb_width, fb_height, fb_stride);
if (!fb_num)
@@ -1301,13 +1351,24 @@ void video_cmd(char *cmd)
if (width < 120 || width > (int)v_cur.item[1]) width = v_cur.item[1];
if (height < 120 || height > (int)v_cur.item[5]) height = v_cur.item[5];
div = 1;
while ((width*(div + 1)) <= (int)v_cur.item[1] && (height*(div + 1)) <= (int)v_cur.item[5]) div++;
int divx = 1;
int divy = 1;
if (cfg.direct_video && (v_cur.item[5] < 300))
{
// TV 240P/288P
while ((width*(divx + 1)) <= (int)v_cur.item[1]) divx++;
while ((height*(divy + 1)) <= (int)v_cur.item[5]) divy++;
}
else
{
while ((width*(divx + 1)) <= (int)v_cur.item[1] && (height*(divx + 1)) <= (int)v_cur.item[5]) divx++;
divy = divx;
}
hmin = (uint16_t)((v_cur.item[1] - (width * div)) / 2);
vmin = (uint16_t)((v_cur.item[5] - (height * div)) / 2);
hmax = hmin + (width * div) - 1;
vmax = vmin + (height * div) - 1;
hmin = (uint16_t)((v_cur.item[1] - (width * divx)) / 2);
vmin = (uint16_t)((v_cur.item[5] - (height * divy)) / 2);
hmax = hmin + (width * divx) - 1;
vmax = vmin + (height * divy) - 1;
accept = 1;
}
@@ -1357,16 +1418,23 @@ void video_cmd(char *cmd)
uint32_t addr = FB_ADDR + 4096;
int xoff = 0, yoff = 0;
if (cfg.direct_video)
{
xoff = v_cur.item[4] - FB_DV_LBRD;
yoff = v_cur.item[8] - FB_DV_UBRD;
}
spi_uio_cmd_cont(UIO_SET_FBUF);
spi_w(FB_EN | sc_fmt); // format, enable flag
spi_w((uint16_t)addr); // base address low word
spi_w(addr >> 16); // base address high word
spi_w(width); // frame width
spi_w(height); // frame height
spi_w(hmin); // scaled left
spi_w(hmax); // scaled right
spi_w(vmin); // scaled top
spi_w(vmax); // scaled bottom
spi_w(xoff + hmin); // scaled left
spi_w(xoff + hmax); // scaled right
spi_w(yoff + vmin); // scaled top
spi_w(yoff + vmax); // scaled bottom
DisableIO();
if (cmd[6] != '2')