summaryrefslogtreecommitdiffstats
path: root/arch/m68k/atari
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /arch/m68k/atari
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'arch/m68k/atari')
-rw-r--r--arch/m68k/atari/Makefile16
-rw-r--r--arch/m68k/atari/atafb.c3278
-rw-r--r--arch/m68k/atari/atafb.h48
-rw-r--r--arch/m68k/atari/ataints.c639
-rw-r--r--arch/m68k/atari/atakeyb.c847
-rw-r--r--arch/m68k/atari/atasound.c149
-rw-r--r--arch/m68k/atari/atasound.h33
-rw-r--r--arch/m68k/atari/config.c1179
-rw-r--r--arch/m68k/atari/joystick.c141
-rw-r--r--arch/m68k/atari/ksyms.c40
-rw-r--r--arch/m68k/atari/stdma.c195
-rw-r--r--arch/m68k/atari/stram.c243
12 files changed, 6808 insertions, 0 deletions
diff --git a/arch/m68k/atari/Makefile b/arch/m68k/atari/Makefile
new file mode 100644
index 000000000..77324bc10
--- /dev/null
+++ b/arch/m68k/atari/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for Linux arch/m68k/atari source directory
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+EXTRA_CFLAGS := -Wa,-m68030
+
+O_TARGET := atari.o
+O_OBJS := config.o atakeyb.o ataints.o \
+ stdma.o atasound.o joystick.o stram.o atafb.o ksyms.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/m68k/atari/atafb.c b/arch/m68k/atari/atafb.c
new file mode 100644
index 000000000..488ecc302
--- /dev/null
+++ b/arch/m68k/atari/atafb.c
@@ -0,0 +1,3278 @@
+/*
+ * atari/atafb.c -- Low level implementation of Atari frame buffer device
+ *
+ * Copyright (C) 1994 Martin Schaller & Roman Hodek
+ *
+ * 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.
+ *
+ * History:
+ * - 03 Jan 95: Original version by Martin Schaller: The TT driver and
+ * all the device independent stuff
+ * - 09 Jan 95: Roman: I've added the hardware abstraction (hw_switch)
+ * and wrote the Falcon, ST(E), and External drivers
+ * based on the original TT driver.
+ * - 07 May 95: Martin: Added colormap operations for the external driver
+ * - 21 May 95: Martin: Added support for overscan
+ * Andreas: some bug fixes for this
+ * - Jul 95: Guenther Kelleter <guenther@pool.informatik.rwth-aachen.de>:
+ * Programmable Falcon video modes
+ * (thanks to Christian Cartus for documentation
+ * of VIDEL registers).
+ * - 27 Dec 95: Guenther: Implemented user definable video modes "user[0-7]"
+ * on minor 24...31. "user0" may be set on commandline by
+ * "R<x>;<y>;<depth>". (Makes sense only on Falcon)
+ * Video mode switch on Falcon now done at next VBL interrupt
+ * to avoid the annoying right shift of the screen.
+ *
+ *
+ * To do:
+ * - For the Falcon it is not possible to set random video modes on
+ * SM124 and SC/TV, only the bootup resolution is supported.
+ *
+ */
+
+#define ATAFB_TT
+#define ATAFB_STE
+#define ATAFB_EXT
+#define ATAFB_FALCON
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+
+#include <linux/fb.h>
+#include <asm/atarikb.h>
+
+#define SWITCH_ACIA 0x01 /* modes for switch on OverScan */
+#define SWITCH_SND6 0x40
+#define SWITCH_SND7 0x80
+#define SWITCH_NONE 0x00
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+#define up(x, r) (((x) + (r) - 1) & ~((r)-1))
+
+
+static int default_par=0; /* default resolution (0=none) */
+
+static int node; /* node of the /dev/fb?current file */
+
+static unsigned long default_mem_req=0;
+
+static int hwscroll=-1;
+
+static int use_hwscroll = 1;
+
+static int sttt_xres=640,st_yres=400,tt_yres=480;
+static int sttt_xres_virtual=640,sttt_yres_virtual=400;
+static int ovsc_offset=0, ovsc_addlen=0;
+int ovsc_switchmode=0;
+
+static struct atari_fb_par {
+ unsigned long screen_base;
+ int yres_virtual;
+ union {
+ struct {
+ int mode;
+ int sync;
+ } tt, st;
+ struct falcon_hw {
+ /* Here are fields for storing a video mode, as direct
+ * parameters for the hardware.
+ */
+ short sync;
+ short line_width;
+ short line_offset;
+ short st_shift;
+ short f_shift;
+ short vid_control;
+ short vid_mode;
+ short xoffset;
+ short hht, hbb, hbe, hdb, hde, hss;
+ short vft, vbb, vbe, vdb, vde, vss;
+ /* auxiliary information */
+ short mono;
+ short ste_mode;
+ short bpp;
+ } falcon;
+ /* Nothing needed for external mode */
+ } hw;
+} current_par;
+
+/* Don't calculate an own resolution, and thus don't change the one found when
+ * booting (currently used for the Falcon to keep settings for internal video
+ * hardware extensions (e.g. ScreenBlaster) */
+static int DontCalcRes = 0;
+
+#define HHT hw.falcon.hht
+#define HBB hw.falcon.hbb
+#define HBE hw.falcon.hbe
+#define HDB hw.falcon.hdb
+#define HDE hw.falcon.hde
+#define HSS hw.falcon.hss
+#define VFT hw.falcon.vft
+#define VBB hw.falcon.vbb
+#define VBE hw.falcon.vbe
+#define VDB hw.falcon.vdb
+#define VDE hw.falcon.vde
+#define VSS hw.falcon.vss
+#define VCO_CLOCK25 0x04
+#define VCO_CSYPOS 0x10
+#define VCO_VSYPOS 0x20
+#define VCO_HSYPOS 0x40
+#define VCO_SHORTOFFS 0x100
+#define VMO_DOUBLE 0x01
+#define VMO_INTER 0x02
+#define VMO_PREMASK 0x0c
+
+static struct fb_info fb_info;
+
+static unsigned long screen_base; /* base address of screen */
+static unsigned long real_screen_base; /* (only for Overscan) */
+
+static int screen_len;
+
+static int current_par_valid=0;
+
+static int currcon=0;
+
+static int mono_moni=0;
+
+static struct display disp[MAX_NR_CONSOLES];
+
+
+#ifdef ATAFB_EXT
+/* external video handling */
+
+static unsigned external_xres;
+static unsigned external_yres;
+static unsigned external_depth;
+static int external_pmode;
+static unsigned long external_addr = 0;
+static unsigned long external_len;
+static unsigned long external_vgaiobase = 0;
+static unsigned int external_bitspercol = 6;
+
+/*
+++JOE <joe@amber.dinoco.de>:
+added card type for external driver, is only needed for
+colormap handling.
+*/
+
+enum cardtype { IS_VGA, IS_MV300 };
+static enum cardtype external_card_type = IS_VGA;
+
+/*
+The MV300 mixes the color registers. So we need an array of munged
+indices in order to acces the correct reg.
+*/
+static int MV300_reg_1bit[2]={0,1};
+static int MV300_reg_4bit[16]={
+0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+static int MV300_reg_8bit[256]={
+0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 };
+
+static int *MV300_reg = MV300_reg_8bit;
+
+/*
+And on the MV300 it's difficult to read out the hardware palette. So we
+just keep track of the set colors in our own array here, and use that!
+*/
+
+struct { unsigned char red,green,blue,pad; } MV300_color[256];
+#endif /* ATAFB_EXT */
+
+
+int inverse=0;
+
+extern int fontheight_8x8;
+extern int fontwidth_8x8;
+extern unsigned char fontdata_8x8[];
+
+extern int fontheight_8x16;
+extern int fontwidth_8x16;
+extern unsigned char fontdata_8x16[];
+
+/* import first 16 colors from fbcon.c */
+extern unsigned short packed16_cmap[16];
+
+
+/* ++roman: This structure abstracts from the underlying hardware (ST(e),
+ * TT, or Falcon.
+ *
+ * int (*detect)( void )
+ * This function should detect the current video mode settings and
+ * store them in atari_fb_predefined[0] for later reference by the
+ * user. Return the index+1 of an equivalent predefined mode or 0
+ * if there is no such.
+ *
+ * int (*encode_fix)( struct fb_fix_screeninfo *fix,
+ * struct atari_fb_par *par )
+ * This function should fill in the 'fix' structure based on the
+ * values in the 'par' structure.
+ *
+ * int (*decode_var)( struct fb_var_screeninfo *var,
+ * struct atari_fb_par *par )
+ * Get the video params out of 'var'. If a value doesn't fit, round
+ * it up, if it's too big, return EINVAL.
+ * Round up in the following order: bits_per_pixel, xres, yres,
+ * xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields,
+ * horizontal timing, vertical timing.
+ *
+ * int (*encode_var)( struct fb_var_screeninfo *var,
+ * struct atari_fb_par *par );
+ * Fill the 'var' structure based on the values in 'par' and maybe
+ * other values read out of the hardware.
+ *
+ * void (*get_par)( struct atari_fb_par *par )
+ * Fill the hardware's 'par' structure.
+ *
+ * void (*set_par)( struct atari_fb_par *par )
+ * Set the hardware according to 'par'.
+ *
+ * int (*setcolreg)( unsigned regno, unsigned red,
+ * unsigned green, unsigned blue,
+ * unsigned transp )
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ *
+ * int (*getcolreg)( unsigned regno, unsigned *red,
+ * unsigned *green, unsigned *blue,
+ * unsigned *transp )
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ *
+ * void (*set_screen_base)( unsigned long s_base )
+ * Set the base address of the displayed frame buffer. Only called
+ * if yres_virtual > yres or xres_virtual > xres.
+ *
+ * int (*blank)( int blank_mode )
+ * Blank the screen if blank_mode!=0, else unblank. If blank==NULL then
+ * the caller blanks by setting the CLUT to all black. Return 0 if blanking
+ * succeeded, !=0 if un-/blanking failed due to e.g. a video mode which
+ * doesn't support it. Implements VESA suspend and powerdown modes on
+ * hardware that supports disabling hsync/vsync:
+ * blank_mode==2: suspend vsync, 3:suspend hsync, 4: powerdown.
+ */
+
+static struct fb_hwswitch {
+ int (*detect)( void );
+ int (*encode_fix)( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par );
+ int (*decode_var)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par );
+ int (*encode_var)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par );
+ void (*get_par)( struct atari_fb_par *par );
+ void (*set_par)( struct atari_fb_par *par );
+ int (*getcolreg)( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp );
+ int (*setcolreg)( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp );
+ void (*set_screen_base)( unsigned long s_base );
+ int (*blank)( int blank_mode );
+ int (*pan_display)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par);
+} *fbhw;
+
+static char *autodetect_names[] = {"autodetect", NULL};
+static char *stlow_names[] = {"stlow", NULL};
+static char *stmid_names[] = {"stmid", "default5", NULL};
+static char *sthigh_names[] = {"sthigh", "default4", NULL};
+static char *ttlow_names[] = {"ttlow", NULL};
+static char *ttmid_names[]= {"ttmid", "default1", NULL};
+static char *tthigh_names[]= {"tthigh", "default2", NULL};
+static char *vga2_names[] = {"vga2", NULL};
+static char *vga4_names[] = {"vga4", NULL};
+static char *vga16_names[] = {"vga16", "default3", NULL};
+static char *vga256_names[] = {"vga256", NULL};
+static char *falh2_names[] = {"falh2", NULL};
+static char *falh16_names[] = {"falh16", NULL};
+static char *user0_names[] = {"user0", NULL};
+static char *user1_names[] = {"user1", NULL};
+static char *user2_names[] = {"user2", NULL};
+static char *user3_names[] = {"user3", NULL};
+static char *user4_names[] = {"user4", NULL};
+static char *user5_names[] = {"user5", NULL};
+static char *user6_names[] = {"user6", NULL};
+static char *user7_names[] = {"user7", NULL};
+static char *dummy_names[] = {"dummy", NULL};
+
+char **fb_var_names[] = {
+ /* Writing the name arrays directly in this array (via "(char *[]){...}")
+ * crashes gcc 2.5.8 (sigsegv) if the inner array
+ * contains more than two items. I've also seen that all elements
+ * were identical to the last (my cross-gcc) :-(*/
+ autodetect_names,
+ stlow_names,
+ stmid_names,
+ sthigh_names,
+ ttlow_names,
+ ttmid_names,
+ tthigh_names,
+ vga2_names,
+ vga4_names,
+ vga16_names,
+ vga256_names,
+ falh2_names,
+ falh16_names,
+ dummy_names, dummy_names, dummy_names, dummy_names,
+ dummy_names, dummy_names, dummy_names, dummy_names,
+ dummy_names, dummy_names,
+ user0_names,
+ user1_names,
+ user2_names,
+ user3_names,
+ user4_names,
+ user5_names,
+ user6_names,
+ user7_names,
+ NULL
+ /* ,NULL */ /* this causes a sigsegv on my gcc-2.5.8 */
+};
+
+struct fb_var_screeninfo atari_fb_predefined[] = {
+ /*
+ * yres_virtual==0 means use hw-scrolling if possible, else yres
+ */
+ { /* autodetect */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* xres-grayscale */
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, /* red green blue tran*/
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st low */
+ 320, 200, 320, 0, 0, 0, 4, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st mid */
+ 640, 200, 640, 0, 0, 0, 2, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st high */
+ 640, 400, 640, 0, 0, 0, 1, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt low */
+ 320, 480, 320, 0, 0, 0, 8, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt mid */
+ 640, 480, 640, 0, 0, 0, 4, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt high */
+ 1280, 960, 1280, 0, 0, 0, 1, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga2 */
+ 640, 480, 640, 0, 0, 0, 1, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga4 */
+ 640, 480, 640, 0, 0, 0, 2, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga16 */
+ 640, 480, 640, 0, 0, 0, 4, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga256 */
+ 640, 480, 640, 0, 0, 0, 8, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* falh2 */
+ 896, 608, 896, 0, 0, 0, 1, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* falh16 */
+ 896, 608, 896, 0, 0, 0, 4, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* Minor 14..23 free for more standard video modes */
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ /* Minor 24..31 reserved for user defined video modes */
+ { /* user0, initialized to Rx;y;d from commandline, if supplied */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user1 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user2 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user3 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user4 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user5 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user6 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user7 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+int num_atari_fb_predefined=arraysize(atari_fb_predefined);
+
+
+static int
+get_video_mode(char *vname)
+{
+ char ***name_list;
+ char **name;
+ int i;
+ name_list=fb_var_names;
+ for (i = 0 ; i < num_atari_fb_predefined ; i++) {
+ name=*(name_list++);
+ if (! name || ! *name)
+ break;
+ while (*name) {
+ if (! strcmp(vname, *name))
+ return i+1;
+ name++;
+ }
+ }
+ return 0;
+}
+
+
+
+/* ------------------- TT specific functions ---------------------- */
+
+#ifdef ATAFB_TT
+
+static int tt_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ int mode, i;
+
+ strcpy(fix->id,"Atari Builtin");
+ fix->smem_start=real_screen_base;
+ fix->smem_len = screen_len;
+ fix->type=FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux=2;
+ fix->visual=FB_VISUAL_PSEUDOCOLOR;
+ mode = par->hw.tt.mode & TT_SHIFTER_MODEMASK;
+ if (mode == TT_SHIFTER_TTHIGH || mode == TT_SHIFTER_STHIGH) {
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->type_aux=0;
+ if (mode == TT_SHIFTER_TTHIGH)
+ fix->visual=FB_VISUAL_MONO01;
+ }
+ fix->xpanstep=0;
+ fix->ypanstep=1;
+ fix->ywrapstep=0;
+ fix->line_length = 0;
+ for (i=0; i<arraysize(fix->reserved); i++)
+ fix->reserved[i]=0;
+ return 0;
+}
+
+
+static int tt_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xres=var->xres;
+ int yres=var->yres;
+ int bpp=var->bits_per_pixel;
+ int linelen;
+ int yres_virtual = var->yres_virtual;
+
+ if (mono_moni) {
+ if (bpp > 1 || xres > sttt_xres*2 || yres >tt_yres*2)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_TTHIGH;
+ xres=sttt_xres*2;
+ yres=tt_yres*2;
+ bpp=1;
+ } else {
+ if (bpp > 8 || xres > sttt_xres || yres > tt_yres)
+ return -EINVAL;
+ if (bpp > 4) {
+ if (xres > sttt_xres/2 || yres > tt_yres)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_TTLOW;
+ xres=sttt_xres/2;
+ yres=tt_yres;
+ bpp=8;
+ }
+ else if (bpp > 2) {
+ if (xres > sttt_xres || yres > tt_yres)
+ return -EINVAL;
+ if (xres > sttt_xres/2 || yres > st_yres/2) {
+ par->hw.tt.mode=TT_SHIFTER_TTMID;
+ xres=sttt_xres;
+ yres=tt_yres;
+ bpp=4;
+ }
+ else {
+ par->hw.tt.mode=TT_SHIFTER_STLOW;
+ xres=sttt_xres/2;
+ yres=st_yres/2;
+ bpp=4;
+ }
+ }
+ else if (bpp > 1) {
+ if (xres > sttt_xres || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_STMID;
+ xres=sttt_xres;
+ yres=st_yres/2;
+ bpp=2;
+ }
+ else if (var->xres > sttt_xres || var->yres > st_yres) {
+ return -EINVAL;
+ }
+ else {
+ par->hw.tt.mode=TT_SHIFTER_STHIGH;
+ xres=sttt_xres;
+ yres=st_yres;
+ bpp=1;
+ }
+ }
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+ if (var->sync & FB_SYNC_EXT)
+ par->hw.tt.sync=0;
+ else
+ par->hw.tt.sync=1;
+ linelen=xres*bpp/8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base = screen_base + var->yoffset * linelen;
+ return 0;
+}
+
+static int tt_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int linelen, i;
+ var->red.offset=0;
+ var->red.length=4;
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these may be incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ if (par->hw.tt.sync & 1)
+ var->sync=0;
+ else
+ var->sync=FB_SYNC_EXT;
+
+ switch (par->hw.tt.mode & TT_SHIFTER_MODEMASK) {
+ case TT_SHIFTER_STLOW:
+ var->xres=sttt_xres/2;
+ var->xres_virtual=sttt_xres_virtual/2;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=4;
+ break;
+ case TT_SHIFTER_STMID:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=2;
+ break;
+ case TT_SHIFTER_STHIGH:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=st_yres;
+ var->bits_per_pixel=1;
+ break;
+ case TT_SHIFTER_TTLOW:
+ var->xres=sttt_xres/2;
+ var->xres_virtual=sttt_xres_virtual/2;
+ var->yres=tt_yres;
+ var->bits_per_pixel=8;
+ break;
+ case TT_SHIFTER_TTMID:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=tt_yres;
+ var->bits_per_pixel=4;
+ break;
+ case TT_SHIFTER_TTHIGH:
+ var->red.length=0;
+ var->xres=sttt_xres*2;
+ var->xres_virtual=sttt_xres_virtual*2;
+ var->yres=tt_yres*2;
+ var->bits_per_pixel=1;
+ break;
+ }
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ linelen=var->xres_virtual * var->bits_per_pixel / 8;
+ if (! use_hwscroll)
+ var->yres_virtual=var->yres;
+ else if (screen_len)
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0;
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void tt_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ par->hw.tt.mode=shifter_tt.tt_shiftmode;
+ par->hw.tt.sync=shifter.syncmode;
+ addr = ((shifter.bas_hi & 0xff) << 16) |
+ ((shifter.bas_md & 0xff) << 8) |
+ ((shifter.bas_lo & 0xff));
+ par->screen_base = PTOV(addr);
+}
+
+static void tt_set_par( struct atari_fb_par *par )
+{
+ shifter_tt.tt_shiftmode=par->hw.tt.mode;
+ shifter.syncmode=par->hw.tt.sync;
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+}
+
+
+static int tt_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
+ regno += 254;
+ if (regno > 255)
+ return 1;
+ *blue = tt_palette[regno];
+ *green = (*blue >> 4) & 0xf;
+ *red = (*blue >> 8) & 0xf;
+ *blue &= 0xf;
+ *transp = 0;
+ return 0;
+}
+
+
+static int tt_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
+ regno += 254;
+ if (regno > 255)
+ return 1;
+ tt_palette[regno] = (red << 8) | (green << 4) | blue;
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) ==
+ TT_SHIFTER_STHIGH && regno == 254)
+ tt_palette[0] = 0;
+ return 0;
+}
+
+
+static int tt_detect( void )
+
+{ struct atari_fb_par par;
+
+ /* Determine the connected monitor: The DMA sound must be
+ * disabled before reading the MFP GPIP, because the Sound
+ * Done Signal and the Monochrome Detect are XORed together!
+ *
+ * Even on a TT, we should look if there is a DMA sound. It was
+ * announced that the Eagle is TT compatible, but only the PCM is
+ * missing...
+ */
+ if (ATARIHW_PRESENT(PCM_8BIT)) {
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ udelay(20); /* wait a while for things to settle down */
+ }
+ mono_moni = (mfp.par_dt_reg & 0x80) == 0;
+
+ tt_get_par(&par);
+ tt_encode_var(&atari_fb_predefined[0], &par);
+
+ return 1;
+}
+
+#endif /* ATAFB_TT */
+
+/* ------------------- Falcon specific functions ---------------------- */
+
+#ifdef ATAFB_FALCON
+
+static int mon_type; /* Falcon connected monitor */
+static int f030_bus_width; /* Falcon ram bus width (for vid_control) */
+#define F_MON_SM 0
+#define F_MON_SC 1
+#define F_MON_VGA 2
+#define F_MON_TV 3
+
+/* Multisync monitor capabilities */
+/* Atari-TOS defaults if no boot option present */
+static long vfmin=58, vfmax=62, hfmin=31000, hfmax=32000;
+
+static struct pixel_clock {
+ unsigned long f; /* f/[Hz] */
+ unsigned long t; /* t/[ps] (=1/f) */
+ int right, hsync, left; /* standard timing in clock cycles, not pixel */
+ /* hsync initialized in falcon_detect() */
+ int sync_mask; /* or-mask for hw.falcon.sync to set this clock */
+ int control_mask; /* ditto, for hw.falcon.vid_control */
+}
+f25 = {25175000, 39721, 18, 0, 42, 0x0, VCO_CLOCK25},
+f32 = {32000000, 31250, 18, 0, 42, 0x0, 0},
+fext = { 0, 0, 18, 0, 42, 0x1, 0};
+
+/* VIDEL-prescale values [mon_type][pixel_length from VCO] */
+static int vdl_prescale[4][3] = {{4,2,1}, {4,2,1}, {4,2,2}, {4,2,1}};
+
+/* Default hsync timing [mon_type] in picoseconds */
+static long h_syncs[4] = {3000000, 4875000, 4000000, 4875000};
+
+
+static inline int hxx_prescale(struct falcon_hw *hw)
+{
+ return hw->ste_mode ? 16 :
+ vdl_prescale[mon_type][hw->vid_mode >> 2 & 0x3];
+}
+
+static int falcon_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+{
+ int i;
+
+ strcpy(fix->id, "Atari Builtin");
+ fix->smem_start = real_screen_base;
+ fix->smem_len = screen_len;
+ fix->type = FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux = 2;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ if (par->hw.falcon.mono) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ /* no smooth scrolling with longword aligned video mem */
+ fix->xpanstep = 32;
+ }
+ else if (par->hw.falcon.f_shift & 0x100) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ /* Is this ok or should it be DIRECTCOLOR? */
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 2;
+ }
+ fix->line_length = 0;
+ for (i=0; i<arraysize(fix->reserved); i++)
+ fix->reserved[i]=0;
+ return 0;
+}
+
+
+static int falcon_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int bpp = var->bits_per_pixel;
+ int xres = var->xres;
+ int yres = var->yres;
+ int xres_virtual = var->xres_virtual;
+ int yres_virtual = var->yres_virtual;
+ int left_margin, right_margin, hsync_len;
+ int upper_margin, lower_margin, vsync_len;
+ int linelen;
+ int interlace = 0, doubleline = 0;
+ struct pixel_clock *pclock;
+ int plen; /* width of pixel in clock cycles */
+ int xstretch;
+ int prescale;
+ int longoffset = 0;
+ int hfreq, vfreq;
+
+/*
+ Get the video params out of 'var'. If a value doesn't fit, round
+ it up, if it's too big, return EINVAL.
+ Round up in the following order: bits_per_pixel, xres, yres,
+ xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields,
+ horizontal timing, vertical timing.
+
+ There is a maximum of screen resolution determined by pixelclock
+ and minimum frame rate -- (X+hmarg.)*(Y+vmarg.)*vfmin <= pixelclock.
+ In interlace mode this is " * " *vfmin <= pixelclock.
+ Additional constraints: hfreq.
+ Frequency range for multisync monitors is given via command line.
+ For TV and SM124 both frequencies are fixed.
+
+ X % 16 == 0 to fit 8x?? font (except 1 bitplane modes must use X%32==0)
+ Y % 16 == 0 to fit 8x16 font
+ Y % 8 == 0 if Y<400
+
+ Currently interlace and doubleline mode in var are ignored.
+ On SM124 and TV only the standard resolutions can be used.
+*/
+
+ /* Reject uninitialized mode */
+ if (!xres || !yres || !bpp)
+ return -EINVAL;
+
+ if (mon_type == F_MON_SM && bpp != 1) {
+ return -EINVAL;
+ }
+ else if (bpp <= 1) {
+ bpp = 1;
+ par->hw.falcon.f_shift = 0x400;
+ par->hw.falcon.st_shift = 0x200;
+ }
+ else if (bpp <= 2) {
+ bpp = 2;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x100;
+ }
+ else if (bpp <= 4) {
+ bpp = 4;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x000;
+ }
+ else if (bpp <= 8) {
+ bpp = 8;
+ par->hw.falcon.f_shift = 0x010;
+ }
+ else if (bpp <= 16) {
+ bpp = 16; /* packed pixel mode */
+ par->hw.falcon.f_shift = 0x100; /* hicolor, no overlay */
+ }
+ else
+ return -EINVAL;
+ par->hw.falcon.bpp = bpp;
+
+ if (mon_type == F_MON_SM || DontCalcRes) {
+ /* Skip all calculations. VGA/TV/SC1224 only supported. */
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+
+ if (bpp > myvar->bits_per_pixel ||
+ var->xres > myvar->xres ||
+ var->yres > myvar->yres)
+ return -EINVAL;
+ fbhw->get_par(par); /* Current par will be new par */
+ goto set_screen_base; /* Don't forget this */
+ }
+
+ /* Only some fixed resolutions < 640x400 */
+ if (xres <= 320)
+ xres = 320;
+ else if (xres <= 640 && bpp != 16)
+ xres = 640;
+ if (yres <= 200)
+ yres = 200;
+ else if (yres <= 240)
+ yres = 240;
+ else if (yres <= 400)
+ yres = 400;
+
+ /* 2 planes must use STE compatibility mode */
+ par->hw.falcon.ste_mode = bpp==2;
+ par->hw.falcon.mono = bpp==1;
+
+ /* Total and visible scanline length must be a multiple of one longword,
+ * this and the console fontwidth yields the alignment for xres and
+ * xres_virtual.
+ * TODO: this way "odd" fontheights are not supported
+ *
+ * Special case in STE mode: blank and graphic positions don't align,
+ * avoid trash at right margin
+ */
+ if (par->hw.falcon.ste_mode)
+ xres = (xres + 63) & ~63;
+ else if (bpp == 1)
+ xres = (xres + 31) & ~31;
+ else
+ xres = (xres + 15) & ~15;
+ if (yres >= 400)
+ yres = (yres + 15) & ~15;
+ else
+ yres = (yres + 7) & ~7;
+
+ if (xres_virtual < xres)
+ xres_virtual = xres;
+ else if (bpp == 1)
+ xres_virtual = (xres_virtual + 31) & ~31;
+ else
+ xres_virtual = (xres_virtual + 15) & ~15;
+
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+
+ /* backward bug-compatibility */
+ if (var->pixclock > 1)
+ var->pixclock -= 1;
+
+ par->hw.falcon.line_width = bpp * xres / 16;
+ par->hw.falcon.line_offset = bpp * (xres_virtual - xres) / 16;
+
+ /* single or double pixel width */
+ xstretch = (xres < 640) ? 2 : 1;
+
+#if 0 /* SM124 supports only 640x400, this is rejected above */
+ if (mon_type == F_MON_SM) {
+ if (xres != 640 && yres != 400)
+ return -EINVAL;
+ plen = 1;
+ pclock = &f32;
+ /* SM124-mode is special */
+ par->hw.falcon.ste_mode = 1;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x200;
+ left_margin = hsync_len = 128 / plen;
+ right_margin = 0;
+ /* TODO set all margins */
+ }
+ else
+#endif
+ if (mon_type == F_MON_SC || mon_type == F_MON_TV) {
+ plen = 2 * xstretch;
+ if (var->pixclock > f32.t * plen)
+ return -EINVAL;
+ pclock = &f32;
+ if (yres > 240)
+ interlace = 1;
+ if (var->pixclock == 0) {
+ /* set some minimal margins which center the screen */
+ left_margin = 32;
+ right_margin = 18;
+ hsync_len = pclock->hsync / plen;
+ upper_margin = 31;
+ lower_margin = 14;
+ vsync_len = interlace ? 3 : 4;
+ } else {
+ left_margin = var->left_margin;
+ right_margin = var->right_margin;
+ hsync_len = var->hsync_len;
+ upper_margin = var->upper_margin;
+ lower_margin = var->lower_margin;
+ vsync_len = var->vsync_len;
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ upper_margin = (upper_margin + 1) / 2;
+ lower_margin = (lower_margin + 1) / 2;
+ vsync_len = (vsync_len + 1) / 2;
+ } else if (var->vmode & FB_VMODE_DOUBLE) {
+ upper_margin *= 2;
+ lower_margin *= 2;
+ vsync_len *= 2;
+ }
+ }
+ }
+ else
+ { /* F_MON_VGA */
+ if (bpp == 16)
+ xstretch = 2; /* Double pixel width only for hicolor */
+ /* Default values are used for vert./hor. timing if no pixelclock given. */
+ if (var->pixclock == 0) {
+ int linesize;
+
+ /* Choose master pixelclock depending on hor. timing */
+ plen = 1 * xstretch;
+ if ((plen * xres + f25.right+f25.hsync+f25.left) * hfmin < f25.f)
+ pclock = &f25;
+ else if ((plen * xres + f32.right+f32.hsync+f32.left) * hfmin < f32.f)
+ pclock = &f32;
+ else if ((plen * xres + fext.right+fext.hsync+fext.left) * hfmin < fext.f
+ && fext.f)
+ pclock = &fext;
+ else
+ return -EINVAL;
+
+ left_margin = pclock->left / plen;
+ right_margin = pclock->right / plen;
+ hsync_len = pclock->hsync / plen;
+ linesize = left_margin + xres + right_margin + hsync_len;
+ upper_margin = 31;
+ lower_margin = 11;
+ vsync_len = 3;
+ }
+ else {
+ /* Choose largest pixelclock <= wanted clock */
+ int i;
+ unsigned long pcl = ULONG_MAX;
+ pclock = 0;
+ for (i=1; i <= 4; i *= 2) {
+ if (f25.t*i >= var->pixclock && f25.t*i < pcl) {
+ pcl = f25.t * i;
+ pclock = &f25;
+ }
+ if (f32.t*i >= var->pixclock && f32.t*i < pcl) {
+ pcl = f32.t * i;
+ pclock = &f32;
+ }
+ if (fext.t && fext.t*i >= var->pixclock && fext.t*i < pcl) {
+ pcl = fext.t * i;
+ pclock = &fext;
+ }
+ }
+ if (!pclock)
+ return -EINVAL;
+ plen = pcl / pclock->t;
+
+ left_margin = var->left_margin;
+ right_margin = var->right_margin;
+ hsync_len = var->hsync_len;
+ upper_margin = var->upper_margin;
+ lower_margin = var->lower_margin;
+ vsync_len = var->vsync_len;
+ /* Internal unit is [single lines per (half-)frame] */
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ /* # lines in half frame */
+ /* External unit is [lines per full frame] */
+ upper_margin = (upper_margin + 1) / 2;
+ lower_margin = (lower_margin + 1) / 2;
+ vsync_len = (vsync_len + 1) / 2;
+ }
+ else if (var->vmode & FB_VMODE_DOUBLE) {
+ /* External unit is [double lines per frame] */
+ upper_margin *= 2;
+ lower_margin *= 2;
+ vsync_len *= 2;
+ }
+ }
+ if (pclock == &fext)
+ longoffset = 1; /* VIDEL doesn't synchronize on short offset */
+ }
+ /* Is video bus bandwidth (32MB/s) too low for this resolution? */
+ /* this is definitely wrong if bus clock != 32MHz */
+ if (pclock->f / plen / 8 * bpp > 32000000L)
+ return -EINVAL;
+
+ if (vsync_len < 1)
+ vsync_len = 1;
+
+ /* include sync lengths in right/lower margin for all calculations */
+ right_margin += hsync_len;
+ lower_margin += vsync_len;
+
+ /* ! In all calculations of margins we use # of lines in half frame
+ * (which is a full frame in non-interlace mode), so we can switch
+ * between interlace and non-interlace without messing around
+ * with these.
+ */
+ again:
+ /* Set base_offset 128 and video bus width */
+ par->hw.falcon.vid_control = mon_type | f030_bus_width;
+ if (!longoffset)
+ par->hw.falcon.vid_control |= VCO_SHORTOFFS; /* base_offset 64 */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->hw.falcon.vid_control |= VCO_HSYPOS;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->hw.falcon.vid_control |= VCO_VSYPOS;
+ /* Pixelclock */
+ par->hw.falcon.vid_control |= pclock->control_mask;
+ /* External or internal clock */
+ par->hw.falcon.sync = pclock->sync_mask | 0x2;
+ /* Pixellength and prescale */
+ par->hw.falcon.vid_mode = (2/plen) << 2;
+ if (doubleline)
+ par->hw.falcon.vid_mode |= VMO_DOUBLE;
+ if (interlace)
+ par->hw.falcon.vid_mode |= VMO_INTER;
+
+ /*********************
+ Horizontal timing: unit = [master clock cycles]
+ unit of hxx-registers: [master clock cycles * prescale]
+ Hxx-registers are 9 bit wide
+
+ 1 line = ((hht + 2) * 2 * prescale) clock cycles
+
+ graphic output = hdb & 0x200 ?
+ ((hht+2)*2 - hdb + hde) * prescale - hdboff + hdeoff:
+ ( hht + 2 - hdb + hde) * prescale - hdboff + hdeoff
+ (this must be a multiple of plen*128/bpp, on VGA pixels
+ to the right may be cut off with a bigger right margin)
+
+ start of graphics relative to start of 1st halfline = hdb & 0x200 ?
+ (hdb - hht - 2) * prescale + hdboff :
+ hdb * prescale + hdboff
+
+ end of graphics relative to start of 1st halfline =
+ (hde + hht + 2) * prescale + hdeoff
+ *********************/
+ /* Calculate VIDEL registers */
+ {
+ int hdb_off, hde_off, base_off;
+ int gstart, gend1, gend2, align;
+
+ prescale = hxx_prescale(&par->hw.falcon);
+ base_off = par->hw.falcon.vid_control & VCO_SHORTOFFS ? 64 : 128;
+
+ /* Offsets depend on video mode */
+ /* Offsets are in clock cycles, divide by prescale to
+ * calculate hd[be]-registers
+ */
+ if (par->hw.falcon.f_shift & 0x100) {
+ align = 1;
+ hde_off = 0;
+ hdb_off = (base_off + 16 * plen) + prescale;
+ }
+ else {
+ align = 128 / bpp;
+ hde_off = ((128 / bpp + 2) * plen);
+ if (par->hw.falcon.ste_mode)
+ hdb_off = (64 + base_off + (128 / bpp + 2) * plen) + prescale;
+ else
+ hdb_off = (base_off + (128 / bpp + 18) * plen) + prescale;
+ }
+
+ gstart = (prescale/2 + plen * left_margin) / prescale;
+ /* gend1 is for hde (gend-gstart multiple of align), shifter's xres */
+ gend1 = gstart + ((xres + align-1) / align)*align * plen / prescale;
+ /* gend2 is for hbb, visible xres (rest to gend1 is cut off by hblank) */
+ gend2 = gstart + xres * plen / prescale;
+ par->HHT = plen * (left_margin + xres + right_margin) /
+ (2 * prescale) - 2;
+/* par->HHT = (gend2 + plen * right_margin / prescale) / 2 - 2;*/
+
+ par->HDB = gstart - hdb_off/prescale;
+ par->HBE = gstart;
+ if (par->HDB < 0) par->HDB += par->HHT + 2 + 0x200;
+ par->HDE = gend1 - par->HHT - 2 - hde_off/prescale;
+ par->HBB = gend2 - par->HHT - 2;
+#if 0
+ /* One more Videl constraint: data fetch of two lines must not overlap */
+ if (par->HDB & 0x200 && par->HDB & ~0x200 - par->HDE <= 5) {
+ /* if this happens increase margins, decrease hfreq. */
+ }
+#endif
+ if (hde_off % prescale)
+ par->HBB++; /* compensate for non matching hde and hbb */
+ par->HSS = par->HHT + 2 - plen * hsync_len / prescale;
+ if (par->HSS < par->HBB)
+ par->HSS = par->HBB;
+ }
+
+ /* check hor. frequency */
+ hfreq = pclock->f / ((par->HHT+2)*prescale*2);
+ if (hfreq > hfmax && mon_type!=F_MON_VGA) {
+ /* ++guenther: ^^^^^^^^^^^^^^^^^^^ can't remember why I did this */
+ /* Too high -> enlarge margin */
+ left_margin += 1;
+ right_margin += 1;
+ goto again;
+ }
+ if (hfreq > hfmax || hfreq < hfmin)
+ return -EINVAL;
+
+ /* Vxx-registers */
+ /* All Vxx must be odd in non-interlace, since frame starts in the middle
+ * of the first displayed line!
+ * One frame consists of VFT+1 half lines. VFT+1 must be even in
+ * non-interlace, odd in interlace mode for synchronisation.
+ * Vxx-registers are 11 bit wide
+ */
+ par->VBE = (upper_margin * 2 + 1); /* must begin on odd halfline */
+ par->VDB = par->VBE;
+ par->VDE = yres;
+ if (!interlace) par->VDE <<= 1;
+ if (doubleline) par->VDE <<= 1; /* VDE now half lines per (half-)frame */
+ par->VDE += par->VDB;
+ par->VBB = par->VDE;
+ par->VFT = par->VBB + (lower_margin * 2 - 1) - 1;
+ par->VSS = par->VFT+1 - (vsync_len * 2 - 1);
+ /* vbb,vss,vft must be even in interlace mode */
+ if (interlace) {
+ par->VBB++;
+ par->VSS++;
+ par->VFT++;
+ }
+
+ /* V-frequency check, hope I didn't create any loop here. */
+ /* Interlace and doubleline are mutually exclusive. */
+ vfreq = (hfreq * 2) / (par->VFT + 1);
+ if (vfreq > vfmax && !doubleline && !interlace) {
+ /* Too high -> try again with doubleline */
+ doubleline = 1;
+ goto again;
+ }
+ else if (vfreq < vfmin && !interlace && !doubleline) {
+ /* Too low -> try again with interlace */
+ interlace = 1;
+ goto again;
+ }
+ else if (vfreq < vfmin && doubleline) {
+ /* Doubleline too low -> clear doubleline and enlarge margins */
+ int lines;
+ doubleline = 0;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines-2*yres)>vfmax; lines++)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq > vfmax && doubleline) {
+ /* Doubleline too high -> enlarge margins */
+ int lines;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines+=2)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq > vfmax && interlace) {
+ /* Interlace, too high -> enlarge margins */
+ int lines;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines++)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq < vfmin || vfreq > vfmax)
+ return -EINVAL;
+
+ set_screen_base:
+ linelen = xres_virtual * bpp / 8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base = screen_base + var->yoffset * linelen;
+ par->hw.falcon.xoffset = 0;
+
+ return 0;
+}
+
+static int falcon_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+/* !!! only for VGA !!! */
+ int linelen, i;
+ int prescale, plen;
+ int hdb_off, hde_off, base_off;
+ struct falcon_hw *hw = &par->hw.falcon;
+
+ /* possible frequencies: 25.175 or 32MHz */
+ var->pixclock = hw->sync & 0x1 ? fext.t :
+ hw->vid_control & VCO_CLOCK25 ? f25.t : f32.t;
+
+ var->height=-1;
+ var->width=-1;
+
+ var->sync=0;
+ if (hw->vid_control & VCO_HSYPOS)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (hw->vid_control & VCO_VSYPOS)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+ var->vmode = FB_VMODE_NONINTERLACED;
+ if (hw->vid_mode & VMO_INTER)
+ var->vmode |= FB_VMODE_INTERLACED;
+ if (hw->vid_mode & VMO_DOUBLE)
+ var->vmode |= FB_VMODE_DOUBLE;
+
+ /* visible y resolution:
+ * Graphics display starts at line VDB and ends at line
+ * VDE. If interlace mode off unit of VC-registers is
+ * half lines, else lines.
+ */
+ var->yres = hw->vde - hw->vdb;
+ if (!(var->vmode & FB_VMODE_INTERLACED))
+ var->yres >>= 1;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ var->yres >>= 1;
+
+ /* to get bpp, we must examine f_shift and st_shift.
+ * f_shift is valid if any of bits no. 10, 8 or 4
+ * is set. Priority in f_shift is: 10 ">" 8 ">" 4, i.e.
+ * if bit 10 set then bit 8 and bit 4 don't care...
+ * If all these bits are 0 get display depth from st_shift
+ * (as for ST and STE)
+ */
+ if (hw->f_shift & 0x400) /* 2 colors */
+ var->bits_per_pixel = 1;
+ else if (hw->f_shift & 0x100) /* hicolor */
+ var->bits_per_pixel = 16;
+ else if (hw->f_shift & 0x010) /* 8 bitplanes */
+ var->bits_per_pixel = 8;
+ else if (hw->st_shift == 0)
+ var->bits_per_pixel = 4;
+ else if (hw->st_shift == 0x100)
+ var->bits_per_pixel = 2;
+ else /* if (hw->st_shift == 0x200) */
+ var->bits_per_pixel = 1;
+
+ var->xres = hw->line_width * 16 / var->bits_per_pixel;
+ var->xres_virtual = var->xres + hw->line_offset * 16 / var->bits_per_pixel;
+ if (hw->xoffset)
+ var->xres_virtual += 16;
+
+ if (var->bits_per_pixel == 16) {
+ var->red.offset=11;
+ var->red.length=5;
+ var->red.msb_right=0;
+ var->green.offset=5;
+ var->green.length=6;
+ var->green.msb_right=0;
+ var->blue.offset=0;
+ var->blue.length=5;
+ var->blue.msb_right=0;
+ }
+ else {
+ var->red.offset=0;
+ var->red.length = hw->ste_mode ? 4 : 6;
+ var->red.msb_right=0;
+ var->grayscale=0;
+ var->blue=var->green=var->red;
+ }
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+
+ linelen = var->xres_virtual * var->bits_per_pixel / 8;
+ if (screen_len)
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0; /* TODO change this */
+
+ /* hdX-offsets */
+ prescale = hxx_prescale(hw);
+ plen = 4 >> (hw->vid_mode >> 2 & 0x3);
+ base_off = hw->vid_control & VCO_SHORTOFFS ? 64 : 128;
+ if (hw->f_shift & 0x100) {
+ hde_off = 0;
+ hdb_off = (base_off + 16 * plen) + prescale;
+ }
+ else {
+ hde_off = ((128 / var->bits_per_pixel + 2) * plen);
+ if (hw->ste_mode)
+ hdb_off = (64 + base_off + (128 / var->bits_per_pixel + 2) * plen)
+ + prescale;
+ else
+ hdb_off = (base_off + (128 / var->bits_per_pixel + 18) * plen)
+ + prescale;
+ }
+
+ /* Right margin includes hsync */
+ var->left_margin = hdb_off + prescale * ((hw->hdb & 0x1ff) -
+ (hw->hdb & 0x200 ? 2+hw->hht : 0));
+ if (hw->ste_mode || mon_type!=F_MON_VGA)
+ var->right_margin = prescale * (hw->hht + 2 - hw->hde) - hde_off;
+ else
+ /* can't use this in ste_mode, because hbb is +1 off */
+ var->right_margin = prescale * (hw->hht + 2 - hw->hbb);
+ var->hsync_len = prescale * (hw->hht + 2 - hw->hss);
+
+ /* Lower margin includes vsync */
+ var->upper_margin = hw->vdb / 2 ; /* round down to full lines */
+ var->lower_margin = (hw->vft+1 - hw->vde + 1) / 2; /* round up */
+ var->vsync_len = (hw->vft+1 - hw->vss + 1) / 2; /* round up */
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ var->upper_margin *= 2;
+ var->lower_margin *= 2;
+ var->vsync_len *= 2;
+ }
+ else if (var->vmode & FB_VMODE_DOUBLE) {
+ var->upper_margin = (var->upper_margin + 1) / 2;
+ var->lower_margin = (var->lower_margin + 1) / 2;
+ var->vsync_len = (var->vsync_len + 1) / 2;
+ }
+
+ var->pixclock *= plen;
+ var->left_margin /= plen;
+ var->right_margin /= plen;
+ var->hsync_len /= plen;
+
+ var->right_margin -= var->hsync_len;
+ var->lower_margin -= var->vsync_len;
+
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0; /* what is this for? */
+ var->activate=0;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static int f_change_mode = 0;
+static struct falcon_hw f_new_mode;
+static int f_pan_display = 0;
+
+static void falcon_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ struct falcon_hw *hw = &par->hw.falcon;
+
+ hw->line_width = shifter_f030.scn_width;
+ hw->line_offset = shifter_f030.off_next;
+ hw->st_shift = videl.st_shift & 0x300;
+ hw->f_shift = videl.f_shift;
+ hw->vid_control = videl.control;
+ hw->vid_mode = videl.mode;
+ hw->sync = shifter.syncmode & 0x1;
+ hw->xoffset = videl.xoffset & 0xf;
+ hw->hht = videl.hht;
+ hw->hbb = videl.hbb;
+ hw->hbe = videl.hbe;
+ hw->hdb = videl.hdb;
+ hw->hde = videl.hde;
+ hw->hss = videl.hss;
+ hw->vft = videl.vft;
+ hw->vbb = videl.vbb;
+ hw->vbe = videl.vbe;
+ hw->vdb = videl.vdb;
+ hw->vde = videl.vde;
+ hw->vss = videl.vss;
+
+ addr = (shifter.bas_hi & 0xff) << 16 |
+ (shifter.bas_md & 0xff) << 8 |
+ (shifter.bas_lo & 0xff);
+ par->screen_base = PTOV(addr);
+
+ /* derived parameters */
+ hw->ste_mode = (hw->f_shift & 0x510)==0 && hw->st_shift==0x100;
+ hw->mono = (hw->f_shift & 0x400) ||
+ ((hw->f_shift & 0x510)==0 && hw->st_shift==0x200);
+}
+
+static void falcon_set_par( struct atari_fb_par *par )
+{
+ f_change_mode = 0;
+
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+
+ /* Don't touch any other registers if we keep the default resolution */
+ if (DontCalcRes)
+ return;
+
+ /* Tell vbl-handler to change video mode.
+ * We change modes only on next VBL, to avoid desynchronisation
+ * (a shift to the right and wrap around by a random number of pixels
+ * in all monochrome modes).
+ * This seems to work on my Falcon.
+ */
+ f_new_mode = par->hw.falcon;
+ f_change_mode = 1;
+}
+
+
+static void falcon_vbl_switcher( int irq, void *dummy, struct pt_regs *fp )
+{
+ struct falcon_hw *hw = &f_new_mode;
+
+ if (f_change_mode) {
+ f_change_mode = 0;
+
+ if (hw->sync & 0x1) {
+ /* Enable external pixelclock. This code only for ScreenWonder */
+ *(volatile unsigned short*)0xffff9202 = 0xffbf;
+ }
+ else {
+ /* Turn off external clocks. Read sets all output bits to 1. */
+ *(volatile unsigned short*)0xffff9202;
+ }
+ shifter.syncmode = hw->sync;
+
+ videl.hht = hw->hht;
+ videl.hbb = hw->hbb;
+ videl.hbe = hw->hbe;
+ videl.hdb = hw->hdb;
+ videl.hde = hw->hde;
+ videl.hss = hw->hss;
+ videl.vft = hw->vft;
+ videl.vbb = hw->vbb;
+ videl.vbe = hw->vbe;
+ videl.vdb = hw->vdb;
+ videl.vde = hw->vde;
+ videl.vss = hw->vss;
+
+ videl.f_shift = 0; /* write enables Falcon palette, 0: 4 planes */
+ if (hw->ste_mode) {
+ videl.st_shift = hw->st_shift; /* write enables STE palette */
+ }
+ else {
+ /* IMPORTANT:
+ * set st_shift 0, so we can tell the screen-depth if f_shift==0.
+ * Writing 0 to f_shift enables 4 plane Falcon mode but
+ * doesn't set st_shift. st_shift!=0 (!=4planes) is impossible
+ * with Falcon palette.
+ */
+ videl.st_shift = 0;
+ /* now back to Falcon palette mode */
+ videl.f_shift = hw->f_shift;
+ }
+ /* writing to st_shift changed scn_width and vid_mode */
+ videl.xoffset = hw->xoffset;
+ shifter_f030.scn_width = hw->line_width;
+ shifter_f030.off_next = hw->line_offset;
+ videl.control = hw->vid_control;
+ videl.mode = hw->vid_mode;
+ }
+ if (f_pan_display) {
+ f_pan_display = 0;
+ videl.xoffset = current_par.hw.falcon.xoffset;
+ shifter_f030.off_next = current_par.hw.falcon.line_offset;
+ }
+}
+
+
+static int falcon_pan_display( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xoffset;
+ int bpp = disp[currcon].var.bits_per_pixel;
+
+ if (bpp == 1)
+ var->xoffset = up(var->xoffset, 32);
+ if (bpp != 16)
+ par->hw.falcon.xoffset = var->xoffset & 15;
+ else {
+ par->hw.falcon.xoffset = 0;
+ var->xoffset = up(var->xoffset, 2);
+ }
+ par->hw.falcon.line_offset = bpp *
+ (disp[currcon].var.xres_virtual - disp[currcon].var.xres) / 16;
+ if (par->hw.falcon.xoffset)
+ par->hw.falcon.line_offset -= bpp;
+ xoffset = var->xoffset - par->hw.falcon.xoffset;
+
+ par->screen_base = screen_base +
+ (var->yoffset * disp[currcon].var.xres_virtual + xoffset) * bpp / 8;
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base (par->screen_base);
+ else
+ return -EINVAL; /* shouldn't happen */
+ f_pan_display = 1;
+ return 0;
+}
+
+
+static int falcon_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{ unsigned long col;
+
+ if (regno > 255)
+ return 1;
+ /* This works in STE-mode (with 4bit/color) since f030_col-registers
+ * hold up to 6bit/color.
+ * Even with hicolor r/g/b=5/6/5 bit!
+ */
+ col = f030_col[regno];
+ *red = (col >> 26) & 0x3f;
+ *green = (col >> 18) & 0x3f;
+ *blue = (col >> 2) & 0x3f;
+ *transp = 0;
+ return 0;
+}
+
+
+static int falcon_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if (regno > 255)
+ return 1;
+ f030_col[regno] = (red << 26) | (green << 18) | (blue << 2);
+ if (regno < 16) {
+ shifter_tt.color_reg[regno] =
+ (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) |
+ (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
+ ((blue & 0xe) >> 1) | ((blue & 1) << 3);
+ packed16_cmap[regno] = (red << 11) | (green << 5) | blue;
+ }
+ return 0;
+}
+
+
+static int falcon_blank( int blank_mode )
+{
+/* ++guenther: we can switch off graphics by changing VDB and VDE,
+ * so VIDEL doesn't hog the bus while saving.
+ * (this may affect usleep()).
+ */
+ int vdb, vss, hbe, hss;
+
+ if (mon_type == F_MON_SM) /* this doesn't work on SM124 */
+ return 1;
+
+ vdb = current_par.VDB;
+ vss = current_par.VSS;
+ hbe = current_par.HBE;
+ hss = current_par.HSS;
+
+ if (blank_mode >= 1) {
+ /* disable graphics output (this speeds up the CPU) ... */
+ vdb = current_par.VFT + 1;
+ /* ... and blank all lines */
+ hbe = current_par.HHT + 2;
+ }
+ /* use VESA suspend modes on VGA monitors */
+ if (mon_type == F_MON_VGA) {
+ if (blank_mode == 2 || blank_mode == 4)
+ vss = current_par.VFT + 1;
+ if (blank_mode == 3 || blank_mode == 4)
+ hss = current_par.HHT + 2;
+ }
+
+ videl.vdb = vdb;
+ videl.vss = vss;
+ videl.hbe = hbe;
+ videl.hss = hss;
+
+ return 0;
+}
+
+
+static int falcon_detect( void )
+{
+ struct atari_fb_par par;
+ unsigned char fhw;
+
+ /* Determine connected monitor and set monitor parameters */
+ fhw = *(unsigned char*)0xffff8006;
+ mon_type = fhw >> 6 & 0x3;
+ /* bit 1 of fhw: 1=32 bit ram bus, 0=16 bit */
+ f030_bus_width = fhw << 6 & 0x80;
+ switch (mon_type) {
+ case F_MON_SM:
+ vfmin = 70;
+ vfmax = 72;
+ hfmin = 35713;
+ hfmax = 35715;
+ break;
+ case F_MON_SC:
+ case F_MON_TV:
+ /* PAL...NTSC */
+ vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */
+ vfmax = 60;
+ hfmin = 15620;
+ hfmax = 15755;
+ break;
+ }
+ /* initialize hsync-len */
+ f25.hsync = h_syncs[mon_type] / f25.t;
+ f32.hsync = h_syncs[mon_type] / f32.t;
+ if (fext.t)
+ fext.hsync = h_syncs[mon_type] / fext.t;
+
+ falcon_get_par(&par);
+ falcon_encode_var(&atari_fb_predefined[0], &par);
+
+ /* Detected mode is always the "autodetect" slot */
+ return 1;
+}
+
+#endif /* ATAFB_FALCON */
+
+/* ------------------- ST(E) specific functions ---------------------- */
+
+#ifdef ATAFB_STE
+
+static int stste_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ int mode, i;
+
+ strcpy(fix->id,"Atari Builtin");
+ fix->smem_start=real_screen_base;
+ fix->smem_len=screen_len;
+ fix->type=FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux=2;
+ fix->visual=FB_VISUAL_PSEUDOCOLOR;
+ mode = par->hw.st.mode & 3;
+ if (mode == ST_HIGH) {
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->type_aux=0;
+ fix->visual=FB_VISUAL_MONO10;
+ }
+ if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ fix->xpanstep = 16;
+ fix->ypanstep = 1;
+ } else {
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ }
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+ for (i=0; i<arraysize(fix->reserved); i++)
+ fix->reserved[i]=0;
+ return 0;
+}
+
+
+static int stste_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xres=var->xres;
+ int yres=var->yres;
+ int bpp=var->bits_per_pixel;
+ int linelen;
+ int yres_virtual = var->yres_virtual;
+
+ if (mono_moni) {
+ if (bpp > 1 || xres > sttt_xres || yres > st_yres)
+ return -EINVAL;
+ par->hw.st.mode=ST_HIGH;
+ xres=sttt_xres;
+ yres=st_yres;
+ bpp=1;
+ } else {
+ if (bpp > 4 || xres > sttt_xres || yres > st_yres)
+ return -EINVAL;
+ if (bpp > 2) {
+ if (xres > sttt_xres/2 || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.st.mode=ST_LOW;
+ xres=sttt_xres/2;
+ yres=st_yres/2;
+ bpp=4;
+ }
+ else if (bpp > 1) {
+ if (xres > sttt_xres || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.st.mode=ST_MID;
+ xres=sttt_xres;
+ yres=st_yres/2;
+ bpp=2;
+ }
+ else
+ return -EINVAL;
+ }
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+ if (var->sync & FB_SYNC_EXT)
+ par->hw.st.sync=(par->hw.st.sync & ~1) | 1;
+ else
+ par->hw.st.sync=(par->hw.st.sync & ~1);
+ linelen=xres*bpp/8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base=screen_base+ var->yoffset*linelen;
+ return 0;
+}
+
+static int stste_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int linelen, i;
+ var->red.offset=0;
+ var->red.length = ATARIHW_PRESENT(EXTD_SHIFTER) ? 4 : 3;
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these are incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ if (!(par->hw.st.sync & 1))
+ var->sync=0;
+ else
+ var->sync=FB_SYNC_EXT;
+
+ switch (par->hw.st.mode & 3) {
+ case ST_LOW:
+ var->xres=sttt_xres/2;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=4;
+ break;
+ case ST_MID:
+ var->xres=sttt_xres;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=2;
+ break;
+ case ST_HIGH:
+ var->xres=sttt_xres;
+ var->yres=st_yres;
+ var->bits_per_pixel=1;
+ break;
+ }
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ var->xres_virtual=sttt_xres_virtual;
+ linelen=var->xres_virtual * var->bits_per_pixel / 8;
+ ovsc_addlen=linelen*(sttt_yres_virtual - st_yres);
+
+ if (! use_hwscroll)
+ var->yres_virtual=var->yres;
+ else if (screen_len)
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0;
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void stste_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ par->hw.st.mode=shifter_tt.st_shiftmode;
+ par->hw.st.sync=shifter.syncmode;
+ addr = ((shifter.bas_hi & 0xff) << 16) |
+ ((shifter.bas_md & 0xff) << 8);
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ addr |= (shifter.bas_lo & 0xff);
+ par->screen_base = PTOV(addr);
+}
+
+static void stste_set_par( struct atari_fb_par *par )
+{
+ shifter_tt.st_shiftmode=par->hw.st.mode;
+ shifter.syncmode=par->hw.st.sync;
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+}
+
+
+static int stste_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{ unsigned col;
+
+ if (regno > 15)
+ return 1;
+ col = shifter_tt.color_reg[regno];
+ if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ *red = ((col >> 7) & 0xe) | ((col >> 11) & 1);
+ *green = ((col >> 3) & 0xe) | ((col >> 7) & 1);
+ *blue = ((col << 1) & 0xe) | ((col >> 3) & 1);
+ }
+ else {
+ *red = (col >> 8) & 0x7;
+ *green = (col >> 4) & 0x7;
+ *blue = col & 0x7;
+ }
+ *transp = 0;
+ return 0;
+}
+
+
+static int stste_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if (regno > 15)
+ return 1;
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ shifter_tt.color_reg[regno] =
+ (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) |
+ (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
+ ((blue & 0xe) >> 1) | ((blue & 1) << 3);
+ else
+ shifter_tt.color_reg[regno] =
+ ((red & 0x7) << 8) |
+ ((green & 0x7) << 4) |
+ (blue & 0x7);
+ return 0;
+}
+
+
+static int stste_detect( void )
+
+{ struct atari_fb_par par;
+
+ /* Determine the connected monitor: The DMA sound must be
+ * disabled before reading the MFP GPIP, because the Sound
+ * Done Signal and the Monochrome Detect are XORed together!
+ */
+ if (ATARIHW_PRESENT(PCM_8BIT)) {
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ udelay(20); /* wait a while for things to settle down */
+ }
+ mono_moni = (mfp.par_dt_reg & 0x80) == 0;
+
+ stste_get_par(&par);
+ stste_encode_var(&atari_fb_predefined[0], &par);
+
+ if (!ATARIHW_PRESENT(EXTD_SHIFTER))
+ use_hwscroll = 0;
+ return 1;
+}
+
+static void stste_set_screen_base(unsigned long s_base)
+{
+ unsigned long addr;
+ addr= VTOP(s_base);
+ /* Setup Screen Memory */
+ shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16);
+ shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8);
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ shifter.bas_lo=(unsigned char) (addr & 0x0000ff);
+}
+
+#endif /* ATAFB_STE */
+
+/* Switching the screen size should be done during vsync, otherwise
+ * the margins may get messed up. This is a well known problem of
+ * the ST's video system.
+ *
+ * Unfortunately there is hardly any way to find the vsync, as the
+ * vertical blank interrupt is no longer in time on machines with
+ * overscan type modifications.
+ *
+ * We can, however, use Timer B to safely detect the black shoulder,
+ * but then we've got to guess an appropriate delay to find the vsync.
+ * This might not work on every machine.
+ *
+ * martin_rogge @ ki.maus.de, 8th Aug 1995
+ */
+
+#define LINE_DELAY (mono_moni ? 30 : 70)
+#define SYNC_DELAY (mono_moni ? 1500 : 2000)
+
+/* SWITCH_ACIA may be used for Falcon (ScreenBlaster III internal!) */
+static void st_ovsc_switch(int switchmode)
+{
+ unsigned long flags;
+ register unsigned char old, new;
+
+ if ((switchmode & (SWITCH_ACIA | SWITCH_SND6 | SWITCH_SND7)) == 0)
+ return;
+ save_flags(flags);
+ cli();
+
+ mfp.tim_ct_b = 0x10;
+ mfp.active_edge |= 8;
+ mfp.tim_ct_b = 0;
+ mfp.tim_dt_b = 0xf0;
+ mfp.tim_ct_b = 8;
+ while (mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */
+ ;
+ new = mfp.tim_dt_b;
+ do {
+ udelay(LINE_DELAY);
+ old = new;
+ new = mfp.tim_dt_b;
+ } while (old != new);
+ mfp.tim_ct_b = 0x10;
+ udelay(SYNC_DELAY);
+
+ if (switchmode == SWITCH_ACIA)
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE);
+ else {
+ sound_ym.rd_data_reg_sel = 14;
+ sound_ym.wd_data = sound_ym.rd_data_reg_sel | switchmode;
+ }
+ restore_flags(flags);
+}
+
+/* ------------------- External Video ---------------------- */
+
+#ifdef ATAFB_EXT
+
+static int ext_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ int i;
+
+ strcpy(fix->id,"Unknown Extern");
+ fix->smem_start=external_addr;
+ fix->smem_len=(external_len + PAGE_SIZE -1) & PAGE_MASK;
+ if (external_depth == 1) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ /* The letters 'n' and 'i' in the "atavideo=external:" stand
+ * for "normal" and "inverted", rsp., in the monochrome case */
+ fix->visual =
+ (external_pmode == FB_TYPE_INTERLEAVED_PLANES ||
+ external_pmode == FB_TYPE_PACKED_PIXELS) ?
+ FB_VISUAL_MONO10 :
+ FB_VISUAL_MONO01;
+ }
+ else {
+ switch (external_pmode) {
+ /* All visuals are STATIC, because we don't know how to change
+ * colors :-(
+ */
+ case -1: /* truecolor */
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->visual=FB_VISUAL_TRUECOLOR;
+ break;
+ case FB_TYPE_PACKED_PIXELS:
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR;
+ break;
+ case FB_TYPE_PLANES:
+ fix->type=FB_TYPE_PLANES;
+ fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR;
+ break;
+ case FB_TYPE_INTERLEAVED_PLANES:
+ fix->type=FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux=2;
+ fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR;
+ break;
+ }
+ }
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+ for (i=0; i<arraysize(fix->reserved); i++)
+ fix->reserved[i]=0;
+ return 0;
+}
+
+
+static int ext_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+
+ if (var->bits_per_pixel > myvar->bits_per_pixel ||
+ var->xres > myvar->xres ||
+ var->yres > myvar->yres ||
+ var->xoffset > 0 ||
+ var->yoffset > 0)
+ return -EINVAL;
+ return 0;
+}
+
+
+static int ext_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int i;
+
+ var->red.offset=0;
+ var->red.length=(external_pmode == -1) ? external_depth/3 :
+ (external_vgaiobase ? external_bitspercol : 0);
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these are surely incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ var->sync=0;
+
+ var->xres = external_xres;
+ var->yres = external_yres;
+ var->bits_per_pixel = external_depth;
+
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ var->xres_virtual=var->xres;
+ var->yres_virtual=var->yres;
+ var->xoffset=0;
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void ext_get_par( struct atari_fb_par *par )
+{
+ par->screen_base = external_addr;
+}
+
+static void ext_set_par( struct atari_fb_par *par )
+{
+}
+
+#define OUTB(port,val) \
+ *((unsigned volatile char *) ((port)+external_vgaiobase))=(val)
+#define INB(port) \
+ (*((unsigned volatile char *) ((port)+external_vgaiobase)))
+#define DACDelay \
+ do { \
+ unsigned char tmp=INB(0x3da); \
+ tmp=INB(0x3da); \
+ } while (0)
+
+static int ext_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+
+{ unsigned char colmask = (1 << external_bitspercol) - 1;
+
+ if (! external_vgaiobase)
+ return 1;
+
+ switch (external_card_type) {
+ case IS_VGA:
+ OUTB(0x3c7, regno);
+ DACDelay;
+ *red=INB(0x3c9) & colmask;
+ DACDelay;
+ *green=INB(0x3c9) & colmask;
+ DACDelay;
+ *blue=INB(0x3c9) & colmask;
+ DACDelay;
+ return 0;
+
+ case IS_MV300:
+ *red = MV300_color[regno].red;
+ *green = MV300_color[regno].green;
+ *blue = MV300_color[regno].blue;
+ *transp=0;
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+static int ext_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+
+{ unsigned char colmask = (1 << external_bitspercol) - 1;
+
+ if (! external_vgaiobase)
+ return 1;
+
+ switch (external_card_type) {
+ case IS_VGA:
+ OUTB(0x3c8, regno);
+ DACDelay;
+ OUTB(0x3c9, red & colmask);
+ DACDelay;
+ OUTB(0x3c9, green & colmask);
+ DACDelay;
+ OUTB(0x3c9, blue & colmask);
+ DACDelay;
+ return 0;
+
+ case IS_MV300:
+ MV300_color[regno].red = red;
+ MV300_color[regno].green = green;
+ MV300_color[regno].blue = blue;
+ OUTB((MV300_reg[regno] << 2)+1, red);
+ OUTB((MV300_reg[regno] << 2)+1, green);
+ OUTB((MV300_reg[regno] << 2)+1, blue);
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+
+static int ext_detect( void )
+
+{
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+ struct atari_fb_par dummy_par;
+
+ myvar->xres = external_xres;
+ myvar->yres = external_yres;
+ myvar->bits_per_pixel = external_depth;
+ ext_encode_var(myvar, &dummy_par);
+ return 1;
+}
+
+#endif /* ATAFB_EXT */
+
+/* ------ This is the same for most hardware types -------- */
+
+static void set_screen_base(unsigned long s_base)
+{
+ unsigned long addr;
+ addr= VTOP(s_base);
+ /* Setup Screen Memory */
+ shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16);
+ shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8);
+ shifter.bas_lo=(unsigned char) (addr & 0x0000ff);
+}
+
+
+static int pan_display( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ if (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset)
+ return -EINVAL;
+ else
+ var->xoffset = up(var->xoffset, 16);
+ par->screen_base = screen_base +
+ (var->yoffset * disp[currcon].var.xres_virtual + var->xoffset)
+ * disp[currcon].var.bits_per_pixel / 8;
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base (par->screen_base);
+ else
+ return -EINVAL;
+ return 0;
+}
+
+
+/* ------------ Interfaces to hardware functions ------------ */
+
+
+#ifdef ATAFB_TT
+struct fb_hwswitch tt_switch = {
+ tt_detect, tt_encode_fix, tt_decode_var, tt_encode_var,
+ tt_get_par, tt_set_par, tt_getcolreg, tt_setcolreg,
+ set_screen_base, NULL, pan_display
+};
+#endif
+
+#ifdef ATAFB_FALCON
+struct fb_hwswitch falcon_switch = {
+ falcon_detect, falcon_encode_fix, falcon_decode_var, falcon_encode_var,
+ falcon_get_par, falcon_set_par, falcon_getcolreg,
+ falcon_setcolreg, set_screen_base, falcon_blank, falcon_pan_display
+};
+#endif
+
+#ifdef ATAFB_STE
+struct fb_hwswitch st_switch = {
+ stste_detect, stste_encode_fix, stste_decode_var, stste_encode_var,
+ stste_get_par, stste_set_par, stste_getcolreg, stste_setcolreg,
+ stste_set_screen_base, NULL, pan_display
+};
+#endif
+
+#ifdef ATAFB_EXT
+struct fb_hwswitch ext_switch = {
+ ext_detect, ext_encode_fix, ext_decode_var, ext_encode_var,
+ ext_get_par, ext_set_par, ext_getcolreg, ext_setcolreg, NULL, NULL, NULL
+};
+#endif
+
+
+
+static void atari_fb_get_par( struct atari_fb_par *par )
+{
+ if (current_par_valid) {
+ *par=current_par;
+ }
+ else
+ fbhw->get_par(par);
+}
+
+
+static void atari_fb_set_par( struct atari_fb_par *par )
+{
+ fbhw->set_par(par);
+ current_par=*par;
+ current_par_valid=1;
+}
+
+
+
+/* =========================================================== */
+/* ============== Hardware Independent Functions ============= */
+/* =========================================================== */
+
+
+/* used for hardware scrolling */
+
+static int
+fb_update_var(int con)
+{
+ int off=disp[con].var.yoffset*disp[con].var.xres_virtual*
+ disp[con].var.bits_per_pixel>>3;
+
+ current_par.screen_base=screen_base + off;
+
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base(current_par.screen_base);
+ return 0;
+}
+
+static int
+do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+ int err,activate;
+ struct atari_fb_par par;
+ if ((err=fbhw->decode_var(var, &par)))
+ return err;
+ activate=var->activate;
+ if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
+ atari_fb_set_par(&par);
+ fbhw->encode_var(var, &par);
+ var->activate=activate;
+ return 0;
+}
+
+/* Functions for handling colormap */
+
+/* there seems to be a bug in gcc 2.5.8 which inhibits using an other solution */
+/* I always get a sigsegv */
+
+static short red16[]=
+ { 0x0000,0x0000,0x0000,0x0000,0xc000,0xc000,0xc000,0xc000,
+ 0x8000,0x0000,0x0000,0x0000,0xffff,0xffff,0xffff,0xffff};
+static short green16[]=
+ { 0x0000,0x0000,0xc000,0xc000,0x0000,0x0000,0xc000,0xc000,
+ 0x8000,0x0000,0xffff,0xffff,0x0000,0x0000,0xffff,0xffff};
+static short blue16[]=
+ { 0x0000,0xc000,0x0000,0xc000,0x0000,0xc000,0x0000,0xc000,
+ 0x8000,0xffff,0x0000,0xffff,0x0000,0xffff,0x0000,0xffff};
+
+static short red4[]=
+ { 0x0000,0xc000,0x8000,0xffff};
+static short green4[]=
+ { 0x0000,0xc000,0x8000,0xffff};
+static short blue4[]=
+ { 0x0000,0xc000,0x8000,0xffff};
+
+static short red2[]=
+ { 0x0000,0xffff};
+static short green2[]=
+ { 0x0000,0xffff};
+static short blue2[]=
+ { 0x0000,0xffff};
+
+struct fb_cmap default_16_colors = { 0, 16, red16, green16, blue16, NULL };
+struct fb_cmap default_4_colors = { 0, 4, red4, green4, blue4, NULL };
+struct fb_cmap default_2_colors = { 0, 2, red2, green2, blue2, NULL };
+
+static struct fb_cmap *
+get_default_colormap(int bpp)
+{
+ if (bpp == 1)
+ return &default_2_colors;
+ if (bpp == 2)
+ return &default_4_colors;
+ return &default_16_colors;
+}
+
+#define CNVT_TOHW(val,width) (((val) << (width)) + 0x7fff - (val)) >> 16
+#define CNVT_FROMHW(val,width) ((width)?((((val) << 16) - (val)) / ((1<<(width))-1)):0)
+
+
+static int
+do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc)
+{
+ int i,start;
+ unsigned short *red,*green,*blue,*transp;
+ unsigned int hred,hgreen,hblue,htransp;
+
+ red=cmap->red;
+ green=cmap->green;
+ blue=cmap->blue;
+ transp=cmap->transp;
+ start=cmap->start;
+ if (start < 0)
+ return EINVAL;
+ for (i=0 ; i < cmap->len ; i++) {
+ if (fbhw->getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
+ return 0;
+ hred=CNVT_FROMHW(hred,var->red.length);
+ hgreen=CNVT_FROMHW(hgreen,var->green.length);
+ hblue=CNVT_FROMHW(hblue,var->blue.length);
+ htransp=CNVT_FROMHW(htransp,var->transp.length);
+ if (kspc) {
+ *red=hred;
+ *green=hgreen;
+ *blue=hblue;
+ if (transp) *transp=htransp;
+ }
+ else {
+ put_user(hred, red);
+ put_user(hgreen, green);
+ put_user(hblue, blue);
+ if (transp) put_user(htransp, transp);
+ }
+ red++;
+ green++;
+ blue++;
+ if (transp) transp++;
+ }
+ return 0;
+}
+
+static int
+do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc)
+{
+ int i,start;
+ unsigned short *red,*green,*blue,*transp;
+ unsigned int hred,hgreen,hblue,htransp;
+
+ red=cmap->red;
+ green=cmap->green;
+ blue=cmap->blue;
+ transp=cmap->transp;
+ start=cmap->start;
+
+ if (start < 0)
+ return -EINVAL;
+ for (i=0 ; i < cmap->len ; i++) {
+ if (kspc) {
+ hred=*red;
+ hgreen=*green;
+ hblue=*blue;
+ htransp=(transp) ? *transp : 0;
+ }
+ else {
+ get_user(hred, red);
+ get_user(hgreen, green);
+ get_user(hblue, blue);
+ if (transp)
+ get_user(htransp, transp);
+ else
+ htransp = 0;
+ }
+ hred=CNVT_TOHW(hred,var->red.length);
+ hgreen=CNVT_TOHW(hgreen,var->green.length);
+ hblue=CNVT_TOHW(hblue,var->blue.length);
+ htransp=CNVT_TOHW(htransp,var->transp.length);
+ red++;
+ green++;
+ blue++;
+ if (transp) transp++;
+ if (fbhw->setcolreg(start++, hred, hgreen, hblue, htransp))
+ return 0;
+ }
+ return 0;
+}
+
+static void
+do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (disp[con].cmap.len)
+ do_fb_set_cmap(&disp[con].cmap, &(disp[con].var), 1);
+ else
+ do_fb_set_cmap(get_default_colormap(
+ disp[con].var.bits_per_pixel), &(disp[con].var), 1);
+}
+
+static void
+memcpy_fs(int fsfromto, void *to, void *from, int len)
+{
+ switch (fsfromto) {
+ case 0:
+ memcpy(to,from,len);
+ return;
+ case 1:
+ copy_from_user(to,from,len);
+ return;
+ case 2:
+ copy_to_user(to,from,len);
+ return;
+ }
+}
+
+static void
+copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+{
+ int size;
+ int tooff=0, fromoff=0;
+
+ if (to->start > from->start)
+ fromoff=to->start-from->start;
+ else
+ tooff=from->start-to->start;
+ size=to->len-tooff;
+ if (size > from->len-fromoff)
+ size=from->len-fromoff;
+ if (size < 0)
+ return;
+ size*=sizeof(unsigned short);
+ memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
+ memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
+ memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
+ if (from->transp && to->transp)
+ memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+}
+
+static int
+alloc_cmap(struct fb_cmap *cmap,int len,int transp)
+{
+ int size=len*sizeof(unsigned short);
+ if (cmap->len != len) {
+ if (cmap->red)
+ kfree(cmap->red);
+ if (cmap->green)
+ kfree(cmap->green);
+ if (cmap->blue)
+ kfree(cmap->blue);
+ if (cmap->transp)
+ kfree(cmap->transp);
+ cmap->red=cmap->green=cmap->blue=cmap->transp=NULL;
+ cmap->len=0;
+ if (! len)
+ return 0;
+ if (! (cmap->red=kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (! (cmap->green=kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (! (cmap->blue=kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (transp) {
+ if (! (cmap->transp=kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ }
+ else
+ cmap->transp=NULL;
+ }
+ cmap->start=0;
+ cmap->len=len;
+ copy_cmap(get_default_colormap(len), cmap, 0);
+ return 0;
+}
+
+static int
+atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct atari_fb_par par;
+ if (con == -1)
+ atari_fb_get_par(&par);
+ else
+ fbhw->decode_var(&disp[con].var,&par);
+ return fbhw->encode_fix(fix, &par);
+}
+
+static int
+atari_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ struct atari_fb_par par;
+ if (con == -1) {
+ atari_fb_get_par(&par);
+ fbhw->encode_var(var, &par);
+ }
+ else
+ *var=disp[con].var;
+ return 0;
+}
+
+static void
+atari_fb_set_disp(int con)
+{
+ struct fb_fix_screeninfo fix;
+
+ atari_fb_get_fix(&fix, con);
+ if (con == -1)
+ con=0;
+ disp[con].screen_base = (u_char *)fix.smem_start;
+ disp[con].visual = fix.visual;
+ disp[con].type = fix.type;
+ disp[con].type_aux = fix.type_aux;
+ disp[con].ypanstep = fix.ypanstep;
+ disp[con].ywrapstep = fix.ywrapstep;
+ disp[con].line_length = fix.line_length;
+ if (fix.visual != FB_VISUAL_PSEUDOCOLOR &&
+ fix.visual != FB_VISUAL_DIRECTCOLOR)
+ disp[con].can_soft_blank = 0;
+ else
+ disp[con].can_soft_blank = 1;
+ disp[con].inverse =
+ (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+}
+
+static int
+atari_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err,oldxres,oldyres,oldbpp,oldxres_virtual,
+ oldyres_virtual,oldyoffset;
+ if ((err=do_fb_set_var(var, con==currcon)))
+ return err;
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres=disp[con].var.xres;
+ oldyres=disp[con].var.yres;
+ oldxres_virtual=disp[con].var.xres_virtual;
+ oldyres_virtual=disp[con].var.yres_virtual;
+ oldbpp=disp[con].var.bits_per_pixel;
+ oldyoffset=disp[con].var.yoffset;
+ disp[con].var=*var;
+ if (oldxres != var->xres || oldyres != var->yres
+ || oldxres_virtual != var->xres_virtual
+ || oldyres_virtual != var->yres_virtual
+ || oldbpp != var->bits_per_pixel
+ || oldyoffset != var->yoffset) {
+ atari_fb_set_disp(con);
+ (*fb_info.changevar)(con);
+ alloc_cmap(&disp[con].cmap, 0, 0);
+ do_install_cmap(con);
+ }
+ }
+ var->activate=0;
+ return 0;
+}
+
+
+
+static int
+atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console ? */
+ return do_fb_get_cmap(cmap, &(disp[con].var), kspc);
+ else
+ if (disp[con].cmap.len) /* non default colormap ? */
+ copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ copy_cmap(get_default_colormap(
+ disp[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+static int
+atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+ if (! disp[con].cmap.len) { /* no colormap allocated ? */
+ if ((err = alloc_cmap(&disp[con].cmap,
+ 1 << disp[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) /* current console ? */
+ return do_fb_set_cmap(cmap, &(disp[con].var), kspc);
+ else
+ copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+static int
+atari_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ int xoffset = var->xoffset;
+ int yoffset = var->yoffset;
+ int err;
+
+ if ( xoffset < 0 || xoffset + disp[con].var.xres > disp[con].var.xres_virtual
+ || yoffset < 0 || yoffset + disp[con].var.yres > disp[con].var.yres_virtual)
+ return -EINVAL;
+
+ if (con == currcon) {
+ if (fbhw->pan_display) {
+ if ((err = fbhw->pan_display(var, &current_par)))
+ return err;
+ }
+ else
+ return -EINVAL;
+ }
+ disp[con].var.xoffset = var->xoffset;
+ disp[con].var.yoffset = var->yoffset;
+ return 0;
+}
+
+static int
+atari_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con)
+{
+ switch (cmd) {
+#ifdef FBCMD_GET_CURRENTPAR
+ case FBCMD_GET_CURRENTPAR:
+ if (copy_to_user((void *)arg, (void *)&current_par,
+ sizeof(struct atari_fb_par)))
+ return -EFAULT;
+ return 0;
+#endif
+#ifdef FBCMD_SET_CURRENTPAR
+ case FBCMD_SET_CURRENTPAR:
+ if (copy_from_user((void *)&current_par, (void *)arg,
+ sizeof(struct atari_fb_par)))
+ return -EFAULT;
+ atari_fb_set_par(&current_par);
+ return 0;
+#endif
+ }
+ return -EINVAL;
+}
+
+static struct fb_ops atari_fb_ops = {
+ atari_fb_get_fix, atari_fb_get_var, atari_fb_set_var, atari_fb_get_cmap,
+ atari_fb_set_cmap, atari_fb_pan_display, atari_fb_ioctl
+};
+
+static void
+check_default_par( int detected_mode )
+{
+ char default_name[10];
+ int i;
+ struct fb_var_screeninfo var;
+ unsigned long min_mem;
+
+ /* First try the user supplied mode */
+ if (default_par) {
+ var=atari_fb_predefined[default_par-1];
+ var.activate = FB_ACTIVATE_TEST;
+ if (do_fb_set_var(&var,1))
+ default_par=0; /* failed */
+ }
+ /* Next is the autodetected one */
+ if (! default_par) {
+ var=atari_fb_predefined[detected_mode-1]; /* autodetect */
+ var.activate = FB_ACTIVATE_TEST;
+ if (!do_fb_set_var(&var,1))
+ default_par=detected_mode;
+ }
+ /* If that also failed, try some default modes... */
+ if (! default_par) {
+ /* try default1, default2... */
+ for (i=1 ; i < 10 ; i++) {
+ sprintf(default_name,"default%d",i);
+ default_par=get_video_mode(default_name);
+ if (! default_par)
+ panic("can't set default video mode\n");
+ var=atari_fb_predefined[default_par-1];
+ var.activate = FB_ACTIVATE_TEST;
+ if (! do_fb_set_var(&var,1))
+ break; /* ok */
+ }
+ }
+ min_mem=var.xres_virtual * var.yres_virtual * var.bits_per_pixel/8;
+ if (default_mem_req < min_mem)
+ default_mem_req=min_mem;
+}
+
+static int
+atafb_switch(int con)
+{
+ /* Do we have to save the colormap ? */
+ if (disp[currcon].cmap.len)
+ do_fb_get_cmap(&disp[currcon].cmap, &(disp[currcon].var), 1);
+ do_fb_set_var(&disp[con].var,1);
+ currcon=con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+/* (un)blank/poweroff
+ * 0 = unblank
+ * 1 = blank
+ * 2 = suspend vsync
+ * 3 = suspend hsync
+ * 4 = off
+ */
+static void
+atafb_blank(int blank)
+{
+ unsigned short black[16];
+ struct fb_cmap cmap;
+ if (fbhw->blank && !fbhw->blank(blank))
+ return;
+ if (blank) {
+ memset(black, 0, 16*sizeof(unsigned short));
+ cmap.red=black;
+ cmap.green=black;
+ cmap.blue=black;
+ cmap.transp=NULL;
+ cmap.start=0;
+ cmap.len=16;
+ do_fb_set_cmap(&cmap, &(disp[currcon].var), 1);
+ }
+ else
+ do_install_cmap(currcon);
+}
+
+struct fb_info *
+atari_fb_init(long *mem_start)
+{
+ int err;
+ int pad;
+ int detected_mode;
+ unsigned long mem_req;
+ struct fb_var_screeninfo *var;
+
+ err=register_framebuffer("Atari Builtin", &node, &atari_fb_ops,
+ num_atari_fb_predefined, atari_fb_predefined);
+ if (err < 0)
+ panic ("Cannot register frame buffer\n");
+ do {
+#ifdef ATAFB_EXT
+ if (external_addr) {
+ fbhw = &ext_switch;
+ break;
+ }
+#endif
+#ifdef ATAFB_TT
+ if (ATARIHW_PRESENT(TT_SHIFTER)) {
+ fbhw = &tt_switch;
+ break;
+ }
+#endif
+#ifdef ATAFB_FALCON
+ if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
+ fbhw = &falcon_switch;
+ request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
+ "framebuffer/modeswitch", falcon_vbl_switcher);
+ break;
+ }
+#endif
+#ifdef ATAFB_STE
+ if (ATARIHW_PRESENT(STND_SHIFTER) ||
+ ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ fbhw = &st_switch;
+ break;
+ }
+ fbhw = &st_switch;
+ printk("Cannot determine video hardware; defaulting to ST(e)\n");
+#else /* ATAFB_STE */
+ /* no default driver included */
+ /* Nobody will ever see this message :-) */
+ panic("Cannot initialize video hardware\n");
+#endif
+ } while (0);
+ detected_mode = fbhw->detect();
+ check_default_par(detected_mode);
+#ifdef ATAFB_EXT
+ if (!external_addr) {
+#endif /* ATAFB_EXT */
+ mem_req = default_mem_req + ovsc_offset +
+ ovsc_addlen;
+ mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE;
+ screen_base = (unsigned long) atari_stram_alloc(mem_req, mem_start);
+ memset((char *) screen_base, 0, mem_req);
+ pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base;
+ screen_base+=pad;
+ real_screen_base=screen_base+ovsc_offset;
+ screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK;
+ st_ovsc_switch(ovsc_switchmode);
+ if (CPU_IS_040_OR_060) {
+ /* On a '040+, the cache mode of video RAM must be set to
+ * write-through also for internal video hardware! */
+ cache_push( VTOP(screen_base), screen_len );
+ kernel_set_cachemode( screen_base, screen_len,
+ KERNELMAP_NO_COPYBACK );
+ }
+#ifdef ATAFB_EXT
+ }
+ else {
+ /* Map the video memory (physical address given) to somewhere
+ * in the kernel address space.
+ */
+ *mem_start = (*mem_start+PAGE_SIZE-1) & ~(PAGE_SIZE-1);
+ external_addr = kernel_map(external_addr, external_len,
+ KERNELMAP_NO_COPYBACK, mem_start);
+ if (external_vgaiobase)
+ external_vgaiobase = kernel_map(external_vgaiobase,
+ 0x10000, KERNELMAP_NOCACHE_SER, mem_start);
+ screen_base =
+ real_screen_base = external_addr;
+ screen_len = external_len & PAGE_MASK;
+ memset ((char *) screen_base, 0, external_len);
+ }
+#endif /* ATAFB_EXT */
+
+ strcpy(fb_info.modename, "Atari Builtin ");
+ fb_info.disp=disp;
+ fb_info.switch_con=&atafb_switch;
+ fb_info.updatevar=&fb_update_var;
+ fb_info.blank=&atafb_blank;
+ var=atari_fb_predefined+default_par-1;
+ do_fb_set_var(var,1);
+ strcat(fb_info.modename,fb_var_names[default_par-1][0]);
+
+ atari_fb_get_var(&disp[0].var, -1);
+ atari_fb_set_disp(-1);
+ printk("Determined %dx%d, depth %d\n",
+ disp[0].var.xres, disp[0].var.yres, disp[0].var.bits_per_pixel );
+ do_install_cmap(0);
+ return &fb_info;
+}
+
+/* a strtok which returns empty strings, too */
+
+static char * strtoke(char * s,const char * ct)
+{
+ char *sbegin, *send;
+ static char *ssave = NULL;
+
+ sbegin = s ? s : ssave;
+ if (!sbegin) {
+ return NULL;
+ }
+ if (*sbegin == '\0') {
+ ssave = NULL;
+ return NULL;
+ }
+ send = strpbrk(sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ssave = send;
+ return sbegin;
+}
+
+void atari_video_setup( char *options, int *ints )
+{
+ char *this_opt;
+ int temp;
+ char ext_str[80], int_str[100];
+ char mcap_spec[80];
+ char user_mode[80];
+
+ ext_str[0] =
+ int_str[0] =
+ mcap_spec[0] =
+ user_mode[0] =
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+ if (!*this_opt) continue;
+ if ((temp=get_video_mode(this_opt)))
+ default_par=temp;
+ else if (! strcmp(this_opt, "inverse"))
+ inverse=1;
+ else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ else if (! strncmp(this_opt, "hwscroll_",9)) {
+ hwscroll=simple_strtoul(this_opt+9, NULL, 10);
+ if (hwscroll < 0)
+ hwscroll = 0;
+ if (hwscroll > 200)
+ hwscroll = 200;
+ }
+ else if (! strncmp(this_opt, "sw_",3)) {
+ if (! strcmp(this_opt+3, "acia"))
+ ovsc_switchmode = SWITCH_ACIA;
+ else if (! strcmp(this_opt+3, "snd6"))
+ ovsc_switchmode = SWITCH_SND6;
+ else if (! strcmp(this_opt+3, "snd7"))
+ ovsc_switchmode = SWITCH_SND7;
+ else ovsc_switchmode = SWITCH_NONE;
+ }
+#ifdef ATAFB_EXT
+ else if (!strcmp(this_opt,"mv300")) {
+ external_bitspercol = 8;
+ external_card_type = IS_MV300;
+ }
+ else if (!strncmp(this_opt,"external:",9))
+ strcpy(ext_str, this_opt+9);
+#endif
+ else if (!strncmp(this_opt,"internal:",9))
+ strcpy(int_str, this_opt+9);
+#ifdef ATAFB_FALCON
+ else if (!strncmp(this_opt, "eclock:", 7)) {
+ fext.f = simple_strtoul(this_opt+7, NULL, 10);
+ /* external pixelclock in kHz --> ps */
+ fext.t = 1000000000/fext.f;
+ fext.f *= 1000;
+ }
+ else if (!strncmp(this_opt, "monitorcap:", 11))
+ strcpy(mcap_spec, this_opt+11);
+#endif
+ else if (!strcmp(this_opt, "keep"))
+ DontCalcRes = 1;
+ else if (!strncmp(this_opt, "R", 1))
+ strcpy(user_mode, this_opt+1);
+ }
+
+ if (*int_str) {
+ /* Format to config extended internal video hardware like OverScan:
+ "<switch-type>,internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>"
+ Explanation:
+ <switch-type> type to switch on higher resolution
+ sw_acia : via keyboard ACIA
+ sw_snd6 : via bit 6 of the soundchip port
+ sw_snd7 : via bit 7 of the soundchip port
+ <xres>: x-resolution
+ <yres>: y-resolution
+ The following are only needed if you have an overscan which
+ needs a black border:
+ <xres_max>: max. length of a line in pixels your OverScan hardware would allow
+ <yres_max>: max. number of lines your OverScan hardware would allow
+ <offset>: Offset from physical beginning to visible beginning
+ of screen in bytes
+ */
+ int xres;
+ char *p;
+
+ if (!(p = strtoke(int_str, ";")) ||!*p) goto int_invalid;
+ xres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, ";")) || !*p) goto int_invalid;
+ sttt_xres=xres;
+ tt_yres=st_yres=simple_strtoul(p, NULL, 10);
+ if ((p=strtoke(NULL, ";")) && *p) {
+ sttt_xres_virtual=simple_strtoul(p, NULL, 10);
+ }
+ if ((p=strtoke(NULL, ";")) && *p) {
+ sttt_yres_virtual=simple_strtoul(p, NULL, 0);
+ }
+ if ((p=strtoke(NULL, ";")) && *p) {
+ ovsc_offset=simple_strtoul(p, NULL, 0);
+ }
+
+ if (ovsc_offset || (sttt_yres_virtual != st_yres))
+ use_hwscroll=0;
+ }
+ else
+ int_invalid: ovsc_switchmode = SWITCH_NONE;
+
+#ifdef ATAFB_EXT
+ if (*ext_str) {
+ int xres, yres, depth, planes;
+ unsigned long addr, len;
+ char *p;
+
+ /* Format is: <xres>;<yres>;<depth>;<plane organ.>;
+ * <screen mem addr>
+ * [;<screen mem length>[;<vgaiobase>[;<colorreg-type>]]]
+ */
+ if (!(p = strtoke(ext_str, ";")) ||!*p) goto ext_invalid;
+ xres = simple_strtoul(p, NULL, 10);
+ if (xres <= 0) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ yres = simple_strtoul(p, NULL, 10);
+ if (yres <= 0) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ depth = simple_strtoul(p, NULL, 10);
+ if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
+ depth != 16 && depth != 24) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ if (*p == 'i')
+ planes = FB_TYPE_INTERLEAVED_PLANES;
+ else if (*p == 'p')
+ planes = FB_TYPE_PACKED_PIXELS;
+ else if (*p == 'n')
+ planes = FB_TYPE_PLANES;
+ else if (*p == 't')
+ planes = -1; /* true color */
+ else
+ goto ext_invalid;
+
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ addr = simple_strtoul(p, NULL, 0);
+
+ if (!(p = strtoke(NULL, ";")) ||!*p)
+ len = xres*yres*depth/8;
+ else
+ len = simple_strtoul(p, NULL, 0);
+
+ if ((p = strtoke(NULL, ";")) && *p)
+ external_vgaiobase=simple_strtoul(p, NULL, 0);
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ external_bitspercol = simple_strtoul(p, NULL, 0);
+ if (external_bitspercol > 8)
+ external_bitspercol = 8;
+ else if (external_bitspercol < 1)
+ external_bitspercol = 1;
+ }
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ if (!strcmp(this_opt, "vga"))
+ external_card_type = IS_VGA;
+ if (!strcmp(this_opt, "mv300"))
+ external_card_type = IS_MV300;
+ }
+
+ external_xres = xres;
+ external_yres = yres;
+ external_depth = depth;
+ external_pmode = planes;
+ external_addr = addr;
+ external_len = len;
+
+ if (external_card_type == IS_MV300)
+ switch (external_depth) {
+ case 1:
+ MV300_reg = MV300_reg_1bit;
+ break;
+ case 4:
+ MV300_reg = MV300_reg_4bit;
+ break;
+ case 8:
+ MV300_reg = MV300_reg_8bit;
+ break;
+ }
+
+ ext_invalid:
+ ;
+ }
+#endif /* ATAFB_EXT */
+
+#ifdef ATAFB_FALCON
+ if (*mcap_spec) {
+ char *p;
+ int vmin, vmax, hmin, hmax;
+
+ /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
+ * <V*> vertical freq. in Hz
+ * <H*> horizontal freq. in kHz
+ */
+ if (!(p = strtoke(mcap_spec, ";")) || !*p) goto cap_invalid;
+ vmin = simple_strtoul(p, NULL, 10);
+ if (vmin <= 0) goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid;
+ vmax = simple_strtoul(p, NULL, 10);
+ if (vmax <= 0 || vmax <= vmin) goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid;
+ hmin = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmin <= 0) goto cap_invalid;
+ if (!(p = strtoke(NULL, "")) || !*p) goto cap_invalid;
+ hmax = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmax <= 0 || hmax <= hmin) goto cap_invalid;
+
+ vfmin = vmin;
+ vfmax = vmax;
+ hfmin = hmin;
+ hfmax = hmax;
+ cap_invalid:
+ ;
+ }
+#endif
+
+ if (*user_mode) {
+ /* Format of user defined video mode is: <xres>;<yres>;<depth>
+ */
+ char *p;
+ int xres, yres, depth, temp;
+
+ if (!(p = strtoke(user_mode, ";")) || !*p) goto user_invalid;
+ xres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, ";")) || !*p) goto user_invalid;
+ yres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, "")) || !*p) goto user_invalid;
+ depth = simple_strtoul(p, NULL, 10);
+ if ((temp=get_video_mode("user0"))) {
+ default_par=temp;
+ atari_fb_predefined[default_par-1].xres = xres;
+ atari_fb_predefined[default_par-1].yres = yres;
+ atari_fb_predefined[default_par-1].bits_per_pixel = depth;
+ }
+
+ user_invalid:
+ ;
+ }
+}
diff --git a/arch/m68k/atari/atafb.h b/arch/m68k/atari/atafb.h
new file mode 100644
index 000000000..2bacb7027
--- /dev/null
+++ b/arch/m68k/atari/atafb.h
@@ -0,0 +1,48 @@
+#include <linux/fb.h>
+#include <linux/console.h>
+
+
+struct display
+{
+ int bytes_per_row; /* offset to one line below */
+
+ int cursor_x; /* current cursor position */
+ int cursor_y;
+
+ int fgcol; /* text colors */
+ int bgcol;
+
+ struct fb_var_screeninfo var; /* variable infos */
+ struct fb_cmap cmap; /* colormap */
+
+ /* the following three are copies from fb_fix_screeninfo */
+ int visual;
+ int type;
+ int type_aux;
+
+ u_char *bitplane; /* pointer to top of physical screen */
+
+ u_char *screen_base; /* pointer to top of virtual screen */
+
+ u_char *fontdata; /* Font associated to this display */
+ int fontheight;
+ int fontwidth;
+
+ int inverse; /* != 0 text black on white as default */
+ struct vc_data *conp; /* pointer to console data */
+ struct display_switch *dispsw; /* pointers to depth specific functions */
+};
+
+struct fb_info
+{
+ char modename[40]; /* name of the at boottime detected video mode */
+ struct display *disp; /* pointer to display variables */
+ int (*changevar)(int); /* tell the console var has changed */
+ int (*switch_con)(int); /* tell the framebuffer to switch consoles */
+ int (*updatevar)(int); /* tell the framebuffer to update the vars */
+ void (*blank)(int); /* tell the framebuffer to (un)blank the screen */
+};
+
+struct fb_info *atafb_init(long *);
+
+
diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c
new file mode 100644
index 000000000..642ff027b
--- /dev/null
+++ b/arch/m68k/atari/ataints.c
@@ -0,0 +1,639 @@
+/*
+ * arch/m68k/atari/ataints.c -- Atari Linux interrupt handling code
+ *
+ * 5/2/94 Roman Hodek:
+ * Added support for TT interrupts; setup for TT SCU (may someone has
+ * twiddled there and we won't get the right interrupts :-()
+ *
+ * Major change: The device-independent code in m68k/ints.c didn't know
+ * about non-autovec ints yet. It hardcoded the number of possible ints to
+ * 7 (IRQ1...IRQ7). But the Atari has lots of non-autovec ints! I made the
+ * number of possible ints a constant defined in interrupt.h, which is
+ * 47 for the Atari. So we can call request_irq() for all Atari interrupts
+ * just the normal way. Additionally, all vectors >= 48 are initialized to
+ * call trap() instead of inthandler(). This must be changed here, too.
+ *
+ * 1995-07-16 Lars Brinkhoff <f93labr@dd.chalmers.se>:
+ * Corrected a bug in atari_add_isr() which rejected all SCC
+ * interrupt sources if there were no TT MFP!
+ *
+ * 12/13/95: New interface functions atari_level_triggered_int() and
+ * atari_register_vme_int() as support for level triggered VME interrupts.
+ *
+ * 02/12/96: (Roman)
+ * Total rewrite of Atari interrupt handling, for new scheme see comments
+ * below.
+ *
+ * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>:
+ * Added new function atari_unregister_vme_int(), and
+ * modified atari_register_vme_int() as well as IS_VALID_INTNO()
+ * to work with it.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+#include <asm/irq.h>
+
+
+/*
+ * Atari interrupt handling scheme:
+ * --------------------------------
+ *
+ * All interrupt source have an internal number (defined in
+ * <asm/atariints.h>): Autovector interrupts are 1..7, then follow ST-MFP,
+ * TT-MFP, SCC, and finally VME interrupts. Vector numbers for the latter can
+ * be allocated by atari_register_vme_int(). Currently, all int source numbers
+ * have the IRQ_MACHSPEC bit set, to keep the general int handling functions
+ * in kernel/ints.c from them.
+ *
+ * Each interrupt can be of three types:
+ *
+ * - SLOW: The handler runs with all interrupts enabled, except the one it
+ * was called by (to avoid reentering). This should be the usual method.
+ * But it is currently possible only for MFP ints, since only the MFP
+ * offers an easy way to mask interrupts.
+ *
+ * - FAST: The handler runs with all interrupts disabled. This should be used
+ * only for really fast handlers, that just do actions immediately
+ * necessary, and let the rest do a bottom half or task queue.
+ *
+ * - PRIORITIZED: The handler can be interrupted by higher-level ints
+ * (greater IPL, no MFP priorities!). This is the method of choice for ints
+ * which should be slow, but are not from a MFP.
+ *
+ * The feature of more than one handler for one int source is still there, but
+ * only applicable if all handers are of the same type. To not slow down
+ * processing of ints with only one handler by the chaining feature, the list
+ * calling function atari_call_irq_list() is only plugged in at the time the
+ * second handler is registered.
+ *
+ * Implementation notes: For fast-as-possible int handling, there are separate
+ * entry points for each type (slow/fast/prio). The assembler handler calls
+ * the irq directly in the usual case, no C wrapper is involved. In case of
+ * multiple handlers, atari_call_irq_list() is registered as handler and calls
+ * in turn the real irq's. To ease access from assembler level to the irq
+ * function pointer and accompanying data, these two are stored in a separate
+ * array, irq_handler[]. The rest of data (type, name) are put into a second
+ * array, irq_param, that is accessed from C only. For each slow interrupt (32
+ * in all) there are separate handler functions, which makes it possible to
+ * hard-code the MFP register address and value, are necessary to mask the
+ * int. If there'd be only one generic function, lots of calculations would be
+ * needed to determine MFP register and int mask from the vector number :-(
+ *
+ * Furthermore, slow ints may not lower the IPL below its previous value
+ * (before the int happened). This is needed so that an int of class PRIO, on
+ * that this int may be stacked, cannot be reentered. This feature is
+ * implemented as follows: If the stack frame format is 1 (throwaway), the int
+ * is not stacked, and the IPL is anded with 0xfbff, resulting in a new level
+ * 2, which still blocks the HSYNC, but no interrupts of interest. If the
+ * frame format is 0, the int is nested, and the old IPL value can be found in
+ * the sr copy in the frame.
+ */
+
+
+#define NUM_INT_SOURCES (8 + NUM_ATARI_SOURCES)
+
+typedef void (*asm_irq_handler)(void);
+
+struct irqhandler {
+ void (*handler)(int, void *, struct pt_regs *);
+ void *dev_id;
+};
+
+struct irqparam {
+ unsigned long flags;
+ const char *devname;
+};
+
+/*
+ * Array with irq's and their parameter data. This array is accessed from low
+ * level assembler code, so an element size of 8 allows usage of index scaling
+ * addressing mode.
+ */
+static struct irqhandler irq_handler[NUM_INT_SOURCES];
+
+/*
+ * This array hold the rest of parameters of int handlers: type
+ * (slow,fast,prio) and the name of the handler. These values are only
+ * accessed from C
+ */
+static struct irqparam irq_param[NUM_INT_SOURCES];
+
+/*
+ * Bitmap for free interrupt vector numbers
+ * (new vectors starting from 0x70 can be allocated by
+ * atari_register_vme_int())
+ */
+static int free_vme_vec_bitmap = 0;
+
+/* check for valid int number (complex, sigh...) */
+#define IS_VALID_INTNO(n) \
+ ((n) > 0 && \
+ /* autovec and ST-MFP ok anyway */ \
+ (((n) < TTMFP_SOURCE_BASE) || \
+ /* TT-MFP ok if present */ \
+ ((n) >= TTMFP_SOURCE_BASE && (n) < SCC_SOURCE_BASE && \
+ ATARIHW_PRESENT(TT_MFP)) || \
+ /* SCC ok if present and number even */ \
+ ((n) >= SCC_SOURCE_BASE && (n) < VME_SOURCE_BASE && \
+ !((n) & 1) && ATARIHW_PRESENT(SCC)) || \
+ /* greater numbers ok if they are registered VME vectors */ \
+ ((n) >= VME_SOURCE_BASE && (n) < VME_SOURCE_BASE + VME_MAX_SOURCES && \
+ free_vme_vec_bitmap & (1 << ((n) - VME_SOURCE_BASE)))))
+
+
+/*
+ * Here start the assembler entry points for interrupts
+ */
+
+#define IRQ_NAME(nr) atari_slow_irq_##nr##_handler(void)
+
+#define MFP_MK_BASE "0xfa13"
+
+/* This must agree with head.S. */
+#define ORIG_DO "0x20"
+#define FORMATVEC "0x2E"
+#define SR "0x28"
+#define SAVE_ALL \
+ "clrl %%sp@-;" /* stk_adj */ \
+ "pea -1:w;" /* orig d0 = -1 */ \
+ "movel %%d0,%%sp@-;" /* d0 */ \
+ "moveml %%d1-%%d5/%%a0-%%a1,%%sp@-"
+
+#define BUILD_SLOW_IRQ(n) \
+asmlinkage void IRQ_NAME(n); \
+/* Dummy function to allow asm with operands. */ \
+void atari_slow_irq_##n##_dummy (void) { \
+__asm__ (ALIGN_STR "\n" \
+SYMBOL_NAME_STR(atari_slow_irq_) #n "_handler:\t" \
+" addql #1,"SYMBOL_NAME_STR(intr_count)"\n" \
+ SAVE_ALL "\n" \
+" andb #~(1<<(" #n "&7))," /* mask this interrupt */ \
+ "("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n" \
+" bfextu %%sp@("SR"){#5,#3},%%d0\n" /* get old IPL from stack frame */ \
+" movew %%sr,%%d1\n" \
+" bfins %%d0,%%d1{#21,#3}\n" \
+" movew %%d1,%%sr\n" /* set IPL = previous value */ \
+" addql #1,%a0\n" \
+" lea "SYMBOL_NAME_STR(irq_handler)"+("#n"+8)*8,%%a0\n" \
+" pea %%sp@\n" /* push addr of frame */ \
+" movel %%a0@(4),%%sp@-\n" /* push handler data */ \
+" pea (" #n "+0x10000008)\n" /* push int number */ \
+" movel %%a0@,%%a0\n" \
+" jbsr %%a0@\n" /* call the handler */ \
+" addql #8,%%sp\n" \
+" addql #4,%%sp\n" \
+" orw #0x0600,%%sr\n" \
+" andw #0xfeff,%%sr\n" /* set IPL = 6 again */ \
+" orb #(1<<(" #n "&7))," /* now unmask the int again */ \
+ "("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n" \
+" jbra "SYMBOL_NAME_STR(ret_from_interrupt)"\n" \
+ : : "i" (&kstat.interrupts[n+8]) \
+); \
+}
+
+BUILD_SLOW_IRQ(0);
+BUILD_SLOW_IRQ(1);
+BUILD_SLOW_IRQ(2);
+BUILD_SLOW_IRQ(3);
+BUILD_SLOW_IRQ(4);
+BUILD_SLOW_IRQ(5);
+BUILD_SLOW_IRQ(6);
+BUILD_SLOW_IRQ(7);
+BUILD_SLOW_IRQ(8);
+BUILD_SLOW_IRQ(9);
+BUILD_SLOW_IRQ(10);
+BUILD_SLOW_IRQ(11);
+BUILD_SLOW_IRQ(12);
+BUILD_SLOW_IRQ(13);
+BUILD_SLOW_IRQ(14);
+BUILD_SLOW_IRQ(15);
+BUILD_SLOW_IRQ(16);
+BUILD_SLOW_IRQ(17);
+BUILD_SLOW_IRQ(18);
+BUILD_SLOW_IRQ(19);
+BUILD_SLOW_IRQ(20);
+BUILD_SLOW_IRQ(21);
+BUILD_SLOW_IRQ(22);
+BUILD_SLOW_IRQ(23);
+BUILD_SLOW_IRQ(24);
+BUILD_SLOW_IRQ(25);
+BUILD_SLOW_IRQ(26);
+BUILD_SLOW_IRQ(27);
+BUILD_SLOW_IRQ(28);
+BUILD_SLOW_IRQ(29);
+BUILD_SLOW_IRQ(30);
+BUILD_SLOW_IRQ(31);
+
+asm_irq_handler slow_handlers[32] = {
+ atari_slow_irq_0_handler,
+ atari_slow_irq_1_handler,
+ atari_slow_irq_2_handler,
+ atari_slow_irq_3_handler,
+ atari_slow_irq_4_handler,
+ atari_slow_irq_5_handler,
+ atari_slow_irq_6_handler,
+ atari_slow_irq_7_handler,
+ atari_slow_irq_8_handler,
+ atari_slow_irq_9_handler,
+ atari_slow_irq_10_handler,
+ atari_slow_irq_11_handler,
+ atari_slow_irq_12_handler,
+ atari_slow_irq_13_handler,
+ atari_slow_irq_14_handler,
+ atari_slow_irq_15_handler,
+ atari_slow_irq_16_handler,
+ atari_slow_irq_17_handler,
+ atari_slow_irq_18_handler,
+ atari_slow_irq_19_handler,
+ atari_slow_irq_20_handler,
+ atari_slow_irq_21_handler,
+ atari_slow_irq_22_handler,
+ atari_slow_irq_23_handler,
+ atari_slow_irq_24_handler,
+ atari_slow_irq_25_handler,
+ atari_slow_irq_26_handler,
+ atari_slow_irq_27_handler,
+ atari_slow_irq_28_handler,
+ atari_slow_irq_29_handler,
+ atari_slow_irq_30_handler,
+ atari_slow_irq_31_handler
+};
+
+asmlinkage void atari_fast_irq_handler( void );
+asmlinkage void atari_prio_irq_handler( void );
+
+/* Dummy function to allow asm with operands. */
+void atari_fast_prio_irq_dummy (void) {
+__asm__ (ALIGN_STR "\n"
+SYMBOL_NAME_STR(atari_fast_irq_handler) ":
+ orw #0x700,%%sr /* disable all interrupts */
+"SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t
+ addql #1,"SYMBOL_NAME_STR(intr_count)"\n"
+ SAVE_ALL "
+ /* get vector number from stack frame and convert to source */
+ bfextu %%sp@(" FORMATVEC "){#4,#10},%%d0
+ subw #(0x40-8),%%d0
+ jpl 1f
+ addw #(0x40-8-0x18),%%d0
+1: lea %a0,%%a0
+ addql #1,%%a0@(%%d0:l:4)
+ lea "SYMBOL_NAME_STR(irq_handler)",%%a0
+ lea %%a0@(%%d0:l:8),%%a0
+ pea %%sp@ /* push frame address */
+ movel %%a0@(4),%%sp@- /* push handler data */
+ bset #28,%%d0 /* set MACHSPEC bit */
+ movel %%d0,%%sp@- /* push int number */
+ movel %%a0@,%%a0
+ jsr %%a0@ /* and call the handler */
+ addql #8,%%sp
+ addql #4,%%sp
+ jbra "SYMBOL_NAME_STR(ret_from_interrupt)
+ : : "i" (&kstat.interrupts)
+);
+}
+
+/* GK:
+ * HBL IRQ handler for Falcon. Nobody needs it :-)
+ * ++andreas: raise ipl to disable further HBLANK interrupts.
+ */
+asmlinkage void falcon_hblhandler(void);
+asm(".text\n"
+ALIGN_STR "\n"
+SYMBOL_NAME_STR(falcon_hblhandler) ":
+ orw #0x200,%sp@ /* set saved ipl to 2 */
+ rte");
+
+/* Defined in entry.S; only increments 'num_spurious' */
+asmlinkage void bad_interrupt(void);
+
+extern void atari_microwire_cmd( int cmd );
+
+/*
+ * void atari_init_IRQ (void)
+ *
+ * Parameters: None
+ *
+ * Returns: Nothing
+ *
+ * This function should be called during kernel startup to initialize
+ * the atari IRQ handling routines.
+ */
+
+void atari_init_IRQ(void)
+{
+ int i;
+
+ /* initialize the vector table */
+ for (i = 0; i < NUM_INT_SOURCES; ++i) {
+ vectors[IRQ_SOURCE_TO_VECTOR(i)] = bad_interrupt;
+ }
+
+ /* Initialize the MFP(s) */
+
+#ifdef ATARI_USE_SOFTWARE_EOI
+ mfp.vec_adr = 0x48; /* Software EOI-Mode */
+#else
+ mfp.vec_adr = 0x40; /* Automatic EOI-Mode */
+#endif
+ mfp.int_en_a = 0x00; /* turn off MFP-Ints */
+ mfp.int_en_b = 0x00;
+ mfp.int_mk_a = 0xff; /* no Masking */
+ mfp.int_mk_b = 0xff;
+
+ if (ATARIHW_PRESENT(TT_MFP)) {
+#ifdef ATARI_USE_SOFTWARE_EOI
+ tt_mfp.vec_adr = 0x58; /* Software EOI-Mode */
+#else
+ tt_mfp.vec_adr = 0x50; /* Automatic EOI-Mode */
+#endif
+ tt_mfp.int_en_a = 0x00; /* turn off MFP-Ints */
+ tt_mfp.int_en_b = 0x00;
+ tt_mfp.int_mk_a = 0xff; /* no Masking */
+ tt_mfp.int_mk_b = 0xff;
+ }
+
+ if (ATARIHW_PRESENT(SCC)) {
+ scc.cha_a_ctrl = 9;
+ MFPDELAY();
+ scc.cha_a_ctrl = (char) 0xc0; /* hardware reset */
+ }
+
+ if (ATARIHW_PRESENT(SCU)) {
+ /* init the SCU if present */
+ tt_scu.sys_mask = 0x10; /* enable VBL (for the cursor) and
+ * disable HSYNC interrupts (who
+ * needs them?) MFP and SCC are
+ * enabled in VME mask
+ */
+ tt_scu.vme_mask = 0x60; /* enable MFP and SCC ints */
+ }
+ else {
+ /* If no SCU, the HSYNC interrupt needs to be disabled this
+ * way. (Else _inthandler in kernel/sys_call.S gets overruns)
+ */
+ vectors[VEC_INT2] = falcon_hblhandler;
+ }
+
+ if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) {
+ /* Initialize the LM1992 Sound Controller to enable
+ the PSG sound. This is misplaced here, it should
+ be in an atasound_init(), that doesn't exist yet. */
+ atari_microwire_cmd(MW_LM1992_PSG_HIGH);
+ }
+
+ stdma_init();
+
+ /* Initialize the PSG: all sounds off, both ports output */
+ sound_ym.rd_data_reg_sel = 7;
+ sound_ym.wd_data = 0xff;
+}
+
+
+static void atari_call_irq_list( int irq, void *dev_id, struct pt_regs *fp )
+{
+ irq_node_t *node;
+
+ for (node = (irq_node_t *)dev_id; node; node = node->next)
+ node->handler(irq, node->dev_id, fp);
+}
+
+
+/*
+ * atari_request_irq : add an interrupt service routine for a particular
+ * machine specific interrupt source.
+ * If the addition was successful, it returns 0.
+ */
+
+int atari_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+ unsigned long flags, const char *devname, void *dev_id)
+{
+ int vector;
+
+ if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) {
+ printk ("%s: Bad irq type %ld requested from %s\n",
+ __FUNCTION__, flags, devname);
+ return -EINVAL;
+ }
+ if (!IS_VALID_INTNO(irq)) {
+ printk ("%s: Unknown irq %d requested from %s\n",
+ __FUNCTION__, irq, devname);
+ return -ENXIO;
+ }
+ vector = IRQ_SOURCE_TO_VECTOR(irq);
+
+ /*
+ * Check type/source combination: slow ints are (currently)
+ * only possible for MFP-interrupts.
+ */
+ if (flags == IRQ_TYPE_SLOW &&
+ (irq < STMFP_SOURCE_BASE || irq >= SCC_SOURCE_BASE)) {
+ printk ("%s: Slow irq requested for non-MFP source %d from %s\n",
+ __FUNCTION__, irq, devname);
+ return -EINVAL;
+ }
+
+ if (vectors[vector] == bad_interrupt) {
+ /* int has no handler yet */
+ irq_handler[irq].handler = handler;
+ irq_handler[irq].dev_id = dev_id;
+ irq_param[irq].flags = flags;
+ irq_param[irq].devname = devname;
+ vectors[vector] =
+ (flags == IRQ_TYPE_SLOW) ? slow_handlers[irq-STMFP_SOURCE_BASE] :
+ (flags == IRQ_TYPE_FAST) ? atari_fast_irq_handler :
+ atari_prio_irq_handler;
+ /* If MFP int, also enable and umask it */
+ atari_turnon_irq(irq);
+ atari_enable_irq(irq);
+
+ return 0;
+ }
+ else if (irq_param[irq].flags == flags) {
+ /* old handler is of same type -> handlers can be chained */
+ irq_node_t *node;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (irq_handler[irq].handler != atari_call_irq_list) {
+ /* Only one handler yet, make a node for this first one */
+ if (!(node = new_irq_node()))
+ return -ENOMEM;
+ node->handler = irq_handler[irq].handler;
+ node->dev_id = irq_handler[irq].dev_id;
+ node->devname = irq_param[irq].devname;
+ node->next = NULL;
+
+ irq_handler[irq].handler = atari_call_irq_list;
+ irq_handler[irq].dev_id = node;
+ irq_param[irq].devname = "chained";
+ }
+
+ if (!(node = new_irq_node()))
+ return -ENOMEM;
+ node->handler = handler;
+ node->dev_id = dev_id;
+ node->devname = devname;
+ /* new handlers are put in front of the queue */
+ node->next = irq_handler[irq].dev_id;
+ irq_handler[irq].dev_id = node;
+
+ restore_flags(flags);
+ return 0;
+ } else {
+ printk ("%s: Irq %d allocated by other type int (call from %s)\n",
+ __FUNCTION__, irq, devname);
+ return -EBUSY;
+ }
+}
+
+void atari_free_irq(unsigned int irq, void *dev_id)
+{
+ unsigned long flags;
+ int vector;
+ irq_node_t **list, *node;
+
+ if (!IS_VALID_INTNO(irq)) {
+ printk("%s: Unknown irq %d\n", __FUNCTION__, irq);
+ return;
+ }
+
+ vector = IRQ_SOURCE_TO_VECTOR(irq);
+ if (vectors[vector] == bad_interrupt)
+ goto not_found;
+
+ save_flags(flags);
+ cli();
+
+ if (irq_handler[irq].handler != atari_call_irq_list) {
+ /* It's the only handler for the interrupt */
+ if (irq_handler[irq].dev_id != dev_id) {
+ restore_flags(flags);
+ goto not_found;
+ }
+ irq_handler[irq].handler = NULL;
+ irq_handler[irq].dev_id = NULL;
+ irq_param[irq].devname = NULL;
+ vectors[vector] = bad_interrupt;
+ /* If MFP int, also disable it */
+ atari_disable_irq(irq);
+ atari_turnoff_irq(irq);
+
+ restore_flags(flags);
+ return;
+ }
+
+ /* The interrupt is chained, find the irq on the list */
+ for(list = (irq_node_t **)&irq_handler[irq].dev_id; *list; list = &(*list)->next) {
+ if ((*list)->dev_id == dev_id) break;
+ }
+ if (!*list) {
+ restore_flags(flags);
+ goto not_found;
+ }
+
+ (*list)->handler = NULL; /* Mark it as free for reallocation */
+ *list = (*list)->next;
+
+ /* If there's now only one handler, unchain the interrupt, i.e. plug in
+ * the handler directly again and omit atari_call_irq_list */
+ node = (irq_node_t *)irq_handler[irq].dev_id;
+ if (node && !node->next) {
+ irq_handler[irq].handler = node->handler;
+ irq_handler[irq].dev_id = node->dev_id;
+ irq_param[irq].devname = node->devname;
+ node->handler = NULL; /* Mark it as free for reallocation */
+ }
+
+ restore_flags(flags);
+ return;
+
+not_found:
+ printk("%s: tried to remove invalid irq\n", __FUNCTION__);
+ return;
+}
+
+
+/*
+ * atari_register_vme_int() returns the number of a free interrupt vector for
+ * hardware with a programmable int vector (probably a VME board).
+ */
+
+unsigned long atari_register_vme_int(void)
+{
+ int i;
+
+ for(i = 0; i < 32; i++)
+ if((free_vme_vec_bitmap & (1 << i)) == 0)
+ break;
+
+ if(i == 16)
+ return 0;
+
+ free_vme_vec_bitmap |= 1 << i;
+ return (VME_SOURCE_BASE + i) | IRQ_MACHSPEC;
+}
+
+
+void atari_unregister_vme_int(unsigned long irq)
+{
+ irq &= ~IRQ_MACHSPEC;
+
+ if(irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) {
+ irq -= VME_SOURCE_BASE;
+ free_vme_vec_bitmap &= ~(1 << irq);
+ }
+}
+
+
+int atari_get_irq_list(char *buf)
+{
+ int i, len = 0;
+
+ for (i = 0; i < NUM_INT_SOURCES; ++i) {
+ if (vectors[IRQ_SOURCE_TO_VECTOR(i)] == bad_interrupt)
+ continue;
+ if (i < STMFP_SOURCE_BASE)
+ len += sprintf(buf+len, "auto %2d: %10u ",
+ i, kstat.interrupts[i]);
+ else
+ len += sprintf(buf+len, "vec $%02x: %10u ",
+ IRQ_SOURCE_TO_VECTOR(i),
+ kstat.interrupts[i]);
+
+ if (irq_handler[i].handler != atari_call_irq_list) {
+ len += sprintf(buf+len, "%s\n", irq_param[i].devname);
+ }
+ else {
+ irq_node_t *p;
+ for( p = (irq_node_t *)irq_handler[i].dev_id; p; p = p->next ) {
+ len += sprintf(buf+len, "%s\n", p->devname);
+ if (p->next)
+ len += sprintf( buf+len, " " );
+ }
+ }
+ }
+ if (num_spurious)
+ len += sprintf(buf+len, "spurio.: %10u\n", num_spurious);
+
+ return len;
+}
+
+
diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c
new file mode 100644
index 000000000..95cc0245f
--- /dev/null
+++ b/arch/m68k/atari/atakeyb.c
@@ -0,0 +1,847 @@
+/*
+ * linux/atari/atakeyb.c
+ *
+ * Atari Keyboard driver for 680x0 Linux
+ *
+ * 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.
+ */
+
+/*
+ * Atari support by Robert de Vries
+ * enhanced by Bjoern Brauel and Roman Hodek
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/keyboard.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/kd.h>
+#include <linux/random.h>
+
+#include <asm/atariints.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/atari_mouse.h>
+#include <asm/atari_joystick.h>
+#include <asm/irq.h>
+
+extern void handle_scancode(unsigned char);
+extern int ovsc_switchmode;
+extern unsigned char mach_keyboard_type;
+static void atakeyb_rep( unsigned long ignore );
+extern unsigned int keymap_count;
+
+/* Hook for MIDI serial driver */
+void (*atari_MIDI_interrupt_hook) (void);
+/* Hook for mouse driver */
+void (*atari_mouse_interrupt_hook) (char *);
+
+#define BREAK_MASK (0x80)
+
+/*
+ * ++roman: The following changes were applied manually:
+ *
+ * - The Alt (= Meta) key works in combination with Shift and
+ * Control, e.g. Alt+Shift+a sends Meta-A (0xc1), Alt+Control+A sends
+ * Meta-Ctrl-A (0x81) ...
+ *
+ * - The parentheses on the keypad send '(' and ')' with all
+ * modifiers (as would do e.g. keypad '+'), but they cannot be used as
+ * application keys (i.e. sending Esc O c).
+ *
+ * - HELP and UNDO are mapped to be F21 and F24, resp, that send the
+ * codes "\E[M" and "\E[P". (This is better than the old mapping to
+ * F11 and F12, because these codes are on Shift+F1/2 anyway.) This
+ * way, applications that allow their own keyboard mappings
+ * (e.g. tcsh, X Windows) can be configured to use them in the way
+ * the label suggests (providing help or undoing).
+ *
+ * - Console switching is done with Alt+Fx (consoles 1..10) and
+ * Shift+Alt+Fx (consoles 11..20).
+ *
+ * - The misc. special function implemented in the kernel are mapped
+ * to the following key combinations:
+ *
+ * ClrHome -> Home/Find
+ * Shift + ClrHome -> End/Select
+ * Shift + Up -> Page Up
+ * Shift + Down -> Page Down
+ * Alt + Help -> show system status
+ * Shift + Help -> show memory info
+ * Ctrl + Help -> show registers
+ * Ctrl + Alt + Del -> Reboot
+ * Alt + Undo -> switch to last console
+ * Shift + Undo -> send interrupt
+ * Alt + Insert -> stop/start output (same as ^S/^Q)
+ * Alt + Up -> Scroll back console (if implemented)
+ * Alt + Down -> Scroll forward console (if implemented)
+ * Alt + CapsLock -> NumLock
+ *
+ * ++Andreas:
+ *
+ * - Help mapped to K_HELP
+ * - Undo mapped to K_UNDO (= K_F246)
+ * - Keypad Left/Right Parenthesis mapped to new K_PPAREN[LR]
+ */
+
+static u_short ataplain_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
+ 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf008, 0xf009,
+ 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+ 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
+ 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
+ 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
+ 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf200,
+ 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
+ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atashift_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
+ 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf008, 0xf009,
+ 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
+ 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53,
+ 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
+ 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
+ 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf200,
+ 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
+ 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf200, 0xf200, 0xf117,
+ 0xf118, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf119, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf205, 0xf203, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atactrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
+ 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf07f, 0xf200, 0xf008, 0xf200,
+ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+ 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
+ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+ 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
+ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf07f, 0xf700, 0xf200,
+ 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
+ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atashift_ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf008, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf201, 0xf702, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200,
+ 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf117,
+ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short ataalt_map[NR_KEYS] = {
+ 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
+ 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf808, 0xf809,
+ 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
+ 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873,
+ 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b,
+ 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876,
+ 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf200,
+ 0xf703, 0xf820, 0xf208, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
+ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf200, 0xf200, 0xf114,
+ 0xf20b, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf20a, 0xf200, 0xf209, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf206, 0xf204, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf907,
+ 0xf908, 0xf909, 0xf904, 0xf905, 0xf906, 0xf901, 0xf902, 0xf903,
+ 0xf900, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atashift_alt_map[NR_KEYS] = {
+ 0xf200, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e,
+ 0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf808, 0xf809,
+ 0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849,
+ 0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf201, 0xf702, 0xf841, 0xf853,
+ 0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a,
+ 0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856,
+ 0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf200,
+ 0xf703, 0xf820, 0xf207, 0xf50a, 0xf50b, 0xf50c, 0xf50d, 0xf50e,
+ 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513, 0xf200, 0xf200, 0xf117,
+ 0xf118, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf119, 0xf200, 0xf115, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atactrl_alt_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf800, 0xf81b, 0xf81c, 0xf81d, 0xf81e,
+ 0xf81f, 0xf87f, 0xf200, 0xf200, 0xf87f, 0xf200, 0xf808, 0xf200,
+ 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
+ 0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf201, 0xf702, 0xf801, 0xf813,
+ 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200,
+ 0xf807, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816,
+ 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf87f, 0xf700, 0xf200,
+ 0xf703, 0xf800, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
+ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short atashift_ctrl_alt_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf808, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf201, 0xf702, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700, 0xf200,
+ 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf117,
+ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
+ 0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
+ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
+ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+typedef enum kb_state_t
+{
+ KEYBOARD, AMOUSE, RMOUSE, JOYSTICK, CLOCK, RESYNC
+} KB_STATE_T;
+
+#define IS_SYNC_CODE(sc) ((sc) >= 0x04 && (sc) <= 0xfb)
+
+typedef struct keyboard_state
+{
+ unsigned char buf[6];
+ int len;
+ KB_STATE_T state;
+} KEYBOARD_STATE;
+
+KEYBOARD_STATE kb_state;
+
+#define DEFAULT_KEYB_REP_DELAY (HZ/4)
+#define DEFAULT_KEYB_REP_RATE (HZ/25)
+
+/* These could be settable by some ioctl() in future... */
+static unsigned int key_repeat_delay = DEFAULT_KEYB_REP_DELAY;
+static unsigned int key_repeat_rate = DEFAULT_KEYB_REP_RATE;
+
+static unsigned char rep_scancode;
+static struct timer_list atakeyb_rep_timer = { NULL, NULL, 0, 0, atakeyb_rep };
+
+extern struct pt_regs *pt_regs;
+
+static void atakeyb_rep( unsigned long ignore )
+
+{
+ pt_regs = NULL;
+
+ /* Disable keyboard for the time we call handle_scancode(), else a race
+ * in the keyboard tty queue may happen */
+ atari_disable_irq( IRQ_MFP_ACIA );
+ del_timer( &atakeyb_rep_timer );
+
+ /* A keyboard int may have come in before we disabled the irq, so
+ * double-check whether rep_scancode is still != 0 */
+ if (rep_scancode) {
+ atakeyb_rep_timer.expires = jiffies + key_repeat_rate;
+ atakeyb_rep_timer.prev = atakeyb_rep_timer.next = NULL;
+ add_timer( &atakeyb_rep_timer );
+
+ handle_scancode(rep_scancode);
+ }
+
+ atari_enable_irq( IRQ_MFP_ACIA );
+}
+
+
+/* ++roman: If a keyboard overrun happened, we can't tell in general how much
+ * bytes have been lost and in which state of the packet structure we are now.
+ * This usually causes keyboards bytes to be interpreted as mouse movements
+ * and vice versa, which is very annoying. It seems better to throw away some
+ * bytes (that are usually mouse bytes) than to misinterpret them. Therefor I
+ * introduced the RESYNC state for IKBD data. In this state, the bytes up to
+ * one that really looks like a key event (0x04..0xf2) or the start of a mouse
+ * packet (0xf8..0xfb) are thrown away, but at most 2 bytes. This at least
+ * speeds up the resynchronization of the event structure, even if maybe a
+ * mouse movement is lost. However, nothing is perfect. For bytes 0x01..0x03,
+ * it's really hard to decide whether they're mouse or keyboard bytes. Since
+ * overruns usually occur when moving the Atari mouse rapidly, they're seen as
+ * mouse bytes here. If this is wrong, only a make code of the keyboard gets
+ * lost, which isn't too bad. Loosing a break code would be disastrous,
+ * because then the keyboard repeat strikes...
+ */
+
+static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+ u_char acia_stat;
+ int scancode;
+ int break_flag;
+
+ /* save frame for register dump */
+ pt_regs = (struct pt_regs *)fp;
+
+ repeat:
+ if (acia.mid_ctrl & ACIA_IRQ)
+ if (atari_MIDI_interrupt_hook)
+ atari_MIDI_interrupt_hook();
+ acia_stat = acia.key_ctrl;
+ /* check out if the interrupt came from this ACIA */
+ if (!((acia_stat | acia.mid_ctrl) & ACIA_IRQ))
+ return;
+
+ if (acia_stat & ACIA_OVRN)
+ {
+ /* a very fast typist or a slow system, give a warning */
+ /* ...happens often if interrupts were disabled for too long */
+ printk( "Keyboard overrun\n" );
+ scancode = acia.key_data;
+ /* Turn off autorepeating in case a break code has been lost */
+ del_timer( &atakeyb_rep_timer );
+ rep_scancode = 0;
+ if (IS_SYNC_CODE(scancode)) {
+ /* This code seem already to be the start of a new packet or a
+ * single scancode */
+ kb_state.state = KEYBOARD;
+ goto interpret_scancode;
+ }
+ else {
+ /* Go to RESYNC state and skip this byte */
+ kb_state.state = RESYNC;
+ kb_state.len = 1; /* skip max. 1 another byte */
+ goto repeat;
+ }
+ }
+
+ if (acia_stat & ACIA_RDRF) /* received a character */
+ {
+ scancode = acia.key_data; /* get it or reset the ACIA, I'll get it! */
+ mark_bh(KEYBOARD_BH);
+ interpret_scancode:
+ switch (kb_state.state)
+ {
+ case KEYBOARD:
+ switch (scancode)
+ {
+ case 0xF7:
+ kb_state.state = AMOUSE;
+ kb_state.len = 0;
+ break;
+
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ kb_state.state = RMOUSE;
+ kb_state.len = 1;
+ kb_state.buf[0] = scancode;
+ break;
+
+ case 0xFC:
+ kb_state.state = CLOCK;
+ kb_state.len = 0;
+ break;
+
+ case 0xFE:
+ case 0xFF:
+ kb_state.state = JOYSTICK;
+ kb_state.len = 1;
+ kb_state.buf[0] = scancode;
+ break;
+
+ default:
+ break_flag = scancode & BREAK_MASK;
+ scancode &= ~BREAK_MASK;
+
+ if (break_flag) {
+ del_timer( &atakeyb_rep_timer );
+ rep_scancode = 0;
+ }
+ else {
+ del_timer( &atakeyb_rep_timer );
+ rep_scancode = scancode;
+ atakeyb_rep_timer.expires = jiffies + key_repeat_delay;
+ atakeyb_rep_timer.prev = atakeyb_rep_timer.next = NULL;
+ add_timer( &atakeyb_rep_timer );
+ }
+
+ handle_scancode(break_flag | scancode);
+ break;
+ }
+ break;
+
+ case AMOUSE:
+ kb_state.buf[kb_state.len++] = scancode;
+ if (kb_state.len == 5)
+ {
+ kb_state.state = KEYBOARD;
+ /* not yet used */
+ /* wake up someone waiting for this */
+ }
+ break;
+
+ case RMOUSE:
+ kb_state.buf[kb_state.len++] = scancode;
+ if (kb_state.len == 3)
+ {
+ kb_state.state = KEYBOARD;
+ if (atari_mouse_interrupt_hook)
+ atari_mouse_interrupt_hook(kb_state.buf);
+ }
+ break;
+
+ case JOYSTICK:
+ kb_state.buf[1] = scancode;
+ kb_state.state = KEYBOARD;
+ atari_joystick_interrupt(kb_state.buf);
+ break;
+
+ case CLOCK:
+ kb_state.buf[kb_state.len++] = scancode;
+ if (kb_state.len == 6)
+ {
+ kb_state.state = KEYBOARD;
+ /* wake up someone waiting for this.
+ But will this ever be used, as Linux keeps its own time.
+ Perhaps for synchronization purposes? */
+ /* wake_up_interruptible(&clock_wait); */
+ }
+ break;
+
+ case RESYNC:
+ if (kb_state.len <= 0 || IS_SYNC_CODE(scancode)) {
+ kb_state.state = KEYBOARD;
+ goto interpret_scancode;
+ }
+ kb_state.len--;
+ break;
+ }
+ }
+
+#ifdef KEYB_WRITE_INTERRUPT
+ if (acia_stat & ACIA_TDRE) /* transmit of character is finished */
+ {
+ if (kb_state.buf)
+ {
+ acia.key_data = *kb_state.buf++;
+ kb_state.len--;
+ if (kb_state.len == 0)
+ {
+ kb_state.buf = NULL;
+ if (!kb_state.kernel_mode)
+ /* unblock something */;
+ }
+ }
+ }
+#endif
+
+#if 0
+ if (acia_stat & ACIA_CTS)
+ /* cannot happen */;
+#endif
+
+ if (acia_stat & (ACIA_FE | ACIA_PE))
+ {
+ printk("Error in keyboard communication\n");
+ }
+
+ /* handle_scancode() can take a lot of time, so check again if
+ * some character arrived
+ */
+ goto repeat;
+}
+
+#ifdef KEYB_WRITE_INTERRUPT
+void ikbd_write(const char *str, int len)
+{
+ u_char acia_stat;
+
+ if (kb_stat.buf)
+ /* wait */;
+ acia_stat = acia.key_ctrl;
+ if (acia_stat & ACIA_TDRE)
+ {
+ if (len != 1)
+ {
+ kb_stat.buf = str + 1;
+ kb_stat.len = len - 1;
+ }
+ acia.key_data = *str;
+ /* poll */
+ }
+}
+#else
+/*
+ * I write to the keyboard without using interrupts, I poll instead.
+ * This takes for the maximum length string allowed (7) at 7812.5 baud
+ * 8 data 1 start 1 stop bit: 9.0 ms
+ * If this takes too long for normal operation, interrupt driven writing
+ * is the solution. (I made a feeble attempt in that direction but I
+ * kept it simple for now.)
+ */
+void ikbd_write(const char *str, int len)
+{
+ u_char acia_stat;
+
+ if ((len < 1) || (len > 7))
+ panic("ikbd: maximum string length exceeded");
+ while (len)
+ {
+ acia_stat = acia.key_ctrl;
+ if (acia_stat & ACIA_TDRE)
+ {
+ acia.key_data = *str++;
+ len--;
+ }
+ }
+}
+#endif
+
+/* Reset (without touching the clock) */
+void ikbd_reset(void)
+{
+ static const char cmd[2] = { 0x80, 0x01 };
+
+ ikbd_write(cmd, 2);
+
+ /* if all's well code 0xF1 is returned, else the break codes of
+ all keys making contact */
+}
+
+/* Set mouse button action */
+void ikbd_mouse_button_action(int mode)
+{
+ char cmd[2] = { 0x07, mode };
+
+ ikbd_write(cmd, 2);
+}
+
+/* Set relative mouse position reporting */
+void ikbd_mouse_rel_pos(void)
+{
+ static const char cmd[1] = { 0x08 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Set absolute mouse position reporting */
+void ikbd_mouse_abs_pos(int xmax, int ymax)
+{
+ char cmd[5] = { 0x09, xmax>>8, xmax&0xFF, ymax>>8, ymax&0xFF };
+
+ ikbd_write(cmd, 5);
+}
+
+/* Set mouse keycode mode */
+void ikbd_mouse_kbd_mode(int dx, int dy)
+{
+ char cmd[3] = { 0x0A, dx, dy };
+
+ ikbd_write(cmd, 3);
+}
+
+/* Set mouse threshold */
+void ikbd_mouse_thresh(int x, int y)
+{
+ char cmd[3] = { 0x0B, x, y };
+
+ ikbd_write(cmd, 3);
+}
+
+/* Set mouse scale */
+void ikbd_mouse_scale(int x, int y)
+{
+ char cmd[3] = { 0x0C, x, y };
+
+ ikbd_write(cmd, 3);
+}
+
+/* Interrogate mouse position */
+void ikbd_mouse_pos_get(int *x, int *y)
+{
+ static const char cmd[1] = { 0x0D };
+
+ ikbd_write(cmd, 1);
+
+ /* wait for returning bytes */
+}
+
+/* Load mouse position */
+void ikbd_mouse_pos_set(int x, int y)
+{
+ char cmd[6] = { 0x0E, 0x00, x>>8, x&0xFF, y>>8, y&0xFF };
+
+ ikbd_write(cmd, 6);
+}
+
+/* Set Y=0 at bottom */
+void ikbd_mouse_y0_bot(void)
+{
+ static const char cmd[1] = { 0x0F };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Set Y=0 at top */
+void ikbd_mouse_y0_top(void)
+{
+ static const char cmd[1] = { 0x10 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Resume */
+void ikbd_resume(void)
+{
+ static const char cmd[1] = { 0x11 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Disable mouse */
+void ikbd_mouse_disable(void)
+{
+ static const char cmd[1] = { 0x12 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Pause output */
+void ikbd_pause(void)
+{
+ static const char cmd[1] = { 0x13 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Set joystick event reporting */
+void ikbd_joystick_event_on(void)
+{
+ static const char cmd[1] = { 0x14 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Set joystick interrogation mode */
+void ikbd_joystick_event_off(void)
+{
+ static const char cmd[1] = { 0x15 };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Joystick interrogation */
+void ikbd_joystick_get_state(void)
+{
+ static const char cmd[1] = { 0x16 };
+
+ ikbd_write(cmd, 1);
+}
+
+#if 0
+/* This disables all other ikbd activities !!!! */
+/* Set joystick monitoring */
+void ikbd_joystick_monitor(int rate)
+{
+ static const char cmd[2] = { 0x17, rate };
+
+ ikbd_write(cmd, 2);
+
+ kb_state.state = JOYSTICK_MONITOR;
+}
+#endif
+
+/* some joystick routines not in yet (0x18-0x19) */
+
+/* Disable joysticks */
+void ikbd_joystick_disable(void)
+{
+ static const char cmd[1] = { 0x1A };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Time-of-day clock set */
+void ikbd_clock_set(int year, int month, int day, int hour, int minute, int second)
+{
+ char cmd[7] = { 0x1B, year, month, day, hour, minute, second };
+
+ ikbd_write(cmd, 7);
+}
+
+/* Interrogate time-of-day clock */
+void ikbd_clock_get(int *year, int *month, int *day, int *hour, int *minute, int second)
+{
+ static const char cmd[1] = { 0x1C };
+
+ ikbd_write(cmd, 1);
+}
+
+/* Memory load */
+void ikbd_mem_write(int address, int size, char *data)
+{
+ panic("Attempt to write data into keyboard memory");
+}
+
+/* Memory read */
+void ikbd_mem_read(int address, char data[6])
+{
+ char cmd[3] = { 0x21, address>>8, address&0xFF };
+
+ ikbd_write(cmd, 3);
+
+ /* receive data and put it in data */
+}
+
+/* Controller execute */
+void ikbd_exec(int address)
+{
+ char cmd[3] = { 0x22, address>>8, address&0xFF };
+
+ ikbd_write(cmd, 3);
+}
+
+/* Status inquiries (0x87-0x9A) not yet implemented */
+
+/* Set the state of the caps lock led. */
+void atari_kbd_leds (unsigned int leds)
+{
+ char cmd[6] = {32, 0, 4, 1, 254 + ((leds & 4) != 0), 0};
+ ikbd_write(cmd, 6);
+}
+
+/*
+ * The original code sometimes left the interrupt line of
+ * the ACIAs low forever. I hope, it is fixed now.
+ *
+ * Martin Rogge, 20 Aug 1995
+ */
+
+int atari_keyb_init(void)
+{
+ /* setup key map */
+ key_maps[0] = ataplain_map;
+ key_maps[1] = atashift_map;
+ key_maps[2] = 0; /* ataaltgr_map */
+ key_maps[4] = atactrl_map;
+ key_maps[5] = atashift_ctrl_map;
+ key_maps[8] = ataalt_map;
+ key_maps[9] = atashift_alt_map;
+ key_maps[12] = atactrl_alt_map;
+ key_maps[13] = atashift_ctrl_alt_map;
+ memcpy (plain_map, ataplain_map, sizeof(plain_map));
+ keymap_count = 8;
+
+ /* say that we don't have an AltGr key */
+ mach_keyboard_type = KB_84;
+
+ kb_state.state = KEYBOARD;
+ kb_state.len = 0;
+
+ request_irq(IRQ_MFP_ACIA, keyboard_interrupt, IRQ_TYPE_SLOW,
+ "keyboard/mouse/MIDI", keyboard_interrupt);
+
+ atari_turnoff_irq(IRQ_MFP_ACIA);
+ do {
+ acia.key_ctrl = ACIA_RESET; /* reset ACIA */
+ (void)acia.key_ctrl;
+ (void)acia.key_data;
+
+ acia.mid_ctrl = ACIA_RESET; /* reset other ACIA */
+ (void)acia.mid_ctrl;
+ (void)acia.mid_data;
+
+ /* divide 500kHz by 64 gives 7812.5 baud */
+ /* 8 data no parity 1 start 1 stop bit */
+ /* receive interrupt enabled */
+#ifdef KEYB_WRITE_INTERRUPT
+ /* RTS low, transmit interrupt enabled */
+ if (ovsc_switchmode == 1)
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTIE|ACIA_RIE);
+ /* switch on OverScan via keyboard ACIA */
+ else
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RLTIE|ACIA_RIE);
+#else
+ /* RTS low, transmit interrupt disabled */
+ if (ovsc_switchmode == 1)
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE);
+ else
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RLTID|ACIA_RIE);
+#endif
+
+ acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S;
+ }
+ /* make sure the interrupt line is up */
+ while ((mfp.par_dt_reg & 0x10) == 0);
+
+ /* enable ACIA Interrupts */
+ mfp.active_edge &= ~0x10;
+ atari_turnon_irq(IRQ_MFP_ACIA);
+
+ ikbd_reset();
+ ikbd_mouse_disable();
+ ikbd_joystick_disable();
+
+ atari_joystick_init();
+
+ return 0;
+}
+
+
+int atari_kbdrate( struct kbd_repeat *k )
+
+{
+ if (k->delay > 0) {
+ /* convert from msec to jiffies */
+ key_repeat_delay = (k->delay * HZ + 500) / 1000;
+ if (key_repeat_delay < 1)
+ key_repeat_delay = 1;
+ }
+ if (k->rate > 0) {
+ key_repeat_rate = (k->rate * HZ + 500) / 1000;
+ if (key_repeat_rate < 1)
+ key_repeat_rate = 1;
+ }
+
+ k->delay = key_repeat_delay * 1000 / HZ;
+ k->rate = key_repeat_rate * 1000 / HZ;
+
+ return( 0 );
+}
diff --git a/arch/m68k/atari/atasound.c b/arch/m68k/atari/atasound.c
new file mode 100644
index 000000000..33f7f79c6
--- /dev/null
+++ b/arch/m68k/atari/atasound.c
@@ -0,0 +1,149 @@
+/*
+linux/arch/m68k/atari/atasound.c
+
+++Geert: Moved almost all stuff to linux/drivers/sound/
+
+The author of atari_nosound, atari_mksound and atari_microwire_cmd is
+unknown.
+(++roman: That's me... :-)
+
+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.
+
+*/
+
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/atariints.h>
+
+
+/*
+ * stuff from the old atasound.c
+ */
+
+
+static void atari_nosound (unsigned long ignored)
+{
+ unsigned char tmp;
+ unsigned long flags;
+
+ /* turn off generator A in mixer control */
+ save_flags(flags);
+ cli();
+ sound_ym.rd_data_reg_sel = 7;
+ tmp = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = tmp | 0x39;
+ restore_flags(flags);
+}
+
+
+void atari_microwire_cmd (int cmd)
+{
+ tt_microwire.mask = 0x7ff;
+ tt_microwire.data = MW_LM1992_ADDR | cmd;
+
+ /* Busy wait for data being completely sent :-( */
+ while( tt_microwire.mask != 0x7ff)
+ ;
+}
+
+
+#define PC_FREQ 1192180
+#define PSG_FREQ 125000
+
+
+void atari_mksound (unsigned int count, unsigned int ticks)
+{
+ static struct timer_list sound_timer = { NULL, NULL, 0, 0,
+ atari_nosound };
+ /*
+ * Generates sound of some count for some number of clock ticks
+ * [count = 1193180 / frequency]
+ */
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+
+ if (count == 750 && ticks == HZ/8) {
+ /* Special case: These values are used by console.c to
+ * generate the console bell. They are cached here and the
+ * sound actually generated is somehow special: it uses the
+ * generator B and an envelope. No timer is needed therefore
+ * and the bell doesn't disturb an other ongoing sound.
+ */
+
+ /* set envelope duration to 492 ms */
+ sound_ym.rd_data_reg_sel = 11;
+ sound_ym.wd_data = 0;
+ sound_ym.rd_data_reg_sel = 12;
+ sound_ym.wd_data = 15;
+ /* envelope form: max -> min single */
+ sound_ym.rd_data_reg_sel = 13;
+ sound_ym.wd_data = 9;
+ /* set generator B frequency to 2400 Hz */
+ sound_ym.rd_data_reg_sel = 2;
+ sound_ym.wd_data = 52;
+ sound_ym.rd_data_reg_sel = 3;
+ sound_ym.wd_data = 0;
+ /* set volume of generator B to envelope control */
+ sound_ym.rd_data_reg_sel = 9;
+ sound_ym.wd_data = 0x10;
+ /* enable generator B in the mixer control */
+ sound_ym.rd_data_reg_sel = 7;
+ tmp = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = (tmp & ~0x02) | 0x38;
+
+ restore_flags(flags);
+ return;
+ }
+
+ del_timer( &sound_timer );
+
+ if (!count) {
+ atari_nosound( 0 );
+ }
+ else {
+
+ /* convert from frequency value
+ * to PSG period value (base frequency 125 kHz).
+ */
+ int period = PSG_FREQ / count;
+
+ if (period > 0xfff) period = 0xfff;
+
+ /* set generator A frequency to 0 */
+ sound_ym.rd_data_reg_sel = 0;
+ sound_ym.wd_data = period & 0xff;
+ sound_ym.rd_data_reg_sel = 1;
+ sound_ym.wd_data = (period >> 8) & 0xf;
+ /* turn on generator A in mixer control (but not noise
+ * generator!) */
+ sound_ym.rd_data_reg_sel = 7;
+ tmp = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = (tmp & ~0x01) | 0x38;
+ /* set generator A level to maximum, no envelope */
+ sound_ym.rd_data_reg_sel = 8;
+ sound_ym.wd_data = 15;
+
+ if (ticks) {
+ sound_timer.expires = jiffies + ticks;
+ add_timer( &sound_timer );
+ }
+ }
+
+ restore_flags(flags);
+}
diff --git a/arch/m68k/atari/atasound.h b/arch/m68k/atari/atasound.h
new file mode 100644
index 000000000..1362762b8
--- /dev/null
+++ b/arch/m68k/atari/atasound.h
@@ -0,0 +1,33 @@
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_NDEVS 256 /* Number of supported devices */
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#define MAX_AUDIO_DEV 5
+#define MAX_MIXER_DEV 2
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 6
+#define MAX_TIMER_DEV 3
diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c
new file mode 100644
index 000000000..f314fcbbd
--- /dev/null
+++ b/arch/m68k/atari/config.c
@@ -0,0 +1,1179 @@
+/*
+ * linux/arch/m68k/atari/config.c
+ *
+ * Copyright (C) 1994 Bjoern Brauel
+ *
+ * 5/2/94 Roman Hodek:
+ * Added setting of time_adj to get a better clock.
+ *
+ * 5/14/94 Roman Hodek:
+ * gettod() for TT
+ *
+ * 5/15/94 Roman Hodek:
+ * hard_reset_now() for Atari (and others?)
+ *
+ * 94/12/30 Andreas Schwab:
+ * atari_sched_init fixed to get precise clock.
+ *
+ * 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.
+ */
+
+/*
+ * Miscellaneous atari stuff
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/mc146818rtc.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atarihdreg.h>
+#include <asm/atariints.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+
+extern void atari_sched_init(void (*)(int, void *, struct pt_regs *));
+/* atari specific keyboard functions */
+extern int atari_keyb_init(void);
+extern int atari_kbdrate (struct kbd_repeat *);
+extern void atari_kbd_leds (unsigned int);
+/* atari specific irq functions */
+extern void atari_init_IRQ (void);
+extern int atari_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+ unsigned long flags, const char *devname, void *dev_id);
+extern int atari_free_irq (unsigned int irq, void *dev_id);
+extern void atari_enable_irq (unsigned int);
+extern void atari_disable_irq (unsigned int);
+extern int atari_get_irq_list (char *buf);
+/* atari specific timer functions */
+extern unsigned long atari_gettimeoffset (void);
+extern void atari_mste_gettod (int *, int *, int *, int *, int *, int *);
+extern void atari_gettod (int *, int *, int *, int *, int *, int *);
+extern int atari_mste_hwclk (int, struct hwclk_time *);
+extern int atari_hwclk (int, struct hwclk_time *);
+extern int atari_mste_set_clock_mmss (unsigned long);
+extern int atari_set_clock_mmss (unsigned long);
+extern void atari_mksound( unsigned int count, unsigned int ticks );
+extern void atari_reset( void );
+#ifdef CONFIG_BLK_DEV_FD
+extern int atari_floppy_init (void);
+extern void atari_floppy_setup(char *, int *);
+#endif
+extern void atari_waitbut (void);
+extern struct consw fb_con;
+extern struct fb_info *atari_fb_init(long *);
+extern void atari_debug_init (void);
+extern void atari_video_setup(char *, int *);
+
+extern void (*kd_mksound)(unsigned int, unsigned int);
+
+/* This function tests for the presence of an address, specially a
+ * hardware register address. It is called very early in the kernel
+ * initialization process, when the VBR register isn't set up yet. On
+ * an Atari, it still points to address 0, which is unmapped. So a bus
+ * error would cause another bus error while fetching the exception
+ * vector, and the CPU would do nothing at all. So we needed to set up
+ * a temporary VBR and a vector table for the duration of the test.
+ */
+
+static int hwreg_present( volatile void *regp )
+{
+ int ret = 0;
+ long save_sp, save_vbr;
+ long tmp_vectors[3];
+
+ __asm__ __volatile__
+ ( "movec %/vbr,%2\n\t"
+ "movel #Lberr1,%4@(8)\n\t"
+ "movec %4,%/vbr\n\t"
+ "movel %/sp,%1\n\t"
+ "moveq #0,%0\n\t"
+ "tstb %3@\n\t"
+ "nop\n\t"
+ "moveq #1,%0\n"
+ "Lberr1:\n\t"
+ "movel %1,%/sp\n\t"
+ "movec %2,%/vbr"
+ : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
+ : "a" (regp), "a" (tmp_vectors)
+ );
+
+ return( ret );
+}
+
+#if 0
+static int hwreg_present_bywrite( volatile void *regp,
+ unsigned char val )
+
+{
+ int ret;
+ long save_sp, save_vbr;
+ static long tmp_vectors[3] = { 0, 0, (long)&&after_test };
+
+ __asm__ __volatile__
+ ( "movec %/vbr,%2\n\t" /* save vbr value */
+ "movec %4,%/vbr\n\t" /* set up temporary vectors */
+ "movel %/sp,%1\n\t" /* save sp */
+ "moveq #0,%0\n\t" /* assume not present */
+ "moveb %5,%3@\n\t" /* write the hardware reg */
+ "cmpb %3@,%5\n\t" /* compare it */
+ "seq %0" /* comes here only if reg */
+ /* is present */
+ : "=d&" (ret), "=r&" (save_sp), "=r&" (save_vbr)
+ : "a" (regp), "r" (tmp_vectors), "d" (val)
+ );
+ after_test:
+ __asm__ __volatile__
+ ( "movel %0,%/sp\n\t" /* restore sp */
+ "movec %1,%/vbr" /* restore vbr */
+ : : "r" (save_sp), "r" (save_vbr) : "sp"
+ );
+
+ return( ret );
+}
+#endif
+
+/* Basically the same, but writes a value into a word register, protected
+ * by a bus error handler */
+
+static int hwreg_write( volatile void *regp, unsigned short val )
+{
+ int ret;
+ long save_sp, save_vbr;
+ long tmp_vectors[3];
+
+ __asm__ __volatile__
+ ( "movec %/vbr,%2\n\t"
+ "movel #Lberr2,%4@(8)\n\t"
+ "movec %4,%/vbr\n\t"
+ "movel %/sp,%1\n\t"
+ "moveq #0,%0\n\t"
+ "movew %5,%3@\n\t"
+ "nop \n\t" /* If this nop isn't present, 'ret' may already be
+ * loaded with 1 at the time the bus error
+ * happens! */
+ "moveq #1,%0\n"
+ "Lberr2:\n\t"
+ "movel %1,%/sp\n\t"
+ "movec %2,%/vbr"
+ : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
+ : "a" (regp), "a" (tmp_vectors), "g" (val)
+ );
+
+ return( ret );
+}
+
+/* ++roman: This is a more elaborate test for an SCC chip, since the plain
+ * Medusa board generates DTACK at the SCC's standard addresses, but a SCC
+ * board in the Medusa is possible. Also, the addresses where the ST_ESCC
+ * resides generate DTACK without the chip, too.
+ * The method is to write values into the interrupt vector register, that
+ * should be readable without trouble (from channel A!).
+ */
+
+static int scc_test( volatile char *ctla )
+{
+ if (!hwreg_present( ctla ))
+ return( 0 );
+ MFPDELAY();
+
+ *ctla = 2; MFPDELAY();
+ *ctla = 0x40; MFPDELAY();
+
+ *ctla = 2; MFPDELAY();
+ if (*ctla != 0x40) return( 0 );
+ MFPDELAY();
+
+ *ctla = 2; MFPDELAY();
+ *ctla = 0x60; MFPDELAY();
+
+ *ctla = 2; MFPDELAY();
+ if (*ctla != 0x60) return( 0 );
+
+ return( 1 );
+}
+
+void config_atari(void)
+{
+ mach_sched_init = atari_sched_init;
+ mach_keyb_init = atari_keyb_init;
+ mach_kbdrate = atari_kbdrate;
+ mach_kbd_leds = atari_kbd_leds;
+ mach_init_IRQ = atari_init_IRQ;
+ mach_request_irq = atari_request_irq;
+ mach_free_irq = atari_free_irq;
+ mach_enable_irq = atari_enable_irq;
+ mach_disable_irq = atari_disable_irq;
+ mach_get_irq_list = atari_get_irq_list;
+ mach_gettimeoffset = atari_gettimeoffset;
+ mach_mksound = atari_mksound;
+ mach_reset = atari_reset;
+#ifdef CONFIG_BLK_DEV_FD
+ mach_floppy_init = atari_floppy_init;
+ mach_floppy_setup = atari_floppy_setup;
+#endif
+ conswitchp = &fb_con;
+ waitbut = atari_waitbut;
+ mach_fb_init = atari_fb_init;
+ mach_max_dma_address = 0xffffff;
+ mach_debug_init = atari_debug_init;
+ mach_video_setup = atari_video_setup;
+ kd_mksound = atari_mksound;
+
+ /* ++bjoern:
+ * Determine hardware present
+ */
+
+ printk( "Atari hardware found: " );
+ if (is_medusa) {
+ /* There's no Atari video hardware on the Medusa, but all the
+ * addresses below generate a DTACK so no bus error occurs! */
+ }
+ else if (hwreg_present( f030_xreg )) {
+ ATARIHW_SET(VIDEL_SHIFTER);
+ printk( "VIDEL " );
+ /* This is a temporary hack: If there is Falcon video
+ * hardware, we assume that the ST-DMA serves SCSI instead of
+ * ACSI. In the future, there should be a better method for
+ * this...
+ */
+ ATARIHW_SET(ST_SCSI);
+ printk( "STDMA-SCSI " );
+ }
+ else if (hwreg_present( tt_palette )) {
+ ATARIHW_SET(TT_SHIFTER);
+ printk( "TT_SHIFTER " );
+ }
+ else if (hwreg_present( &shifter.bas_hi )) {
+ if (hwreg_present( &shifter.bas_lo ) &&
+ (shifter.bas_lo = 0x0aau, shifter.bas_lo == 0x0aau)) {
+ ATARIHW_SET(EXTD_SHIFTER);
+ printk( "EXTD_SHIFTER " );
+ }
+ else {
+ ATARIHW_SET(STND_SHIFTER);
+ printk( "STND_SHIFTER " );
+ }
+ }
+ if (hwreg_present( &mfp.par_dt_reg )) {
+ ATARIHW_SET(ST_MFP);
+ printk( "ST_MFP " );
+ }
+ if (hwreg_present( &tt_mfp.par_dt_reg )) {
+ ATARIHW_SET(TT_MFP);
+ printk( "TT_MFP " );
+ }
+ if (hwreg_present( &tt_scsi_dma.dma_addr_hi )) {
+ ATARIHW_SET(SCSI_DMA);
+ printk( "TT_SCSI_DMA " );
+ }
+ if (hwreg_present( &st_dma.dma_hi )) {
+ ATARIHW_SET(STND_DMA);
+ printk( "STND_DMA " );
+ }
+ if (is_medusa || /* The ST-DMA address registers aren't readable
+ * on all Medusas, so the test below may fail */
+ (hwreg_present( &st_dma.dma_vhi ) &&
+ (st_dma.dma_vhi = 0x55) && (st_dma.dma_hi = 0xaa) &&
+ st_dma.dma_vhi == 0x55 && st_dma.dma_hi == 0xaa &&
+ (st_dma.dma_vhi = 0xaa) && (st_dma.dma_hi = 0x55) &&
+ st_dma.dma_vhi == 0xaa && st_dma.dma_hi == 0x55)) {
+ ATARIHW_SET(EXTD_DMA);
+ printk( "EXTD_DMA " );
+ }
+ if (hwreg_present( &tt_scsi.scsi_data )) {
+ ATARIHW_SET(TT_SCSI);
+ printk( "TT_SCSI " );
+ }
+ if (hwreg_present( &sound_ym.rd_data_reg_sel )) {
+ ATARIHW_SET(YM_2149);
+ printk( "YM2149 " );
+ }
+ if (!is_medusa && hwreg_present( &tt_dmasnd.ctrl )) {
+ ATARIHW_SET(PCM_8BIT);
+ printk( "PCM " );
+ }
+ if (hwreg_present( (void *)(0xffff8940) )) {
+ ATARIHW_SET(CODEC);
+ printk( "CODEC " );
+ }
+ if (hwreg_present( &tt_scc_dma.dma_ctrl ) &&
+#if 0
+ /* This test sucks! Who knows some better? */
+ (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) &&
+ (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0)
+#else
+ !is_medusa
+#endif
+ ) {
+ ATARIHW_SET(SCC_DMA);
+ printk( "SCC_DMA " );
+ }
+ if (scc_test( &scc.cha_a_ctrl )) {
+ ATARIHW_SET(SCC);
+ printk( "SCC " );
+ }
+ if (scc_test( &st_escc.cha_b_ctrl )) {
+ ATARIHW_SET( ST_ESCC );
+ printk( "ST_ESCC " );
+ }
+ if (hwreg_present( &tt_scu.sys_mask )) {
+ ATARIHW_SET(SCU);
+ /* Assume a VME bus if there's a SCU */
+ ATARIHW_SET( VME );
+ printk( "VME SCU " );
+ }
+ if (hwreg_present( (void *)(0xffff9210) )) {
+ ATARIHW_SET(ANALOG_JOY);
+ printk( "ANALOG_JOY " );
+ }
+ if (hwreg_present( blitter.halftone )) {
+ ATARIHW_SET(BLITTER);
+ printk( "BLITTER " );
+ }
+ if (hwreg_present( (void *)(ATA_HD_BASE+ATA_HD_CMD) )) {
+ ATARIHW_SET(IDE);
+ printk( "IDE " );
+ }
+#if 1 /* This maybe wrong */
+ if (!is_medusa &&
+ hwreg_present( &tt_microwire.data ) &&
+ hwreg_present( &tt_microwire.mask ) &&
+ (tt_microwire.mask = 0x7ff,
+ tt_microwire.data = MW_LM1992_PSG_HIGH | MW_LM1992_ADDR,
+ tt_microwire.data != 0)) {
+ ATARIHW_SET(MICROWIRE);
+ while (tt_microwire.mask != 0x7ff) ;
+ printk( "MICROWIRE " );
+ }
+#endif
+ if (hwreg_present( &tt_rtc.regsel )) {
+ ATARIHW_SET(TT_CLK);
+ printk( "TT_CLK " );
+ mach_gettod = atari_gettod;
+ mach_hwclk = atari_hwclk;
+ mach_set_clock_mmss = atari_set_clock_mmss;
+ }
+ if (hwreg_present( &mste_rtc.sec_ones)) {
+ ATARIHW_SET(MSTE_CLK);
+ printk( "MSTE_CLK ");
+ mach_gettod = atari_mste_gettod;
+ mach_hwclk = atari_mste_hwclk;
+ mach_set_clock_mmss = atari_mste_set_clock_mmss;
+ }
+ if (!is_medusa &&
+ hwreg_present( &dma_wd.fdc_speed ) &&
+ hwreg_write( &dma_wd.fdc_speed, 0 )) {
+ ATARIHW_SET(FDCSPEED);
+ printk( "FDC_SPEED ");
+ }
+ if (!ATARIHW_PRESENT(ST_SCSI)) {
+ ATARIHW_SET(ACSI);
+ printk( "ACSI " );
+ }
+ printk("\n");
+
+ if (CPU_IS_040_OR_060)
+ /* Now it seems to be safe to turn of the tt0 transparent
+ * translation (the one that must not be turned off in
+ * head.S...)
+ */
+ __asm__ volatile ("moveq #0,%/d0;"
+ ".long 0x4e7b0004;" /* movec d0,itt0 */
+ ".long 0x4e7b0006;" /* movec d0,dtt0 */
+ : /* no outputs */
+ : /* no inputs */
+ : "d0");
+
+ /* allocator for memory that must reside in st-ram */
+ atari_stram_init ();
+
+ /* Set up a mapping for the VMEbus address region:
+ *
+ * VME is either at phys. 0xfexxxxxx (TT) or 0xa00000..0xdfffff
+ * (MegaSTE) In both cases, the whole 16 MB chunk is mapped at
+ * 0xfe000000 virt., because this can be done with a single
+ * transparent translation. On the 68040, lots of often unused
+ * page tables would be needed otherwise. On a MegaSTE or similar,
+ * the highest byte is stripped off by hardware due to the 24 bit
+ * design of the bus.
+ */
+
+ if (CPU_IS_020_OR_030) {
+ unsigned long tt1_val;
+ tt1_val = 0xfe008543; /* Translate 0xfexxxxxx, enable, cache
+ * inhibit, read and write, FDC mask = 3,
+ * FDC val = 4 -> Supervisor only */
+ __asm__ __volatile__ ( "pmove %0@,%/tt1" : : "a" (&tt1_val) );
+ }
+ else {
+ __asm__ __volatile__
+ ( "movel %0,%/d0\n\t"
+ ".long 0x4e7b0005\n\t" /* movec d0,itt1 */
+ ".long 0x4e7b0007" /* movec d0,dtt1 */
+ :
+ : "g" (0xfe00a040) /* Translate 0xfexxxxxx, enable,
+ * supervisor only, non-cacheable/
+ * serialized, writable */
+ : "d0" );
+
+ }
+}
+
+void atari_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))
+{
+ /* set Timer C data Register */
+ mfp.tim_dt_c = INT_TICKS;
+ /* start timer C, div = 1:100 */
+ mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60;
+ /* install interrupt service routine for MFP Timer C */
+ request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
+ "timer", timer_routine);
+}
+
+/* ++andreas: gettimeoffset fixed to check for pending interrupt */
+
+#define TICK_SIZE 10000
+
+/* This is always executed with interrupts disabled. */
+unsigned long atari_gettimeoffset (void)
+{
+ unsigned long ticks, offset = 0;
+
+ /* read MFP timer C current value */
+ ticks = mfp.tim_dt_c;
+ /* The probability of underflow is less than 2% */
+ if (ticks > INT_TICKS - INT_TICKS / 50)
+ /* Check for pending timer interrupt */
+ if (mfp.int_pn_b & (1 << 5))
+ offset = TICK_SIZE;
+
+ ticks = INT_TICKS - ticks;
+ ticks = ticks * 10000L / INT_TICKS;
+
+ return ticks + offset;
+}
+
+
+static void
+mste_read(struct MSTE_RTC *val)
+{
+#define COPY(v) val->v=(mste_rtc.v & 0xf)
+ do {
+ COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
+ COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
+ COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
+ COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
+ COPY(year_tens) ;
+ /* prevent from reading the clock while it changed */
+ } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
+#undef COPY
+}
+
+static void
+mste_write(struct MSTE_RTC *val)
+{
+#define COPY(v) mste_rtc.v=val->v
+ do {
+ COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
+ COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
+ COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
+ COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
+ COPY(year_tens) ;
+ /* prevent from writing the clock while it changed */
+ } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
+#undef COPY
+}
+
+#define RTC_READ(reg) \
+ ({ unsigned char __val; \
+ outb(reg,&tt_rtc.regsel); \
+ __val = tt_rtc.data; \
+ __val; \
+ })
+
+#define RTC_WRITE(reg,val) \
+ do { \
+ outb(reg,&tt_rtc.regsel); \
+ tt_rtc.data = (val); \
+ } while(0)
+
+
+void atari_mste_gettod (int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ int hr24=0, hour;
+ struct MSTE_RTC val;
+
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ hr24=mste_rtc.mon_tens & 1;
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+
+ mste_read(&val);
+ *secp = val.sec_ones + val.sec_tens * 10;
+ *minp = val.min_ones + val.min_tens * 10;
+ hour = val.hr_ones + val.hr_tens * 10;
+ if (!hr24) {
+ if (hour == 12 || hour == 12 + 20)
+ hour -= 12;
+ if (hour >= 20)
+ hour += 12 - 20;
+ }
+ *hourp = hour;
+ *dayp = val.day_ones + val.day_tens * 10;
+ *monp = val.mon_ones + val.mon_tens * 10;
+ *yearp = val.year_ones + val.year_tens * 10 + 80;
+}
+
+
+void atari_gettod (int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ unsigned char ctrl;
+ unsigned short tos_version;
+ int hour, pm;
+
+ while (!(RTC_READ(RTC_FREQ_SELECT) & RTC_UIP)) ;
+ while (RTC_READ(RTC_FREQ_SELECT) & RTC_UIP) ;
+
+ *secp = RTC_READ(RTC_SECONDS);
+ *minp = RTC_READ(RTC_MINUTES);
+ hour = RTC_READ(RTC_HOURS);
+ *dayp = RTC_READ(RTC_DAY_OF_MONTH);
+ *monp = RTC_READ(RTC_MONTH);
+ *yearp = RTC_READ(RTC_YEAR);
+ pm = hour & 0x80;
+ hour &= ~0x80;
+
+ ctrl = RTC_READ(RTC_CONTROL);
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BCD_TO_BIN(*secp);
+ BCD_TO_BIN(*minp);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(*dayp);
+ BCD_TO_BIN(*monp);
+ BCD_TO_BIN(*yearp);
+ }
+ if (!(ctrl & RTC_24H)) {
+ if (!pm && hour == 12)
+ hour = 0;
+ else if (pm && hour != 12)
+ hour += 12;
+ }
+ *hourp = hour;
+
+ /* Adjust values (let the setup valid) */
+
+ /* Fetch tos version at Physical 2 */
+ /* We my not be able to access this address if the kernel is
+ loaded to st ram, since the first page is unmapped. On the
+ Medusa this is always the case and there is nothing we can do
+ about this, so we just assume the smaller offset. For the TT
+ we use the fact that in head.S we have set up a mapping
+ 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible
+ in the last 16MB of the address space. */
+ tos_version = is_medusa ? 0xfff : *(unsigned short *)0xFF000002;
+ *yearp += (tos_version < 0x306) ? 70 : 68;
+}
+
+#define HWCLK_POLL_INTERVAL 5
+
+int atari_mste_hwclk( int op, struct hwclk_time *t )
+{
+ int hour, year;
+ int hr24=0;
+ struct MSTE_RTC val;
+
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ hr24=mste_rtc.mon_tens & 1;
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+
+ if (op) {
+ /* write: prepare values */
+
+ val.sec_ones = t->sec % 10;
+ val.sec_tens = t->sec / 10;
+ val.min_ones = t->min % 10;
+ val.min_tens = t->min / 10;
+ hour = t->hour;
+ if (!hr24) {
+ if (hour > 11)
+ hour += 20 - 12;
+ if (hour == 0 || hour == 20)
+ hour += 12;
+ }
+ val.hr_ones = hour % 10;
+ val.hr_tens = hour / 10;
+ val.day_ones = t->day % 10;
+ val.day_tens = t->day / 10;
+ val.mon_ones = (t->mon+1) % 10;
+ val.mon_tens = (t->mon+1) / 10;
+ year = t->year - 80;
+ val.year_ones = year % 10;
+ val.year_tens = year / 10;
+ val.weekday = t->wday;
+ mste_write(&val);
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ val.year_ones = (year % 4); /* leap year register */
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+ }
+ else {
+ mste_read(&val);
+ t->sec = val.sec_ones + val.sec_tens * 10;
+ t->min = val.min_ones + val.min_tens * 10;
+ hour = val.hr_ones + val.hr_tens * 10;
+ if (!hr24) {
+ if (hour == 12 || hour == 12 + 20)
+ hour -= 12;
+ if (hour >= 20)
+ hour += 12 - 20;
+ }
+ t->hour = hour;
+ t->day = val.day_ones + val.day_tens * 10;
+ t->mon = val.mon_ones + val.mon_tens * 10 - 1;
+ t->year = val.year_ones + val.year_tens * 10 + 80;
+ t->wday = val.weekday;
+ }
+ return 0;
+}
+
+int atari_hwclk( int op, struct hwclk_time *t )
+{
+ int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
+ unsigned long flags;
+ unsigned short tos_version;
+ unsigned char ctrl;
+ int pm = 0;
+
+ /* Tos version at Physical 2. See above for explanation why we
+ cannot use PTOV(2). */
+ tos_version = is_medusa ? 0xfff : *(unsigned short *)0xff000002;
+
+ ctrl = RTC_READ(RTC_CONTROL); /* control registers are
+ * independent from the UIP */
+
+ if (op) {
+ /* write: prepare values */
+
+ sec = t->sec;
+ min = t->min;
+ hour = t->hour;
+ day = t->day;
+ mon = t->mon + 1;
+ year = t->year - ((tos_version < 0x306) ? 70 : 68);
+ wday = t->wday + (t->wday >= 0);
+
+ if (!(ctrl & RTC_24H)) {
+ if (hour > 11) {
+ pm = 0x80;
+ if (hour != 12)
+ hour -= 12;
+ }
+ else if (hour == 0)
+ hour = 12;
+ }
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hour);
+ BIN_TO_BCD(day);
+ BIN_TO_BCD(mon);
+ BIN_TO_BCD(year);
+ if (wday >= 0) BIN_TO_BCD(wday);
+ }
+ }
+
+ /* Reading/writing the clock registers is a bit critical due to
+ * the regular update cycle of the RTC. While an update is in
+ * progress, registers 0..9 shouldn't be touched.
+ * The problem is solved like that: If an update is currently in
+ * progress (the UIP bit is set), the process sleeps for a while
+ * (50ms). This really should be enough, since the update cycle
+ * normally needs 2 ms.
+ * If the UIP bit reads as 0, we have at least 244 usecs until the
+ * update starts. This should be enough... But to be sure,
+ * additionally the RTC_SET bit is set to prevent an update cycle.
+ */
+
+ while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + HWCLK_POLL_INTERVAL;
+ schedule();
+ }
+
+ save_flags(flags);
+ cli();
+ RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
+ if (!op) {
+ sec = RTC_READ( RTC_SECONDS );
+ min = RTC_READ( RTC_MINUTES );
+ hour = RTC_READ( RTC_HOURS );
+ day = RTC_READ( RTC_DAY_OF_MONTH );
+ mon = RTC_READ( RTC_MONTH );
+ year = RTC_READ( RTC_YEAR );
+ wday = RTC_READ( RTC_DAY_OF_WEEK );
+ }
+ else {
+ RTC_WRITE( RTC_SECONDS, sec );
+ RTC_WRITE( RTC_MINUTES, min );
+ RTC_WRITE( RTC_HOURS, hour + pm);
+ RTC_WRITE( RTC_DAY_OF_MONTH, day );
+ RTC_WRITE( RTC_MONTH, mon );
+ RTC_WRITE( RTC_YEAR, year );
+ if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
+ }
+ RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
+ restore_flags(flags);
+
+ if (!op) {
+ /* read: adjust values */
+
+ if (hour & 0x80) {
+ hour &= ~0x80;
+ pm = 1;
+ }
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ BCD_TO_BIN(wday);
+ }
+
+ if (!(ctrl & RTC_24H)) {
+ if (!pm && hour == 12)
+ hour = 0;
+ else if (pm && hour != 12)
+ hour += 12;
+ }
+
+ t->sec = sec;
+ t->min = min;
+ t->hour = hour;
+ t->day = day;
+ t->mon = mon - 1;
+ t->year = year + ((tos_version < 0x306) ? 70 : 68);
+ t->wday = wday - 1;
+ }
+
+ return( 0 );
+}
+
+
+int atari_mste_set_clock_mmss (unsigned long nowtime)
+{
+ short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+ struct MSTE_RTC val;
+ unsigned char rtc_minutes;
+
+ mste_read(&val);
+ rtc_minutes= val.min_ones + val.min_tens * 10;
+ if ((rtc_minutes < real_minutes
+ ? real_minutes - rtc_minutes
+ : rtc_minutes - real_minutes) < 30)
+ {
+ val.sec_ones = real_seconds % 10;
+ val.sec_tens = real_seconds / 10;
+ val.min_ones = real_minutes % 10;
+ val.min_tens = real_minutes / 10;
+ mste_write(&val);
+ }
+ else
+ return -1;
+ return 0;
+}
+
+int atari_set_clock_mmss (unsigned long nowtime)
+{
+ int retval = 0;
+ short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+ unsigned char save_control, save_freq_select, rtc_minutes;
+
+ save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
+ RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
+
+ save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
+ RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
+
+ rtc_minutes = RTC_READ (RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY))
+ BCD_TO_BIN (rtc_minutes);
+
+ /* Since we're only adjusting minutes and seconds, don't interfere
+ with hour overflow. This avoids messing with unknown time zones
+ but requires your RTC not to be off by more than 30 minutes. */
+ if ((rtc_minutes < real_minutes
+ ? real_minutes - rtc_minutes
+ : rtc_minutes - real_minutes) < 30)
+ {
+ if (!(save_control & RTC_DM_BINARY))
+ {
+ BIN_TO_BCD (real_seconds);
+ BIN_TO_BCD (real_minutes);
+ }
+ RTC_WRITE (RTC_SECONDS, real_seconds);
+ RTC_WRITE (RTC_MINUTES, real_minutes);
+ }
+ else
+ retval = -1;
+
+ RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
+ RTC_WRITE (RTC_CONTROL, save_control);
+ return retval;
+}
+
+
+void atari_waitbut (void)
+{
+ /* sorry, no-op */
+}
+
+
+static inline void ata_mfp_out (char c)
+{
+ while (!(mfp.trn_stat & 0x80)) /* wait for tx buf empty */
+ barrier ();
+ mfp.usart_dta = c;
+}
+
+void ata_mfp_print (const char *str)
+{
+ for( ; *str; ++str ) {
+ if (*str == '\n')
+ ata_mfp_out( '\r' );
+ ata_mfp_out( *str );
+ }
+}
+
+static inline void ata_scc_out (char c)
+{
+ do {
+ MFPDELAY();
+ } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */
+ MFPDELAY();
+ scc.cha_b_data = c;
+}
+
+void ata_scc_print (const char *str)
+{
+ for( ; *str; ++str ) {
+ if (*str == '\n')
+ ata_scc_out( '\r' );
+ ata_scc_out( *str );
+ }
+}
+
+static int ata_par_out (char c)
+{
+ extern unsigned long loops_per_sec;
+ unsigned char tmp;
+ /* This a some-seconds timeout in case no printer is connected */
+ unsigned long i = loops_per_sec > 1 ? loops_per_sec : 10000000;
+
+ while( (mfp.par_dt_reg & 1) && --i ) /* wait for BUSY == L */
+ ;
+ if (!i) return( 0 );
+
+ sound_ym.rd_data_reg_sel = 15; /* select port B */
+ sound_ym.wd_data = c; /* put char onto port */
+ sound_ym.rd_data_reg_sel = 14; /* select port A */
+ tmp = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = tmp & ~0x20; /* set strobe L */
+ MFPDELAY(); /* wait a bit */
+ sound_ym.wd_data = tmp | 0x20; /* set strobe H */
+ return( 1 );
+}
+
+void ata_par_print (const char *str)
+{
+ static int printer_present = 1;
+
+ if (!printer_present)
+ return;
+
+ for( ; *str; ++str ) {
+ if (*str == '\n')
+ if (!ata_par_out( '\r' )) {
+ printer_present = 0;
+ return;
+ }
+ if (!ata_par_out( *str )) {
+ printer_present = 0;
+ return;
+ }
+ }
+}
+
+
+void atari_debug_init( void )
+{
+ extern void (*debug_print_proc)(const char *);
+ extern char m68k_debug_device[];
+
+ if (!strcmp( m68k_debug_device, "ser" )) {
+ /* defaults to ser2 for a Falcon and ser1 otherwise */
+ strcpy( m68k_debug_device,
+ ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_FALCON) ?
+ "ser2" : "ser1" );
+
+ }
+
+ if (!strcmp( m68k_debug_device, "ser1" )) {
+ /* ST-MFP Modem1 serial port */
+ mfp.trn_stat &= ~0x01; /* disable TX */
+ mfp.usart_ctr = 0x88; /* clk 1:16, 8N1 */
+ mfp.tim_ct_cd &= 0x70; /* stop timer D */
+ mfp.tim_dt_d = 2; /* 9600 bps */
+ mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */
+ mfp.trn_stat |= 0x01; /* enable TX */
+ debug_print_proc = ata_mfp_print;
+ }
+ else if (!strcmp( m68k_debug_device, "ser2" )) {
+ /* SCC Modem2 serial port */
+ static unsigned char *p, scc_table[] = {
+ 9, 12, /* Reset */
+ 4, 0x44, /* x16, 1 stopbit, no parity */
+ 3, 0xc0, /* receiver: 8 bpc */
+ 5, 0xe2, /* transmitter: 8 bpc, assert dtr/rts */
+ 9, 0, /* no interrupts */
+ 10, 0, /* NRZ */
+ 11, 0x50, /* use baud rate generator */
+ 12, 24, 13, 0, /* 9600 baud */
+ 14, 2, 14, 3, /* use master clock for BRG, enable */
+ 3, 0xc1, /* enable receiver */
+ 5, 0xea, /* enable transmitter */
+ 0
+ };
+
+ (void)scc.cha_b_ctrl; /* reset reg pointer */
+ for( p = scc_table; *p != 0; ) {
+ scc.cha_b_ctrl = *p++;
+ MFPDELAY();
+ scc.cha_b_ctrl = *p++;
+ MFPDELAY();
+ }
+ debug_print_proc = ata_scc_print;
+ }
+ else if (!strcmp( m68k_debug_device, "par" )) {
+ /* parallel printer */
+ atari_turnoff_irq( IRQ_MFP_BUSY ); /* avoid ints */
+ sound_ym.rd_data_reg_sel = 7; /* select mixer control */
+ sound_ym.wd_data = 0xff; /* sound off, ports are output */
+ sound_ym.rd_data_reg_sel = 15; /* select port B */
+ sound_ym.wd_data = 0; /* no char */
+ sound_ym.rd_data_reg_sel = 14; /* select port A */
+ sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x20; /* strobe H */
+ debug_print_proc = ata_par_print;
+ }
+ else
+ debug_print_proc = NULL;
+}
+
+
+void ata_serial_print (const char *str)
+{
+ int c;
+
+ while (c = *str++, c != 0)
+ {
+ if (c == '\n')
+ {
+ while (!(mfp.trn_stat & (1 << 7)))
+ barrier ();
+ mfp.usart_dta = '\r';
+ }
+ while (!(mfp.trn_stat & (1 << 7)))
+ barrier ();
+ mfp.usart_dta = c;
+ }
+}
+
+/* ++roman:
+ *
+ * This function does a reset on machines that lack the ability to
+ * assert the processor's _RESET signal somehow via hardware. It is
+ * based on the fact that you can find the initial SP and PC values
+ * after a reset at physical addresses 0 and 4. This works pretty well
+ * for Atari machines, since the lowest 8 bytes of physical memory are
+ * really ROM (mapped by hardware). For other 680x0 machines: don't
+ * know if it works...
+ *
+ * To get the values at addresses 0 and 4, the MMU better is turned
+ * off first. After that, we have to jump into physical address space
+ * (the PC before the pmove statement points to the virtual address of
+ * the code). Getting that physical address is not hard, but the code
+ * becomes a bit complex since I've tried to ensure that the jump
+ * statement after the pmove is in the cache already (otherwise the
+ * processor can't fetch it!). For that, the code first jumps to the
+ * jump statement with the (virtual) address of the pmove section in
+ * an address register . The jump statement is surely in the cache
+ * now. After that, that physical address of the reset code is loaded
+ * into the same address register, pmove is done and the same jump
+ * statements goes to the reset code. Since there are not many
+ * statements between the two jumps, I hope it stays in the cache.
+ *
+ * The C code makes heavy use of the GCC features that you can get the
+ * address of a C label. No hope to compile this with another compiler
+ * than GCC!
+ */
+
+/* ++andreas: no need for complicated code, just depend on prefetch */
+
+void atari_reset (void)
+{
+ long tc_val = 0;
+ long reset_addr;
+
+ /* On the Medusa, phys. 0x4 may contain garbage because it's no
+ ROM. See above for explanation why we cannot use PTOV(4). */
+ reset_addr = is_medusa ? 0xe00030 : *(unsigned long *) 0xff000004;
+
+ acia.key_ctrl = ACIA_RESET; /* reset ACIA for switch off OverScan, if it's active */
+
+ /* processor independent: turn off interrupts and reset the VBR;
+ * the caches must be left enabled, else prefetching the final jump
+ * instruction doesn't work. */
+ cli();
+ __asm__ __volatile__
+ ("moveq #0,%/d0\n\t"
+ "movec %/d0,%/vbr"
+ : : : "d0" );
+
+ if (CPU_IS_040_OR_060) {
+ unsigned long jmp_addr040 = VTOP(&&jmp_addr_label040);
+ if (CPU_IS_060) {
+ /* 68060: clear PCR to turn off superscalar operation */
+ __asm__ __volatile__
+ ("moveq #0,%/d0\n\t"
+ ".long 0x4e7b0808" /* movec d0,pcr */
+ : : : "d0" );
+ }
+
+ __asm__ __volatile__
+ ("movel %0,%/d0\n\t"
+ "andl #0xff000000,%/d0\n\t"
+ "orw #0xe020,%/d0\n\t" /* map 16 MB, enable, cacheable */
+ ".long 0x4e7b0004\n\t" /* movec d0,itt0 */
+ ".long 0x4e7b0006\n\t" /* movec d0,dtt0 */
+ "jmp %0@\n\t"
+ : /* no outputs */
+ : "a" (jmp_addr040)
+ : "d0" );
+ jmp_addr_label040:
+ __asm__ __volatile__
+ ("moveq #0,%/d0\n\t"
+ "nop\n\t"
+ ".word 0xf4d8\n\t" /* cinva i/d */
+ ".word 0xf518\n\t" /* pflusha */
+ ".long 0x4e7b0003\n\t" /* movec d0,tc */
+ "jmp %0@"
+ : /* no outputs */
+ : "a" (reset_addr)
+ : "d0");
+ }
+ else
+ __asm__ __volatile__
+ ("pmove %0@,%/tc\n\t"
+ "jmp %1@"
+ : /* no outputs */
+ : "a" (&tc_val), "a" (reset_addr));
+}
+
+
+void atari_get_model(char *model)
+{
+ strcpy(model, "Atari ");
+ switch (boot_info.bi_atari.mch_cookie >> 16) {
+ case ATARI_MCH_ST:
+ if (ATARIHW_PRESENT(MSTE_CLK))
+ strcat (model, "Mega ST");
+ else
+ strcat (model, "ST");
+ break;
+ case ATARI_MCH_STE:
+ if ((boot_info.bi_atari.mch_cookie & 0xffff) == 0x10)
+ strcat (model, "Mega STE");
+ else
+ strcat (model, "STE");
+ break;
+ case ATARI_MCH_TT:
+ if (is_medusa)
+ /* Medusa has TT _MCH cookie */
+ strcat (model, "Medusa");
+ else
+ strcat (model, "TT");
+ break;
+ case ATARI_MCH_FALCON:
+ strcat (model, "Falcon");
+ break;
+ default:
+ sprintf (model + strlen (model), "(unknown mach cookie 0x%lx)",
+ boot_info.bi_atari.mch_cookie);
+ break;
+ }
+}
+
+
+int atari_get_hardware_list(char *buffer)
+{
+ int len = 0, i;
+
+ for (i = 0; i < boot_info.num_memory; i++)
+ len += sprintf (buffer+len, "\t%3ld MB at 0x%08lx (%s)\n",
+ boot_info.memory[i].size >> 20,
+ boot_info.memory[i].addr,
+ (boot_info.memory[i].addr & 0xff000000 ?
+ "alternate RAM" : "ST-RAM"));
+
+#define ATARIHW_ANNOUNCE(name,str) \
+ if (ATARIHW_PRESENT(name)) \
+ len += sprintf (buffer + len, "\t%s\n", str)
+
+ len += sprintf (buffer + len, "Detected hardware:\n");
+ ATARIHW_ANNOUNCE(STND_SHIFTER, "ST Shifter");
+ ATARIHW_ANNOUNCE(EXTD_SHIFTER, "STe Shifter");
+ ATARIHW_ANNOUNCE(TT_SHIFTER, "TT Shifter");
+ ATARIHW_ANNOUNCE(VIDEL_SHIFTER, "Falcon Shifter");
+ ATARIHW_ANNOUNCE(YM_2149, "Programmable Sound Generator");
+ ATARIHW_ANNOUNCE(PCM_8BIT, "PCM 8 Bit Sound");
+ ATARIHW_ANNOUNCE(CODEC, "CODEC Sound");
+ ATARIHW_ANNOUNCE(TT_SCSI, "SCSI Controller NCR5380 (TT style)");
+ ATARIHW_ANNOUNCE(ST_SCSI, "SCSI Controller NCR5380 (Falcon style)");
+ ATARIHW_ANNOUNCE(ACSI, "ACSI Interface");
+ ATARIHW_ANNOUNCE(IDE, "IDE Interface");
+ ATARIHW_ANNOUNCE(FDCSPEED, "8/16 Mhz Switch for FDC");
+ ATARIHW_ANNOUNCE(ST_MFP, "Multi Function Peripheral MFP 68901");
+ ATARIHW_ANNOUNCE(TT_MFP, "Second Multi Function Peripheral MFP 68901");
+ ATARIHW_ANNOUNCE(SCC, "Serial Communications Controller SCC 8530");
+ ATARIHW_ANNOUNCE(ST_ESCC, "Extended Serial Communications Controller SCC 85230");
+ ATARIHW_ANNOUNCE(ANALOG_JOY, "Paddle Interface");
+ ATARIHW_ANNOUNCE(MICROWIRE, "MICROWIRE(tm) Interface");
+ ATARIHW_ANNOUNCE(STND_DMA, "DMA Controller (24 bit)");
+ ATARIHW_ANNOUNCE(EXTD_DMA, "DMA Controller (32 bit)");
+ ATARIHW_ANNOUNCE(SCSI_DMA, "DMA Controller for NCR5380");
+ ATARIHW_ANNOUNCE(SCC_DMA, "DMA Controller for SCC");
+ ATARIHW_ANNOUNCE(TT_CLK, "Clock Chip MC146818A");
+ ATARIHW_ANNOUNCE(MSTE_CLK, "Clock Chip RP5C15");
+ ATARIHW_ANNOUNCE(SCU, "System Control Unit");
+ ATARIHW_ANNOUNCE(BLITTER, "Blitter");
+ ATARIHW_ANNOUNCE(VME, "VME Bus");
+
+ return(len);
+}
diff --git a/arch/m68k/atari/joystick.c b/arch/m68k/atari/joystick.c
new file mode 100644
index 000000000..67acec33b
--- /dev/null
+++ b/arch/m68k/atari/joystick.c
@@ -0,0 +1,141 @@
+/*
+ * Atari Joystick Driver for Linux
+ * by Robert de Vries (robert@and.nl) 19Jul93
+ *
+ * 16 Nov 1994 Andreas Schwab
+ * Support for three button mouse (shamelessly stolen from MiNT)
+ * third button wired to one of the joystick directions on joystick 1
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+
+#include <asm/atarikb.h>
+#include <asm/atari_joystick.h>
+#include <asm/atari_mouse.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR JOYSTICK_MAJOR
+
+#define ANALOG_JOY(n) (!(n & 0x80))
+#define DIGITAL_JOY(n) (n & 0x80)
+#define DEVICE_NR(n) (MINOR(n) & 0x7f)
+
+
+static struct joystick_status joystick[2];
+int atari_mouse_buttons; /* for three-button mouse */
+
+void atari_joystick_interrupt(char *buf)
+{
+ int j;
+/* ikbd_joystick_disable(); */
+
+ j = buf[0] & 0x1;
+ joystick[j].dir = buf[1] & 0xF;
+ joystick[j].fire = (buf[1] & 0x80) >> 7;
+ joystick[j].ready = 1;
+ wake_up_interruptible(&joystick[j].wait);
+
+ /* For three-button mouse emulation fake a mouse packet */
+ if (atari_mouse_interrupt_hook &&
+ j == 1 && (buf[1] & 1) != ((atari_mouse_buttons & 2) >> 1))
+ {
+ char faked_packet[3];
+
+ atari_mouse_buttons = (atari_mouse_buttons & 5) | ((buf[1] & 1) << 1);
+ faked_packet[0] = (atari_mouse_buttons & 1) |
+ (atari_mouse_buttons & 4 ? 2 : 0);
+ faked_packet[1] = 0;
+ faked_packet[2] = 0;
+ atari_mouse_interrupt_hook (faked_packet);
+ }
+
+/* ikbd_joystick_event_on(); */
+}
+
+static void release_joystick(struct inode *inode, struct file *file)
+{
+ int minor = DEVICE_NR(inode->i_rdev);
+
+ joystick[minor].active = 0;
+ joystick[minor].ready = 0;
+
+ if ((joystick[0].active == 0) && (joystick[1].active == 0))
+ ikbd_joystick_disable();
+}
+
+static int open_joystick(struct inode *inode, struct file *file)
+{
+ int minor = DEVICE_NR(inode->i_rdev);
+
+ if (!DIGITAL_JOY(inode->i_rdev) || minor > 1)
+ return -ENODEV;
+ if (joystick[minor].active)
+ return -EBUSY;
+ joystick[minor].active = 1;
+ joystick[minor].ready = 0;
+ ikbd_joystick_event_on();
+ return 0;
+}
+
+static long write_joystick(struct inode *inode, struct file *file,
+ const char *buffer, unsigned long count)
+{
+ return -EINVAL;
+}
+
+static long read_joystick(struct inode *inode, struct file *file,
+ char *buffer, unsigned long count)
+{
+ int minor = DEVICE_NR(inode->i_rdev);
+ int i;
+
+ if (count < 2)
+ return -EINVAL;
+ if (!joystick[minor].ready)
+ return -EAGAIN;
+ put_user(joystick[minor].fire, buffer++);
+ put_user(joystick[minor].dir, buffer++);
+ for (i = 0; i < count; i++)
+ put_user(0, buffer++);
+ joystick[minor].ready = 0;
+
+ return i;
+}
+
+static int joystick_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+ int minor = DEVICE_NR(inode->i_rdev);
+
+ if (sel_type != SEL_IN)
+ return 0;
+ if (joystick[minor].ready)
+ return 1;
+ select_wait(&joystick[minor].wait, wait);
+ return 0;
+}
+
+struct file_operations atari_joystick_fops = {
+ NULL, /* joystick_seek */
+ read_joystick,
+ write_joystick,
+ NULL, /* joystick_readdir */
+ joystick_select,
+ NULL, /* joystick_ioctl */
+ NULL, /* joystick_mmap */
+ open_joystick,
+ release_joystick
+};
+
+int atari_joystick_init(void)
+{
+ joystick[0].active = joystick[1].active = 0;
+ joystick[0].ready = joystick[1].ready = 0;
+ joystick[0].wait = joystick[1].wait = NULL;
+
+ if (register_chrdev(MAJOR_NR, "joystick", &atari_joystick_fops))
+ printk("unable to get major %d for joystick devices\n", MAJOR_NR);
+
+ return 0;
+}
diff --git a/arch/m68k/atari/ksyms.c b/arch/m68k/atari/ksyms.c
new file mode 100644
index 000000000..303babbe1
--- /dev/null
+++ b/arch/m68k/atari/ksyms.c
@@ -0,0 +1,40 @@
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atarikb.h>
+#include <asm/atari_joystick.h>
+#include <asm/atari_stdma.h>
+
+extern void atari_microwire_cmd( int cmd );
+
+static struct symbol_table mach_atari_symbol_table = {
+#include <linux/symtab_begin.h>
+
+ X(is_medusa),
+ X(atari_register_vme_int),
+ X(atari_unregister_vme_int),
+ X(stdma_lock),
+ X(stdma_release),
+ X(stdma_others_waiting),
+ X(stdma_islocked),
+
+ X(atari_mouse_buttons),
+ X(atari_mouse_interrupt_hook),
+ X(atari_MIDI_interrupt_hook),
+ X(ikbd_write),
+ X(ikbd_mouse_y0_top),
+ X(ikbd_mouse_thresh),
+ X(ikbd_mouse_rel_pos),
+ X(ikbd_mouse_disable),
+
+ X(atari_microwire_cmd),
+
+#include <linux/symtab_end.h>
+};
+
+void mach_atari_syms_export(void)
+{
+ register_symtab(&mach_atari_symbol_table);
+}
diff --git a/arch/m68k/atari/stdma.c b/arch/m68k/atari/stdma.c
new file mode 100644
index 000000000..af4a6c7b6
--- /dev/null
+++ b/arch/m68k/atari/stdma.c
@@ -0,0 +1,195 @@
+
+/*
+ * linux/atari/stmda.c
+ *
+ * Copyright (C) 1994 Roman Hodek
+ *
+ *
+ * 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.
+ */
+
+
+/* This file contains some function for controlling the access to the */
+/* ST-DMA chip that may be shared between devices. Currently we have: */
+/* TT: Floppy and ACSI bus */
+/* Falcon: Floppy and SCSI */
+/* */
+/* The controlling functions set up a wait queue for access to the */
+/* ST-DMA chip. Callers to stdma_lock() that cannot granted access are */
+/* put onto a queue and waked up later if the owner calls */
+/* stdma_release(). Additionally, the caller gives his interrupt */
+/* service routine to stdma_lock(). */
+/* */
+/* On the Falcon, the IDE bus uses just the ACSI/Floppy interrupt, but */
+/* not the ST-DMA chip itself. So falhd.c needs not to lock the */
+/* chip. The interrupt is routed to falhd.c if IDE is configured, the */
+/* model is a Falcon and the interrupt was caused by the HD controller */
+/* (can be determined by looking at its status register). */
+
+
+#include <linux/types.h>
+#include <linux/genhd.h>
+#include <linux/sched.h>
+#include <asm/setup.h>
+#include <asm/atari_stdma.h>
+#include <asm/atariints.h>
+#include <asm/atarihw.h>
+#include <asm/atarihdreg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+static int stdma_locked = 0; /* the semaphore */
+ /* int func to be called */
+static void (*stdma_isr)(int, void *, struct pt_regs *) = NULL;
+static void *stdma_isr_data = NULL; /* data passed to isr */
+static struct wait_queue *stdma_wait = NULL; /* wait queue for ST-DMA */
+
+
+
+
+/***************************** Prototypes *****************************/
+
+static void stdma_int (int irq, void *dummy, struct pt_regs *fp);
+
+/************************* End of Prototypes **************************/
+
+
+
+/*
+ * Function: void stdma_lock( isrfunc isr, void *data )
+ *
+ * Purpose: Tries to get a lock on the ST-DMA chip that is used by more
+ * then one device driver. Waits on stdma_wait until lock is free.
+ * stdma_lock() may not be called from an interrupt! You have to
+ * get the lock in your main routine and release it when your
+ * request is finished.
+ *
+ * Inputs: A interrupt function that is called until the lock is
+ * released.
+ *
+ * Returns: nothing
+ *
+ */
+
+void stdma_lock(void (*handler)(int, void *, struct pt_regs *), void *data)
+{
+ unsigned long oldflags;
+
+ save_flags(oldflags);
+ cli(); /* protect lock */
+
+ while(stdma_locked)
+ /* Since the DMA is used for file system purposes, we
+ have to sleep uninterruptible (there may be locked
+ buffers) */
+ sleep_on(&stdma_wait);
+
+ stdma_locked = 1;
+ stdma_isr = handler;
+ stdma_isr_data = data;
+ restore_flags(oldflags);
+}
+
+
+/*
+ * Function: void stdma_release( void )
+ *
+ * Purpose: Releases the lock on the ST-DMA chip.
+ *
+ * Inputs: none
+ *
+ * Returns: nothing
+ *
+ */
+
+void stdma_release(void)
+{
+ unsigned long oldflags;
+
+ save_flags(oldflags);
+ cli();
+
+ stdma_locked = 0;
+ stdma_isr = NULL;
+ stdma_isr_data = NULL;
+ wake_up(&stdma_wait);
+
+ restore_flags(oldflags);
+}
+
+
+/*
+ * Function: int stdma_others_waiting( void )
+ *
+ * Purpose: Check if someone waits for the ST-DMA lock.
+ *
+ * Inputs: none
+ *
+ * Returns: 0 if no one is waiting, != 0 otherwise
+ *
+ */
+
+int stdma_others_waiting(void)
+{
+ return waitqueue_active(&stdma_wait);
+}
+
+
+/*
+ * Function: int stdma_islocked( void )
+ *
+ * Purpose: Check if the ST-DMA is currently locked.
+ * Note: Returned status is only valid if ints are disabled while calling and
+ * as long as they remain disabled.
+ * If called with ints enabled, status can change only from locked to
+ * unlocked, because ints may not lock the ST-DMA.
+ *
+ * Inputs: none
+ *
+ * Returns: != 0 if locked, 0 otherwise
+ *
+ */
+
+int stdma_islocked(void)
+{
+ return stdma_locked;
+}
+
+
+/*
+ * Function: void stdma_init( void )
+ *
+ * Purpose: Initialize the ST-DMA chip access controlling.
+ * It sets up the interrupt and its service routine. The int is registered
+ * as slow int, client devices have to live with that (no problem
+ * currently).
+ *
+ * Inputs: none
+ *
+ * Return: nothing
+ *
+ */
+
+void stdma_init(void)
+{
+ stdma_isr = NULL;
+ request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW,
+ "ST-DMA: floppy/ACSI/IDE/Falcon-SCSI", stdma_int);
+}
+
+
+/*
+ * Function: void stdma_int()
+ *
+ * Purpose: The interrupt routine for the ST-DMA. It calls the isr
+ * registered by stdma_lock().
+ *
+ */
+
+static void stdma_int(int irq, void *dummy, struct pt_regs *fp)
+{
+ if (stdma_isr)
+ (*stdma_isr)(irq, stdma_isr_data, fp);
+}
diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c
new file mode 100644
index 000000000..a82988dae
--- /dev/null
+++ b/arch/m68k/atari/stram.c
@@ -0,0 +1,243 @@
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#if 0
+
+struct stram_desc
+ {
+ unsigned first:1;
+ unsigned last:1;
+ unsigned alloced:1;
+ unsigned length:24;
+ };
+
+#define DP(ptr) ((struct stram_desc *) (ptr))
+
+static unsigned long stramsize;
+static unsigned long stramaddr;
+
+void
+atari_stram_init (void)
+{
+ struct stram_desc *dp;
+ stramaddr = boot_info.bi_atari.stram_start;
+ stramsize = boot_info.bi_atari.stram_size;
+
+ /* initialize start boundary */
+ dp = DP (stramaddr);
+ dp->first = 1;
+ dp->alloced = 0;
+ dp->length = stramsize - 2 * sizeof (*dp);
+
+ /* initialize end boundary */
+ dp = DP (stramaddr + stramsize) - 1;
+ dp->last = 1;
+ dp->alloced = 0;
+ dp->length = stramsize - 2 * sizeof (*dp);
+
+#ifdef DEBUG
+ printk ("stram end boundary is %p, length is %d\n", dp,
+ dp->length);
+#endif
+}
+
+void *
+atari_stram_alloc (long size)
+{
+ /* last chunk */
+ struct stram_desc *dp;
+ void *ptr;
+
+ /* round off */
+ size = (size + 3) & ~3;
+
+#ifdef DEBUG
+ printk ("stram_alloc: allocate %ld bytes\n", size);
+#endif
+
+ /*
+ * get pointer to descriptor for last chunk by
+ * going backwards from end chunk
+ */
+ dp = DP (stramaddr + stramsize) - 1;
+ dp = DP ((unsigned long) dp - dp->length) - 1;
+
+ while ((dp->alloced || dp->length < size) && !dp->first)
+ dp = DP ((unsigned long) dp - dp[-1].length) - 2;
+
+ if (dp->alloced || dp->length < size)
+ {
+ printk ("no stram available for %ld allocation\n", size);
+ return NULL;
+ }
+
+ if (dp->length < size + 2 * sizeof (*dp))
+ {
+ /* length too small to split; allocate the whole thing */
+ dp->alloced = 1;
+ ptr = (void *) (dp + 1);
+ dp = DP ((unsigned long) ptr + dp->length);
+ dp->alloced = 1;
+#ifdef DEBUG
+ printk ("stram_alloc: no split\n");
+#endif
+ }
+ else
+ {
+ /* split the extent; use the end part */
+ long newsize = dp->length - (2 * sizeof (*dp) + size);
+
+#ifdef DEBUG
+ printk ("stram_alloc: splitting %d to %ld\n", dp->length,
+ newsize);
+#endif
+ dp->length = newsize;
+ dp = DP ((unsigned long) (dp + 1) + newsize);
+ dp->first = dp->last = 0;
+ dp->alloced = 0;
+ dp->length = newsize;
+ dp++;
+ dp->first = dp->last = 0;
+ dp->alloced = 1;
+ dp->length = size;
+ ptr = (void *) (dp + 1);
+ dp = DP ((unsigned long) ptr + size);
+ dp->alloced = 1;
+ dp->length = size;
+ }
+
+#ifdef DEBUG
+ printk ("stram_alloc: returning %p\n", ptr);
+#endif
+ return ptr;
+}
+
+void
+atari_stram_free (void *ptr)
+{
+ struct stram_desc *sdp = DP (ptr) - 1, *dp2;
+ struct stram_desc *edp = DP ((unsigned long) ptr + sdp->length);
+
+ /* deallocate the chunk */
+ sdp->alloced = edp->alloced = 0;
+
+ /* check if we should merge with the previous chunk */
+ if (!sdp->first && !sdp[-1].alloced)
+ {
+ dp2 = DP ((unsigned long) sdp - sdp[-1].length) - 2;
+ dp2->length += sdp->length + 2 * sizeof (*sdp);
+ edp->length = dp2->length;
+ sdp = dp2;
+ }
+
+ /* check if we should merge with the following chunk */
+ if (!edp->last && !edp[1].alloced)
+ {
+ dp2 = DP ((unsigned long) edp + edp[1].length) + 2;
+ dp2->length += edp->length + 2 * sizeof (*sdp);
+ sdp->length = dp2->length;
+ edp = dp2;
+ }
+}
+
+#else
+
+#include <linux/mm.h>
+
+/* ++roman:
+ *
+ * New version of ST-Ram buffer allocation. Instead of using the
+ * 1 MB - 4 KB that remain when the the ST-Ram chunk starts at $1000
+ * (1 MB granularity!), such buffers are reserved like this:
+ *
+ * - If the kernel resides in ST-Ram anyway, we can take the buffer
+ * from behind the current kernel data space the normal way
+ * (incrementing start_mem).
+ *
+ * - If the kernel is in TT-Ram, stram_init() initializes start and
+ * end of the available region. Buffers are allocated from there
+ * and mem_init() later marks the such used pages as reserved.
+ * Since each TT-Ram chunk is at least 4 MB in size, I hope there
+ * won't be an overrun of the ST-Ram region by normal kernel data
+ * space.
+ *
+ * For that, ST-Ram may only be allocated while kernel initialization
+ * is going on, or exactly: before mem_init() is called. There is also
+ * no provision now for freeing ST-Ram buffers. It seems that isn't
+ * really needed.
+ *
+ * ToDo:
+ * Check the high level scsi code what is done when the
+ * UNCHECKED_ISA_DMA flag is set. It guess, it is just a test for adr
+ * < 16 Mega. There should be call to atari_stram_alloc() instead.
+ *
+ * Also ToDo:
+ * Go through head.S and delete parts no longer needed (transparent
+ * mapping of ST-Ram etc.)
+ *
+ */
+
+
+unsigned long rsvd_stram_beg, rsvd_stram_end;
+ /* Start and end of the reserved ST-Ram region */
+static unsigned long stram_end;
+ /* Overall end of ST-Ram */
+
+
+void atari_stram_init( void )
+
+{ int i;
+
+ for( i = 0; i < boot_info.num_memory; ++i ) {
+ if (boot_info.memory[i].addr == 0) {
+ rsvd_stram_beg = PTOV( 0x800 ); /* skip super-only first 2 KB! */
+ rsvd_stram_end = rsvd_stram_beg;
+ stram_end = rsvd_stram_beg - 0x800 + boot_info.memory[i].size;
+ return;
+ }
+ }
+ /* Should never come here! (There is always ST-Ram!) */
+}
+
+
+void *atari_stram_alloc( long size, unsigned long *start_mem )
+
+{
+ static int kernel_in_stram = -1;
+
+ void *adr = 0;
+
+ if (kernel_in_stram < 0)
+ kernel_in_stram = (PTOV( 0 ) == 0);
+
+ if (kernel_in_stram) {
+ /* Get memory from kernel data space */
+ adr = (void *) *start_mem;
+ *start_mem += size;
+ }
+ else {
+ /* Get memory from rsvd_stram_beg */
+ if (rsvd_stram_end + size < stram_end) {
+ adr = (void *) rsvd_stram_end;
+ rsvd_stram_end += size;
+ }
+ }
+
+ return( adr );
+}
+
+void atari_stram_free( void *ptr )
+
+{
+ /* Sorry, this is a dummy. It isn't needed anyway. */
+}
+
+#endif
+
+