162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (C) by Paul Barton-Davis 1998-1999
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Some portions of this file are taken from work that is
562306a36Sopenharmony_ci * copyright (C) by Hannu Savolainen 1993-1996
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth
1062306a36Sopenharmony_ci *                                             (Maui, Tropez, Tropez Plus)
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * This driver supports the onboard wavetable synthesizer (an ICS2115),
1362306a36Sopenharmony_ci * including patch, sample and program loading and unloading, conversion
1462306a36Sopenharmony_ci * of GUS patches during loading, and full user-level access to all
1562306a36Sopenharmony_ci * WaveFront commands. It tries to provide semi-intelligent patch and
1662306a36Sopenharmony_ci * sample management as well.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/time.h>
2562306a36Sopenharmony_ci#include <linux/wait.h>
2662306a36Sopenharmony_ci#include <linux/sched/signal.h>
2762306a36Sopenharmony_ci#include <linux/firmware.h>
2862306a36Sopenharmony_ci#include <linux/moduleparam.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <sound/core.h>
3262306a36Sopenharmony_ci#include <sound/snd_wavefront.h>
3362306a36Sopenharmony_ci#include <sound/initval.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int wf_raw = 0; /* we normally check for "raw state" to firmware
3662306a36Sopenharmony_ci			  loading. if non-zero, then during driver loading, the
3762306a36Sopenharmony_ci			  state of the board is ignored, and we reset the
3862306a36Sopenharmony_ci			  board and load the firmware anyway.
3962306a36Sopenharmony_ci		       */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int fx_raw = 1; /* if this is zero, we'll leave the FX processor in
4262306a36Sopenharmony_ci			  whatever state it is when the driver is loaded.
4362306a36Sopenharmony_ci			  The default is to download the microprogram and
4462306a36Sopenharmony_ci			  associated coefficients to set it up for "default"
4562306a36Sopenharmony_ci			  operation, whatever that means.
4662306a36Sopenharmony_ci		       */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int debug_default = 0;  /* you can set this to control debugging
4962306a36Sopenharmony_ci				  during driver loading. it takes any combination
5062306a36Sopenharmony_ci				  of the WF_DEBUG_* flags defined in
5162306a36Sopenharmony_ci				  wavefront.h
5262306a36Sopenharmony_ci			       */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* XXX this needs to be made firmware and hardware version dependent */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define DEFAULT_OSPATH	"wavefront.os"
5762306a36Sopenharmony_cistatic char *ospath = DEFAULT_OSPATH; /* the firmware file name */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int wait_usecs = 150; /* This magic number seems to give pretty optimal
6062306a36Sopenharmony_ci				throughput based on my limited experimentation.
6162306a36Sopenharmony_ci				If you want to play around with it and find a better
6262306a36Sopenharmony_ci				value, be my guest. Remember, the idea is to
6362306a36Sopenharmony_ci				get a number that causes us to just busy wait
6462306a36Sopenharmony_ci				for as many WaveFront commands as possible, without
6562306a36Sopenharmony_ci				coming up with a number so large that we hog the
6662306a36Sopenharmony_ci				whole CPU.
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci				Specifically, with this number, out of about 134,000
6962306a36Sopenharmony_ci				status waits, only about 250 result in a sleep.
7062306a36Sopenharmony_ci			    */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int sleep_interval = 100;   /* HZ/sleep_interval seconds per sleep */
7362306a36Sopenharmony_cistatic int sleep_tries = 50;       /* number of times we'll try to sleep */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int reset_time = 2;        /* hundreths of a second we wait after a HW
7662306a36Sopenharmony_ci				     reset for the expected interrupt.
7762306a36Sopenharmony_ci				  */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int ramcheck_time = 20;    /* time in seconds to wait while ROM code
8062306a36Sopenharmony_ci				     checks on-board RAM.
8162306a36Sopenharmony_ci				  */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int osrun_time = 10;       /* time in seconds we wait for the OS to
8462306a36Sopenharmony_ci				     start running.
8562306a36Sopenharmony_ci				  */
8662306a36Sopenharmony_cimodule_param(wf_raw, int, 0444);
8762306a36Sopenharmony_ciMODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS");
8862306a36Sopenharmony_cimodule_param(fx_raw, int, 0444);
8962306a36Sopenharmony_ciMODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help");
9062306a36Sopenharmony_cimodule_param(debug_default, int, 0444);
9162306a36Sopenharmony_ciMODULE_PARM_DESC(debug_default, "debug parameters for card initialization");
9262306a36Sopenharmony_cimodule_param(wait_usecs, int, 0444);
9362306a36Sopenharmony_ciMODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs");
9462306a36Sopenharmony_cimodule_param(sleep_interval, int, 0444);
9562306a36Sopenharmony_ciMODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply");
9662306a36Sopenharmony_cimodule_param(sleep_tries, int, 0444);
9762306a36Sopenharmony_ciMODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait");
9862306a36Sopenharmony_cimodule_param(ospath, charp, 0444);
9962306a36Sopenharmony_ciMODULE_PARM_DESC(ospath, "pathname to processed ICS2115 OS firmware");
10062306a36Sopenharmony_cimodule_param(reset_time, int, 0444);
10162306a36Sopenharmony_ciMODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect");
10262306a36Sopenharmony_cimodule_param(ramcheck_time, int, 0444);
10362306a36Sopenharmony_ciMODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test");
10462306a36Sopenharmony_cimodule_param(osrun_time, int, 0444);
10562306a36Sopenharmony_ciMODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS");
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* if WF_DEBUG not defined, no run-time debugging messages will
10862306a36Sopenharmony_ci   be available via the debug flag setting. Given the current
10962306a36Sopenharmony_ci   beta state of the driver, this will remain set until a future
11062306a36Sopenharmony_ci   version.
11162306a36Sopenharmony_ci*/
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define WF_DEBUG 1
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#ifdef WF_DEBUG
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define DPRINT(cond, ...) \
11862306a36Sopenharmony_ci       if ((dev->debug & (cond)) == (cond)) { \
11962306a36Sopenharmony_ci	     snd_printk (__VA_ARGS__); \
12062306a36Sopenharmony_ci       }
12162306a36Sopenharmony_ci#else
12262306a36Sopenharmony_ci#define DPRINT(cond, args...)
12362306a36Sopenharmony_ci#endif /* WF_DEBUG */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define LOGNAME "WaveFront: "
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* bitmasks for WaveFront status port value */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define STAT_RINTR_ENABLED	0x01
13062306a36Sopenharmony_ci#define STAT_CAN_READ		0x02
13162306a36Sopenharmony_ci#define STAT_INTR_READ		0x04
13262306a36Sopenharmony_ci#define STAT_WINTR_ENABLED	0x10
13362306a36Sopenharmony_ci#define STAT_CAN_WRITE		0x20
13462306a36Sopenharmony_ci#define STAT_INTR_WRITE		0x40
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int wavefront_delete_sample (snd_wavefront_t *, int sampnum);
13762306a36Sopenharmony_cistatic int wavefront_find_free_sample (snd_wavefront_t *);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct wavefront_command {
14062306a36Sopenharmony_ci	int cmd;
14162306a36Sopenharmony_ci	char *action;
14262306a36Sopenharmony_ci	unsigned int read_cnt;
14362306a36Sopenharmony_ci	unsigned int write_cnt;
14462306a36Sopenharmony_ci	int need_ack;
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic struct {
14862306a36Sopenharmony_ci	int errno;
14962306a36Sopenharmony_ci	const char *errstr;
15062306a36Sopenharmony_ci} wavefront_errors[] = {
15162306a36Sopenharmony_ci	{ 0x01, "Bad sample number" },
15262306a36Sopenharmony_ci	{ 0x02, "Out of sample memory" },
15362306a36Sopenharmony_ci	{ 0x03, "Bad patch number" },
15462306a36Sopenharmony_ci	{ 0x04, "Error in number of voices" },
15562306a36Sopenharmony_ci	{ 0x06, "Sample load already in progress" },
15662306a36Sopenharmony_ci	{ 0x0B, "No sample load request pending" },
15762306a36Sopenharmony_ci	{ 0x0E, "Bad MIDI channel number" },
15862306a36Sopenharmony_ci	{ 0x10, "Download Record Error" },
15962306a36Sopenharmony_ci	{ 0x80, "Success" },
16062306a36Sopenharmony_ci	{ 0x0 }
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci#define NEEDS_ACK 1
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic struct wavefront_command wavefront_commands[] = {
16662306a36Sopenharmony_ci	{ WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK },
16762306a36Sopenharmony_ci	{ WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0},
16862306a36Sopenharmony_ci	{ WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK },
16962306a36Sopenharmony_ci	{ WFC_GET_NVOICES, "get number of voices", 1, 0, 0 },
17062306a36Sopenharmony_ci	{ WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK },
17162306a36Sopenharmony_ci	{ WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 },
17262306a36Sopenharmony_ci	{ WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK },
17362306a36Sopenharmony_ci	{ WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK },
17462306a36Sopenharmony_ci	{ WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 },
17562306a36Sopenharmony_ci	{ WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK },
17662306a36Sopenharmony_ci	{ WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK },
17762306a36Sopenharmony_ci	{ WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK },
17862306a36Sopenharmony_ci	{ WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK },
17962306a36Sopenharmony_ci	{ WFC_MIDI_STATUS, "report midi status", 1, 0, 0 },
18062306a36Sopenharmony_ci	{ WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 },
18162306a36Sopenharmony_ci	{ WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 },
18262306a36Sopenharmony_ci	{ WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 },
18362306a36Sopenharmony_ci	{ WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 },
18462306a36Sopenharmony_ci	{ WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 },
18562306a36Sopenharmony_ci	{ WFC_DOWNLOAD_SAMPLE, "download sample",
18662306a36Sopenharmony_ci	  0, WF_SAMPLE_BYTES, NEEDS_ACK },
18762306a36Sopenharmony_ci	{ WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK},
18862306a36Sopenharmony_ci	{ WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header",
18962306a36Sopenharmony_ci	  0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK },
19062306a36Sopenharmony_ci	{ WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 },
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* This command requires a variable number of bytes to be written.
19362306a36Sopenharmony_ci	   There is a hack in snd_wavefront_cmd() to support this. The actual
19462306a36Sopenharmony_ci	   count is passed in as the read buffer ptr, cast appropriately.
19562306a36Sopenharmony_ci	   Ugh.
19662306a36Sopenharmony_ci	*/
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	{ WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK },
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* This one is a hack as well. We just read the first byte of the
20162306a36Sopenharmony_ci	   response, don't fetch an ACK, and leave the rest to the
20262306a36Sopenharmony_ci	   calling function. Ugly, ugly, ugly.
20362306a36Sopenharmony_ci	*/
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	{ WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 },
20662306a36Sopenharmony_ci	{ WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias",
20762306a36Sopenharmony_ci	  0, WF_ALIAS_BYTES, NEEDS_ACK },
20862306a36Sopenharmony_ci	{ WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0},
20962306a36Sopenharmony_ci	{ WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK },
21062306a36Sopenharmony_ci	{ WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 },
21162306a36Sopenharmony_ci	{ WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" },
21262306a36Sopenharmony_ci	{ WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 },
21362306a36Sopenharmony_ci	{ WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK },
21462306a36Sopenharmony_ci	{ WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 },
21562306a36Sopenharmony_ci	{ WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK },
21662306a36Sopenharmony_ci	{ WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 },
21762306a36Sopenharmony_ci	{ WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9,
21862306a36Sopenharmony_ci	  NEEDS_ACK},
21962306a36Sopenharmony_ci	{ WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0},
22062306a36Sopenharmony_ci	{ WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel",
22162306a36Sopenharmony_ci	  0, 1, NEEDS_ACK },
22262306a36Sopenharmony_ci	{ WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK },
22362306a36Sopenharmony_ci	{ WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers",
22462306a36Sopenharmony_ci	  32, 0, 0 },
22562306a36Sopenharmony_ci	{ WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK },
22662306a36Sopenharmony_ci	{ 0x00 }
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const char *
23062306a36Sopenharmony_ciwavefront_errorstr (int errnum)
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int i;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; wavefront_errors[i].errstr; i++) {
23662306a36Sopenharmony_ci		if (wavefront_errors[i].errno == errnum) {
23762306a36Sopenharmony_ci			return wavefront_errors[i].errstr;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return "Unknown WaveFront error";
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic struct wavefront_command *
24562306a36Sopenharmony_ciwavefront_get_command (int cmd)
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	int i;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	for (i = 0; wavefront_commands[i].cmd != 0; i++) {
25162306a36Sopenharmony_ci		if (cmd == wavefront_commands[i].cmd) {
25262306a36Sopenharmony_ci			return &wavefront_commands[i];
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return NULL;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic inline int
26062306a36Sopenharmony_ciwavefront_status (snd_wavefront_t *dev)
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	return inb (dev->status_port);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int
26762306a36Sopenharmony_ciwavefront_sleep (int limit)
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	schedule_timeout_interruptible(limit);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return signal_pending(current);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int
27662306a36Sopenharmony_ciwavefront_wait (snd_wavefront_t *dev, int mask)
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int             i;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Spin for a short period of time, because >99% of all
28262306a36Sopenharmony_ci	   requests to the WaveFront can be serviced inline like this.
28362306a36Sopenharmony_ci	*/
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	for (i = 0; i < wait_usecs; i += 5) {
28662306a36Sopenharmony_ci		if (wavefront_status (dev) & mask) {
28762306a36Sopenharmony_ci			return 1;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		udelay(5);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	for (i = 0; i < sleep_tries; i++) {
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (wavefront_status (dev) & mask) {
29562306a36Sopenharmony_ci			return 1;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		if (wavefront_sleep (HZ/sleep_interval)) {
29962306a36Sopenharmony_ci			return (0);
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return (0);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int
30762306a36Sopenharmony_ciwavefront_read (snd_wavefront_t *dev)
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (wavefront_wait (dev, STAT_CAN_READ))
31162306a36Sopenharmony_ci		return inb (dev->data_port);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	DPRINT (WF_DEBUG_DATA, "read timeout.\n");
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return -1;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int
31962306a36Sopenharmony_ciwavefront_write (snd_wavefront_t *dev, unsigned char data)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	if (wavefront_wait (dev, STAT_CAN_WRITE)) {
32362306a36Sopenharmony_ci		outb (data, dev->data_port);
32462306a36Sopenharmony_ci		return 0;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	DPRINT (WF_DEBUG_DATA, "write timeout.\n");
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return -1;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciint
33362306a36Sopenharmony_cisnd_wavefront_cmd (snd_wavefront_t *dev,
33462306a36Sopenharmony_ci		   int cmd, unsigned char *rbuf, unsigned char *wbuf)
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int ack;
33862306a36Sopenharmony_ci	unsigned int i;
33962306a36Sopenharmony_ci	int c;
34062306a36Sopenharmony_ci	struct wavefront_command *wfcmd;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	wfcmd = wavefront_get_command(cmd);
34362306a36Sopenharmony_ci	if (!wfcmd) {
34462306a36Sopenharmony_ci		snd_printk ("command 0x%x not supported.\n",
34562306a36Sopenharmony_ci			cmd);
34662306a36Sopenharmony_ci		return 1;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Hack to handle the one variable-size write command. See
35062306a36Sopenharmony_ci	   wavefront_send_multisample() for the other half of this
35162306a36Sopenharmony_ci	   gross and ugly strategy.
35262306a36Sopenharmony_ci	*/
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (cmd == WFC_DOWNLOAD_MULTISAMPLE) {
35562306a36Sopenharmony_ci		wfcmd->write_cnt = (unsigned long) rbuf;
35662306a36Sopenharmony_ci		rbuf = NULL;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n",
36062306a36Sopenharmony_ci			       cmd, wfcmd->action, wfcmd->read_cnt,
36162306a36Sopenharmony_ci			       wfcmd->write_cnt, wfcmd->need_ack);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (wavefront_write (dev, cmd)) {
36462306a36Sopenharmony_ci		DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request "
36562306a36Sopenharmony_ci						     "0x%x [%s].\n",
36662306a36Sopenharmony_ci						     cmd, wfcmd->action);
36762306a36Sopenharmony_ci		return 1;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (wfcmd->write_cnt > 0) {
37162306a36Sopenharmony_ci		DPRINT (WF_DEBUG_DATA, "writing %d bytes "
37262306a36Sopenharmony_ci					"for 0x%x\n",
37362306a36Sopenharmony_ci					wfcmd->write_cnt, cmd);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		for (i = 0; i < wfcmd->write_cnt; i++) {
37662306a36Sopenharmony_ci			if (wavefront_write (dev, wbuf[i])) {
37762306a36Sopenharmony_ci				DPRINT (WF_DEBUG_IO, "bad write for byte "
37862306a36Sopenharmony_ci						      "%d of 0x%x [%s].\n",
37962306a36Sopenharmony_ci						      i, cmd, wfcmd->action);
38062306a36Sopenharmony_ci				return 1;
38162306a36Sopenharmony_ci			}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci			DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n",
38462306a36Sopenharmony_ci						i, wbuf[i]);
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (wfcmd->read_cnt > 0) {
38962306a36Sopenharmony_ci		DPRINT (WF_DEBUG_DATA, "reading %d ints "
39062306a36Sopenharmony_ci					"for 0x%x\n",
39162306a36Sopenharmony_ci					wfcmd->read_cnt, cmd);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		for (i = 0; i < wfcmd->read_cnt; i++) {
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci			c = wavefront_read(dev);
39662306a36Sopenharmony_ci			if (c == -1) {
39762306a36Sopenharmony_ci				DPRINT (WF_DEBUG_IO, "bad read for byte "
39862306a36Sopenharmony_ci						      "%d of 0x%x [%s].\n",
39962306a36Sopenharmony_ci						      i, cmd, wfcmd->action);
40062306a36Sopenharmony_ci				return 1;
40162306a36Sopenharmony_ci			}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci			/* Now handle errors. Lots of special cases here */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci			if (c == 0xff) {
40662306a36Sopenharmony_ci				c = wavefront_read(dev);
40762306a36Sopenharmony_ci				if (c == -1) {
40862306a36Sopenharmony_ci					DPRINT (WF_DEBUG_IO, "bad read for "
40962306a36Sopenharmony_ci							      "error byte at "
41062306a36Sopenharmony_ci							      "read byte %d "
41162306a36Sopenharmony_ci							      "of 0x%x [%s].\n",
41262306a36Sopenharmony_ci							      i, cmd,
41362306a36Sopenharmony_ci							      wfcmd->action);
41462306a36Sopenharmony_ci					return 1;
41562306a36Sopenharmony_ci				}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci				/* Can you believe this madness ? */
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci				if (c == 1 &&
42062306a36Sopenharmony_ci				    wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) {
42162306a36Sopenharmony_ci					rbuf[0] = WF_ST_EMPTY;
42262306a36Sopenharmony_ci					return (0);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci				} else if (c == 3 &&
42562306a36Sopenharmony_ci					   wfcmd->cmd == WFC_UPLOAD_PATCH) {
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci					return 3;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci				} else if (c == 1 &&
43062306a36Sopenharmony_ci					   wfcmd->cmd == WFC_UPLOAD_PROGRAM) {
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci					return 1;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci				} else {
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci					DPRINT (WF_DEBUG_IO, "error %d (%s) "
43762306a36Sopenharmony_ci							      "during "
43862306a36Sopenharmony_ci							      "read for byte "
43962306a36Sopenharmony_ci							      "%d of 0x%x "
44062306a36Sopenharmony_ci							      "[%s].\n",
44162306a36Sopenharmony_ci							      c,
44262306a36Sopenharmony_ci							      wavefront_errorstr (c),
44362306a36Sopenharmony_ci							      i, cmd,
44462306a36Sopenharmony_ci							      wfcmd->action);
44562306a36Sopenharmony_ci					return 1;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci				}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		} else {
45062306a36Sopenharmony_ci				rbuf[i] = c;
45162306a36Sopenharmony_ci			}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci			DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]);
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* Some commands need an ACK, but return zero instead
46262306a36Sopenharmony_ci		   of the standard value.
46362306a36Sopenharmony_ci		*/
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		ack = wavefront_read(dev);
46662306a36Sopenharmony_ci		if (ack == 0)
46762306a36Sopenharmony_ci			ack = WF_ACK;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (ack != WF_ACK) {
47062306a36Sopenharmony_ci			if (ack == -1) {
47162306a36Sopenharmony_ci				DPRINT (WF_DEBUG_IO, "cannot read ack for "
47262306a36Sopenharmony_ci						      "0x%x [%s].\n",
47362306a36Sopenharmony_ci						      cmd, wfcmd->action);
47462306a36Sopenharmony_ci				return 1;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci			} else {
47762306a36Sopenharmony_ci				int err = -1; /* something unknown */
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci				if (ack == 0xff) { /* explicit error */
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci					err = wavefront_read(dev);
48262306a36Sopenharmony_ci					if (err == -1) {
48362306a36Sopenharmony_ci						DPRINT (WF_DEBUG_DATA,
48462306a36Sopenharmony_ci							"cannot read err "
48562306a36Sopenharmony_ci							"for 0x%x [%s].\n",
48662306a36Sopenharmony_ci							cmd, wfcmd->action);
48762306a36Sopenharmony_ci					}
48862306a36Sopenharmony_ci				}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci				DPRINT (WF_DEBUG_IO, "0x%x [%s] "
49162306a36Sopenharmony_ci					"failed (0x%x, 0x%x, %s)\n",
49262306a36Sopenharmony_ci					cmd, wfcmd->action, ack, err,
49362306a36Sopenharmony_ci					wavefront_errorstr (err));
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci				return -err;
49662306a36Sopenharmony_ci			}
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		DPRINT (WF_DEBUG_DATA, "ack received "
50062306a36Sopenharmony_ci					"for 0x%x [%s]\n",
50162306a36Sopenharmony_ci					cmd, wfcmd->action);
50262306a36Sopenharmony_ci	} else {
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need "
50562306a36Sopenharmony_ci				       "ACK (%d,%d,%d)\n",
50662306a36Sopenharmony_ci				       cmd, wfcmd->action, wfcmd->read_cnt,
50762306a36Sopenharmony_ci				       wfcmd->write_cnt, wfcmd->need_ack);
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci/***********************************************************************
51562306a36Sopenharmony_ciWaveFront data munging
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ciThings here are weird. All data written to the board cannot
51862306a36Sopenharmony_cihave its most significant bit set. Any data item with values
51962306a36Sopenharmony_cipotentially > 0x7F (127) must be split across multiple bytes.
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ciSometimes, we need to munge numeric values that are represented on
52262306a36Sopenharmony_cithe x86 side as 8-32 bit values. Sometimes, we need to munge data
52362306a36Sopenharmony_cithat is represented on the x86 side as an array of bytes. The most
52462306a36Sopenharmony_ciefficient approach to handling both cases seems to be to use 2
52562306a36Sopenharmony_cidifferent functions for munging and 2 for de-munging. This avoids
52662306a36Sopenharmony_ciweird casting and worrying about bit-level offsets.
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci**********************************************************************/
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic unsigned char *
53162306a36Sopenharmony_cimunge_int32 (unsigned int src,
53262306a36Sopenharmony_ci	     unsigned char *dst,
53362306a36Sopenharmony_ci	     unsigned int dst_size)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	unsigned int i;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	for (i = 0; i < dst_size; i++) {
53862306a36Sopenharmony_ci		*dst = src & 0x7F;  /* Mask high bit of LSB */
53962306a36Sopenharmony_ci		src = src >> 7;     /* Rotate Right 7 bits  */
54062306a36Sopenharmony_ci	                            /* Note: we leave the upper bits in place */
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		dst++;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	return dst;
54562306a36Sopenharmony_ci};
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int
54862306a36Sopenharmony_cidemunge_int32 (unsigned char* src, int src_size)
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	int i;
55262306a36Sopenharmony_ci 	int outval = 0;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci 	for (i = src_size - 1; i >= 0; i--) {
55562306a36Sopenharmony_ci		outval=(outval<<7)+src[i];
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return outval;
55962306a36Sopenharmony_ci};
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic
56262306a36Sopenharmony_ciunsigned char *
56362306a36Sopenharmony_cimunge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size)
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	unsigned int i;
56762306a36Sopenharmony_ci	unsigned int last = dst_size / 2;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	for (i = 0; i < last; i++) {
57062306a36Sopenharmony_ci		*dst++ = src[i] & 0x7f;
57162306a36Sopenharmony_ci		*dst++ = src[i] >> 7;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	return dst;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic
57762306a36Sopenharmony_ciunsigned char *
57862306a36Sopenharmony_cidemunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	int i;
58262306a36Sopenharmony_ci	unsigned char *end = src + src_bytes;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* NOTE: src and dst *CAN* point to the same address */
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	for (i = 0; src != end; i++) {
58762306a36Sopenharmony_ci		dst[i] = *src++;
58862306a36Sopenharmony_ci		dst[i] |= (*src++)<<7;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return dst;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/***********************************************************************
59562306a36Sopenharmony_ciWaveFront: sample, patch and program management.
59662306a36Sopenharmony_ci***********************************************************************/
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int
59962306a36Sopenharmony_ciwavefront_delete_sample (snd_wavefront_t *dev, int sample_num)
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	unsigned char wbuf[2];
60362306a36Sopenharmony_ci	int x;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	wbuf[0] = sample_num & 0x7f;
60662306a36Sopenharmony_ci	wbuf[1] = sample_num >> 7;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	x = snd_wavefront_cmd(dev, WFC_DELETE_SAMPLE, NULL, wbuf);
60962306a36Sopenharmony_ci	if (!x)
61062306a36Sopenharmony_ci		dev->sample_status[sample_num] = WF_ST_EMPTY;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	return x;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int
61662306a36Sopenharmony_ciwavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	int i;
62062306a36Sopenharmony_ci	unsigned char rbuf[32], wbuf[32];
62162306a36Sopenharmony_ci	unsigned int    sc_real, sc_alias, sc_multi;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* check sample status */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) {
62662306a36Sopenharmony_ci		snd_printk ("cannot request sample count.\n");
62762306a36Sopenharmony_ci		return -1;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	sc_real = sc_alias = sc_multi = dev->samples_used = 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	for (i = 0; i < WF_MAX_SAMPLE; i++) {
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		wbuf[0] = i & 0x7f;
63562306a36Sopenharmony_ci		wbuf[1] = i >> 7;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
63862306a36Sopenharmony_ci			snd_printk(KERN_WARNING "cannot identify sample "
63962306a36Sopenharmony_ci				   "type of slot %d\n", i);
64062306a36Sopenharmony_ci			dev->sample_status[i] = WF_ST_EMPTY;
64162306a36Sopenharmony_ci			continue;
64262306a36Sopenharmony_ci		}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		if (assume_rom) {
64762306a36Sopenharmony_ci			dev->sample_status[i] |= WF_SLOT_ROM;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		switch (rbuf[0] & WF_ST_MASK) {
65162306a36Sopenharmony_ci		case WF_ST_SAMPLE:
65262306a36Sopenharmony_ci			sc_real++;
65362306a36Sopenharmony_ci			break;
65462306a36Sopenharmony_ci		case WF_ST_MULTISAMPLE:
65562306a36Sopenharmony_ci			sc_multi++;
65662306a36Sopenharmony_ci			break;
65762306a36Sopenharmony_ci		case WF_ST_ALIAS:
65862306a36Sopenharmony_ci			sc_alias++;
65962306a36Sopenharmony_ci			break;
66062306a36Sopenharmony_ci		case WF_ST_EMPTY:
66162306a36Sopenharmony_ci			break;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		default:
66462306a36Sopenharmony_ci			snd_printk ("unknown sample type for "
66562306a36Sopenharmony_ci				    "slot %d (0x%x)\n",
66662306a36Sopenharmony_ci				    i, rbuf[0]);
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		if (rbuf[0] != WF_ST_EMPTY) {
67062306a36Sopenharmony_ci			dev->samples_used++;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	snd_printk ("%d samples used (%d real, %d aliases, %d multi), "
67562306a36Sopenharmony_ci		    "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi,
67662306a36Sopenharmony_ci		    WF_MAX_SAMPLE - dev->samples_used);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return (0);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int
68462306a36Sopenharmony_ciwavefront_get_patch_status (snd_wavefront_t *dev)
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	unsigned char patchbuf[WF_PATCH_BYTES];
68862306a36Sopenharmony_ci	unsigned char patchnum[2];
68962306a36Sopenharmony_ci	wavefront_patch *p;
69062306a36Sopenharmony_ci	int i, x, cnt, cnt2;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	for (i = 0; i < WF_MAX_PATCH; i++) {
69362306a36Sopenharmony_ci		patchnum[0] = i & 0x7f;
69462306a36Sopenharmony_ci		patchnum[1] = i >> 7;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		x = snd_wavefront_cmd(dev, WFC_UPLOAD_PATCH, patchbuf,
69762306a36Sopenharmony_ci				      patchnum);
69862306a36Sopenharmony_ci		if (x == 0) {
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci			dev->patch_status[i] |= WF_SLOT_FILLED;
70162306a36Sopenharmony_ci			p = (wavefront_patch *) patchbuf;
70262306a36Sopenharmony_ci			dev->sample_status
70362306a36Sopenharmony_ci				[p->sample_number|(p->sample_msb<<7)] |=
70462306a36Sopenharmony_ci				WF_SLOT_USED;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		} else if (x == 3) { /* Bad patch number */
70762306a36Sopenharmony_ci			dev->patch_status[i] = 0;
70862306a36Sopenharmony_ci		} else {
70962306a36Sopenharmony_ci			snd_printk ("upload patch "
71062306a36Sopenharmony_ci				    "error 0x%x\n", x);
71162306a36Sopenharmony_ci			dev->patch_status[i] = 0;
71262306a36Sopenharmony_ci			return 1;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* program status has already filled in slot_used bits */
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) {
71962306a36Sopenharmony_ci		if (dev->patch_status[i] & WF_SLOT_FILLED) {
72062306a36Sopenharmony_ci			cnt++;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci		if (dev->patch_status[i] & WF_SLOT_USED) {
72362306a36Sopenharmony_ci			cnt2++;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci	snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return (0);
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int
73362306a36Sopenharmony_ciwavefront_get_program_status (snd_wavefront_t *dev)
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	unsigned char progbuf[WF_PROGRAM_BYTES];
73762306a36Sopenharmony_ci	wavefront_program prog;
73862306a36Sopenharmony_ci	unsigned char prognum;
73962306a36Sopenharmony_ci	int i, x, l, cnt;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	for (i = 0; i < WF_MAX_PROGRAM; i++) {
74262306a36Sopenharmony_ci		prognum = i;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		x = snd_wavefront_cmd(dev, WFC_UPLOAD_PROGRAM, progbuf,
74562306a36Sopenharmony_ci				      &prognum);
74662306a36Sopenharmony_ci		if (x == 0) {
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci			dev->prog_status[i] |= WF_SLOT_USED;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci			demunge_buf (progbuf, (unsigned char *) &prog,
75162306a36Sopenharmony_ci				     WF_PROGRAM_BYTES);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci			for (l = 0; l < WF_NUM_LAYERS; l++) {
75462306a36Sopenharmony_ci				if (prog.layer[l].mute) {
75562306a36Sopenharmony_ci					dev->patch_status
75662306a36Sopenharmony_ci						[prog.layer[l].patch_number] |=
75762306a36Sopenharmony_ci						WF_SLOT_USED;
75862306a36Sopenharmony_ci				}
75962306a36Sopenharmony_ci			}
76062306a36Sopenharmony_ci		} else if (x == 1) { /* Bad program number */
76162306a36Sopenharmony_ci			dev->prog_status[i] = 0;
76262306a36Sopenharmony_ci		} else {
76362306a36Sopenharmony_ci			snd_printk ("upload program "
76462306a36Sopenharmony_ci				    "error 0x%x\n", x);
76562306a36Sopenharmony_ci			dev->prog_status[i] = 0;
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) {
77062306a36Sopenharmony_ci		if (dev->prog_status[i]) {
77162306a36Sopenharmony_ci			cnt++;
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	snd_printk ("%d programs slots in use\n", cnt);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	return (0);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int
78162306a36Sopenharmony_ciwavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	unsigned char buf[WF_PATCH_BYTES+2];
78562306a36Sopenharmony_ci	unsigned char *bptr;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
78862306a36Sopenharmony_ci				      header->number);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (header->number >= ARRAY_SIZE(dev->patch_status))
79162306a36Sopenharmony_ci		return -EINVAL;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	dev->patch_status[header->number] |= WF_SLOT_FILLED;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	bptr = munge_int32 (header->number, buf, 2);
79662306a36Sopenharmony_ci	munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) {
79962306a36Sopenharmony_ci		snd_printk ("download patch failed\n");
80062306a36Sopenharmony_ci		return -EIO;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	return (0);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int
80762306a36Sopenharmony_ciwavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	unsigned char buf[WF_PROGRAM_BYTES+1];
81162306a36Sopenharmony_ci	int i;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
81462306a36Sopenharmony_ci		header->number);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (header->number >= ARRAY_SIZE(dev->prog_status))
81762306a36Sopenharmony_ci		return -EINVAL;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	dev->prog_status[header->number] = WF_SLOT_USED;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* XXX need to zero existing SLOT_USED bit for program_status[i]
82262306a36Sopenharmony_ci	   where `i' is the program that's being (potentially) overwritten.
82362306a36Sopenharmony_ci	*/
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	for (i = 0; i < WF_NUM_LAYERS; i++) {
82662306a36Sopenharmony_ci		if (header->hdr.pr.layer[i].mute) {
82762306a36Sopenharmony_ci			dev->patch_status[header->hdr.pr.layer[i].patch_number] |=
82862306a36Sopenharmony_ci				WF_SLOT_USED;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci			/* XXX need to mark SLOT_USED for sample used by
83162306a36Sopenharmony_ci			   patch_number, but this means we have to load it. Ick.
83262306a36Sopenharmony_ci			*/
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	buf[0] = header->number;
83762306a36Sopenharmony_ci	munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) {
84062306a36Sopenharmony_ci		snd_printk ("download patch failed\n");
84162306a36Sopenharmony_ci		return -EIO;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return (0);
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int
84862306a36Sopenharmony_ciwavefront_freemem (snd_wavefront_t *dev)
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	char rbuf[8];
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, NULL)) {
85462306a36Sopenharmony_ci		snd_printk ("can't get memory stats.\n");
85562306a36Sopenharmony_ci		return -1;
85662306a36Sopenharmony_ci	} else {
85762306a36Sopenharmony_ci		return demunge_int32 (rbuf, 4);
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic int
86262306a36Sopenharmony_ciwavefront_send_sample (snd_wavefront_t *dev,
86362306a36Sopenharmony_ci		       wavefront_patch_info *header,
86462306a36Sopenharmony_ci		       u16 __user *dataptr,
86562306a36Sopenharmony_ci		       int data_is_unsigned)
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	/* samples are downloaded via a 16-bit wide i/o port
86962306a36Sopenharmony_ci	   (you could think of it as 2 adjacent 8-bit wide ports
87062306a36Sopenharmony_ci	   but its less efficient that way). therefore, all
87162306a36Sopenharmony_ci	   the blocksizes and so forth listed in the documentation,
87262306a36Sopenharmony_ci	   and used conventionally to refer to sample sizes,
87362306a36Sopenharmony_ci	   which are given in 8-bit units (bytes), need to be
87462306a36Sopenharmony_ci	   divided by 2.
87562306a36Sopenharmony_ci        */
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	u16 sample_short = 0;
87862306a36Sopenharmony_ci	u32 length;
87962306a36Sopenharmony_ci	u16 __user *data_end = NULL;
88062306a36Sopenharmony_ci	unsigned int i;
88162306a36Sopenharmony_ci	const unsigned int max_blksize = 4096/2;
88262306a36Sopenharmony_ci	unsigned int written;
88362306a36Sopenharmony_ci	unsigned int blocksize;
88462306a36Sopenharmony_ci	int dma_ack;
88562306a36Sopenharmony_ci	int blocknum;
88662306a36Sopenharmony_ci	unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES];
88762306a36Sopenharmony_ci	unsigned char *shptr;
88862306a36Sopenharmony_ci	int skip = 0;
88962306a36Sopenharmony_ci	int initial_skip = 0;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, "
89262306a36Sopenharmony_ci				      "type %d, %d bytes from 0x%lx\n",
89362306a36Sopenharmony_ci				      header->size ? "" : "header ",
89462306a36Sopenharmony_ci				      header->number, header->subkey,
89562306a36Sopenharmony_ci				      header->size,
89662306a36Sopenharmony_ci				      (unsigned long) header->dataptr);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
89962306a36Sopenharmony_ci		int x;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		x = wavefront_find_free_sample(dev);
90262306a36Sopenharmony_ci		if (x < 0)
90362306a36Sopenharmony_ci			return -ENOMEM;
90462306a36Sopenharmony_ci		snd_printk ("unspecified sample => %d\n", x);
90562306a36Sopenharmony_ci		header->number = x;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (header->number >= WF_MAX_SAMPLE)
90962306a36Sopenharmony_ci		return -EINVAL;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (header->size) {
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		/* XXX it's a debatable point whether or not RDONLY semantics
91462306a36Sopenharmony_ci		   on the ROM samples should cover just the sample data or
91562306a36Sopenharmony_ci		   the sample header. For now, it only covers the sample data,
91662306a36Sopenharmony_ci		   so anyone is free at all times to rewrite sample headers.
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		   My reason for this is that we have the sample headers
91962306a36Sopenharmony_ci		   available in the WFB file for General MIDI, and so these
92062306a36Sopenharmony_ci		   can always be reset if needed. The sample data, however,
92162306a36Sopenharmony_ci		   cannot be recovered without a complete reset and firmware
92262306a36Sopenharmony_ci		   reload of the ICS2115, which is a very expensive operation.
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		   So, doing things this way allows us to honor the notion of
92562306a36Sopenharmony_ci		   "RESETSAMPLES" reasonably cheaply. Note however, that this
92662306a36Sopenharmony_ci		   is done purely at user level: there is no WFB parser in
92762306a36Sopenharmony_ci		   this driver, and so a complete reset (back to General MIDI,
92862306a36Sopenharmony_ci		   or theoretically some other configuration) is the
92962306a36Sopenharmony_ci		   responsibility of the user level library.
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		   To try to do this in the kernel would be a little
93262306a36Sopenharmony_ci		   crazy: we'd need 158K of kernel space just to hold
93362306a36Sopenharmony_ci		   a copy of the patch/program/sample header data.
93462306a36Sopenharmony_ci		*/
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci		if (dev->rom_samples_rdonly) {
93762306a36Sopenharmony_ci			if (dev->sample_status[header->number] & WF_SLOT_ROM) {
93862306a36Sopenharmony_ci				snd_printk ("sample slot %d "
93962306a36Sopenharmony_ci					    "write protected\n",
94062306a36Sopenharmony_ci					    header->number);
94162306a36Sopenharmony_ci				return -EACCES;
94262306a36Sopenharmony_ci			}
94362306a36Sopenharmony_ci		}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		wavefront_delete_sample (dev, header->number);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (header->size) {
94962306a36Sopenharmony_ci		dev->freemem = wavefront_freemem (dev);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		if (dev->freemem < (int)header->size) {
95262306a36Sopenharmony_ci			snd_printk ("insufficient memory to "
95362306a36Sopenharmony_ci				    "load %d byte sample.\n",
95462306a36Sopenharmony_ci				    header->size);
95562306a36Sopenharmony_ci			return -ENOMEM;
95662306a36Sopenharmony_ci		}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	skip = WF_GET_CHANNEL(&header->hdr.s);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
96362306a36Sopenharmony_ci		snd_printk ("channel selection only "
96462306a36Sopenharmony_ci			    "possible on 16-bit samples");
96562306a36Sopenharmony_ci		return -EINVAL;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	switch (skip) {
96962306a36Sopenharmony_ci	case 0:
97062306a36Sopenharmony_ci		initial_skip = 0;
97162306a36Sopenharmony_ci		skip = 1;
97262306a36Sopenharmony_ci		break;
97362306a36Sopenharmony_ci	case 1:
97462306a36Sopenharmony_ci		initial_skip = 0;
97562306a36Sopenharmony_ci		skip = 2;
97662306a36Sopenharmony_ci		break;
97762306a36Sopenharmony_ci	case 2:
97862306a36Sopenharmony_ci		initial_skip = 1;
97962306a36Sopenharmony_ci		skip = 2;
98062306a36Sopenharmony_ci		break;
98162306a36Sopenharmony_ci	case 3:
98262306a36Sopenharmony_ci		initial_skip = 2;
98362306a36Sopenharmony_ci		skip = 3;
98462306a36Sopenharmony_ci		break;
98562306a36Sopenharmony_ci	case 4:
98662306a36Sopenharmony_ci		initial_skip = 3;
98762306a36Sopenharmony_ci		skip = 4;
98862306a36Sopenharmony_ci		break;
98962306a36Sopenharmony_ci	case 5:
99062306a36Sopenharmony_ci		initial_skip = 4;
99162306a36Sopenharmony_ci		skip = 5;
99262306a36Sopenharmony_ci		break;
99362306a36Sopenharmony_ci	case 6:
99462306a36Sopenharmony_ci		initial_skip = 5;
99562306a36Sopenharmony_ci		skip = 6;
99662306a36Sopenharmony_ci		break;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => "
100062306a36Sopenharmony_ci				      "initial skip = %d, skip = %d\n",
100162306a36Sopenharmony_ci				      WF_GET_CHANNEL (&header->hdr.s),
100262306a36Sopenharmony_ci				      initial_skip, skip);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Be safe, and zero the "Unused" bits ... */
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	WF_SET_CHANNEL(&header->hdr.s, 0);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* adjust size for 16 bit samples by dividing by two.  We always
100962306a36Sopenharmony_ci	   send 16 bits per write, even for 8 bit samples, so the length
101062306a36Sopenharmony_ci	   is always half the size of the sample data in bytes.
101162306a36Sopenharmony_ci	*/
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	length = header->size / 2;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* the data we're sent has not been munged, and in fact, the
101662306a36Sopenharmony_ci	   header we have to send isn't just a munged copy either.
101762306a36Sopenharmony_ci	   so, build the sample header right here.
101862306a36Sopenharmony_ci	*/
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	shptr = &sample_hdr[0];
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	shptr = munge_int32 (header->number, shptr, 2);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (header->size) {
102562306a36Sopenharmony_ci		shptr = munge_int32 (length, shptr, 4);
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* Yes, a 4 byte result doesn't contain all of the offset bits,
102962306a36Sopenharmony_ci	   but the offset only uses 24 bits.
103062306a36Sopenharmony_ci	*/
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleStartOffset),
103362306a36Sopenharmony_ci			     shptr, 4);
103462306a36Sopenharmony_ci	shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset),
103562306a36Sopenharmony_ci			     shptr, 4);
103662306a36Sopenharmony_ci	shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset),
103762306a36Sopenharmony_ci			     shptr, 4);
103862306a36Sopenharmony_ci	shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset),
103962306a36Sopenharmony_ci			     shptr, 4);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* This one is truly weird. What kind of weirdo decided that in
104262306a36Sopenharmony_ci	   a system dominated by 16 and 32 bit integers, they would use
104362306a36Sopenharmony_ci	   a just 12 bits ?
104462306a36Sopenharmony_ci	*/
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	/* Why is this nybblified, when the MSB is *always* zero ?
104962306a36Sopenharmony_ci	   Anyway, we can't take address of bitfield, so make a
105062306a36Sopenharmony_ci	   good-faith guess at where it starts.
105162306a36Sopenharmony_ci	*/
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1),
105462306a36Sopenharmony_ci			     shptr, 2);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev,
105762306a36Sopenharmony_ci			   header->size ?
105862306a36Sopenharmony_ci			   WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
105962306a36Sopenharmony_ci			   NULL, sample_hdr)) {
106062306a36Sopenharmony_ci		snd_printk ("sample %sdownload refused.\n",
106162306a36Sopenharmony_ci			    header->size ? "" : "header ");
106262306a36Sopenharmony_ci		return -EIO;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (header->size == 0) {
106662306a36Sopenharmony_ci		goto sent; /* Sorry. Just had to have one somewhere */
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	data_end = dataptr + length;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	/* Do any initial skip over an unused channel's data */
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	dataptr += initial_skip;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	for (written = 0, blocknum = 0;
107662306a36Sopenharmony_ci	     written < length; written += max_blksize, blocknum++) {
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		if ((length - written) > max_blksize) {
107962306a36Sopenharmony_ci			blocksize = max_blksize;
108062306a36Sopenharmony_ci		} else {
108162306a36Sopenharmony_ci			/* round to nearest 16-byte value */
108262306a36Sopenharmony_ci			blocksize = ALIGN(length - written, 8);
108362306a36Sopenharmony_ci		}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) {
108662306a36Sopenharmony_ci			snd_printk ("download block "
108762306a36Sopenharmony_ci				    "request refused.\n");
108862306a36Sopenharmony_ci			return -EIO;
108962306a36Sopenharmony_ci		}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		for (i = 0; i < blocksize; i++) {
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci			if (dataptr < data_end) {
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci				if (get_user(sample_short, dataptr))
109662306a36Sopenharmony_ci					return -EFAULT;
109762306a36Sopenharmony_ci				dataptr += skip;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci				if (data_is_unsigned) { /* GUS ? */
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci					if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) {
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci						/* 8 bit sample
110462306a36Sopenharmony_ci						 resolution, sign
110562306a36Sopenharmony_ci						 extend both bytes.
110662306a36Sopenharmony_ci						*/
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci						((unsigned char*)
110962306a36Sopenharmony_ci						 &sample_short)[0] += 0x7f;
111062306a36Sopenharmony_ci						((unsigned char*)
111162306a36Sopenharmony_ci						 &sample_short)[1] += 0x7f;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci					} else {
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci						/* 16 bit sample
111662306a36Sopenharmony_ci						 resolution, sign
111762306a36Sopenharmony_ci						 extend the MSB.
111862306a36Sopenharmony_ci						*/
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci						sample_short += 0x7fff;
112162306a36Sopenharmony_ci					}
112262306a36Sopenharmony_ci				}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci			} else {
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci				/* In padding section of final block:
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci				   Don't fetch unsupplied data from
112962306a36Sopenharmony_ci				   user space, just continue with
113062306a36Sopenharmony_ci				   whatever the final value was.
113162306a36Sopenharmony_ci				*/
113262306a36Sopenharmony_ci			}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci			if (i < blocksize - 1) {
113562306a36Sopenharmony_ci				outw (sample_short, dev->block_port);
113662306a36Sopenharmony_ci			} else {
113762306a36Sopenharmony_ci				outw (sample_short, dev->last_block_port);
113862306a36Sopenharmony_ci			}
113962306a36Sopenharmony_ci		}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		/* Get "DMA page acknowledge", even though its really
114262306a36Sopenharmony_ci		   nothing to do with DMA at all.
114362306a36Sopenharmony_ci		*/
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci		dma_ack = wavefront_read(dev);
114662306a36Sopenharmony_ci		if (dma_ack != WF_DMA_ACK) {
114762306a36Sopenharmony_ci			if (dma_ack == -1) {
114862306a36Sopenharmony_ci				snd_printk ("upload sample "
114962306a36Sopenharmony_ci					    "DMA ack timeout\n");
115062306a36Sopenharmony_ci				return -EIO;
115162306a36Sopenharmony_ci			} else {
115262306a36Sopenharmony_ci				snd_printk ("upload sample "
115362306a36Sopenharmony_ci					    "DMA ack error 0x%x\n",
115462306a36Sopenharmony_ci					    dma_ack);
115562306a36Sopenharmony_ci				return -EIO;
115662306a36Sopenharmony_ci			}
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	/* Note, label is here because sending the sample header shouldn't
116362306a36Sopenharmony_ci	   alter the sample_status info at all.
116462306a36Sopenharmony_ci	*/
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci sent:
116762306a36Sopenharmony_ci	return (0);
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int
117162306a36Sopenharmony_ciwavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	unsigned char alias_hdr[WF_ALIAS_BYTES];
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is "
117762306a36Sopenharmony_ci				      "alias for %d\n",
117862306a36Sopenharmony_ci				      header->number,
117962306a36Sopenharmony_ci				      header->hdr.a.OriginalSample);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (header->number >= WF_MAX_SAMPLE)
118262306a36Sopenharmony_ci		return -EINVAL;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	munge_int32 (header->number, &alias_hdr[0], 2);
118562306a36Sopenharmony_ci	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
118662306a36Sopenharmony_ci	munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
118762306a36Sopenharmony_ci		     &alias_hdr[4], 4);
118862306a36Sopenharmony_ci	munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset),
118962306a36Sopenharmony_ci		     &alias_hdr[8], 4);
119062306a36Sopenharmony_ci	munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset),
119162306a36Sopenharmony_ci		     &alias_hdr[12], 4);
119262306a36Sopenharmony_ci	munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset),
119362306a36Sopenharmony_ci		     &alias_hdr[16], 4);
119462306a36Sopenharmony_ci	munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3);
119562306a36Sopenharmony_ci	munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) {
119862306a36Sopenharmony_ci		snd_printk ("download alias failed.\n");
119962306a36Sopenharmony_ci		return -EIO;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return (0);
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int
120862306a36Sopenharmony_ciwavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	int i;
121162306a36Sopenharmony_ci	int num_samples;
121262306a36Sopenharmony_ci	unsigned char *msample_hdr;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (header->number >= WF_MAX_SAMPLE)
121562306a36Sopenharmony_ci		return -EINVAL;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
121862306a36Sopenharmony_ci	if (! msample_hdr)
121962306a36Sopenharmony_ci		return -ENOMEM;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	munge_int32 (header->number, &msample_hdr[0], 2);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	/* You'll recall at this point that the "number of samples" value
122462306a36Sopenharmony_ci	   in a wavefront_multisample struct is actually the log2 of the
122562306a36Sopenharmony_ci	   real number of samples.
122662306a36Sopenharmony_ci	*/
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
122962306a36Sopenharmony_ci	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n",
123262306a36Sopenharmony_ci				      header->number,
123362306a36Sopenharmony_ci				      header->hdr.ms.NumberOfSamples,
123462306a36Sopenharmony_ci				      num_samples);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	for (i = 0; i < num_samples; i++) {
123762306a36Sopenharmony_ci		DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n",
123862306a36Sopenharmony_ci		       i, header->hdr.ms.SampleNumber[i]);
123962306a36Sopenharmony_ci		munge_int32 (header->hdr.ms.SampleNumber[i],
124062306a36Sopenharmony_ci		     &msample_hdr[3+(i*2)], 2);
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* Need a hack here to pass in the number of bytes
124462306a36Sopenharmony_ci	   to be written to the synth. This is ugly, and perhaps
124562306a36Sopenharmony_ci	   one day, I'll fix it.
124662306a36Sopenharmony_ci	*/
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE,
124962306a36Sopenharmony_ci			   (unsigned char *) (long) ((num_samples*2)+3),
125062306a36Sopenharmony_ci			   msample_hdr)) {
125162306a36Sopenharmony_ci		snd_printk ("download of multisample failed.\n");
125262306a36Sopenharmony_ci		kfree(msample_hdr);
125362306a36Sopenharmony_ci		return -EIO;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	kfree(msample_hdr);
125962306a36Sopenharmony_ci	return (0);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic int
126362306a36Sopenharmony_ciwavefront_fetch_multisample (snd_wavefront_t *dev,
126462306a36Sopenharmony_ci			     wavefront_patch_info *header)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	int i;
126762306a36Sopenharmony_ci	unsigned char log_ns[1];
126862306a36Sopenharmony_ci	unsigned char number[2];
126962306a36Sopenharmony_ci	int num_samples;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	munge_int32 (header->number, number, 2);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
127462306a36Sopenharmony_ci		snd_printk ("upload multisample failed.\n");
127562306a36Sopenharmony_ci		return -EIO;
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
127962306a36Sopenharmony_ci				header->number, log_ns[0]);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	header->hdr.ms.NumberOfSamples = log_ns[0];
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* get the number of samples ... */
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	num_samples = (1 << log_ns[0]);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	for (i = 0; i < num_samples; i++) {
128862306a36Sopenharmony_ci		char d[2];
128962306a36Sopenharmony_ci		int val;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci		val = wavefront_read(dev);
129262306a36Sopenharmony_ci		if (val == -1) {
129362306a36Sopenharmony_ci			snd_printk ("upload multisample failed "
129462306a36Sopenharmony_ci				    "during sample loop.\n");
129562306a36Sopenharmony_ci			return -EIO;
129662306a36Sopenharmony_ci		}
129762306a36Sopenharmony_ci		d[0] = val;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci		val = wavefront_read(dev);
130062306a36Sopenharmony_ci		if (val == -1) {
130162306a36Sopenharmony_ci			snd_printk ("upload multisample failed "
130262306a36Sopenharmony_ci				    "during sample loop.\n");
130362306a36Sopenharmony_ci			return -EIO;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci		d[1] = val;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		header->hdr.ms.SampleNumber[i] =
130862306a36Sopenharmony_ci			demunge_int32 ((unsigned char *) d, 2);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n",
131162306a36Sopenharmony_ci					i, header->hdr.ms.SampleNumber[i]);
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	return (0);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic int
131962306a36Sopenharmony_ciwavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	unsigned char drumbuf[WF_DRUM_BYTES];
132362306a36Sopenharmony_ci	wavefront_drum *drum = &header->hdr.d;
132462306a36Sopenharmony_ci	int i;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI "
132762306a36Sopenharmony_ci		"note %d, patch = %d\n",
132862306a36Sopenharmony_ci		header->number, drum->PatchNumber);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	drumbuf[0] = header->number & 0x7f;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
133362306a36Sopenharmony_ci		munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2);
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) {
133762306a36Sopenharmony_ci		snd_printk ("download drum failed.\n");
133862306a36Sopenharmony_ci		return -EIO;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	return (0);
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic int
134562306a36Sopenharmony_ciwavefront_find_free_sample (snd_wavefront_t *dev)
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	int i;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	for (i = 0; i < WF_MAX_SAMPLE; i++) {
135162306a36Sopenharmony_ci		if (!(dev->sample_status[i] & WF_SLOT_FILLED)) {
135262306a36Sopenharmony_ci			return i;
135362306a36Sopenharmony_ci		}
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci	snd_printk ("no free sample slots!\n");
135662306a36Sopenharmony_ci	return -1;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci#if 0
136062306a36Sopenharmony_cistatic int
136162306a36Sopenharmony_ciwavefront_find_free_patch (snd_wavefront_t *dev)
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci{
136462306a36Sopenharmony_ci	int i;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	for (i = 0; i < WF_MAX_PATCH; i++) {
136762306a36Sopenharmony_ci		if (!(dev->patch_status[i] & WF_SLOT_FILLED)) {
136862306a36Sopenharmony_ci			return i;
136962306a36Sopenharmony_ci		}
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci	snd_printk ("no free patch slots!\n");
137262306a36Sopenharmony_ci	return -1;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci#endif
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic int
137762306a36Sopenharmony_ciwavefront_load_patch (snd_wavefront_t *dev, const char __user *addr)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	wavefront_patch_info *header;
138062306a36Sopenharmony_ci	int err;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	header = kmalloc(sizeof(*header), GFP_KERNEL);
138362306a36Sopenharmony_ci	if (! header)
138462306a36Sopenharmony_ci		return -ENOMEM;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (copy_from_user (header, addr, sizeof(wavefront_patch_info) -
138762306a36Sopenharmony_ci			    sizeof(wavefront_any))) {
138862306a36Sopenharmony_ci		snd_printk ("bad address for load patch.\n");
138962306a36Sopenharmony_ci		err = -EFAULT;
139062306a36Sopenharmony_ci		goto __error;
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	DPRINT (WF_DEBUG_LOAD_PATCH, "download "
139462306a36Sopenharmony_ci				      "Sample type: %d "
139562306a36Sopenharmony_ci				      "Sample number: %d "
139662306a36Sopenharmony_ci				      "Sample size: %d\n",
139762306a36Sopenharmony_ci				      header->subkey,
139862306a36Sopenharmony_ci				      header->number,
139962306a36Sopenharmony_ci				      header->size);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	switch (header->subkey) {
140262306a36Sopenharmony_ci	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.s, header->hdrptr,
140562306a36Sopenharmony_ci				    sizeof (wavefront_sample))) {
140662306a36Sopenharmony_ci			err = -EFAULT;
140762306a36Sopenharmony_ci			break;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		err = wavefront_send_sample (dev, header, header->dataptr, 0);
141162306a36Sopenharmony_ci		break;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	case WF_ST_MULTISAMPLE:
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.s, header->hdrptr,
141662306a36Sopenharmony_ci				    sizeof (wavefront_multisample))) {
141762306a36Sopenharmony_ci			err = -EFAULT;
141862306a36Sopenharmony_ci			break;
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		err = wavefront_send_multisample (dev, header);
142262306a36Sopenharmony_ci		break;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	case WF_ST_ALIAS:
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.a, header->hdrptr,
142762306a36Sopenharmony_ci				    sizeof (wavefront_alias))) {
142862306a36Sopenharmony_ci			err = -EFAULT;
142962306a36Sopenharmony_ci			break;
143062306a36Sopenharmony_ci		}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		err = wavefront_send_alias (dev, header);
143362306a36Sopenharmony_ci		break;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	case WF_ST_DRUM:
143662306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.d, header->hdrptr,
143762306a36Sopenharmony_ci				    sizeof (wavefront_drum))) {
143862306a36Sopenharmony_ci			err = -EFAULT;
143962306a36Sopenharmony_ci			break;
144062306a36Sopenharmony_ci		}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		err = wavefront_send_drum (dev, header);
144362306a36Sopenharmony_ci		break;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	case WF_ST_PATCH:
144662306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.p, header->hdrptr,
144762306a36Sopenharmony_ci				    sizeof (wavefront_patch))) {
144862306a36Sopenharmony_ci			err = -EFAULT;
144962306a36Sopenharmony_ci			break;
145062306a36Sopenharmony_ci		}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		err = wavefront_send_patch (dev, header);
145362306a36Sopenharmony_ci		break;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	case WF_ST_PROGRAM:
145662306a36Sopenharmony_ci		if (copy_from_user (&header->hdr.pr, header->hdrptr,
145762306a36Sopenharmony_ci				    sizeof (wavefront_program))) {
145862306a36Sopenharmony_ci			err = -EFAULT;
145962306a36Sopenharmony_ci			break;
146062306a36Sopenharmony_ci		}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		err = wavefront_send_program (dev, header);
146362306a36Sopenharmony_ci		break;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	default:
146662306a36Sopenharmony_ci		snd_printk ("unknown patch type %d.\n",
146762306a36Sopenharmony_ci			    header->subkey);
146862306a36Sopenharmony_ci		err = -EINVAL;
146962306a36Sopenharmony_ci		break;
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci __error:
147362306a36Sopenharmony_ci	kfree(header);
147462306a36Sopenharmony_ci	return err;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci/***********************************************************************
147862306a36Sopenharmony_ciWaveFront: hardware-dependent interface
147962306a36Sopenharmony_ci***********************************************************************/
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic void
148262306a36Sopenharmony_ciprocess_sample_hdr (u8 *buf)
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	wavefront_sample s;
148662306a36Sopenharmony_ci	u8 *ptr;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	ptr = buf;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	/* The board doesn't send us an exact copy of a "wavefront_sample"
149162306a36Sopenharmony_ci	   in response to an Upload Sample Header command. Instead, we
149262306a36Sopenharmony_ci	   have to convert the data format back into our data structure,
149362306a36Sopenharmony_ci	   just as in the Download Sample command, where we have to do
149462306a36Sopenharmony_ci	   something very similar in the reverse direction.
149562306a36Sopenharmony_ci	*/
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	*((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
149862306a36Sopenharmony_ci	*((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
149962306a36Sopenharmony_ci	*((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
150062306a36Sopenharmony_ci	*((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
150162306a36Sopenharmony_ci	*((u32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	s.SampleResolution = *ptr & 0x3;
150462306a36Sopenharmony_ci	s.Loop = *ptr & 0x8;
150562306a36Sopenharmony_ci	s.Bidirectional = *ptr & 0x10;
150662306a36Sopenharmony_ci	s.Reverse = *ptr & 0x40;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* Now copy it back to where it came from */
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample));
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int
151462306a36Sopenharmony_ciwavefront_synth_control (snd_wavefront_card_t *acard,
151562306a36Sopenharmony_ci			 wavefront_control *wc)
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	snd_wavefront_t *dev = &acard->wavefront;
151962306a36Sopenharmony_ci	unsigned char patchnumbuf[2];
152062306a36Sopenharmony_ci	int i;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	DPRINT (WF_DEBUG_CMD, "synth control with "
152362306a36Sopenharmony_ci		"cmd 0x%x\n", wc->cmd);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/* Pre-handling of or for various commands */
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	switch (wc->cmd) {
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	case WFC_DISABLE_INTERRUPTS:
153062306a36Sopenharmony_ci		snd_printk ("interrupts disabled.\n");
153162306a36Sopenharmony_ci		outb (0x80|0x20, dev->control_port);
153262306a36Sopenharmony_ci		dev->interrupts_are_midi = 1;
153362306a36Sopenharmony_ci		return 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	case WFC_ENABLE_INTERRUPTS:
153662306a36Sopenharmony_ci		snd_printk ("interrupts enabled.\n");
153762306a36Sopenharmony_ci		outb (0x80|0x40|0x20, dev->control_port);
153862306a36Sopenharmony_ci		dev->interrupts_are_midi = 1;
153962306a36Sopenharmony_ci		return 0;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	case WFC_INTERRUPT_STATUS:
154262306a36Sopenharmony_ci		wc->rbuf[0] = dev->interrupts_are_midi;
154362306a36Sopenharmony_ci		return 0;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	case WFC_ROMSAMPLES_RDONLY:
154662306a36Sopenharmony_ci		dev->rom_samples_rdonly = wc->wbuf[0];
154762306a36Sopenharmony_ci		wc->status = 0;
154862306a36Sopenharmony_ci		return 0;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	case WFC_IDENTIFY_SLOT_TYPE:
155162306a36Sopenharmony_ci		i = wc->wbuf[0] | (wc->wbuf[1] << 7);
155262306a36Sopenharmony_ci		if (i <0 || i >= WF_MAX_SAMPLE) {
155362306a36Sopenharmony_ci			snd_printk ("invalid slot ID %d\n",
155462306a36Sopenharmony_ci				i);
155562306a36Sopenharmony_ci			wc->status = EINVAL;
155662306a36Sopenharmony_ci			return -EINVAL;
155762306a36Sopenharmony_ci		}
155862306a36Sopenharmony_ci		wc->rbuf[0] = dev->sample_status[i];
155962306a36Sopenharmony_ci		wc->status = 0;
156062306a36Sopenharmony_ci		return 0;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	case WFC_DEBUG_DRIVER:
156362306a36Sopenharmony_ci		dev->debug = wc->wbuf[0];
156462306a36Sopenharmony_ci		snd_printk ("debug = 0x%x\n", dev->debug);
156562306a36Sopenharmony_ci		return 0;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	case WFC_UPLOAD_PATCH:
156862306a36Sopenharmony_ci		munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2);
156962306a36Sopenharmony_ci		memcpy (wc->wbuf, patchnumbuf, 2);
157062306a36Sopenharmony_ci		break;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	case WFC_UPLOAD_MULTISAMPLE:
157362306a36Sopenharmony_ci		/* multisamples have to be handled differently, and
157462306a36Sopenharmony_ci		   cannot be dealt with properly by snd_wavefront_cmd() alone.
157562306a36Sopenharmony_ci		*/
157662306a36Sopenharmony_ci		wc->status = wavefront_fetch_multisample
157762306a36Sopenharmony_ci			(dev, (wavefront_patch_info *) wc->rbuf);
157862306a36Sopenharmony_ci		return 0;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	case WFC_UPLOAD_SAMPLE_ALIAS:
158162306a36Sopenharmony_ci		snd_printk ("support for sample alias upload "
158262306a36Sopenharmony_ci			"being considered.\n");
158362306a36Sopenharmony_ci		wc->status = EINVAL;
158462306a36Sopenharmony_ci		return -EINVAL;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* Post-handling of certain commands.
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	   In particular, if the command was an upload, demunge the data
159262306a36Sopenharmony_ci	   so that the user-level doesn't have to think about it.
159362306a36Sopenharmony_ci	*/
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (wc->status == 0) {
159662306a36Sopenharmony_ci		switch (wc->cmd) {
159762306a36Sopenharmony_ci			/* intercept any freemem requests so that we know
159862306a36Sopenharmony_ci			   we are always current with the user-level view
159962306a36Sopenharmony_ci			   of things.
160062306a36Sopenharmony_ci			*/
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		case WFC_REPORT_FREE_MEMORY:
160362306a36Sopenharmony_ci			dev->freemem = demunge_int32 (wc->rbuf, 4);
160462306a36Sopenharmony_ci			break;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci		case WFC_UPLOAD_PATCH:
160762306a36Sopenharmony_ci			demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES);
160862306a36Sopenharmony_ci			break;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci		case WFC_UPLOAD_PROGRAM:
161162306a36Sopenharmony_ci			demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES);
161262306a36Sopenharmony_ci			break;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci		case WFC_UPLOAD_EDRUM_PROGRAM:
161562306a36Sopenharmony_ci			demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1);
161662306a36Sopenharmony_ci			break;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		case WFC_UPLOAD_SAMPLE_HEADER:
161962306a36Sopenharmony_ci			process_sample_hdr (wc->rbuf);
162062306a36Sopenharmony_ci			break;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci		case WFC_UPLOAD_SAMPLE_ALIAS:
162362306a36Sopenharmony_ci			snd_printk ("support for "
162462306a36Sopenharmony_ci				    "sample aliases still "
162562306a36Sopenharmony_ci				    "being considered.\n");
162662306a36Sopenharmony_ci			break;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci		case WFC_VMIDI_OFF:
162962306a36Sopenharmony_ci			snd_wavefront_midi_disable_virtual (acard);
163062306a36Sopenharmony_ci			break;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci		case WFC_VMIDI_ON:
163362306a36Sopenharmony_ci			snd_wavefront_midi_enable_virtual (acard);
163462306a36Sopenharmony_ci			break;
163562306a36Sopenharmony_ci		}
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	return 0;
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ciint
164262306a36Sopenharmony_cisnd_wavefront_synth_open (struct snd_hwdep *hw, struct file *file)
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	if (!try_module_get(hw->card->module))
164662306a36Sopenharmony_ci		return -EFAULT;
164762306a36Sopenharmony_ci	file->private_data = hw;
164862306a36Sopenharmony_ci	return 0;
164962306a36Sopenharmony_ci}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ciint
165262306a36Sopenharmony_cisnd_wavefront_synth_release (struct snd_hwdep *hw, struct file *file)
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci{
165562306a36Sopenharmony_ci	module_put(hw->card->module);
165662306a36Sopenharmony_ci	return 0;
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ciint
166062306a36Sopenharmony_cisnd_wavefront_synth_ioctl (struct snd_hwdep *hw, struct file *file,
166162306a36Sopenharmony_ci			   unsigned int cmd, unsigned long arg)
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci{
166462306a36Sopenharmony_ci	struct snd_card *card;
166562306a36Sopenharmony_ci	snd_wavefront_t *dev;
166662306a36Sopenharmony_ci	snd_wavefront_card_t *acard;
166762306a36Sopenharmony_ci	wavefront_control *wc;
166862306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
166962306a36Sopenharmony_ci	int err;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	card = (struct snd_card *) hw->card;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (snd_BUG_ON(!card))
167462306a36Sopenharmony_ci		return -ENODEV;
167562306a36Sopenharmony_ci	if (snd_BUG_ON(!card->private_data))
167662306a36Sopenharmony_ci		return -ENODEV;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	acard = card->private_data;
167962306a36Sopenharmony_ci	dev = &acard->wavefront;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	switch (cmd) {
168262306a36Sopenharmony_ci	case WFCTL_LOAD_SPP:
168362306a36Sopenharmony_ci		if (wavefront_load_patch (dev, argp) != 0) {
168462306a36Sopenharmony_ci			return -EIO;
168562306a36Sopenharmony_ci		}
168662306a36Sopenharmony_ci		break;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	case WFCTL_WFCMD:
168962306a36Sopenharmony_ci		wc = memdup_user(argp, sizeof(*wc));
169062306a36Sopenharmony_ci		if (IS_ERR(wc))
169162306a36Sopenharmony_ci			return PTR_ERR(wc);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci		if (wavefront_synth_control (acard, wc) < 0)
169462306a36Sopenharmony_ci			err = -EIO;
169562306a36Sopenharmony_ci		else if (copy_to_user (argp, wc, sizeof (*wc)))
169662306a36Sopenharmony_ci			err = -EFAULT;
169762306a36Sopenharmony_ci		else
169862306a36Sopenharmony_ci			err = 0;
169962306a36Sopenharmony_ci		kfree(wc);
170062306a36Sopenharmony_ci		return err;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	default:
170362306a36Sopenharmony_ci		return -EINVAL;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return 0;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci/***********************************************************************/
171162306a36Sopenharmony_ci/*  WaveFront: interface for card-level wavefront module               */
171262306a36Sopenharmony_ci/***********************************************************************/
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_civoid
171562306a36Sopenharmony_cisnd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	snd_wavefront_t *dev = &card->wavefront;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	/*
172062306a36Sopenharmony_ci	   Some comments on interrupts. I attempted a version of this
172162306a36Sopenharmony_ci	   driver that used interrupts throughout the code instead of
172262306a36Sopenharmony_ci	   doing busy and/or sleep-waiting. Alas, it appears that once
172362306a36Sopenharmony_ci	   the Motorola firmware is downloaded, the card *never*
172462306a36Sopenharmony_ci	   generates an RX interrupt. These are successfully generated
172562306a36Sopenharmony_ci	   during firmware loading, and after that wavefront_status()
172662306a36Sopenharmony_ci	   reports that an interrupt is pending on the card from time
172762306a36Sopenharmony_ci	   to time, but it never seems to be delivered to this
172862306a36Sopenharmony_ci	   driver. Note also that wavefront_status() continues to
172962306a36Sopenharmony_ci	   report that RX interrupts are enabled, suggesting that I
173062306a36Sopenharmony_ci	   didn't goof up and disable them by mistake.
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	   Thus, I stepped back to a prior version of
173362306a36Sopenharmony_ci	   wavefront_wait(), the only place where this really
173462306a36Sopenharmony_ci	   matters. Its sad, but I've looked through the code to check
173562306a36Sopenharmony_ci	   on things, and I really feel certain that the Motorola
173662306a36Sopenharmony_ci	   firmware prevents RX-ready interrupts.
173762306a36Sopenharmony_ci	*/
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) {
174062306a36Sopenharmony_ci		return;
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	spin_lock(&dev->irq_lock);
174462306a36Sopenharmony_ci	dev->irq_ok = 1;
174562306a36Sopenharmony_ci	dev->irq_cnt++;
174662306a36Sopenharmony_ci	spin_unlock(&dev->irq_lock);
174762306a36Sopenharmony_ci	wake_up(&dev->interrupt_sleeper);
174862306a36Sopenharmony_ci}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci/* STATUS REGISTER
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci0 Host Rx Interrupt Enable (1=Enabled)
175362306a36Sopenharmony_ci1 Host Rx Register Full (1=Full)
175462306a36Sopenharmony_ci2 Host Rx Interrupt Pending (1=Interrupt)
175562306a36Sopenharmony_ci3 Unused
175662306a36Sopenharmony_ci4 Host Tx Interrupt (1=Enabled)
175762306a36Sopenharmony_ci5 Host Tx Register empty (1=Empty)
175862306a36Sopenharmony_ci6 Host Tx Interrupt Pending (1=Interrupt)
175962306a36Sopenharmony_ci7 Unused
176062306a36Sopenharmony_ci*/
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_cistatic int
176362306a36Sopenharmony_cisnd_wavefront_interrupt_bits (int irq)
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	int bits;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	switch (irq) {
176962306a36Sopenharmony_ci	case 9:
177062306a36Sopenharmony_ci		bits = 0x00;
177162306a36Sopenharmony_ci		break;
177262306a36Sopenharmony_ci	case 5:
177362306a36Sopenharmony_ci		bits = 0x08;
177462306a36Sopenharmony_ci		break;
177562306a36Sopenharmony_ci	case 12:
177662306a36Sopenharmony_ci		bits = 0x10;
177762306a36Sopenharmony_ci		break;
177862306a36Sopenharmony_ci	case 15:
177962306a36Sopenharmony_ci		bits = 0x18;
178062306a36Sopenharmony_ci		break;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	default:
178362306a36Sopenharmony_ci		snd_printk ("invalid IRQ %d\n", irq);
178462306a36Sopenharmony_ci		bits = -1;
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	return bits;
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic void
179162306a36Sopenharmony_ciwavefront_should_cause_interrupt (snd_wavefront_t *dev,
179262306a36Sopenharmony_ci				  int val, int port, unsigned long timeout)
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci{
179562306a36Sopenharmony_ci	wait_queue_entry_t wait;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	init_waitqueue_entry(&wait, current);
179862306a36Sopenharmony_ci	spin_lock_irq(&dev->irq_lock);
179962306a36Sopenharmony_ci	add_wait_queue(&dev->interrupt_sleeper, &wait);
180062306a36Sopenharmony_ci	dev->irq_ok = 0;
180162306a36Sopenharmony_ci	outb (val,port);
180262306a36Sopenharmony_ci	spin_unlock_irq(&dev->irq_lock);
180362306a36Sopenharmony_ci	while (!dev->irq_ok && time_before(jiffies, timeout)) {
180462306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
180562306a36Sopenharmony_ci		barrier();
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_cistatic int
181062306a36Sopenharmony_ciwavefront_reset_to_cleanliness (snd_wavefront_t *dev)
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	int bits;
181462306a36Sopenharmony_ci	int hwv[2];
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/* IRQ already checked */
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	bits = snd_wavefront_interrupt_bits (dev->irq);
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* try reset of port */
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	outb (0x0, dev->control_port);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* At this point, the board is in reset, and the H/W initialization
182562306a36Sopenharmony_ci	   register is accessed at the same address as the data port.
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	   Bit 7 - Enable IRQ Driver
182862306a36Sopenharmony_ci	   0 - Tri-state the Wave-Board drivers for the PC Bus IRQs
182962306a36Sopenharmony_ci	   1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus.
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	   Bit 6 - MIDI Interface Select
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	   0 - Use the MIDI Input from the 26-pin WaveBlaster
183462306a36Sopenharmony_ci	   compatible header as the serial MIDI source
183562306a36Sopenharmony_ci	   1 - Use the MIDI Input from the 9-pin D connector as the
183662306a36Sopenharmony_ci	   serial MIDI source.
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	   Bits 5:3 - IRQ Selection
183962306a36Sopenharmony_ci	   0 0 0 - IRQ 2/9
184062306a36Sopenharmony_ci	   0 0 1 - IRQ 5
184162306a36Sopenharmony_ci	   0 1 0 - IRQ 12
184262306a36Sopenharmony_ci	   0 1 1 - IRQ 15
184362306a36Sopenharmony_ci	   1 0 0 - Reserved
184462306a36Sopenharmony_ci	   1 0 1 - Reserved
184562306a36Sopenharmony_ci	   1 1 0 - Reserved
184662306a36Sopenharmony_ci	   1 1 1 - Reserved
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	   Bits 2:1 - Reserved
184962306a36Sopenharmony_ci	   Bit 0 - Disable Boot ROM
185062306a36Sopenharmony_ci	   0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM
185162306a36Sopenharmony_ci	   1 - memory accesses to 03FC30-03FFFFH are directed to external
185262306a36Sopenharmony_ci	   storage.
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	*/
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	/* configure hardware: IRQ, enable interrupts,
185762306a36Sopenharmony_ci	   plus external 9-pin MIDI interface selected
185862306a36Sopenharmony_ci	*/
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	outb (0x80 | 0x40 | bits, dev->data_port);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	/* CONTROL REGISTER
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	   0 Host Rx Interrupt Enable (1=Enabled)      0x1
186562306a36Sopenharmony_ci	   1 Unused                                    0x2
186662306a36Sopenharmony_ci	   2 Unused                                    0x4
186762306a36Sopenharmony_ci	   3 Unused                                    0x8
186862306a36Sopenharmony_ci	   4 Host Tx Interrupt Enable                 0x10
186962306a36Sopenharmony_ci	   5 Mute (0=Mute; 1=Play)                    0x20
187062306a36Sopenharmony_ci	   6 Master Interrupt Enable (1=Enabled)      0x40
187162306a36Sopenharmony_ci	   7 Master Reset (0=Reset; 1=Run)            0x80
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	   Take us out of reset, mute output, master + TX + RX interrupts on.
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	   We'll get an interrupt presumably to tell us that the TX
187662306a36Sopenharmony_ci	   register is clear.
187762306a36Sopenharmony_ci	*/
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1,
188062306a36Sopenharmony_ci					 dev->control_port,
188162306a36Sopenharmony_ci					 (reset_time*HZ)/100);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	/* Note: data port is now the data port, not the h/w initialization
188462306a36Sopenharmony_ci	   port.
188562306a36Sopenharmony_ci	 */
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (!dev->irq_ok) {
188862306a36Sopenharmony_ci		snd_printk ("intr not received after h/w un-reset.\n");
188962306a36Sopenharmony_ci		goto gone_bad;
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	/* Note: data port is now the data port, not the h/w initialization
189362306a36Sopenharmony_ci	   port.
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	   At this point, only "HW VERSION" or "DOWNLOAD OS" commands
189662306a36Sopenharmony_ci	   will work. So, issue one of them, and wait for TX
189762306a36Sopenharmony_ci	   interrupt. This can take a *long* time after a cold boot,
189862306a36Sopenharmony_ci	   while the ISC ROM does its RAM test. The SDK says up to 4
189962306a36Sopenharmony_ci	   seconds - with 12MB of RAM on a Tropez+, it takes a lot
190062306a36Sopenharmony_ci	   longer than that (~16secs). Note that the card understands
190162306a36Sopenharmony_ci	   the difference between a warm and a cold boot, so
190262306a36Sopenharmony_ci	   subsequent ISC2115 reboots (say, caused by module
190362306a36Sopenharmony_ci	   reloading) will get through this much faster.
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	   XXX Interesting question: why is no RX interrupt received first ?
190662306a36Sopenharmony_ci	*/
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION,
190962306a36Sopenharmony_ci					 dev->data_port, ramcheck_time*HZ);
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	if (!dev->irq_ok) {
191262306a36Sopenharmony_ci		snd_printk ("post-RAM-check interrupt not received.\n");
191362306a36Sopenharmony_ci		goto gone_bad;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (!wavefront_wait (dev, STAT_CAN_READ)) {
191762306a36Sopenharmony_ci		snd_printk ("no response to HW version cmd.\n");
191862306a36Sopenharmony_ci		goto gone_bad;
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	hwv[0] = wavefront_read(dev);
192262306a36Sopenharmony_ci	if (hwv[0] == -1) {
192362306a36Sopenharmony_ci		snd_printk ("board not responding correctly.\n");
192462306a36Sopenharmony_ci		goto gone_bad;
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (hwv[0] == 0xFF) { /* NAK */
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci		/* Board's RAM test failed. Try to read error code,
193062306a36Sopenharmony_ci		   and tell us about it either way.
193162306a36Sopenharmony_ci		*/
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci		hwv[0] = wavefront_read(dev);
193462306a36Sopenharmony_ci		if (hwv[0] == -1) {
193562306a36Sopenharmony_ci			snd_printk ("on-board RAM test failed "
193662306a36Sopenharmony_ci				    "(bad error code).\n");
193762306a36Sopenharmony_ci		} else {
193862306a36Sopenharmony_ci			snd_printk ("on-board RAM test failed "
193962306a36Sopenharmony_ci				    "(error code: 0x%x).\n",
194062306a36Sopenharmony_ci				hwv[0]);
194162306a36Sopenharmony_ci		}
194262306a36Sopenharmony_ci		goto gone_bad;
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* We're OK, just get the next byte of the HW version response */
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	hwv[1] = wavefront_read(dev);
194862306a36Sopenharmony_ci	if (hwv[1] == -1) {
194962306a36Sopenharmony_ci		snd_printk ("incorrect h/w response.\n");
195062306a36Sopenharmony_ci		goto gone_bad;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	snd_printk ("hardware version %d.%d\n",
195462306a36Sopenharmony_ci		    hwv[0], hwv[1]);
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	return 0;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci     gone_bad:
196062306a36Sopenharmony_ci	return (1);
196162306a36Sopenharmony_ci}
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_cistatic int
196462306a36Sopenharmony_ciwavefront_download_firmware (snd_wavefront_t *dev, char *path)
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci{
196762306a36Sopenharmony_ci	const unsigned char *buf;
196862306a36Sopenharmony_ci	int len, err;
196962306a36Sopenharmony_ci	int section_cnt_downloaded = 0;
197062306a36Sopenharmony_ci	const struct firmware *firmware;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	err = request_firmware(&firmware, path, dev->card->dev);
197362306a36Sopenharmony_ci	if (err < 0) {
197462306a36Sopenharmony_ci		snd_printk(KERN_ERR "firmware (%s) download failed!!!\n", path);
197562306a36Sopenharmony_ci		return 1;
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	len = 0;
197962306a36Sopenharmony_ci	buf = firmware->data;
198062306a36Sopenharmony_ci	for (;;) {
198162306a36Sopenharmony_ci		int section_length = *(signed char *)buf;
198262306a36Sopenharmony_ci		if (section_length == 0)
198362306a36Sopenharmony_ci			break;
198462306a36Sopenharmony_ci		if (section_length < 0 || section_length > WF_SECTION_MAX) {
198562306a36Sopenharmony_ci			snd_printk(KERN_ERR
198662306a36Sopenharmony_ci				   "invalid firmware section length %d\n",
198762306a36Sopenharmony_ci				   section_length);
198862306a36Sopenharmony_ci			goto failure;
198962306a36Sopenharmony_ci		}
199062306a36Sopenharmony_ci		buf++;
199162306a36Sopenharmony_ci		len++;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci		if (firmware->size < len + section_length) {
199462306a36Sopenharmony_ci			snd_printk(KERN_ERR "firmware section read error.\n");
199562306a36Sopenharmony_ci			goto failure;
199662306a36Sopenharmony_ci		}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci		/* Send command */
199962306a36Sopenharmony_ci		if (wavefront_write(dev, WFC_DOWNLOAD_OS))
200062306a36Sopenharmony_ci			goto failure;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci		for (; section_length; section_length--) {
200362306a36Sopenharmony_ci			if (wavefront_write(dev, *buf))
200462306a36Sopenharmony_ci				goto failure;
200562306a36Sopenharmony_ci			buf++;
200662306a36Sopenharmony_ci			len++;
200762306a36Sopenharmony_ci		}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci		/* get ACK */
201062306a36Sopenharmony_ci		if (!wavefront_wait(dev, STAT_CAN_READ)) {
201162306a36Sopenharmony_ci			snd_printk(KERN_ERR "time out for firmware ACK.\n");
201262306a36Sopenharmony_ci			goto failure;
201362306a36Sopenharmony_ci		}
201462306a36Sopenharmony_ci		err = inb(dev->data_port);
201562306a36Sopenharmony_ci		if (err != WF_ACK) {
201662306a36Sopenharmony_ci			snd_printk(KERN_ERR
201762306a36Sopenharmony_ci				   "download of section #%d not "
201862306a36Sopenharmony_ci				   "acknowledged, ack = 0x%x\n",
201962306a36Sopenharmony_ci				   section_cnt_downloaded + 1, err);
202062306a36Sopenharmony_ci			goto failure;
202162306a36Sopenharmony_ci		}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci		section_cnt_downloaded++;
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	release_firmware(firmware);
202762306a36Sopenharmony_ci	return 0;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci failure:
203062306a36Sopenharmony_ci	release_firmware(firmware);
203162306a36Sopenharmony_ci	snd_printk(KERN_ERR "firmware download failed!!!\n");
203262306a36Sopenharmony_ci	return 1;
203362306a36Sopenharmony_ci}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_cistatic int
203762306a36Sopenharmony_ciwavefront_do_reset (snd_wavefront_t *dev)
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	char voices[1];
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	if (wavefront_reset_to_cleanliness (dev)) {
204362306a36Sopenharmony_ci		snd_printk ("hw reset failed.\n");
204462306a36Sopenharmony_ci		goto gone_bad;
204562306a36Sopenharmony_ci	}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	if (dev->israw) {
204862306a36Sopenharmony_ci		if (wavefront_download_firmware (dev, ospath)) {
204962306a36Sopenharmony_ci			goto gone_bad;
205062306a36Sopenharmony_ci		}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		dev->israw = 0;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci		/* Wait for the OS to get running. The protocol for
205562306a36Sopenharmony_ci		   this is non-obvious, and was determined by
205662306a36Sopenharmony_ci		   using port-IO tracing in DOSemu and some
205762306a36Sopenharmony_ci		   experimentation here.
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		   Rather than using timed waits, use interrupts creatively.
206062306a36Sopenharmony_ci		*/
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		wavefront_should_cause_interrupt (dev, WFC_NOOP,
206362306a36Sopenharmony_ci						  dev->data_port,
206462306a36Sopenharmony_ci						  (osrun_time*HZ));
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci		if (!dev->irq_ok) {
206762306a36Sopenharmony_ci			snd_printk ("no post-OS interrupt.\n");
206862306a36Sopenharmony_ci			goto gone_bad;
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci		/* Now, do it again ! */
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci		wavefront_should_cause_interrupt (dev, WFC_NOOP,
207462306a36Sopenharmony_ci						  dev->data_port, (10*HZ));
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci		if (!dev->irq_ok) {
207762306a36Sopenharmony_ci			snd_printk ("no post-OS interrupt(2).\n");
207862306a36Sopenharmony_ci			goto gone_bad;
207962306a36Sopenharmony_ci		}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci		/* OK, no (RX/TX) interrupts any more, but leave mute
208262306a36Sopenharmony_ci		   in effect.
208362306a36Sopenharmony_ci		*/
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci		outb (0x80|0x40, dev->control_port);
208662306a36Sopenharmony_ci	}
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	/* SETUPSND.EXE asks for sample memory config here, but since i
208962306a36Sopenharmony_ci	   have no idea how to interpret the result, we'll forget
209062306a36Sopenharmony_ci	   about it.
209162306a36Sopenharmony_ci	*/
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	dev->freemem = wavefront_freemem(dev);
209462306a36Sopenharmony_ci	if (dev->freemem < 0)
209562306a36Sopenharmony_ci		goto gone_bad;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	snd_printk ("available DRAM %dk\n", dev->freemem / 1024);
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	if (wavefront_write (dev, 0xf0) ||
210062306a36Sopenharmony_ci	    wavefront_write (dev, 1) ||
210162306a36Sopenharmony_ci	    (wavefront_read (dev) < 0)) {
210262306a36Sopenharmony_ci		dev->debug = 0;
210362306a36Sopenharmony_ci		snd_printk ("MPU emulation mode not set.\n");
210462306a36Sopenharmony_ci		goto gone_bad;
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	voices[0] = 32;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, NULL, voices)) {
211062306a36Sopenharmony_ci		snd_printk ("cannot set number of voices to 32.\n");
211162306a36Sopenharmony_ci		goto gone_bad;
211262306a36Sopenharmony_ci	}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	return 0;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci gone_bad:
211862306a36Sopenharmony_ci	/* reset that sucker so that it doesn't bother us. */
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	outb (0x0, dev->control_port);
212162306a36Sopenharmony_ci	dev->interrupts_are_midi = 0;
212262306a36Sopenharmony_ci	return 1;
212362306a36Sopenharmony_ci}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ciint
212662306a36Sopenharmony_cisnd_wavefront_start (snd_wavefront_t *dev)
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	int samples_are_from_rom;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	/* IMPORTANT: assumes that snd_wavefront_detect() and/or
213262306a36Sopenharmony_ci	   wavefront_reset_to_cleanliness() has already been called
213362306a36Sopenharmony_ci	*/
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	if (dev->israw) {
213662306a36Sopenharmony_ci		samples_are_from_rom = 1;
213762306a36Sopenharmony_ci	} else {
213862306a36Sopenharmony_ci		/* XXX is this always true ? */
213962306a36Sopenharmony_ci		samples_are_from_rom = 0;
214062306a36Sopenharmony_ci	}
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	if (dev->israw || fx_raw) {
214362306a36Sopenharmony_ci		if (wavefront_do_reset (dev)) {
214462306a36Sopenharmony_ci			return -1;
214562306a36Sopenharmony_ci		}
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci	/* Check for FX device, present only on Tropez+ */
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	dev->has_fx = (snd_wavefront_fx_detect (dev) == 0);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	if (dev->has_fx && fx_raw) {
215262306a36Sopenharmony_ci		snd_wavefront_fx_start (dev);
215362306a36Sopenharmony_ci	}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	wavefront_get_sample_status (dev, samples_are_from_rom);
215662306a36Sopenharmony_ci	wavefront_get_program_status (dev);
215762306a36Sopenharmony_ci	wavefront_get_patch_status (dev);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	/* Start normal operation: unreset, master interrupt enabled, no mute
216062306a36Sopenharmony_ci	*/
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	outb (0x80|0x40|0x20, dev->control_port);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	return (0);
216562306a36Sopenharmony_ci}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ciint
216862306a36Sopenharmony_cisnd_wavefront_detect (snd_wavefront_card_t *card)
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci{
217162306a36Sopenharmony_ci	unsigned char   rbuf[4], wbuf[4];
217262306a36Sopenharmony_ci	snd_wavefront_t *dev = &card->wavefront;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	/* returns zero if a WaveFront card is successfully detected.
217562306a36Sopenharmony_ci	   negative otherwise.
217662306a36Sopenharmony_ci	*/
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	dev->israw = 0;
217962306a36Sopenharmony_ci	dev->has_fx = 0;
218062306a36Sopenharmony_ci	dev->debug = debug_default;
218162306a36Sopenharmony_ci	dev->interrupts_are_midi = 0;
218262306a36Sopenharmony_ci	dev->irq_cnt = 0;
218362306a36Sopenharmony_ci	dev->rom_samples_rdonly = 1;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci		dev->fw_version[0] = rbuf[0];
218862306a36Sopenharmony_ci		dev->fw_version[1] = rbuf[1];
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci		snd_printk ("firmware %d.%d already loaded.\n",
219162306a36Sopenharmony_ci			    rbuf[0], rbuf[1]);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci		/* check that a command actually works */
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci		if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION,
219662306a36Sopenharmony_ci				       rbuf, wbuf) == 0) {
219762306a36Sopenharmony_ci			dev->hw_version[0] = rbuf[0];
219862306a36Sopenharmony_ci			dev->hw_version[1] = rbuf[1];
219962306a36Sopenharmony_ci		} else {
220062306a36Sopenharmony_ci			snd_printk ("not raw, but no "
220162306a36Sopenharmony_ci				    "hardware version!\n");
220262306a36Sopenharmony_ci			return -1;
220362306a36Sopenharmony_ci		}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci		if (!wf_raw) {
220662306a36Sopenharmony_ci			return 0;
220762306a36Sopenharmony_ci		} else {
220862306a36Sopenharmony_ci			snd_printk ("reloading firmware as you requested.\n");
220962306a36Sopenharmony_ci			dev->israw = 1;
221062306a36Sopenharmony_ci		}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	} else {
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci		dev->israw = 1;
221562306a36Sopenharmony_ci		snd_printk ("no response to firmware probe, assume raw.\n");
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	return 0;
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ciMODULE_FIRMWARE(DEFAULT_OSPATH);
2223