2014-04-25 21:45:14 +08:00
|
|
|
/*
|
|
|
|
* bebob.h - a part of driver for BeBoB based devices
|
|
|
|
*
|
|
|
|
* Copyright (c) 2013-2014 Takashi Sakamoto
|
|
|
|
*
|
|
|
|
* Licensed under the terms of the GNU General Public License, version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef SOUND_BEBOB_H_INCLUDED
|
|
|
|
#define SOUND_BEBOB_H_INCLUDED
|
|
|
|
|
|
|
|
#include <linux/compat.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/firewire.h>
|
|
|
|
#include <linux/firewire-constants.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mod_devicetable.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/initval.h>
|
2014-04-25 21:45:17 +08:00
|
|
|
#include <sound/info.h>
|
2014-04-25 21:45:18 +08:00
|
|
|
#include <sound/rawmidi.h>
|
2014-04-25 21:45:19 +08:00
|
|
|
#include <sound/pcm.h>
|
|
|
|
#include <sound/pcm_params.h>
|
2014-04-25 21:45:20 +08:00
|
|
|
#include <sound/firewire.h>
|
|
|
|
#include <sound/hwdep.h>
|
2014-04-25 21:45:14 +08:00
|
|
|
|
|
|
|
#include "../lib.h"
|
|
|
|
#include "../fcp.h"
|
2014-04-25 21:45:15 +08:00
|
|
|
#include "../packets-buffer.h"
|
|
|
|
#include "../iso-resources.h"
|
2015-09-19 10:21:55 +08:00
|
|
|
#include "../amdtp-am824.h"
|
2014-04-25 21:45:15 +08:00
|
|
|
#include "../cmp.h"
|
2014-04-25 21:45:14 +08:00
|
|
|
|
|
|
|
/* basic register addresses on DM1000/DM1100/DM1500 */
|
2014-05-27 23:14:42 +08:00
|
|
|
#define BEBOB_ADDR_REG_INFO 0xffffc8020000ULL
|
|
|
|
#define BEBOB_ADDR_REG_REQ 0xffffc8021000ULL
|
2014-04-25 21:45:14 +08:00
|
|
|
|
2014-04-25 21:45:15 +08:00
|
|
|
struct snd_bebob;
|
|
|
|
|
|
|
|
#define SND_BEBOB_STRM_FMT_ENTRIES 7
|
|
|
|
struct snd_bebob_stream_formation {
|
|
|
|
unsigned int pcm;
|
|
|
|
unsigned int midi;
|
|
|
|
};
|
|
|
|
/* this is a lookup table for index of stream formations */
|
|
|
|
extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
|
|
|
|
|
2014-04-25 21:45:21 +08:00
|
|
|
/* device specific operations */
|
2015-06-14 11:49:28 +08:00
|
|
|
enum snd_bebob_clock_type {
|
|
|
|
SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
|
|
|
|
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
|
|
|
|
SND_BEBOB_CLOCK_TYPE_SYT,
|
|
|
|
};
|
2014-04-25 21:45:21 +08:00
|
|
|
struct snd_bebob_clock_spec {
|
|
|
|
unsigned int num;
|
2014-10-21 14:26:10 +08:00
|
|
|
const char *const *labels;
|
2015-06-14 11:49:28 +08:00
|
|
|
enum snd_bebob_clock_type *types;
|
2014-04-25 21:45:21 +08:00
|
|
|
int (*get)(struct snd_bebob *bebob, unsigned int *id);
|
|
|
|
};
|
|
|
|
struct snd_bebob_rate_spec {
|
|
|
|
int (*get)(struct snd_bebob *bebob, unsigned int *rate);
|
|
|
|
int (*set)(struct snd_bebob *bebob, unsigned int rate);
|
|
|
|
};
|
|
|
|
struct snd_bebob_meter_spec {
|
|
|
|
unsigned int num;
|
2014-10-21 14:26:10 +08:00
|
|
|
const char *const *labels;
|
2014-04-25 21:45:21 +08:00
|
|
|
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
|
|
|
|
};
|
|
|
|
struct snd_bebob_spec {
|
2015-10-11 14:10:55 +08:00
|
|
|
const struct snd_bebob_clock_spec *clock;
|
|
|
|
const struct snd_bebob_rate_spec *rate;
|
|
|
|
const struct snd_bebob_meter_spec *meter;
|
2014-04-25 21:45:21 +08:00
|
|
|
};
|
|
|
|
|
2014-04-25 21:45:14 +08:00
|
|
|
struct snd_bebob {
|
|
|
|
struct snd_card *card;
|
|
|
|
struct fw_unit *unit;
|
|
|
|
int card_index;
|
|
|
|
|
|
|
|
struct mutex mutex;
|
|
|
|
spinlock_t lock;
|
2014-04-25 21:45:15 +08:00
|
|
|
|
2014-04-25 21:45:21 +08:00
|
|
|
const struct snd_bebob_spec *spec;
|
|
|
|
|
2014-04-25 21:45:15 +08:00
|
|
|
unsigned int midi_input_ports;
|
|
|
|
unsigned int midi_output_ports;
|
|
|
|
|
2014-04-25 21:45:16 +08:00
|
|
|
bool connected;
|
|
|
|
|
2014-04-25 21:45:15 +08:00
|
|
|
struct amdtp_stream *master;
|
|
|
|
struct amdtp_stream tx_stream;
|
|
|
|
struct amdtp_stream rx_stream;
|
|
|
|
struct cmp_connection out_conn;
|
|
|
|
struct cmp_connection in_conn;
|
2015-06-14 11:49:36 +08:00
|
|
|
atomic_t substreams_counter;
|
2014-04-25 21:45:15 +08:00
|
|
|
|
|
|
|
struct snd_bebob_stream_formation
|
|
|
|
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
|
|
|
|
struct snd_bebob_stream_formation
|
|
|
|
rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
|
|
|
|
|
|
|
|
int sync_input_plug;
|
2014-04-25 21:45:20 +08:00
|
|
|
|
|
|
|
/* for uapi */
|
|
|
|
int dev_lock_count;
|
|
|
|
bool dev_lock_changed;
|
|
|
|
wait_queue_head_t hwdep_wait;
|
2014-04-25 21:45:26 +08:00
|
|
|
|
|
|
|
/* for M-Audio special devices */
|
|
|
|
void *maudio_special_quirk;
|
2014-04-25 21:45:30 +08:00
|
|
|
bool deferred_registration;
|
2015-06-14 11:49:33 +08:00
|
|
|
|
|
|
|
/* For BeBoB version quirk. */
|
|
|
|
unsigned int version;
|
2014-04-25 21:45:14 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
|
|
|
|
{
|
|
|
|
return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
|
|
|
|
BEBOB_ADDR_REG_INFO + addr,
|
|
|
|
buf, size, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
|
|
|
|
{
|
|
|
|
return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
|
|
|
|
BEBOB_ADDR_REG_INFO + addr,
|
|
|
|
(void *)buf, sizeof(u32), 0);
|
|
|
|
}
|
|
|
|
|
2014-04-25 21:45:21 +08:00
|
|
|
/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
|
|
|
|
int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
|
|
|
|
unsigned int fb_id, unsigned int num);
|
|
|
|
int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
|
|
|
|
unsigned int fb_id, unsigned int *num);
|
|
|
|
|
2014-04-25 21:45:15 +08:00
|
|
|
/*
|
|
|
|
* AVC command extensions, AV/C Unit and Subunit, Revision 17
|
|
|
|
* (Nov 2003, BridgeCo)
|
|
|
|
*/
|
|
|
|
#define AVC_BRIDGECO_ADDR_BYTES 6
|
|
|
|
enum avc_bridgeco_plug_dir {
|
|
|
|
AVC_BRIDGECO_PLUG_DIR_IN = 0x00,
|
|
|
|
AVC_BRIDGECO_PLUG_DIR_OUT = 0x01
|
|
|
|
};
|
|
|
|
enum avc_bridgeco_plug_mode {
|
|
|
|
AVC_BRIDGECO_PLUG_MODE_UNIT = 0x00,
|
|
|
|
AVC_BRIDGECO_PLUG_MODE_SUBUNIT = 0x01,
|
|
|
|
AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK = 0x02
|
|
|
|
};
|
|
|
|
enum avc_bridgeco_plug_unit {
|
|
|
|
AVC_BRIDGECO_PLUG_UNIT_ISOC = 0x00,
|
|
|
|
AVC_BRIDGECO_PLUG_UNIT_EXT = 0x01,
|
|
|
|
AVC_BRIDGECO_PLUG_UNIT_ASYNC = 0x02
|
|
|
|
};
|
|
|
|
enum avc_bridgeco_plug_type {
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_ISOC = 0x00,
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_ASYNC = 0x01,
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
|
ALSA: bebob: improve signal mode detection for clock source
With BeBoB version 3, current ALSA BeBoB driver detects the type of
current clock signal source wrongly. This is due to a lack of proper
implementation to parse the information.
This commit renews the parser. As a result, this driver detects
SYT-Match clock signal, thus it can start streams with two modes;
SYT-Match mode and the others. SYT-Match mode will be supported in future
commits.
There's a constrain about detected internal/external clock source.
When detecting external clock source, this driver allows userspace
applications to use current sampling rate only. This is due to consider
abour synchronization to external clock sources such as S/PDIF, ADAT or
word-clock.
According to several information from some devices, I guesss that the
internal clock of most devices synchronize to IEEE 1394 cycle start
packet. In this case, by a usual way, it's detect as 'Sync type
of output Music Sub-Unit' connected to 'Sync type of PCR output Unit
(oPCR)', and this driver judges it as internal clock. Therefore,
userspace applications is allowed to request arbitrary supported sampling
rates.
On the other hand, several devices based on BeBoB version 3 have
additional internal clock. In this case, by a usual way, it's detect as
'Sync/Additional type of External input Unit'. Unfortunately, there's no
way to distinguish this sync type from the other external clock sources
such as word-clock. In this case, this driver handles it as external and
userspace applications is forced to use current sampling rate.
I note that when the source of clock is detected as 'Isochronous stream
type of input PCR[0]', it's under 'SYT-Match' mode. In this mode, the
synchronization clock is generated according to SYT-series in received
packets. In this case, this driver generates the series by myself. I
experienced this mode often make the device silent suddenly during
playbacking. This means that the mode is easy to lost synchronization.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-06-14 11:49:27 +08:00
|
|
|
AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
|
|
|
|
AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
|
2014-04-25 21:45:15 +08:00
|
|
|
};
|
|
|
|
static inline void
|
|
|
|
avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
enum avc_bridgeco_plug_dir dir,
|
|
|
|
enum avc_bridgeco_plug_unit unit,
|
|
|
|
unsigned int pid)
|
|
|
|
{
|
|
|
|
buf[0] = 0xff; /* Unit */
|
|
|
|
buf[1] = dir;
|
|
|
|
buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
|
|
|
|
buf[3] = unit;
|
|
|
|
buf[4] = 0xff & pid;
|
|
|
|
buf[5] = 0xff; /* reserved */
|
|
|
|
}
|
|
|
|
static inline void
|
|
|
|
avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
enum avc_bridgeco_plug_dir dir,
|
|
|
|
unsigned int pid)
|
|
|
|
{
|
|
|
|
buf[0] = 0x60; /* Music subunit */
|
|
|
|
buf[1] = dir;
|
|
|
|
buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
|
|
|
|
buf[3] = 0xff & pid;
|
|
|
|
buf[4] = 0xff; /* reserved */
|
|
|
|
buf[5] = 0xff; /* reserved */
|
|
|
|
}
|
|
|
|
int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
|
|
|
|
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
u8 *buf, unsigned int len);
|
|
|
|
int avc_bridgeco_get_plug_type(struct fw_unit *unit,
|
|
|
|
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
enum avc_bridgeco_plug_type *type);
|
|
|
|
int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
|
|
|
|
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
unsigned int id, u8 *type);
|
|
|
|
int avc_bridgeco_get_plug_input(struct fw_unit *unit,
|
|
|
|
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
|
|
|
|
u8 input[7]);
|
|
|
|
int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
|
|
|
|
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
|
|
|
|
unsigned int *len, unsigned int eid);
|
|
|
|
|
|
|
|
/* for AMDTP streaming */
|
|
|
|
int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
|
|
|
|
int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
|
2015-06-14 11:49:30 +08:00
|
|
|
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
|
|
|
|
enum snd_bebob_clock_type *src);
|
2014-04-25 21:45:15 +08:00
|
|
|
int snd_bebob_stream_discover(struct snd_bebob *bebob);
|
|
|
|
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
|
2014-05-27 23:14:40 +08:00
|
|
|
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
|
2014-04-25 21:45:15 +08:00
|
|
|
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
|
|
|
|
void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
|
|
|
|
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:20 +08:00
|
|
|
void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
|
|
|
|
int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
|
|
|
|
void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:17 +08:00
|
|
|
void snd_bebob_proc_init(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:18 +08:00
|
|
|
int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:19 +08:00
|
|
|
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:20 +08:00
|
|
|
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
|
|
|
|
|
2014-04-25 21:45:22 +08:00
|
|
|
/* model specific operations */
|
2015-10-11 14:10:55 +08:00
|
|
|
extern const struct snd_bebob_spec phase88_rack_spec;
|
|
|
|
extern const struct snd_bebob_spec phase24_series_spec;
|
|
|
|
extern const struct snd_bebob_spec yamaha_go_spec;
|
|
|
|
extern const struct snd_bebob_spec saffirepro_26_spec;
|
|
|
|
extern const struct snd_bebob_spec saffirepro_10_spec;
|
|
|
|
extern const struct snd_bebob_spec saffire_le_spec;
|
|
|
|
extern const struct snd_bebob_spec saffire_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_fw410_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_audiophile_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_solo_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_ozonic_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_nrv10_spec;
|
|
|
|
extern const struct snd_bebob_spec maudio_special_spec;
|
2014-04-25 21:45:26 +08:00
|
|
|
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
|
2014-04-25 21:45:29 +08:00
|
|
|
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
|
2014-04-25 21:45:22 +08:00
|
|
|
|
2014-04-25 21:45:21 +08:00
|
|
|
#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
|
2014-04-25 21:45:14 +08:00
|
|
|
{ \
|
|
|
|
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
|
|
|
IEEE1394_MATCH_MODEL_ID, \
|
|
|
|
.vendor_id = vendor, \
|
|
|
|
.model_id = model, \
|
2014-04-25 21:45:21 +08:00
|
|
|
.driver_data = (kernel_ulong_t)data \
|
2014-04-25 21:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|