Merge tag 'asoc-v4.3-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Mon, 26 Oct 2015 07:32:46 +0000 (08:32 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 26 Oct 2015 11:14:49 +0000 (12:14 +0100)
ASoC: Updates for v4.4

Not much core work here, a few small tweaks to interfaces but mainly the
changes here are driver ones.  Highlights include:

 - Updates to the topology userspace interface
 - Big updates to the Renesas support from Morimoto-san
 - Most of the support for Intel Sky Lake systems.
 - New drivers for Asahi Kasei Microdevices AK4613, Allwinnner A10,
   Cirrus Logic WM8998, Dialog DA7219, Nuvoton NAU8825 and Rockchip
   S/PDIF.
 - A new driver for the Atmel Class D speaker drivers

135 files changed:
Documentation/DocBook/writing-an-alsa-driver.tmpl
Documentation/sound/alsa/hda_codec.txt [deleted file]
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_audio.c
include/drm/i915_component.h
include/linux/mod_devicetable.h
include/sound/hda_regmap.h
include/sound/hdaudio.h
include/sound/hdaudio_ext.h
include/sound/pcm.h
include/uapi/sound/asound.h
include/uapi/sound/emu10k1.h
include/uapi/sound/firewire.h
include/uapi/sound/hdspm.h
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c
sound/core/Kconfig
sound/core/Makefile
sound/core/oss/mixer_oss.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/core/seq/oss/seq_oss_readq.c
sound/core/seq/oss/seq_oss_writeq.c
sound/firewire/Kconfig
sound/firewire/Makefile
sound/firewire/amdtp-am824.c [new file with mode: 0644]
sound/firewire/amdtp-am824.h [new file with mode: 0644]
sound/firewire/amdtp-stream.c [new file with mode: 0644]
sound/firewire/amdtp-stream.h [new file with mode: 0644]
sound/firewire/amdtp.c [deleted file]
sound/firewire/amdtp.h [deleted file]
sound/firewire/bebob/Makefile
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_focusrite.c
sound/firewire/bebob/bebob_maudio.c
sound/firewire/bebob/bebob_midi.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/bebob/bebob_proc.c
sound/firewire/bebob/bebob_stream.c
sound/firewire/bebob/bebob_terratec.c
sound/firewire/bebob/bebob_yamaha.c
sound/firewire/dice/Makefile
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h
sound/firewire/digi00x/Makefile [new file with mode: 0644]
sound/firewire/digi00x/amdtp-dot.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-hwdep.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-midi.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-pcm.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-proc.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-stream.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-transaction.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x.h [new file with mode: 0644]
sound/firewire/fcp.c
sound/firewire/fireworks/Makefile
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks.h
sound/firewire/fireworks/fireworks_command.c
sound/firewire/fireworks/fireworks_midi.c
sound/firewire/fireworks/fireworks_pcm.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/lib.c
sound/firewire/lib.h
sound/firewire/oxfw/Makefile
sound/firewire/oxfw/oxfw-midi.c
sound/firewire/oxfw/oxfw-pcm.c
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/firewire/tascam/Makefile [new file with mode: 0644]
sound/firewire/tascam/amdtp-tascam.c [new file with mode: 0644]
sound/firewire/tascam/tascam-hwdep.c [new file with mode: 0644]
sound/firewire/tascam/tascam-midi.c [new file with mode: 0644]
sound/firewire/tascam/tascam-pcm.c [new file with mode: 0644]
sound/firewire/tascam/tascam-proc.c [new file with mode: 0644]
sound/firewire/tascam/tascam-stream.c [new file with mode: 0644]
sound/firewire/tascam/tascam-transaction.c [new file with mode: 0644]
sound/firewire/tascam/tascam.c [new file with mode: 0644]
sound/firewire/tascam/tascam.h [new file with mode: 0644]
sound/hda/ext/hdac_ext_stream.c
sound/hda/hda_bus_type.c
sound/hda/hdac_device.c
sound/hda/hdac_sysfs.c
sound/pci/hda/hda_bind.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_sysfs.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/korg1212/korg1212.c
sound/pci/lx6464es/lx6464es.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/soc/codecs/adav80x.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/wl1273.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cht_bsw_rt5672.c
sound/soc/omap/n810.c
sound/soc/omap/rx51.c
sound/soc/soc-pcm.c
sound/soc/ux500/ux500_msp_dai.c
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/midi.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/stream.c
sound/usb/usbaudio.h

index 84ef6a90131c2ea44410f9f3c53f6b56a499bfe5..a27ab9f53fb68a62652bff8ed847322a838cd41d 100644 (file)
@@ -2181,10 +2181,6 @@ struct _snd_pcm_runtime {
        struct snd_pcm_hardware hw;
        struct snd_pcm_hw_constraints hw_constraints;
 
-       /* -- interrupt callbacks -- */
-       void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
-       void (*transfer_ack_end)(struct snd_pcm_substream *substream);
-
        /* -- timer -- */
        unsigned int timer_resolution;  /* timer resolution */
 
@@ -2209,9 +2205,8 @@ struct _snd_pcm_runtime {
          For the operators (callbacks) of each sound driver, most of
        these records are supposed to be read-only.  Only the PCM
        middle-layer changes / updates them.  The exceptions are
-       the hardware description (hw), interrupt callbacks
-       (transfer_ack_xxx), DMA buffer information, and the private
-       data.  Besides, if you use the standard buffer allocation
+       the hardware description (hw) DMA buffer information and the
+       private data.  Besides, if you use the standard buffer allocation
        method via <function>snd_pcm_lib_malloc_pages()</function>,
        you don't need to set the DMA buffer information by yourself.
        </para>
@@ -2538,16 +2533,6 @@ struct _snd_pcm_runtime {
         </para>
        </section>
 
-       <section id="pcm-interface-runtime-intr">
-       <title>Interrupt Callbacks</title>
-       <para>
-       The field <structfield>transfer_ack_begin</structfield> and
-       <structfield>transfer_ack_end</structfield> are called at
-       the beginning and at the end of
-       <function>snd_pcm_period_elapsed()</function>, respectively. 
-       </para>
-       </section>
-
     </section>
 
     <section id="pcm-interface-operators">
diff --git a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt
deleted file mode 100644 (file)
index de8efbc..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-Notes on Universal Interface for Intel High Definition Audio Codec
-------------------------------------------------------------------
-
-Takashi Iwai <tiwai@suse.de>
-
-
-[Still a draft version]
-
-
-General
-=======
-
-The snd-hda-codec module supports the generic access function for the
-High Definition (HD) audio codecs.  It's designed to be independent
-from the controller code like ac97 codec module.  The real accessors
-from/to the controller must be implemented in the lowlevel driver.
-
-The structure of this module is similar with ac97_codec module.
-Each codec chip belongs to a bus class which communicates with the
-controller.
-
-
-Initialization of Bus Instance
-==============================
-
-The card driver has to create struct hda_bus at first.  The template
-struct should be filled and passed to the constructor:
-
-struct hda_bus_template {
-       void *private_data;
-       struct pci_dev *pci;
-       const char *modelname;
-       struct hda_bus_ops ops;
-};
-
-The card driver can set and use the private_data field to retrieve its
-own data in callback functions.  The pci field is used when the patch
-needs to check the PCI subsystem IDs, so on.  For non-PCI system, it
-doesn't have to be set, of course.
-The modelname field specifies the board's specific configuration.  The
-string is passed to the codec parser, and it depends on the parser how
-the string is used.
-These fields, private_data, pci and modelname are all optional.
-
-The ops field contains the callback functions as the following:
-
-struct hda_bus_ops {
-       int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
-                      unsigned int verb, unsigned int parm);
-       unsigned int (*get_response)(struct hda_codec *codec);
-       void (*private_free)(struct hda_bus *);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       void (*pm_notify)(struct hda_codec *codec);
-#endif
-};
-
-The command callback is called when the codec module needs to send a
-VERB to the controller.  It's always a single command.
-The get_response callback is called when the codec requires the answer
-for the last command.  These two callbacks are mandatory and have to
-be given.
-The third, private_free callback, is optional.  It's called in the
-destructor to release any necessary data in the lowlevel driver.
-
-The pm_notify callback is available only with
-CONFIG_SND_HDA_POWER_SAVE kconfig.  It's called when the codec needs
-to power up or may power down.  The controller should check the all
-belonging codecs on the bus whether they are actually powered off
-(check codec->power_on), and optionally the driver may power down the
-controller side, too.
-
-The bus instance is created via snd_hda_bus_new().  You need to pass
-the card instance, the template, and the pointer to store the
-resultant bus instance.
-
-int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
-                   struct hda_bus **busp);
-
-It returns zero if successful.  A negative return value means any
-error during creation.
-
-
-Creation of Codec Instance
-==========================
-
-Each codec chip on the board is then created on the BUS instance.
-To create a codec instance, call snd_hda_codec_new().
-
-int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                     struct hda_codec **codecp);
-
-The first argument is the BUS instance, the second argument is the
-address of the codec, and the last one is the pointer to store the
-resultant codec instance (can be NULL if not needed).
-
-The codec is stored in a linked list of bus instance.  You can follow
-the codec list like:
-
-       struct hda_codec *codec;
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               ...
-       }
-
-The codec isn't initialized at this stage properly.  The
-initialization sequence is called when the controls are built later.
-
-
-Codec Access
-============
-
-To access codec, use snd_hda_codec_read() and snd_hda_codec_write().
-snd_hda_param_read() is for reading parameters.
-For writing a sequence of verbs, use snd_hda_sequence_write().
-
-There are variants of cached read/write, snd_hda_codec_write_cache(),
-snd_hda_sequence_write_cache().  These are used for recording the
-register states for the power-management resume.  When no PM is needed,
-these are equivalent with non-cached version.
-
-To retrieve the number of sub nodes connected to the given node, use
-snd_hda_get_sub_nodes().  The connection list can be obtained via
-snd_hda_get_connections() call.
-
-When an unsolicited event happens, pass the event via
-snd_hda_queue_unsol_event() so that the codec routines will process it
-later.
-
-
-(Mixer) Controls
-================
-
-To create mixer controls of all codecs, call
-snd_hda_build_controls().  It then builds the mixers and does
-initialization stuff on each codec.
-
-
-PCM Stuff
-=========
-
-snd_hda_build_pcms() gives the necessary information to create PCM
-streams.  When it's called, each codec belonging to the bus stores 
-codec->num_pcms and codec->pcm_info fields.  The num_pcms indicates
-the number of elements in pcm_info array.  The card driver is supposed
-to traverse the codec linked list, read the pcm information in
-pcm_info array, and build pcm instances according to them. 
-
-The pcm_info array contains the following record:
-
-/* PCM information for each substream */
-struct hda_pcm_stream {
-       unsigned int substreams;        /* number of substreams, 0 = not exist */
-       unsigned int channels_min;      /* min. number of channels */
-       unsigned int channels_max;      /* max. number of channels */
-       hda_nid_t nid;  /* default NID to query rates/formats/bps, or set up */
-       u32 rates;      /* supported rates */
-       u64 formats;    /* supported formats (SNDRV_PCM_FMTBIT_) */
-       unsigned int maxbps;    /* supported max. bit per sample */
-       struct hda_pcm_ops ops;
-};
-
-/* for PCM creation */
-struct hda_pcm {
-       char *name;
-       struct hda_pcm_stream stream[2];
-};
-
-The name can be passed to snd_pcm_new().  The stream field contains
-the information  for playback (SNDRV_PCM_STREAM_PLAYBACK = 0) and
-capture (SNDRV_PCM_STREAM_CAPTURE = 1) directions.  The card driver
-should pass substreams to snd_pcm_new() for the number of substreams
-to create.
-
-The channels_min, channels_max, rates and formats should be copied to
-runtime->hw record.  They and maxbps fields are used also to compute
-the format value for the HDA codec and controller.  Call
-snd_hda_calc_stream_format() to get the format value.
-
-The ops field contains the following callback functions:
-
-struct hda_pcm_ops {
-       int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                   struct snd_pcm_substream *substream);
-       int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                    struct snd_pcm_substream *substream);
-       int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                      unsigned int stream_tag, unsigned int format,
-                      struct snd_pcm_substream *substream);
-       int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                      struct snd_pcm_substream *substream);
-};
-
-All are non-NULL, so you can call them safely without NULL check.
-
-The open callback should be called in PCM open after runtime->hw is
-set up.  It may override some setting and constraints additionally.
-Similarly, the close callback should be called in the PCM close.
-
-The prepare callback should be called in PCM prepare.  This will set
-up the codec chip properly for the operation.  The cleanup should be
-called in hw_free to clean up the configuration.
-
-The caller should check the return value, at least for open and
-prepare callbacks.  When a negative value is returned, some error
-occurred.
-
-
-Proc Files
-==========
-
-Each codec dumps the widget node information in
-/proc/asound/card*/codec#* file.  This information would be really
-helpful for debugging.  Please provide its contents together with the
-bug report.
-
-
-Power Management
-================
-
-It's simple:
-Call snd_hda_suspend() in the PM suspend callback.
-Call snd_hda_resume() in the PM resume callback.
-
-
-Codec Preset (Patch)
-====================
-
-To set up and handle the codec functionality fully, each codec may
-have a codec preset (patch).  It's defined in struct hda_codec_preset:
-
-       struct hda_codec_preset {
-               unsigned int id;
-               unsigned int mask;
-               unsigned int subs;
-               unsigned int subs_mask;
-               unsigned int rev;
-               const char *name;
-               int (*patch)(struct hda_codec *codec);
-       };
-
-When the codec id and codec subsystem id match with the given id and
-subs fields bitwise (with bitmask mask and subs_mask), the callback
-patch is called.  The patch callback should initialize the codec and
-set the codec->patch_ops field.  This is defined as below:
-
-       struct hda_codec_ops {
-               int (*build_controls)(struct hda_codec *codec);
-               int (*build_pcms)(struct hda_codec *codec);
-               int (*init)(struct hda_codec *codec);
-               void (*free)(struct hda_codec *codec);
-               void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-       #ifdef CONFIG_PM
-               int (*suspend)(struct hda_codec *codec, pm_message_t state);
-               int (*resume)(struct hda_codec *codec);
-       #endif
-       #ifdef CONFIG_SND_HDA_POWER_SAVE
-               int (*check_power_status)(struct hda_codec *codec,
-                                         hda_nid_t nid);
-       #endif
-       };
-
-The build_controls callback is called from snd_hda_build_controls().
-Similarly, the build_pcms callback is called from
-snd_hda_build_pcms().  The init callback is called after
-build_controls to initialize the hardware.
-The free callback is called as a destructor.
-
-The unsol_event callback is called when an unsolicited event is
-received.
-
-The suspend and resume callbacks are for power management.
-They can be NULL if no special sequence is required.  When the resume
-callback is NULL, the driver calls the init callback and resumes the
-registers from the cache.  If other handling is needed, you'd need to
-write your own resume callback.  There, the amp values can be resumed
-via
-       void snd_hda_codec_resume_amp(struct hda_codec *codec);
-and the other codec registers via
-       void snd_hda_codec_resume_cache(struct hda_codec *codec);
-
-The check_power_status callback is called when the amp value of the
-given widget NID is changed.  The codec code can turn on/off the power
-appropriately from this information.
-
-Each entry can be NULL if not necessary to be called.
-
-
-Generic Parser
-==============
-
-When the device doesn't match with any given presets, the widgets are
-parsed via th generic parser (hda_generic.c).  Its support is
-limited: no multi-channel support, for example.
-
-
-Digital I/O
-===========
-
-Call snd_hda_create_spdif_out_ctls() from the patch to create controls
-related with SPDIF out.
-
-
-Helper Functions
-================
-
-snd_hda_get_codec_name() stores the codec name on the given string.
-
-snd_hda_check_board_config() can be used to obtain the configuration
-information matching with the device.  Define the model string table
-and the table with struct snd_pci_quirk entries (zero-terminated),
-and pass it to the function.  The function checks the modelname given
-as a module parameter, and PCI subsystem IDs.  If the matching entry
-is found, it returns the config field value.
-
-snd_hda_add_new_ctls() can be used to create and add control entries.
-Pass the zero-terminated array of struct snd_kcontrol_new
-
-Macros HDA_CODEC_VOLUME(), HDA_CODEC_MUTE() and their variables can be
-used for the entry of struct snd_kcontrol_new.
-
-The input MUX helper callbacks for such a control are provided, too:
-snd_hda_input_mux_info() and snd_hda_input_mux_put().  See
-patch_realtek.c for example.
index ab37d1121be8277728bff5d25a0cb4a4599de0aa..990f656e6ab051eda034dd2cf24045de7813cd36 100644 (file)
@@ -832,6 +832,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        mutex_init(&dev_priv->sb_lock);
        mutex_init(&dev_priv->modeset_restore_lock);
        mutex_init(&dev_priv->csr_lock);
+       mutex_init(&dev_priv->av_mutex);
 
        intel_pm_setup(dev);
 
index e1db8de52851b979c31607abf7b6995bd1febe0b..22dd7043c9efb428120406d2aa5f73d025dc97dd 100644 (file)
@@ -1885,6 +1885,11 @@ struct drm_i915_private {
        /* hda/i915 audio component */
        struct i915_audio_component *audio_component;
        bool audio_component_registered;
+       /**
+        * av_mutex - mutex for audio/video sync
+        *
+        */
+       struct mutex av_mutex;
 
        uint32_t hw_context_size;
        struct list_head context_list;
index 2a5c76faf9f8dbc19ec6d17c490ff833d61d6afd..ae8df0a43de68a154685d84001f29f31b66a463c 100644 (file)
@@ -68,6 +68,31 @@ static const struct {
        { 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
 };
 
+/* HDMI N/CTS table */
+#define TMDS_297M 297000
+#define TMDS_296M DIV_ROUND_UP(297000 * 1000, 1001)
+static const struct {
+       int sample_rate;
+       int clock;
+       int n;
+       int cts;
+} aud_ncts[] = {
+       { 44100, TMDS_296M, 4459, 234375 },
+       { 44100, TMDS_297M, 4704, 247500 },
+       { 48000, TMDS_296M, 5824, 281250 },
+       { 48000, TMDS_297M, 5120, 247500 },
+       { 32000, TMDS_296M, 5824, 421875 },
+       { 32000, TMDS_297M, 3072, 222750 },
+       { 88200, TMDS_296M, 8918, 234375 },
+       { 88200, TMDS_297M, 9408, 247500 },
+       { 96000, TMDS_296M, 11648, 281250 },
+       { 96000, TMDS_297M, 10240, 247500 },
+       { 176400, TMDS_296M, 17836, 234375 },
+       { 176400, TMDS_297M, 18816, 247500 },
+       { 192000, TMDS_296M, 23296, 281250 },
+       { 192000, TMDS_297M, 20480, 247500 },
+};
+
 /* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
 static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
 {
@@ -90,6 +115,45 @@ static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
        return hdmi_audio_clock[i].config;
 }
 
+static int audio_config_get_n(const struct drm_display_mode *mode, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aud_ncts); i++) {
+               if ((rate == aud_ncts[i].sample_rate) &&
+                       (mode->clock == aud_ncts[i].clock)) {
+                       return aud_ncts[i].n;
+               }
+       }
+       return 0;
+}
+
+static uint32_t audio_config_setup_n_reg(int n, uint32_t val)
+{
+       int n_low, n_up;
+       uint32_t tmp = val;
+
+       n_low = n & 0xfff;
+       n_up = (n >> 12) & 0xff;
+       tmp &= ~(AUD_CONFIG_UPPER_N_MASK | AUD_CONFIG_LOWER_N_MASK);
+       tmp |= ((n_up << AUD_CONFIG_UPPER_N_SHIFT) |
+                       (n_low << AUD_CONFIG_LOWER_N_SHIFT) |
+                       AUD_CONFIG_N_PROG_ENABLE);
+       return tmp;
+}
+
+/* check whether N/CTS/M need be set manually */
+static bool audio_rate_need_prog(struct intel_crtc *crtc,
+                                const struct drm_display_mode *mode)
+{
+       if (((mode->clock == TMDS_297M) ||
+                (mode->clock == TMDS_296M)) &&
+               intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI))
+               return true;
+       else
+               return false;
+}
+
 static bool intel_eld_uptodate(struct drm_connector *connector,
                               int reg_eldv, uint32_t bits_eldv,
                               int reg_elda, uint32_t bits_elda,
@@ -184,6 +248,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
 
        DRM_DEBUG_KMS("Disable audio codec on pipe %c\n", pipe_name(pipe));
 
+       mutex_lock(&dev_priv->av_mutex);
+
        /* Disable timestamps */
        tmp = I915_READ(HSW_AUD_CFG(pipe));
        tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
@@ -199,6 +265,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
        tmp &= ~AUDIO_ELD_VALID(pipe);
        tmp &= ~AUDIO_OUTPUT_ENABLE(pipe);
        I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
 }
 
 static void hsw_audio_codec_enable(struct drm_connector *connector,
@@ -208,13 +276,20 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        enum pipe pipe = intel_crtc->pipe;
+       struct i915_audio_component *acomp = dev_priv->audio_component;
        const uint8_t *eld = connector->eld;
+       struct intel_digital_port *intel_dig_port =
+               enc_to_dig_port(&encoder->base);
+       enum port port = intel_dig_port->port;
        uint32_t tmp;
        int len, i;
+       int n, rate;
 
        DRM_DEBUG_KMS("Enable audio codec on pipe %c, %u bytes ELD\n",
                      pipe_name(pipe), drm_eld_size(eld));
 
+       mutex_lock(&dev_priv->av_mutex);
+
        /* Enable audio presence detect, invalidate ELD */
        tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
        tmp |= AUDIO_OUTPUT_ENABLE(pipe);
@@ -246,13 +321,32 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
        /* Enable timestamps */
        tmp = I915_READ(HSW_AUD_CFG(pipe));
        tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
-       tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
        tmp &= ~AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK;
        if (intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DISPLAYPORT))
                tmp |= AUD_CONFIG_N_VALUE_INDEX;
        else
                tmp |= audio_config_hdmi_pixel_clock(mode);
+
+       tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+       if (audio_rate_need_prog(intel_crtc, mode)) {
+               if (!acomp)
+                       rate = 0;
+               else if (port >= PORT_A && port <= PORT_E)
+                       rate = acomp->aud_sample_rate[port];
+               else {
+                       DRM_ERROR("invalid port: %d\n", port);
+                       rate = 0;
+               }
+               n = audio_config_get_n(mode, rate);
+               if (n != 0)
+                       tmp = audio_config_setup_n_reg(n, tmp);
+               else
+                       DRM_DEBUG_KMS("no suitable N value is found\n");
+       }
+
        I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
 }
 
 static void ilk_audio_codec_disable(struct intel_encoder *encoder)
@@ -527,12 +621,91 @@ static int i915_audio_component_get_cdclk_freq(struct device *dev)
        return ret;
 }
 
+static int i915_audio_component_sync_audio_rate(struct device *dev,
+                                               int port, int rate)
+{
+       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_device *drm_dev = dev_priv->dev;
+       struct intel_encoder *intel_encoder;
+       struct intel_digital_port *intel_dig_port;
+       struct intel_crtc *crtc;
+       struct drm_display_mode *mode;
+       struct i915_audio_component *acomp = dev_priv->audio_component;
+       enum pipe pipe = -1;
+       u32 tmp;
+       int n;
+
+       /* HSW, BDW SKL need this fix */
+       if (!IS_SKYLAKE(dev_priv) &&
+               !IS_BROADWELL(dev_priv) &&
+               !IS_HASWELL(dev_priv))
+               return 0;
+
+       mutex_lock(&dev_priv->av_mutex);
+       /* 1. get the pipe */
+       for_each_intel_encoder(drm_dev, intel_encoder) {
+               if (intel_encoder->type != INTEL_OUTPUT_HDMI)
+                       continue;
+               intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+               if (port == intel_dig_port->port) {
+                       crtc = to_intel_crtc(intel_encoder->base.crtc);
+                       if (!crtc) {
+                               DRM_DEBUG_KMS("%s: crtc is NULL\n", __func__);
+                               continue;
+                       }
+                       pipe = crtc->pipe;
+                       break;
+               }
+       }
+
+       if (pipe == INVALID_PIPE) {
+               DRM_DEBUG_KMS("no pipe for the port %c\n", port_name(port));
+               mutex_unlock(&dev_priv->av_mutex);
+               return -ENODEV;
+       }
+       DRM_DEBUG_KMS("pipe %c connects port %c\n",
+                                 pipe_name(pipe), port_name(port));
+       mode = &crtc->config->base.adjusted_mode;
+
+       /* port must be valid now, otherwise the pipe will be invalid */
+       acomp->aud_sample_rate[port] = rate;
+
+       /* 2. check whether to set the N/CTS/M manually or not */
+       if (!audio_rate_need_prog(crtc, mode)) {
+               tmp = I915_READ(HSW_AUD_CFG(pipe));
+               tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+               I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+               mutex_unlock(&dev_priv->av_mutex);
+               return 0;
+       }
+
+       n = audio_config_get_n(mode, rate);
+       if (n == 0) {
+               DRM_DEBUG_KMS("Using automatic mode for N value on port %c\n",
+                                         port_name(port));
+               tmp = I915_READ(HSW_AUD_CFG(pipe));
+               tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+               I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+               mutex_unlock(&dev_priv->av_mutex);
+               return 0;
+       }
+
+       /* 3. set the N/CTS/M */
+       tmp = I915_READ(HSW_AUD_CFG(pipe));
+       tmp = audio_config_setup_n_reg(n, tmp);
+       I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
+       return 0;
+}
+
 static const struct i915_audio_component_ops i915_audio_component_ops = {
        .owner          = THIS_MODULE,
        .get_power      = i915_audio_component_get_power,
        .put_power      = i915_audio_component_put_power,
        .codec_wake_override = i915_audio_component_codec_wake_override,
        .get_cdclk_freq = i915_audio_component_get_cdclk_freq,
+       .sync_audio_rate = i915_audio_component_sync_audio_rate,
 };
 
 static int i915_audio_component_bind(struct device *i915_dev,
@@ -540,6 +713,7 @@ static int i915_audio_component_bind(struct device *i915_dev,
 {
        struct i915_audio_component *acomp = data;
        struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+       int i;
 
        if (WARN_ON(acomp->ops || acomp->dev))
                return -EEXIST;
@@ -547,6 +721,9 @@ static int i915_audio_component_bind(struct device *i915_dev,
        drm_modeset_lock_all(dev_priv->dev);
        acomp->ops = &i915_audio_component_ops;
        acomp->dev = i915_dev;
+       BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS);
+       for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++)
+               acomp->aud_sample_rate[i] = 0;
        dev_priv->audio_component = acomp;
        drm_modeset_unlock_all(dev_priv->dev);
 
index b2d56dd483d9c8ad197b6a59071c2dd2378652da..89dc7d6bc1ccb94a8a65f06a45a1d35e18d8562c 100644 (file)
 #ifndef _I915_COMPONENT_H_
 #define _I915_COMPONENT_H_
 
+/* MAX_PORT is the number of port
+ * It must be sync with I915_MAX_PORTS defined i915_drv.h
+ * 5 should be enough as only HSW, BDW, SKL need such fix.
+ */
+#define MAX_PORTS 5
+
 struct i915_audio_component {
        struct device *dev;
+       /**
+        * @aud_sample_rate: the array of audio sample rate per port
+        */
+       int aud_sample_rate[MAX_PORTS];
 
        const struct i915_audio_component_ops {
                struct module *owner;
@@ -33,6 +43,13 @@ struct i915_audio_component {
                void (*put_power)(struct device *);
                void (*codec_wake_override)(struct device *, bool enable);
                int (*get_cdclk_freq)(struct device *);
+               /**
+                * @sync_audio_rate: set n/cts based on the sample rate
+                *
+                * Called from audio driver. After audio driver sets the
+                * sample rate, it will call this function to set n/cts
+                */
+               int (*sync_audio_rate)(struct device *, int port, int rate);
        } *ops;
 
        const struct i915_audio_component_audio_ops {
index 688997a24aadde7e131e7027a1036eb24e48a9e4..00825672d256eb26b6a29a40214b5fb37b910be3 100644 (file)
@@ -219,6 +219,14 @@ struct serio_device_id {
        __u8 proto;
 };
 
+struct hda_device_id {
+       __u32 vendor_id;
+       __u32 rev_id;
+       __u8 api_version;
+       const char *name;
+       unsigned long driver_data;
+};
+
 /*
  * Struct used for matching a device
  */
index df705908480aebbf754900731834162fa8097f75..2767c55a641edd64b7018a0d7693826ae60dbad9 100644 (file)
@@ -67,7 +67,7 @@ int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
  * @reg: verb to write
  * @val: value to write
  *
- * For writing an amp value, use snd_hda_regmap_amp_update().
+ * For writing an amp value, use snd_hdac_regmap_update_amp().
  */
 static inline int
 snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
@@ -85,7 +85,7 @@ snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
  * @mask: bit mask to update
  * @val: value to update
  *
- * For updating an amp value, use snd_hda_regmap_amp_update().
+ * For updating an amp value, use snd_hdac_regmap_update_amp().
  */
 static inline int
 snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid,
index 49bc836fcd8483d522c48dc604fc3ba0d1bf153e..e2b712c90d3f22d4bcbb75ded3a296304d23d7c6 100644 (file)
@@ -21,22 +21,13 @@ struct hdac_stream;
 struct hdac_device;
 struct hdac_driver;
 struct hdac_widget_tree;
+struct hda_device_id;
 
 /*
  * exported bus type
  */
 extern struct bus_type snd_hda_bus_type;
 
-/*
- * HDA device table
- */
-struct hda_device_id {
-       __u32 vendor_id;
-       __u32 rev_id;
-       const char *name;
-       unsigned long driver_data;
-};
-
 /*
  * generic arrays
  */
@@ -117,6 +108,8 @@ int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus,
 void snd_hdac_device_exit(struct hdac_device *dev);
 int snd_hdac_device_register(struct hdac_device *codec);
 void snd_hdac_device_unregister(struct hdac_device *codec);
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name);
+int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
 
 int snd_hdac_refresh_widgets(struct hdac_device *codec);
 int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec);
@@ -147,6 +140,12 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
 bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
                                  unsigned int format);
 
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm);
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm);
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+               hda_nid_t nid, unsigned int target_state);
 /**
  * snd_hdac_read_parm - read a codec parameter
  * @codec: the codec object
index 94210dcdb6eacf5329fa8250f42a470a84d600ff..a4cadd9c297a810e1bb548d2cb02e4ab92b5eea2 100644 (file)
@@ -40,6 +40,13 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus);
 #define hbus_to_ebus(_bus) \
        container_of(_bus, struct hdac_ext_bus, bus)
 
+#define HDA_CODEC_REV_EXT_ENTRY(_vid, _rev, _name, drv_data) \
+       { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+         .api_version = HDA_DEV_ASOC, \
+         .driver_data = (unsigned long)(drv_data) }
+#define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
+       HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data)
+
 int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus);
 void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable);
 void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable);
index 691e7ee0a510ae6354ccb4d311823a687d47ee33..4e6ad74fd8a2f7a4b38787e181e53b0e6456bf96 100644 (file)
@@ -285,8 +285,6 @@ struct snd_pcm_hw_constraint_ranges {
        unsigned int mask;
 };
 
-struct snd_pcm_hwptr_log;
-
 /*
  * userspace-provided audio timestamp config to kernel,
  * structure is for internal use only and filled with dedicated unpack routine
@@ -404,10 +402,6 @@ struct snd_pcm_runtime {
        struct snd_pcm_hardware hw;
        struct snd_pcm_hw_constraints hw_constraints;
 
-       /* -- interrupt callbacks -- */
-       void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
-       void (*transfer_ack_end)(struct snd_pcm_substream *substream);
-
        /* -- timer -- */
        unsigned int timer_resolution;  /* timer resolution */
        int tstamp_type;                /* timestamp type */
@@ -428,10 +422,6 @@ struct snd_pcm_runtime {
        /* -- OSS things -- */
        struct snd_pcm_oss_runtime oss;
 #endif
-
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       struct snd_pcm_hwptr_log *hwptr_log;
-#endif
 };
 
 struct snd_pcm_group {         /* keep linked substreams */
@@ -1034,6 +1024,22 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime,
                        snd_pcm_hw_rule_func_t func, void *private,
                        int dep, ...);
 
+/**
+ * snd_pcm_hw_constraint_single() - Constrain parameter to a single value
+ * @runtime: PCM runtime instance
+ * @var: The hw_params variable to constrain
+ * @val: The value to constrain to
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
+ */
+static inline int snd_pcm_hw_constraint_single(
+       struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
+       unsigned int val)
+{
+       return snd_pcm_hw_constraint_minmax(runtime, var, val, val);
+}
+
 int snd_pcm_format_signed(snd_pcm_format_t format);
 int snd_pcm_format_unsigned(snd_pcm_format_t format);
 int snd_pcm_format_linear(snd_pcm_format_t format);
@@ -1117,10 +1123,16 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea
  *  Timer interface
  */
 
+#ifdef CONFIG_SND_PCM_TIMER
 void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
 void snd_pcm_timer_init(struct snd_pcm_substream *substream);
 void snd_pcm_timer_done(struct snd_pcm_substream *substream);
-
+#else
+static inline void
+snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
+#endif
 /**
  * snd_pcm_gettime - Fill the timespec depending on the timestamp mode
  * @runtime: PCM runtime instance
index a45be6bdcf5bb88b2871a764e070ed9737818088..a82108e5d1c0ccfb627daf84ac80b153fe123f66 100644 (file)
@@ -100,9 +100,11 @@ enum {
        SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
        SNDRV_HWDEP_IFACE_FW_BEBOB,     /* BridgeCo BeBoB based device */
        SNDRV_HWDEP_IFACE_FW_OXFW,      /* Oxford OXFW970/971 based device */
