468 lines
13 KiB
C
468 lines
13 KiB
C
/* $NetBSD: arcvideo.c,v 1.17 2001/06/30 16:20:37 bjh21 Exp $ */
|
||
/*-
|
||
* Copyright (c) 1998, 2000 Ben Harris
|
||
* All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* 3. The name of the author may not be used to endorse or promote products
|
||
* derived from this software without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
/* This file is part of NetBSD/arm26 -- a port of NetBSD to ARM2/3 machines. */
|
||
/*
|
||
* arcvideo.c - Archimedes video system driver.
|
||
*/
|
||
|
||
/*
|
||
* The Arc video system is rather closely tied into the heart of the
|
||
* machine, being partly controlled by the MEMC. Similarly, this
|
||
* driver will probably end up with its tentacles throughout the
|
||
* kernel, though in theory it should be posible to leave it out.
|
||
*/
|
||
|
||
#include <sys/param.h>
|
||
|
||
__RCSID("$NetBSD: arcvideo.c,v 1.17 2001/06/30 16:20:37 bjh21 Exp $");
|
||
|
||
#include <sys/device.h>
|
||
#include <sys/errno.h>
|
||
#include <sys/reboot.h> /* For bootverbose */
|
||
#include <sys/systm.h>
|
||
|
||
#include <uvm/uvm_extern.h>
|
||
|
||
#include <dev/wscons/wsconsio.h>
|
||
#define WSDISPLAY_TYPE_ARCHIMEDES 42 /* XXX Should be in wsconsio.h */
|
||
#include <dev/wscons/wscons_raster.h>
|
||
#include <dev/wscons/wsdisplayvar.h>
|
||
#include <dev/rasops/rasops.h>
|
||
|
||
#include <machine/boot.h>
|
||
#include <machine/bus.h>
|
||
#include <machine/intr.h>
|
||
#include <machine/irq.h>
|
||
#include <machine/machdep.h>
|
||
#include <machine/memcreg.h>
|
||
|
||
#include <arch/arm26/iobus/iocreg.h>
|
||
#include <arch/arm26/iobus/iocvar.h>
|
||
#include <arch/arm26/vidc/vidcreg.h>
|
||
#include <arch/arm26/vidc/arcvideovar.h>
|
||
|
||
static int arcvideo_match(struct device *parent, struct cfdata *cf, void *aux);
|
||
static void arcvideo_attach(struct device *parent, struct device *self,
|
||
void *aux);
|
||
#if 0
|
||
static int arcvideo_setmode(struct device *self, struct arcvideo_mode *mode);
|
||
static void arcvideo_await_vsync(struct device *self);
|
||
#endif
|
||
static int arcvideo_intr(void *cookie);
|
||
static int arcvideo_ioctl(void *cookie, u_long cmd, caddr_t data,
|
||
int flag, struct proc *p);
|
||
static paddr_t arcvideo_mmap(void *cookie, off_t off, int prot);
|
||
static int arcvideo_alloc_screen(void *cookie, const struct wsscreen_descr *scr,
|
||
void **scookiep, int *curxp, int *curyp,
|
||
long *defattrp);
|
||
static void arcvideo_free_screen(void *cookie, void *scookie);
|
||
static int arcvideo_show_screen(void *cookie, void *scookie, int waitok,
|
||
void (*cb)(void *, int, int),
|
||
void *cbarg);
|
||
static int arcvideo_load_font(void *cookie, void *scookie,
|
||
struct wsdisplay_font *);
|
||
static void arccons_8bpp_hack(struct rasops_info *ri);
|
||
|
||
struct arcvideo_softc {
|
||
struct device sc_dev;
|
||
paddr_t sc_screenmem_base;
|
||
struct arcvideo_mode sc_current_mode;
|
||
u_int32_t sc_vidc_ctl;
|
||
struct irq_handler *sc_irq;
|
||
struct evcnt sc_intrcnt;
|
||
int sc_flags;
|
||
#define AV_VIDEO_ON 0x01
|
||
};
|
||
|
||
struct cfattach arcvideo_ca = {
|
||
sizeof(struct arcvideo_softc), arcvideo_match, arcvideo_attach
|
||
};
|
||
|
||
struct device *the_arcvideo;
|
||
|
||
static struct rasops_info arccons_ri;
|
||
|
||
static struct wsscreen_descr arcscreen;
|
||
|
||
static struct wsdisplay_accessops arcvideo_accessops = {
|
||
arcvideo_ioctl, arcvideo_mmap, arcvideo_alloc_screen,
|
||
arcvideo_free_screen, arcvideo_show_screen, arcvideo_load_font
|
||
};
|
||
|
||
static int arcvideo_isconsole = 0;
|
||
|
||
static int
|
||
arcvideo_match(struct device *parent, struct cfdata *cf, void *aux)
|
||
{
|
||
|
||
/* A system can't sensibly have more than one VIDC. */
|
||
if (the_arcvideo == NULL)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
arcvideo_attach(struct device *parent, struct device *self, void *aux)
|
||
{
|
||
struct wsemuldisplaydev_attach_args da;
|
||
struct wsscreen_list scrdata;
|
||
const struct wsscreen_descr *screenp;
|
||
struct arcvideo_softc *sc = (void *)self;
|
||
|
||
the_arcvideo = self;
|
||
if (!arcvideo_isconsole) {
|
||
printf(": Not console -- I can't cope with this!\n");
|
||
return;
|
||
}
|
||
|
||
sc->sc_flags = AV_VIDEO_ON;
|
||
/* Detect monitor type? */
|
||
/* Reset VIDC */
|
||
|
||
/* Find IRQ */
|
||
evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
|
||
sc->sc_dev.dv_xname, "vsync intr");
|
||
sc->sc_irq = irq_establish(IOC_IRQ_IR, IPL_TTY, arcvideo_intr, self,
|
||
&sc->sc_intrcnt);
|
||
if (bootverbose)
|
||
printf(": VSYNC interrupts at %s", irq_string(sc->sc_irq));
|
||
irq_disable(sc->sc_irq);
|
||
|
||
printf("\n");
|
||
|
||
scrdata.nscreens = 1;
|
||
scrdata.screens = &screenp;
|
||
screenp = &arcscreen;
|
||
|
||
da.console = arcvideo_isconsole;
|
||
da.scrdata = &scrdata;
|
||
da.accessops = &arcvideo_accessops;
|
||
da.accesscookie = sc;
|
||
|
||
config_found(self, &da, wsemuldisplaydevprint);
|
||
}
|
||
|
||
#if 0
|
||
static int
|
||
arcvideo_setmode(struct device *self, struct arcvideo_mode *mode)
|
||
{
|
||
struct arcvideo_softc *sc = (void *)self;
|
||
u_int32_t newctl, ctlmask;
|
||
u_int32_t newhswr, newhbsr, newhdsr, newhder, newhber, newhcr, newhir;
|
||
u_int32_t newvswr, newvbsr, newvdsr, newvder, newvber, newvcr;
|
||
|
||
newctl = 0;
|
||
/* Dot clock */
|
||
/* XXX: should this be abstracted a little to allow for
|
||
variable VIDCLKs? */
|
||
switch (mode->timings.pixelrate) {
|
||
case 8000000:
|
||
newctl |= VIDC_CTL_DOTCLOCK_8MHZ;
|
||
break;
|
||
case 12000000:
|
||
newctl |= VIDC_CTL_DOTCLOCK_12MHZ;
|
||
break;
|
||
case 16000000:
|
||
newctl |= VIDC_CTL_DOTCLOCK_16MHZ;
|
||
break;
|
||
case 24000000:
|
||
newctl |= VIDC_CTL_DOTCLOCK_24MHZ;
|
||
break;
|
||
default:
|
||
return ENXIO;
|
||
};
|
||
/* Bits per pixel */
|
||
switch (mode->bpp) {
|
||
case 1:
|
||
newctl |= VIDC_CTL_BPP_ONE;
|
||
break;
|
||
case 2:
|
||
newctl |= VIDC_CTL_BPP_TWO;
|
||
break;
|
||
case 4:
|
||
newctl |= VIDC_CTL_BPP_FOUR;
|
||
break;
|
||
case 8:
|
||
newctl |= VIDC_CTL_BPP_EIGHT;
|
||
break;
|
||
default:
|
||
return EINVAL;
|
||
}
|
||
/* DMA timings */
|
||
/* XXX: should work this out from pixelrate, bpp and MCLK rate. */
|
||
newctl |= VIDC_CTL_DMARQ_37;
|
||
|
||
ctlmask = ~(VIDC_CTL_DOTCLOCK_MASK
|
||
| VIDC_CTL_BPP_MASK
|
||
| VIDC_CTL_DMARQ_MASK);
|
||
|
||
newhswr = (mode->timings.hsw - 2) / 2 << 14;
|
||
newhbsr = (mode->timings.hbs - 1) / 2 << 14;
|
||
switch (mode->bpp) {
|
||
case 8:
|
||
newhdsr = (mode->timings.hds - 5) / 2 << 14;
|
||
newhder = (mode->timings.hde - 5) / 2 << 14;
|
||
break;
|
||
case 4:
|
||
newhdsr = (mode->timings.hds - 7) / 2 << 14;
|
||
newhder = (mode->timings.hde - 7) / 2 << 14;
|
||
break;
|
||
case 2:
|
||
newhdsr = (mode->timings.hds - 11) / 2 << 14;
|
||
newhder = (mode->timings.hde - 11) / 2 << 14;
|
||
break;
|
||
case 1:
|
||
newhdsr = (mode->timings.hds - 19) / 2 << 14;
|
||
newhder = (mode->timings.hde - 19) / 2 << 14;
|
||
break;
|
||
}
|
||
newhber = (mode->timings.hbe - 1) / 2 << 14;
|
||
newhcr = (mode->timings.hc - 2) / 2 << 14;
|
||
newhir = mode->timings.hc / 4 << 14;
|
||
newvswr = (mode->timings.vsw - 1) << 14;
|
||
newvbsr = (mode->timings.vbs - 1) << 14;
|
||
newvdsr = (mode->timings.vds - 1) << 14;
|
||
newvder = (mode->timings.vde - 1) << 14;
|
||
newvber = (mode->timings.vbe - 1) << 14;
|
||
newvcr = (mode->timings.vc - 1) << 14;
|
||
arcvideo_await_vsync(self);
|
||
spltty(); /* XXX audio? */
|
||
newctl |= sc->sc_vidc_ctl & ctlmask;
|
||
VIDC_WRITE(VIDC_CONTROL | newctl);
|
||
sc->sc_vidc_ctl = newctl;
|
||
VIDC_WRITE(VIDC_VCR | newvcr);
|
||
VIDC_WRITE(VIDC_VSWR | newvswr);
|
||
VIDC_WRITE(VIDC_VBSR | newvbsr);
|
||
VIDC_WRITE(VIDC_VDSR | newvdsr);
|
||
VIDC_WRITE(VIDC_VDER | newvder);
|
||
VIDC_WRITE(VIDC_VBER | newvber);
|
||
VIDC_WRITE(VIDC_HCR | newhcr);
|
||
VIDC_WRITE(VIDC_HIR | newhir);
|
||
VIDC_WRITE(VIDC_HSWR | newhswr);
|
||
VIDC_WRITE(VIDC_HBSR | newhbsr);
|
||
VIDC_WRITE(VIDC_HDSR | newhdsr);
|
||
VIDC_WRITE(VIDC_HDER | newhder);
|
||
VIDC_WRITE(VIDC_HBER | newhber);
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
arcvideo_await_vsync(struct device *self)
|
||
{
|
||
|
||
panic("arcvideo_await_vsync not implemented");
|
||
}
|
||
#endif
|
||
|
||
static int
|
||
arcvideo_intr(void *cookie)
|
||
{
|
||
/* struct arcvideo_softc *sc = cookie; */
|
||
|
||
return IRQ_HANDLED;
|
||
}
|
||
|
||
/*
|
||
* In the standard RISC OS 8-bit palette (which we use), the bits go
|
||
* BGgRbrTt, feeding RrTt, GgTt and BbTt to the DACs. The top four of
|
||
* these bits are fixed in hardware.
|
||
*
|
||
* The following table is the closest match I can get to the colours
|
||
* at the top of rasops.c.
|
||
*/
|
||
|
||
static u_int8_t rasops_cmap_8bpp[] = {
|
||
0x00, 0x10, 0x40, 0x50, 0x80, 0x90, 0xc0, 0xfc,
|
||
0xd0, 0x17, 0x63, 0x77, 0x8b, 0x9f, 0xeb, 0xff,
|
||
};
|
||
|
||
void
|
||
arccons_init(void)
|
||
{
|
||
long defattr;
|
||
int clear = 0;
|
||
int crow;
|
||
int i;
|
||
struct rasops_info *ri = &arccons_ri;
|
||
|
||
/* Force the screen to be at a known location */
|
||
if (bootconfig.screenbase != 0)
|
||
clear = 1;
|
||
MEMC_WRITE(MEMC_SET_PTR(MEMC_VSTART, 0));
|
||
MEMC_WRITE(MEMC_SET_PTR(MEMC_VINIT, 0));
|
||
MEMC_WRITE(MEMC_SET_PTR(MEMC_VEND, 0x00080000));
|
||
|
||
/* TODO: We should really set up the VIDC ourselves here. */
|
||
|
||
/* Set up arccons_ri */
|
||
memset(ri, 0, sizeof(*ri));
|
||
ri->ri_depth = bootconfig.bpp;
|
||
ri->ri_bits = (u_char *)(MEMC_PHYS_BASE + 0);
|
||
ri->ri_width = bootconfig.xpixels;
|
||
ri->ri_height = bootconfig.ypixels;
|
||
ri->ri_stride = ((bootconfig.xpixels * bootconfig.bpp + 31) >> 5) << 2;
|
||
ri->ri_flg = RI_CENTER | (clear ? RI_CLEAR : 0);
|
||
|
||
if (rasops_init(ri, 1000, 1000) < 0)
|
||
panic("rasops_init failed");
|
||
|
||
/* Register video memory with UVM now we know how much we're using. */
|
||
uvm_page_physload(0, atop(MEMC_DMA_MAX),
|
||
atop(round_page(ri->ri_height * ri->ri_stride)),
|
||
atop(MEMC_DMA_MAX), VM_FREELIST_LOW);
|
||
|
||
if (ri->ri_depth == 8)
|
||
arccons_8bpp_hack(&arccons_ri);
|
||
else if (ri->ri_depth == 4)
|
||
for (i = 0; i < 1 << ri->ri_depth; i++)
|
||
VIDC_WRITE(VIDC_PALETTE_LCOL(i) |
|
||
VIDC_PALETTE_ENTRY(rasops_cmap[3*i + 0] >> 4,
|
||
rasops_cmap[3*i + 1] >> 4,
|
||
rasops_cmap[3*i + 2] >> 4, 0));
|
||
|
||
/* Take rcons stuff and put it in arcscreen */
|
||
/* XXX shouldn't this kind of thing be done by rcons_init? */
|
||
arcscreen.name = "arccons";
|
||
arcscreen.ncols = ri->ri_cols;
|
||
arcscreen.nrows = ri->ri_rows;
|
||
arcscreen.textops = &ri->ri_ops;
|
||
arcscreen.fontwidth = ri->ri_font->fontwidth;
|
||
arcscreen.fontheight = ri->ri_font->fontheight;
|
||
arcscreen.capabilities = ri->ri_caps;
|
||
|
||
/* work out cursor row */
|
||
if (clear)
|
||
crow = 0;
|
||
else
|
||
/* +/-1 is to round up */
|
||
crow = (bootconfig.cpixelrow - ri->ri_yorigin - 1) /
|
||
ri->ri_font->fontheight + 1;
|
||
if (crow < 0) crow = 0;
|
||
if (crow > ri->ri_rows) crow = ri->ri_rows;
|
||
|
||
if ((arccons_ri.ri_ops.alloc_attr)(&arccons_ri, 0, 0, 0, &defattr) !=
|
||
0)
|
||
panic("alloc_attr failed");
|
||
wsdisplay_cnattach(&arcscreen, &arccons_ri, 0, crow, defattr);
|
||
|
||
/* That should be all */
|
||
arcvideo_isconsole = 1;
|
||
}
|
||
|
||
/*
|
||
* The following is a gross hack because the rasops code has no way
|
||
* for us to specify the devcmap if we don't want the default. I think
|
||
* it assumes that all 8-bit displays are PseudoColor.
|
||
*/
|
||
|
||
static void
|
||
arccons_8bpp_hack(struct rasops_info *ri)
|
||
{
|
||
int i, c;
|
||
|
||
for (i = 0; i < 16; i++) {
|
||
c = rasops_cmap_8bpp[i];
|
||
ri->ri_devcmap[i] = c | (c<<8) | (c<<16) | (c<<24);
|
||
}
|
||
}
|
||
|
||
|
||
/* wsdisplay access functions */
|
||
|
||
static int
|
||
arcvideo_ioctl(void *cookie, u_long cmd, caddr_t data, int flag,
|
||
struct proc *p)
|
||
{
|
||
struct arcvideo_softc *sc = cookie;
|
||
|
||
switch (cmd) {
|
||
case WSDISPLAYIO_GTYPE:
|
||
*(u_int *)data = WSDISPLAY_TYPE_ARCHIMEDES;
|
||
return 0;
|
||
case WSDISPLAYIO_GVIDEO:
|
||
if (sc->sc_flags & AV_VIDEO_ON)
|
||
*(u_int *)data = WSDISPLAYIO_VIDEO_ON;
|
||
else
|
||
*(u_int *)data = WSDISPLAYIO_VIDEO_OFF;
|
||
return 0;
|
||
case WSDISPLAYIO_SVIDEO:
|
||
switch (*(u_int *)data) {
|
||
case WSDISPLAYIO_VIDEO_OFF:
|
||
sc->sc_flags &= ~AV_VIDEO_ON;
|
||
update_memc(MEMC_CTL_VIDEODMA | MEMC_CTL_RFRSH_MASK,
|
||
MEMC_CTL_RFRSH_CONTIN);
|
||
return 0;
|
||
case WSDISPLAYIO_VIDEO_ON:
|
||
sc->sc_flags |= AV_VIDEO_ON;
|
||
update_memc(MEMC_CTL_VIDEODMA | MEMC_CTL_RFRSH_MASK,
|
||
MEMC_CTL_VIDEODMA |
|
||
MEMC_CTL_RFRSH_FLYBACK);
|
||
return 0;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static paddr_t
|
||
arcvideo_mmap(void *cookie, off_t off, int prot)
|
||
{
|
||
|
||
return ENODEV;
|
||
}
|
||
|
||
static int
|
||
arcvideo_alloc_screen(void *cookie, const struct wsscreen_descr *scr,
|
||
void **scookiep, int *curxp, int *curyp, long *defattrp)
|
||
{
|
||
|
||
return ENODEV;
|
||
}
|
||
|
||
static void
|
||
arcvideo_free_screen(void *cookie, void *scookie)
|
||
{
|
||
|
||
panic("arcvideo_free_screen not implemented");
|
||
}
|
||
|
||
static int
|
||
arcvideo_show_screen(void *cookie, void *scookie, int waitok,
|
||
void (*cb)(void *cbarg, int error, int waitok), void *cbarg)
|
||
{
|
||
|
||
/* Do nothing, since there can only be one screen. */
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
arcvideo_load_font(void *cookie, void *emulcookie, struct wsdisplay_font *font)
|
||
{
|
||
|
||
return ENODEV;
|
||
}
|