diff options
33 files changed, 250 insertions, 446 deletions
diff --git a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt deleted file mode 100644 index de8efbc7e4bd..000000000000 --- a/Documentation/sound/alsa/hda_codec.txt +++ /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. diff --git a/include/sound/pcm.h b/include/sound/pcm.h index a4fcc9456194..2882dddfc91c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1111,10 +1111,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 diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 6c96feeaf01e..e3e949126a56 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -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 diff --git a/sound/core/Makefile b/sound/core/Makefile index 3354f91e003a..48ab4b8f8279 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -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 diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7d45645f10ba..6dc4277937b8 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1883,8 +1883,10 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *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) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 139887011ba2..a8b27cdc2844 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -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) { @@ -1043,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 = { @@ -1093,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); @@ -1209,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); } } @@ -1268,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); } @@ -1374,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 = { diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 6ae50f50db62..f5fb62551c60 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,6 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp-stream.o amdtp-am824.o -snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 6cf470c80d1f..af7ed6643266 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -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 diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 7b86a6b99f07..07e5abdbceb5 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -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; diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 9ef228ef7baf..55b4be9b0034 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -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 diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 2108f7f1a764..a6a39f7ef58d 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -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: diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 70a111d7f428..5d99436dfcae 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -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), diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index bbe3be7fea9b..1f33b7a1fca4 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -34,8 +34,7 @@ static int name_card(struct snd_dg00x *dg00x) strcpy(dg00x->card->mixername, model); snprintf(dg00x->card->longname, sizeof(dg00x->card->longname), "Digidesign %s, GUID %08x%08x at %s, S%d", model, - cpu_to_be32(fw_dev->config_rom[3]), - cpu_to_be32(fw_dev->config_rom[4]), + fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed); return 0; diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index 0c7440826db8..15ef7f75a8ef 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -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 diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c index 166f80584c2a..94bab0476a65 100644 --- a/sound/firewire/fireworks/fireworks_command.c +++ b/sound/firewire/fireworks/fireworks_command.c @@ -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; diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index edf1c8bd25a6..f80aafa44c89 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -74,7 +74,11 @@ static void async_midi_port_callback(struct fw_card *card, int rcode, struct snd_fw_async_midi_port *port = callback_data; struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream); - if (rcode == RCODE_COMPLETE && substream != NULL) + /* 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. */ diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index a926850864f6..06ff50f4e6c0 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -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 diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 37a86cf69cbf..8665e1043d41 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -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 */ diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 2c63058bd245..7cb5743c073b 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -148,7 +148,7 @@ 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) { @@ -629,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 */ @@ -648,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; } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index d606e3a9ce97..588b93f20c2e 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -18,6 +18,7 @@ #define VENDOR_GRIFFIN 0x001292 #define VENDOR_BEHRINGER 0x001564 #define VENDOR_LACIE 0x00d04b +#define VENDOR_TASCAM 0x00022e #define MODEL_SATELLITE 0x00200f @@ -154,6 +155,15 @@ static void detect_quirks(struct snd_oxfw *oxfw) */ 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, @@ -323,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); diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 1c9a88be55c8..d4f64ae182e7 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -158,7 +158,7 @@ static void handle_midi_tx(struct fw_card *card, struct fw_request *request, port = b[0] >> 4; /* TODO: support virtual MIDI ports. */ - if (port > tscm->spec->midi_capture_ports) + if (port >= tscm->spec->midi_capture_ports) goto end; /* Assume the message length. */ @@ -249,7 +249,7 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm) /* Turn on messaging. */ reg = cpu_to_be32(0x00000001); - return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, ®, sizeof(reg), 0); if (err < 0) diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index c6747a45795b..ee0bc1839508 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -40,7 +40,7 @@ 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[8]; + char model[9]; unsigned int i; u8 c; @@ -73,8 +73,7 @@ static int identify_model(struct snd_tscm *tscm) strcpy(tscm->card->mixername, model); snprintf(tscm->card->longname, sizeof(tscm->card->longname), "TASCAM %s, GUID %08x%08x at %s, S%d", model, - cpu_to_be32(fw_dev->config_rom[3]), - cpu_to_be32(fw_dev->config_rom[4]), + fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&tscm->unit->device), 100 << fw_dev->max_speed); return 0; diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 152acdaa0a45..70671ad65d24 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -63,11 +63,11 @@ int snd_hda_codec_set_name(struct hda_codec *codec, const char *name) /* update the mixer name */ if (!*codec->card->mixername || - codec->mixer_assigned >= codec->core.addr) { + 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->mixer_assigned = codec->core.addr; + codec->bus->mixer_assigned = codec->core.addr; } return 0; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0e55c6a6cc7e..2eeaf5ea20f9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -851,7 +851,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - codec->mixer_assigned = -1; #ifdef CONFIG_PM codec->power_jiffies = jiffies; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 055a80522282..373fcad840ea 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -70,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 */ @@ -260,7 +261,6 @@ struct hda_codec { unsigned long power_off_acct; unsigned long power_jiffies; #endif - unsigned int mixer_assigned; /* filter the requested power state per nid */ unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid, @@ -301,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); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 944455997fdc..d6b93a20361b 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -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) diff --git a/sound/usb/card.h b/sound/usb/card.h index ef580b43f1e3..71778ca4b26a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -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) */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a77d9c812dc6..7b1cb365ffab 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -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) { /* @@ -627,11 +652,17 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, */ 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); } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index cdac5179db3f..9245f52d43bd 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -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; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index e4756651a52c..1a1e2e4df35e 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2664,6 +2664,15 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + 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) { /* .vendor_name = "Novation", */ @@ -3182,10 +3191,9 @@ 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) { @@ -3193,14 +3201,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), .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 */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 00ebc0ca008e..4897ea171194 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -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); diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 970086015cde..8ee14f2365e7 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -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; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 33a176437e2e..15a12715bd05 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -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; |