+       SNDRV_HWDEP_IFACE_FW_DIGI00X,   /* Digidesign Digi 002/003 family */
+       SNDRV_HWDEP_IFACE_FW_TASCAM,    /* TASCAM FireWire series */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
 };
 
 struct snd_hwdep_info {
index ec1535bb6aedd043df4d5b4950805d8fc2aeaa05..5175e166987d173fb88ed1279614d89602ac22dc 100644 (file)
 
 #define EMU10K1_FX8010_PCM_COUNT               8
 
+/*
+ * Following definition is copied from linux/types.h to support compiling
+ * this header file in userspace since they are not generally available for
+ * uapi headers.
+ */
+#define __EMU10K1_DECLARE_BITMAP(name,bits) \
+       unsigned long name[(bits) / (sizeof(unsigned long) * 8)]
+
 /* instruction set */
 #define iMAC0   0x00   /* R = A + (X * Y >> 31)   ; saturation */
 #define iMAC1   0x01   /* R = A + (-X * Y >> 31)  ; saturation */
@@ -300,7 +308,7 @@ struct snd_emu10k1_fx8010_control_old_gpr {
 struct snd_emu10k1_fx8010_code {
        char name[128];
 
-       DECLARE_BITMAP(gpr_valid, 0x200); /* bitmask of valid initializers */
+       __EMU10K1_DECLARE_BITMAP(gpr_valid, 0x200); /* bitmask of valid initializers */
        __u32 __user *gpr_map;          /* initializers */
 
        unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */
@@ -313,11 +321,11 @@ struct snd_emu10k1_fx8010_code {
        unsigned int gpr_list_control_total; /* total count of GPR controls */
        struct snd_emu10k1_fx8010_control_gpr __user *gpr_list_controls; /* listed GPR controls */
 
-       DECLARE_BITMAP(tram_valid, 0x100); /* bitmask of valid initializers */
+       __EMU10K1_DECLARE_BITMAP(tram_valid, 0x100); /* bitmask of valid initializers */
        __u32 __user *tram_data_map;      /* data initializers */
        __u32 __user *tram_addr_map;      /* map initializers */
 
-       DECLARE_BITMAP(code_valid, 1024); /* bitmask of valid instructions */
+       __EMU10K1_DECLARE_BITMAP(code_valid, 1024); /* bitmask of valid instructions */
        __u32 __user *code;               /* one instruction - 64 bits */
 };
 
index 49122df3b56b31efcd7e37b739d67ccb13f02b00..db79a12fcc78db70d234b89d4b8bf6f573598224 100644 (file)
@@ -9,6 +9,7 @@
 #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS       0x000010cc
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE      0x4e617475
+#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE   0x746e736c
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response {
        __be32 response[0];     /* some responses */
 };
 
+struct snd_firewire_event_digi00x_message {
+       unsigned int type;
+       __u32 message;  /* Digi00x-specific message */
+};
+
 union snd_firewire_event {
        struct snd_firewire_event_common            common;
        struct snd_firewire_event_lock_status       lock_status;
        struct snd_firewire_event_dice_notification dice_notification;
        struct snd_firewire_event_efw_response      efw_response;
+       struct snd_firewire_event_digi00x_message   digi00x_message;
 };
 
 
@@ -56,6 +63,8 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_TYPE_FIREWORKS  2
 #define SNDRV_FIREWIRE_TYPE_BEBOB      3
 #define SNDRV_FIREWIRE_TYPE_OXFW       4
+#define SNDRV_FIREWIRE_TYPE_DIGI00X    5
+#define SNDRV_FIREWIRE_TYPE_TASCAM     6
 /* RME, MOTU, ... */
 
 struct snd_firewire_get_info {
index 5737332d38f2cea1f8c2f47f14e9d01282c87a52..c4db6f5b306ecb951cfa7cda58ab44add749147b 100644 (file)
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#ifdef __KERNEL__
 #include <linux/types.h>
-#else
-#include <stdint.h>
-#endif
 
 /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
 #define HDSPM_MAX_CHANNELS      64
@@ -46,15 +42,15 @@ enum hdspm_speed {
 /* -------------------- IOCTL Peak/RMS Meters -------------------- */
 
 struct hdspm_peak_rms {
-       uint32_t input_peaks[64];
-       uint32_t playback_peaks[64];
-       uint32_t output_peaks[64];
+       __u32 input_peaks[64];
+       __u32 playback_peaks[64];
+       __u32 output_peaks[64];
 
-       uint64_t input_rms[64];
-       uint64_t playback_rms[64];
-       uint64_t output_rms[64];
+       __u64 input_rms[64];
+       __u64 playback_rms[64];
+       __u64 output_rms[64];
 
-       uint8_t speed; /* enum {ss, ds, qs} */
+       __u8 speed; /* enum {ss, ds, qs} */
        int status2;
 };
 
@@ -155,21 +151,21 @@ enum hdspm_syncsource {
 };
 
 struct hdspm_status {
-       uint8_t card_type; /* enum hdspm_io_type */
+       __u8 card_type; /* enum hdspm_io_type */
        enum hdspm_syncsource autosync_source;
 
-       uint64_t card_clock;
-       uint32_t master_period;
+       __u64 card_clock;
+       __u32 master_period;
 
        union {
                struct {
-                       uint8_t sync_wc; /* enum hdspm_sync */
-                       uint8_t sync_madi; /* enum hdspm_sync */
-                       uint8_t sync_tco; /* enum hdspm_sync */
-                       uint8_t sync_in; /* enum hdspm_sync */
-                       uint8_t madi_input; /* enum hdspm_madi_input */
-                       uint8_t channel_format; /* enum hdspm_madi_channel_format */
-                       uint8_t frame_format; /* enum hdspm_madi_frame_format */
+                       __u8 sync_wc; /* enum hdspm_sync */
+                       __u8 sync_madi; /* enum hdspm_sync */
+                       __u8 sync_tco; /* enum hdspm_sync */
+                       __u8 sync_in; /* enum hdspm_sync */
+                       __u8 madi_input; /* enum hdspm_madi_input */
+                       __u8 channel_format; /* enum hdspm_madi_channel_format */
+                       __u8 frame_format; /* enum hdspm_madi_frame_format */
                } madi;
        } card_specific;
 };
@@ -184,7 +180,7 @@ struct hdspm_status {
 #define HDSPM_ADDON_TCO 1
 
 struct hdspm_version {
-       uint8_t card_type; /* enum hdspm_io_type */
+       __u8 card_type; /* enum hdspm_io_type */
        char cardname[20];
        unsigned int serial;
        unsigned short firmware_rev;
index e70fcd12eeebe813bafd7e56f014bcf5b4e33647..e1a5110bd63b03cdd19ae4324a51291adc738651 100644 (file)
@@ -196,5 +196,10 @@ int main(void)
        DEVID_FIELD(ulpi_device_id, vendor);
        DEVID_FIELD(ulpi_device_id, product);
 
+       DEVID(hda_device_id);
+       DEVID_FIELD(hda_device_id, vendor_id);
+       DEVID_FIELD(hda_device_id, rev_id);
+       DEVID_FIELD(hda_device_id, api_version);
+
        return 0;
 }
index 5f208820913259651246d71a3def44f49fa8da44..fc51d4bff3f879fdea3a7861e8a3f82dbf982c44 100644 (file)
@@ -1250,6 +1250,23 @@ static int do_ulpi_entry(const char *filename, void *symval,
 }
 ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry);
 
+/* Looks like: hdaudio:vNrNaN */
+static int do_hda_entry(const char *filename, void *symval, char *alias)
+{
+       DEF_FIELD(symval, hda_device_id, vendor_id);
+       DEF_FIELD(symval, hda_device_id, rev_id);
+       DEF_FIELD(symval, hda_device_id, api_version);
+
+       strcpy(alias, "hdaudio:");
+       ADD(alias, "v", vendor_id != 0, vendor_id);
+       ADD(alias, "r", rev_id != 0, rev_id);
+       ADD(alias, "a", api_version != 0, api_version);
+
+       add_wildcard(alias);
+       return 1;
+}
+ADD_TO_DEVTABLE("hdaudio", hda_device_id, do_hda_entry);
+
 /* Does namelen bytes of name exactly match the symbol? */
 static bool sym_is(const char *name, unsigned namelen, const char *symbol)
 {
index 6c96feeaf01eb08794be6321fa0ac35aba66b3e8..e3e949126a5639c6a4a623750ec35922704c5110 100644 (file)
@@ -4,7 +4,7 @@ config SND_TIMER
 
 config SND_PCM
        tristate
-       select SND_TIMER
+       select SND_TIMER if SND_PCM_TIMER
 
 config SND_PCM_ELD
        bool
@@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
           support conversion of channels, formats and rates. It will
           behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
 
+config SND_PCM_TIMER
+       bool "PCM timer interface" if EXPERT
+       default y
+       help
+         If you disable this option, pcm timer will be inavailable, so
+         those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
+         incorrectlly.
+
+         For some embedded device, we may disable it to reduce memory
+         footprint, about 20KB on x86_64 platform.
+
 config SND_SEQUENCER_OSS
        bool "OSS Sequencer API"
        depends on SND_SEQUENCER
index 3354f91e003ad13e100887c9bf9050c5ae51252a..48ab4b8f82790809838c3a87535587636ea69ed7 100644 (file)
@@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
 snd-$(CONFIG_SND_VMASTER) += vmaster.o
 snd-$(CONFIG_SND_JACK)   += ctljack.o jack.o
 
-snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
                pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
 snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
 snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
index a99f7200ff3f2929dd17bc85a64c296460346e02..7a8c79dd9734039d2e14ae1a7f23763e7d6fc519 100644 (file)
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
        struct snd_mixer_oss *mixer = entry->private_data;
        char line[128], str[32], idxstr[16];
        const char *cptr;
-       int ch, idx;
+       unsigned int idx;
+       int ch;
        struct snd_mixer_oss_assign_table *tbl;
        struct slot *slot;
 
index 02bd96954dc42c05266ef9d04a3d77918cf9cf6c..308c9ecf73db18415aa1157ee4e895731a5abc36 100644 (file)
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        snd_free_pages((void*)runtime->control,
                       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
        kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       kfree(runtime->hwptr_log);
-#endif
        kfree(runtime);
        substream->runtime = NULL;
        put_pid(substream->pid);
index 7d45645f10ba99e33b54b3218559778db3aca36a..05a3ca93c6474b3fa120f20c25403439bb469d1c 100644 (file)
@@ -1875,20 +1875,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
                return;
        runtime = substream->runtime;
 
-       if (runtime->transfer_ack_begin)
-               runtime->transfer_ack_begin(substream);
-
        snd_pcm_stream_lock_irqsave(substream, flags);
        if (!snd_pcm_running(substream) ||
            snd_pcm_update_hw_ptr0(substream, 1) < 0)
                goto _end;
 
+#ifdef CONFIG_SND_PCM_TIMER
        if (substream->timer_running)
                snd_timer_interrupt(substream->timer, 1);
+#endif
  _end:
        snd_pcm_stream_unlock_irqrestore(substream, flags);
-       if (runtime->transfer_ack_end)
-               runtime->transfer_ack_end(substream);
        kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
 }
 
index 75888dd38a7fc87acf5fcd7789570ad353dce482..a8b27cdc2844855fb2d030a246bf2374e7e27269 100644 (file)
@@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
        snd_pcm_stream_unlock_irq(substream);
 }
 
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+                                       int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+       if (substream->timer)
+               snd_timer_notify(substream->timer, event,
+                                       &substream->runtime->trigger_tstamp);
+#endif
+}
+
 static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -650,7 +660,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        }
        snd_pcm_stream_unlock_irq(substream);
 
-       if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+       if (params->tstamp_mode < 0 ||
+           params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
                return -EINVAL;
        if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
            params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, ULONG_MAX);
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
 }
 
 static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
        if (runtime->status->state != state) {
                snd_pcm_trigger_tstamp(substream);
                runtime->status->state = state;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
        }
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
@@ -1208,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
        snd_pcm_trigger_tstamp(substream);
        if (push) {
                runtime->status->state = SNDRV_PCM_STATE_PAUSED;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MPAUSE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
                wake_up(&runtime->sleep);
                wake_up(&runtime->tsleep);
        } else {
                runtime->status->state = SNDRV_PCM_STATE_RUNNING;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MCONTINUE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
        }
 }
 
@@ -1267,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
        snd_pcm_trigger_tstamp(substream);
        runtime->status->suspended_state = runtime->status->state;
        runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
 }
@@ -1373,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_trigger_tstamp(substream);
        runtime->status->state = runtime->status->suspended_state;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
 }
 
 static struct action_ops snd_pcm_action_resume = {
@@ -2226,7 +2223,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
 
        snd_pcm_drop(substream);
        if (substream->hw_opened) {
-               if (substream->ops->hw_free != NULL)
+               if (substream->ops->hw_free &&
+                   substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
                        substream->ops->hw_free(substream);
                substream->ops->close(substream);
                substream->hw_opened = 0;
index ccd893566f1decbe80d21d350c7041d97c969988..046cb586fb2f7271c88dc1e02354625d6dcca864 100644 (file)
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
                q->head = q->tail = 0;
        }
        /* if someone sleeping, wake'em up */
-       if (waitqueue_active(&q->midi_sleep))
-               wake_up(&q->midi_sleep);
+       wake_up(&q->midi_sleep);
        q->input_time = (unsigned long)-1;
 }
 
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
        q->qlen++;
 
        /* wake up sleeper */
-       if (waitqueue_active(&q->midi_sleep))
-               wake_up(&q->midi_sleep);
+       wake_up(&q->midi_sleep);
 
        spin_unlock_irqrestore(&q->lock, flags);
 
index d50338bbc21f1dbc2637ae9b2938716679b919c9..1f6788a18444111c724bc417741c2a398da89900 100644 (file)
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
        spin_lock_irqsave(&q->sync_lock, flags);
        q->sync_time = time;
        q->sync_event_put = 0;
-       if (waitqueue_active(&q->sync_sleep)) {
-               wake_up(&q->sync_sleep);
-       }
+       wake_up(&q->sync_sleep);
        spin_unlock_irqrestore(&q->sync_lock, flags);
 }
 
index 8850b7de1d380585a784e6f72a5dccc5812b5ad4..bee0e5f1a1166d18e443f8294754f01bf55101ca 100644 (file)
@@ -120,4 +120,31 @@ config SND_BEBOB
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
 
+config SND_FIREWIRE_DIGI00X
+       tristate "Digidesign Digi 002/003 family support"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+       help
+        Say Y here to include support for Digidesign Digi 002/003 family.
+         * Digi 002 Console
+         * Digi 002 Rack
+         * Digi 003 Console
+         * Digi 003 Rack
+         * Digi 003 Rack+
+
+        To compile this driver as a module, choose M here: the module
+        will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+       tristate "TASCAM FireWire series support"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+       help
+        Say Y here to include support for TASCAM.
+         * FW-1884
+         * FW-1082
+
+        To compile this driver as a module, choose M here: the module
+        will be called snd-firewire-tascam.
+
 endif # SND_FIREWIRE
index 8b37f084b2aba800fb4c82a34089b4957609b2d2..f5fb62551c600cd02664244108470f8613a512ef 100644 (file)
@@ -1,6 +1,5 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
-                        fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
+                        fcp.o cmp.o amdtp-stream.o amdtp-am824.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o
 
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
 obj-$(CONFIG_SND_FIREWORKS) += fireworks/
 obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644 (file)
