162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *  Routines for control of MPU-401 in UART mode
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   Modified for the Aureal Vortex based Soundcards
762306a36Sopenharmony_ci *   by Manuel Jander (mjande@embedded.cl).
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/time.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <sound/core.h>
1362306a36Sopenharmony_ci#include <sound/mpu401.h>
1462306a36Sopenharmony_ci#include "au88x0.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Check for mpu401 mmio support. */
1762306a36Sopenharmony_ci/* MPU401 legacy support is only provided as a emergency fallback *
1862306a36Sopenharmony_ci * for older versions of ALSA. Its usage is strongly discouraged. */
1962306a36Sopenharmony_ci#ifndef MPU401_HW_AUREAL
2062306a36Sopenharmony_ci#define VORTEX_MPU401_LEGACY
2162306a36Sopenharmony_ci#endif
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Vortex MPU401 defines. */
2462306a36Sopenharmony_ci#define MIDI_CLOCK_DIV      0x61
2562306a36Sopenharmony_ci/* Standart MPU401 defines. */
2662306a36Sopenharmony_ci#define MPU401_RESET		0xff
2762306a36Sopenharmony_ci#define MPU401_ENTER_UART	0x3f
2862306a36Sopenharmony_ci#define MPU401_ACK		    0xfe
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int snd_vortex_midi(vortex_t *vortex)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
3362306a36Sopenharmony_ci	int temp, mode;
3462306a36Sopenharmony_ci	struct snd_mpu401 *mpu;
3562306a36Sopenharmony_ci	unsigned long port;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifdef VORTEX_MPU401_LEGACY
3862306a36Sopenharmony_ci	/* EnableHardCodedMPU401Port() */
3962306a36Sopenharmony_ci	/* Enable Legacy MIDI Interface port. */
4062306a36Sopenharmony_ci	port = (0x03 << 5);	/* FIXME: static address. 0x330 */
4162306a36Sopenharmony_ci	temp =
4262306a36Sopenharmony_ci	    (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) |
4362306a36Sopenharmony_ci	    CTRL_MIDI_EN | port;
4462306a36Sopenharmony_ci	hwwrite(vortex->mmio, VORTEX_CTRL, temp);
4562306a36Sopenharmony_ci#else
4662306a36Sopenharmony_ci	/* Disable Legacy MIDI Interface port. */
4762306a36Sopenharmony_ci	temp =
4862306a36Sopenharmony_ci	    (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) &
4962306a36Sopenharmony_ci	    ~CTRL_MIDI_EN;
5062306a36Sopenharmony_ci	hwwrite(vortex->mmio, VORTEX_CTRL, temp);
5162306a36Sopenharmony_ci#endif
5262306a36Sopenharmony_ci	/* Mpu401UartInit() */
5362306a36Sopenharmony_ci	mode = 1;
5462306a36Sopenharmony_ci	temp = hwread(vortex->mmio, VORTEX_CTRL2) & 0xffff00cf;
5562306a36Sopenharmony_ci	temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4;
5662306a36Sopenharmony_ci	hwwrite(vortex->mmio, VORTEX_CTRL2, temp);
5762306a36Sopenharmony_ci	hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Check if anything is OK. */
6062306a36Sopenharmony_ci	temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
6162306a36Sopenharmony_ci	if (temp != MPU401_ACK /*0xfe */ ) {
6262306a36Sopenharmony_ci		dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n");
6362306a36Sopenharmony_ci		return -ENODEV;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	/* Enable MPU401 interrupts. */
6662306a36Sopenharmony_ci	hwwrite(vortex->mmio, VORTEX_IRQ_CTRL,
6762306a36Sopenharmony_ci		hwread(vortex->mmio, VORTEX_IRQ_CTRL) | IRQ_MIDI);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* Create MPU401 instance. */
7062306a36Sopenharmony_ci#ifdef VORTEX_MPU401_LEGACY
7162306a36Sopenharmony_ci	temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
7262306a36Sopenharmony_ci				   MPU401_INFO_IRQ_HOOK, -1, &rmidi);
7362306a36Sopenharmony_ci	if (temp) {
7462306a36Sopenharmony_ci		hwwrite(vortex->mmio, VORTEX_CTRL,
7562306a36Sopenharmony_ci			(hwread(vortex->mmio, VORTEX_CTRL) &
7662306a36Sopenharmony_ci			 ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
7762306a36Sopenharmony_ci		return temp;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci#else
8062306a36Sopenharmony_ci	port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA);
8162306a36Sopenharmony_ci	temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
8262306a36Sopenharmony_ci				   MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO |
8362306a36Sopenharmony_ci				   MPU401_INFO_IRQ_HOOK, -1, &rmidi);
8462306a36Sopenharmony_ci	if (temp) {
8562306a36Sopenharmony_ci		hwwrite(vortex->mmio, VORTEX_CTRL,
8662306a36Sopenharmony_ci			(hwread(vortex->mmio, VORTEX_CTRL) &
8762306a36Sopenharmony_ci			 ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
8862306a36Sopenharmony_ci		return temp;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	mpu = rmidi->private_data;
9162306a36Sopenharmony_ci	mpu->cport = (unsigned long)(vortex->mmio + VORTEX_MIDI_CMD);
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci	/* Overwrite MIDI name */
9462306a36Sopenharmony_ci	snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI %d", CARD_NAME_SHORT , vortex->card->number);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	vortex->rmidi = rmidi;
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
99