18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Manuel Jander. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the work of: 68c2ecf20Sopenharmony_ci * Vojtech Pavlik 78c2ecf20Sopenharmony_ci * Raymond Ingles 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Should you need to contact me, the author, you can do so either by 108c2ecf20Sopenharmony_ci * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: 118c2ecf20Sopenharmony_ci * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Based 90% on Vojtech Pavlik pcigame driver. 148c2ecf20Sopenharmony_ci * Merged and modified by Manuel Jander, for the OpenVortex 158c2ecf20Sopenharmony_ci * driver. (email: mjander@embedded.cl). 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/time.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <sound/core.h> 228c2ecf20Sopenharmony_ci#include "au88x0.h" 238c2ecf20Sopenharmony_ci#include <linux/gameport.h> 248c2ecf20Sopenharmony_ci#include <linux/export.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define VORTEX_GAME_DWAIT 20 /* 20 ms */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic unsigned char vortex_game_read(struct gameport *gameport) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci vortex_t *vortex = gameport_get_port_data(gameport); 338c2ecf20Sopenharmony_ci return hwread(vortex->mmio, VORTEX_GAME_LEGACY); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void vortex_game_trigger(struct gameport *gameport) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci vortex_t *vortex = gameport_get_port_data(gameport); 398c2ecf20Sopenharmony_ci hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int 438c2ecf20Sopenharmony_civortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci vortex_t *vortex = gameport_get_port_data(gameport); 468c2ecf20Sopenharmony_ci int i; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci *buttons = (~hwread(vortex->mmio, VORTEX_GAME_LEGACY) >> 4) & 0xf; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 518c2ecf20Sopenharmony_ci axes[i] = 528c2ecf20Sopenharmony_ci hwread(vortex->mmio, VORTEX_GAME_AXIS + (i * AXIS_SIZE)); 538c2ecf20Sopenharmony_ci if (axes[i] == AXIS_RANGE) 548c2ecf20Sopenharmony_ci axes[i] = -1; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int vortex_game_open(struct gameport *gameport, int mode) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci vortex_t *vortex = gameport_get_port_data(gameport); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci switch (mode) { 648c2ecf20Sopenharmony_ci case GAMEPORT_MODE_COOKED: 658c2ecf20Sopenharmony_ci hwwrite(vortex->mmio, VORTEX_CTRL2, 668c2ecf20Sopenharmony_ci hwread(vortex->mmio, 678c2ecf20Sopenharmony_ci VORTEX_CTRL2) | CTRL2_GAME_ADCMODE); 688c2ecf20Sopenharmony_ci msleep(VORTEX_GAME_DWAIT); 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci case GAMEPORT_MODE_RAW: 718c2ecf20Sopenharmony_ci hwwrite(vortex->mmio, VORTEX_CTRL2, 728c2ecf20Sopenharmony_ci hwread(vortex->mmio, 738c2ecf20Sopenharmony_ci VORTEX_CTRL2) & ~CTRL2_GAME_ADCMODE); 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci return -1; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int vortex_gameport_register(vortex_t *vortex) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct gameport *gp; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci vortex->gameport = gp = gameport_allocate_port(); 878c2ecf20Sopenharmony_ci if (!gp) { 888c2ecf20Sopenharmony_ci dev_err(vortex->card->dev, 898c2ecf20Sopenharmony_ci "cannot allocate memory for gameport\n"); 908c2ecf20Sopenharmony_ci return -ENOMEM; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci gameport_set_name(gp, "AU88x0 Gameport"); 948c2ecf20Sopenharmony_ci gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev)); 958c2ecf20Sopenharmony_ci gameport_set_dev_parent(gp, &vortex->pci_dev->dev); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci gp->read = vortex_game_read; 988c2ecf20Sopenharmony_ci gp->trigger = vortex_game_trigger; 998c2ecf20Sopenharmony_ci gp->cooked_read = vortex_game_cooked_read; 1008c2ecf20Sopenharmony_ci gp->open = vortex_game_open; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci gameport_set_port_data(gp, vortex); 1038c2ecf20Sopenharmony_ci gp->fuzz = 64; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci gameport_register_port(gp); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void vortex_gameport_unregister(vortex_t * vortex) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci if (vortex->gameport) { 1138c2ecf20Sopenharmony_ci gameport_unregister_port(vortex->gameport); 1148c2ecf20Sopenharmony_ci vortex->gameport = NULL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#else 1198c2ecf20Sopenharmony_cistatic inline int vortex_gameport_register(vortex_t * vortex) { return -ENOSYS; } 1208c2ecf20Sopenharmony_cistatic inline void vortex_gameport_unregister(vortex_t * vortex) { } 1218c2ecf20Sopenharmony_ci#endif 122