index 0000000..bebddc6
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM             0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824                0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND  3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS     8
+
+struct amdtp_am824 {
+       struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+       int midi_fifo_limit;
+       int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+       unsigned int pcm_channels;
+       unsigned int midi_ports;
+
+       u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+       u8 midi_position;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+
+       unsigned int frame_multiplier;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ *                as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                              unsigned int pcm_channels,
+                              unsigned int midi_ports,
+                              bool double_pcm_frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int midi_channels;
+       unsigned int i;
+       int err;
+
+       if (amdtp_stream_running(s))
+               return -EINVAL;
+
+       if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+               return -EINVAL;
+
+       midi_channels = DIV_ROUND_UP(midi_ports, 8);
+       if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+               return -EINVAL;
+
+       if (WARN_ON(amdtp_stream_running(s)) ||
+           WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+           WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+               return -EINVAL;
+
+       err = amdtp_stream_set_parameters(s, rate,
+                                         pcm_channels + midi_channels);
+       if (err < 0)
+               return err;
+
+       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+       p->pcm_channels = pcm_channels;
+       p->midi_ports = midi_ports;
+
+       /*
+        * In IEC 61883-6, one data block represents one event. In ALSA, one
+        * event equals to one PCM frame. But Dice has a quirk at higher
+        * sampling rate to transfer two PCM frames in one data block.
+        */
+       if (double_pcm_frames)
+               p->frame_multiplier = 2;
+       else
+               p->frame_multiplier = 1;
+
+       /* init the position map for PCM and MIDI channels */
+       for (i = 0; i < pcm_channels; i++)
+               p->pcm_positions[i] = i;
+       p->midi_position = p->pcm_channels;
+
+       /*
+        * We do not know the actual MIDI FIFO size of most devices.  Just
+        * assume two bytes, i.e., one byte can be received over the bus while
+        * the previous one is transmitted over MIDI.
+        * (The value here is adjusted for midi_ratelimit_per_packet().)
+        */
+       p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ *                               of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+                                unsigned int position)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (index < p->pcm_channels)
+               p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ *                                conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+                                  unsigned int position)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[p->pcm_positions[c]] =
+                                       cpu_to_be32((*src >> 8) | 0x40000000);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[p->pcm_positions[c]] =
+                                       cpu_to_be32((*src << 8) | 0x42000000);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+                        struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+                             __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int i, c, channels = p->pcm_channels;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
+       }
+}
+
+/**
+ * amdtp_am824_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set after the other parameters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
+ */
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s:         the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime:   the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                      struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       if (err < 0)
+               return err;
+
+       /* AM824 in IEC 61883-6 can deliver 24bit data. */
+       return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data.  This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                             struct snd_rawmidi_substream *midi)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (port < p->midi_ports)
+               ACCESS_ONCE(p->midi[port]) = midi;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled.  This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate.  One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate.  To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_am824 *p = s->protocol;
+       int used;
+
+       used = p->midi_fifo_used[port];
+       if (used == 0) /* common shortcut */
+               return true;
+
+       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+       used = max(used, 0);
+       p->midi_fifo_used[port] = used;
+
+       return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                               unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int f, port;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               b = (u8 *)&buffer[p->midi_position];
+
+               port = (s->data_block_counter + f) % 8;
+               if (f < MAX_MIDI_RX_BLOCKS &&
+                   midi_ratelimit_per_packet(s, port) &&
+                   p->midi[port] != NULL &&
+                   snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+                       midi_rate_use_one_byte(s, port);
+                       b[0] = 0x81;
+               } else {
+                       b[0] = 0x80;
+                       b[1] = 0;
+               }
+               b[2] = 0;
+               b[3] = 0;
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void read_midi_messages(struct amdtp_stream *s,
+                              __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int f, port;
+       int len;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               port = (s->data_block_counter + f) % 8;
+               b = (u8 *)&buffer[p->midi_position];
+
+               len = b[0] - 0x80;
+               if ((1 <= len) &&  (len <= 3) && (p->midi[port]))
+                       snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+                                          unsigned int data_blocks, unsigned int *syt)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+       unsigned int pcm_frames;
+
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks * p->frame_multiplier;
+       } else {
+               write_pcm_silence(s, buffer, data_blocks);
+               pcm_frames = 0;
+       }
+
+       if (p->midi_ports)
+               write_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+                                          unsigned int data_blocks, unsigned int *syt)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+       unsigned int pcm_frames;
+
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks * p->frame_multiplier;
+       } else {
+               pcm_frames = 0;
+       }
+
+       if (p->midi_ports)
+               read_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ *                   data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+                    enum amdtp_stream_direction dir, enum cip_flags flags)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+
+       if (dir == AMDTP_IN_STREAM)
+               process_data_blocks = process_tx_data_blocks;
+       else
+               process_data_blocks = process_rx_data_blocks;
+
+       return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+                                process_data_blocks,
+                                sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644 (file)
index 0000000..73b07b3
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS       SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
+                                        SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM     64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI    1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                              unsigned int pcm_channels,
+                              unsigned int midi_ports,
+                              bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+                                unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+                                  unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                      struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
+                               snd_pcm_format_t format);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                             struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+                    enum amdtp_stream_direction dir, enum cip_flags flags);
+#endif
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
new file mode 100644 (file)
index 0000000..ed29026
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
+ * with Common Isochronous Packet (IEC 61883-1) headers
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp-stream.h"
+
+#define TICKS_PER_CYCLE                3072
+#define CYCLES_PER_SECOND      8000
+#define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 microseconds */
+
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT  16
+#define TAG_CIP                        1
+
+/* common isochronous packet header parameters */
+#define CIP_EOH_SHIFT          31
+#define CIP_EOH                        (1u << CIP_EOH_SHIFT)
+#define CIP_EOH_MASK           0x80000000
+#define CIP_SID_SHIFT          24
+#define CIP_SID_MASK           0x3f000000
+#define CIP_DBS_MASK           0x00ff0000
+#define CIP_DBS_SHIFT          16
+#define CIP_DBC_MASK           0x000000ff
+#define CIP_FMT_SHIFT          24
+#define CIP_FMT_MASK           0x3f000000
+#define CIP_FDF_MASK           0x00ff0000
+#define CIP_FDF_SHIFT          16
+#define CIP_SYT_MASK           0x0000ffff
+#define CIP_SYT_NO_INFO                0xffff
+
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM             0x10
+#define AMDTP_FDF_NO_DATA      0xff
+
+/* TODO: make these configurable */
+#define INTERRUPT_INTERVAL     16
+#define QUEUE_LENGTH           48
+
+#define IN_PACKET_HEADER_SIZE  4
+#define OUT_PACKET_HEADER_SIZE 0
+
+static void pcm_period_tasklet(unsigned long data);
+
+/**
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ * @fmt: the value of fmt field in CIP header
+ * @process_data_blocks: callback handler to process data blocks
+ * @protocol_size: the size to allocate newly for protocol
+ */
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir, enum cip_flags flags,
+                     unsigned int fmt,
+                     amdtp_stream_process_data_blocks_t process_data_blocks,
+                     unsigned int protocol_size)
+{
+       if (process_data_blocks == NULL)
+               return -EINVAL;
+
+       s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+       if (!s->protocol)
+               return -ENOMEM;
+
+       s->unit = unit;
+       s->direction = dir;
+       s->flags = flags;
+       s->context = ERR_PTR(-1);
+       mutex_init(&s->mutex);
+       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
+       s->packet_index = 0;
+
+       init_waitqueue_head(&s->callback_wait);
+       s->callbacked = false;
+       s->sync_slave = NULL;
+
+       s->fmt = fmt;
+       s->process_data_blocks = process_data_blocks;
+
+       return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_init);
+
+/**
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
+ */
+void amdtp_stream_destroy(struct amdtp_stream *s)
+{
+       WARN_ON(amdtp_stream_running(s));
+       kfree(s->protocol);
+       mutex_destroy(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_destroy);
+
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  8,
+       [CIP_SFC_44100]  =  8,
+       [CIP_SFC_48000]  =  8,
+       [CIP_SFC_88200]  = 16,
+       [CIP_SFC_96000]  = 16,
+       [CIP_SFC_176400] = 32,
+       [CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  32000,
+       [CIP_SFC_44100]  =  44100,
+       [CIP_SFC_48000]  =  48000,
+       [CIP_SFC_88200]  =  88200,
+       [CIP_SFC_96000]  =  96000,
+       [CIP_SFC_176400] = 176400,
+       [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s:         the AMDTP stream, which must be initialized.
+ * @runtime:   the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /*
+        * Currently firewire-lib processes 16 packets in one software
+        * interrupt callback. This equals to 2msec but actually the
+        * interval of the interrupts has a jitter.
+        * Additionally, even if adding a constraint to fit period size to
+        * 2msec, actual calculated frames per period doesn't equal to 2msec,
+        * depending on sampling rate.
+        * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
+        * Here let us use 5msec for safe period interrupt.
+        */
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          5000, UINT_MAX);
+       if (err < 0)
+               goto end;
+
+       /* Non-Blocking stream has no more constraints */
+       if (!(s->flags & CIP_BLOCKING))
+               goto end;
+
+       /*
+        * One AMDTP packet can include some frames. In blocking mode, the
+        * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+        * depending on its sampling rate. For accurate period interrupt, it's
+        * preferrable to align period/buffer sizes to current SYT_INTERVAL.
+        *
+        * TODO: These constraints can be improved with proper rules.
+        * Currently apply LCM of SYT_INTERVALs.
+        */
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+       if (err < 0)
+               goto end;
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+end:
+       return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
+/**
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @data_block_quadlets: the size of a data block in quadlet unit
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                               unsigned int data_block_quadlets)
+{
+       unsigned int sfc;
+
+       for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
+               if (amdtp_rate_table[sfc] == rate)
+                       break;
+       }
+       if (sfc == ARRAY_SIZE(amdtp_rate_table))
+               return -EINVAL;
+
+       s->sfc = sfc;
+       s->data_block_quadlets = data_block_quadlets;
+       s->syt_interval = amdtp_syt_intervals[sfc];
+
+       /* default buffering in the device */
+       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+       if (s->flags & CIP_BLOCKING)
+               /* additional buffering needed to adjust for no-data packets */
+               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+       return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
+
+/**
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
+ *
+ * This function must not be called before the stream has been configured
+ * with amdtp_stream_set_parameters().
+ */
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
+{
+       unsigned int multiplier = 1;
+
+       if (s->flags & CIP_JUMBO_PAYLOAD)
+               multiplier = 5;
+
+       return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+}
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
+
+/**
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
+{
+       tasklet_kill(&s->period_tasklet);
+       s->pcm_buffer_pointer = 0;
+       s->pcm_period_pointer = 0;
+       s->pointer_flush = true;
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
+
+static unsigned int calculate_data_blocks(struct amdtp_stream *s,
+                                         unsigned int syt)
+{
+       unsigned int phase, data_blocks;
+
+       /* Blocking mode. */
+       if (s->flags & CIP_BLOCKING) {
+               /* This module generate empty packet for 'no data'. */
+               if (syt == CIP_SYT_NO_INFO)
+                       data_blocks = 0;
+               else
+                       data_blocks = s->syt_interval;
+       /* Non-blocking mode. */
+       } else {
+               if (!cip_sfc_is_base_44100(s->sfc)) {
+                       /* Sample_rate / 8000 is an integer, and precomputed. */
+                       data_blocks = s->data_block_state;
+               } else {
+                       phase = s->data_block_state;
+
+               /*
+                * This calculates the number of data blocks per packet so that
+                * 1) the overall rate is correct and exactly synchronized to
+                *    the bus clock, and
+                * 2) packets with a rounded-up number of blocks occur as early
+                *    as possible in the sequence (to prevent underruns of the
+                *    device's buffer).
+                */
+                       if (s->sfc == CIP_SFC_44100)
+                               /* 6 6 5 6 5 6 5 ... */
+                               data_blocks = 5 + ((phase & 1) ^
+                                                  (phase == 0 || phase >= 40));
+                       else
+                               /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+                               data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
+                       if (++phase >= (80 >> (s->sfc >> 1)))
+                               phase = 0;
+                       s->data_block_state = phase;
+               }
+       }
+
+       return data_blocks;
+}
+
+static unsigned int calculate_syt(struct amdtp_stream *s,
+                                 unsigned int cycle)
+{
+       unsigned int syt_offset, phase, index, syt;
+
+       if (s->last_syt_offset < TICKS_PER_CYCLE) {
+               if (!cip_sfc_is_base_44100(s->sfc))
+                       syt_offset = s->last_syt_offset + s->syt_offset_state;
+               else {
+               /*
+                * The time, in ticks, of the n'th SYT_INTERVAL sample is:
+                *   n * SYT_INTERVAL * 24576000 / sample_rate
+                * Modulo TICKS_PER_CYCLE, the difference between successive
+                * elements is about 1386.23.  Rounding the results of this
+                * formula to the SYT precision results in a sequence of
+                * differences that begins with:
+                *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
+                * This code generates _exactly_ the same sequence.
+                */
+                       phase = s->syt_offset_state;
+                       index = phase % 13;
+                       syt_offset = s->last_syt_offset;
+                       syt_offset += 1386 + ((index && !(index & 3)) ||
+                                             phase == 146);
+                       if (++phase >= 147)
+                               phase = 0;
+                       s->syt_offset_state = phase;
+               }
+       } else
+               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
+       s->last_syt_offset = syt_offset;
+
+       if (syt_offset < TICKS_PER_CYCLE) {
+               syt_offset += s->transfer_delay;
+               syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
+               syt += syt_offset % TICKS_PER_CYCLE;
+
+               return syt & CIP_SYT_MASK;
+       } else {
+               return CIP_SYT_NO_INFO;
+       }
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+                               struct snd_pcm_substream *pcm,
+                               unsigned int frames)
+{
+       unsigned int ptr;
+
+       ptr = s->pcm_buffer_pointer + frames;
+       if (ptr >= pcm->runtime->buffer_size)
+               ptr -= pcm->runtime->buffer_size;
+       ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+       s->pcm_period_pointer += frames;
+       if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+               s->pcm_period_pointer -= pcm->runtime->period_size;
+               s->pointer_flush = false;
+               tasklet_hi_schedule(&s->period_tasklet);
+       }
+}
+
+static void pcm_period_tasklet(unsigned long data)
+{
+       struct amdtp_stream *s = (void *)data;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+       if (pcm)
+               snd_pcm_period_elapsed(pcm);
+}
+
+static int queue_packet(struct amdtp_stream *s,
+                       unsigned int header_length,
+                       unsigned int payload_length, bool skip)
+{
+       struct fw_iso_packet p = {0};
+       int err = 0;
+
+       if (IS_ERR(s->context))
+               goto end;
+
+       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+       p.tag = TAG_CIP;
+       p.header_length = header_length;
+       p.payload_length = (!skip) ? payload_length : 0;
+       p.skip = skip;
+       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+                                  s->buffer.packets[s->packet_index].offset);
+       if (err < 0) {
+               dev_err(&s->unit->device, "queueing error: %d\n", err);
+               goto end;
+       }
+
+       if (++s->packet_index >= QUEUE_LENGTH)
+               s->packet_index = 0;
+end:
+       return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+                                  unsigned int payload_length, bool skip)
+{
+       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
+                           payload_length, skip);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+       return queue_packet(s, IN_PACKET_HEADER_SIZE,
+                           amdtp_stream_get_max_payload(s), false);
+}
+
+static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
+                            unsigned int syt)
+{
+       __be32 *buffer;
+       unsigned int payload_length;
+       unsigned int pcm_frames;
+       struct snd_pcm_substream *pcm;
+
+       buffer = s->buffer.packets[s->packet_index].buffer;
+       pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+
+       buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
+                               (s->data_block_quadlets << CIP_DBS_SHIFT) |
+                               s->data_block_counter);
+       buffer[1] = cpu_to_be32(CIP_EOH |
+                               ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+                               ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+                               (syt & CIP_SYT_MASK));
+
+       s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+       payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+       if (queue_out_packet(s, payload_length, false) < 0)
+               return -EIO;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm && pcm_frames > 0)
+               update_pcm_pointers(s, pcm, pcm_frames);
+
+       /* No need to return the number of handled data blocks. */
+       return 0;
+}
+
+static int handle_in_packet(struct amdtp_stream *s,
+                           unsigned int payload_quadlets, __be32 *buffer,
+                           unsigned int *data_blocks, unsigned int syt)
+{
+       u32 cip_header[2];
+       unsigned int fmt, fdf;
+       unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+       bool lost;
+
+       cip_header[0] = be32_to_cpu(buffer[0]);
+       cip_header[1] = be32_to_cpu(buffer[1]);
+
+       /*
+        * This module supports 'Two-quadlet CIP header with SYT field'.
+        * For convenience, also check FMT field is AM824 or not.
+        */
+       if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+           ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+               dev_info_ratelimited(&s->unit->device,
+                               "Invalid CIP header for AMDTP: %08X:%08X\n",
+                               cip_header[0], cip_header[1]);
+               *data_blocks = 0;
+               pcm_frames = 0;
+               goto end;
+       }
+
+       /* Check valid protocol or not. */
+       fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+       if (fmt != s->fmt) {
+               dev_info_ratelimited(&s->unit->device,
+                                    "Detect unexpected protocol: %08x %08x\n",
+                                    cip_header[0], cip_header[1]);
+               *data_blocks = 0;
+               pcm_frames = 0;
+               goto end;
+       }
+
+       /* Calculate data blocks */
+       fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
+       if (payload_quadlets < 3 ||
+           (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+               *data_blocks = 0;
+       } else {
+               data_block_quadlets =
+                       (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+               /* avoid division by zero */
+               if (data_block_quadlets == 0) {
+                       dev_err(&s->unit->device,
+                               "Detect invalid value in dbs field: %08X\n",
+                               cip_header[0]);
+                       return -EPROTO;
+               }
+               if (s->flags & CIP_WRONG_DBS)
+                       data_block_quadlets = s->data_block_quadlets;
+
+               *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+       }
+
+       /* Check data block counter continuity */
+       data_block_counter = cip_header[0] & CIP_DBC_MASK;
+       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+           s->data_block_counter != UINT_MAX)
+               data_block_counter = s->data_block_counter;
+
+       if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
+            data_block_counter == s->tx_first_dbc) ||
+           s->data_block_counter == UINT_MAX) {
+               lost = false;
+       } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+               lost = data_block_counter != s->data_block_counter;
+       } else {
+               if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
+                       dbc_interval = s->tx_dbc_interval;
+               else
+                       dbc_interval = *data_blocks;
+
+               lost = data_block_counter !=
+                      ((s->data_block_counter + dbc_interval) & 0xff);
+       }
+
+       if (lost) {
+               dev_err(&s->unit->device,
+                       "Detect discontinuity of CIP: %02X %02X\n",
+                       s->data_block_counter, data_block_counter);
+               return -EIO;
+       }
+
+       pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
+
+       if (s->flags & CIP_DBC_IS_END_EVENT)
+               s->data_block_counter = data_block_counter;
+       else
+               s->data_block_counter =
+                               (data_block_counter + *data_blocks) & 0xff;
+end:
+       if (queue_in_packet(s) < 0)
+               return -EIO;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm && pcm_frames > 0)
+               update_pcm_pointers(s, pcm, pcm_frames);
+
+       return 0;
+}
+
+static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+                               size_t header_length, void *header,
+                               void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+       unsigned int i, syt, packets = header_length / 4;
+       unsigned int data_blocks;
+
+       if (s->packet_index < 0)
+               return;
+
+       /*
+        * Compute the cycle of the last queued packet.
+        * (We need only the four lowest bits for the SYT, so we can ignore
+        * that bits 0-11 must wrap around at 3072.)
+        */
+       cycle += QUEUE_LENGTH - packets;
+
+       for (i = 0; i < packets; ++i) {
+               syt = calculate_syt(s, ++cycle);
+               data_blocks = calculate_data_blocks(s, syt);
+
+               if (handle_out_packet(s, data_blocks, syt) < 0) {
+                       s->packet_index = -1;
+                       amdtp_stream_pcm_abort(s);
+                       return;
+               }
+       }
+
+       fw_iso_context_queue_flush(s->context);
+}
+
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+                              size_t header_length, void *header,
+                              void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+       unsigned int p, syt, packets;
+       unsigned int payload_quadlets, max_payload_quadlets;
+       unsigned int data_blocks;
+       __be32 *buffer, *headers = header;
+
+       if (s->packet_index < 0)
+               return;
+
+       /* The number of packets in buffer */
+       packets = header_length / IN_PACKET_HEADER_SIZE;
+
+       /* For buffer-over-run prevention. */
+       max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+
+       for (p = 0; p < packets; p++) {
+               buffer = s->buffer.packets[s->packet_index].buffer;
+
+               /* The number of quadlets in this packet */
+               payload_quadlets =
+                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+               if (payload_quadlets > max_payload_quadlets) {
+                       dev_err(&s->unit->device,
+                               "Detect jumbo payload: %02x %02x\n",
+                               payload_quadlets, max_payload_quadlets);
+                       s->packet_index = -1;
+                       break;
+               }
+
+               syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+               if (handle_in_packet(s, payload_quadlets, buffer,
+                                               &data_blocks, syt) < 0) {
+                       s->packet_index = -1;
+                       break;
+               }
+
+               /* Process sync slave stream */
+               if (s->sync_slave && s->sync_slave->callbacked) {
+                       if (handle_out_packet(s->sync_slave,
+                                             data_blocks, syt) < 0) {
+                               s->packet_index = -1;
+                               break;
+                       }
+               }
+       }
+
+       /* Queueing error or detecting discontinuity */
+       if (s->packet_index < 0) {
+               amdtp_stream_pcm_abort(s);
+
+               /* Abort sync slave. */
+               if (s->sync_slave) {
+                       s->sync_slave->packet_index = -1;
+                       amdtp_stream_pcm_abort(s->sync_slave);
+               }
+               return;
+       }
+
+       /* when sync to device, flush the packets for slave stream */
+       if (s->sync_slave && s->sync_slave->callbacked)
+               fw_iso_context_queue_flush(s->sync_slave->context);
+
+       fw_iso_context_queue_flush(s->context);
+}
+
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+                                 size_t header_length, void *header,
+                                 void *private_data)
+{
+       return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+                                       u32 cycle, size_t header_length,
+                                       void *header, void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+
+       /*
+        * For in-stream, first packet has come.
+        * For out-stream, prepared to transmit first packet
+        */
+       s->callbacked = true;
+       wake_up(&s->callback_wait);
+
+       if (s->direction == AMDTP_IN_STREAM)
+               context->callback.sc = in_stream_callback;
+       else if (s->flags & CIP_SYNC_TO_DEVICE)
+               context->callback.sc = slave_stream_callback;
+       else
+               context->callback.sc = out_stream_callback;
+
+       context->callback.sc(context, cycle, header_length, header, s);
+}
+
+/**
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
+ * @channel: the isochronous channel on the bus
+ * @speed: firewire speed code
+ *
+ * The stream cannot be started until it has been configured with
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
+ */
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
+{
+       static const struct {
+               unsigned int data_block;
+               unsigned int syt_offset;
+       } initial_state[] = {
+               [CIP_SFC_32000]  = {  4, 3072 },
+               [CIP_SFC_48000]  = {  6, 1024 },
+               [CIP_SFC_96000]  = { 12, 1024 },
+               [CIP_SFC_192000] = { 24, 1024 },
+               [CIP_SFC_44100]  = {  0,   67 },
+               [CIP_SFC_88200]  = {  0,   67 },
+               [CIP_SFC_176400] = {  0,   67 },
+       };
+       unsigned int header_size;
+       enum dma_data_direction dir;
+       int type, tag, err;
+
+       mutex_lock(&s->mutex);
+
+       if (WARN_ON(amdtp_stream_running(s) ||
+                   (s->data_block_quadlets < 1))) {
+               err = -EBADFD;
+               goto err_unlock;
+       }
+
+       if (s->direction == AMDTP_IN_STREAM &&
+           s->flags & CIP_SKIP_INIT_DBC_CHECK)
+               s->data_block_counter = UINT_MAX;
+       else
+               s->data_block_counter = 0;
+       s->data_block_state = initial_state[s->sfc].data_block;
+       s->syt_offset_state = initial_state[s->sfc].syt_offset;
+       s->last_syt_offset = TICKS_PER_CYCLE;
+
+       /* initialize packet buffer */
+       if (s->direction == AMDTP_IN_STREAM) {
+               dir = DMA_FROM_DEVICE;
+               type = FW_ISO_CONTEXT_RECEIVE;
+               header_size = IN_PACKET_HEADER_SIZE;
+       } else {
+               dir = DMA_TO_DEVICE;
+               type = FW_ISO_CONTEXT_TRANSMIT;
+               header_size = OUT_PACKET_HEADER_SIZE;
+       }
+       err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
+                                     amdtp_stream_get_max_payload(s), dir);
+       if (err < 0)
+               goto err_unlock;
+
+       s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+                                          type, channel, speed, header_size,
+                                          amdtp_stream_first_callback, s);
+       if (IS_ERR(s->context)) {
+               err = PTR_ERR(s->context);
+               if (err == -EBUSY)
+                       dev_err(&s->unit->device,
+                               "no free stream on this controller\n");
+               goto err_buffer;
+       }
+
+       amdtp_stream_update(s);
+
+       s->packet_index = 0;
+       do {
+               if (s->direction == AMDTP_IN_STREAM)
+                       err = queue_in_packet(s);
+               else
+                       err = queue_out_packet(s, 0, true);
+               if (err < 0)
+                       goto err_context;
+       } while (s->packet_index > 0);
+
+       /* NOTE: TAG1 matches CIP. This just affects in stream. */
+       tag = FW_ISO_CONTEXT_MATCH_TAG1;
+       if (s->flags & CIP_EMPTY_WITH_TAG0)
+               tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+       s->callbacked = false;
+       err = fw_iso_context_start(s->context, -1, 0, tag);
+       if (err < 0)
+               goto err_context;
+
+       mutex_unlock(&s->mutex);
+
+       return 0;
+
+err_context:
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+err_buffer:
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+err_unlock:
+       mutex_unlock(&s->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(amdtp_stream_start);
+
+/**
+ * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
+{
+       /* this optimization is allowed to be racy */
+       if (s->pointer_flush && amdtp_stream_running(s))
+               fw_iso_context_flush_completions(s->context);
+       else
+               s->pointer_flush = true;
+
+       return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
+
+/**
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
+ */
+void amdtp_stream_update(struct amdtp_stream *s)
+{
+       /* Precomputing. */
+       ACCESS_ONCE(s->source_node_id_field) =
+               (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
+                                                               CIP_SID_MASK;
+}
+EXPORT_SYMBOL(amdtp_stream_update);
+
+/**
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
+ *
+ * All PCM and MIDI devices of the stream must be stopped before the stream
+ * itself can be stopped.
+ */
+void amdtp_stream_stop(struct amdtp_stream *s)
+{
+       mutex_lock(&s->mutex);
+
+       if (!amdtp_stream_running(s)) {
+               mutex_unlock(&s->mutex);
+               return;
+       }
+
+       tasklet_kill(&s->period_tasklet);
+       fw_iso_context_stop(s->context);
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+
+       s->callbacked = false;
+
+       mutex_unlock(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_stop);
+
+/**
+ * amdtp_stream_pcm_abort - abort the running PCM device
+ * @s: the AMDTP stream about to be stopped
+ *
+ * If the isochronous stream needs to be stopped asynchronously, call this
+ * function first to stop the PCM device.
+ */
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
+{
+       struct snd_pcm_substream *pcm;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm)
+               snd_pcm_stop_xrun(pcm);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
new file mode 100644 (file)
index 0000000..8775704
--- /dev/null
@@ -0,0 +1,258 @@
+#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <sound/asound.h>
+#include "packets-buffer.h"
+
+/**
+ * enum cip_flags - describes details of the streaming protocol
+ * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
+ *     sample_rate/8000 samples, with rounding up or down to adjust
+ *     for clock skew and left-over fractional samples.  This should
+ *     be used if supported by the device.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ *     SYT_INTERVAL samples, with these two types alternating so that
+ *     the overall sample rate comes out right.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ *     generated by in packets. Defaultly this driver generates timestamp.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
+ *     corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ *     The value of data_block_quadlets is used instead of reported value.
+ * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
+ *     skipped for detecting discontinuity.
+ * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
+ *     packet is not continuous from an initial value.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ *     packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ *     packet is larger than IEC 61883-6 defines. Current implementation
+ *     allows 5 times as large as IEC 61883-6 defines.
+ */
+enum cip_flags {
+       CIP_NONBLOCKING         = 0x00,
+       CIP_BLOCKING            = 0x01,
+       CIP_SYNC_TO_DEVICE      = 0x02,
+       CIP_EMPTY_WITH_TAG0     = 0x04,
+       CIP_DBC_IS_END_EVENT    = 0x08,
+       CIP_WRONG_DBS           = 0x10,
+       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
+       CIP_SKIP_INIT_DBC_CHECK = 0x40,
+       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
+       CIP_JUMBO_PAYLOAD       = 0x100,
+};
+
+/**
+ * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
+ * @CIP_SFC_32000:   32,000 data blocks
+ * @CIP_SFC_44100:   44,100 data blocks
+ * @CIP_SFC_48000:   48,000 data blocks
+ * @CIP_SFC_88200:   88,200 data blocks
+ * @CIP_SFC_96000:   96,000 data blocks
+ * @CIP_SFC_176400: 176,400 data blocks
+ * @CIP_SFC_192000: 192,000 data blocks
+ * @CIP_SFC_COUNT: the number of supported SFCs
+ *
+ * These values are used to show nominal Sampling Frequency Code in
+ * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
+ * this code means the number of events per second. Actually the code
+ * represents the number of data blocks transferred per second in an AMDTP
+ * stream.
+ *
+ * In IEC 61883-6:2005, some extensions were added to support more types of
+ * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
+ * different depending on the types.
+ *
+ * Currently our implementation is compatible with IEC 61883-6:2002.
+ */
+enum cip_sfc {
+       CIP_SFC_32000  = 0,
+       CIP_SFC_44100  = 1,
+       CIP_SFC_48000  = 2,
+       CIP_SFC_88200  = 3,
+       CIP_SFC_96000  = 4,
+       CIP_SFC_176400 = 5,
+       CIP_SFC_192000 = 6,
+       CIP_SFC_COUNT
+};
+
+struct fw_unit;
+struct fw_iso_context;
+struct snd_pcm_substream;
+struct snd_pcm_runtime;
+
+enum amdtp_stream_direction {
+       AMDTP_OUT_STREAM = 0,
+       AMDTP_IN_STREAM
+};
+
+struct amdtp_stream;
+typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
+                                               struct amdtp_stream *s,
+                                               __be32 *buffer,
+                                               unsigned int data_blocks,
+                                               unsigned int *syt);
+struct amdtp_stream {
+       struct fw_unit *unit;
+       enum cip_flags flags;
+       enum amdtp_stream_direction direction;
+       struct mutex mutex;
+
+       /* For packet processing. */
+       struct fw_iso_context *context;
+       struct iso_packets_buffer buffer;
+       int packet_index;
+
+       /* For CIP headers. */
+       unsigned int source_node_id_field;
+       unsigned int data_block_quadlets;
+       unsigned int data_block_counter;
+       unsigned int fmt;
+       unsigned int fdf;
+       /* quirk: fixed interval of dbc between previos/current packets. */
+       unsigned int tx_dbc_interval;
+       /* quirk: indicate the value of dbc field in a first packet. */
+       unsigned int tx_first_dbc;
+
+       /* Internal flags. */
+       enum cip_sfc sfc;
+       unsigned int syt_interval;
+       unsigned int transfer_delay;
+       unsigned int data_block_state;
+       unsigned int last_syt_offset;
+       unsigned int syt_offset_state;
+
+       /* For a PCM substream processing. */
+       struct snd_pcm_substream *pcm;
+       struct tasklet_struct period_tasklet;
+       unsigned int pcm_buffer_pointer;
+       unsigned int pcm_period_pointer;
+       bool pointer_flush;
+
+       /* To wait for first packet. */
+       bool callbacked;
+       wait_queue_head_t callback_wait;
+       struct amdtp_stream *sync_slave;
+
+       /* For backends to process data blocks. */
+       void *protocol;
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+};
+
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir, enum cip_flags flags,
+                     unsigned int fmt,
+                     amdtp_stream_process_data_blocks_t process_data_blocks,
+                     unsigned int protocol_size);
+void amdtp_stream_destroy(struct amdtp_stream *s);
+
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                               unsigned int data_block_quadlets);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
+
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
+void amdtp_stream_update(struct amdtp_stream *s);
+void amdtp_stream_stop(struct amdtp_stream *s);
+
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime);
+
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
+
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
+
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
+{
+       return !IS_ERR(s->context);
+}
+
+/**
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
+{
+       return s->packet_index < 0;
+}
+
+/**
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+       return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
+ * @pcm: the PCM device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of PCM data.  This function should be called from the PCM
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+                                           struct snd_pcm_substream *pcm)
+{
+       ACCESS_ONCE(s->pcm) = pcm;
+}
+
+static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
+{
+       return sfc & 1;
+}
+
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+                                        struct amdtp_stream *master,
+                                        struct amdtp_stream *slave)
+{
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master->flags |= CIP_SYNC_TO_DEVICE;
+               slave->flags |= CIP_SYNC_TO_DEVICE;
+               master->sync_slave = slave;
+       } else {
+               master->flags &= ~CIP_SYNC_TO_DEVICE;
+               slave->flags &= ~CIP_SYNC_TO_DEVICE;
+               master->sync_slave = NULL;
+       }
+
+       slave->sync_slave = NULL;
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+                                             unsigned int timeout)
+{
+       return wait_event_timeout(s->callback_wait,
+                                 s->callbacked == true,
+                                 msecs_to_jiffies(timeout)) > 0;
+}
+
+#endif
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
deleted file mode 100644 (file)
index 2a153d2..0000000
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
- * with Common Isochronous Packet (IEC 61883-1) headers
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/firewire.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/rawmidi.h>
-#include "amdtp.h"
-
-#define TICKS_PER_CYCLE                3072
-#define CYCLES_PER_SECOND      8000
-#define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
-
-/*
- * Nominally 3125 bytes/second, but the MIDI port's clock might be
- * 1% too slow, and the bus clock 100 ppm too fast.
- */
-#define MIDI_BYTES_PER_SECOND  3093
-
-/*
- * Several devices look only at the first eight data blocks.
- * In any case, this is more than enough for the MIDI data rate.
- */
-#define MAX_MIDI_RX_BLOCKS     8
-
-#define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 microseconds */
-
-/* isochronous header parameters */
-#define ISO_DATA_LENGTH_SHIFT  16
-#define TAG_CIP                        1
-
-/* common isochronous packet header parameters */
-#define CIP_EOH_SHIFT          31
-#define CIP_EOH                        (1u << CIP_EOH_SHIFT)
-#define CIP_EOH_MASK           0x80000000
-#define CIP_SID_SHIFT          24
-#define CIP_SID_MASK           0x3f000000
-#define CIP_DBS_MASK           0x00ff0000
-#define CIP_DBS_SHIFT          16
-#define CIP_DBC_MASK           0x000000ff
-#define CIP_FMT_SHIFT          24
-#define CIP_FMT_MASK           0x3f000000
-#define CIP_FDF_MASK           0x00ff0000
-#define CIP_FDF_SHIFT          16
-#define CIP_SYT_MASK           0x0000ffff
-#define CIP_SYT_NO_INFO                0xffff
-
-/*
- * Audio and Music transfer protocol specific parameters
- * only "Clock-based rate control mode" is supported
- */
-#define CIP_FMT_AM             (0x10 << CIP_FMT_SHIFT)
-#define AMDTP_FDF_AM824                (0 << (CIP_FDF_SHIFT + 3))
-#define AMDTP_FDF_NO_DATA      0xff
-
-/* TODO: make these configurable */
-#define INTERRUPT_INTERVAL     16
-#define QUEUE_LENGTH           48
-
-#define IN_PACKET_HEADER_SIZE  4
-#define OUT_PACKET_HEADER_SIZE 0
-
-static void pcm_period_tasklet(unsigned long data);
-
-/**
- * amdtp_stream_init - initialize an AMDTP stream structure
- * @s: the AMDTP stream to initialize
- * @unit: the target of the stream
- * @dir: the direction of stream
- * @flags: the packet transmission method to use
- */
-int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
-                     enum amdtp_stream_direction dir, enum cip_flags flags)
-{
-       s->unit = unit;
-       s->direction = dir;
-       s->flags = flags;
-       s->context = ERR_PTR(-1);
-       mutex_init(&s->mutex);
-       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
-       s->packet_index = 0;
-
-       init_waitqueue_head(&s->callback_wait);
-       s->callbacked = false;
-       s->sync_slave = NULL;
-
-       return 0;
-}
-EXPORT_SYMBOL(amdtp_stream_init);
-
-/**
- * amdtp_stream_destroy - free stream resources
- * @s: the AMDTP stream to destroy
- */
-void amdtp_stream_destroy(struct amdtp_stream *s)
-{
-       WARN_ON(amdtp_stream_running(s));
-       mutex_destroy(&s->mutex);
-}
-EXPORT_SYMBOL(amdtp_stream_destroy);
-
-const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
-       [CIP_SFC_32000]  =  8,
-       [CIP_SFC_44100]  =  8,
-       [CIP_SFC_48000]  =  8,
-       [CIP_SFC_88200]  = 16,
-       [CIP_SFC_96000]  = 16,
-       [CIP_SFC_176400] = 32,
-       [CIP_SFC_192000] = 32,
-};
-EXPORT_SYMBOL(amdtp_syt_intervals);
-
-const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
-       [CIP_SFC_32000]  =  32000,
-       [CIP_SFC_44100]  =  44100,
-       [CIP_SFC_48000]  =  48000,
-       [CIP_SFC_88200]  =  88200,
-       [CIP_SFC_96000]  =  96000,
-       [CIP_SFC_176400] = 176400,
-       [CIP_SFC_192000] = 192000,
-};
-EXPORT_SYMBOL(amdtp_rate_table);
-
-/**
- * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
- * @s:         the AMDTP stream, which must be initialized.
- * @runtime:   the PCM substream runtime
- */
-int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
-                                       struct snd_pcm_runtime *runtime)
-{
-       int err;
-
-       /* AM824 in IEC 61883-6 can deliver 24bit data */
-       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
-       if (err < 0)
-               goto end;
-
-       /*
-        * Currently firewire-lib processes 16 packets in one software
-        * interrupt callback. This equals to 2msec but actually the
-        * interval of the interrupts has a jitter.
-        * Additionally, even if adding a constraint to fit period size to
-        * 2msec, actual calculated frames per period doesn't equal to 2msec,
-        * depending on sampling rate.
-        * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
-        * Here let us use 5msec for safe period interrupt.
-        */
-       err = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                          5000, UINT_MAX);
-       if (err < 0)
-               goto end;
-
-       /* Non-Blocking stream has no more constraints */
-       if (!(s->flags & CIP_BLOCKING))
-               goto end;
-
-       /*
-        * One AMDTP packet can include some frames. In blocking mode, the
-        * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
-        * depending on its sampling rate. For accurate period interrupt, it's
-        * preferrable to align period/buffer sizes to current SYT_INTERVAL.
-        *
-        * TODO: These constraints can be improved with proper rules.
-        * Currently apply LCM of SYT_INTERVALs.
-        */
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
-       if (err < 0)
-               goto end;
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
-end:
-       return err;
-}
-EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
-
-/**
- * amdtp_stream_set_parameters - set stream parameters
- * @s: the AMDTP stream to configure
- * @rate: the sample rate
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- *                as AM824 multi-bit linear audio
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
- *
- * The parameters must be set before the stream is started, and must not be
- * changed while the stream is running.
- */
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
-                                unsigned int rate,
-                                unsigned int pcm_channels,
-                                unsigned int midi_ports)
-{
-       unsigned int i, sfc, midi_channels;
-
-       midi_channels = DIV_ROUND_UP(midi_ports, 8);
-
-       if (WARN_ON(amdtp_stream_running(s)) |
-           WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
-           WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
-               return;
-
-       for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
-               if (amdtp_rate_table[sfc] == rate)
-                       goto sfc_found;
-       WARN_ON(1);
-       return;
-
-sfc_found:
-       s->pcm_channels = pcm_channels;
-       s->sfc = sfc;
-       s->data_block_quadlets = s->pcm_channels + midi_channels;
-       s->midi_ports = midi_ports;
-
-       s->syt_interval = amdtp_syt_intervals[sfc];
-
-       /* default buffering in the device */
-       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
-       if (s->flags & CIP_BLOCKING)
-               /* additional buffering needed to adjust for no-data packets */
-               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
-
-       /* init the position map for PCM and MIDI channels */
-       for (i = 0; i < pcm_channels; i++)
-               s->pcm_positions[i] = i;
-       s->midi_position = s->pcm_channels;
-
-       /*
-        * We do not know the actual MIDI FIFO size of most devices.  Just
-        * assume two bytes, i.e., one byte can be received over the bus while
-        * the previous one is transmitted over MIDI.
-        * (The value here is adjusted for midi_ratelimit_per_packet().)
-        */
-       s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
-}
-EXPORT_SYMBOL(amdtp_stream_set_parameters);
-
-/**
- * amdtp_stream_get_max_payload - get the stream's packet size
- * @s: the AMDTP stream
- *
- * This function must not be called before the stream has been configured
- * with amdtp_stream_set_parameters().
- */
-unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
-{
-       unsigned int multiplier = 1;
-
-       if (s->flags & CIP_JUMBO_PAYLOAD)
-               multiplier = 5;
-
-       return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
-}
-EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-
-static void write_pcm_s16(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames);
-static void write_pcm_s32(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames);
-static void read_pcm_s32(struct amdtp_stream *s,
-                        struct snd_pcm_substream *pcm,
-                        __be32 *buffer, unsigned int frames);
-
-/**
- * amdtp_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP stream to configure
- * @format: the format of the ALSA PCM device
- *
- * The sample format must be set after the other parameters (rate/PCM channels/
- * MIDI) and before the stream is started, and must not be changed while the
- * stream is running.
- */
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
-                                snd_pcm_format_t format)
-{
-       if (WARN_ON(amdtp_stream_pcm_running(s)))
-               return;
-
-       switch (format) {
-       default:
-               WARN_ON(1);
-               /* fall through */
-       case SNDRV_PCM_FORMAT_S16:
-               if (s->direction == AMDTP_OUT_STREAM) {
-                       s->transfer_samples = write_pcm_s16;
-                       break;
-               }
-               WARN_ON(1);
-               /* fall through */
-       case SNDRV_PCM_FORMAT_S32:
-               if (s->direction == AMDTP_OUT_STREAM)
-                       s->transfer_samples = write_pcm_s32;
-               else
-                       s->transfer_samples = read_pcm_s32;
-               break;
-       }
-}
-EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
-
-/**
- * amdtp_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
-{
-       tasklet_kill(&s->period_tasklet);
-       s->pcm_buffer_pointer = 0;
-       s->pcm_period_pointer = 0;
-       s->pointer_flush = true;
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-
-static unsigned int calculate_data_blocks(struct amdtp_stream *s,
-                                         unsigned int syt)
-{
-       unsigned int phase, data_blocks;
-
-       /* Blocking mode. */
-       if (s->flags & CIP_BLOCKING) {
-               /* This module generate empty packet for 'no data'. */
-               if (syt == CIP_SYT_NO_INFO)
-                       data_blocks = 0;
-               else
-                       data_blocks = s->syt_interval;
-       /* Non-blocking mode. */
-       } else {
-               if (!cip_sfc_is_base_44100(s->sfc)) {
-                       /* Sample_rate / 8000 is an integer, and precomputed. */
-                       data_blocks = s->data_block_state;
-               } else {
-                       phase = s->data_block_state;
-
-               /*
-                * This calculates the number of data blocks per packet so that
-                * 1) the overall rate is correct and exactly synchronized to
-                *    the bus clock, and
-                * 2) packets with a rounded-up number of blocks occur as early
-                *    as possible in the sequence (to prevent underruns of the
-                *    device's buffer).
-                */
-                       if (s->sfc == CIP_SFC_44100)
-                               /* 6 6 5 6 5 6 5 ... */
-                               data_blocks = 5 + ((phase & 1) ^
-                                                  (phase == 0 || phase >= 40));
-                       else
-                               /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
-                               data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
-                       if (++phase >= (80 >> (s->sfc >> 1)))
-                               phase = 0;
-                       s->data_block_state = phase;
-               }
-       }
-
-       return data_blocks;
-}
-
-static unsigned int calculate_syt(struct amdtp_stream *s,
-                                 unsigned int cycle)
-{
-       unsigned int syt_offset, phase, index, syt;
-
-       if (s->last_syt_offset < TICKS_PER_CYCLE) {
-               if (!cip_sfc_is_base_44100(s->sfc))
-                       syt_offset = s->last_syt_offset + s->syt_offset_state;
-               else {
-               /*
-                * The time, in ticks, of the n'th SYT_INTERVAL sample is:
-                *   n * SYT_INTERVAL * 24576000 / sample_rate
-                * Modulo TICKS_PER_CYCLE, the difference between successive
-                * elements is about 1386.23.  Rounding the results of this
-                * formula to the SYT precision results in a sequence of
-                * differences that begins with:
-                *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
-                * This code generates _exactly_ the same sequence.
-                */
-                       phase = s->syt_offset_state;
-                       index = phase % 13;
-                       syt_offset = s->last_syt_offset;
-                       syt_offset += 1386 + ((index && !(index & 3)) ||
-                                             phase == 146);
-                       if (++phase >= 147)
-                               phase = 0;
-                       s->syt_offset_state = phase;
-               }
-       } else
-               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
-       s->last_syt_offset = syt_offset;
-
-       if (syt_offset < TICKS_PER_CYCLE) {
-               syt_offset += s->transfer_delay;
-               syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
-               syt += syt_offset % TICKS_PER_CYCLE;
-
-               return syt & CIP_SYT_MASK;
-       } else {
-               return CIP_SYT_NO_INFO;
-       }
-}
-
-static void write_pcm_s32(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       const u32 *src;
-
-       channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       buffer[s->pcm_positions[c]] =
-                                       cpu_to_be32((*src >> 8) | 0x40000000);
-                       src++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       src = (void *)runtime->dma_area;
-       }
-}
-
-static void write_pcm_s16(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       const u16 *src;
-
-       channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       buffer[s->pcm_positions[c]] =
-                                       cpu_to_be32((*src << 8) | 0x42000000);
-                       src++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       src = (void *)runtime->dma_area;
-       }
-}
-
-static void read_pcm_s32(struct amdtp_stream *s,
-                        struct snd_pcm_substream *pcm,
-                        __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       u32 *dst;
-
-       channels = s->pcm_channels;
-       dst  = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
-                       dst++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       dst = (void *)runtime->dma_area;
-       }
-}
-
-static void write_pcm_silence(struct amdtp_stream *s,
-                             __be32 *buffer, unsigned int frames)
-{
-       unsigned int i, c;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < s->pcm_channels; ++c)
-                       buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
-               buffer += s->data_block_quadlets;
-       }
-}
-
-/*
- * To avoid sending MIDI bytes at too high a rate, assume that the receiving
- * device has a FIFO, and track how much it is filled.  This values increases
- * by one whenever we send one byte in a packet, but the FIFO empties at
- * a constant rate independent of our packet rate.  One packet has syt_interval
- * samples, so the number of bytes that empty out of the FIFO, per packet(!),
- * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate.  To avoid storing
- * fractional values, the values in midi_fifo_used[] are measured in bytes
- * multiplied by the sample rate.
- */
-static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
-{
-       int used;
-
-       used = s->midi_fifo_used[port];
-       if (used == 0) /* common shortcut */
-               return true;
-
-       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
-       used = max(used, 0);
-       s->midi_fifo_used[port] = used;
-
-       return used < s->midi_fifo_limit;
-}
-
-static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
-{
-       s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
-}
-
-static void write_midi_messages(struct amdtp_stream *s,
-                               __be32 *buffer, unsigned int frames)
-{
-       unsigned int f, port;
-       u8 *b;
-
-       for (f = 0; f < frames; f++) {
-               b = (u8 *)&buffer[s->midi_position];
-
-               port = (s->data_block_counter + f) % 8;
-               if (f < MAX_MIDI_RX_BLOCKS &&
-                   midi_ratelimit_per_packet(s, port) &&
-                   s->midi[port] != NULL &&
-                   snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
-                       midi_rate_use_one_byte(s, port);
-                       b[0] = 0x81;
-               } else {
-                       b[0] = 0x80;
-                       b[1] = 0;
-               }
-               b[2] = 0;
-               b[3] = 0;
-
-               buffer += s->data_block_quadlets;
-       }
-}
-
-static void read_midi_messages(struct amdtp_stream *s,
-                              __be32 *buffer, unsigned int frames)
-{
-       unsigned int f, port;
-       int len;
-       u8 *b;
-
-       for (f = 0; f < frames; f++) {
-               port = (s->data_block_counter + f) % 8;
-               b = (u8 *)&buffer[s->midi_position];
-
-               len = b[0] - 0x80;
-               if ((1 <= len) &&  (len <= 3) && (s->midi[port]))
-                       snd_rawmidi_receive(s->midi[port], b + 1, len);
-
-               buffer += s->data_block_quadlets;
-       }
-}
-
-static void update_pcm_pointers(struct amdtp_stream *s,
-                               struct snd_pcm_substream *pcm,
-                               unsigned int frames)
-{
-       unsigned int ptr;
-
-       /*
-        * In IEC 61883-6, one data block represents one event. In ALSA, one
-        * event equals to one PCM frame. But Dice has a quirk to transfer
-        * two PCM frames in one data block.
-        */
-       if (s->double_pcm_frames)
-               frames *= 2;
-
-       ptr = s->pcm_buffer_pointer + frames;
-       if (ptr >= pcm->runtime->buffer_size)
-               ptr -= pcm->runtime->buffer_size;
-       ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
-
-       s->pcm_period_pointer += frames;
-       if (s->pcm_period_pointer >= pcm->runtime->period_size) {
-               s->pcm_period_pointer -= pcm->runtime->period_size;
-               s->pointer_flush = false;
-               tasklet_hi_schedule(&s->period_tasklet);
-       }
-}
-
-static void pcm_period_tasklet(unsigned long data)
-{
-       struct amdtp_stream *s = (void *)data;
-       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
-
-       if (pcm)
-               snd_pcm_period_elapsed(pcm);
-}
-
-static int queue_packet(struct amdtp_stream *s,
-                       unsigned int header_length,
-                       unsigned int payload_length, bool skip)
-{
-       struct fw_iso_packet p = {0};
-       int err = 0;
-
-       if (IS_ERR(s->context))
-               goto end;
-
-       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
-       p.tag = TAG_CIP;
-       p.header_length = header_length;
-       p.payload_length = (!skip) ? payload_length : 0;
-       p.skip = skip;
-       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
-                                  s->buffer.packets[s->packet_index].offset);
-       if (err < 0) {
-               dev_err(&s->unit->device, "queueing error: %d\n", err);
-               goto end;
-       }
-
-       if (++s->packet_index >= QUEUE_LENGTH)
-               s->packet_index = 0;
-end:
-       return err;
-}
-
-static inline int queue_out_packet(struct amdtp_stream *s,
-                                  unsigned int payload_length, bool skip)
-{
-       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
-                           payload_length, skip);
-}
-
-static inline int queue_in_packet(struct amdtp_stream *s)
-{
-       return queue_packet(s, IN_PACKET_HEADER_SIZE,
-                           amdtp_stream_get_max_payload(s), false);
-}
-
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
-                            unsigned int syt)
-{
-       __be32 *buffer;
-       unsigned int payload_length;
-       struct snd_pcm_substream *pcm;
-
-       buffer = s->buffer.packets[s->packet_index].buffer;
-       buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
-                               (s->data_block_quadlets << CIP_DBS_SHIFT) |
-                               s->data_block_counter);
-       buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
-                               (s->sfc << CIP_FDF_SHIFT) | syt);
-       buffer += 2;
-
-       pcm = ACCESS_ONCE(s->pcm);
-       if (pcm)
-               s->transfer_samples(s, pcm, buffer, data_blocks);
-       else
-               write_pcm_silence(s, buffer, data_blocks);
-       if (s->midi_ports)
-               write_midi_messages(s, buffer, data_blocks);
-
-       s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-
-       payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-       if (queue_out_packet(s, payload_length, false) < 0)
-               return -EIO;
-
-       if (pcm)
-               update_pcm_pointers(s, pcm, data_blocks);
-
-       /* No need to return the number of handled data blocks. */
-       return 0;
-}
-
-static int handle_in_packet(struct amdtp_stream *s,
-                           unsigned int payload_quadlets, __be32 *buffer,
-                           unsigned int *data_blocks)
-{
-       u32 cip_header[2];
-       unsigned int data_block_quadlets, data_block_counter, dbc_interval;
-       struct snd_pcm_substream *pcm = NULL;
-       bool lost;
-
-       cip_header[0] = be32_to_cpu(buffer[0]);
-       cip_header[1] = be32_to_cpu(buffer[1]);
-
-       /*
-        * This module supports 'Two-quadlet CIP header with SYT field'.
-        * For convenience, also check FMT field is AM824 or not.
-        */
-       if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
-           ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
-           ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
-               dev_info_ratelimited(&s->unit->device,
-                               "Invalid CIP header for AMDTP: %08X:%08X\n",
-                               cip_header[0], cip_header[1]);
-               *data_blocks = 0;
-               goto end;
-       }
-
-       /* Calculate data blocks */
-       if (payload_quadlets < 3 ||
-           ((cip_header[1] & CIP_FDF_MASK) ==
-                               (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
-               *data_blocks = 0;
-       } else {
-               data_block_quadlets =
-                       (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
-               /* avoid division by zero */
-               if (data_block_quadlets == 0) {
-                       dev_err(&s->unit->device,
-                               "Detect invalid value in dbs field: %08X\n",
-                               cip_header[0]);
-                       return -EPROTO;
-               }
-               if (s->flags & CIP_WRONG_DBS)
-                       data_block_quadlets = s->data_block_quadlets;
-
-               *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
-       }
-
-       /* Check data block counter continuity */
-       data_block_counter = cip_header[0] & CIP_DBC_MASK;
-       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
-           s->data_block_counter != UINT_MAX)
-               data_block_counter = s->data_block_counter;
-
-       if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
-            data_block_counter == s->tx_first_dbc) ||
-           s->data_block_counter == UINT_MAX) {
-               lost = false;
-       } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
-               lost = data_block_counter != s->data_block_counter;
-       } else {
-               if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
-                       dbc_interval = s->tx_dbc_interval;
-               else
-                       dbc_interval = *data_blocks;
-
-               lost = data_block_counter !=
-                      ((s->data_block_counter + dbc_interval) & 0xff);
-       }
-
-       if (lost) {
-               dev_err(&s->unit->device,
-                       "Detect discontinuity of CIP: %02X %02X\n",
-                       s->data_block_counter, data_block_counter);
-               return -EIO;
-       }
-
-       if (*data_blocks > 0) {
-               buffer += 2;
-
-               pcm = ACCESS_ONCE(s->pcm);
-               if (pcm)
-                       s->transfer_samples(s, pcm, buffer, *data_blocks);
-
-               if (s->midi_ports)
-                       read_midi_messages(s, buffer, *data_blocks);
-       }
-
-       if (s->flags & CIP_DBC_IS_END_EVENT)
-               s->data_block_counter = data_block_counter;
-       else
-               s->data_block_counter =
-                               (data_block_counter + *data_blocks) & 0xff;
-end:
-       if (queue_in_packet(s) < 0)
-               return -EIO;
-
-       if (pcm)
-               update_pcm_pointers(s, pcm, *data_blocks);
-
-       return 0;
-}
-
-static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
-                               size_t header_length, void *header,
-                               void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-       unsigned int i, syt, packets = header_length / 4;
-       unsigned int data_blocks;
-
-       if (s->packet_index < 0)
-               return;
-
-       /*
-        * Compute the cycle of the last queued packet.
-        * (We need only the four lowest bits for the SYT, so we can ignore
-        * that bits 0-11 must wrap around at 3072.)
-        */
-       cycle += QUEUE_LENGTH - packets;
-
-       for (i = 0; i < packets; ++i) {
-               syt = calculate_syt(s, ++cycle);
-               data_blocks = calculate_data_blocks(s, syt);
-
-               if (handle_out_packet(s, data_blocks, syt) < 0) {
-                       s->packet_index = -1;
-                       amdtp_stream_pcm_abort(s);
-                       return;
-               }
-       }
-
-       fw_iso_context_queue_flush(s->context);
-}
-
-static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
-                              size_t header_length, void *header,
-                              void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-       unsigned int p, syt, packets;
-       unsigned int payload_quadlets, max_payload_quadlets;
-       unsigned int data_blocks;
-       __be32 *buffer, *headers = header;
-
-       if (s->packet_index < 0)
-               return;
-
-       /* The number of packets in buffer */
-       packets = header_length / IN_PACKET_HEADER_SIZE;
-
-       /* For buffer-over-run prevention. */
-       max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
-
-       for (p = 0; p < packets; p++) {
-               buffer = s->buffer.packets[s->packet_index].buffer;
-
-               /* The number of quadlets in this packet */
-               payload_quadlets =
-                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
-               if (payload_quadlets > max_payload_quadlets) {
-                       dev_err(&s->unit->device,
-                               "Detect jumbo payload: %02x %02x\n",
-                               payload_quadlets, max_payload_quadlets);
-                       s->packet_index = -1;
-                       break;
-               }
-
-               if (handle_in_packet(s, payload_quadlets, buffer,
-                                                       &data_blocks) < 0) {
-                       s->packet_index = -1;
-                       break;
-               }
-
-               /* Process sync slave stream */
-               if (s->sync_slave && s->sync_slave->callbacked) {
-                       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
-                       if (handle_out_packet(s->sync_slave,
-                                             data_blocks, syt) < 0) {
-                               s->packet_index = -1;
-                               break;
-                       }
-               }
-       }
-
-       /* Queueing error or detecting discontinuity */
-       if (s->packet_index < 0) {
-               amdtp_stream_pcm_abort(s);
-
-               /* Abort sync slave. */
-               if (s->sync_slave) {
-                       s->sync_slave->packet_index = -1;
-                       amdtp_stream_pcm_abort(s->sync_slave);
-               }
-               return;
-       }
-
-       /* when sync to device, flush the packets for slave stream */
-       if (s->sync_slave && s->sync_slave->callbacked)
-               fw_iso_context_queue_flush(s->sync_slave->context);
-
-       fw_iso_context_queue_flush(s->context);
-}
-
-/* processing is done by master callback */
-static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
-                                 size_t header_length, void *header,
-                                 void *private_data)
-{
-       return;
-}
-
-/* this is executed one time */
-static void amdtp_stream_first_callback(struct fw_iso_context *context,
-                                       u32 cycle, size_t header_length,
-                                       void *header, void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-
-       /*
-        * For in-stream, first packet has come.
-        * For out-stream, prepared to transmit first packet
-        */
-       s->callbacked = true;
-       wake_up(&s->callback_wait);
-
-       if (s->direction == AMDTP_IN_STREAM)
-               context->callback.sc = in_stream_callback;
-       else if (s->flags & CIP_SYNC_TO_DEVICE)
-               context->callback.sc = slave_stream_callback;
-       else
-               context->callback.sc = out_stream_callback;
-
-       context->callback.sc(context, cycle, header_length, header, s);
-}
-
-/**
- * amdtp_stream_start - start transferring packets
- * @s: the AMDTP stream to start
- * @channel: the isochronous channel on the bus
- * @speed: firewire speed code
- *
- * The stream cannot be started until it has been configured with
- * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
- * device can be started.
- */
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
-{
-       static const struct {
-               unsigned int data_block;
-               unsigned int syt_offset;
-       } initial_state[] = {
-               [CIP_SFC_32000]  = {  4, 3072 },
-               [CIP_SFC_48000]  = {  6, 1024 },
-               [CIP_SFC_96000]  = { 12, 1024 },
-               [CIP_SFC_192000] = { 24, 1024 },
-               [CIP_SFC_44100]  = {  0,   67 },
-               [CIP_SFC_88200]  = {  0,   67 },
-               [CIP_SFC_176400] = {  0,   67 },
-       };
-       unsigned int header_size;
-       enum dma_data_direction dir;
-       int type, tag, err;
-
-       mutex_lock(&s->mutex);
-
-       if (WARN_ON(amdtp_stream_running(s) ||
-                   (s->data_block_quadlets < 1))) {
-               err = -EBADFD;
-               goto err_unlock;
-       }
-
-       if (s->direction == AMDTP_IN_STREAM &&
-           s->flags & CIP_SKIP_INIT_DBC_CHECK)
-               s->data_block_counter = UINT_MAX;
-       else
-               s->data_block_counter = 0;
-       s->data_block_state = initial_state[s->sfc].data_block;
-       s->syt_offset_state = initial_state[s->sfc].syt_offset;
-       s->last_syt_offset = TICKS_PER_CYCLE;
-
-       /* initialize packet buffer */
-       if (s->direction == AMDTP_IN_STREAM) {
-               dir = DMA_FROM_DEVICE;
-               type = FW_ISO_CONTEXT_RECEIVE;
-               header_size = IN_PACKET_HEADER_SIZE;
-       } else {
-               dir = DMA_TO_DEVICE;
-               type = FW_ISO_CONTEXT_TRANSMIT;
-               header_size = OUT_PACKET_HEADER_SIZE;
-       }
-       err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-                                     amdtp_stream_get_max_payload(s), dir);
-       if (err < 0)
-               goto err_unlock;
-
-       s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-                                          type, channel, speed, header_size,
-                                          amdtp_stream_first_callback, s);
-       if (IS_ERR(s->context)) {
-               err = PTR_ERR(s->context);
-               if (err == -EBUSY)
-                       dev_err(&s->unit->device,
-                               "no free stream on this controller\n");
-               goto err_buffer;
-       }
-
-       amdtp_stream_update(s);
-
-       s->packet_index = 0;
-       do {
-               if (s->direction == AMDTP_IN_STREAM)
-                       err = queue_in_packet(s);
-               else
-                       err = queue_out_packet(s, 0, true);
-               if (err < 0)
-                       goto err_context;
-       } while (s->packet_index > 0);
-
-       /* NOTE: TAG1 matches CIP. This just affects in stream. */
-       tag = FW_ISO_CONTEXT_MATCH_TAG1;
-       if (s->flags & CIP_EMPTY_WITH_TAG0)
-               tag |= FW_ISO_CONTEXT_MATCH_TAG0;
-
-       s->callbacked = false;
-       err = fw_iso_context_start(s->context, -1, 0, tag);
-       if (err < 0)
-               goto err_context;
-
-       mutex_unlock(&s->mutex);
-
-       return 0;
-
-err_context:
-       fw_iso_context_destroy(s->context);
-       s->context = ERR_PTR(-1);
-err_buffer:
-       iso_packets_buffer_destroy(&s->buffer, s->unit);
-err_unlock:
-       mutex_unlock(&s->mutex);
-
-       return err;
-}
-EXPORT_SYMBOL(amdtp_stream_start);
-
-/**
- * amdtp_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP stream that transports the PCM data
- *
- * Returns the current buffer position, in frames.
- */
-unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
-{
-       /* this optimization is allowed to be racy */
-       if (s->pointer_flush && amdtp_stream_running(s))
-               fw_iso_context_flush_completions(s->context);
-       else
-               s->pointer_flush = true;
-
-       return ACCESS_ONCE(s->pcm_buffer_pointer);
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
-
-/**
- * amdtp_stream_update - update the stream after a bus reset
- * @s: the AMDTP stream
- */
-void amdtp_stream_update(struct amdtp_stream *s)
-{
-       /* Precomputing. */
-       ACCESS_ONCE(s->source_node_id_field) =
-               (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
-                                                               CIP_SID_MASK;
-}
-EXPORT_SYMBOL(amdtp_stream_update);
-
-/**
- * amdtp_stream_stop - stop sending packets
- * @s: the AMDTP stream to stop
- *
- * All PCM and MIDI devices of the stream must be stopped before the stream
- * itself can be stopped.
- */
-void amdtp_stream_stop(struct amdtp_stream *s)
-{
-       mutex_lock(&s->mutex);
-
-       if (!amdtp_stream_running(s)) {
-               mutex_unlock(&s->mutex);
-               return;
-       }
-
-       tasklet_kill(&s->period_tasklet);
-       fw_iso_context_stop(s->context);
-       fw_iso_context_destroy(s->context);
-       s->context = ERR_PTR(-1);
-       iso_packets_buffer_destroy(&s->buffer, s->unit);
-
-       s->callbacked = false;
-
-       mutex_unlock(&s->mutex);
-}
-EXPORT_SYMBOL(amdtp_stream_stop);
-
-/**
- * amdtp_stream_pcm_abort - abort the running PCM device
- * @s: the AMDTP stream about to be stopped
- *
- * If the isochronous stream needs to be stopped asynchronously, call this
- * function first to stop the PCM device.
- */
-void amdtp_stream_pcm_abort(struct amdtp_stream *s)
-{
-       struct snd_pcm_substream *pcm;
-
-       pcm = ACCESS_ONCE(s->pcm);
-       if (pcm)
-               snd_pcm_stop_xrun(pcm);
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
deleted file mode 100644 (file)
index b2cf9e7..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
-#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <sound/asound.h>
-#include "packets-buffer.h"
-
-/**
- * enum cip_flags - describes details of the streaming protocol
- * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
- *     sample_rate/8000 samples, with rounding up or down to adjust
- *     for clock skew and left-over fractional samples.  This should
- *     be used if supported by the device.
- * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
- *     SYT_INTERVAL samples, with these two types alternating so that
- *     the overall sample rate comes out right.
- * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
- *     generated by in packets. Defaultly this driver generates timestamp.
- * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
- * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
- *     corresponds to the end of event in the packet. Out of IEC 61883.
- * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
- *     The value of data_block_quadlets is used instead of reported value.
- * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
- *     skipped for detecting discontinuity.
- * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
- *     packet is not continuous from an initial value.
- * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
- *     packet is wrong but the others are correct.
- * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
- *     packet is larger than IEC 61883-6 defines. Current implementation
- *     allows 5 times as large as IEC 61883-6 defines.
- */
-enum cip_flags {
-       CIP_NONBLOCKING         = 0x00,
-       CIP_BLOCKING            = 0x01,
-       CIP_SYNC_TO_DEVICE      = 0x02,
-       CIP_EMPTY_WITH_TAG0     = 0x04,
-       CIP_DBC_IS_END_EVENT    = 0x08,
-       CIP_WRONG_DBS           = 0x10,
-       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
-       CIP_SKIP_INIT_DBC_CHECK = 0x40,
-       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
-       CIP_JUMBO_PAYLOAD       = 0x100,
-};
-
-/**
- * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
- * @CIP_SFC_32000:   32,000 data blocks
- * @CIP_SFC_44100:   44,100 data blocks
- * @CIP_SFC_48000:   48,000 data blocks
- * @CIP_SFC_88200:   88,200 data blocks
- * @CIP_SFC_96000:   96,000 data blocks
- * @CIP_SFC_176400: 176,400 data blocks
- * @CIP_SFC_192000: 192,000 data blocks
- * @CIP_SFC_COUNT: the number of supported SFCs
- *
- * These values are used to show nominal Sampling Frequency Code in
- * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
- * this code means the number of events per second. Actually the code
- * represents the number of data blocks transferred per second in an AMDTP
- * stream.
- *
- * In IEC 61883-6:2005, some extensions were added to support more types of
- * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
- * different depending on the types.
- *
- * Currently our implementation is compatible with IEC 61883-6:2002.
- */
-enum cip_sfc {
-       CIP_SFC_32000  = 0,
-       CIP_SFC_44100  = 1,
-       CIP_SFC_48000  = 2,
-       CIP_SFC_88200  = 3,
-       CIP_SFC_96000  = 4,
-       CIP_SFC_176400 = 5,
-       CIP_SFC_192000 = 6,
-       CIP_SFC_COUNT
-};
-
-#define AMDTP_IN_PCM_FORMAT_BITS       SNDRV_PCM_FMTBIT_S32
-
-#define AMDTP_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
-                                        SNDRV_PCM_FMTBIT_S32)
-
-
-/*
- * This module supports maximum 64 PCM channels for one PCM stream
- * This is for our convenience.
- */
-#define AMDTP_MAX_CHANNELS_FOR_PCM     64
-
-/*
- * AMDTP packet can include channels for MIDI conformant data.
- * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
- * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
- *
- * This module supports maximum 1 MIDI conformant data channels.
- * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
- */
-#define AMDTP_MAX_CHANNELS_FOR_MIDI    1
-
-struct fw_unit;
-struct fw_iso_context;
-struct snd_pcm_substream;
-struct snd_pcm_runtime;
-struct snd_rawmidi_substream;
-
-enum amdtp_stream_direction {
-       AMDTP_OUT_STREAM = 0,
-       AMDTP_IN_STREAM
-};
-
-struct amdtp_stream {
-       struct fw_unit *unit;
-       enum cip_flags flags;
-       enum amdtp_stream_direction direction;
-       struct fw_iso_context *context;
-       struct mutex mutex;
-
-       enum cip_sfc sfc;
-       unsigned int data_block_quadlets;
-       unsigned int pcm_channels;
-       unsigned int midi_ports;
-       void (*transfer_samples)(struct amdtp_stream *s,
-                                struct snd_pcm_substream *pcm,
-                                __be32 *buffer, unsigned int frames);
-       u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
-       u8 midi_position;
-
-       unsigned int syt_interval;
-       unsigned int transfer_delay;
-       unsigned int source_node_id_field;
-       struct iso_packets_buffer buffer;
-
-       struct snd_pcm_substream *pcm;
-       struct tasklet_struct period_tasklet;
-
-       int packet_index;
-       unsigned int data_block_counter;
-
-       unsigned int data_block_state;
-
-       unsigned int last_syt_offset;
-       unsigned int syt_offset_state;
-
-       unsigned int pcm_buffer_pointer;
-       unsigned int pcm_period_pointer;
-       bool pointer_flush;
-       bool double_pcm_frames;
-
-       struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-       int midi_fifo_limit;
-       int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-
-       /* quirk: fixed interval of dbc between previos/current packets. */
-       unsigned int tx_dbc_interval;
-       /* quirk: indicate the value of dbc field in a first packet. */
-       unsigned int tx_first_dbc;
-
-       bool callbacked;
-       wait_queue_head_t callback_wait;
-       struct amdtp_stream *sync_slave;
-};
-
-int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
-                     enum amdtp_stream_direction dir,
-                     enum cip_flags flags);
-void amdtp_stream_destroy(struct amdtp_stream *s);
-
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
-                                unsigned int rate,
-                                unsigned int pcm_channels,
-                                unsigned int midi_ports);
-unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
-
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
-void amdtp_stream_update(struct amdtp_stream *s);
-void amdtp_stream_stop(struct amdtp_stream *s);
-
-int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
-                                       struct snd_pcm_runtime *runtime);
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
-                                snd_pcm_format_t format);
-void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
-unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
-void amdtp_stream_pcm_abort(struct amdtp_stream *s);
-
-extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
-extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
-
-/**
- * amdtp_stream_running - check stream is running or not
- * @s: the AMDTP stream
- *
- * If this function returns true, the stream is running.
- */
-static inline bool amdtp_stream_running(struct amdtp_stream *s)
-{
-       return !IS_ERR(s->context);
-}
-
-/**
- * amdtp_streaming_error - check for streaming error
- * @s: the AMDTP stream
- *
- * If this function returns true, the stream's packet queue has stopped due to
- * an asynchronous error.
- */
-static inline bool amdtp_streaming_error(struct amdtp_stream *s)
-{
-       return s->packet_index < 0;
-}
-
-/**
- * amdtp_stream_pcm_running - check PCM substream is running or not
- * @s: the AMDTP stream
- *
- * If this function returns true, PCM substream in the AMDTP stream is running.
- */
-static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
-{
-       return !!s->pcm;
-}
-
-/**
- * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
- * @s: the AMDTP stream
- * @pcm: the PCM device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of PCM data.  This function should be called from the PCM
- * device's .trigger callback.
- */
-static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
-                                           struct snd_pcm_substream *pcm)
-{
-       ACCESS_ONCE(s->pcm) = pcm;
-}
-
-/**
- * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
- * @s: the AMDTP stream
- * @port: index of MIDI port
- * @midi: the MIDI device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of MIDI data.  This function should be called from the MIDI
- * device's .trigger callback.
- */
-static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
-                                            unsigned int port,
-                                            struct snd_rawmidi_substream *midi)
-{
-       if (port < s->midi_ports)
-               ACCESS_ONCE(s->midi[port]) = midi;
-}
-
-static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
-{
-       return sfc & 1;
-}
-
-static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
-                                        struct amdtp_stream *master,
-                                        struct amdtp_stream *slave)
-{
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master->flags |= CIP_SYNC_TO_DEVICE;
-               slave->flags |= CIP_SYNC_TO_DEVICE;
-               master->sync_slave = slave;
-       } else {
-               master->flags &= ~CIP_SYNC_TO_DEVICE;
-               slave->flags &= ~CIP_SYNC_TO_DEVICE;
-               master->sync_slave = NULL;
-       }
-
-       slave->sync_slave = NULL;
-}
-
-/**
- * amdtp_stream_wait_callback - sleep till callbacked or timeout
- * @s: the AMDTP stream
- * @timeout: msec till timeout
- *
- * If this function return false, the AMDTP stream should be stopped.
- */
-static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
-                                             unsigned int timeout)
-{
-       return wait_event_timeout(s->callback_wait,
-                                 s->callbacked == true,
-                                 msecs_to_jiffies(timeout)) > 0;
-}
-
-#endif
index 6cf470c80d1fd1e37f92b65d933838da405524cd..af7ed66432661d307cded6269f9072d17ee99b35 100644 (file)
@@ -1,4 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
                  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
                  bebob_focusrite.o bebob_maudio.o bebob.o
