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