/* * controlfb.c -- frame buffer device for the PowerMac 'control' display * * Created 12 July 1998 by Dan Jacobowitz <dan@debian.org> * Copyright (C) 1998 Dan Jacobowitz * Copyright (C) 2001 Takashi Oe * * Mmap code by Michel Lanners <mlan@cpu.lu> * * Frame buffer structure from: * drivers/video/chipsfb.c -- frame buffer device for * Chips & Technologies 65550 chip. * * Copyright (C) 1998 Paul Mackerras * * This file is derived from the Powermac "chips" driver: * Copyright (C) 1997 Fabio Riccardi. * And from the frame buffer device for Open Firmware-initialized devices: * Copyright (C) 1997 Geert Uytterhoeven. * * Hardware information from: * control.c: Console support for PowerMac "control" display adaptor. * Copyright (C) 1996 Paul Mackerras * * Updated to 2.5 framebuffer API by Ben Herrenschmidt * <benh@kernel.crashing.org>, Paul Mackerras <paulus@samba.org>, * and James Simmons <jsimmons@infradead.org>. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details.
*/
struct fb_par_control { int vmode, cmode; int xres, yres; int vxres, vyres; int xoffset, yoffset; int pitch; struct control_regvals regvals; unsignedlong sync; unsignedchar ctrl;
};
out_8(&p->cmap_regs->addr, regno); /* tell clut what addr to fill */
out_8(&p->cmap_regs->lut, r); /* send one color channel at */
out_8(&p->cmap_regs->lut, g); /* a time... */
out_8(&p->cmap_regs->lut, b);
if (regno < 16) { int i; switch (p->par.cmode) { case CMODE_16:
p->pseudo_palette[regno] =
(regno << 10) | (regno << 5) | regno; break; case CMODE_32:
i = (regno << 8) | regno;
p->pseudo_palette[regno] = (i << 16) | i; break;
}
}
return 0;
}
/******************** End of controlfb_ops implementation ******************/
for (i = 0; i < 3; ++i) {
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x50, i + 1, params[i]); while (!req.complete)
cuda_poll();
} #endif
}
/* * Set screen start address according to var offset values
*/ staticinlinevoid set_screen_start(int xoffset, int yoffset, struct fb_info_control *p)
{ struct fb_par_control *par = &p->par;
/* Now how about actually saying, Make it so! */ /* Some things in here probably don't need to be done each time. */ staticvoid control_set_hardware(struct fb_info_control *p, struct fb_par_control *par)
{ struct control_regvals *r; volatilestruct preg __iomem *rp; int i, cmode;
if (PAR_EQUAL(&p->par, par)) { /* * check if only xoffset or yoffset differs. * this prevents flickers in typical VT switch case.
*/ if (p->par.xoffset != par->xoffset ||
p->par.yoffset != par->yoffset)
set_screen_start(par->xoffset, par->yoffset, p);
return;
}
p->par = *par;
cmode = p->par.cmode;
r = &par->regvals;
/* Turn off display */
out_le32(CNTRL_REG(p,ctrl), 0x400 | par->ctrl);
/* Work out which banks of VRAM we have installed. */ /* danj: I guess the card just ignores writes to nonexistant VRAM... */
staticvoid __init find_vram_size(struct fb_info_control *p)
{ int bank1, bank2;
/* * Set VRAM in 2MB (bank 1) mode * VRAM Bank 2 will be accessible through offset 0x600000 if present * and VRAM Bank 1 will not respond at that offset even if present
*/
out_le32(CNTRL_REG(p,vram_attr), 0x31);
/* * Set VRAM in 2MB (bank 2) mode * VRAM Bank 1 will be accessible through offset 0x000000 if present * and VRAM Bank 2 will not respond at that offset even if present
*/
out_le32(CNTRL_REG(p,vram_attr), 0x39);
if (bank2) { if (!bank1) { /* * vram bank 2 only
*/
p->control_use_bank2 = 1;
p->vram_attr = 0x39;
p->frame_buffer += 0x600000;
p->frame_buffer_phys += 0x600000;
} else { /* * 4 MB vram
*/
p->vram_attr = 0x51;
}
} else { /* * vram bank 1 only
*/
p->vram_attr = 0x31;
}
p->total_vram = (bank1 + bank2) * 0x200000;
printk(KERN_INFO "controlfb: VRAM Total = %dMB " "(%dMB @ bank 1, %dMB @ bank 2)\n",
(bank1 + bank2) << 1, bank1 << 1, bank2 << 1);
}
/* * Get the monitor sense value. * Note that this can be called before calibrate_delay, * so we can't use udelay.
*/ staticint read_control_sense(struct fb_info_control *p)
{ int sense;
out_le32(CNTRL_REG(p,mon_sense), 7); /* drive all lines high */
__delay(200);
out_le32(CNTRL_REG(p,mon_sense), 077); /* turn off drivers */
__delay(2000);
sense = (in_le32(CNTRL_REG(p,mon_sense)) & 0x1c0) << 2;
/* drive each sense line low in turn and collect the other 2 */
out_le32(CNTRL_REG(p,mon_sense), 033); /* drive A low */
__delay(2000);
sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0xc0) >> 2;
out_le32(CNTRL_REG(p,mon_sense), 055); /* drive B low */
__delay(2000);
sense |= ((in_le32(CNTRL_REG(p,mon_sense)) & 0x100) >> 5)
| ((in_le32(CNTRL_REG(p,mon_sense)) & 0x40) >> 4);
out_le32(CNTRL_REG(p,mon_sense), 066); /* drive C low */
__delay(2000);
sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0x180) >> 7;
out_le32(CNTRL_REG(p,mon_sense), 077); /* turn off drivers */
return sense;
}
/********************** Various translation functions **********************/
/* * calculate the clock parameters to be sent to CUDA according to given * pixclock in pico second.
*/ staticint calc_clock_params(unsignedlong clk, unsignedchar *param)
{ unsignedlong p0, p1, p2, k, l, m, n, min;
if (clk > (CONTROL_PIXCLOCK_BASE << 3)) return 1;
p2 = ((clk << 4) < CONTROL_PIXCLOCK_BASE)? 3: 2;
l = clk << p2;
p0 = 0;
p1 = 0; for (k = 1, min = l; k < 32; k++) { unsignedlong rem;
m = CONTROL_PIXCLOCK_BASE * k;
n = m / l;
rem = m % l; if (n && (n < 128) && rem < min) {
p0 = k;
p1 = n;
min = rem;
}
} if (!p0 || !p1) return 1;
param[0] = p0;
param[1] = p1;
param[2] = p2;
return 0;
}
/* * This routine takes a user-supplied var, and picks the best vmode/cmode * from it.
*/
ctrl = in_le32(CNTRL_REG(p, ctrl)); if (blank_mode > 0) switch (blank_mode) { case FB_BLANK_VSYNC_SUSPEND:
ctrl &= ~3; break; case FB_BLANK_HSYNC_SUSPEND:
ctrl &= ~0x30; break; case FB_BLANK_POWERDOWN:
ctrl &= ~0x33;
fallthrough; case FB_BLANK_NORMAL:
ctrl |= 0x400; break; default: break;
} else {
ctrl &= ~0x400;
ctrl |= 0x33;
}
out_le32(CNTRL_REG(p,ctrl), ctrl);
return 0;
}
/* * Private mmap since we want to have a different caching on the framebuffer * for controlfb. * Note there's no locking in here; it's done in fb_mmap() in fbmem.c.
*/ staticint controlfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{ unsignedlong mmio_pgoff; unsignedlong start;
u32 len;
while ((this_opt = strsep(&options, ",")) != NULL) { if (!strncmp(this_opt, "vmode:", 6)) { int vmode = simple_strtoul(this_opt+6, NULL, 0); if (vmode > 0 && vmode <= VMODE_MAX &&
control_mac_modes[vmode - 1].m[1] >= 0)
default_vmode = vmode;
} elseif (!strncmp(this_opt, "cmode:", 6)) { int depth = simple_strtoul(this_opt+6, NULL, 0); switch (depth) { case CMODE_8: case CMODE_16: case CMODE_32:
default_cmode = depth; break; case 8:
default_cmode = CMODE_8; break; case 15: case 16:
default_cmode = CMODE_16; break; case 24: case 32:
default_cmode = CMODE_32; break;
}
}
}
}
/* * finish off the driver initialization and register
*/ staticint __init init_control(struct fb_info_control *p)
{ int full, sense, vmode, cmode, vyres; struct fb_var_screeninfo var; int rc;
printk(KERN_INFO "controlfb: ");
full = p->total_vram == 0x400000;
/* Try to pick a video mode out of NVRAM if we have one. */
cmode = default_cmode; if (IS_REACHABLE(CONFIG_NVRAM) && cmode == CMODE_NVRAM)
cmode = nvram_read_byte(NV_CMODE); if (cmode < CMODE_8 || cmode > CMODE_32)
cmode = CMODE_8;
vmode = default_vmode; if (IS_REACHABLE(CONFIG_NVRAM) && vmode == VMODE_NVRAM)
vmode = nvram_read_byte(NV_VMODE); if (vmode < 1 || vmode > VMODE_MAX ||
control_mac_modes[vmode - 1].m[full] < cmode) {
sense = read_control_sense(p);
printk(KERN_CONT "Monitor sense value = 0x%x, ", sense);
vmode = mac_map_monitor_sense(sense); if (control_mac_modes[vmode - 1].m[full] < 0)
vmode = VMODE_640_480_60;
cmode = min(cmode, control_mac_modes[vmode - 1].m[full]);
}
/* Initialize info structure */
control_init_info(&p->info, p);
/* Setup default var */ if (mac_vmode_to_var(vmode, cmode, &var) < 0) { /* This shouldn't happen! */
printk("mac_vmode_to_var(%d, %d,) failed\n", vmode, cmode);
try_again:
vmode = VMODE_640_480_60;
cmode = CMODE_8; if (mac_vmode_to_var(vmode, cmode, &var) < 0) {
printk(KERN_ERR "controlfb: mac_vmode_to_var() failed\n"); return -ENXIO;
}
printk(KERN_INFO "controlfb: ");
}
printk("using video mode %d and color mode %d.\n", vmode, cmode);
if (p->cmap_regs)
iounmap(p->cmap_regs); if (p->control_regs)
iounmap(p->control_regs); if (p->frame_buffer) { if (p->control_use_bank2)
p->frame_buffer -= 0x600000;
iounmap(p->frame_buffer);
} if (p->cmap_regs_phys)
release_mem_region(p->cmap_regs_phys, 0x1000); if (p->control_regs_phys)
release_mem_region(p->control_regs_phys, p->control_regs_size); if (p->fb_orig_base)
release_mem_region(p->fb_orig_base, p->fb_orig_size);
kfree(p);
}
if (control_fb) {
printk(KERN_ERR "controlfb: only one control is supported\n"); return -ENXIO;
}
if (of_pci_address_to_resource(dp, 2, &fb_res) ||
of_pci_address_to_resource(dp, 1, ®_res)) {
printk(KERN_ERR "can't get 2 addresses for control\n"); return -ENXIO;
}
p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM;
control_fb = p; /* save it for cleanups */
/* Map in frame buffer and registers */
p->fb_orig_base = fb_res.start;
p->fb_orig_size = resource_size(&fb_res); /* use the big-endian aperture (??) */
p->frame_buffer_phys = fb_res.start + 0x800000;
p->control_regs_phys = reg_res.start;
p->control_regs_size = resource_size(®_res);
if (!p->fb_orig_base ||
!request_mem_region(p->fb_orig_base,p->fb_orig_size,"controlfb")) {
p->fb_orig_base = 0; goto error_out;
} /* map at most 8MB for the frame buffer */
p->frame_buffer = ioremap_wt(p->frame_buffer_phys, 0x800000);
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.