-obj-m += snd-bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
index 27a04ac8ffee938fc5e1cd023f6472c3f57f8c2e..091290d1f3ea0a99b8a6d81d501a385863efe64d 100644 (file)
@@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
 #define VEN_EDIROL     0x000040ab
 #define VEN_PRESONUS   0x00000a92
 #define VEN_BRIDGECO   0x000007f5
-#define VEN_MACKIE     0x0000000f
+#define VEN_MACKIE1    0x0000000f
+#define VEN_MACKIE2    0x00000ff2
 #define VEN_STANTON    0x00001260
 #define VEN_TASCAM     0x0000022e
 #define VEN_BEHRINGER  0x00001564
@@ -334,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
        snd_card_free_when_closed(bebob->card);
 }
 
-static struct snd_bebob_rate_spec normal_rate_spec = {
+static const struct snd_bebob_rate_spec normal_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate
 };
@@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
        /* BridgeCo, Audio5 */
        SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
        /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
-       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
        /* Mackie, d.2 (Firewire Option) */
-       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
        /* Stanton, ScratchAmp */
        SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
        /* Tascam, IF-FW DM */
index d23caca7f369076fe5f9bd7c76c01f5c76e259ab..4d8fcc78e747cb5d4232ed9f76c3662a242d72d3 100644 (file)
@@ -31,7 +31,7 @@
 #include "../fcp.h"
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 
 /* basic register addresses on DM1000/DM1100/DM1500 */
@@ -70,9 +70,9 @@ struct snd_bebob_meter_spec {
        int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
 };
 struct snd_bebob_spec {
-       struct snd_bebob_clock_spec *clock;
-       struct snd_bebob_rate_spec *rate;
-       struct snd_bebob_meter_spec *meter;
+       const struct snd_bebob_clock_spec *clock;
+       const struct snd_bebob_rate_spec *rate;
+       const struct snd_bebob_meter_spec *meter;
 };
 
 struct snd_bebob {
@@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
 int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 
 /* model specific operations */
-extern struct snd_bebob_spec phase88_rack_spec;
-extern struct snd_bebob_spec phase24_series_spec;
-extern struct snd_bebob_spec yamaha_go_spec;
-extern struct snd_bebob_spec saffirepro_26_spec;
-extern struct snd_bebob_spec saffirepro_10_spec;
-extern struct snd_bebob_spec saffire_le_spec;
-extern struct snd_bebob_spec saffire_spec;
-extern struct snd_bebob_spec maudio_fw410_spec;
-extern struct snd_bebob_spec maudio_audiophile_spec;
-extern struct snd_bebob_spec maudio_solo_spec;
-extern struct snd_bebob_spec maudio_ozonic_spec;
-extern struct snd_bebob_spec maudio_nrv10_spec;
-extern struct snd_bebob_spec maudio_special_spec;
+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;
 int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
 int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
 
index a1a39494ea6c9bb985815f9ef18f799dd7b0062f..f1109005794944cd759b90ede92a6fc601ab251e 100644 (file)
@@ -200,7 +200,7 @@ end:
        return err;
 }
 
-struct snd_bebob_spec saffire_le_spec;
+const struct snd_bebob_spec saffire_le_spec;
 static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
        SND_BEBOB_CLOCK_TYPE_INTERNAL,
        SND_BEBOB_CLOCK_TYPE_EXTERNAL,
@@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
 static int
 saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
 {
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        unsigned int channels;
        u64 offset;
        int err;
@@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
        return err;
 }
 
-static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
        .get    = &saffirepro_both_clk_freq_get,
        .set    = &saffirepro_both_clk_freq_set,
 };
 /* Saffire Pro 26 I/O  */
-static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
        .num    = ARRAY_SIZE(saffirepro_26_clk_src_types),
        .types  = saffirepro_26_clk_src_types,
        .get    = &saffirepro_both_clk_src_get,
 };
-struct snd_bebob_spec saffirepro_26_spec = {
+const struct snd_bebob_spec saffirepro_26_spec = {
        .clock  = &saffirepro_26_clk_spec,
        .rate   = &saffirepro_both_rate_spec,
        .meter  = NULL
 };
 /* Saffire Pro 10 I/O */
-static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
        .num    = ARRAY_SIZE(saffirepro_10_clk_src_types),
        .types  = saffirepro_10_clk_src_types,
        .get    = &saffirepro_both_clk_src_get,
 };
-struct snd_bebob_spec saffirepro_10_spec = {
+const struct snd_bebob_spec saffirepro_10_spec = {
        .clock  = &saffirepro_10_clk_spec,
        .rate   = &saffirepro_both_rate_spec,
        .meter  = NULL
 };
 
-static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
        .num    = ARRAY_SIZE(saffire_both_clk_src_types),
        .types  = saffire_both_clk_src_types,
        .get    = &saffire_both_clk_src_get,
 };
 /* Saffire LE */
-static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
        .num    = ARRAY_SIZE(saffire_le_meter_labels),
        .labels = saffire_le_meter_labels,
        .get    = &saffire_meter_get,
 };
-struct snd_bebob_spec saffire_le_spec = {
+const struct snd_bebob_spec saffire_le_spec = {
        .clock  = &saffire_both_clk_spec,
        .rate   = &saffire_both_rate_spec,
        .meter  = &saffire_le_meter_spec
 };
 /* Saffire */
-static struct snd_bebob_meter_spec saffire_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
        .num    = ARRAY_SIZE(saffire_meter_labels),
        .labels = saffire_meter_labels,
        .get    = &saffire_meter_get,
 };
