// SPDX-License-Identifier: GPL-2.0-only /* * drivers/video/pvr2fb.c * * Frame buffer and fbcon support for the NEC PowerVR2 found within the Sega * Dreamcast. * * Copyright (c) 2001 M. R. Brown <mrbrown@0xd6.org> * Copyright (c) 2001 - 2008 Paul Mundt <lethal@linux-sh.org> * * This driver is mostly based on the excellent amifb and vfb sources. It uses * an odd scheme for converting hardware values to/from framebuffer values, * here are some hacked-up formulas: * * The Dreamcast has screen offsets from each side of its four borders and * the start offsets of the display window. I used these values to calculate * 'pseudo' values (think of them as placeholders) for the fb video mode, so * that when it came time to convert these values back into their hardware * values, I could just add mode- specific offsets to get the correct mode * settings: * * left_margin = diwstart_h - borderstart_h; * right_margin = borderstop_h - (diwstart_h + xres); * upper_margin = diwstart_v - borderstart_v; * lower_margin = borderstop_v - (diwstart_h + yres); * * hsync_len = borderstart_h + (hsync_total - borderstop_h); * vsync_len = borderstart_v + (vsync_total - borderstop_v); * * Then, when it's time to convert back to hardware settings, the only * constants are the borderstart_* offsets, all other values are derived from * the fb video mode: * * // PAL * borderstart_h = 116; * borderstart_v = 44; * ... * borderstop_h = borderstart_h + hsync_total - hsync_len; * ... * diwstart_v = borderstart_v - upper_margin; * * However, in the current implementation, the borderstart values haven't had * the benefit of being fully researched, so some modes may be broken.
*/
/* Pixel clocks, one for TV output, doubled for VGA output */ #define TV_CLK 74239 #define VGA_CLK 37119
/* This is for 60Hz - the VTOTAL is doubled for interlaced modes */ #define PAL_HTOTAL 863 #define PAL_VTOTAL 312 #define NTSC_HTOTAL 857 #define NTSC_VTOTAL 262
staticstruct pvr2fb_par { unsignedint hsync_total; /* Clocks/line */ unsignedint vsync_total; /* Lines/field */ unsignedint borderstart_h; unsignedint borderstop_h; unsignedint borderstart_v; unsignedint borderstop_v; unsignedint diwstart_h; /* Horizontal offset of the display field */ unsignedint diwstart_v; /* Vertical offset of the display field, for
interlaced modes, this is the long field */ unsignedlong disp_start; /* Address of image within VRAM */ unsignedchar is_interlaced; /* Is the display interlaced? */ unsignedchar is_doublescan; /* Are scanlines output twice? (doublescan) */ unsignedchar is_lowres; /* Is horizontal pixel-doubling enabled? */
/* * We do all updating, blanking, etc. during the vertical retrace period
*/ staticunsignedint do_vmode_full = 0; /* Change the video mode */ staticunsignedint do_vmode_pan = 0; /* Update the video mode */ staticshort do_blank = 0; /* (Un)Blank the screen */
staticunsignedint is_blanked = 0; /* Is the screen blanked? */
staticstruct fb_videomode pvr2_modedb[] = { /* * Broadcast video modes (PAL and NTSC). I'm unfamiliar with * PAL-M and PAL-N, but from what I've read both modes parallel PAL and * NTSC, so it shouldn't be a problem (I hope).
*/
/* * We only support the hardware palette for 16 and 32bpp. It's also * expected that the palette format has been set by the time we get * here, so we don't waste time setting it again.
*/ switch (info->var.bits_per_pixel) { case 16: /* RGB 565 */
tmp = (red & 0xf800) |
((green & 0xfc00) >> 5) |
((blue & 0xf800) >> 11);
pvr2fb_set_pal_entry(par, regno, tmp); break; case 24: /* RGB 888 */
red >>= 8; green >>= 8; blue >>= 8;
tmp = (red << 16) | (green << 8) | blue; break; case 32: /* ARGB 8888 */
red >>= 8; green >>= 8; blue >>= 8;
tmp = (transp << 24) | (red << 16) | (green << 8) | blue;
/* Now select the output format (either composite or other) */ /* XXX: Save the previous val first, as this reg is also AICA
related */ if (cable_type == CT_COMPOSITE)
fb_writel(3 << 8, VOUTC); elseif (cable_type == CT_RGB)
fb_writel(1 << 9, VOUTC); else
fb_writel(0, VOUTC);
/* * XXX: It's possible that a user could use a VGA box, change the cable * type in hardware (i.e. switch from VGA<->composite), then change * modes (i.e. switching to another VT). If that happens we should * automagically change the output format to cope, but currently I * don't have a VGA box to make sure this works properly.
*/
cable_type = pvr2_init_cable(); if (cable_type == CT_VGA && video_output != VO_VGA)
video_output = VO_VGA;
var->vmode &= FB_VMODE_MASK; if (var->vmode & FB_VMODE_INTERLACED && video_output != VO_VGA)
par->is_interlaced = 1; /* * XXX: Need to be more creative with this (i.e. allow doublecan for * PAL/NTSC output).
*/ if (var->vmode & FB_VMODE_DOUBLE && video_output == VO_VGA)
par->is_doublescan = 1;
/* * XXX: Need to be more creative with this (i.e. allow doublecan for * PAL/NTSC output).
*/ if (var->yres < 480 && video_output == VO_VGA)
var->vmode |= FB_VMODE_DOUBLE;
if (var->sync & FB_SYNC_BROADCAST) { if (var->vmode & FB_VMODE_INTERLACED)
vtotal /= 2; if (vtotal > (PAL_VTOTAL + NTSC_VTOTAL)/2) { /* PAL video output */ /* XXX: Should be using a range here ... ? */ if (hsync_total != PAL_HTOTAL) {
pr_debug("invalid hsync total for PAL\n"); return -EINVAL;
}
} else { /* NTSC video output */ if (hsync_total != NTSC_HTOTAL) {
pr_debug("invalid hsync total for NTSC\n"); return -EINVAL;
}
}
}
/* Update the start address of the display image */
fb_writel(par->disp_start, DISP_DIWADDRL);
fb_writel(par->disp_start +
get_line_length(var->xoffset+var->xres, var->bits_per_pixel),
DISP_DIWADDRS);
}
/* * Initialize the video mode. Currently, the 16bpp and 24bpp modes aren't * very stable. It's probably due to the fact that a lot of the 2D video * registers are still undocumented.
*/
/* column height, modulo, row width */ /* since we're "panning" within vram, we need to offset things based
* on the offset from the virtual x start to our real gfx. */ if (video_output != VO_VGA && par->is_interlaced)
diw_modulo += info->fix.line_length / 4;
diw_height = (par->is_interlaced ? var->yres / 2 : var->yres);
diw_width = get_line_length(var->xres, var->bits_per_pixel) / 4;
fb_writel((diw_modulo << 20) | (--diw_height << 10) | --diw_width,
DISP_DIWSIZE);
/* display address, long and short fields */
fb_writel(par->disp_start, DISP_DIWADDRL);
fb_writel(par->disp_start +
get_line_length(var->xoffset+var->xres, var->bits_per_pixel),
DISP_DIWADDRS);
ret = pin_user_pages_fast((unsignedlong)buf, nr_pages, FOLL_WRITE, pages); if (ret < nr_pages) { if (ret < 0) { /* * Clamp the unsigned nr_pages to zero so that the * error handling works. And leave ret at whatever * -errno value was returned from GUP.
*/
nr_pages = 0;
} else {
nr_pages = ret; /* * Use -EINVAL to represent a mildly desperate guess at * why we got fewer pages (maybe even zero pages) than * requested.
*/
ret = -EINVAL;
} goto out_unmap;
}
dma_configure_channel(shdma, 0x12c1);
dst = (unsignedlong)fb_info->screen_base + *ppos;
start = (unsignedlong)page_address(pages[0]);
end = (unsignedlong)page_address(pages[nr_pages]);
len = nr_pages << PAGE_SHIFT;
/* Half-assed contig check */ if (start + len == end) { /* As we do this in one shot, it's either all or nothing.. */ if ((*ppos + len) > fb_info->fix.smem_len) {
ret = -ENOSPC; goto out_unmap;
}
/* Not contiguous, writeout per-page instead.. */ for (i = 0; i < nr_pages; i++, dst += PAGE_SIZE) { if ((*ppos + (i << PAGE_SHIFT)) > fb_info->fix.smem_len) {
ret = -ENOSPC; goto out_unmap;
}
#ifndef MODULE staticint pvr2_get_param_val(conststruct pvr2_params *p, constchar *s, int size)
{ int i;
for (i = 0; i < size; i++) { if (!strncasecmp(p[i].name, s, strlen(s))) return p[i].val;
} return -1;
} #endif
staticchar *pvr2_get_param_name(conststruct pvr2_params *p, int val, int size)
{ int i;
for (i = 0; i < size; i++) { if (p[i].val == val) return p[i].name;
} return NULL;
}
/** * pvr2fb_common_init * * Common init code for the PVR2 chips. * * This mostly takes care of the common aspects of the fb setup and * registration. It's expected that the board-specific init code has * already setup pvr2_fix with something meaningful at this point. * * Device info reporting is also done here, as well as picking a sane * default from the modedb. For board-specific modelines, simply define * a per-board modedb. * * Also worth noting is that the cable and video output types are likely * always going to be VGA for the PCI-based PVR2 boards, but we leave this * in for flexibility anyways. Who knows, maybe someone has tv-out on a * PCI-based version of these things ;-)
*/ staticint __maybe_unused pvr2fb_common_init(void)
{ struct pvr2fb_par *par = currentpar; unsignedlong modememused, rev;
if (register_framebuffer(fb_info) < 0) goto out_err; /*Must write PIXDEPTH to register before anything is displayed - so force init */
pvr2_init_display(fb_info);
fb_notice(fb_info, "Mapped video memory to SQ addr 0x%lx\n",
pvr2fb_map); #endif
return 0;
out_err: if (fb_info->screen_base)
iounmap(fb_info->screen_base); if (par->mmio_base)
iounmap(par->mmio_base);
return -ENXIO;
}
#ifdef CONFIG_SH_DREAMCAST staticint __init pvr2fb_dc_init(void)
{ if (!mach_is_dreamcast()) return -ENXIO;
/* Make a guess at the monitor based on the attached cable */ if (pvr2_init_cable() == CT_VGA) {
fb_info->monspecs.hfmin = 30000;
fb_info->monspecs.hfmax = 70000;
fb_info->monspecs.vfmin = 60;
fb_info->monspecs.vfmax = 60;
} else { /* Not VGA, using a TV (taken from acornfb) */
fb_info->monspecs.hfmin = 15469;
fb_info->monspecs.hfmax = 15781;
fb_info->monspecs.vfmin = 49;
fb_info->monspecs.vfmax = 51;
}
/* * XXX: This needs to pull default video output via BIOS or other means
*/ if (video_output < 0) { if (cable_type == CT_VGA) {
video_output = VO_VGA;
} else {
video_output = VO_NTSC;
}
}
/* * Nothing exciting about the DC PVR2 .. only a measly 8MiB.
*/
pvr2_fix.smem_start = 0xa5000000; /* RAM starts here */
pvr2_fix.smem_len = 8 << 20;
ret = aperture_remove_conflicting_pci_devices(pdev, "pvrfb"); if (ret) return ret;
ret = pci_enable_device(pdev); if (ret) {
printk(KERN_ERR "pvr2fb: PCI enable failed\n"); return ret;
}
ret = pci_request_regions(pdev, "pvr2fb"); if (ret) {
printk(KERN_ERR "pvr2fb: PCI request regions failed\n"); return ret;
}
/* * Slightly more exciting than the DC PVR2 .. 16MiB!
*/
pvr2_fix.smem_start = pci_resource_start(pdev, 0);
pvr2_fix.smem_len = pci_resource_len(pdev, 0);
/* * Parse command arguments. Supported arguments are: * inverse Use inverse color maps * cable:composite|rgb|vga Override the video cable type * output:NTSC|PAL|VGA Override the video output format * * <xres>x<yres>[-<bpp>][@<refresh>] or, * <name>[-<bpp>][@<refresh>] Startup using this video mode
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.