diff options
Diffstat (limited to 'arch/m68k/atari')
-rw-r--r-- | arch/m68k/atari/Makefile | 16 | ||||
-rw-r--r-- | arch/m68k/atari/atafb.c | 3278 | ||||
-rw-r--r-- | arch/m68k/atari/atafb.h | 48 | ||||
-rw-r--r-- | arch/m68k/atari/ataints.c | 639 | ||||
-rw-r--r-- | arch/m68k/atari/atakeyb.c | 847 | ||||
-rw-r--r-- | arch/m68k/atari/atasound.c | 149 | ||||
-rw-r--r-- | arch/m68k/atari/atasound.h | 33 | ||||
-rw-r--r-- | arch/m68k/atari/config.c | 1179 | ||||
-rw-r--r-- | arch/m68k/atari/joystick.c | 141 | ||||
-rw-r--r-- | arch/m68k/atari/ksyms.c | 40 | ||||
-rw-r--r-- | arch/m68k/atari/stdma.c | 195 | ||||
-rw-r--r-- | arch/m68k/atari/stram.c | 243 |
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, ¤t_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 *)¤t_par, + sizeof(struct atari_fb_par))) + return -EFAULT; + return 0; +#endif +#ifdef FBCMD_SET_CURRENTPAR + case FBCMD_SET_CURRENTPAR: + if (copy_from_user((void *)¤t_par, (void *)arg, + sizeof(struct atari_fb_par))) + return -EFAULT; + atari_fb_set_par(¤t_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 + + |