-struct snd_bebob_spec saffire_spec = {
+const struct snd_bebob_spec saffire_spec = {
        .clock  = &saffire_both_clk_spec,
        .rate   = &saffire_both_rate_spec,
        .meter  = &saffire_meter_spec
index 057495d54ab029e7f1fc5b1b7cf630ee20c3c817..07e5abdbceb59cec189726d79ee5d200d452e53b 100644 (file)
@@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = {
 static int
 special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
 {
-       u16 *buf;
+       __be16 *buf;
        unsigned int i, c, channels;
        int err;
 
@@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
 static int
 normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
 {
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        unsigned int c, channels;
        int err;
 
@@ -712,85 +712,85 @@ end:
 }
 
 /* for special customized devices */
-static struct snd_bebob_rate_spec special_rate_spec = {
+static const struct snd_bebob_rate_spec special_rate_spec = {
        .get    = &special_get_rate,
        .set    = &special_set_rate,
 };
-static struct snd_bebob_clock_spec special_clk_spec = {
+static const struct snd_bebob_clock_spec special_clk_spec = {
        .num    = ARRAY_SIZE(special_clk_types),
        .types  = special_clk_types,
        .get    = &special_clk_get,
 };
-static struct snd_bebob_meter_spec special_meter_spec = {
+static const struct snd_bebob_meter_spec special_meter_spec = {
        .num    = ARRAY_SIZE(special_meter_labels),
        .labels = special_meter_labels,
        .get    = &special_meter_get
 };
-struct snd_bebob_spec maudio_special_spec = {
+const struct snd_bebob_spec maudio_special_spec = {
        .clock  = &special_clk_spec,
        .rate   = &special_rate_spec,
        .meter  = &special_meter_spec
 };
 
 /* Firewire 410 specification */
-static struct snd_bebob_rate_spec usual_rate_spec = {
+static const struct snd_bebob_rate_spec usual_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-static struct snd_bebob_meter_spec fw410_meter_spec = {
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
        .num    = ARRAY_SIZE(fw410_meter_labels),
        .labels = fw410_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_fw410_spec = {
+const struct snd_bebob_spec maudio_fw410_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &fw410_meter_spec
 };
 
 /* Firewire Audiophile specification */
-static struct snd_bebob_meter_spec audiophile_meter_spec = {
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
        .num    = ARRAY_SIZE(audiophile_meter_labels),
        .labels = audiophile_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_audiophile_spec = {
+const struct snd_bebob_spec maudio_audiophile_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &audiophile_meter_spec
 };
 
 /* Firewire Solo specification */
-static struct snd_bebob_meter_spec solo_meter_spec = {
+static const struct snd_bebob_meter_spec solo_meter_spec = {
        .num    = ARRAY_SIZE(solo_meter_labels),
        .labels = solo_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_solo_spec = {
+const struct snd_bebob_spec maudio_solo_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &solo_meter_spec
 };
 
 /* Ozonic specification */
-static struct snd_bebob_meter_spec ozonic_meter_spec = {
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
        .num    = ARRAY_SIZE(ozonic_meter_labels),
        .labels = ozonic_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_ozonic_spec = {
+const struct snd_bebob_spec maudio_ozonic_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &ozonic_meter_spec
 };
 
 /* NRV10 specification */
-static struct snd_bebob_meter_spec nrv10_meter_spec = {
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
        .num    = ARRAY_SIZE(nrv10_meter_labels),
        .labels = nrv10_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_nrv10_spec = {
+const struct snd_bebob_spec maudio_nrv10_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &nrv10_meter_spec
index 5681143925cda919987350a9a054fc47cf1b0a1e..90d95be499b079c5d32c99b1b7b56a409ac377ec 100644 (file)
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&bebob->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&bebob->tx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&bebob->tx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&bebob->tx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&bebob->tx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&bebob->lock, flags);
 }
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&bebob->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&bebob->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&bebob->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&bebob->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&bebob->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&bebob->lock, flags);
 }
index c0f018a61fdc039b0e331a40d9bed4e305645320..ef224d6f5c248d0d28cf9754f96f20b8fc99df5c 100644 (file)
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                s = &bebob->tx_stream;
                formations = bebob->tx_stream_formations;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                s = &bebob->rx_stream;
                formations = bebob->rx_stream_formations;
        }
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
 end:
        return err;
 }
@@ -155,7 +155,7 @@ static int
 pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
-       struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+       const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
        unsigned int sampling_rate;
        enum snd_bebob_clock_type src;
        int err;
@@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&bebob->substreams_counter);
-       amdtp_stream_set_pcm_format(&bebob->tx_stream,
-                                   params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&bebob->substreams_counter);
-       amdtp_stream_set_pcm_format(&bebob->rx_stream,
-                                   params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 301cc6a9394542817c9d17feda1b87da4505b65c..ec24f96794f50794737578c93d06261a9d93330c 100644 (file)
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
                 struct snd_info_buffer *buffer)
 {
        struct snd_bebob *bebob = entry->private_data;
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        u32 *buf;
        unsigned int i, c, channels, size;
 
@@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry,
                "SYT-Match",
        };
        struct snd_bebob *bebob = entry->private_data;
-       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        enum snd_bebob_clock_type src;
        unsigned int rate;
 
index 5be5242e1ed86a535b875346429f5113d37dcad5..926e5dcbb66a1a3ec523482ceb5e199f8c455b64 100644 (file)
@@ -119,7 +119,7 @@ end:
 int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
                                   enum snd_bebob_clock_type *src)
 {
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
        unsigned int id;
        enum avc_bridgeco_plug_type type;
@@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
                                        err = -ENOSYS;
                                        goto end;
                                }
-                               s->midi_position = stm_pos;
+                               amdtp_am824_set_midi_position(s, stm_pos);
                                midi = stm_pos;
                                break;
                        /* for PCM data channel */
@@ -354,11 +354,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
                        case 0x09:      /* Digital */
                        default:
                                location = pcm + sec_loc;
-                               if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+                               if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
                                        err = -ENOSYS;
                                        goto end;
                                }
-                               s->pcm_positions[location] = stm_pos;
+                               amdtp_am824_set_pcm_position(s, location,
+                                                            stm_pos);
                                break;
                        }
                }
@@ -427,12 +428,19 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
        index = get_formation_index(rate);
        pcm_channels = bebob->tx_stream_formations[index].pcm;
        midi_channels = bebob->tx_stream_formations[index].midi;
-       amdtp_stream_set_parameters(&bebob->tx_stream,
-                                   rate, pcm_channels, midi_channels * 8);
+       err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
+                                        pcm_channels, midi_channels * 8,
+                                        false);
+       if (err < 0)
+               goto end;
+
        pcm_channels = bebob->rx_stream_formations[index].pcm;
        midi_channels = bebob->rx_stream_formations[index].midi;
-       amdtp_stream_set_parameters(&bebob->rx_stream,
-                                   rate, pcm_channels, midi_channels * 8);
+       err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
+                                        pcm_channels, midi_channels * 8,
+                                        false);
+       if (err < 0)
+               goto end;
 
        /* establish connections for both streams */
        err = cmp_connection_establish(&bebob->out_conn,
@@ -530,8 +538,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
-                               AMDTP_IN_STREAM, CIP_BLOCKING);
+       err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
+                              AMDTP_IN_STREAM, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(&bebob->tx_stream);
                destroy_both_connections(bebob);
@@ -559,8 +567,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
        if (bebob->maudio_special_quirk)
                bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
 
-       err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
-                               AMDTP_OUT_STREAM, CIP_BLOCKING);
+       err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
+                              AMDTP_OUT_STREAM, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(&bebob->tx_stream);
                amdtp_stream_destroy(&bebob->rx_stream);
@@ -572,7 +580,7 @@ end:
 
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
 {
-       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
        struct amdtp_stream *master, *slave;
        enum cip_flags sync_mode;
        unsigned int curr_rate;
@@ -864,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
                }
        }
 
-       if (formation[i].pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+       if (formation[i].pcm  > AM824_MAX_CHANNELS_FOR_PCM ||
+           formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
                return -ENOSYS;
 
        return 0;
@@ -959,7 +967,7 @@ end:
 
 int snd_bebob_stream_discover(struct snd_bebob *bebob)
 {
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
        enum avc_bridgeco_plug_type type;
        unsigned int i;
index 9242e33d2cf1c1d26dc83680315c82c006332164..c38358b82ada0ac909ff9e999ee3f57761b5a632 100644 (file)
@@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
        return 0;
 }
 
-static struct snd_bebob_rate_spec phase_series_rate_spec = {
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
 
 /* PHASE 88 Rack FW */
-static struct snd_bebob_clock_spec phase88_rack_clk = {
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
        .num    = ARRAY_SIZE(phase88_rack_clk_src_types),
        .types  = phase88_rack_clk_src_types,
        .get    = &phase88_rack_clk_src_get,
 };
-struct snd_bebob_spec phase88_rack_spec = {
+const struct snd_bebob_spec phase88_rack_spec = {
        .clock  = &phase88_rack_clk,
        .rate   = &phase_series_rate_spec,
        .meter  = NULL
 };
 
 /* 'PHASE 24 FW' and 'PHASE X24 FW' */
-static struct snd_bebob_clock_spec phase24_series_clk = {
+static const struct snd_bebob_clock_spec phase24_series_clk = {
        .num    = ARRAY_SIZE(phase24_series_clk_src_types),
        .types  = phase24_series_clk_src_types,
        .get    = &phase24_series_clk_src_get,
 };
-struct snd_bebob_spec phase24_series_spec = {
+const struct snd_bebob_spec phase24_series_spec = {
        .clock  = &phase24_series_clk,
        .rate   = &phase_series_rate_spec,
        .meter  = NULL
index 58101702410b64b17e1733fc3a643221f9d62e86..90d4404f77ce07d1ec98af09aea4463d7fdce25d 100644 (file)
@@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id)
 
        return 0;
 }
-static struct snd_bebob_clock_spec clock_spec = {
+static const struct snd_bebob_clock_spec clock_spec = {
        .num    = ARRAY_SIZE(clk_src_types),
        .types  = clk_src_types,
        .get    = &clk_src_get,
 };
-static struct snd_bebob_rate_spec rate_spec = {
+static const struct snd_bebob_rate_spec rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_go_spec = {
        .clock  = &clock_spec,
        .rate   = &rate_spec,
        .meter  = NULL
index 9ef228ef7baf2728b4bd8fb9e932c0f3e00dc791..55b4be9b0034093bc221ff1955057f739e299520 100644 (file)
@@ -1,3 +1,3 @@
 snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
                 dice-pcm.o dice-hwdep.o dice.o
-obj-m += snd-dice.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
index fe43ce791f845dfccdb7e10ce6f9df72ffc1f549..151b09f240f280d14233817ff64ef77d14d3a31b 100644 (file)
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream,
                                          substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream,
                                          substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&dice->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&dice->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&dice->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&dice->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
 }
index 4e67b1da0fe6ffbe9350b0e17a71a87f2b03e73b..9b3431999fc8b6df8dac3b58c42d6e1604c617e4 100644 (file)
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
                   SNDRV_PCM_INFO_BLOCK_TRANSFER;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+               hw->formats = AM824_IN_PCM_FORMAT_BITS;
                stream = &dice->tx_stream;
                pcm_channels = dice->tx_channels;
        } else {
-               hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               hw->formats = AM824_OUT_PCM_FORMAT_BITS;
                stream = &dice->rx_stream;
                pcm_channels = dice->rx_channels;
        }
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 end:
        return err;
 }
@@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&dice->tx_stream,
-                                   params_format(hw_params));
+       amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&dice->rx_stream,
-                                   params_format(hw_params));
+       amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 07dbd01d7a6bd336d901fa78b83365b44307b1a0..a6a39f7ef58d8e32cf6e6ff60d45bcbbb23b1e04 100644 (file)
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
 static void release_resources(struct snd_dice *dice,
                              struct fw_iso_resources *resources)
 {
-       unsigned int channel;
+       __be32 channel;
 
        /* Reset channel number */
        channel = cpu_to_be32((u32)-1);
        if (resources == &dice->tx_resources)
                snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                             &channel, 4);
+                                             &channel, sizeof(channel));
        else
                snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                             &channel, 4);
+                                             &channel, sizeof(channel));
 
        fw_iso_resources_free(resources);
 }
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
                          struct fw_iso_resources *resources,
                          unsigned int max_payload_bytes)
 {
-       unsigned int channel;
+       __be32 channel;
        int err;
 
        err = fw_iso_resources_allocate(resources, max_payload_bytes,
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
        channel = cpu_to_be32(resources->channel);
        if (resources == &dice->tx_resources)
                err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                                   &channel, 4);
+                                                   &channel, sizeof(channel));
        else
                err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                                   &channel, 4);
+                                                   &channel, sizeof(channel));
        if (err < 0)
                release_resources(dice, resources);
 end:
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
 {
        struct fw_iso_resources *resources;
        unsigned int i, mode, pcm_chs, midi_ports;
+       bool double_pcm_frames;
        int err;
 
        err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
         * For this quirk, blocking mode is required and PCM buffer size should
         * be aligned to SYT_INTERVAL.
         */
-       if (mode > 1) {
+       double_pcm_frames = mode > 1;
+       if (double_pcm_frames) {
                rate /= 2;
                pcm_chs *= 2;
-               stream->double_pcm_frames = true;
-       } else {
-               stream->double_pcm_frames = false;
        }
 
-       amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
-       if (mode > 1) {
+       err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+                                        double_pcm_frames);
+       if (err < 0)
+               goto end;
+
+       if (double_pcm_frames) {
                pcm_chs /= 2;
 
                for (i = 0; i < pcm_chs; i++) {
-                       stream->pcm_positions[i] = i * 2;
-                       stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+                       amdtp_am824_set_pcm_position(stream, i, i * 2);
+                       amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+                                                    i * 2 + 1);
                }
        }
 
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
                goto end;
        resources->channels_mask = 0x00000000ffffffffuLL;
 
-       err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+       err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                fw_iso_resources_destroy(resources);
index 70a111d7f428af4a0487f80cf350767935c8cac0..5d99436dfcaee5e70f3957a8f8b59be3d9e213d3 100644 (file)
@@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit)
        struct fw_csr_iterator it;
        int key, val, vendor = -1, model = -1, err;
        unsigned int category, i;
-       __be32 *pointers, value;
+       __be32 *pointers;
+       u32 value;
        __be32 version;
 
        pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
index ecf5dc86223544ed848fcfa9918630e74f17e7f9..101550ac1a242fc35af22f9f291b0fc2771d4532 100644 (file)
@@ -34,7 +34,7 @@
 #include <sound/pcm_params.h>
 #include <sound/rawmidi.h>
 
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../iso-resources.h"
 #include "../lib.h"
 #include "dice-interface.h"
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644 (file)
index 0000000..1123e68
--- /dev/null
@@ -0,0 +1,4 @@
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+                            digi00x-pcm.o digi00x-hwdep.o \
+                            digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644 (file)
index 0000000..b02a5e8
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM             0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824                0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND  3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS     8
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+       u8 carry;
+       u8 idx;
+       unsigned int off;
+};
+
+struct amdtp_dot {
+       unsigned int pcm_channels;
+       struct dot_state state;
+
+       unsigned int midi_ports;
+       /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
+       struct snd_rawmidi_substream *midi[2];
+       int midi_fifo_used[2];
+       int midi_fifo_limit;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static const u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+       /*
+        * the length of the added pattern only depends on the lower nibble
+        * of the last non-zero data
+        */
+       static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+                                  12, 10, 8, 6, 4, 2, 0};
+
+       /*
+        * the lower nibble of the salt. Interleaved sequence.
+        * this is walked backwards according to len[]
+        */
+       static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+                                  0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+       /* circular list for the salt's hi nibble. */
+       static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+                                  0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+       /*
+        * start offset for upper nibble mapping.
+        * note: 9 is /special/. In the case where the high nibble == 0x9,
+        * hir[] is not used and - coincidentally - the salt's hi nibble is
+        * 0x09 regardless of the offset.
+        */
+       static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+                                  3, 0x00, 14, 13, 8, 9, 10, 2};
+
+       const u8 ln = idx & 0xf;
+       const u8 hn = (idx >> 4) & 0xf;
+       const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+       if (len[ln] < off)
+               return 0x00;
+
+       return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+       u8 * const data = (u8 *) buffer;
+
+       if (data[MAGIC_DOT_BYTE] != 0x00) {
+               state->off = 0;
+               state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+       }
+       data[MAGIC_DOT_BYTE] ^= state->carry;
+       state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                            unsigned int pcm_channels)
+{
+       struct amdtp_dot *p = s->protocol;
+       int err;
+
+       if (amdtp_stream_running(s))
+               return -EBUSY;
+
+       /*
+        * A first data channel is for MIDI conformant data channel, the rest is
+        * Multi Bit Linear Audio data channel.
+        */
+       err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+       if (err < 0)
+               return err;
+
+       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+       p->pcm_channels = pcm_channels;
+
+       if (s->direction == AMDTP_IN_STREAM)
+               p->midi_ports = DOT_MIDI_IN_PORTS;
+       else
+               p->midi_ports = DOT_MIDI_OUT_PORTS;
+
+       /*
+        * We do not know the actual MIDI FIFO size of most devices.  Just
+        * assume two bytes, i.e., one byte can be received over the bus while
+        * the previous one is transmitted over MIDI.
+        * (The value here is adjusted for midi_ratelimit_per_packet().)
+        */
+       p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+       return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+                       dot_encode_step(&p->state, &buffer[c]);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
+                       dot_encode_step(&p->state, &buffer[c]);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[c]) << 8;
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+                             unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int channels, i, c;
+
+       channels = p->pcm_channels;
+
+       buffer++;
+       for (i = 0; i < data_blocks; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[c] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_dot *p = s->protocol;
+       int used;
+
+       used = p->midi_fifo_used[port];
+       if (used == 0)
+               return true;
+
+       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+       used = max(used, 0);
+       p->midi_fifo_used[port] = used;
+
+       return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+                                 unsigned int port, unsigned int count)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                               unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int f, port;
+       int len;
+       u8 *b;
+
+       for (f = 0; f < data_blocks; f++) {
+               port = (s->data_block_counter + f) % 8;
+               b = (u8 *)&buffer[0];
+
+               len = 0;
+               if (port < p->midi_ports &&
+                   midi_ratelimit_per_packet(s, port) &&
+                   p->midi[port] != NULL)
+                       len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+               if (len > 0) {
+                       b[3] = (0x10 << port) | len;
+                       midi_use_bytes(s, port, len);
+               } else {
+                       b[1] = 0;
+                       b[2] = 0;
+                       b[3] = 0;
+               }
+               b[0] = 0x80;
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                              unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int f, port, len;
+       u8 *b;
+
+       for (f = 0; f < data_blocks; f++) {
+               b = (u8 *)&buffer[0];
+               port = b[3] >> 4;
+               len = b[3] & 0x0f;
+
+               if (port < p->midi_ports && p->midi[port] && len > 0)
+                       snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                    struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /* This protocol delivers 24 bit data in 32bit data channel. */
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               return err;
+
+       return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                         struct snd_rawmidi_substream *midi)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       if (port < p->midi_ports)
+               ACCESS_ONCE(p->midi[port]) = midi;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks;
+       } else {
+               pcm_frames = 0;
+       }
+
+       read_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks;
+       } else {
+               write_pcm_silence(s, buffer, data_blocks);
+               pcm_frames = 0;
+       }
+
+       write_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+                enum amdtp_stream_direction dir)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+       enum cip_flags flags;
+
+       /* Use different mode between incoming/outgoing. */
+       if (dir == AMDTP_IN_STREAM) {
+               flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+               process_data_blocks = process_tx_data_blocks;
+       } else {
+               flags = CIP_BLOCKING;
+               process_data_blocks = process_rx_data_blocks;
+       }
+
+       return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+                                process_data_blocks, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       p->state.carry = 0x00;
+       p->state.idx = 0x00;
+       p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644 (file)
index 0000000..f188e47
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+                      loff_t *offset)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&dg00x->lock);
+
+       while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+               prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&dg00x->lock);
+               schedule();
+               finish_wait(&dg00x->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&dg00x->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       if (dg00x->dev_lock_changed) {
+               event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+               event.lock_status.status = (dg00x->dev_lock_count > 0);
+               dg00x->dev_lock_changed = false;
+
+               count = min_t(long, count, sizeof(event.lock_status));
+       } else {
+               event.digi00x_message.type =
+                                       SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+               event.digi00x_message.message = dg00x->msg;
+               dg00x->msg = 0;
+
+               count = min_t(long, count, sizeof(event.digi00x_message));
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+                              poll_table *wait)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &dg00x->hwdep_wait, wait);
+
+       spin_lock_irq(&dg00x->lock);
+       if (dg00x->dev_lock_changed || dg00x->msg)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&dg00x->lock);
+
+       return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(dg00x->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       if (dg00x->dev_lock_count == 0) {
+               dg00x->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       if (dg00x->dev_lock_count == -1) {
+               dg00x->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+
+       spin_lock_irq(&dg00x->lock);
+       if (dg00x->dev_lock_count == -1)
+               dg00x->dev_lock_count = 0;
+       spin_unlock_irq(&dg00x->lock);
+
+       return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(dg00x, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(dg00x);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(dg00x);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+       if (err < 0)
+               return err;
+
+       strcpy(hwdep->name, "Digi00x");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = dg00x;
+       hwdep->exclusive = true;
+
+       return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644 (file)
index 0000000..9aa8b46
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       int err;
+
+       /* This port is for asynchronous transaction. */
+       if (substream->number == 0)
+               return 0;
+
+       err = snd_dg00x_stream_lock_try(dg00x);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&dg00x->mutex);
+       dg00x->substreams_counter++;
+       err = snd_dg00x_stream_start_duplex(dg00x, 0);
+       mutex_unlock(&dg00x->mutex);
+       if (err < 0)
+               snd_dg00x_stream_lock_release(dg00x);
+
+       return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+       /* This port is for asynchronous transaction. */
+       if (substream->number == 0)
+               return 0;
+
+       mutex_lock(&dg00x->mutex);
+       dg00x->substreams_counter--;
+       snd_dg00x_stream_stop_duplex(dg00x);
+       mutex_unlock(&dg00x->mutex);
+
+       snd_dg00x_stream_lock_release(dg00x);
+       return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_dg00x *dg00x = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       /* This port is for asynchronous transaction. */
+       if (substrm->number == 0) {
+               if (up)
+                       dg00x->in_control = substrm;
+               else
+                       dg00x->in_control = NULL;
+       } else {
+               if (up)
+                       amdtp_dot_midi_trigger(&dg00x->tx_stream,
+                                              substrm->number - 1, substrm);
+               else
+                       amdtp_dot_midi_trigger(&dg00x->tx_stream,
+                                              substrm->number - 1, NULL);
+       }
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_dg00x *dg00x = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       /* This port is for asynchronous transaction. */
+       if (substrm->number == 0) {
+               if (up)
+                       snd_fw_async_midi_port_run(&dg00x->out_control,
+                                                  substrm);
+       } else {
+               if (up)
+                       amdtp_dot_midi_trigger(&dg00x->rx_stream,
+                                              substrm->number - 1, substrm);
+               else
+                       amdtp_dot_midi_trigger(&dg00x->rx_stream,
+                                              substrm->number - 1, NULL);
+       }
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+       .open           = midi_open,
+       .close          = midi_close,
+       .trigger        = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+       .open           = midi_open,
+       .close          = midi_close,
+       .trigger        = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dg00x *dg00x,
+                                    struct snd_rawmidi_str *str)
+{
+       struct snd_rawmidi_substream *subs;
+
+       list_for_each_entry(subs, &str->substreams, list) {
+               if (subs->number > 0)
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                dg00x->card->shortname, subs->number);
+               else
+                       /* This port is for asynchronous transaction. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s control",
+                                dg00x->card->shortname);
+       }
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_str *str;
+       int err;
+
+       err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
+                       DOT_MIDI_OUT_PORTS + 1, DOT_MIDI_IN_PORTS + 1, &rmidi);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi->name, sizeof(rmidi->name),
+                "%s MIDI", dg00x->card->shortname);
+       rmidi->private_data = dg00x;
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                           &midi_capture_ops);
+       str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+       set_midi_substream_names(dg00x, str);
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &midi_playback_ops);
+       str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+       set_midi_substream_names(dg00x, str);
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644 (file)
index 0000000..cac28f7
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *r =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       const struct snd_interval *c =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1,
+       };
+       unsigned int i;
+
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (!snd_interval_test(c,
+                                      snd_dg00x_stream_pcm_channels[i]))
+                       continue;
+
+               t.min = min(t.min, snd_dg00x_stream_rates[i]);
+               t.max = max(t.max, snd_dg00x_stream_rates[i]);
+       }
+
+       return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+                           struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       const struct snd_interval *r =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1,
+       };
+       unsigned int i;
+
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+                       continue;
+
+               t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+               t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+       }
+
+       return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+                             struct snd_pcm_substream *substream)
+{
+       static const struct snd_pcm_hardware hardware = {
+               .info = SNDRV_PCM_INFO_BATCH |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_JOINT_DUPLEX |
+                       SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID,
+               .rates = SNDRV_PCM_RATE_44100 |
+                        SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 |
+                        SNDRV_PCM_RATE_96000,
+               .rate_min = 44100,
+               .rate_max = 96000,
+               .channels_min = 10,
+               .channels_max = 18,
+               .period_bytes_min = 4 * 18,
+               .period_bytes_max = 4 * 18 * 2048,
+               .buffer_bytes_max = 4 * 18 * 2048 * 2,
+               .periods_min = 2,
+               .periods_max = UINT_MAX,
+       };
+       struct amdtp_stream *s;
+       int err;
+
+       substream->runtime->hw = hardware;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+               s = &dg00x->tx_stream;
+       } else {
+               substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
+                                                SNDRV_PCM_FMTBIT_S32;
+               s = &dg00x->rx_stream;
+       }
+
+       err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels, NULL,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               return err;
+
+       err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                 SNDRV_PCM_HW_PARAM_RATE,
+                                 hw_rule_rate, NULL,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               return err;
+
+       return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       enum snd_dg00x_clock clock;
+       bool detect;
+       unsigned int rate;
+       int err;
+
+       err = snd_dg00x_stream_lock_try(dg00x);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(dg00x, substream);
+       if (err < 0)
+               goto err_locked;
+
+       /* Check current clock source. */
+       err = snd_dg00x_stream_get_clock(dg00x, &clock);
+       if (err < 0)
+               goto err_locked;
+       if (clock != SND_DG00X_CLOCK_INTERNAL) {
+               err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+               if (err < 0)
+                       goto err_locked;
+               if (!detect) {
+                       err = -EBUSY;
+                       goto err_locked;
+               }
+       }
+
+       if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+           amdtp_stream_pcm_running(&dg00x->rx_stream) ||
+           amdtp_stream_pcm_running(&dg00x->tx_stream)) {
+               err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+               if (err < 0)
+                       goto err_locked;
+               substream->runtime->hw.rate_min = rate;
+               substream->runtime->hw.rate_max = rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_dg00x_stream_lock_release(dg00x);
+       return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       snd_dg00x_stream_lock_release(dg00x);
+
+       return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&dg00x->mutex);
+               dg00x->substreams_counter++;
+               mutex_unlock(&dg00x->mutex);
+       }
+
+       amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&dg00x->mutex);
+               dg00x->substreams_counter++;
+               mutex_unlock(&dg00x->mutex);
+       }
+
+       amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       mutex_lock(&dg00x->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               dg00x->substreams_counter--;
+
+       snd_dg00x_stream_stop_duplex(dg00x);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       mutex_lock(&dg00x->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               dg00x->substreams_counter--;
+
+       snd_dg00x_stream_stop_duplex(dg00x);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&dg00x->mutex);
+
+       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&dg00x->mutex);
+
+       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       if (err >= 0) {
+               amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+               amdtp_dot_reset(&dg00x->rx_stream);
+       }
+
+       mutex_unlock(&dg00x->mutex);
+
+       return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_dg00x *dg00x = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_dg00x *dg00x = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+
+       pcm->private_data = dg00x;
+       snprintf(pcm->name, sizeof(pcm->name),
+                "%s PCM", dg00x->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+       return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644 (file)
index 0000000..a1d601f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+                                 enum snd_dg00x_optical_mode *mode)
+{
+       __be32 data;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+                                &data, sizeof(data), 0);
+       if (err >= 0)
+               *mode = be32_to_cpu(data) & 0x01;
+
+       return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buf)
+{
+       static const char *const source_name[] = {
+               [SND_DG00X_CLOCK_INTERNAL] = "internal",
+               [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+               [SND_DG00X_CLOCK_ADAT] = "adat",
+               [SND_DG00X_CLOCK_WORD] = "word clock",
+       };
+       static const char *const optical_name[] = {
+               [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+               [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+       };
+       struct snd_dg00x *dg00x = entry->private_data;
+       enum snd_dg00x_optical_mode mode;
+       unsigned int rate;
+       enum snd_dg00x_clock clock;
+       bool detect;
+
+       if (get_optical_iface_mode(dg00x, &mode) < 0)
+               return;
+       if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+               return;
+       if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+               return;
+
+       snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+       snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+       snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+       if (clock == SND_DG00X_CLOCK_INTERNAL)
+               return;
+
+       if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+               return;
+       snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+       if (!detect)
+               return;
+
+       if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+               snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+       struct snd_info_entry *root, *entry;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(dg00x->card, "firewire",
+                                         dg00x->card->proc_root);
+       if (root == NULL)
+               return;
+
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+       if (entry == NULL) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+       if (snd_info_register(entry) < 0) {
+               snd_info_free_entry(entry);
+               snd_info_free_entry(root);
+       }
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644 (file)
index 0000000..4d3b4eb
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+       [SND_DG00X_RATE_44100] = 44100,
+       [SND_DG00X_RATE_48000] = 48000,
+       [SND_DG00X_RATE_88200] = 88200,
+       [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+       /* Analog/ADAT/SPDIF */
+       [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+       [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+       /* Analog/SPDIF */
+       [SND_DG00X_RATE_88200] = (8 + 2),
+       [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = be32_to_cpu(reg) & 0x0f;
+       if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+               *rate = snd_dg00x_stream_rates[data];
+       else
+               err = -EIO;
+
+       return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       __be32 reg;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+               if (rate == snd_dg00x_stream_rates[i])
+                       break;
+       }
+       if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+               return -EINVAL;
+
+       reg = cpu_to_be32(i);
+       return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+                                 &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+                              enum snd_dg00x_clock *clock)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       *clock = be32_to_cpu(reg) & 0x0f;
+       if (*clock >= SND_DG00X_CLOCK_COUNT)
+               err = -EIO;
+
+       return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+                                &reg, sizeof(reg), 0);
+       if (err >= 0)
+               *detect = be32_to_cpu(reg) > 0;
+
+       return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+                                      unsigned int *rate)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = be32_to_cpu(reg) & 0x0f;
+       if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+               *rate = snd_dg00x_stream_rates[data];
+       /* This means desync. */
+       else
+               err = -EBUSY;
+
+       return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+       __be32 data = cpu_to_be32(0x00000003);
+
+       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+                          &data, sizeof(data), 0);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+       __be32 data;
+       u32 curr;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               goto error;
+       curr = be32_to_cpu(data);
+
+       if (curr == 0)
+               curr = 2;
+
+       curr--;
+       while (curr > 0) {
+               data = cpu_to_be32(curr);
+               err = snd_fw_transaction(dg00x->unit,
+                                        TCODE_WRITE_QUADLET_REQUEST,
+                                        DG00X_ADDR_BASE +
+                                        DG00X_OFFSET_STREAMING_SET,
+                                        &data, sizeof(data), 0);
+               if (err < 0)
+                       goto error;
+
+               msleep(20);
+               curr--;
+       }
+
+       return 0;
+error:
+       finish_session(dg00x);
+       return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+       __be32 data = 0;
+
+       /* Unregister isochronous channels for both direction. */
+       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                          &data, sizeof(data), 0);
+
+       /* Release isochronous resources. */
+       fw_iso_resources_free(&dg00x->tx_resources);
+       fw_iso_resources_free(&dg00x->rx_resources);
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       unsigned int i;
+       __be32 data;
+       int err;
+
+       /* Check sampling rate. */
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (snd_dg00x_stream_rates[i] == rate)
+                       break;
+       }
+       if (i == SND_DG00X_RATE_COUNT)
+               return -EINVAL;
+
+       /* Keep resources for out-stream. */
+       err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+                                      snd_dg00x_stream_pcm_channels[i]);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&dg00x->rx_resources,
+                               amdtp_stream_get_max_payload(&dg00x->rx_stream),
+                               fw_parent_device(dg00x->unit)->max_speed);
+       if (err < 0)
+               return err;
+
+       /* Keep resources for in-stream. */
+       err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+                                      snd_dg00x_stream_pcm_channels[i]);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&dg00x->tx_resources,
+                               amdtp_stream_get_max_payload(&dg00x->tx_stream),
+                               fw_parent_device(dg00x->unit)->max_speed);
+       if (err < 0)
+               goto error;
+
+       /* Register isochronous channels for both direction. */
+       data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+                          dg00x->rx_resources.channel);
+       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       release_resources(dg00x);
+       return err;
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       /* For out-stream. */
+       err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+       if (err < 0)
+               goto error;
+       err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
+       if (err < 0)
+               goto error;
+
+       /* For in-stream. */
+       err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+       if (err < 0)
+               goto error;
+       err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       snd_dg00x_stream_destroy_duplex(dg00x);
+       return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+       amdtp_stream_destroy(&dg00x->rx_stream);
+       fw_iso_resources_destroy(&dg00x->rx_resources);
+
+       amdtp_stream_destroy(&dg00x->tx_stream);
+       fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err = 0;
+
+       if (dg00x->substreams_counter == 0)
+               goto end;
+
+       /* Check current sampling rate. */
+       err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+       if (err < 0)
+               goto error;
+       if (rate == 0)
+               rate = curr_rate;
+       if (curr_rate != rate ||
+           amdtp_streaming_error(&dg00x->tx_stream) ||
+           amdtp_streaming_error(&dg00x->rx_stream)) {
+               finish_session(dg00x);
+
+               amdtp_stream_stop(&dg00x->tx_stream);
+               amdtp_stream_stop(&dg00x->rx_stream);
+               release_resources(dg00x);
+       }
+
+       /*
+        * No packets are transmitted without receiving packets, reagardless of
+        * which source of clock is used.
+        */
+       if (!amdtp_stream_running(&dg00x->rx_stream)) {
+               err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+               if (err < 0)
+                       goto error;
+
+               err = keep_resources(dg00x, rate);
+               if (err < 0)
+                       goto error;
+
+               err = begin_session(dg00x);
+               if (err < 0)
+                       goto error;
+
+               err = amdtp_stream_start(&dg00x->rx_stream,
+                               dg00x->rx_resources.channel,
+                               fw_parent_device(dg00x->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
+                                             CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       /*
+        * The value of SYT field in transmitted packets is always 0x0000. Thus,
+        * duplex streams with timestamp synchronization cannot be built.
+        */
+       if (!amdtp_stream_running(&dg00x->tx_stream)) {
+               err = amdtp_stream_start(&dg00x->tx_stream,
+                               dg00x->tx_resources.channel,
+                               fw_parent_device(dg00x->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
+                                             CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+end:
+       return err;
+error:
+       finish_session(dg00x);
+
+       amdtp_stream_stop(&dg00x->tx_stream);
+       amdtp_stream_stop(&dg00x->rx_stream);
+       release_resources(dg00x);
+
+       return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+       if (dg00x->substreams_counter > 0)
+               return;
+
+       amdtp_stream_stop(&dg00x->tx_stream);
+       amdtp_stream_stop(&dg00x->rx_stream);
+       finish_session(dg00x);
+       release_resources(dg00x);
+
+       /*
+        * Just after finishing the session, the device may lost transmitting
+        * functionality for a short time.
+        */
+       msleep(50);
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+       fw_iso_resources_update(&dg00x->tx_resources);
+       fw_iso_resources_update(&dg00x->rx_resources);
+
+       amdtp_stream_update(&dg00x->tx_stream);
+       amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+       dg00x->dev_lock_changed = true;
+       wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       /* user land lock this */
+       if (dg00x->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (dg00x->dev_lock_count++ == 0)
+               snd_dg00x_stream_lock_changed(dg00x);
+       err = 0;
+end:
+       spin_unlock_irq(&dg00x->lock);
+       return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+       spin_lock_irq(&dg00x->lock);
+
+       if (WARN_ON(dg00x->dev_lock_count <= 0))
+               goto end;
+       if (--dg00x->dev_lock_count == 0)
+               snd_dg00x_stream_lock_changed(dg00x);
+end:
+       spin_unlock_irq(&dg00x->lock);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644 (file)
index 0000000..554324d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+       int bytes;
+
+       buf[0] = 0x80;
+       bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
+       if (bytes >= 0)
+               buf[3] = 0xc0 | bytes;
+
+       return bytes;
+}
+
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
+                               unsigned int length)
+{
+       struct snd_rawmidi_substream *substream;
+       unsigned int i;
+       unsigned int len;
+       u8 *b;
+
+       substream = ACCESS_ONCE(dg00x->in_control);
+       if (substream == NULL)
+               return;
+
+       length /= 4;
+
+       for (i = 0; i < length; i++) {
+               b = (u8 *)&buf[i];
+               len = b[3] & 0xf;
+               if (len > 0)
+                       snd_rawmidi_receive(dg00x->in_control, b + 1, len);
+       }
+}
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+                                  unsigned long long offset, __be32 *buf)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+       dg00x->msg = be32_to_cpu(*buf);
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+
+       wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       struct snd_dg00x *dg00x = callback_data;
+       __be32 *buf = (__be32 *)data;
+
+       if (offset == dg00x->async_handler.offset)
+               handle_unknown_message(dg00x, offset, buf);
+       else if (offset == dg00x->async_handler.offset + 4)
+               handle_midi_control(dg00x, buf, length);
+
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+       struct fw_device *device = fw_parent_device(dg00x->unit);
+       __be32 data[2];
+       int err;
+
+       /* Unknown. 4bytes. */
+       data[0] = cpu_to_be32((device->card->node_id << 16) |
+                             (dg00x->async_handler.offset >> 32));
+       data[1] = cpu_to_be32(dg00x->async_handler.offset);
+       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               return err;
+
+       /* Asynchronous transactions for MIDI control message. */
+       data[0] = cpu_to_be32((device->card->node_id << 16) |
+                             (dg00x->async_handler.offset >> 32));
+       data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
+       return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                 DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+                                 &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       int err;
+
+       dg00x->async_handler.length = 12;
+       dg00x->async_handler.address_callback = handle_message;
+       dg00x->async_handler.callback_data = dg00x;
+
+       err = fw_core_add_address_handler(&dg00x->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_dg00x_transaction_reregister(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
+                                         DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
+                                         4, fill_midi_message);
+       if (err < 0)
+               goto error;
+
+       return err;
+error:
+       fw_core_remove_address_handler(&dg00x->async_handler);
+       dg00x->async_handler.address_callback = NULL;
+       return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+       snd_fw_async_midi_port_destroy(&dg00x->out_control);
+       fw_core_remove_address_handler(&dg00x->async_handler);
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644 (file)
index 0000000..1f33b7a
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN      0x00a07e
+#define MODEL_DIGI00X          0x000002
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+       struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+       char name[32] = {0};
+       char *model;
+       int err;
+
+       err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+                           sizeof(name));
+       if (err < 0)
+               return err;
+
+       model = skip_spaces(name);
+
+       strcpy(dg00x->card->driver, "Digi00x");
+       strcpy(dg00x->card->shortname, model);
+       strcpy(dg00x->card->mixername, model);
+       snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+                "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+                fw_dev->config_rom[3], fw_dev->config_rom[4],
+                dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+       return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+       struct snd_dg00x *dg00x = card->private_data;
+
+       snd_dg00x_stream_destroy_duplex(dg00x);
+       snd_dg00x_transaction_unregister(dg00x);
+
+       fw_unit_put(dg00x->unit);
+
+       mutex_destroy(&dg00x->mutex);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_dg00x *dg00x;
+       int err;
+
+       /* create card */
+       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+                          sizeof(struct snd_dg00x), &card);
+       if (err < 0)
+               return err;
+       card->private_free = dg00x_card_free;
+
+       /* initialize myself */
+       dg00x = card->private_data;
+       dg00x->card = card;
+       dg00x->unit = fw_unit_get(unit);
+
+       mutex_init(&dg00x->mutex);
+       spin_lock_init(&dg00x->lock);
+       init_waitqueue_head(&dg00x->hwdep_wait);
+
+       err = name_card(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_stream_init_duplex(dg00x);
+       if (err < 0)
+               goto error;
+
+       snd_dg00x_proc_init(dg00x);
+
+       err = snd_dg00x_create_pcm_devices(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_create_midi_devices(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_create_hwdep_device(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_transaction_register(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(&unit->device, dg00x);
+
+       return err;
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+       struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+       snd_dg00x_transaction_reregister(dg00x);
+
+       mutex_lock(&dg00x->mutex);
+       snd_dg00x_stream_update_duplex(dg00x);
+       mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+       struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+       /* No need to wait for releasing card object in this context. */
+       snd_card_free_when_closed(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+       /* Both of 002/003 use the same ID. */
+       {
+               .match_flags = IEEE1394_MATCH_VENDOR_ID |
+                              IEEE1394_MATCH_MODEL_ID,
+               .vendor_id = VENDOR_DIGIDESIGN,
+               .model_id = MODEL_DIGI00X,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "snd-firewire-digi00x",
+               .bus = &fw_bus_type,
+       },
+       .probe    = snd_dg00x_probe,
+       .update   = snd_dg00x_update,
+       .remove   = snd_dg00x_remove,
+       .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+       return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+       driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644 (file)
index 0000000..907e739
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.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>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+       struct snd_card *card;
+       struct fw_unit *unit;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       struct amdtp_stream tx_stream;
+       struct fw_iso_resources tx_resources;
+
+       struct amdtp_stream rx_stream;
+       struct fw_iso_resources rx_resources;
+
+       unsigned int substreams_counter;
+
+       /* for uapi */
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* For asynchronous messages. */
+       struct fw_address_handler async_handler;
+       u32 msg;
+
+       /* For asynchronous MIDI controls. */
+       struct snd_rawmidi_substream *in_control;
+       struct snd_fw_async_midi_port out_control;
+};
+
+#define DG00X_ADDR_BASE                0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE   0x0000
+#define DG00X_OFFSET_STREAMING_SET     0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR     0x0008
+/* For LSB of the address              0x000c */
+/* unknown                             0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR      0x0014
+/* For LSB of the address              0x0018 */
+/* unknown                             0x001c */
+/* unknown                             0x0020 */
+/* not used                    0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS     0x0100
+/* unknown                             0x0104 */
+/* unknown                             0x0108 */
+/* unknown                             0x010c */
+#define DG00X_OFFSET_LOCAL_RATE                0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE     0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE      0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE    0x011c
+/* unknown                             0x0120 */
+/* Mixer control on/off                        0x0124 */
+/* unknown                             0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL   0x012c
+/* unknown                             0x0138 */
+#define DG00X_OFFSET_MMC               0x0400
+
+enum snd_dg00x_rate {
+       SND_DG00X_RATE_44100 = 0,
+       SND_DG00X_RATE_48000,
+       SND_DG00X_RATE_88200,
+       SND_DG00X_RATE_96000,
+       SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+       SND_DG00X_CLOCK_INTERNAL = 0,
+       SND_DG00X_CLOCK_SPDIF,
+       SND_DG00X_CLOCK_ADAT,
+       SND_DG00X_CLOCK_WORD,
+       SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+       SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+       SND_DG00X_OPT_IFACE_MODE_SPDIF,
+       SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS      1
+#define DOT_MIDI_OUT_PORTS     2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+                  enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                            unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                    struct snd_pcm_runtime *runtime);
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                         struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+                                      unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+                                   unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+                              enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+                                         bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
index 0619597e3a3f250b5e5b40d07d6c0e68de1a8ccb..cce19768f43d0a3f32c9d3c0187c1077f5bbfeab 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/delay.h>
 #include "fcp.h"
 #include "lib.h"
-#include "amdtp.h"
+#include "amdtp-stream.h"
 
 #define CTS_AVC 0x00
 
index 0c7440826db8e5b7f2e21d087da6fc0cc8ea50f4..15ef7f75a8ef123b5ee335fa4dcd4bef6c3c9122 100644 (file)
@@ -1,4 +1,4 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
                      fireworks_stream.o fireworks_proc.o fireworks_midi.o \
                      fireworks_pcm.o fireworks_hwdep.o fireworks.o
-obj-m += snd-fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
index c94a432f7cc653dca45316ea67bdc481b9e8e887..d5b19bc11e5999480f030e682fa4087cc91067ac 100644 (file)
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
        efw->midi_out_ports = hwinfo->midi_out_ports;
        efw->midi_in_ports = hwinfo->midi_in_ports;
 
-       if (hwinfo->amdtp_tx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+       if (hwinfo->amdtp_tx_pcm_channels    > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels    > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
                err = -ENOSYS;
                goto end;
        }
index 084d414b228cf425dc99440cc081d65459bbe175..c7cb7deafe48487fd802fbf5ce84e34c2f5ab2eb 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 #include "../lib.h"
 
index 166f80584c2a923da199990ca0b1920f646e52a0..94bab0476a65ce69011aaad0bf1ac8eac8a60e76 100644 (file)
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
                                    struct snd_efw_phys_meters *meters,
                                    unsigned int len)
 {
-       __be32 *buf = (__be32 *)meters;
+       u32 *buf = (u32 *)meters;
        unsigned int i;
        int err;
 
index cf9c6526043938aa905019c23992fdf496247403..fba01bbba4564be493af246a3d40957da2c1465d 100644 (file)
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&efw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&efw->tx_stream,
+               amdtp_am824_midi_trigger(&efw->tx_stream,
                                          substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&efw->tx_stream,
+               amdtp_am824_midi_trigger(&efw->tx_stream,
                                          substrm->number, NULL);
 
        spin_unlock_irqrestore(&efw->lock, flags);
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&efw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&efw->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&efw->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&efw->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&efw->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&efw->lock, flags);
 }
index c30b2ffa8dfb1db8354c25b4681938ac82627194..d27135bac5133e5d57bf5444e06f1e105559932e 100644 (file)
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                s = &efw->tx_stream;
                pcm_channels = efw->pcm_capture_channels;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                s = &efw->rx_stream;
                pcm_channels = efw->pcm_playback_channels;
        }
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
 end:
        return err;
 }
@@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&efw->capture_substreams);
-       amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&efw->playback_substreams);
-       amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 7e353f1f7bff359ea8845630aeff2521759e508f..759f6e3ed44ab08fb8d480b63dacf135ca734bab 100644 (file)
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+       err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                cmp_connection_destroy(conn);
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
                midi_ports = efw->midi_in_ports;
        }
 
-       amdtp_stream_set_parameters(stream, sampling_rate,
-                                   pcm_channels, midi_ports);
+       err = amdtp_am824_set_parameters(stream, sampling_rate,
+                                        pcm_channels, midi_ports, false);
+       if (err < 0)
+               goto end;
 
        /*  establish connection via CMP */
        err = cmp_connection_establish(conn,
index 7409edba9f06761b11d8582bd60fb43bcc8a0ff6..f80aafa44c89499ff92b0b3af0bc761ca49c0567 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/device.h>
 #include <linux/firewire.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include "lib.h"
 
 #define ERROR_RETRY_DELAY_MS   20
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
 }
 EXPORT_SYMBOL(snd_fw_transaction);
 
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+                                    void *data, size_t length,
+                                    void *callback_data)
+{
+       struct snd_fw_async_midi_port *port = callback_data;
+       struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+       /* This port is closed. */
+       if (substream == NULL)
+               return;
+
+       if (rcode == RCODE_COMPLETE)
+               snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+       else if (!rcode_is_permanent_error(rcode))
+               /* To start next transaction immediately for recovery. */
+               port->next_ktime = ktime_set(0, 0);
+       else
+               /* Don't continue processing. */
+               port->error = true;
+
+       port->idling = true;
+
+       if (!snd_rawmidi_transmit_empty(substream))
+               schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+       struct snd_fw_async_midi_port *port =
+                       container_of(work, struct snd_fw_async_midi_port, work);
+       struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+       int generation;
+       int type;
+
+       /* Under transacting or error state. */
+       if (!port->idling || port->error)
+               return;
+
+       /* Nothing to do. */
+       if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+               return;
+
+       /* Do it in next chance. */
+       if (ktime_after(port->next_ktime, ktime_get())) {
+               schedule_work(&port->work);
+               return;
+       }
+
+       /*
+        * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+        * Later, snd_rawmidi_transmit_ack() is called.
+        */
+       memset(port->buf, 0, port->len);
+       port->consume_bytes = port->fill(substream, port->buf);
+       if (port->consume_bytes <= 0) {
+               /* Do it in next chance, immediately. */
+               if (port->consume_bytes == 0) {
+                       port->next_ktime = ktime_set(0, 0);
+                       schedule_work(&port->work);
+               } else {
+                       /* Fatal error. */
+                       port->error = true;
+               }
+               return;
+       }
+
+       /* Calculate type of transaction. */
+       if (port->len == 4)
+               type = TCODE_WRITE_QUADLET_REQUEST;
+       else
+               type = TCODE_WRITE_BLOCK_REQUEST;
+
+       /* Set interval to next transaction. */
+       port->next_ktime = ktime_add_ns(ktime_get(),
+                               port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+       /* Start this transaction. */
+       port->idling = false;
+
+       /*
+        * In Linux FireWire core, when generation is updated with memory
+        * barrier, node id has already been updated. In this module, After
+        * this smp_rmb(), load/store instructions to memory are completed.
+        * Thus, both of generation and node id are available with recent
+        * values. This is a light-serialization solution to handle bus reset
+        * events on IEEE 1394 bus.
+        */
+       generation = port->parent->generation;
+       smp_rmb();
+
+       fw_send_request(port->parent->card, &port->transaction, type,
+                       port->parent->node_id, generation,
+                       port->parent->max_speed, port->addr,
+                       port->buf, port->len, async_midi_port_callback,
+                       port);
+}
+
+/**
+ * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port to initialize
+ * @unit: the target of the asynchronous transaction
+ * @addr: the address to which transactions are transferred
+ * @len: the length of transaction
+ * @fill: the callback function to fill given buffer, and returns the
+ *            number of consumed bytes for MIDI message.
+ *
+ */
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+               struct fw_unit *unit, u64 addr, unsigned int len,
+               snd_fw_async_midi_port_fill fill)
+{
+       port->len = DIV_ROUND_UP(len, 4) * 4;
+       port->buf = kzalloc(port->len, GFP_KERNEL);
+       if (port->buf == NULL)
+               return -ENOMEM;
+
+       port->parent = fw_parent_device(unit);
+       port->addr = addr;
+       port->fill = fill;
+       port->idling = true;
+       port->next_ktime = ktime_set(0, 0);
+       port->error = false;
+
+       INIT_WORK(&port->work, midi_port_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_init);
+
+/**
+ * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port structure
+ */
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
+{
+       snd_fw_async_midi_port_finish(port);
+       cancel_work_sync(&port->work);
+       kfree(port->buf);
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
+
 MODULE_DESCRIPTION("FireWire audio helper functions");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
index 02cfabc9c3c4ba53d18e7b827600491fd9c2df84..f3f6f84c48d69747cc0fa3bd815d986c79ca0d78 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <linux/firewire-constants.h>
 #include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
 
 struct fw_unit;
 
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
        return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
 }
 
+struct snd_fw_async_midi_port;
+typedef int (*snd_fw_async_midi_port_fill)(
+                               struct snd_rawmidi_substream *substream,
+                               u8 *buf);
+
+struct snd_fw_async_midi_port {
+       struct fw_device *parent;
+       struct work_struct work;
+       bool idling;
+       ktime_t next_ktime;
+       bool error;
+
+       u64 addr;
+       struct fw_transaction transaction;
+
+       u8 *buf;
+       unsigned int len;
+
+       struct snd_rawmidi_substream *substream;
+       snd_fw_async_midi_port_fill fill;
+       unsigned int consume_bytes;
+};
+
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+               struct fw_unit *unit, u64 addr, unsigned int len,
+               snd_fw_async_midi_port_fill fill);
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
+
+/**
+ * snd_fw_async_midi_port_run - run transactions for the async MIDI port
+ * @port: the asynchronous MIDI port
+ * @substream: the MIDI substream
+ */
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+                          struct snd_rawmidi_substream *substream)
+{
+       if (!port->error) {
+               port->substream = substream;
+               schedule_work(&port->work);
+       }
+}
+
+/**
+ * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
+ * @port: the asynchronous MIDI port
+ */
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+       port->substream = NULL;
+       port->error = false;
+}
+
 #endif
index a926850864f6706e45b3245d02c8962ddcd7647c..06ff50f4e6c0b19826caabbfcbd28998927a8929 100644 (file)
@@ -1,3 +1,3 @@
 snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
                 oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
-obj-m += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
index 540a303385169d011a3813aaa8483d7d8b67bbe0..8665e1043d41fa380a8a94f213098cbca29d87ef 100644 (file)
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&oxfw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&oxfw->tx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&oxfw->tx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&oxfw->tx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&oxfw->tx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&oxfw->lock, flags);
 }
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&oxfw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&oxfw->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&oxfw->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&oxfw->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&oxfw->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&oxfw->lock, flags);
 }
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
 
 int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
 {
-       struct snd_oxfw_stream_formation formation;
        struct snd_rawmidi *rmidi;
        struct snd_rawmidi_str *str;
-       u8 *format;
-       int i, err;
-
-       /* If its stream has MIDI conformant data channel, add one MIDI port */
-       for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
-               format = oxfw->tx_stream_formats[i];
-               if (format != NULL) {
-                       err = snd_oxfw_stream_parse_format(format, &formation);
-                       if (err >= 0 && formation.midi > 0)
-                               oxfw->midi_input_ports = 1;
-               }
-
-               format = oxfw->rx_stream_formats[i];
-               if (format != NULL) {
-                       err = snd_oxfw_stream_parse_format(format, &formation);
-                       if (err >= 0 && formation.midi > 0)
-                               oxfw->midi_output_ports = 1;
-               }
-       }
-       if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+       int err;
+
+       if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
                return 0;
 
        /* create midi ports */
