#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#ifdef CONFIG_ZORRO
#include <linux/zorro.h>
#endif
#ifdef CONFIG_PCI
#include <linux/pci.h>
#endif
#ifdef CONFIG_AMIGA
#include <asm/amigahw.h>
#endif
#include <video/vga.h>
#include <video/cirrus.h>
#ifndef CIRRUSFB_NDEBUG
#define assert(expr) …
#else
#define assert …
#endif
#define MB_ …
enum cirrus_board { … };
static const struct cirrusfb_board_info_rec { … } cirrusfb_board_info[] = …;
#ifdef CONFIG_PCI
#define CHIP …
static struct pci_device_id cirrusfb_pci_table[] = …;
MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
#undef CHIP
#endif
#ifdef CONFIG_ZORRO
struct zorrocl {
enum cirrus_board type;
u32 regoffset;
u32 ramsize;
u32 ramoffset;
zorro_id ramid;
zorro_id ramid2;
};
static const struct zorrocl zcl_sd64 = {
.type = BT_SD64,
.ramid = ZORRO_PROD_HELFRICH_SD64_RAM,
};
static const struct zorrocl zcl_piccolo = {
.type = BT_PICCOLO,
.ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
};
static const struct zorrocl zcl_picasso = {
.type = BT_PICASSO,
.ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
};
static const struct zorrocl zcl_spectrum = {
.type = BT_SPECTRUM,
.ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
};
static const struct zorrocl zcl_picasso4_z3 = {
.type = BT_PICASSO4,
.regoffset = 0x00600000,
.ramsize = 4 * MB_,
.ramoffset = 0x01000000,
};
static const struct zorrocl zcl_picasso4_z2 = {
.type = BT_PICASSO4,
.regoffset = 0x10000,
.ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1,
.ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2,
};
static const struct zorro_device_id cirrusfb_zorro_table[] = {
{
.id = ZORRO_PROD_HELFRICH_SD64_REG,
.driver_data = (unsigned long)&zcl_sd64,
}, {
.id = ZORRO_PROD_HELFRICH_PICCOLO_REG,
.driver_data = (unsigned long)&zcl_piccolo,
}, {
.id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
.driver_data = (unsigned long)&zcl_picasso,
}, {
.id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
.driver_data = (unsigned long)&zcl_spectrum,
}, {
.id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
.driver_data = (unsigned long)&zcl_picasso4_z3,
}, {
.id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG,
.driver_data = (unsigned long)&zcl_picasso4_z2,
},
{ 0 }
};
MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
#endif
#ifdef CIRRUSFB_DEBUG
enum cirrusfb_dbg_reg_class {
CRT,
SEQ
};
#endif
struct cirrusfb_info { … };
static bool noaccel;
static char *mode_option = …;
static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info);
static void init_vgachip(struct fb_info *info);
static void switch_monitor(struct cirrusfb_info *cinfo, int on);
static void WGen(const struct cirrusfb_info *cinfo,
int regnum, unsigned char val);
static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
static void AttrOn(const struct cirrusfb_info *cinfo);
static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
unsigned char red, unsigned char green, unsigned char blue);
#if 0
static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
unsigned char *red, unsigned char *green,
unsigned char *blue);
#endif
static void cirrusfb_WaitBLT(u8 __iomem *regbase);
static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
u_short curx, u_short cury,
u_short destx, u_short desty,
u_short width, u_short height,
u_short line_length);
static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
u_short x, u_short y,
u_short width, u_short height,
u32 fg_color, u32 bg_color,
u_short line_length, u_char blitmode);
static void bestclock(long freq, int *nom, int *den, int *div);
#ifdef CIRRUSFB_DEBUG
static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
static void cirrusfb_dbg_print_regs(struct fb_info *info,
caddr_t regbase,
enum cirrusfb_dbg_reg_class reg_class, ...);
#endif
static inline int is_laguna(const struct cirrusfb_info *cinfo)
{ … }
static int opencount;
static int cirrusfb_open(struct fb_info *info, int user)
{ … }
static int cirrusfb_release(struct fb_info *info, int user)
{ … }
static int cirrusfb_check_mclk(struct fb_info *info, long freq)
{ … }
static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var,
struct fb_info *info)
{ … }
static int cirrusfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{ … }
static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
{ … }
static int cirrusfb_set_par_foo(struct fb_info *info)
{ … }
static int cirrusfb_set_par(struct fb_info *info)
{ … }
static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{ … }
static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{ … }
static int cirrusfb_blank(int blank_mode, struct fb_info *info)
{ … }
static void init_vgachip(struct fb_info *info)
{ … }
static void switch_monitor(struct cirrusfb_info *cinfo, int on)
{ … }
static int cirrusfb_sync(struct fb_info *info)
{ … }
static void cirrusfb_fillrect(struct fb_info *info,
const struct fb_fillrect *region)
{ … }
static void cirrusfb_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{ … }
static void cirrusfb_imageblit(struct fb_info *info,
const struct fb_image *image)
{ … }
#ifdef CONFIG_PCI
static int release_io_ports;
static unsigned int cirrusfb_get_memsize(struct fb_info *info,
u8 __iomem *regbase)
{ … }
static void get_pci_addrs(const struct pci_dev *pdev,
unsigned long *display, unsigned long *registers)
{ … }
static void cirrusfb_pci_unmap(struct fb_info *info)
{ … }
#endif
#ifdef CONFIG_ZORRO
static void cirrusfb_zorro_unmap(struct fb_info *info)
{
struct cirrusfb_info *cinfo = info->par;
struct zorro_dev *zdev = to_zorro_dev(info->device);
if (info->fix.smem_start > 16 * MB_)
iounmap(info->screen_base);
if (info->fix.mmio_start > 16 * MB_)
iounmap(cinfo->regbase);
zorro_release_device(zdev);
}
#endif
static const struct fb_ops cirrusfb_ops = …;
static int cirrusfb_set_fbinfo(struct fb_info *info)
{ … }
static int cirrusfb_register(struct fb_info *info)
{ … }
static void cirrusfb_cleanup(struct fb_info *info)
{ … }
#ifdef CONFIG_PCI
static int cirrusfb_pci_register(struct pci_dev *pdev,
const struct pci_device_id *ent)
{ … }
static void cirrusfb_pci_unregister(struct pci_dev *pdev)
{ … }
static struct pci_driver cirrusfb_pci_driver = …;
#endif
#ifdef CONFIG_ZORRO
static int cirrusfb_zorro_register(struct zorro_dev *z,
const struct zorro_device_id *ent)
{
struct fb_info *info;
int error;
const struct zorrocl *zcl;
enum cirrus_board btype;
unsigned long regbase, ramsize, rambase;
struct cirrusfb_info *cinfo;
info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
if (!info)
return -ENOMEM;
zcl = (const struct zorrocl *)ent->driver_data;
btype = zcl->type;
regbase = zorro_resource_start(z) + zcl->regoffset;
ramsize = zcl->ramsize;
if (ramsize) {
rambase = zorro_resource_start(z) + zcl->ramoffset;
if (zorro_resource_len(z) == 64 * MB_) {
rambase += zcl->ramoffset;
}
} else {
struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
if (!ram || !zorro_resource_len(ram)) {
dev_err(info->device, "No video RAM found\n");
error = -ENODEV;
goto err_release_fb;
}
rambase = zorro_resource_start(ram);
ramsize = zorro_resource_len(ram);
if (zcl->ramid2 &&
(ram = zorro_find_device(zcl->ramid2, NULL))) {
if (zorro_resource_start(ram) != rambase + ramsize) {
dev_warn(info->device,
"Skipping non-contiguous RAM at %pR\n",
&ram->resource);
} else {
ramsize += zorro_resource_len(ram);
}
}
}
dev_info(info->device,
"%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
rambase);
if (!zorro_request_device(z, "cirrusfb")) {
dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
error = -EBUSY;
goto err_release_fb;
}
cinfo = info->par;
cinfo->btype = btype;
info->fix.mmio_start = regbase;
cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
: ZTWO_VADDR(regbase);
if (!cinfo->regbase) {
dev_err(info->device, "Cannot map registers\n");
error = -EIO;
goto err_release_dev;
}
info->fix.smem_start = rambase;
info->screen_size = ramsize;
info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
: ZTWO_VADDR(rambase);
if (!info->screen_base) {
dev_err(info->device, "Cannot map video RAM\n");
error = -EIO;
goto err_unmap_reg;
}
cinfo->unmap = cirrusfb_zorro_unmap;
dev_info(info->device,
"Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
ramsize / MB_, rambase);
if (cirrusfb_board_info[btype].init_sr1f)
vga_wseq(cinfo->regbase, CL_SEQR1F,
cirrusfb_board_info[btype].sr1f);
error = cirrusfb_register(info);
if (error) {
dev_err(info->device, "Failed to register device, error %d\n",
error);
goto err_unmap_ram;
}
zorro_set_drvdata(z, info);
return 0;
err_unmap_ram:
if (rambase > 16 * MB_)
iounmap(info->screen_base);
err_unmap_reg:
if (regbase > 16 * MB_)
iounmap(cinfo->regbase);
err_release_dev:
zorro_release_device(z);
err_release_fb:
framebuffer_release(info);
return error;
}
static void cirrusfb_zorro_unregister(struct zorro_dev *z)
{
struct fb_info *info = zorro_get_drvdata(z);
cirrusfb_cleanup(info);
zorro_set_drvdata(z, NULL);
}
static struct zorro_driver cirrusfb_zorro_driver = {
.name = "cirrusfb",
.id_table = cirrusfb_zorro_table,
.probe = cirrusfb_zorro_register,
.remove = cirrusfb_zorro_unregister,
};
#endif
#ifndef MODULE
static int __init cirrusfb_setup(char *options)
{ … }
#endif
MODULE_AUTHOR(…) …;
MODULE_DESCRIPTION(…) …;
MODULE_LICENSE(…) …;
static int __init cirrusfb_init(void)
{ … }
static void __exit cirrusfb_exit(void)
{ … }
module_init(…) …;
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(…) …;
module_param(noaccel, bool, 0);
MODULE_PARM_DESC(…) …;
#ifdef MODULE
module_exit(cirrusfb_exit);
#endif
static void WGen(const struct cirrusfb_info *cinfo,
int regnum, unsigned char val)
{ … }
static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
{ … }
static void AttrOn(const struct cirrusfb_info *cinfo)
{ … }
static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
{ … }
static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
{ … }
static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
{ … }
static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
unsigned char green, unsigned char blue)
{ … }
#if 0
static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
unsigned char *green, unsigned char *blue)
{
unsigned int data = VGA_PEL_D;
vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
if (cinfo->btype == BT_PICASSO)
data += 0xfff;
*red = vga_r(cinfo->regbase, data);
*green = vga_r(cinfo->regbase, data);
*blue = vga_r(cinfo->regbase, data);
} else {
*blue = vga_r(cinfo->regbase, data);
*green = vga_r(cinfo->regbase, data);
*red = vga_r(cinfo->regbase, data);
}
}
#endif
static void cirrusfb_WaitBLT(u8 __iomem *regbase)
{ … }
static void cirrusfb_set_blitter(u8 __iomem *regbase,
u_short nwidth, u_short nheight,
u_long nsrc, u_long ndest,
u_short bltmode, u_short line_length)
{ … }
static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
u_short curx, u_short cury,
u_short destx, u_short desty,
u_short width, u_short height,
u_short line_length)
{ … }
static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
u_short x, u_short y, u_short width, u_short height,
u32 fg_color, u32 bg_color, u_short line_length,
u_char blitmode)
{ … }
static void bestclock(long freq, int *nom, int *den, int *div)
{ … }
#ifdef CIRRUSFB_DEBUG
static void cirrusfb_dbg_print_regs(struct fb_info *info,
caddr_t regbase,
enum cirrusfb_dbg_reg_class reg_class, ...)
{
va_list list;
unsigned char val = 0;
unsigned reg;
char *name;
va_start(list, reg_class);
name = va_arg(list, char *);
while (name != NULL) {
reg = va_arg(list, int);
switch (reg_class) {
case CRT:
val = vga_rcrt(regbase, (unsigned char) reg);
break;
case SEQ:
val = vga_rseq(regbase, (unsigned char) reg);
break;
default:
assert(false);
break;
}
dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
name = va_arg(list, char *);
}
va_end(list);
}
static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
{
dev_dbg(info->device, "VGA CRTC register dump:\n");
cirrusfb_dbg_print_regs(info, regbase, CRT,
"CR00", 0x00,
"CR01", 0x01,
"CR02", 0x02,
"CR03", 0x03,
"CR04", 0x04,
"CR05", 0x05,
"CR06", 0x06,
"CR07", 0x07,
"CR08", 0x08,
"CR09", 0x09,
"CR0A", 0x0A,
"CR0B", 0x0B,
"CR0C", 0x0C,
"CR0D", 0x0D,
"CR0E", 0x0E,
"CR0F", 0x0F,
"CR10", 0x10,
"CR11", 0x11,
"CR12", 0x12,
"CR13", 0x13,
"CR14", 0x14,
"CR15", 0x15,
"CR16", 0x16,
"CR17", 0x17,
"CR18", 0x18,
"CR22", 0x22,
"CR24", 0x24,
"CR26", 0x26,
"CR2D", 0x2D,
"CR2E", 0x2E,
"CR2F", 0x2F,
"CR30", 0x30,
"CR31", 0x31,
"CR32", 0x32,
"CR33", 0x33,
"CR34", 0x34,
"CR35", 0x35,
"CR36", 0x36,
"CR37", 0x37,
"CR38", 0x38,
"CR39", 0x39,
"CR3A", 0x3A,
"CR3B", 0x3B,
"CR3C", 0x3C,
"CR3D", 0x3D,
"CR3E", 0x3E,
"CR3F", 0x3F,
NULL);
dev_dbg(info->device, "\n");
dev_dbg(info->device, "VGA SEQ register dump:\n");
cirrusfb_dbg_print_regs(info, regbase, SEQ,
"SR00", 0x00,
"SR01", 0x01,
"SR02", 0x02,
"SR03", 0x03,
"SR04", 0x04,
"SR08", 0x08,
"SR09", 0x09,
"SR0A", 0x0A,
"SR0B", 0x0B,
"SR0D", 0x0D,
"SR10", 0x10,
"SR11", 0x11,
"SR12", 0x12,
"SR13", 0x13,
"SR14", 0x14,
"SR15", 0x15,
"SR16", 0x16,
"SR17", 0x17,
"SR18", 0x18,
"SR19", 0x19,
"SR1A", 0x1A,
"SR1B", 0x1B,
"SR1C", 0x1C,
"SR1D", 0x1D,
"SR1E", 0x1E,
"SR1F", 0x1F,
NULL);
dev_dbg(info->device, "\n");
}
#endif