index 9c73930d0278820a11fd46a2db2fb877b6a61a88..8d233417695d613141c2a49f44c279fc0b57b45e 100644 (file)
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                stream = &oxfw->tx_stream;
                formats = oxfw->tx_stream_formats;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                stream = &oxfw->rx_stream;
                formats = oxfw->rx_stream_formats;
        }
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 end:
        return err;
 }
@@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&oxfw->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&oxfw->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 77ad5b98e806fa694ddd5c0a4c2ae439c78aebf8..7cb5743c073bbed3f88d2452b6b6a7047f7619f3 100644 (file)
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
        }
 
        pcm_channels = formation.pcm;
-       midi_ports = DIV_ROUND_UP(formation.midi, 8);
+       midi_ports = formation.midi * 8;
 
        /* The stream should have one pcm channels at least */
        if (pcm_channels == 0) {
                err = -EINVAL;
                goto end;
        }
-       amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+       err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
+                                        false);
+       if (err < 0)
+               goto end;
 
        err = cmp_connection_establish(conn,
                                       amdtp_stream_get_max_payload(stream));
@@ -225,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+       err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                cmp_connection_destroy(conn);
@@ -238,9 +241,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
         * packets. As a result, next isochronous packet includes more data
         * blocks than IEC 61883-6 defines.
         */
-       if (stream == &oxfw->tx_stream)
+       if (stream == &oxfw->tx_stream) {
                oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
                                         CIP_JUMBO_PAYLOAD;
+               if (oxfw->wrong_dbs)
+                       oxfw->tx_stream.flags |= CIP_WRONG_DBS;
+       }
 end:
        return err;
 }
@@ -480,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
                }
        }
 
-       if (formation->pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+       if (formation->pcm  > AM824_MAX_CHANNELS_FOR_PCM ||
+           formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
                return -ENOSYS;
 
        return 0;
@@ -623,6 +629,9 @@ end:
 int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
 {
        u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+       struct snd_oxfw_stream_formation formation;
+       u8 *format;
+       unsigned int i;
        int err;
 
        /* the number of plugs for isoc in/out, ext in/out  */
@@ -642,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
                err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
                if (err < 0)
                        goto end;
+
+               for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+                       format = oxfw->tx_stream_formats[i];
+                       if (format == NULL)
+                               continue;
+                       err = snd_oxfw_stream_parse_format(format, &formation);
+                       if (err < 0)
+                               continue;
+
+                       /* Add one MIDI port. */
+                       if (formation.midi > 0)
+                               oxfw->midi_input_ports = 1;
+               }
+
                oxfw->has_output = true;
        }
 
        /* use iPCR[0] if exists */
-       if (plugs[0] > 0)
+       if (plugs[0] > 0) {
                err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+               if (err < 0)
+                       goto end;
+
+               for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+                       format = oxfw->rx_stream_formats[i];
+                       if (format == NULL)
+                               continue;
+                       err = snd_oxfw_stream_parse_format(format, &formation);
+                       if (err < 0)
+                               continue;
+
+                       /* Add one MIDI port. */
+                       if (formation.midi > 0)
+                               oxfw->midi_output_ports = 1;
+               }
+       }
 end:
        return err;
 }
index 8c6ce019f437c310043a3686b634def68c92e424..588b93f20c2e23e6955ca76c5d0b5076561b6fd5 100644 (file)
@@ -18,6 +18,9 @@
 #define VENDOR_GRIFFIN         0x001292
 #define VENDOR_BEHRINGER       0x001564
 #define VENDOR_LACIE           0x00d04b
+#define VENDOR_TASCAM          0x00022e
+
+#define MODEL_SATELLITE                0x00200f
 
 #define SPECIFIER_1394TA       0x00a02d
 #define VERSION_AVC            0x010001
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
        mutex_destroy(&oxfw->mutex);
 }
 
+static void detect_quirks(struct snd_oxfw *oxfw)
+{
+       struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+       struct fw_csr_iterator it;
+       int key, val;
+       int vendor, model;
+
+       /* Seek from Root Directory of Config ROM. */
+       vendor = model = 0;
+       fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+       while (fw_csr_iterator_next(&it, &key, &val)) {
+               if (key == CSR_VENDOR)
+                       vendor = val;
+               else if (key == CSR_MODEL)
+                       model = val;
+       }
+
+       /*
+        * Mackie Onyx Satellite with base station has a quirk to report a wrong
+        * value in 'dbs' field of CIP header against its format information.
+        */
+       if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
+               oxfw->wrong_dbs = true;
+
+       /*
+        * TASCAM FireOne has physical control and requires a pair of additional
+        * MIDI ports.
+        */
+       if (vendor == VENDOR_TASCAM) {
+               oxfw->midi_input_ports++;
+               oxfw->midi_output_ports++;
+       }
+}
+
 static int oxfw_probe(struct fw_unit *unit,
                       const struct ieee1394_device_id *id)
 {
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
+       detect_quirks(oxfw);
+
        err = name_card(oxfw);
        if (err < 0)
                goto error;
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
                .specifier_id   = SPECIFIER_1394TA,
                .version        = VERSION_AVC,
        },
+       /* TASCAM, FireOne */
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = VENDOR_TASCAM,
+               .model_id       = 0x800007,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
index cace5ad4fe76134a0fb4ece9a27f2c321021d998..8392c424ad1d53bb91ea04f7dc78f67362b2f543 100644 (file)
@@ -28,7 +28,7 @@
 #include "../fcp.h"
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 
 struct device_info {
@@ -49,6 +49,7 @@ struct snd_oxfw {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool wrong_dbs;
        bool has_output;
        u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
        u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644 (file)
index 0000000..0fc955d
--- /dev/null
@@ -0,0 +1,4 @@
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+                           tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+                           tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644 (file)
index 0000000..9dd0fcc
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX      0x1e
+#define AMDTP_FMT_TSCM_RX      0x3e
+
+struct amdtp_tscm {
+       unsigned int pcm_channels;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+       struct amdtp_tscm *p = s->protocol;
+       unsigned int data_channels;
+
+       if (amdtp_stream_running(s))
+               return -EBUSY;
+
+       data_channels = p->pcm_channels;
+
+       /* Packets in in-stream have extra 2 data channels. */
+       if (s->direction == AMDTP_IN_STREAM)
+               data_channels += 2;
+
+       return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32(*src);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32(*src << 16);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+                        struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       /* The first data channel is for event counter. */
+       buffer += 1;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[c]);
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+                             unsigned int data_blocks)
+{
+       struct amdtp_tscm *p = s->protocol;
+       unsigned int channels, i, c;
+
+       channels = p->pcm_channels;
+
+       for (i = 0; i < data_blocks; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[c] = 0x00000000;
+               buffer += s->data_block_quadlets;
+       }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                     struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /*
+        * Our implementation allows this protocol to deliver 24 bit sample in
+        * 32bit data channel.
+        */
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               return err;
+
+       return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_tscm *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+       struct snd_pcm_substream *pcm;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (data_blocks > 0 && pcm)
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+
+       /* A place holder for control messages. */
+
+       return data_blocks;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+       struct snd_pcm_substream *pcm;
+
+       /* This field is not used. */
+       *syt = 0x0000;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm)
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+       else
+               write_pcm_silence(s, buffer, data_blocks);
+
+       return data_blocks;
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+                   enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+       struct amdtp_tscm *p;
+       unsigned int fmt;
+       int err;
+
+       if (dir == AMDTP_IN_STREAM) {
+               fmt = AMDTP_FMT_TSCM_TX;
+               process_data_blocks = process_tx_data_blocks;
+       } else {
+               fmt = AMDTP_FMT_TSCM_RX;
+               process_data_blocks = process_rx_data_blocks;
+       }
+
+       err = amdtp_stream_init(s, unit, dir,
+                               CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
+                               process_data_blocks, sizeof(struct amdtp_tscm));
+       if (err < 0)
+               return 0;
+
+       /* Use fixed value for FDF field. */
+       s->fdf = 0x00;
+
+       /* This protocol uses fixed number of data channels for PCM samples. */
+       p = s->protocol;
+       p->pcm_channels = pcm_channels;
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644 (file)
index 0000000..131267c
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+                             long count)
+{
+       union snd_firewire_event event;
+
+       memset(&event, 0, sizeof(event));
+
+       event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+       event.lock_status.status = (tscm->dev_lock_count > 0);
+       tscm->dev_lock_changed = false;
+
+       count = min_t(long, count, sizeof(event.lock_status));
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+                      loff_t *offset)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&tscm->lock);
+
+       while (!tscm->dev_lock_changed) {
+               prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&tscm->lock);
+               schedule();
+               finish_wait(&tscm->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&tscm->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       count = hwdep_read_locked(tscm, buf, count);
+       spin_unlock_irq(&tscm->lock);
+
+       return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+                              poll_table *wait)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &tscm->hwdep_wait, wait);
+
+       spin_lock_irq(&tscm->lock);
+       if (tscm->dev_lock_changed)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&tscm->lock);
+
+       return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(tscm->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       if (tscm->dev_lock_count == 0) {
+               tscm->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&tscm->lock);
+
+       return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       if (tscm->dev_lock_count == -1) {
+               tscm->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&tscm->lock);
+
+       return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+
+       spin_lock_irq(&tscm->lock);
+       if (tscm->dev_lock_count == -1)
+               tscm->dev_lock_count = 0;
+       spin_unlock_irq(&tscm->lock);
+
+       return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(tscm, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(tscm);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(tscm);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+       if (err < 0)
+               return err;
+
+       strcpy(hwdep->name, "Tascam");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = tscm;
+       hwdep->exclusive = true;
+
+       return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644 (file)
index 0000000..41f8420
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+
+       /* Initialize internal status. */
+       tscm->running_status[substream->number] = 0;
+       tscm->on_sysex[substream->number] = 0;
+       return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+
+       snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+
+       return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_tscm *tscm = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tscm->lock, flags);
+
+       if (up)
+               tscm->tx_midi_substreams[substrm->number] = substrm;
+       else
+               tscm->tx_midi_substreams[substrm->number] = NULL;
+
+       spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_tscm *tscm = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tscm->lock, flags);
+
+       if (up)
+               snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+                                          substrm);
+
+       spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+       .open           = midi_capture_open,
+       .close          = midi_capture_close,
+       .trigger        = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+       .open           = midi_playback_open,
+       .close          = midi_playback_close,
+       .trigger        = midi_playback_trigger,
+};
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_str *stream;
+       struct snd_rawmidi_substream *subs;
+       int err;
+
+       err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+                             tscm->spec->midi_playback_ports,
+                             tscm->spec->midi_capture_ports,
+                             &rmidi);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi->name, sizeof(rmidi->name),
+                "%s MIDI", tscm->card->shortname);
+       rmidi->private_data = tscm;
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                           &midi_capture_ops);
+       stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+       /* Set port names for MIDI input. */
+       list_for_each_entry(subs, &stream->substreams, list) {
+               /* TODO: support virtual MIDI ports. */
+               if (subs->number < tscm->spec->midi_capture_ports) {
+                       /* Hardware MIDI ports. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                tscm->card->shortname, subs->number + 1);
+               }
+       }
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &midi_playback_ops);
+       stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+       /* Set port names for MIDI ourput. */
+       list_for_each_entry(subs, &stream->substreams, list) {
+               if (subs->number < tscm->spec->midi_playback_ports) {
+                       /* Hardware MIDI ports only. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                tscm->card->shortname, subs->number + 1);
+               }
+       }
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644 (file)
index 0000000..380d3db
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static void set_buffer_params(struct snd_pcm_hardware *hw)
+{
+       hw->period_bytes_min = 4 * hw->channels_min;
+       hw->period_bytes_max = hw->period_bytes_min * 2048;
+       hw->buffer_bytes_max = hw->period_bytes_max * 2;
+
+       hw->periods_min = 2;
+       hw->periods_max = UINT_MAX;
+}
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+                             struct snd_pcm_substream *substream)
+{
+       static const struct snd_pcm_hardware hardware = {
+               .info = SNDRV_PCM_INFO_BATCH |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_JOINT_DUPLEX |
+                       SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID,
+               .rates = SNDRV_PCM_RATE_44100 |
+                        SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 |
+                        SNDRV_PCM_RATE_96000,
+               .rate_min = 44100,
+               .rate_max = 96000,
+               .channels_min = 10,
+               .channels_max = 18,
+       };
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct amdtp_stream *stream;
+       unsigned int pcm_channels;
+
+       runtime->hw = hardware;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+               stream = &tscm->tx_stream;
+               pcm_channels = tscm->spec->pcm_capture_analog_channels;
+       } else {
+               runtime->hw.formats =
+                               SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
+               stream = &tscm->rx_stream;
+               pcm_channels = tscm->spec->pcm_playback_analog_channels;
+       }
+
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+       set_buffer_params(&runtime->hw);
+
+       return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       enum snd_tscm_clock clock;
+       unsigned int rate;
+       int err;
+
+       err = snd_tscm_stream_lock_try(tscm);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(tscm, substream);
+       if (err < 0)
+               goto err_locked;
+
+       err = snd_tscm_stream_get_clock(tscm, &clock);
+       if (clock != SND_TSCM_CLOCK_INTERNAL ||
+           amdtp_stream_pcm_running(&tscm->rx_stream) ||
+           amdtp_stream_pcm_running(&tscm->tx_stream)) {
+               err = snd_tscm_stream_get_rate(tscm, &rate);
+               if (err < 0)
+                       goto err_locked;
+               substream->runtime->hw.rate_min = rate;
+               substream->runtime->hw.rate_max = rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_tscm_stream_lock_release(tscm);
+       return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       snd_tscm_stream_lock_release(tscm);
+
+       return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&tscm->mutex);
+               tscm->substreams_counter++;
+               mutex_unlock(&tscm->mutex);
+       }
+
+       amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&tscm->mutex);
+               tscm->substreams_counter++;
+               mutex_unlock(&tscm->mutex);
+       }
+
+       amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       mutex_lock(&tscm->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               tscm->substreams_counter--;
+
+       snd_tscm_stream_stop_duplex(tscm);
+
+       mutex_unlock(&tscm->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       mutex_lock(&tscm->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               tscm->substreams_counter--;
+
+       snd_tscm_stream_stop_duplex(tscm);
+
+       mutex_unlock(&tscm->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&tscm->mutex);
+
+       err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+       mutex_unlock(&tscm->mutex);
+
+       return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&tscm->mutex);
+
+       err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+       mutex_unlock(&tscm->mutex);
+
+       return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_tscm *tscm = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_tscm *tscm = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&tscm->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+
+       pcm->private_data = tscm;
+       snprintf(pcm->name, sizeof(pcm->name),
+                "%s PCM", tscm->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644 (file)
index 0000000..bfd4a4c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+                              struct snd_info_buffer *buffer)
+{
+       struct snd_tscm *tscm = entry->private_data;
+       __be32 data;
+       unsigned int reg, fpga, arm, hw;
+       int err;
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       reg = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       fpga = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       arm = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       hw = be32_to_cpu(data);
+
+       snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+       snd_iprintf(buffer, "FPGA:     %d (0x%08x)\n", fpga & 0xffff, fpga);
+       snd_iprintf(buffer, "ARM:      %d (0x%08x)\n", arm & 0xffff, arm);
+       snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+                    const char *name,
+                    void (*op)(struct snd_info_entry *e,
+                               struct snd_info_buffer *b))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(tscm->card, name, root);
+       if (entry == NULL)
+               return;
+
+       snd_info_set_text_ops(entry, tscm, op);
+       if (snd_info_register(entry) < 0)
+               snd_info_free_entry(entry);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+       struct snd_info_entry *root;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(tscm->card, "firewire",
+                                         tscm->card->proc_root);
+       if (root == NULL)
+               return;
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644 (file)
index 0000000..0e6dd5c
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CALLBACK_TIMEOUT 500
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+                                &reg, sizeof(reg), 0);
+       if (err >= 0)
+               *data = be32_to_cpu(reg);
+
+       return err;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+                    enum snd_tscm_clock clock)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = get_clock(tscm, &data);
+       if (err < 0)
+               return err;
+       data &= 0x0000ffff;
+
+       if (rate > 0) {
+               data &= 0x000000ff;
+               /* Base rate. */
+               if ((rate % 44100) == 0) {
+                       data |= 0x00000100;
+                       /* Multiplier. */
+                       if (rate / 44100 == 2)
+                               data |= 0x00008000;
+               } else if ((rate % 48000) == 0) {
+                       data |= 0x00000200;
+                       /* Multiplier. */
+                       if (rate / 48000 == 2)
+                               data |= 0x00008000;
+               } else {
+                       return -EAGAIN;
+               }
+       }
+
+       if (clock != INT_MAX) {
+               data &= 0x0000ff00;
+               data |= clock + 1;
+       }
+
+       reg = cpu_to_be32(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       if (data & 0x00008000)
+               reg = cpu_to_be32(0x0000001a);
+       else
+               reg = cpu_to_be32(0x0000000d);
+
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+                                 &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+       u32 data = 0x0;
+       unsigned int trials = 0;
+       int err;
+
+       while (data == 0x0 || trials++ < 5) {
+               err = get_clock(tscm, &data);
+               if (err < 0)
+                       return err;
+
+               data = (data & 0xff000000) >> 24;
+       }
+
+       /* Check base rate. */
+       if ((data & 0x0f) == 0x01)
+               *rate = 44100;
+       else if ((data & 0x0f) == 0x02)
+               *rate = 48000;
+       else
+               return -EAGAIN;
+
+       /* Check multiplier. */
+       if ((data & 0xf0) == 0x80)
+               *rate *= 2;
+       else if ((data & 0xf0) != 0x00)
+               return -EAGAIN;
+
+       return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+       u32 data;
+       int err;
+
+       err = get_clock(tscm, &data);
+       if (err < 0)
+               return err;
+
+       *clock = ((data & 0x00ff0000) >> 16) - 1;
+       if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+               return -EIO;
+
+       return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       u32 data;
+       unsigned int i;
+       int err;
+
+       data = 0;
+       for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+               data |= BIT(i);
+       if (tscm->spec->has_adat)
+               data |= 0x0000ff00;
+       if (tscm->spec->has_spdif)
+               data |= 0x00030000;
+
+       reg = cpu_to_be32(data);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = 0;
+       for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+               data |= BIT(i);
+       if (tscm->spec->has_adat)
+               data |= 0x0000ff00;
+       if (tscm->spec->has_spdif)
+               data |= 0x00030000;
+
+       reg = cpu_to_be32(data);
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+                                 &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+       __be32 reg;
+       int err;
+
+       /* Set an option for unknown purpose. */
+       reg = cpu_to_be32(0x00200000);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       err = enable_data_channels(tscm);
+       if (err < 0)
+               return err;
+
+       return set_clock(tscm, rate, INT_MAX);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+       __be32 reg;
+
+       reg = 0;
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+                          &reg, sizeof(reg), 0);
+
+       reg = 0;
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+                          &reg, sizeof(reg), 0);
+
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       int err;
+
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Set an option for unknown purpose. */
+       reg = cpu_to_be32(0x00002000);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Start multiplexing PCM samples on packets. */
+       reg = cpu_to_be32(0x00000001);
+       return snd_fw_transaction(tscm->unit,
+                                 TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+                                 &reg, sizeof(reg), 0);
+}
+
+static void release_resources(struct snd_tscm *tscm)
+{
+       __be32 reg;
+
+       /* Unregister channels. */
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+                          &reg, sizeof(reg), 0);
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+                          &reg, sizeof(reg), 0);
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+                          &reg, sizeof(reg), 0);
+
+       /* Release isochronous resources. */
+       fw_iso_resources_free(&tscm->tx_resources);
+       fw_iso_resources_free(&tscm->rx_resources);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+{
+       __be32 reg;
+       int err;
+
+       /* Keep resources for in-stream. */
+       err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&tscm->tx_resources,
+                       amdtp_stream_get_max_payload(&tscm->tx_stream),
+                       fw_parent_device(tscm->unit)->max_speed);
+       if (err < 0)
+               goto error;
+
+       /* Keep resources for out-stream. */
+       err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&tscm->rx_resources,
+                       amdtp_stream_get_max_payload(&tscm->rx_stream),
+                       fw_parent_device(tscm->unit)->max_speed);
+       if (err < 0)
+               return err;
+
+       /* Register the isochronous channel for transmitting stream. */
+       reg = cpu_to_be32(tscm->tx_resources.channel);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       /* Unknown */
+       reg = cpu_to_be32(0x00000002);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       /* Register the isochronous channel for receiving stream. */
+       reg = cpu_to_be32(tscm->rx_resources.channel);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       release_resources(tscm);
+       return err;
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+       unsigned int pcm_channels;
+       int err;
+
+       /* For out-stream. */
+       err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
+       if (err < 0)
+               return err;
+       pcm_channels = tscm->spec->pcm_playback_analog_channels;
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
+                             pcm_channels);
+       if (err < 0)
+               return err;
+
+       /* For in-stream. */
+       err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
+       if (err < 0)
+               return err;
+       pcm_channels = tscm->spec->pcm_capture_analog_channels;
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
+                             pcm_channels);
+       if (err < 0)
+               amdtp_stream_destroy(&tscm->rx_stream);
+
+       return 0;
+}
+
+/* At bus reset, streaming is stopped and some registers are clear. */
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+       amdtp_stream_pcm_abort(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->tx_stream);
+
+       amdtp_stream_pcm_abort(&tscm->rx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+       amdtp_stream_destroy(&tscm->rx_stream);
+       amdtp_stream_destroy(&tscm->tx_stream);
+
+       fw_iso_resources_destroy(&tscm->rx_resources);
+       fw_iso_resources_destroy(&tscm->tx_resources);
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
+
+       if (tscm->substreams_counter == 0)
+               return 0;
+
+       err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+       if (err < 0)
+               return err;
+       if (curr_rate != rate ||
+           amdtp_streaming_error(&tscm->tx_stream) ||
+           amdtp_streaming_error(&tscm->rx_stream)) {
+               finish_session(tscm);
+
+               amdtp_stream_stop(&tscm->tx_stream);
+               amdtp_stream_stop(&tscm->rx_stream);
+
+               release_resources(tscm);
+       }
+
+       if (!amdtp_stream_running(&tscm->tx_stream)) {
+               amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
+                                     &tscm->tx_stream, &tscm->rx_stream);
+               err = keep_resources(tscm, rate);
+               if (err < 0)
+                       goto error;
+
+               err = set_stream_formats(tscm, rate);
+               if (err < 0)
+                       goto error;
+
+               err = begin_session(tscm);
+               if (err < 0)
+                       goto error;
+
+               err = amdtp_stream_start(&tscm->tx_stream,
+                               tscm->tx_resources.channel,
+                               fw_parent_device(tscm->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       if (!amdtp_stream_running(&tscm->rx_stream)) {
+               err = amdtp_stream_start(&tscm->rx_stream,
+                               tscm->rx_resources.channel,
+                               fw_parent_device(tscm->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       return 0;
+error:
+       amdtp_stream_stop(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+
+       finish_session(tscm);
+       release_resources(tscm);
+
+       return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+       if (tscm->substreams_counter > 0)
+               return;
+
+       amdtp_stream_stop(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+
+       finish_session(tscm);
+       release_resources(tscm);
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+       tscm->dev_lock_changed = true;
+       wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       /* user land lock this */
+       if (tscm->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (tscm->dev_lock_count++ == 0)
+               snd_tscm_stream_lock_changed(tscm);
+       err = 0;
+end:
+       spin_unlock_irq(&tscm->lock);
+       return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+       spin_lock_irq(&tscm->lock);
+
+       if (WARN_ON(tscm->dev_lock_count <= 0))
+               goto end;
+       if (--tscm->dev_lock_count == 0)
+               snd_tscm_stream_lock_changed(tscm);
+end:
+       spin_unlock_irq(&tscm->lock);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644 (file)
index 0000000..904ce03
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+       switch (status) {
+       case 0xf6:      /* Tune request. */
+       case 0xf8:      /* Timing clock. */
+       case 0xfa:      /* Start. */
+       case 0xfb:      /* Continue. */
+       case 0xfc:      /* Stop. */
+       case 0xfe:      /* Active sensing. */
+       case 0xff:      /* System reset. */
+               return 1;
+       case 0xf1:      /* MIDI time code quarter frame. */
+       case 0xf3:      /* Song select. */
+               return 2;
+       case 0xf2:      /* Song position pointer. */
+               return 3;
+       case 0xf0:      /* Exclusive. */
+               return 0;
+       case 0xf7:      /* End of exclusive. */
+               break;
+       case 0xf4:      /* Undefined. */
+       case 0xf5:      /* Undefined. */
+       case 0xf9:      /* Undefined. */
+       case 0xfd:      /* Undefined. */
+               break;
+       default:
+               switch (status & 0xf0) {
+               case 0x80:      /* Note on. */
+               case 0x90:      /* Note off. */
+               case 0xa0:      /* Polyphonic key pressure. */
+               case 0xb0:      /* Control change and Mode change. */
+               case 0xe0:      /* Pitch bend change. */
+                       return 3;
+               case 0xc0:      /* Program change. */
+               case 0xd0:      /* Channel pressure. */
+                       return 2;
+               default:
+               break;
+               }
+       break;
+       }
+
+       return -EINVAL;
+}
+
+static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+       unsigned int port = substream->number;
+       int i, len, consume;
+       u8 *label, *msg;
+       u8 status;
+
+       /* The first byte is used for label, the rest for MIDI bytes. */
+       label = buf;
+       msg = buf + 1;
+
+       consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+       if (consume == 0)
+               return 0;
+
+       /* On exclusive message. */
+       if (tscm->on_sysex[port]) {
+               /* Seek the end of exclusives. */
+               for (i = 0; i < consume; ++i) {
+                       if (msg[i] == 0xf7) {
+                               tscm->on_sysex[port] = false;
+                               break;
+                       }
+               }
+
+               /* At the end of exclusive message, use label 0x07. */
+               if (!tscm->on_sysex[port]) {
+                       consume = i + 1;
+                       *label = (port << 4) | 0x07;
+               /* During exclusive message, use label 0x04. */
+               } else if (consume == 3) {
+                       *label = (port << 4) | 0x04;
+               /* We need to fill whole 3 bytes. Go to next change. */
+               } else {
+                       return 0;
+               }
+
+               len = consume;
+       } else {
+               /* The beginning of exclusives. */
+               if (msg[0] == 0xf0) {
+                       /* Transfer it in next chance in another condition. */
+                       tscm->on_sysex[port] = true;
+                       return 0;
+               } else {
+                       /* On running-status. */
+                       if ((msg[0] & 0x80) != 0x80)
+                               status = tscm->running_status[port];
+                       else
+                               status = msg[0];
+
+                       /* Calculate consume bytes. */
+                       len = calculate_message_bytes(status);
+                       if (len <= 0)
+                               return 0;
+
+                       /* On running-status. */
+                       if ((msg[0] & 0x80) != 0x80) {
+                               /* Enough MIDI bytes were not retrieved. */
+                               if (consume < len - 1)
+                                       return 0;
+                               consume = len - 1;
+
+                               msg[2] = msg[1];
+                               msg[1] = msg[0];
+                               msg[0] = tscm->running_status[port];
+                       } else {
+                               /* Enough MIDI bytes were not retrieved. */
+                               if (consume < len)
+                                       return 0;
+                               consume = len;
+
+                               tscm->running_status[port] = msg[0];
+                       }
+               }
+
+               *label = (port << 4) | (msg[0] >> 4);
+       }
+
+       if (len > 0 && len < 3)
+               memset(msg + len, 0, 3 - len);
+
+       return consume;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       struct snd_tscm *tscm = callback_data;
+       u32 *buf = (u32 *)data;
+       unsigned int messages;
+       unsigned int i;
+       unsigned int port;
+       struct snd_rawmidi_substream *substream;
+       u8 *b;
+       int bytes;
+
+       if (offset != tscm->async_handler.offset)
+               goto end;
+
+       messages = length / 8;
+       for (i = 0; i < messages; i++) {
+               b = (u8 *)(buf + i * 2);
+
+               port = b[0] >> 4;
+               /* TODO: support virtual MIDI ports. */
+               if (port >= tscm->spec->midi_capture_ports)
+                       goto end;
+
+               /* Assume the message length. */
+               bytes = calculate_message_bytes(b[1]);
+               /* On MIDI data or exclusives. */
+               if (bytes <= 0) {
+                       /* Seek the end of exclusives. */
+                       for (bytes = 1; bytes < 4; bytes++) {
+                               if (b[bytes] == 0xf7)
+                                       break;
+                       }
+                       if (bytes == 4)
+                               bytes = 3;
+               }
+
+               substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
+               if (substream != NULL)
+                       snd_rawmidi_receive(substream, b + 1, bytes);
+       }
+end:
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       unsigned int i;
+       int err;
+
+       /*
+        * Usually, two quadlets are transferred by one transaction. The first
+        * quadlet has MIDI messages, the rest includes timestamp.
+        * Sometimes, 8 set of the data is transferred by a block transaction.
+        */
+       tscm->async_handler.length = 8 * 8;
+       tscm->async_handler.address_callback = handle_midi_tx;
+       tscm->async_handler.callback_data = tscm;
+
+       err = fw_core_add_address_handler(&tscm->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_tscm_transaction_reregister(tscm);
+       if (err < 0)
+               goto error;
+
+       for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+               err = snd_fw_async_midi_port_init(
+                               &tscm->out_ports[i], tscm->unit,
+                               TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+                               4, fill_message);
+               if (err < 0)
+                       goto error;
+       }
+
+       return err;
+error:
+       fw_core_remove_address_handler(&tscm->async_handler);
+       return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+       struct fw_device *device = fw_parent_device(tscm->unit);
+       __be32 reg;
+       int err;
+
+       /* Register messaging address. Block transaction is not allowed. */
+       reg = cpu_to_be32((device->card->node_id << 16) |
+                         (tscm->async_handler.offset >> 32));
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       reg = cpu_to_be32(tscm->async_handler.offset);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Turn on messaging. */
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+                                 &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Turn on FireWire LED. */
+       reg = cpu_to_be32(0x0001008e);
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+                                 &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       unsigned int i;
+
+       /* Turn off FireWire LED. */
+       reg = cpu_to_be32(0x0000008e);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+                          &reg, sizeof(reg), 0);
+
+       /* Turn off messaging. */
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+                          &reg, sizeof(reg), 0);
+
+       /* Unregister the address. */
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+                          &reg, sizeof(reg), 0);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+                          &reg, sizeof(reg), 0);
+
+       fw_core_remove_address_handler(&tscm->async_handler);
+       for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
+               snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644 (file)
index 0000000..ee0bc18
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static struct snd_tscm_spec model_specs[] = {
+       {
+               .name = "FW-1884",
+               .has_adat = true,
+               .has_spdif = true,
+               .pcm_capture_analog_channels = 8,
+               .pcm_playback_analog_channels = 8,
+               .midi_capture_ports = 4,
+               .midi_playback_ports = 4,
+               .is_controller = true,
+       },
+       {
+               .name = "FW-1082",
+               .has_adat = false,
+               .has_spdif = true,
+               .pcm_capture_analog_channels = 8,
+               .pcm_playback_analog_channels = 2,
+               .midi_capture_ports = 2,
+               .midi_playback_ports = 2,
+               .is_controller = true,
+       },
+       /* FW-1804 may be supported. */
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+       struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+       const u32 *config_rom = fw_dev->config_rom;
+       char model[9];
+       unsigned int i;
+       u8 c;
+
+       if (fw_dev->config_rom_length < 30) {
+               dev_err(&tscm->unit->device,
+                       "Configuration ROM is too short.\n");
+               return -ENODEV;
+       }
+
+       /* Pick up model name from certain addresses. */
+       for (i = 0; i < 8; i++) {
+               c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+               if (c == '\0')
+                       break;
+               model[i] = c;
+       }
+       model[i] = '\0';
+
+       for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+               if (strcmp(model, model_specs[i].name) == 0) {
+                       tscm->spec = &model_specs[i];
+                       break;
+               }
+       }
+       if (tscm->spec == NULL)
+               return -ENODEV;
+
+       strcpy(tscm->card->driver, "FW-TASCAM");
+       strcpy(tscm->card->shortname, model);
+       strcpy(tscm->card->mixername, model);
+       snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+                "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+                fw_dev->config_rom[3], fw_dev->config_rom[4],
+                dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+       return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+       struct snd_tscm *tscm = card->private_data;
+
+       snd_tscm_transaction_unregister(tscm);
+       snd_tscm_stream_destroy_duplex(tscm);
+
+       fw_unit_put(tscm->unit);
+
+       mutex_destroy(&tscm->mutex);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_tscm *tscm;
+       int err;
+
+       /* create card */
+       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+                          sizeof(struct snd_tscm), &card);
+       if (err < 0)
+               return err;
+       card->private_free = tscm_card_free;
+
+       /* initialize myself */
+       tscm = card->private_data;
+       tscm->card = card;
+       tscm->unit = fw_unit_get(unit);
+
+       mutex_init(&tscm->mutex);
+       spin_lock_init(&tscm->lock);
+       init_waitqueue_head(&tscm->hwdep_wait);
+
+       err = identify_model(tscm);
+       if (err < 0)
+               goto error;
+
+       snd_tscm_proc_init(tscm);
+
+       err = snd_tscm_stream_init_duplex(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_pcm_devices(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_transaction_register(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_midi_devices(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_hwdep_device(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(&unit->device, tscm);
+
+       return err;
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+       struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+       snd_tscm_transaction_reregister(tscm);
+
+       mutex_lock(&tscm->mutex);
+       snd_tscm_stream_update_duplex(tscm);
+       mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+       struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+       /* No need to wait for releasing card object in this context. */
+       snd_card_free_when_closed(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+       {
+               .match_flags = IEEE1394_MATCH_VENDOR_ID |
+                              IEEE1394_MATCH_SPECIFIER_ID,
+               .vendor_id = 0x00022e,
+               .specifier_id = 0x00022e,
+       },
+       /* FE-08 requires reverse-engineering because it just has faders. */
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "snd-firewire-tascam",
+               .bus = &fw_bus_type,
+       },
+       .probe    = snd_tscm_probe,
+       .update   = snd_tscm_update,
+       .remove   = snd_tscm_remove,
+       .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+       return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+       driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644 (file)
index 0000000..2d028d2
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+       const char *const name;
+       bool has_adat;
+       bool has_spdif;
+       unsigned int pcm_capture_analog_channels;
+       unsigned int pcm_playback_analog_channels;
+       unsigned int midi_capture_ports;
+       unsigned int midi_playback_ports;
+       bool is_controller;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX  4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_tscm {
+       struct snd_card *card;
+       struct fw_unit *unit;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       const struct snd_tscm_spec *spec;
+
+       struct fw_iso_resources tx_resources;
+       struct fw_iso_resources rx_resources;
+       struct amdtp_stream tx_stream;
+       struct amdtp_stream rx_stream;
+       unsigned int substreams_counter;
+
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* For MIDI message incoming transactions. */
+       struct fw_address_handler async_handler;
+       struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+       /* For MIDI message outgoing transactions. */
+       struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+       u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
+       bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
+
+       /* For control messages. */
+       struct snd_firewire_tascam_status *status;
+};
+
+#define TSCM_ADDR_BASE                 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER  0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA      0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM       0x0008
+#define TSCM_OFFSET_FIRMWARE_HW                0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH         0x0200
+#define TSCM_OFFSET_UNKNOWN            0x0204
+#define TSCM_OFFSET_START_STREAMING    0x0208
+#define TSCM_OFFSET_ISOC_RX_CH         0x020c
+#define TSCM_OFFSET_ISOC_RX_ON         0x0210  /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS    0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS    0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE     0x021c
+#define TSCM_OFFSET_ISOC_TX_ON         0x0220
+/* Unknown                             0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS       0x0228
+#define TSCM_OFFSET_SET_OPTION         0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON         0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI    0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO    0x0308
+
+#define TSCM_OFFSET_LED_POWER          0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD       0x4000
+
+enum snd_tscm_clock {
+       SND_TSCM_CLOCK_INTERNAL = 0,
+       SND_TSCM_CLOCK_WORD     = 1,
+       SND_TSCM_CLOCK_SPDIF    = 2,
+       SND_TSCM_CLOCK_ADAT     = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+                 enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                     struct snd_pcm_runtime *runtime);
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+                             enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
index 33ba77dd32f2f174fd4d57dd52ff979ec9b0b510..cb89ec7c8147b2d44a90061ac531514ef5ce4de2 100644 (file)
@@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
 void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
                                 int stream)
 {
-       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
 
@@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
                break;
 
        case HDAC_EXT_STREAM_TYPE_HOST:
-               if (stream->decoupled) {
+               if (stream->decoupled && !stream->link_locked)
                        snd_hdac_ext_stream_decouple(ebus, stream, false);
-                       snd_hdac_stream_release(&stream->hstream);
-               }
+               snd_hdac_stream_release(&stream->hstream);
                break;
 
        case HDAC_EXT_STREAM_TYPE_LINK:
-               if (stream->decoupled)
+               if (stream->decoupled && !stream->hstream.opened)
                        snd_hdac_ext_stream_decouple(ebus, stream, false);
                spin_lock_irq(&bus->reg_lock);
                stream->link_locked = 0;
index 89c2711baaaf40c72753b50e05628e53cd0771ea..3060e2aee36fd736ce3755559d26c7049ba11218 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/mod_devicetable.h>
 #include <linux/export.h>
 #include <sound/hdaudio.h>
 
@@ -63,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
        return 1;
 }
 
+static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       char modalias[32];
+
+       snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+                               sizeof(modalias));
+       if (add_uevent_var(env, "MODALIAS=%s", modalias))
+               return -ENOMEM;
+       return 0;
+}
+
 struct bus_type snd_hda_bus_type = {
        .name = "hdaudio",
        .match = hda_bus_match,
+       .uevent = hda_uevent,
 };
 EXPORT_SYMBOL_GPL(snd_hda_bus_type);
 
index db96042a497f040059585adb9465d2357b28b265..bbdb25f5bbb91a1ad63e59f7b0093fe684952ebb 100644 (file)
@@ -163,6 +163,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
 
+/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+       char *newname;
+
+       if (!name)
+               return 0;
+       newname = kstrdup(name, GFP_KERNEL);
+       if (!newname)
+               return -ENOMEM;
+       kfree(codec->chip_name);
+       codec->chip_name = newname;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+{
+       return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+                       codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
 /**
  * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
  *     HD-audio controller
@@ -952,3 +989,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
        return true;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+       unsigned int res;
+
+       if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+               return -1;
+
+       return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+       return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/*
+ * snd_hdac_check_power_state: check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+               hda_nid_t nid, unsigned int target_state)
+{
+       unsigned int state = codec_read(hdac, nid, 0,
+                               AC_VERB_GET_POWER_STATE, 0);
+
+       if (state & AC_PWRST_ERROR)
+               return true;
+       state = (state >> 4) & 0x0f;
+       return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
index c71142dea98a18f5355f1395c4c34200a773c52e..42d61bf41969c2176f1e10842e0c8e651d202a23 100644 (file)
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
 CODEC_ATTR_STR(vendor_name);
 CODEC_ATTR_STR(chip_name);
 
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
 static struct attribute *hdac_dev_attrs[] = {
        &dev_attr_type.attr,
        &dev_attr_vendor_id.attr,
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
        &dev_attr_mfg.attr,
        &dev_attr_vendor_name.attr,
        &dev_attr_chip_name.attr,
+       &dev_attr_modalias.attr,
        NULL
 };
 
index d5ac25cc7fee1fbc87b577da634f9f13706091a5..70671ad65d24565fb6c8522bd69444817aac2933 100644 (file)
 #include "hda_local.h"
 
 /*
- * find a matching codec preset
+ * find a matching codec id
  */
 static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
 {
        struct hda_codec *codec = container_of(dev, struct hda_codec, core);
        struct hda_codec_driver *driver =
                container_of(drv, struct hda_codec_driver, core);
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *list;
        /* check probe_id instead of vendor_id if set */
        u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+       u32 rev_id = codec->core.revision_id;
 
-       for (preset = driver->preset; preset->id; preset++) {
-               if (preset->id == id &&
-                   (!preset->rev || preset->rev == codec->core.revision_id)) {
-                       codec->preset = preset;
+       for (list = driver->id; list->vendor_id; list++) {
+               if (list->vendor_id == id &&
+                   (!list->rev_id || list->rev_id == rev_id)) {
+                       codec->preset = list;
                        return 1;
                }
        }
@@ -45,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
                codec->patch_ops.unsol_event(codec, ev);
 }
 
-/* reset the codec name from the preset */
-static int codec_refresh_name(struct hda_codec *codec, const char *name)
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
 {
-       if (name) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup(name, GFP_KERNEL);
+       int err;
+
+       if (!name)
+               return 0;
+       err = snd_hdac_device_set_chip_name(&codec->core, name);
+       if (err < 0)
+               return err;
+
+       /* update the mixer name */
+       if (!*codec->card->mixername ||
+           codec->bus->mixer_assigned >= codec->core.addr) {
+               snprintf(codec->card->mixername,
+                        sizeof(codec->card->mixername), "%s %s",
+                        codec->core.vendor_name, codec->core.chip_name);
+               codec->bus->mixer_assigned = codec->core.addr;
        }
-       return codec->core.chip_name ? 0 : -ENOMEM;
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
 
 static int hda_codec_driver_probe(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
        struct module *owner = dev->driver->owner;
+       hda_codec_patch_t patch;
        int err;
 
        if (WARN_ON(!codec->preset))
                return -EINVAL;
 
-       err = codec_refresh_name(codec, codec->preset->name);
+       err = snd_hda_codec_set_name(codec, codec->preset->name);
        if (err < 0)
                goto error;
        err = snd_hdac_regmap_init(&codec->core);
@@ -76,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
                goto error;
        }
 
-       err = codec->preset->patch(codec);
-       if (err < 0)
-               goto error_module;
+       patch = (hda_codec_patch_t)codec->preset->driver_data;
+       if (patch) {
+               err = patch(codec);
+               if (err < 0)
+                       goto error_module;
+       }
 
        err = snd_hda_codec_build_pcms(codec);
        if (err < 0)
@@ -155,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec)
 static void codec_bind_module(struct hda_codec *codec)
 {
 #ifdef MODULE
-       request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
-       if (codec_probed(codec))
-               return;
-       request_module("snd-hda-codec-id:%04x*",
-                      (codec->core.vendor_id >> 16) & 0xffff);
+       char modalias[32];
+
+       snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+       request_module(modalias);
        if (codec_probed(codec))
                return;
 #endif
@@ -251,11 +273,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
                }
        }
 
-       /* audio codec should override the mixer name */
-       if (codec->core.afg || !*codec->card->mixername)
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername), "%s %s",
-                        codec->core.vendor_name, codec->core.chip_name);
        return 0;
 
  error:
index a249d5486889dca683af566e0818d95ee49b12ae..83741887faa189c1700abc34bebbd3c55234384d 100644 (file)
@@ -90,50 +90,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
        return err;
 }
 
-/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
-                               int flags,
-                               unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
-       unsigned int res;
-       if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
-               return -1;
-       return res;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
-                       unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
-       return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
-
 /**
  * snd_hda_sequence_write - sequence writes
  * @codec: the HDA codec
index 2970413f18a01817b9500ed7f1c00f61e3b1e92b..373fcad840ea6ff5c18b4c4aa93b6473c7a60f1c 100644 (file)
@@ -22,6 +22,7 @@
 #define __SOUND_HDA_CODEC_H
 
 #include <linux/kref.h>
+#include <linux/mod_devicetable.h>
 #include <sound/info.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -69,6 +70,7 @@ struct hda_bus {
        unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
 
        int primary_dig_out_type;       /* primary digital out PCM type */
+       unsigned int mixer_assigned;    /* codec addr for mixer name */
 };
 
 /* from hdac_bus to hda_bus */
@@ -80,19 +82,21 @@ struct hda_bus {
  * Known codecs have the patch to build and set up the controls/PCMs
  * better than the generic parser.
  */
-struct hda_codec_preset {
-       unsigned int id;
-       unsigned int rev;
-       const char *name;
-       int (*patch)(struct hda_codec *codec);
-};
+typedef int (*hda_codec_patch_t)(struct hda_codec *);
        
 #define HDA_CODEC_ID_GENERIC_HDMI      0x00000101
 #define HDA_CODEC_ID_GENERIC           0x00000201
 
+#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
+       { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+         .api_version = HDA_DEV_LEGACY, \
+         .driver_data = (unsigned long)(_patch) }
+#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
+       HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
+
 struct hda_codec_driver {
        struct hdac_driver core;
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *id;
 };
 
 int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -183,7 +187,7 @@ struct hda_codec {
        u32 probe_id; /* overridden id for probing */
 
        /* detected preset */
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *preset;
        const char *modelname;  /* model name for preset */
 
        /* set by patch */
@@ -297,10 +301,6 @@ struct hda_codec {
 /*
  * constructors
  */
-int snd_hda_bus_new(struct snd_card *card,
-                   const struct hdac_bus_ops *ops,
-                   const struct hdac_io_ops *io_ops,
-                   struct hda_bus **busp);
 int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
                      unsigned int codec_addr, struct hda_codec **codecp);
 int snd_hda_codec_configure(struct hda_codec *codec);
@@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
 /*
  * low level functions
  */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
+static inline unsigned int
+snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                int flags,
-                               unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
-                       unsigned int verb, unsigned int parm);
+                               unsigned int verb, unsigned int parm)
+{
+       return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
+}
+
+static inline int
+snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+                       unsigned int verb, unsigned int parm)
+{
+       return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
+}
+
 #define snd_hda_param_read(codec, nid, param) \
        snd_hdac_read_parm(&(codec)->core, nid, param)
 #define snd_hda_get_sub_nodes(codec, nid, start_nid) \
@@ -453,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
 void snd_hda_bus_reset(struct hda_bus *bus);
 void snd_hda_bus_reset_codecs(struct hda_bus *bus);
 
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
+
 /*
  * power management
  */
index 944455997fdcc8fd80758b4fb5794488543bd966..d6b93a20361b01695f3964add27e5c384749f00a 100644 (file)
@@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model,
        mutex_init(&bus->prepare_mutex);
        bus->pci = chip->pci;
        bus->modelname = model;
+       bus->mixer_assigned = -1;
        bus->core.snoop = azx_snoop(chip);
        if (chip->get_position[0] != azx_get_pos_lpib ||
            chip->get_position[1] != azx_get_pos_lpib)
index 24f91114a32cc73f54d72d8b008db51935519893..c6e8a651cea1357df06a2934d1854c926f6e50c4 100644 (file)
@@ -5877,13 +5877,14 @@ error:
        return err;
 }
 
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
-       { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+       HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
        {} /* terminator */
 };
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
 
 static struct hda_codec_driver generic_driver = {
-       .preset = snd_hda_preset_generic,
+       .id = snd_hda_id_generic,
 };
 
 module_hda_codec_driver(generic_driver);
index 4a21c2199e0219d980a69d82a83bd91942c22f83..d0e066e4c9856028b1f9cbc0faeb3dc2e3a5ae9a 100644 (file)
@@ -681,12 +681,7 @@ static inline bool
 snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
                          unsigned int target_state)
 {
-       unsigned int state = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_POWER_STATE, 0);
-       if (state & AC_PWRST_ERROR)
-               return true;
-       state = (state >> 4) & 0x0f;
-       return (state == target_state);
+       return snd_hdac_check_power_state(&codec->core, nid, target_state);
 }
 
 unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
index a6e3d9b511ab5f0a2bfa446cbb004c002b81f029..64e0d1d81ca5afd66625669079bdce05fa23dc63 100644 (file)
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
 static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
                                 struct hda_codec **codecp)
 {
-       kfree((*codecp)->core.chip_name);
-       (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
+       snd_hda_codec_set_name(*codecp, buf);
 }
 
 #define DEFINE_PARSE_ID_MODE(name) \
index c033a4ee65470fb592e77836437262063f00f125..e0fb8c6d1bc274e4ff5f6a8bf55afa9852bf9026 100644 (file)
@@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_analog[] = {
-       { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
-       { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
-       { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
-       { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
-       { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
-       { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
-       { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
-       { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
-       { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
-       { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
-       { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
-       { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
-       { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
-       { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
-       { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+       HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+       HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+       HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+       HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+       HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+       HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Analog Devices HD-audio codec");
 
 static struct hda_codec_driver analog_driver = {
-       .preset = snd_hda_preset_analog,
+       .id = snd_hda_id_analog,
 };
 
 module_hda_codec_driver(analog_driver);
index 484bbf4134cd127e8b9a9cf03bbc66ab361755f6..c2d9ee9cfdc00900aee82957fee356442710faa3 100644 (file)
@@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
-       { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
-       { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
-       { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+       HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+       HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+       HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
 
 static struct hda_codec_driver ca0110_driver = {
-       .preset = snd_hda_preset_ca0110,
+       .id = snd_hda_id_ca0110,
 };
 
 module_hda_codec_driver(ca0110_driver);
index 186792fe226e08d95d8bb67c959e0fbd7d86bd25..3a02e5c14d09b7321cc2604fb88a2ca0a05bde31 100644 (file)
@@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec)
 /*
  * patch entries
  */
-static struct hda_codec_preset snd_hda_preset_ca0132[] = {
-       { .id = 0x11020011, .name = "CA0132",     .patch = patch_ca0132 },
+static struct hda_device_id snd_hda_id_ca0132[] = {
+       HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:11020011");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Creative Sound Core3D codec");
 
 static struct hda_codec_driver ca0132_driver = {
-       .preset = snd_hda_preset_ca0132,
+       .id = snd_hda_id_ca0132,
 };
 
 module_hda_codec_driver(ca0132_driver);
index 85813de26da87715df7d1d259339e30450c7815a..a12ae8ac091451261a2ba613543677fabedb8cd0 100644 (file)
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
                return NULL;
        codec->spec = spec;
        spec->vendor_nid = vendor_nid;
+       codec->power_save_node = 1;
        snd_hda_gen_spec_init(&spec->gen);
 
        return spec;
@@ -1200,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
-       { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
-       { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
-       { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
-       { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
-       { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+       HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+       HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+       HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+       HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+       HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-MODULE_ALIAS("snd-hda-codec-id:10134208");
-MODULE_ALIAS("snd-hda-codec-id:10134210");
-MODULE_ALIAS("snd-hda-codec-id:10134213");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
 
 static struct hda_codec_driver cirrus_driver = {
-       .preset = snd_hda_preset_cirrus,
+       .id = snd_hda_id_cirrus,
 };
 
 module_hda_codec_driver(cirrus_driver);
index f5ed078710f8494918786657613180f1d75a2286..1b2195dd2b26588d8075f79b14636cef1ced47b3 100644 (file)
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
-       { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
-       { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
-       { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+       HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+       HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+       HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:13f68888");
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("C-Media HD-audio codec");
 
 static struct hda_codec_driver cmedia_driver = {
-       .preset = snd_hda_preset_cmedia,
+       .id = snd_hda_id_cmedia,
 };
 
 module_hda_codec_driver(cmedia_driver);
index 2f0ec7c45fc70d6232339761e5773349d24213a9..c8b8ef5246a6f94c99b281c837e220e118dbf8a9 100644 (file)
@@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec)
 /*
  */
 
-static const struct hda_codec_preset snd_hda_preset_conexant[] = {
-       { .id = 0x14f15045, .name = "CX20549 (Venice)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15066, .name = "CX20582 (Pebble)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15068, .name = "CX20584",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15069, .name = "CX20585",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1506c, .name = "CX20588",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1506e, .name = "CX20590",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15097, .name = "CX20631",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15098, .name = "CX20632",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150a1, .name = "CX20641",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150a2, .name = "CX20642",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150ab, .name = "CX20651",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150ac, .name = "CX20652",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150b8, .name = "CX20664",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150b9, .name = "CX20665",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f1, .name = "CX20721",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f2, .name = "CX20722",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f3, .name = "CX20723",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f4, .name = "CX20724",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1510f, .name = "CX20751/2",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15110, .name = "CX20751/2",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15111, .name = "CX20753/4",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15113, .name = "CX20755",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15114, .name = "CX20756",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15115, .name = "CX20757",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f151d7, .name = "CX20952",
-         .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+       HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f1506c");
-MODULE_ALIAS("snd-hda-codec-id:14f1506e");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-MODULE_ALIAS("snd-hda-codec-id:14f150f1");
-MODULE_ALIAS("snd-hda-codec-id:14f150f2");
-MODULE_ALIAS("snd-hda-codec-id:14f150f3");
-MODULE_ALIAS("snd-hda-codec-id:14f150f4");
-MODULE_ALIAS("snd-hda-codec-id:14f1510f");
-MODULE_ALIAS("snd-hda-codec-id:14f15110");
-MODULE_ALIAS("snd-hda-codec-id:14f15111");
-MODULE_ALIAS("snd-hda-codec-id:14f15113");
-MODULE_ALIAS("snd-hda-codec-id:14f15114");
-MODULE_ALIAS("snd-hda-codec-id:14f15115");
-MODULE_ALIAS("snd-hda-codec-id:14f151d7");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
 
 static struct hda_codec_driver conexant_driver = {
-       .preset = snd_hda_preset_conexant,
+       .id = snd_hda_id_conexant,
 };
 
 module_hda_codec_driver(conexant_driver);
index acbfbe087ee86d41f5688b0af85c4ee6f7eec43f..f503a883bef31d0fd55a38dd816f70609edcaf82 100644 (file)
@@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        return non_pcm;
 }
 
+/* There is a fixed mapping between audio pin node and display port
+ * on current Intel platforms:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_pin2port(hda_nid_t pin_nid)
+{
+       return pin_nid - 4;
+}
 
 /*
  * HDMI callbacks
@@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        int pin_idx = hinfo_to_pin_index(codec, hinfo);
        struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct i915_audio_component *acomp = codec->bus->core.audio_component;
        bool non_pcm;
        int pinctl;
 
@@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
        }
 
+       /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+       /* Todo: add DP1.2 MST audio support later */
+       if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
+               acomp->ops->sync_audio_rate(acomp->dev,
+                               intel_pin2port(pin_nid),
+                               runtime->rate);
+
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
        per_pin->channels = substream->runtime->channels;
@@ -2561,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
 
-       switch (codec->preset->id) {
+       switch (codec->preset->vendor_id) {
        case 0x10de0002:
        case 0x10de0003:
        case 0x10de0005:
@@ -2879,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
                                     snd_pcm_alt_chmaps, 8, 0, &chmap);
        if (err < 0)
                return err;
-       switch (codec->preset->id) {
+       switch (codec->preset->vendor_id) {
        case 0x10de0002:
        case 0x10de0003:
        case 0x10de0005:
@@ -3487,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI",      .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI",      .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI",  .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI",       .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI",      .patch = patch_nvhdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP",  .patch = patch_nvhdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI",      patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI",      patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI",  patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI",       patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI",      patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP",  patch_nvhdmi),
 /* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0020, .name = "Tegra30 HDMI",    .patch = patch_tegra_hdmi },
-{ .id = 0x10de0022, .name = "Tegra114 HDMI",   .patch = patch_tegra_hdmi },
-{ .id = 0x10de0028, .name = "Tegra124 HDMI",   .patch = patch_tegra_hdmi },
-{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP",        .patch = patch_tegra_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0067, .name = "MCP67 HDMI",      .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de8001, .name = "MCP73 HDMI",      .patch = patch_nvhdmi_2ch },
-{ .id = 0x11069f80, .name = "VX900 HDMI/DP",   .patch = patch_via_hdmi },
-{ .id = 0x11069f81, .name = "VX900 HDMI/DP",   .patch = patch_via_hdmi },
-{ .id = 0x11069f84, .name = "VX11 HDMI/DP",    .patch = patch_generic_hdmi },
-{ .id = 0x11069f85, .name = "VX11 HDMI/DP",    .patch = patch_generic_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI",  .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI",        .patch = patch_generic_hdmi },
-{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862807, .name = "Haswell HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862808, .name = "Broadwell HDMI",  .patch = patch_generic_hdmi },
-{ .id = 0x80862809, .name = "Skylake HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x8086280a, .name = "Broxton HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862882, .name = "Valleyview2 HDMI",        .patch = patch_generic_hdmi },
-{ .id = 0x80862883, .name = "Braswell HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI",  .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI",    patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI",   patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI",   patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP",        patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI",      patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI",      patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP",   patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP",   patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI",  patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI",  patch_generic_hdmi),
 /* special ID for generic HDMI */
-{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
 {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0015");
-MODULE_ALIAS("snd-hda-codec-id:10de0016");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0028");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0051");
-MODULE_ALIAS("snd-hda-codec-id:10de0060");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de0070");
-MODULE_ALIAS("snd-hda-codec-id:10de0071");
-MODULE_ALIAS("snd-hda-codec-id:10de0072");
-MODULE_ALIAS("snd-hda-codec-id:10de007d");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:11069f80");
-MODULE_ALIAS("snd-hda-codec-id:11069f81");
-MODULE_ALIAS("snd-hda-codec-id:11069f84");
-MODULE_ALIAS("snd-hda-codec-id:11069f85");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80862806");
-MODULE_ALIAS("snd-hda-codec-id:80862807");
-MODULE_ALIAS("snd-hda-codec-id:80862808");
-MODULE_ALIAS("snd-hda-codec-id:80862809");
-MODULE_ALIAS("snd-hda-codec-id:8086280a");
-MODULE_ALIAS("snd-hda-codec-id:80862880");
-MODULE_ALIAS("snd-hda-codec-id:80862882");
-MODULE_ALIAS("snd-hda-codec-id:80862883");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -3627,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
 MODULE_ALIAS("snd-hda-codec-atihdmi");
 
 static struct hda_codec_driver hdmi_driver = {
-       .preset = snd_hda_preset_hdmi,
+       .id = snd_hda_id_hdmi,
 };
 
 module_hda_codec_driver(hdmi_driver);
index 16b8dcba5c12d2d13ed7c80c4e6f93df69d9944f..0214b7df6d9dc2576bd333cf7cdd9756936f31ec 100644 (file)
@@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = {
 };
 
 
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
-       kfree(codec->core.chip_name);
-       codec->core.chip_name = kstrdup(name, GFP_KERNEL);
-       if (!codec->core.chip_name) {
-               alc_free(codec);
-               return -ENOMEM;
-       }
-       return 0;
-}
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
 
 /*
  * Rename codecs appropriately from COEF value or subvendor id
@@ -6627,78 +6617,70 @@ static int patch_alc680(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_realtek[] = {
-       { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
-       { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
-       { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
-       { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
-       { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
-       { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
-       { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
-       { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
-       { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
-       { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
-       { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
-       { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
-       { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
-       { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
-       { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
-       { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
-       { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
-       { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
-       { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
-       { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
-       { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
-       { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
-       { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
-       { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
-       { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
-       { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
-       { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
-         .patch = patch_alc861 },
-       { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
-       { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
-       { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
-       { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
-         .patch = patch_alc662 },
-       { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
-         .patch = patch_alc662 },
-       { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
-       { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
-       { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
-       { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
-       { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
-       { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
-       { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
-       { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
-       { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
-       { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
-       { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
-       { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
-       { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
-       { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
-       { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
-       { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
-       { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
-       { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+       HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+       HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+       HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+       HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+       HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+       HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+       HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+       HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+       HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+       HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+       HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Realtek HD-audio codec");
 
 static struct hda_codec_driver realtek_driver = {
-       .preset = snd_hda_preset_realtek,
+       .id = snd_hda_id_realtek,
 };
 
 module_hda_codec_driver(realtek_driver);
index 5104bebb228699f1a04e754429f57e31622a521a..ffda38c45509583dd9e43f5f119c5d835b23e05b 100644 (file)
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_si3054[] = {
-       { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+       HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
        /* VIA HDA on Clevo m540 */
-       { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
        /* Asus A8J Modem (SM56) */
-       { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
        /* LG LW20 modem */
-       { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
        {}
 };
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
 
 static struct hda_codec_driver si3054_driver = {
-       .preset = snd_hda_preset_si3054,
+       .id = snd_hda_id_si3054,
 };
 
 module_hda_codec_driver(si3054_driver);
index def5cc8dff0293c2f70c4c3fcf67da1aea1bf55e..08a0f6a35cca5305d106b8f07574d7cc9a7a5960 100644 (file)
@@ -5012,121 +5012,119 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
-       { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
-       { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
-       { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
-       { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
-       { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
-       { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
-       { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
-       { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
-       { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
-       { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
-       { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
-       { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
-       { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
-       { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
-       { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
-       { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
-       { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
-       { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
-       { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
-       { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
-       { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
-       { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
-       { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
-       { .id = 0x83847632, .name = "STAC9202",  .patch = patch_stac925x },
-       { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
-       { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
-       { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
-       { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
-       { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
-       { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
-       { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
-       /* The following does not take into account .id=0x83847661 when subsys =
-        * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
-        * currently not fully supported.
-        */
-       { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
-       { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
-       { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
-       { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
-       { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
-       { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
-       { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
-       { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
-       { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
-       { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
-       { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
-       { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
-       { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
-       { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
-       { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
-       { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+       HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+       HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847632, "STAC9202",  patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+       /* The following does not take into account .id=0x83847661 when subsys =
+        * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+        * currently not fully supported.
+        */
+       HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+       HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
 
 static struct hda_codec_driver sigmatel_driver = {
-       .preset = snd_hda_preset_sigmatel,
+       .id = snd_hda_id_sigmatel,
 };
 
 module_hda_codec_driver(sigmatel_driver);
index da5366405eda55a6eccbca0e14bce5631c851cdc..fc30d1e8aa76a6b2aa9ca1de992b5a1c937bb971 100644 (file)
@@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
        override_mic_boost(codec, 0x1e, 0, 3, 40);
 
        /* correct names for VT1708BCE */
-       if (get_codec_type(codec) == VT1708BCE) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername),
-                        "%s %s", codec->core.vendor_name, codec->core.chip_name);
-       }
+       if (get_codec_type(codec) == VT1708BCE)
+               snd_hda_codec_set_name(codec, "VT1708BCE");
        /* correct names for VT1705 */
-       if (codec->core.vendor_id == 0x11064397) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername),
-                        "%s %s", codec->core.vendor_name, codec->core.chip_name);
-       }
+       if (codec->core.vendor_id == 0x11064397)
+               snd_hda_codec_set_name(codec, "VT1705");
 
        /* automatic parse from the BIOS config */
        err = via_parse_auto_config(codec);
@@ -1210,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_via[] = {
-       { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106e710, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e711, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e712, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e713, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e714, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e715, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e716, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e717, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e720, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e721, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e722, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e723, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e724, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e725, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e726, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e727, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x11060397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11061397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11062397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11063397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11064397, .name = "VT1705",
-         .patch = patch_vt1708S},
-       { .id = 0x11065397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11066397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11067397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11060398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11061398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11062398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11063398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11064398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11065398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11066398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11067398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11060428, .name = "VT1718S",
-         .patch = patch_vt1718S},
-       { .id = 0x11064428, .name = "VT1718S",
-         .patch = patch_vt1718S},
-       { .id = 0x11060441, .name = "VT2020",
-         .patch = patch_vt1718S},
-       { .id = 0x11064441, .name = "VT1828S",
-         .patch = patch_vt1718S},
-       { .id = 0x11060433, .name = "VT1716S",
-         .patch = patch_vt1716S},
-       { .id = 0x1106a721, .name = "VT1716S",
-         .patch = patch_vt1716S},
-       { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
-       { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
-       { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
-       { .id = 0x11060440, .name = "VT1818S",
-         .patch = patch_vt1708S},
-       { .id = 0x11060446, .name = "VT1802",
-               .patch = patch_vt2002P},
-       { .id = 0x11068446, .name = "VT1802",
-               .patch = patch_vt2002P},
-       { .id = 0x11064760, .name = "VT1705CF",
-               .patch = patch_vt3476},
-       { .id = 0x11064761, .name = "VT1708SCE",
-               .patch = patch_vt3476},
-       { .id = 0x11064762, .name = "VT1808",
-               .patch = patch_vt3476},
+static const struct hda_device_id snd_hda_id_via[] = {
+       HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+       HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+       HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+       HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+       HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+       HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
 
 static struct hda_codec_driver via_driver = {
-       .preset = snd_hda_preset_via,
+       .id = snd_hda_id_via,
 };
 
 MODULE_LICENSE("GPL");
index 7acbc21d642a53fe8658c52bdcf91992f08816ac..9e1ad119a3ce0430579d648ae34e6eedc3b7af10 100644 (file)
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
 
         spin_unlock_irqrestore(&korg1212->lock, flags);
 
-        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    kPlayBufferFrames);
+
         return 0;
 }
 
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
 
         spin_unlock_irqrestore(&korg1212->lock, flags);
 
-        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                    kPlayBufferFrames, kPlayBufferFrames);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    kPlayBufferFrames);
         return 0;
 }
 
index cba89beb2b38fac6e376a2231c1e4f68caa644e5..8b8e2e54fba3f1520d3ed6c6f22185274ca6ae5b 100644 (file)
@@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
 
        /* the clock rate cannot be changed */
        board_rate = chip->board_sample_rate;
-       err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
-                                          board_rate, board_rate);
+       err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+                                          board_rate);
 
        if (err < 0) {
                dev_warn(chip->card->dev, "could not constrain periods\n");
index 23d7f5d30c4106e107436999673358e0fb65ff8b..cd94ac548ba383a99ee10886c94343877f760211 100644 (file)
@@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
 static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
 {
        if (! rme32->fullduplex_mode) {
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                            RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+                                            RME32_BUFFER_SIZE);
                snd_pcm_hw_constraint_list(runtime, 0,
                                           SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
                                           &hw_constraints_period_bytes);
index 2306ccf7281e29c9e428b4349c34ade9d12e2296..714df906249eab42c54816fed4630cc4890d7603 100644 (file)
@@ -1152,13 +1152,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
 {
        unsigned int size;
 
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                    RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                    RME96_BUFFER_SIZE);
        if ((size = rme96->playback_periodsize) != 0 ||
            (size = rme96->capture_periodsize) != 0)
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                                            size, size);
+                                            size);
        else
                snd_pcm_hw_constraint_list(runtime, 0,
                                           SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
index 9bba275b4c9b08ba3dd4ff763afd6b17669af48d..2875b4f6d8c9e6a792638547d4b6850fa620a857 100644 (file)
@@ -5112,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
                dev_err(hdsp->card->dev,
                        "too short firmware size %d (expected %d)\n",
                           (int)fw->size, HDSP_FIRMWARE_SIZE);
+               release_firmware(fw);
                return -EINVAL;
        }
 
index cb666c73712d15276b411cdce11c7994380d4ffb..8bc8016c173d6a6005e33222df1381272bb7819e 100644 (file)
@@ -6080,18 +6080,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
                                             SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                             32, 4096);
                /* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-                                            16384, 16384);
+                                            16384);
                break;
 
        default:
                snd_pcm_hw_constraint_minmax(runtime,
                                             SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                             64, 8192);
-               snd_pcm_hw_constraint_minmax(runtime,
-                                            SNDRV_PCM_HW_PARAM_PERIODS,
-                                            2, 2);
+               snd_pcm_hw_constraint_single(runtime,
+                                            SNDRV_PCM_HW_PARAM_PERIODS, 2);
                break;
        }
 
index 198c924551b78e268b8480a38d481703fe53d3a9..acff8d62059cf49de24585aeac9c2143412c0175 100644 (file)
@@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
        if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
                return 0;
 
-       return snd_pcm_hw_constraint_minmax(substream->runtime,
-                       SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
 }
 
 static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
index 2713e1845cbcd93f71232fbc2141784a7c540223..a5a4e9f75c57f89e88f3047d980cf286f36569af 100644 (file)
@@ -1612,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
                return;
 
        /* Set the constraints according to the already configured stream */
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_RATE,
-                               twl4030->rate,
                                twl4030->rate);
 
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                               twl4030->sample_bits,
                                twl4030->sample_bits);
 
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                               twl4030->channels,
                                twl4030->channels);
 }
 
@@ -1669,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
                        /* In option2 4 channel is not supported, set the
                         * constraint for the first stream for channels, the
                         * second stream will 'inherit' this cosntraint */
-                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                       snd_pcm_hw_constraint_single(substream->runtime,
                                                     SNDRV_PCM_HW_PARAM_CHANNELS,
-                                                    2, 2);
+                                                    2);
                }
                twl4030->master_substream = substream;
        }
index e1902638053490dda1ab695cf989c91239769ea7..e4c694c758b810e4ae041436fdb364baf4684fd6 100644 (file)
@@ -150,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
                         master_runtime->sample_bits,
                         master_runtime->rate);
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
+               snd_pcm_hw_constraint_single(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
                                             master_runtime->rate);
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
+               snd_pcm_hw_constraint_single(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                            master_runtime->sample_bits,
                                             master_runtime->sample_bits);
 
                uda134x->slave_substream = substream;
index 80fb1dc81f6cfb89544da1ee393ce1fb3794f253..7693c1129babf0e42c58b5ca93832052374cb426 100644 (file)
@@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream,
 
        switch (wl1273->mode) {
        case WL1273_MODE_BT:
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            8000, 8000);
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_RATE, 8000);
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_CHANNELS, 1);
                break;
        case WL1273_MODE_FM_RX:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
index c4453120b11a33c60dc08422b49096f2700ce872..7a5c9a36c1db670081c70480e71e6d6b2a3ef538 100644 (file)
@@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static unsigned int rates_48000[] = {
-       48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
-       .count = ARRAY_SIZE(rates_48000),
-       .list  = rates_48000,
-};
-
 static int byt_aif1_startup(struct snd_pcm_substream *substream)
 {
-       return snd_pcm_hw_constraint_list(substream->runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_48000);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops byt_aif1_ops = {
index 49f4869cec48a273595f145cae120fa1485e88e6..4e2fcf188dd1e0be1dce9a618929235f35bcb5a0 100644 (file)
@@ -193,20 +193,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static unsigned int rates_48000[] = {
-       48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
-       .count = ARRAY_SIZE(rates_48000),
-       .list  = rates_48000,
-};
-
 static int cht_aif1_startup(struct snd_pcm_substream *substream)
 {
-       return snd_pcm_hw_constraint_list(substream->runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_48000);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static int cht_max98090_headset_init(struct snd_soc_component *component)
index 7be8461e4d3bd45c32b921b96acc1817bcb3584f..38d65a3529c4fc1fda0464d05057e0055b6afe35 100644 (file)
@@ -235,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static unsigned int rates_48000[] = {
-       48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
-       .count = ARRAY_SIZE(rates_48000),
-       .list  = rates_48000,
-};
-
 static int cht_aif1_startup(struct snd_pcm_substream *substream)
 {
-       return snd_pcm_hw_constraint_list(substream->runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_48000);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops cht_aif1_ops = {
index 23fe0407514209d2ddfd67b7e076f099c5e73f5d..5621ccd92992d9f198bd5d0fd6149f06d0a3631a 100644 (file)
@@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static unsigned int rates_48000[] = {
-       48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
-       .count = ARRAY_SIZE(rates_48000),
-       .list  = rates_48000,
-};
-
 static int cht_aif1_startup(struct snd_pcm_substream *substream)
 {
-       return snd_pcm_hw_constraint_list(substream->runtime, 0,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       &constraints_48000);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops cht_aif1_ops = {
index dcb5336b569815b8958e2ecb572710930ca425cb..190f868e78b24af37d8442d378c06aeb8ec6aaf2 100644 (file)
@@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
-       snd_pcm_hw_constraint_minmax(runtime,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 
        n810_ext_control(&rtd->card->dapm);
        return clk_prepare_enable(sys_clkout2);
index 99538900a253395fe04eae7349a505f1eb87d659..5e21f08579d804c5f7895a22e87de6cdff8b79ef 100644 (file)
@@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_card *card = rtd->card;
 
-       snd_pcm_hw_constraint_minmax(runtime,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
        rx51_ext_control(&card->dapm);
 
        return 0;
index 317395824cd794d093e74b98013a8f2537b89056..c86dc96e8986f39cd08bcf7e92c63f49459d50f2 100644 (file)
@@ -200,9 +200,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
                                soc_dai->rate);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_RATE,
-                                               soc_dai->rate, soc_dai->rate);
+                                               soc_dai->rate);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
                                "ASoC: Unable to apply rate constraint: %d\n",
@@ -216,9 +216,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
                                soc_dai->channels);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                                               soc_dai->channels,
                                                soc_dai->channels);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
@@ -233,9 +232,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
                                soc_dai->sample_bits);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                               soc_dai->sample_bits,
                                                soc_dai->sample_bits);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
index 6ba8ae9ecc7a13a04624b2fe760cdad9d4425735..6d5698b25bd4b25c92c128860527245167d665d0 100644 (file)
@@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
                slots_active = hweight32(mask);
                dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
 
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                               slots_active, slots_active);
+                               slots_active);
                break;
 
        default:
index ef580b43f1e3b1e71dcc4ed0f6a16f11928e071e..71778ca4b26aafcb3dacedcc660947a5df61f7e2 100644 (file)
@@ -122,6 +122,7 @@ struct snd_usb_substream {
        unsigned int buffer_periods;    /* current periods per buffer */
        unsigned int altset_idx;     /* USB data format: index of alternate setting */
        unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
+       unsigned int tx_length_quirk:1; /* add length specifier to transfers */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
        unsigned int pkt_offset_adj;    /* Bytes to drop from beginning of packets (for non-compliant devices) */
 
index e6f71894ecdc950117776d2ef9e2cc4df4661904..7b1cb365ffab74d6028206adb8012226da963bac 100644 (file)
@@ -183,13 +183,53 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
                ep->retire_data_urb(ep->data_subs, urb);
 }
 
+static void prepare_silent_urb(struct snd_usb_endpoint *ep,
+                              struct snd_urb_ctx *ctx)
+{
+       struct urb *urb = ctx->urb;
+       unsigned int offs = 0;
+       unsigned int extra = 0;
+       __le32 packet_length;
+       int i;
+
+       /* For tx_length_quirk, put packet length at start of packet */
+       if (ep->chip->tx_length_quirk)
+               extra = sizeof(packet_length);
+
+       for (i = 0; i < ctx->packets; ++i) {
+               unsigned int offset;
+               unsigned int length;
+               int counts;
+
+               if (ctx->packet_size[i])
+                       counts = ctx->packet_size[i];
+               else
+                       counts = snd_usb_endpoint_next_packet_size(ep);
+
+               length = counts * ep->stride; /* number of silent bytes */
+               offset = offs * ep->stride + extra * i;
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length = length + extra;
+               if (extra) {
+                       packet_length = cpu_to_le32(length);
+                       memcpy(urb->transfer_buffer + offset,
+                              &packet_length, sizeof(packet_length));
+               }
+               memset(urb->transfer_buffer + offset + extra,
+                      ep->silence_value, length);
+               offs += counts;
+       }
+
+       urb->number_of_packets = ctx->packets;
+       urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+}
+
 /*
  * Prepare a PLAYBACK urb for submission to the bus.
  */
 static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                                 struct snd_urb_ctx *ctx)
 {
-       int i;
        struct urb *urb = ctx->urb;
        unsigned char *cp = urb->transfer_buffer;
 
@@ -201,24 +241,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                        ep->prepare_data_urb(ep->data_subs, urb);
                } else {
                        /* no data provider, so send silence */
-                       unsigned int offs = 0;
-                       for (i = 0; i < ctx->packets; ++i) {
-                               int counts;
-
-                               if (ctx->packet_size[i])
-                                       counts = ctx->packet_size[i];
-                               else
-                                       counts = snd_usb_endpoint_next_packet_size(ep);
-
-                               urb->iso_frame_desc[i].offset = offs * ep->stride;
-                               urb->iso_frame_desc[i].length = counts * ep->stride;
-                               offs += counts;
-                       }
-
-                       urb->number_of_packets = ctx->packets;
-                       urb->transfer_buffer_length = offs * ep->stride;
-                       memset(urb->transfer_buffer, ep->silence_value,
-                              offs * ep->stride);
+                       prepare_silent_urb(ep, ctx);
                }
                break;
 
@@ -594,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
        unsigned int max_packs_per_period, urbs_per_period, urb_packs;
        unsigned int max_urbs, i;
        int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+       int tx_length_quirk = (ep->chip->tx_length_quirk &&
+                              usb_pipeout(ep->pipe));
 
        if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
                /*
@@ -610,13 +635,34 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
 
        /* assume max. frequency is 25% higher than nominal */
        ep->freqmax = ep->freqn + (ep->freqn >> 2);
-       maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
-                               >> (16 - ep->datainterval);
+       /* Round up freqmax to nearest integer in order to calculate maximum
+        * packet size, which must represent a whole number of frames.
+        * This is accomplished by adding 0x0.ffff before converting the
+        * Q16.16 format into integer.
+        * In order to accurately calculate the maximum packet size when
+        * the data interval is more than 1 (i.e. ep->datainterval > 0),
+        * multiply by the data interval prior to rounding. For instance,
+        * a freqmax of 41 kHz will result in a max packet size of 6 (5.125)
+        * frames with a data interval of 1, but 11 (10.25) frames with a
+        * data interval of 2.
+        * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the
+        * maximum datainterval value of 3, at USB full speed, higher for
+        * USB high speed, noting that ep->freqmax is in units of
+        * frames per packet in Q16.16 format.)
+        */
+       maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
+                        (frame_bits >> 3);
+       if (tx_length_quirk)
+               maxsize += sizeof(__le32); /* Space for length descriptor */
        /* but wMaxPacketSize might reduce this */
        if (ep->maxpacksize && ep->maxpacksize < maxsize) {
                /* whatever fits into a max. size packet */
-               maxsize = ep->maxpacksize;
-               ep->freqmax = (maxsize / (frame_bits >> 3))
+               unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+               if (tx_length_quirk)
+                       /* Need to remove the length descriptor to calc freq */
+                       data_maxsize -= sizeof(__le32);
+               ep->freqmax = (data_maxsize / (frame_bits >> 3))
                                << (16 - ep->datainterval);
        }
 
index 417ebb11cf4896b8009c0aa86c7dab71c34d61a4..7661616f36361d142144842bd6595994829edec4 100644 (file)
@@ -1903,11 +1903,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
 
        hostif = &intf->altsetting[1];
        intfd = get_iface_desc(hostif);
+       /* If either or both of the endpoints support interrupt transfer,
+        * then use the alternate setting
+        */
        if (intfd->bNumEndpoints != 2 ||
-           (get_endpoint(hostif, 0)->bmAttributes &
-            USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
-           (get_endpoint(hostif, 1)->bmAttributes &
-            USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+           !((get_endpoint(hostif, 0)->bmAttributes &
+              USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
+             (get_endpoint(hostif, 1)->bmAttributes &
+              USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
                return;
 
        dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
index d3608c0a29f343f32179b33b74f537172484fb4b..fe91184ce83247d8b0360bab09e94c8f7cb720d8 100644 (file)
@@ -338,7 +338,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
        struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
        struct usb_mixer_interface *mixer = list->mixer;
        int index = kcontrol->private_value & 0xff;
-       int value = ucontrol->value.integer.value[0];
+       unsigned int value = ucontrol->value.integer.value[0];
        int old_value = kcontrol->private_value >> 8;
        int err;
 
index cdac5179db3f349f8ac61e010a8a9ff05c2e93ae..9245f52d43bdecfeb710b6cfc6ae99b3202aae90 100644 (file)
@@ -1383,6 +1383,56 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
                        subs->hwptr_done++;
                }
        }
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+                       int offset, int stride, unsigned int bytes)
+{
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
+               memcpy(urb->transfer_buffer + offset,
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + offset + bytes1,
+                      runtime->dma_area, bytes - bytes1);
+       } else {
+               memcpy(urb->transfer_buffer + offset,
+                      runtime->dma_area + subs->hwptr_done, bytes);
+       }
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+                                     struct urb *urb, int stride,
+                                     unsigned int bytes)
+{
+       __le32 packet_length;
+       int i;
+
+       /* Put __le32 length descriptor at start of each packet. */
+       for (i = 0; i < urb->number_of_packets; i++) {
+               unsigned int length = urb->iso_frame_desc[i].length;
+               unsigned int offset = urb->iso_frame_desc[i].offset;
+
+               packet_length = cpu_to_le32(length);
+               offset += i * sizeof(packet_length);
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length += sizeof(packet_length);
+               memcpy(urb->transfer_buffer + offset,
+                      &packet_length, sizeof(packet_length));
+               copy_to_urb(subs, urb, offset + sizeof(packet_length),
+                           stride, length);
+       }
+       /* Adjust transfer size accordingly. */
+       bytes += urb->number_of_packets * sizeof(packet_length);
+       return bytes;
 }
 
 static void prepare_playback_urb(struct snd_usb_substream *subs,
@@ -1460,27 +1510,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                }
 
                subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
        } else {
                /* usual PCM */
-               if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-                       /* err, the transferred area goes over buffer boundary. */
-                       unsigned int bytes1 =
-                               runtime->buffer_size * stride - subs->hwptr_done;
-                       memcpy(urb->transfer_buffer,
-                              runtime->dma_area + subs->hwptr_done, bytes1);
-                       memcpy(urb->transfer_buffer + bytes1,
-                              runtime->dma_area, bytes - bytes1);
-               } else {
-                       memcpy(urb->transfer_buffer,
-                              runtime->dma_area + subs->hwptr_done, bytes);
-               }
-
-               subs->hwptr_done += bytes;
+               if (!subs->tx_length_quirk)
+                       copy_to_urb(subs, urb, 0, stride, bytes);
+               else
+                       bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+                       /* bytes is now amount of outgoing data */
        }
 
-       if (subs->hwptr_done >= runtime->buffer_size * stride)
-               subs->hwptr_done -= runtime->buffer_size * stride;
-
        /* update delay with exact number of samples queued */
        runtime->delay = subs->last_delay;
        runtime->delay += frames;
index e4756651a52c8873457ae340450934dcfbd5dfad..1a1e2e4df35e5809e7f7d81b405ca6dddf2f4311 100644 (file)
@@ -2663,6 +2663,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                .type = QUIRK_MIDI_NOVATION
        }
 },
+{
+       USB_DEVICE(0x1235, 0x000a),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               /* .vendor_name = "Novation", */
+               /* .product_name = "Nocturn", */
+               .ifnum = 0,
+               .type = QUIRK_MIDI_RAW_BYTES
+       }
+},
 {
        USB_DEVICE(0x1235, 0x000e),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3182,25 +3191,19 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
 {
        /*
         * ZOOM R16/24 in audio interface mode.
-        * Mixer descriptors are garbage, further quirks will be needed
-        * to make any of it functional, thus disabled for now.
-        * Playback stream appears to start and run fine but no sound
-        * is produced, so also disabled for now.
+        * Playback requires an extra four byte LE length indicator
+        * at the start of each isochronous packet. This quirk is
+        * enabled in create_standard_audio_quirk().
         */
        USB_DEVICE(0x1686, 0x00dd),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
                .ifnum = QUIRK_ANY_INTERFACE,
                .type = QUIRK_COMPOSITE,
                .data = (const struct snd_usb_audio_quirk[]) {
-                       {
-                               /* Mixer */
-                               .ifnum = 0,
-                               .type = QUIRK_IGNORE_INTERFACE,
-                       },
                        {
                                /* Playback  */
                                .ifnum = 1,
-                               .type = QUIRK_IGNORE_INTERFACE,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE,
                        },
                        {
                                /* Capture */
index 00ebc0ca008e08b98b2f5fb6aed67dfb0d642ec1..4897ea171194f474c352c45579f62aa036d219ea 100644 (file)
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
        struct usb_interface_descriptor *altsd;
        int err;
 
+       if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
+               chip->tx_length_quirk = 1;
+
        alts = &iface->altsetting[0];
        altsd = get_iface_desc(alts);
        err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
index 970086015cded9f0a75f4d66a1471b1e99d9bfd4..8ee14f2365e749d964c369acef5ac45f95a060ca 100644 (file)
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
        subs->direction = stream;
        subs->dev = as->chip->dev;
        subs->txfr_quirk = as->chip->txfr_quirk;
+       subs->tx_length_quirk = as->chip->tx_length_quirk;
        subs->speed = snd_usb_get_speed(subs->dev);
        subs->pkt_offset_adj = 0;
 
index 33a176437e2e4fc34f5064fc23984bab60b22795..15a12715bd05154bd9c0b4bbe7084f3df15022b2 100644 (file)
@@ -43,6 +43,7 @@ struct snd_usb_audio {
        atomic_t usage_count;
        wait_queue_head_t shutdown_wait;
        unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+       unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
        
        int num_interfaces;
        int num_suspended_intf;