diff options
60 files changed, 1114 insertions, 564 deletions
diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index 21ab5e6f7062..5f31fa5e2435 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -70,7 +70,7 @@ dsp_map PCM device number maps assigned to the 1st OSS device; Default: 0 adsp_map - PCM device number maps assigned to the 2st OSS device; + PCM device number maps assigned to the 2nd OSS device; Default: 1 nonblock_open Don't block opening busy PCM devices; @@ -97,7 +97,7 @@ midi_map MIDI device number maps assigned to the 1st OSS device; Default: 0 amidi_map - MIDI device number maps assigned to the 2st OSS device; + MIDI device number maps assigned to the 2nd OSS device; Default: 1 Module snd-soc-core @@ -727,9 +727,9 @@ Module for EMU10K1/EMU10k2 based PCI sound cards. * Sound Blaster Audigy extin - bitmap of available external inputs for FX8010 (see bellow) + bitmap of available external inputs for FX8010 (see below) extout - bitmap of available external outputs for FX8010 (see bellow) + bitmap of available external outputs for FX8010 (see below) seq_ports allocated sequencer ports (4 by default) max_synth_voices diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst index f3f4640ee2af..c506f8d16f2e 100644 --- a/Documentation/sound/cards/audigy-mixer.rst +++ b/Documentation/sound/cards/audigy-mixer.rst @@ -17,7 +17,7 @@ Digital mixer controls ====================== These controls are built using the DSP instructions. They offer extended -functionality. Only the default build-in code in the ALSA driver is described +functionality. Only the default built-in code in the ALSA driver is described here. Note that the controls work as attenuators: the maximum value is the neutral position leaving the signal unchanged. Note that if the same destination is mentioned in multiple controls, the signal is accumulated and can be wrapped diff --git a/Documentation/sound/cards/maya44.rst b/Documentation/sound/cards/maya44.rst index bf09a584b443..ab973f66c973 100644 --- a/Documentation/sound/cards/maya44.rst +++ b/Documentation/sound/cards/maya44.rst @@ -156,7 +156,7 @@ IEC958 Output S/PDIF should output the same signal as channel 3+4. [untested!] -Digitial output selectors +Digital output selectors These switches allow a direct digital routing from the ADCs to the DACs. Each switch determines where the digital input data to one of the DACs comes from. They are not supported by the ESI windows driver. diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst index 2ce41d3822d8..357fcd619d39 100644 --- a/Documentation/sound/cards/sb-live-mixer.rst +++ b/Documentation/sound/cards/sb-live-mixer.rst @@ -31,7 +31,7 @@ Digital mixer controls ====================== These controls are built using the DSP instructions. They offer extended -functionality. Only the default build-in code in the ALSA driver is described +functionality. Only the default built-in code in the ALSA driver is described here. Note that the controls work as attenuators: the maximum value is the neutral position leaving the signal unchanged. Note that if the same destination is mentioned in multiple controls, the signal is accumulated and can be wrapped diff --git a/Documentation/sound/designs/jack-controls.rst b/Documentation/sound/designs/jack-controls.rst index ae25b1531bb0..e8a18f126a63 100644 --- a/Documentation/sound/designs/jack-controls.rst +++ b/Documentation/sound/designs/jack-controls.rst @@ -8,7 +8,7 @@ Why we need Jack kcontrols ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...) to user space. This means userspace applications like pulseaudio can switch off headphones and switch on speakers when no headphones are -pluged in. +plugged in. The old ALSA jack code only created input devices for each registered jack. These jack input devices are not readable by userspace devices diff --git a/Documentation/sound/designs/seq-oss.rst b/Documentation/sound/designs/seq-oss.rst index e82ffe0e7f43..ec6304a07441 100644 --- a/Documentation/sound/designs/seq-oss.rst +++ b/Documentation/sound/designs/seq-oss.rst @@ -96,7 +96,7 @@ if you use an AWE64 card, you'll see like the following: Number of synth devices: 1 synth 0: [EMU8000] type 0x1 : subtype 0x20 : voices 32 - capabilties : ioctl enabled / load_patch enabled + capabilities : ioctl enabled / load_patch enabled Number of MIDI devices: 3 midi 0: [Emu8000 Port-0] ALSA port 65:0 diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index d118b6fe269b..3432feb0fa33 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -500,7 +500,7 @@ add_jack_modes (bool) change the headphone amp and mic bias VREF capabilities power_save_node (bool) advanced power management for each widget, controlling the power - sate (D0/D3) of each widget node depending on the actual pin and + state (D0/D3) of each widget node depending on the actual pin and stream states power_down_unused (bool) power down the unused widgets, a subset of power_save_node, and diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index 07a620c5ca74..5c9523b7d55c 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -1720,16 +1720,16 @@ Typically, you'll have a hardware descriptor as below: - ``rate_min`` and ``rate_max`` define the minimum and maximum sample rate. This should correspond somehow to ``rates`` bits. -- ``channel_min`` and ``channel_max`` define, as you might already +- ``channels_min`` and ``channels_max`` define, as you might already expected, the minimum and maximum number of channels. - ``buffer_bytes_max`` defines the maximum buffer size in bytes. There is no ``buffer_bytes_min`` field, since it can be calculated from the minimum period size and the minimum number of - periods. Meanwhile, ``period_bytes_min`` and define the minimum and - maximum size of the period in bytes. ``periods_max`` and - ``periods_min`` define the maximum and minimum number of periods in - the buffer. + periods. Meanwhile, ``period_bytes_min`` and ``period_bytes_max`` + define the minimum and maximum size of the period in bytes. + ``periods_max`` and ``periods_min`` define the maximum and minimum + number of periods in the buffer. The “period” is a term that corresponds to a fragment in the OSS world. The period defines the size at which a PCM interrupt is diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 958aa4662ccb..2c16ee8fd842 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -111,6 +111,7 @@ struct inbound_transaction_resource { struct client_resource resource; struct fw_card *card; struct fw_request *request; + bool is_fcp; void *data; size_t length; }; @@ -643,19 +644,14 @@ static int ioctl_send_request(struct client *client, union ioctl_arg *arg) client->device->max_speed); } -static inline bool is_fcp_request(struct fw_request *request) -{ - return request == NULL; -} - static void release_request(struct client *client, struct client_resource *resource) { struct inbound_transaction_resource *r = container_of(resource, struct inbound_transaction_resource, resource); - if (is_fcp_request(r->request)) - kfree(r->data); + if (r->is_fcp) + fw_request_put(r->request); else fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); @@ -669,15 +665,20 @@ static void handle_request(struct fw_card *card, struct fw_request *request, void *payload, size_t length, void *callback_data) { struct address_handler_resource *handler = callback_data; + bool is_fcp = is_in_fcp_region(offset, length); struct inbound_transaction_resource *r; struct inbound_transaction_event *e; size_t event_size0; - void *fcp_frame = NULL; int ret; /* card may be different from handler->client->device->card */ fw_card_get(card); + // Extend the lifetime of data for request so that its payload is safely accessible in + // the process context for the client. + if (is_fcp) + fw_request_get(request); + r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); if (r == NULL || e == NULL) @@ -685,21 +686,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r->card = card; r->request = request; + r->is_fcp = is_fcp; r->data = payload; r->length = length; - if (is_fcp_request(request)) { - /* - * FIXME: Let core-transaction.c manage a - * single reference-counted copy? - */ - fcp_frame = kmemdup(payload, length, GFP_ATOMIC); - if (fcp_frame == NULL) - goto failed; - - r->data = fcp_frame; - } - r->resource.release = release_request; ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); if (ret < 0) @@ -741,10 +731,11 @@ static void handle_request(struct fw_card *card, struct fw_request *request, failed: kfree(r); kfree(e); - kfree(fcp_frame); - if (!is_fcp_request(request)) + if (!is_fcp) fw_send_response(card, request, RCODE_CONFLICT_ERROR); + else + fw_request_put(request); fw_card_put(card); } @@ -819,19 +810,19 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) r = container_of(resource, struct inbound_transaction_resource, resource); - if (is_fcp_request(r->request)) { - kfree(r->data); + if (r->is_fcp) { + fw_request_put(r->request); goto out; } if (a->length != fw_get_response_length(r->request)) { ret = -EINVAL; - kfree(r->request); + fw_request_put(r->request); goto out; } if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { ret = -EFAULT; - kfree(r->request); + fw_request_put(r->request); goto out; } fw_send_response(r->card, r->request, a->rcode); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index af498d767702..a9f70c96323e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -535,12 +535,6 @@ const struct fw_address_region fw_unit_space_region = { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; #endif /* 0 */ -static bool is_in_fcp_region(u64 offset, size_t length) -{ - return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && - offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END); -} - /** * fw_core_add_address_handler() - register for incoming requests * @handler: callback @@ -617,6 +611,7 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler) EXPORT_SYMBOL(fw_core_remove_address_handler); struct fw_request { + struct kref kref; struct fw_packet response; u32 request_header[4]; int ack; @@ -625,13 +620,33 @@ struct fw_request { u32 data[]; }; +void fw_request_get(struct fw_request *request) +{ + kref_get(&request->kref); +} + +static void release_request(struct kref *kref) +{ + struct fw_request *request = container_of(kref, struct fw_request, kref); + + kfree(request); +} + +void fw_request_put(struct fw_request *request) +{ + kref_put(&request->kref, release_request); +} + static void free_response_callback(struct fw_packet *packet, struct fw_card *card, int status) { - struct fw_request *request; + struct fw_request *request = container_of(packet, struct fw_request, response); - request = container_of(packet, struct fw_request, response); - kfree(request); + // Decrease the reference count since not at in-flight. + fw_request_put(request); + + // Decrease the reference count to release the object. + fw_request_put(request); } int fw_get_response_length(struct fw_request *r) @@ -782,6 +797,7 @@ static struct fw_request *allocate_request(struct fw_card *card, request = kmalloc(sizeof(*request) + length, GFP_ATOMIC); if (request == NULL) return NULL; + kref_init(&request->kref); request->response.speed = p->speed; request->response.timestamp = @@ -800,16 +816,22 @@ static struct fw_request *allocate_request(struct fw_card *card, return request; } +/** + * fw_send_response: - send response packet for asynchronous transaction. + * @card: interface to send the response at. + * @request: firewire request data for the transaction. + * @rcode: response code to send. + * + * Submit a response packet into the asynchronous response transmission queue. The @request + * is going to be released when the transmission successfully finishes later. + */ void fw_send_response(struct fw_card *card, struct fw_request *request, int rcode) { - if (WARN_ONCE(!request, "invalid for FCP address handlers")) - return; - /* unified transaction or broadcast transaction: don't respond */ if (request->ack != ACK_PENDING || HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { - kfree(request); + fw_request_put(request); return; } @@ -821,6 +843,9 @@ void fw_send_response(struct fw_card *card, fw_fill_response(&request->response, request->request_header, rcode, NULL, 0); + // Increase the reference count so that the object is kept during in-flight. + fw_request_get(request); + card->driver->send_response(card, &request->response); } EXPORT_SYMBOL(fw_send_response); @@ -910,7 +935,7 @@ static void handle_fcp_region_request(struct fw_card *card, rcu_read_lock(); list_for_each_entry_rcu(handler, &address_handler_list, link) { if (is_enclosing_handler(handler, offset, request->length)) - handler->address_callback(card, NULL, tcode, + handler->address_callback(card, request, tcode, destination, source, p->generation, offset, request->data, diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 71d5f16f311c..eafa4eaae737 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -244,6 +244,9 @@ int fw_get_response_length(struct fw_request *request); void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length); +void fw_request_get(struct fw_request *request); +void fw_request_put(struct fw_request *request); + #define FW_PHY_CONFIG_NO_NODE_ID -1 #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, @@ -254,4 +257,10 @@ static inline bool is_ping_packet(u32 *data) return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1]; } +static inline bool is_in_fcp_region(u64 offset, size_t length) +{ + return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && + offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END); +} + #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c index 9a2331eb1bfa..663acbb1854c 100644 --- a/drivers/mfd/wm97xx-core.c +++ b/drivers/mfd/wm97xx-core.c @@ -319,13 +319,11 @@ err_free_compat: return ret; } -static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +static void wm97xx_ac97_remove(struct ac97_codec_device *adev) { struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); snd_ac97_compat_release(wm97xx->ac97); - - return 0; } static const struct ac97_id wm97xx_ac97_ids[] = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 980019053e54..56505436d159 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -278,9 +278,8 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, * Otherwise there is a danger of recursion of inbound and outbound * transactions from and to the local node. * - * The callback is responsible that either fw_send_response() or kfree() - * is called on the @request, except for FCP registers for which the core - * takes care of that. + * The callback is responsible that fw_send_response() is called on the @request, except for FCP + * registers for which the core takes care of that. */ typedef void (*fw_address_callback_t)(struct fw_card *card, struct fw_request *request, diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h index 9792d25fa369..2fc641cb1982 100644 --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -63,7 +63,7 @@ struct ac97_codec_device { struct ac97_codec_driver { struct device_driver driver; int (*probe)(struct ac97_codec_device *); - int (*remove)(struct ac97_codec_device *); + void (*remove)(struct ac97_codec_device *dev); void (*shutdown)(struct ac97_codec_device *); const struct ac97_id *id_table; }; diff --git a/include/sound/core.h b/include/sound/core.h index 4365c35d038b..3edc4ab08774 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -286,10 +286,10 @@ int snd_devm_card_new(struct device *parent, int idx, const char *xid, struct module *module, size_t extra_size, struct snd_card **card_ret); -int snd_card_disconnect(struct snd_card *card); +void snd_card_disconnect(struct snd_card *card); void snd_card_disconnect_sync(struct snd_card *card); -int snd_card_free(struct snd_card *card); -int snd_card_free_when_closed(struct snd_card *card); +void snd_card_free(struct snd_card *card); +void snd_card_free_when_closed(struct snd_card *card); int snd_card_free_on_error(struct device *dev, int ret); void snd_card_set_id(struct snd_card *card, const char *id); int snd_card_register(struct snd_card *card); diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index eba23daf2c29..bbb7805e85d8 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -259,6 +259,7 @@ struct hda_codec { unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ + unsigned int ctl_dev_id:1; /* old control element id build behaviour */ #ifdef CONFIG_PM unsigned long power_on_acct; diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index a6872537724d..536612c6ab0c 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -575,7 +575,7 @@ void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev); int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev); int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int format_val); -void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start); +void snd_hdac_stream_start(struct hdac_stream *azx_dev); void snd_hdac_stream_stop(struct hdac_stream *azx_dev); void snd_hdac_stop_streams(struct hdac_bus *bus); void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus); diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 3532ac7046d7..1e86872c151f 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -14,6 +14,7 @@ #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479 #define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL 0x7473636d #define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE 0x4d545244 +#define SNDRV_FIREWIRE_EVENT_FF400_MESSAGE 0x4f6c6761 struct snd_firewire_event_common { unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ @@ -72,6 +73,30 @@ struct snd_firewire_event_motu_register_dsp_change { __u32 changes[]; /* Encoded event for change of register DSP. */ }; +/** + * struct snd_firewire_event_ff400_message - the container for message from Fireface 400 when + * operating hardware knob. + * + * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE. + * @message_count: The number of messages. + * @messages.message: The messages expressing hardware knob operation. + * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous + * transaction was sent to deliver the message. It has 16 bit unsigned integer + * value. The higher 3 bits of value expresses the lower three bits of second + * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle + * field up to 7999. + * + * The structure expresses message transmitted by Fireface 400 when operating hardware knob. + */ +struct snd_firewire_event_ff400_message { + unsigned int type; + unsigned int message_count; + struct { + __u32 message; + __u32 tstamp; + } messages[]; +}; + union snd_firewire_event { struct snd_firewire_event_common common; struct snd_firewire_event_lock_status lock_status; @@ -81,6 +106,7 @@ union snd_firewire_event { struct snd_firewire_event_tascam_control tascam_control; struct snd_firewire_event_motu_notification motu_notification; struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change; + struct snd_firewire_event_ff400_message ff400_message; }; diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 045330883a96..6067c04ce4c0 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -524,10 +524,9 @@ static void ac97_bus_remove(struct device *dev) if (ret < 0) return; - ret = adrv->remove(adev); + adrv->remove(adev); pm_runtime_put_noidle(dev); - if (ret == 0) - ac97_put_disable_clk(adev); + ac97_put_disable_clk(adev); pm_runtime_disable(dev); } diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index ec4ef18555bc..850dc8c53e9b 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1094,7 +1094,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) return -ENODEV; } -static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) +static void aoa_fabric_layout_remove(struct soundbus_dev *sdev) { struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); int i; @@ -1123,7 +1123,6 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) kfree(ldev); sdev->pcmid = -1; sdev->pcmname = NULL; - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index 3a99c1f1a3ca..db40f9d042b4 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -185,7 +185,7 @@ struct soundbus_driver { /* we don't implement any matching at all */ int (*probe)(struct soundbus_dev* dev); - int (*remove)(struct soundbus_dev* dev); + void (*remove)(struct soundbus_dev *dev); int (*shutdown)(struct soundbus_dev* dev); diff --git a/sound/core/init.c b/sound/core/init.c index 5377f94eb211..df0c22480375 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -489,17 +489,17 @@ static const struct file_operations snd_shutdown_f_ops = * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). */ -int snd_card_disconnect(struct snd_card *card) +void snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; if (!card) - return -EINVAL; + return; spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); - return 0; + return; } card->shutdown = 1; @@ -548,7 +548,6 @@ int snd_card_disconnect(struct snd_card *card) wake_up(&card->power_sleep); snd_power_sync_ref(card); #endif - return 0; } EXPORT_SYMBOL(snd_card_disconnect); @@ -563,15 +562,7 @@ EXPORT_SYMBOL(snd_card_disconnect); */ void snd_card_disconnect_sync(struct snd_card *card) { - int err; - - err = snd_card_disconnect(card); - if (err < 0) { - dev_err(card->dev, - "snd_card_disconnect error (%d), skipping sync\n", - err); - return; - } + snd_card_disconnect(card); spin_lock_irq(&card->files_lock); wait_event_lock_irq(card->remove_sleep, @@ -617,13 +608,14 @@ static int snd_card_do_free(struct snd_card *card) * * Return: zero if successful, or a negative error code */ -int snd_card_free_when_closed(struct snd_card *card) +void snd_card_free_when_closed(struct snd_card *card) { - int ret = snd_card_disconnect(card); - if (ret) - return ret; + if (!card) + return; + + snd_card_disconnect(card); put_device(&card->card_dev); - return 0; + return; } EXPORT_SYMBOL(snd_card_free_when_closed); @@ -640,10 +632,9 @@ EXPORT_SYMBOL(snd_card_free_when_closed); * Return: Zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ -int snd_card_free(struct snd_card *card) +void snd_card_free(struct snd_card *card) { DECLARE_COMPLETION_ONSTACK(released); - int ret; /* The call of snd_card_free() is allowed from various code paths; * a manual call from the driver and the call via devres_free, and @@ -652,16 +643,13 @@ int snd_card_free(struct snd_card *card) * the check here at the beginning. */ if (card->releasing) - return 0; + return; card->release_completion = &released; - ret = snd_card_free_when_closed(card); - if (ret) - return ret; + snd_card_free_when_closed(card); + /* wait, until all devices are ready for the free operation */ wait_for_completion(&released); - - return 0; } EXPORT_SYMBOL(snd_card_free); diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index d9c700f652bb..3660c312bf33 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -36,8 +36,6 @@ struct amdtp_am824 { u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; u8 midi_position; - - unsigned int frame_multiplier; }; /** @@ -59,8 +57,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, { struct amdtp_am824 *p = s->protocol; unsigned int midi_channels; - unsigned int i; - int err; + unsigned int pcm_frame_multiplier; + int i, err; if (amdtp_stream_running(s)) return -EINVAL; @@ -77,8 +75,18 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) return -EINVAL; - err = amdtp_stream_set_parameters(s, rate, - pcm_channels + midi_channels); + /* + * 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) + pcm_frame_multiplier = 2; + else + pcm_frame_multiplier = 1; + + err = amdtp_stream_set_parameters(s, rate, pcm_channels + midi_channels, + pcm_frame_multiplier); if (err < 0) return err; @@ -88,16 +96,6 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, 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; @@ -346,23 +344,20 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; if (pcm) { write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); - pcm_frames += data_blocks * p->frame_multiplier; + pcm_frames += data_blocks * s->pcm_frame_multiplier; } else { write_pcm_silence(s, buf, data_blocks); } @@ -371,37 +366,34 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks, desc->data_block_counter); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; if (pcm) { read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); - pcm_frames += data_blocks * p->frame_multiplier; + pcm_frames += data_blocks * s->pcm_frame_multiplier; } if (p->midi_ports) { read_midi_messages(s, buf, data_blocks, desc->data_block_counter); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } /** diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 5fd2aeccdfc2..208f97cf8de6 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -14,9 +14,10 @@ #include <linux/tracepoint.h> TRACE_EVENT(amdtp_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index), - TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index), + TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index, u32 curr_cycle_time), + TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index, curr_cycle_time), TP_STRUCT__entry( + __field(unsigned int, cycle_time) __field(unsigned int, second) __field(unsigned int, cycle) __field(int, channel) @@ -31,6 +32,7 @@ TRACE_EVENT(amdtp_packet, __field(unsigned int, index) ), TP_fast_assign( + __entry->cycle_time = curr_cycle_time; __entry->second = cycles / CYCLES_PER_SECOND; __entry->cycle = cycles % CYCLES_PER_SECOND; __entry->channel = s->context->channel; @@ -53,7 +55,8 @@ TRACE_EVENT(amdtp_packet, __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s", + "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s", + __entry->cycle_time, __entry->second, __entry->cycle, __entry->src, diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9be2260e4ca2..a13c0b408aad 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -271,12 +271,14 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * @s: the AMDTP stream to configure * @rate: the sample rate * @data_block_quadlets: the size of a data block in quadlet unit + * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP + * events. * * 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 data_block_quadlets, unsigned int pcm_frame_multiplier) { unsigned int sfc; @@ -298,6 +300,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, if (s->flags & CIP_BLOCKING) s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + s->pcm_frame_multiplier = pcm_frame_multiplier; + return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -348,27 +352,29 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); +#define prev_packet_desc(s, desc) \ + list_prev_entry_circular(desc, &s->packet_descs_list, link) + static void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, - unsigned int count) + unsigned int size, unsigned int pos, unsigned int count) { const unsigned int syt_interval = s->syt_interval; int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; if (desc->syt_offset != CIP_SYT_NO_INFO) desc->data_blocks = syt_interval; else desc->data_blocks = 0; - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } } static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, + unsigned int size, unsigned int pos, unsigned int count) { const enum cip_sfc sfc = s->sfc; @@ -376,7 +382,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; if (!cip_sfc_is_base_44100(sfc)) { // Sample_rate / 8000 is an integer, and precomputed. @@ -403,7 +409,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se state = phase; } - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } s->ctx_data.rx.data_block_state = state; @@ -449,8 +455,7 @@ static unsigned int calculate_syt_offset(unsigned int *last_syt_offset, } static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, - unsigned int count) + unsigned int size, unsigned int pos, unsigned int count) { const enum cip_sfc sfc = s->sfc; unsigned int last = s->ctx_data.rx.last_syt_offset; @@ -458,11 +463,11 @@ static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *desc int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; desc->syt_offset = calculate_syt_offset(&last, &state, sfc); - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } s->ctx_data.rx.last_syt_offset = last; @@ -497,7 +502,7 @@ static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle, static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head) { const unsigned int cache_size = s->ctx_data.tx.cache.size; - unsigned int cycles = s->ctx_data.tx.cache.tail; + unsigned int cycles = s->ctx_data.tx.cache.pos; if (cycles < head) cycles += cache_size; @@ -506,18 +511,17 @@ static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigne return cycles; } -static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsigned int desc_count) +static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count) { const unsigned int transfer_delay = s->transfer_delay; const unsigned int cache_size = s->ctx_data.tx.cache.size; struct seq_desc *cache = s->ctx_data.tx.cache.descs; - unsigned int cache_tail = s->ctx_data.tx.cache.tail; + unsigned int cache_pos = s->ctx_data.tx.cache.pos; bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); int i; for (i = 0; i < desc_count; ++i) { - struct seq_desc *dst = cache + cache_tail; - const struct pkt_desc *src = descs + i; + struct seq_desc *dst = cache + cache_pos; if (aware_syt && src->syt != CIP_SYT_NO_INFO) dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay); @@ -525,70 +529,68 @@ static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsi dst->syt_offset = CIP_SYT_NO_INFO; dst->data_blocks = src->data_blocks; - cache_tail = (cache_tail + 1) % cache_size; + cache_pos = (cache_pos + 1) % cache_size; + src = amdtp_stream_next_packet_desc(s, src); } - s->ctx_data.tx.cache.tail = cache_tail; + s->ctx_data.tx.cache.pos = cache_pos; } -static void pool_ideal_seq_descs(struct amdtp_stream *s, unsigned int count) +static void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { - struct seq_desc *descs = s->ctx_data.rx.seq.descs; - unsigned int seq_tail = s->ctx_data.rx.seq.tail; - const unsigned int seq_size = s->ctx_data.rx.seq.size; - - pool_ideal_syt_offsets(s, descs, seq_size, seq_tail, count); + pool_ideal_syt_offsets(s, descs, size, pos, count); if (s->flags & CIP_BLOCKING) - pool_blocking_data_blocks(s, descs, seq_size, seq_tail, count); + pool_blocking_data_blocks(s, descs, size, pos, count); else - pool_ideal_nonblocking_data_blocks(s, descs, seq_size, seq_tail, count); - - s->ctx_data.rx.seq.tail = (seq_tail + count) % seq_size; + pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count); } -static void pool_replayed_seq(struct amdtp_stream *s, unsigned int count) +static void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { struct amdtp_stream *target = s->ctx_data.rx.replay_target; const struct seq_desc *cache = target->ctx_data.tx.cache.descs; const unsigned int cache_size = target->ctx_data.tx.cache.size; - unsigned int cache_head = s->ctx_data.rx.cache_head; - struct seq_desc *descs = s->ctx_data.rx.seq.descs; - const unsigned int seq_size = s->ctx_data.rx.seq.size; - unsigned int seq_tail = s->ctx_data.rx.seq.tail; + unsigned int cache_pos = s->ctx_data.rx.cache_pos; int i; for (i = 0; i < count; ++i) { - descs[seq_tail] = cache[cache_head]; - seq_tail = (seq_tail + 1) % seq_size; - cache_head = (cache_head + 1) % cache_size; + descs[pos] = cache[cache_pos]; + cache_pos = (cache_pos + 1) % cache_size; + pos = (pos + 1) % size; } - s->ctx_data.rx.seq.tail = seq_tail; - s->ctx_data.rx.cache_head = cache_head; + s->ctx_data.rx.cache_pos = cache_pos; } -static void pool_seq_descs(struct amdtp_stream *s, unsigned int count) +static void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { struct amdtp_domain *d = s->domain; + void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count); if (!d->replay.enable || !s->ctx_data.rx.replay_target) { - pool_ideal_seq_descs(s, count); + pool_seq_descs = pool_ideal_seq_descs; } else { if (!d->replay.on_the_fly) { - pool_replayed_seq(s, count); + pool_seq_descs = pool_replayed_seq; } else { struct amdtp_stream *tx = s->ctx_data.rx.replay_target; const unsigned int cache_size = tx->ctx_data.tx.cache.size; - const unsigned int cache_head = s->ctx_data.rx.cache_head; - unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_head); + const unsigned int cache_pos = s->ctx_data.rx.cache_pos; + unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos); if (cached_cycles > count && cached_cycles > cache_size / 2) - pool_replayed_seq(s, count); + pool_seq_descs = pool_replayed_seq; else - pool_ideal_seq_descs(s, count); + pool_seq_descs = pool_ideal_seq_descs; } } + + pool_seq_descs(s, descs, size, pos, count); } static void update_pcm_pointers(struct amdtp_stream *s, @@ -679,7 +681,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, struct fw_iso_packet *params, unsigned int header_length, unsigned int data_blocks, unsigned int data_block_counter, - unsigned int syt, unsigned int index) + unsigned int syt, unsigned int index, u32 curr_cycle_time) { unsigned int payload_length; __be32 *cip_header; @@ -696,7 +698,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, } trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks, - data_block_counter, s->packet_index, index); + data_block_counter, s->packet_index, index, curr_cycle_time); } static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, @@ -798,7 +800,8 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, const __be32 *ctx_header, unsigned int *data_blocks, unsigned int *data_block_counter, - unsigned int *syt, unsigned int packet_index, unsigned int index) + unsigned int *syt, unsigned int packet_index, unsigned int index, + u32 curr_cycle_time) { unsigned int payload_length; const __be32 *cip_header; @@ -843,7 +846,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, } trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks, - *data_block_counter, packet_index, index); + *data_block_counter, packet_index, index, curr_cycle_time); return 0; } @@ -851,10 +854,15 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, // In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On // the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent // it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. +static inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp) +{ + return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff); +} + static inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp) { u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK; - return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff); + return compute_ohci_iso_ctx_cycle_count(tstamp); } static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend) @@ -865,6 +873,14 @@ static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend) return cycle; } +static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend) +{ + if (minuend < subtrahend) + minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND; + + return minuend - subtrahend; +} + static int compare_ohci_cycle_count(u32 lval, u32 rval) { if (lval == rval) @@ -886,22 +902,23 @@ static inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp, return increment_ohci_cycle_count(cycle, queue_size); } -static int generate_device_pkt_descs(struct amdtp_stream *s, - struct pkt_desc *descs, - const __be32 *ctx_header, - unsigned int packets, - unsigned int *desc_count) +static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, + const __be32 *ctx_header, unsigned int packet_count, + unsigned int *desc_count) { unsigned int next_cycle = s->next_cycle; unsigned int dbc = s->data_block_counter; unsigned int packet_index = s->packet_index; unsigned int queue_size = s->queue_size; + u32 curr_cycle_time = 0; int i; int err; + if (trace_amdtp_packet_enabled()) + (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); + *desc_count = 0; - for (i = 0; i < packets; ++i) { - struct pkt_desc *desc = descs + *desc_count; + for (i = 0; i < packet_count; ++i) { unsigned int cycle; bool lost; unsigned int data_blocks; @@ -925,7 +942,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, desc->data_blocks = 0; desc->data_block_counter = dbc; desc->ctx_payload = NULL; - ++desc; + desc = amdtp_stream_next_packet_desc(s, desc); ++(*desc_count); } } else if (s->flags & CIP_JUMBO_PAYLOAD) { @@ -944,7 +961,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, } err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt, - packet_index, i); + packet_index, i, curr_cycle_time); if (err < 0) return err; @@ -958,6 +975,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, dbc = (dbc + desc->data_blocks) & 0xff; next_cycle = increment_ohci_cycle_count(next_cycle, 1); + desc = amdtp_stream_next_packet_desc(s, desc); ++(*desc_count); ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); packet_index = (packet_index + 1) % queue_size; @@ -980,20 +998,21 @@ static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, return syt & CIP_SYT_MASK; } -static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header, unsigned int packets) +static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, + const __be32 *ctx_header, unsigned int packet_count) { - struct pkt_desc *descs = s->pkt_descs; - const struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs; - const unsigned int seq_size = s->ctx_data.rx.seq.size; + struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs; + unsigned int seq_size = s->ctx_data.rx.seq.size; + unsigned int seq_pos = s->ctx_data.rx.seq.pos; unsigned int dbc = s->data_block_counter; - unsigned int seq_head = s->ctx_data.rx.seq.head; bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); int i; - for (i = 0; i < packets; ++i) { - struct pkt_desc *desc = descs + i; + pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count); + + for (i = 0; i < packet_count; ++i) { unsigned int index = (s->packet_index + i) % s->queue_size; - const struct seq_desc *seq = seq_descs + seq_head; + const struct seq_desc *seq = seq_descs + seq_pos; desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size); @@ -1014,13 +1033,14 @@ static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header, desc->ctx_payload = s->buffer.packets[index].buffer; - seq_head = (seq_head + 1) % seq_size; + seq_pos = (seq_pos + 1) % seq_size; + desc = amdtp_stream_next_packet_desc(s, desc); ++ctx_header; } s->data_block_counter = dbc; - s->ctx_data.rx.seq.head = seq_head; + s->ctx_data.rx.seq.pos = seq_pos; } static inline void cancel_stream(struct amdtp_stream *s) @@ -1031,17 +1051,85 @@ static inline void cancel_stream(struct amdtp_stream *s) WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } +static snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) +{ + unsigned int data_block_count = 0; + u32 latest_cycle; + u32 cycle_time; + u32 curr_cycle; + u32 cycle_gap; + int i, err; + + if (count == 0) + goto end; + + // Forward to the latest record. + for (i = 0; i < count - 1; ++i) + desc = amdtp_stream_next_packet_desc(s, desc); + latest_cycle = desc->cycle; + + err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time); + if (err < 0) + goto end; + + // Compute cycle count with lower 3 bits of second field and cycle field like timestamp + // format of 1394 OHCI isochronous context. + curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff); + + if (s->direction == AMDTP_IN_STREAM) { + // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since + // it corresponds to arrived isochronous packet. + if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0) + goto end; + cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle); + + // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated + // value expectedly corresponds to a few packets (0-2) since the packet arrived at + // the most recent isochronous cycle has been already processed. + for (i = 0; i < cycle_gap; ++i) { + desc = amdtp_stream_next_packet_desc(s, desc); + data_block_count += desc->data_blocks; + } + } else { + // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle + // since it was already scheduled. + if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0) + goto end; + cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle); + + // NOTE: use history of scheduled packets. + for (i = 0; i < cycle_gap; ++i) { + data_block_count += desc->data_blocks; + desc = prev_packet_desc(s, desc); + } + } +end: + return data_block_count * s->pcm_frame_multiplier; +} + static void process_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets) + const struct pkt_desc *desc, + unsigned int count) { struct snd_pcm_substream *pcm; - unsigned int pcm_frames; + int i; pcm = READ_ONCE(s->pcm); - pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm); - if (pcm) - update_pcm_pointers(s, pcm, pcm_frames); + s->process_ctx_payloads(s, desc, count, pcm); + + if (pcm) { + unsigned int data_block_count = 0; + + pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count); + + for (i = 0; i < count; ++i) { + data_block_count += desc->data_blocks; + desc = amdtp_stream_next_packet_desc(s, desc); + } + + update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier); + } } static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -1052,8 +1140,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ const __be32 *ctx_header = header; const unsigned int events_per_period = d->events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; + struct pkt_desc *desc = s->packet_descs_cursor; unsigned int pkt_header_length; unsigned int packets; + u32 curr_cycle_time; bool need_hw_irq; int i; @@ -1063,11 +1153,9 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ // Calculate the number of packets in buffer and check XRUN. packets = header_length / sizeof(*ctx_header); - pool_seq_descs(s, packets); - - generate_pkt_descs(s, ctx_header, packets); + generate_rx_packet_descs(s, desc, ctx_header, packets); - process_ctx_payloads(s, s->pkt_descs, packets); + process_ctx_payloads(s, desc, packets); if (!(s->flags & CIP_NO_HEADER)) pkt_header_length = IT_PKT_HEADER_SIZE_CIP; @@ -1084,8 +1172,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ need_hw_irq = false; } + if (trace_amdtp_packet_enabled()) + (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); + for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = s->pkt_descs + i; struct { struct fw_iso_packet params; __be32 header[CIP_HEADER_QUADLETS]; @@ -1094,7 +1184,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, desc->data_blocks, desc->data_block_counter, - desc->syt, i); + desc->syt, i, curr_cycle_time); if (s == s->domain->irq_target) { event_count += desc->data_blocks; @@ -1108,9 +1198,12 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ cancel_stream(s); return; } + + desc = amdtp_stream_next_packet_desc(s, desc); } s->ctx_data.rx.event_count = event_count; + s->packet_descs_cursor = desc; } static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -1188,6 +1281,9 @@ static void process_rx_packets_intermediately(struct fw_iso_context *context, u3 s->ready_processing = true; wake_up(&s->ready_wait); + if (d->replay.enable) + s->ctx_data.rx.cache_pos = 0; + process_rx_packets(context, tstamp, header_length, ctx_header, private_data); if (amdtp_streaming_error(s)) return; @@ -1204,7 +1300,8 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ { struct amdtp_stream *s = private_data; __be32 *ctx_header = header; - unsigned int packets; + struct pkt_desc *desc = s->packet_descs_cursor; + unsigned int packet_count; unsigned int desc_count; int i; int err; @@ -1213,10 +1310,10 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ return; // Calculate the number of packets in buffer and check XRUN. - packets = header_length / s->ctx_data.tx.ctx_header_size; + packet_count = header_length / s->ctx_data.tx.ctx_header_size; desc_count = 0; - err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets, &desc_count); + err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count); if (err < 0) { if (err != -EAGAIN) { cancel_stream(s); @@ -1225,13 +1322,17 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ } else { struct amdtp_domain *d = s->domain; - process_ctx_payloads(s, s->pkt_descs, desc_count); + process_ctx_payloads(s, desc, desc_count); if (d->replay.enable) - cache_seq(s, s->pkt_descs, desc_count); + cache_seq(s, desc, desc_count); + + for (i = 0; i < desc_count; ++i) + desc = amdtp_stream_next_packet_desc(s, desc); + s->packet_descs_cursor = desc; } - for (i = 0; i < packets; ++i) { + for (i = 0; i < packet_count; ++i) { struct fw_iso_packet params = {0}; if (queue_in_packet(s, ¶ms) < 0) { @@ -1551,7 +1652,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; - int type, tag, err; + struct pkt_desc *descs; + int i, type, tag, err; mutex_lock(&s->mutex); @@ -1616,7 +1718,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, // possible to cache much unexpectedly. s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2, queue_size * 3 / 2); - s->ctx_data.tx.cache.tail = 0; + s->ctx_data.tx.cache.pos = 0; s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size, sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL); if (!s->ctx_data.tx.cache.descs) { @@ -1644,8 +1746,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, goto err_context; } s->ctx_data.rx.seq.size = queue_size; - s->ctx_data.rx.seq.tail = 0; - s->ctx_data.rx.seq.head = 0; + s->ctx_data.rx.seq.pos = 0; entry = &initial_state[s->sfc]; s->ctx_data.rx.data_block_state = entry->data_block; @@ -1660,12 +1761,24 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, else s->tag = TAG_CIP; - s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs), - GFP_KERNEL); - if (!s->pkt_descs) { + // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request + // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It + // could take a round over queue of AMDTP packet descriptors and small loss of history. For + // safe, keep more 8 elements for the queue, equivalent to 1 ms. + descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL); + if (!descs) { err = -ENOMEM; goto err_context; } + s->packet_descs = descs; + + INIT_LIST_HEAD(&s->packet_descs_list); + for (i = 0; i < s->queue_size; ++i) { + INIT_LIST_HEAD(&descs->link); + list_add_tail(&descs->link, &s->packet_descs_list); + ++descs; + } + s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link); s->packet_index = 0; do { @@ -1704,7 +1817,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, return 0; err_pkt_descs: - kfree(s->pkt_descs); + kfree(s->packet_descs); + s->packet_descs = NULL; err_context: if (s->direction == AMDTP_OUT_STREAM) { kfree(s->ctx_data.rx.seq.descs); @@ -1798,7 +1912,8 @@ static void amdtp_stream_stop(struct amdtp_stream *s) fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit); - kfree(s->pkt_descs); + kfree(s->packet_descs); + s->packet_descs = NULL; if (s->direction == AMDTP_OUT_STREAM) { kfree(s->ctx_data.rx.seq.descs); @@ -1917,7 +2032,6 @@ static int make_association(struct amdtp_domain *d) } rx->ctx_data.rx.replay_target = tx; - rx->ctx_data.rx.cache_head = 0; ++dst_index; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 1f957c946c95..b7ff44751ab9 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -103,14 +103,14 @@ struct pkt_desc { unsigned int data_blocks; unsigned int data_block_counter; __be32 *ctx_payload; + struct list_head link; }; struct amdtp_stream; -typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( - struct amdtp_stream *s, - const struct pkt_desc *desc, - unsigned int packets, - struct snd_pcm_substream *pcm); +typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s, + const struct pkt_desc *desc, + unsigned int count, + struct snd_pcm_substream *pcm); struct amdtp_domain; struct amdtp_stream { @@ -125,7 +125,9 @@ struct amdtp_stream { struct iso_packets_buffer buffer; unsigned int queue_size; int packet_index; - struct pkt_desc *pkt_descs; + struct pkt_desc *packet_descs; + struct list_head packet_descs_list; + struct pkt_desc *packet_descs_cursor; int tag; union { struct { @@ -145,7 +147,7 @@ struct amdtp_stream { struct { struct seq_desc *descs; unsigned int size; - unsigned int tail; + unsigned int pos; } cache; } tx; struct { @@ -159,8 +161,7 @@ struct amdtp_stream { struct { struct seq_desc *descs; unsigned int size; - unsigned int tail; - unsigned int head; + unsigned int pos; } seq; unsigned int data_block_state; @@ -168,7 +169,7 @@ struct amdtp_stream { unsigned int last_syt_offset; struct amdtp_stream *replay_target; - unsigned int cache_head; + unsigned int cache_pos; } rx; } ctx_data; @@ -188,6 +189,7 @@ struct amdtp_stream { struct snd_pcm_substream *pcm; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer; + unsigned int pcm_frame_multiplier; // To start processing content of packets at the same cycle in several contexts for // each direction. @@ -214,7 +216,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, 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 data_block_quadlets, unsigned int pcm_frame_multiplier); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); void amdtp_stream_update(struct amdtp_stream *s); @@ -277,6 +279,16 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, WRITE_ONCE(s->pcm, pcm); } +/** + * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet. + * @s: the AMDTP stream + * @desc: the descriptor of packet + * + * This macro computes next descriptor so that the list of descriptors behaves circular queue. + */ +#define amdtp_stream_next_packet_desc(s, desc) \ + list_next_entry_circular(desc, &s->packet_descs_list, link) + static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1; diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index 59b86c8d89e1..7db0024495b7 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -123,7 +123,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, * A first data channel is for MIDI messages, the rest is Multi Bit * Linear Audio data channel. */ - err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1); + err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1); if (err < 0) return err; @@ -341,16 +341,13 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, WRITE_ONCE(p->midi[port], midi); } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -360,21 +357,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, } read_midi_messages(s, buf, data_blocks); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -387,9 +381,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks, desc->data_block_counter); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c index 98177b0666d3..76c9d33ed572 100644 --- a/sound/firewire/fireface/amdtp-ff.c +++ b/sound/firewire/fireface/amdtp-ff.c @@ -24,7 +24,7 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate, p->pcm_channels = pcm_channels; data_channels = pcm_channels; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -112,16 +112,13 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -131,21 +128,18 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, } else { write_pcm_silence(s, buf, data_blocks); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -153,9 +147,9 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); pcm_frames += data_blocks; } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index ea64a2a41eea..8a741b3b0436 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -15,16 +15,23 @@ #include "ff.h" +static bool has_msg(struct snd_ff *ff) +{ + if (ff->spec->protocol->has_msg) + return ff->spec->protocol->has_msg(ff); + else + return 0; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_ff *ff = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event; spin_lock_irq(&ff->lock); - while (!ff->dev_lock_changed) { + while (!ff->dev_lock_changed && !has_msg(ff)) { prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&ff->lock); schedule(); @@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&ff->lock); } - memset(&event, 0, sizeof(event)); - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = (ff->dev_lock_count > 0); - ff->dev_lock_changed = false; + if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) { + struct snd_firewire_event_lock_status ev = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + .status = (ff->dev_lock_count > 0), + }; - count = min_t(long, count, sizeof(event.lock_status)); + ff->dev_lock_changed = false; - spin_unlock_irq(&ff->lock); + spin_unlock_irq(&ff->lock); - if (copy_to_user(buf, &event, count)) - return -EFAULT; + if (copy_to_user(buf, &ev, sizeof(ev))) + return -EFAULT; + count = sizeof(ev); + } else if (has_msg(ff)) { + // NOTE: Acquired spin lock should be released before accessing to user space in the + // callback since the access can cause page fault. + count = ff->spec->protocol->copy_msg_to_user(ff, buf, count); + spin_unlock_irq(&ff->lock); + } else { + spin_unlock_irq(&ff->lock); + + count = 0; + } return count; } @@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &ff->hwdep_wait, wait); spin_lock_irq(&ff->lock); - if (ff->dev_lock_changed) + if (ff->dev_lock_changed || has_msg(ff)) events = EPOLLIN | EPOLLRDNORM; else events = 0; diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8900ffe517ed..efd59e9d9935 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff) // address. // A write transaction to clear registered higher 4 bytes of destination address // has an effect to suppress asynchronous transaction from device. -static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { int i; @@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, } const struct snd_ff_protocol snd_ff_protocol_ff800 = { - .handle_midi_msg = ff800_handle_midi_msg, + .handle_msg = ff800_handle_midi_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, @@ -534,6 +534,35 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); } +static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) +{ + struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]); + + if (substream != NULL) { + u8 byte = (quad >> (16 * port)) & 0x000000ff; + + snd_rawmidi_receive(substream, &byte, 1); + } +} + +#define FF400_QUEUE_SIZE 32 + +struct ff400_msg_parser { + struct { + u32 msg; + u32 tstamp; + } msgs[FF400_QUEUE_SIZE]; + size_t push_pos; + size_t pull_pos; +}; + +static bool ff400_has_msg(struct snd_ff *ff) +{ + struct ff400_msg_parser *parser = ff->msg_parser; + + return (parser->push_pos != parser->pull_pos); +} + // For Fireface 400, lower 4 bytes of destination address is configured by bit // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can // select one of 4 options: @@ -553,46 +582,147 @@ static void ff400_finish_session(struct snd_ff *ff) // input attenuation. This driver allocates destination address with '0000'0000 // in its lower offset and expects userspace application to configure the // register for it. -static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) + +// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of +// stereo physical port. +// - 0: Microphone input 0/1 +// - 1: Line input 0/1 +// - [2-4]: Line output 0-5 +// - 5: Headphone output 0/1 +// - 6: S/PDIF output 0/1 +// - [7-10]: ADAT output 0-7 +// +// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone +// input: +// +// - 0: 0.0 dB +// - 10: +10.0 dB +// - 11: +11.0 dB +// - 12: +12.0 dB +// - ... +// - 63: +63.0 dB: +// - 64: +64.0 dB: +// - 65: +65.0 dB: +// +// For signal level of line input: +// +// - 0: 0.0 dB +// - 1: +0.5 dB +// - 2: +1.0 dB +// - 3: +1.5 dB +// - ... +// - 34: +17.0 dB: +// - 35: +17.5 dB: +// - 36: +18.0 dB: +// +// For signal level of any type of output: +// +// - 63: -infinite +// - 62: -58.0 dB +// - 61: -56.0 dB +// - 60: -54.0 dB +// - 59: -53.0 dB +// - 58: -52.0 dB +// - ... +// - 7: -1.0 dB +// - 6: 0.0 dB +// - 5: +1.0 dB +// - ... +// - 2: +4.0 dB +// - 1: +5.0 dB +// - 0: +6.0 dB +// +// When the message is not for signal level operation, it's for MIDI bytes. When matching to +// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When +// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000. +#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000 +#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000 +#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000 +#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000 +#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00 +#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 +#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff +#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 +#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 + +static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { + bool need_hwdep_wake_up = false; int i; for (i = 0; i < length / 4; i++) { u32 quad = le32_to_cpu(buf[i]); - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } + if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) { + struct ff400_msg_parser *parser = ff->msg_parser; - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } + parser->msgs[parser->push_pos].msg = quad; + parser->msgs[parser->push_pos].tstamp = tstamp; + ++parser->push_pos; + if (parser->push_pos >= FF400_QUEUE_SIZE) + parser->push_pos = 0; + + need_hwdep_wake_up = true; + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) { + parse_midi_msg(ff, quad, 0); + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) { + parse_midi_msg(ff, quad, 1); } } + + if (need_hwdep_wake_up) + wake_up(&ff->hwdep_wait); +} + +static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count) +{ + struct snd_firewire_event_ff400_message ev = { + .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE, + .message_count = 0, + }; + struct ff400_msg_parser *parser = ff->msg_parser; + long consumed = 0; + long ret = 0; + + if (count < sizeof(ev) || parser->pull_pos == parser->push_pos) + return 0; + + count -= sizeof(ev); + consumed += sizeof(ev); + + while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) { + spin_unlock_irq(&ff->lock); + if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos, + sizeof(*parser->msgs))) + ret = -EFAULT; + spin_lock_irq(&ff->lock); + if (ret) + return ret; + + ++parser->pull_pos; + if (parser->pull_pos >= FF400_QUEUE_SIZE) + parser->pull_pos = 0; + ++ev.message_count; + count -= sizeof(*parser->msgs); + consumed += sizeof(*parser->msgs); + } + + spin_unlock_irq(&ff->lock); + if (copy_to_user(buf, &ev, sizeof(ev))) + ret = -EFAULT; + spin_lock_irq(&ff->lock); + if (ret) + return ret; + + return consumed; } const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .handle_midi_msg = ff400_handle_midi_msg, + .msg_parser_size = sizeof(struct ff400_msg_parser), + .has_msg = ff400_has_msg, + .copy_msg_to_user = ff400_copy_msg_to_user, + .handle_msg = ff400_handle_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 76c3eab36d4e..9947e0c2e0aa 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer // input attenuation. This driver allocates for the first option // (0x'....'....'0000'0000) and expects userspace application to configure the // register for it. -static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { u32 data = le32_to_cpu(*buf); unsigned int index = (data & 0x000000f0) >> 4; @@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff, } const struct snd_ff_protocol snd_ff_protocol_latter = { - .handle_midi_msg = latter_handle_midi_msg, + .handle_msg = latter_handle_midi_msg, .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index ee7122c461d4..6b89e39f4a43 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -125,19 +125,22 @@ static void transmit_midi1_msg(struct work_struct *work) transmit_midi_msg(ff, 1); } -static void handle_midi_msg(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) +static void handle_msg(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_ff *ff = callback_data; __le32 *buf = data; + u32 tstamp = fw_request_get_timestamp(request); + unsigned long flag; fw_send_response(card, request, RCODE_COMPLETE); offset -= ff->async_handler.offset; - ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, - length); + + spin_lock_irqsave(&ff->lock, flag); + ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp); + spin_unlock_irqrestore(&ff->lock, flag); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -146,7 +149,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) int err; ff->async_handler.length = ff->spec->midi_addr_range; - ff->async_handler.address_callback = handle_midi_msg; + ff->async_handler.address_callback = handle_msg; ff->async_handler.callback_data = ff; midi_msg_region.start = 0x000100000000ull * i; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 7bf51d062021..448e972028d9 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card) snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); + kfree(ff->msg_parser); + mutex_destroy(&ff->mutex); fw_unit_put(ff->unit); } @@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e if (err < 0) goto error; + if (ff->spec->protocol->msg_parser_size > 0) { + ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL); + if (!ff->msg_parser) { + err = -ENOMEM; + goto error; + } + } + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 0535f0b58b67..7e42f5778a8a 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -97,6 +97,8 @@ struct snd_ff { wait_queue_head_t hwdep_wait; struct amdtp_domain domain; + + void *msg_parser; }; enum snd_ff_clock_src { @@ -110,8 +112,11 @@ enum snd_ff_clock_src { }; struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length); + size_t msg_parser_size; + bool (*has_msg)(struct snd_ff *ff); + long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count); + void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp); int (*fill_midi_msg)(struct snd_ff *ff, struct snd_rawmidi_substream *substream, unsigned int port); diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 2fb52f481d12..39ed57d2c5a0 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -73,7 +73,7 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, data_chunks = formats->msg_chunks + pcm_chunks; data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4); - err = amdtp_stream_set_parameters(s, rate, data_block_quadlets); + err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1); if (err < 0) return err; @@ -284,19 +284,19 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer, } } -static void probe_tracepoints_events(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets) +static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count) { int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; trace_data_block_sph(s, data_blocks, buf); trace_data_block_message(s, data_blocks, buf); + + desc = amdtp_stream_next_packet_desc(s, desc); } } @@ -328,13 +328,12 @@ static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *bu cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND; } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); struct amdtp_motu *p = s->protocol; + const struct pkt_desc *cursor = desc; unsigned int pcm_frames = 0; int i; @@ -342,8 +341,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND); // For data block processing. - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -356,22 +354,20 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, if (p->midi_ports) read_midi_messages(s, buf, data_blocks); - } - if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { - snd_motu_register_dsp_message_parser_parse(motu, descs, packets, - s->data_block_quadlets); - } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { - snd_motu_command_dsp_message_parser_parse(motu, descs, packets, - s->data_block_quadlets); + desc = amdtp_stream_next_packet_desc(s, desc); } + desc = cursor; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) + snd_motu_register_dsp_message_parser_parse(s, desc, count); + else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) + snd_motu_command_dsp_message_parser_parse(s, desc, count); + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) - probe_tracepoints_events(s, descs, packets); - - return pcm_frames; + probe_tracepoints_events(s, desc, count); } static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks, @@ -396,12 +392,11 @@ static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned i cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND; } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_motu *p = s->protocol; + const struct pkt_desc *cursor = desc; unsigned int pcm_frames = 0; int i; @@ -409,8 +404,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND); // For data block processing. - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -425,14 +419,16 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks); write_sph(p->cache, buf, data_blocks, s->data_block_quadlets); + + desc = amdtp_stream_next_packet_desc(s, desc); } + desc = cursor; + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) - probe_tracepoints_events(s, descs, packets); - - return pcm_frames; + probe_tracepoints_events(s, desc, count); } int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c index 9efe4d364baf..5d8a86a12f1f 100644 --- a/sound/firewire/motu/motu-command-dsp-message-parser.c +++ b/sound/firewire/motu/motu-command-dsp-message-parser.c @@ -80,9 +80,11 @@ int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc #define FRAGMENTS_PER_VALUE 4 #define VALUES_AT_IMAGE_END 0xffffffffffffffff -void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets) +void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); + unsigned int data_block_quadlets = s->data_block_quadlets; struct msg_parser *parser = motu->message_parser; unsigned int interval = parser->interval; unsigned long flags; @@ -90,12 +92,13 @@ void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const stru spin_lock_irqsave(&parser->lock, flags); - for (i = 0; i < desc_count; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buffer = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; int j; + desc = amdtp_stream_next_packet_desc(s, desc); + for (j = 0; j < data_blocks; ++j) { u8 *b = (u8 *)buffer; buffer += data_block_quadlets; diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c index 0c587567540f..ef3b0b0f0dab 100644 --- a/sound/firewire/motu/motu-register-dsp-message-parser.c +++ b/sound/firewire/motu/motu-register-dsp-message-parser.c @@ -142,9 +142,11 @@ static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 i parser->push_pos = pos; } -void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets) +void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); + unsigned int data_block_quadlets = s->data_block_quadlets; struct msg_parser *parser = motu->message_parser; bool meter_pos_quirk = parser->meter_pos_quirk; unsigned int pos = parser->push_pos; @@ -153,12 +155,13 @@ void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const str spin_lock_irqsave(&parser->lock, flags); - for (i = 0; i < desc_count; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buffer = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; int j; + desc = amdtp_stream_next_packet_desc(s, desc); + for (j = 0; j < data_blocks; ++j) { u8 *b = (u8 *)buffer; u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 4189f2192284..3b1dc98a7be0 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -279,8 +279,8 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu); int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu); -void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *descs, unsigned int count); void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu, struct snd_firewire_motu_register_dsp_meter *meter); void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu, @@ -290,8 +290,8 @@ bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu); int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc); -void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *descs, unsigned int count); void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, struct snd_firewire_motu_command_dsp_meter *meter); diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 64d66a802545..0b42d6559008 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -29,7 +29,7 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) if (s->direction == AMDTP_IN_STREAM) data_channels += 2; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -176,16 +176,13 @@ static void read_status_messages(struct amdtp_stream *s, } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -195,21 +192,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, } read_status_messages(s, buf, data_blocks); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -219,9 +213,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, } else { write_pcm_silence(s, buf, data_blocks); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 547adbc22590..1f56fd33b9af 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -124,11 +124,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); /** * snd_hdac_stream_start - start a stream * @azx_dev: HD-audio core stream to start - * @fresh_start: false = wallclock timestamp relative to period wallclock * * Start a stream, set start_wallclk and set the running flag. */ -void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) +void snd_hdac_stream_start(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; int stripe_ctl; @@ -136,8 +135,6 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) trace_snd_hdac_stream_start(bus, azx_dev); azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK); - if (!fresh_start) - azx_dev->start_wallclk -= azx_dev->period_wallclk; /* enable SIE */ snd_hdac_chip_updatel(bus, INTCTL, @@ -966,7 +963,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare); void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start) { if (start) - snd_hdac_stream_start(azx_dev, true); + snd_hdac_stream_start(azx_dev); else snd_hdac_stream_stop(azx_dev); } diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 62a9615dcf52..60b0a70428d5 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -148,7 +148,7 @@ static void widget_release(struct kobject *kobj) kfree(kobj); } -static struct kobj_type widget_ktype = { +static const struct kobj_type widget_ktype = { .release = widget_release, .sysfs_ops = &widget_sysfs_ops, }; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 06d304db4183..886255a03e8b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -302,6 +302,20 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM This feature can impact power consumption as resources are kept reserved both at transmitter and receiver. +config SND_HDA_CTL_DEV_ID + bool "Use the device identifier field for controls" + depends on SND_HDA_INTEL + help + Say Y to use the device identifier field for (mixer) + controls (old behaviour until this option is available). + + When enabled, the multiple HDA codecs may set the device + field in control (mixer) element identifiers. The use + of this field is not recommended and defined for mixer controls. + + The old behaviour (Y) is obsolete and will be removed. Consider + to not enable this option. + endif endmenu diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index f7815ee24f83..75020edd39e7 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -58,7 +58,7 @@ static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL - { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; @@ -82,13 +82,13 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON - { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB }; static const struct reg_sequence cs35l41_hda_mute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB - { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute + { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute }; static void cs35l41_add_controls(struct cs35l41_hda *cs35l41) @@ -178,11 +178,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ @@ -191,10 +190,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, - cs35l41->amp_name, cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ @@ -209,11 +208,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub.wmfw */ @@ -224,29 +222,16 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, cs35l41->speaker_id, "bin"); + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, cs35l41->speaker_id, "bin"); - return 0; - } - - /* fallback try cirrus/part-dspN-fwtype.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); - if (!ret) { - /* fallback try cirrus/part-dspN-fwtype.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); } - dev_warn(cs35l41->dev, "Failed to request firmware\n"); - return ret; } @@ -258,9 +243,12 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, { int ret; - if (cs35l41->speaker_id > -1) - return cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); + if (cs35l41->speaker_id > -1) { + ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); + goto out; + + } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, @@ -268,10 +256,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, - cs35l41->amp_name, -1, "bin"); - return 0; + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + -1, "bin"); + goto out; } /* try cirrus/part-dspN-fwtype-sub.wmfw */ @@ -286,25 +275,35 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, -1, "bin"); - return 0; + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, -1, + "bin"); } +out: + if (!ret) + return 0; + + /* Handle fallback */ + dev_warn(cs35l41->dev, "Falling back to default firmware.\n"); + + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + /* fallback try cirrus/part-dspN-fwtype.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); - if (!ret) { + if (!ret) /* fallback try cirrus/part-dspN-fwtype.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); - return 0; - } - - dev_warn(cs35l41->dev, "Failed to request firmware\n"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + if (ret) { + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n"); + } return ret; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ac1cc7c5290e..dc4a042b3ac1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3389,7 +3389,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; - if (addr > 0) + /* Do not use the id.device field for MIXER elements. + * This field is for real device numbers (like PCM) but codecs + * are hidden components from the user space view (unrelated + * to the mixer element identification). + */ + if (addr > 0 && codec->ctl_dev_id) kctl->id.device = addr; if (idx > 0) kctl->id.index = idx; @@ -3400,9 +3405,11 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, * the codec addr; if it still fails (or it's the * primary codec), then try another control index */ - if (!addr && codec->core.addr) + if (!addr && codec->core.addr) { addr = codec->core.addr; - else if (!idx && !knew->index) { + if (!codec->ctl_dev_id) + idx += 10 * addr; + } else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, knew->name, 0); if (idx <= 0) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 0ff286b7b66b..406779625fb5 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -257,7 +257,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) azx_dev = get_azx_dev(s); if (start) { azx_dev->insufficient = 1; - snd_hdac_stream_start(azx_stream(azx_dev), true); + snd_hdac_stream_start(azx_stream(azx_dev)); } else { snd_hdac_stream_stop(azx_stream(azx_dev)); } @@ -1231,6 +1231,7 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) continue; codec->jackpoll_interval = chip->jackpoll_interval; codec->beep_mode = chip->beep_mode; + codec->ctl_dev_id = chip->ctl_dev_id; codecs++; } } diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index f5bf295eb830..8556031bcd68 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -124,6 +124,7 @@ struct azx { /* HD codec */ int codec_probe_mask; /* copied from probe_mask option */ unsigned int beep_mode; + bool ctl_dev_id; #ifdef CONFIG_SND_HDA_PATCH_LOADER const struct firmware *fw; diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c index 5433f6227ac9..463ca06036bf 100644 --- a/sound/pci/hda/hda_cs_dsp_ctl.c +++ b/sound/pci/hda/hda_cs_dsp_ctl.c @@ -218,10 +218,10 @@ int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type, cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); mutex_unlock(&dsp->pwr_lock); - if (ret) + if (ret < 0) return ret; - if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) + if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS)) return 0; ctl = cs_ctl->priv; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cfd2ddfde112..81c4a45254ff 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -120,6 +120,7 @@ static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = CONFIG_SND_HDA_INPUT_BEEP_MODE}; #endif static bool dmic_detect = 1; +static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -158,6 +159,8 @@ module_param(dmic_detect, bool, 0444); MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) " "(0=off, 1=on) (default=1); " "deprecated, use snd-intel-dspcfg.dsp_driver option instead"); +module_param(ctl_dev_id, bool, 0444); +MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address)."); #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); @@ -2279,6 +2282,8 @@ static int azx_probe_continue(struct azx *chip) chip->beep_mode = beep_mode[dev]; #endif + chip->ctl_dev_id = ctl_dev_id; + /* create codec instances */ if (bus->codec_mask) { err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 976a112c7d00..c2bf86781894 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -582,12 +582,10 @@ static void hda_tegra_probe_work(struct work_struct *work) static int hda_tegra_remove(struct platform_device *pdev) { - int ret; - - ret = snd_card_free(dev_get_drvdata(&pdev->dev)); + snd_card_free(dev_get_drvdata(&pdev->dev)); pm_runtime_disable(&pdev->dev); - return ret; + return 0; } static void hda_tegra_shutdown(struct platform_device *pdev) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0a292bf271f2..acde4cd58785 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2455,7 +2455,7 @@ static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) { int status = 0; - unsigned int size = sizeof(dma_chan); + unsigned int size = sizeof(*dma_chan); codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); status = dspio_scp(codec, MASTERCONTROL, 0x20, diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 631a61ce52f4..a6cff2c46ac7 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -1046,16 +1046,13 @@ clean_open: /* called when module removal */ static void snd_ps3_driver_remove(struct ps3_system_bus_device *dev) { - int ret; pr_info("%s:start id=%d\n", __func__, dev->match_id); /* * ctl and preallocate buffer will be freed in * snd_card_free */ - ret = snd_card_free(the_card.card); - if (ret) - pr_info("%s: ctl freecard=%d\n", __func__, ret); + snd_card_free(the_card.card); dma_free_coherent(&dev->core, PAGE_SIZE, diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index f930c5e86a84..b673b84ead32 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -647,7 +647,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_start(hdac_stream(host_stream), true); + snd_hdac_stream_start(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, flags); /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 29d63f2a9616..741565c6465a 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -190,7 +190,7 @@ static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: spin_lock_irqsave(&bus->reg_lock, cookie); - snd_hdac_stream_start(hdac_stream(host_stream), true); + snd_hdac_stream_start(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, cookie); break; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dc627d18518d..a4209d88b0c6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -449,7 +449,7 @@ static int skl_decoupled_trigger(struct snd_pcm_substream *substream, spin_lock_irqsave(&bus->reg_lock, cookie); if (start) { - snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_start(hdac_stream(stream)); snd_hdac_stream_timecounter_init(hstr, 0); } else { snd_hdac_stream_stop(hdac_stream(stream)); @@ -1134,7 +1134,7 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, continue; stream = get_hdac_ext_stream(s); if (start) - snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_start(hdac_stream(stream)); else snd_hdac_stream_stop(hdac_stream(stream)); } diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile index a8c0383878d3..901949db80ad 100644 --- a/tools/testing/selftests/alsa/Makefile +++ b/tools/testing/selftests/alsa/Makefile @@ -8,13 +8,15 @@ LDLIBS += -lasound endif CFLAGS += -L$(OUTPUT) -Wl,-rpath=./ +LDLIBS+=-lpthread + OVERRIDE_TARGETS = 1 TEST_GEN_PROGS := mixer-test pcm-test TEST_GEN_PROGS_EXTENDED := libatest.so -TEST_FILES := conf.d +TEST_FILES := conf.d pcm-test.conf include ../lib.mk diff --git a/tools/testing/selftests/alsa/alsa-local.h b/tools/testing/selftests/alsa/alsa-local.h index 65f197ea9773..de030dc23bd1 100644 --- a/tools/testing/selftests/alsa/alsa-local.h +++ b/tools/testing/selftests/alsa/alsa-local.h @@ -12,6 +12,7 @@ snd_config_t *get_alsalib_config(void); +snd_config_t *conf_load_from_file(const char *filename); void conf_load(void); void conf_free(void); snd_config_t *conf_by_card(int card); @@ -20,5 +21,7 @@ int conf_get_count(snd_config_t *root, const char *key1, const char *key2); const char *conf_get_string(snd_config_t *root, const char *key1, const char *key2, const char *def); long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long def); int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def); +void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2, + const char **array, int array_size, const char *def); #endif /* __ALSA_LOCAL_H */ diff --git a/tools/testing/selftests/alsa/conf.c b/tools/testing/selftests/alsa/conf.c index c7ffc8f04195..d7aafe5a1993 100644 --- a/tools/testing/selftests/alsa/conf.c +++ b/tools/testing/selftests/alsa/conf.c @@ -125,7 +125,7 @@ static int dump_config_tree(snd_config_t *top) snd_output_close(out); } -static snd_config_t *load(const char *filename) +snd_config_t *conf_load_from_file(const char *filename) { snd_config_t *dst; snd_input_t *input; @@ -235,7 +235,7 @@ static bool test_filename1(int card, const char *filename, const char *sysfs_car snd_config_t *config, *sysfs_config, *card_config, *sysfs_card_config, *node; snd_config_iterator_t i, next; - config = load(filename); + config = conf_load_from_file(filename); if (snd_config_search(config, "sysfs", &sysfs_config) || snd_config_get_type(sysfs_config) != SND_CONFIG_TYPE_COMPOUND) ksft_exit_fail_msg("Missing global sysfs block in filename %s\n", filename); @@ -446,3 +446,25 @@ int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int de ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1, key2); return !!ret; } + +void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2, + const char **array, int array_size, const char *def) +{ + snd_config_t *cfg; + char buf[16]; + int ret, index; + + ret = conf_get_by_keys(root, key1, key2, &cfg); + if (ret == -ENOENT) + cfg = NULL; + else if (ret < 0) + ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret)); + for (index = 0; index < array_size; index++) { + if (cfg == NULL) { + array[index] = def; + } else { + sprintf(buf, "%i", index); + array[index] = conf_get_string(cfg, buf, NULL, def); + } + } +} diff --git a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf index 9eca985e0c08..5b40a916295d 100644 --- a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf +++ b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf @@ -39,25 +39,30 @@ card.hda { # pcm.0.0 { PLAYBACK { - # - # Uncomment to override values for specific tests - # - #test_name1 { - # access RW_INTERLEAVED - # format S16_LE - # rate 48000 - # channels 2 - # period_size 512 - # buffer_size 4096 - #} - #test_name2 { - # access RW_INTERLEAVED - # format S16_LE - # rate 48000 - # channels 2 - # period_size 24000 - # buffer_size 192000 - #} + test.time1 { + access RW_INTERLEAVED # can be omitted - default + format S16_LE # can be omitted - default + rate 48000 # can be omitted - default + channels 2 # can be omitted - default + period_size 512 + buffer_size 4096 + } + test.time2 { + access RW_INTERLEAVED + format S16_LE + rate 48000 + channels 2 + period_size 24000 + buffer_size 192000 + } + test.time3 { + access RW_INTERLEAVED + format S16_LE + rate 44100 + channels 2 + period_size 24000 + buffer_size 192000 + } } CAPTURE { # use default tests, check for the presence diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c index f293c7d81009..58b525a4a32c 100644 --- a/tools/testing/selftests/alsa/pcm-test.c +++ b/tools/testing/selftests/alsa/pcm-test.c @@ -15,12 +15,21 @@ #include <stdbool.h> #include <errno.h> #include <assert.h> +#include <pthread.h> #include "../kselftest.h" #include "alsa-local.h" typedef struct timespec timestamp_t; +struct card_data { + int card; + pthread_t thread; + struct card_data *next; +}; + +struct card_data *card_list = NULL; + struct pcm_data { snd_pcm_t *handle; int card; @@ -31,19 +40,19 @@ struct pcm_data { struct pcm_data *next; }; -int num_pcms = 0; struct pcm_data *pcm_list = NULL; int num_missing = 0; struct pcm_data *pcm_missing = NULL; -struct time_test_def { - const char *cfg_prefix; - const char *format; - long rate; - long channels; - long period_size; - long buffer_size; +snd_config_t *default_pcm_config; + +/* Lock while reporting results since kselftest doesn't */ +pthread_mutex_t results_lock = PTHREAD_MUTEX_INITIALIZER; + +enum test_class { + TEST_CLASS_DEFAULT, + TEST_CLASS_SYSTEM, }; void timestamp_now(timestamp_t *tstamp) @@ -146,6 +155,7 @@ static void find_pcms(void) snd_ctl_t *handle; snd_pcm_info_t *pcm_info; snd_config_t *config, *card_config, *pcm_config; + struct card_data *card_data; snd_pcm_info_alloca(&pcm_info); @@ -167,6 +177,13 @@ static void find_pcms(void) card_config = conf_by_card(card); + card_data = calloc(1, sizeof(*card_data)); + if (!card_data) + ksft_exit_fail_msg("Out of memory\n"); + card_data->card = card; + card_data->next = card_list; + card_list = card_data; + dev = -1; while (1) { if (snd_ctl_pcm_next_device(handle, &dev) < 0) @@ -209,7 +226,6 @@ static void find_pcms(void) pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL); pcm_data->next = pcm_list; pcm_list = pcm_data; - num_pcms++; } } } @@ -228,45 +244,64 @@ static void find_pcms(void) snd_config_delete(config); } -static void test_pcm_time1(struct pcm_data *data, - const struct time_test_def *test) +static void test_pcm_time(struct pcm_data *data, enum test_class class, + const char *test_name, snd_config_t *pcm_cfg) { char name[64], key[128], msg[256]; const char *cs; int i, err; snd_pcm_t *handle = NULL; snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED; - snd_pcm_format_t format; + snd_pcm_format_t format, old_format; + const char *alt_formats[8]; unsigned char *samples = NULL; snd_pcm_sframes_t frames; long long ms; long rate, channels, period_size, buffer_size; - unsigned int rchannels; unsigned int rrate; snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold; timestamp_t tstamp; - bool pass = false, automatic = true; + bool pass = false; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; - bool skip = false; + const char *test_class_name; + bool skip = true; + const char *desc; + + switch (class) { + case TEST_CLASS_DEFAULT: + test_class_name = "default"; + break; + case TEST_CLASS_SYSTEM: + test_class_name = "system"; + break; + default: + ksft_exit_fail_msg("Unknown test class %d\n", class); + break; + } + + desc = conf_get_string(pcm_cfg, "description", NULL, NULL); + if (desc) + ksft_print_msg("%s.%s.%d.%d.%d.%s - %s\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + desc); + snd_pcm_hw_params_alloca(&hw_params); snd_pcm_sw_params_alloca(&sw_params); - cs = conf_get_string(data->pcm_config, test->cfg_prefix, "format", test->format); + cs = conf_get_string(pcm_cfg, "format", NULL, "S16_LE"); format = snd_pcm_format_value(cs); if (format == SND_PCM_FORMAT_UNKNOWN) ksft_exit_fail_msg("Wrong format '%s'\n", cs); - rate = conf_get_long(data->pcm_config, test->cfg_prefix, "rate", test->rate); - channels = conf_get_long(data->pcm_config, test->cfg_prefix, "channels", test->channels); - period_size = conf_get_long(data->pcm_config, test->cfg_prefix, "period_size", test->period_size); - buffer_size = conf_get_long(data->pcm_config, test->cfg_prefix, "buffer_size", test->buffer_size); - - automatic = strcmp(test->format, snd_pcm_format_name(format)) == 0 && - test->rate == rate && - test->channels == channels && - test->period_size == period_size && - test->buffer_size == buffer_size; + conf_get_string_array(pcm_cfg, "alt_formats", NULL, + alt_formats, ARRAY_SIZE(alt_formats), NULL); + rate = conf_get_long(pcm_cfg, "rate", NULL, 48000); + channels = conf_get_long(pcm_cfg, "channels", NULL, 2); + period_size = conf_get_long(pcm_cfg, "period_size", NULL, 4096); + buffer_size = conf_get_long(pcm_cfg, "buffer_size", NULL, 16384); samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8); if (!samples) @@ -296,32 +331,39 @@ static void test_pcm_time1(struct pcm_data *data, snd_pcm_access_name(access), snd_strerror(err)); goto __close; } + i = -1; __format: err = snd_pcm_hw_params_set_format(handle, hw_params, format); if (err < 0) { - if (automatic && format == SND_PCM_FORMAT_S16_LE) { - format = SND_PCM_FORMAT_S32_LE; - ksft_print_msg("%s.%d.%d.%d.%s.%s format S16_LE -> S32_LE\n", - test->cfg_prefix, - data->card, data->device, data->subdevice, - snd_pcm_stream_name(data->stream), - snd_pcm_access_name(access)); + i++; + if (i < ARRAY_SIZE(alt_formats) && alt_formats[i]) { + old_format = format; + format = snd_pcm_format_value(alt_formats[i]); + if (format != SND_PCM_FORMAT_UNKNOWN) { + ksft_print_msg("%s.%d.%d.%d.%s.%s format %s -> %s\n", + test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + snd_pcm_access_name(access), + snd_pcm_format_name(old_format), + snd_pcm_format_name(format)); + samples = realloc(samples, (rate * channels * + snd_pcm_format_physical_width(format)) / 8); + if (!samples) + ksft_exit_fail_msg("Out of memory\n"); + snd_pcm_format_set_silence(format, samples, rate * channels); + goto __format; + } } snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s", snd_pcm_format_name(format), snd_strerror(err)); goto __close; } - rchannels = channels; - err = snd_pcm_hw_params_set_channels_near(handle, hw_params, &rchannels); + err = snd_pcm_hw_params_set_channels(handle, hw_params, channels); if (err < 0) { snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err)); goto __close; } - if (rchannels != channels) { - snprintf(msg, sizeof(msg), "channels unsupported %ld != %ld", channels, rchannels); - skip = true; - goto __close; - } rrate = rate; err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0); if (err < 0) { @@ -329,8 +371,7 @@ __format: goto __close; } if (rrate != rate) { - snprintf(msg, sizeof(msg), "rate unsupported %ld != %ld", rate, rrate); - skip = true; + snprintf(msg, sizeof(msg), "rate mismatch %ld != %ld", rate, rrate); goto __close; } rperiod_size = period_size; @@ -377,8 +418,8 @@ __format: goto __close; } - ksft_print_msg("%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n", - test->cfg_prefix, + ksft_print_msg("%s.%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n", + test_class_name, test_name, data->card, data->device, data->subdevice, snd_pcm_stream_name(data->stream), snd_pcm_access_name(access), @@ -387,6 +428,9 @@ __format: (long)rperiod_size, (long)rbuffer_size, (long)start_threshold); + /* Set all the params, actually run the test */ + skip = false; + timestamp_now(&tstamp); for (i = 0; i < 4; i++) { if (data->stream == SND_PCM_STREAM_PLAYBACK) { @@ -426,48 +470,120 @@ __format: msg[0] = '\0'; pass = true; __close: - if (!skip) { - ksft_test_result(pass, "%s.%d.%d.%d.%s%s%s\n", - test->cfg_prefix, + pthread_mutex_lock(&results_lock); + + switch (class) { + case TEST_CLASS_SYSTEM: + test_class_name = "system"; + /* + * Anything specified as specific to this system + * should always be supported. + */ + ksft_test_result(!skip, "%s.%s.%d.%d.%d.%s.params\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream)); + break; + default: + break; + } + + if (!skip) + ksft_test_result(pass, "%s.%s.%d.%d.%d.%s%s%s\n", + test_class_name, test_name, data->card, data->device, data->subdevice, snd_pcm_stream_name(data->stream), msg[0] ? " " : "", msg); - } else { - ksft_test_result_skip("%s.%d.%d.%d.%s%s%s\n", - test->cfg_prefix, - data->card, data->device, - data->subdevice, - snd_pcm_stream_name(data->stream), - msg[0] ? " " : "", msg); - } + else + ksft_test_result_skip("%s.%s.%d.%d.%d.%s%s%s\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + msg[0] ? " " : "", msg); + + pthread_mutex_unlock(&results_lock); + free(samples); if (handle) snd_pcm_close(handle); } -static const struct time_test_def time_tests[] = { - /* name format rate chan period buffer */ - { "8k.1.big", "S16_LE", 8000, 2, 8000, 32000 }, - { "8k.2.big", "S16_LE", 8000, 2, 8000, 32000 }, - { "44k1.2.big", "S16_LE", 44100, 2, 22050, 192000 }, - { "48k.2.small", "S16_LE", 48000, 2, 512, 4096 }, - { "48k.2.big", "S16_LE", 48000, 2, 24000, 192000 }, - { "48k.6.big", "S16_LE", 48000, 6, 48000, 576000 }, - { "96k.2.big", "S16_LE", 96000, 2, 48000, 192000 }, -}; +void run_time_tests(struct pcm_data *pcm, enum test_class class, + snd_config_t *cfg) +{ + const char *test_name, *test_type; + snd_config_t *pcm_cfg; + snd_config_iterator_t i, next; + + if (!cfg) + return; + + cfg = conf_get_subtree(cfg, "test", NULL); + if (cfg == NULL) + return; + + snd_config_for_each(i, next, cfg) { + pcm_cfg = snd_config_iterator_entry(i); + if (snd_config_get_id(pcm_cfg, &test_name) < 0) + ksft_exit_fail_msg("snd_config_get_id\n"); + test_type = conf_get_string(pcm_cfg, "type", NULL, "time"); + if (strcmp(test_type, "time") == 0) + test_pcm_time(pcm, class, test_name, pcm_cfg); + else + ksft_exit_fail_msg("unknown test type '%s'\n", test_type); + } +} + +void *card_thread(void *data) +{ + struct card_data *card = data; + struct pcm_data *pcm; + + for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { + if (pcm->card != card->card) + continue; + + run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config); + run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config); + } + + return 0; +} int main(void) { + struct card_data *card; struct pcm_data *pcm; - int i; + snd_config_t *global_config, *cfg, *pcm_cfg; + int num_pcm_tests = 0, num_tests, num_std_pcm_tests; + int ret; + void *thread_ret; ksft_print_header(); + global_config = conf_load_from_file("pcm-test.conf"); + default_pcm_config = conf_get_subtree(global_config, "pcm", NULL); + if (default_pcm_config == NULL) + ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n"); + conf_load(); find_pcms(); - ksft_set_plan(num_missing + num_pcms * ARRAY_SIZE(time_tests)); + num_std_pcm_tests = conf_get_count(default_pcm_config, "test", NULL); + + for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { + num_pcm_tests += num_std_pcm_tests; + cfg = pcm->pcm_config; + if (cfg == NULL) + continue; + /* Setting params is reported as a separate test */ + num_tests = conf_get_count(cfg, "test", NULL) * 2; + if (num_tests > 0) + num_pcm_tests += num_tests; + } + + ksft_set_plan(num_missing + num_pcm_tests); for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) { ksft_test_result(false, "test.missing.%d.%d.%d.%s\n", @@ -475,12 +591,25 @@ int main(void) snd_pcm_stream_name(pcm->stream)); } - for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { - for (i = 0; i < ARRAY_SIZE(time_tests); i++) { - test_pcm_time1(pcm, &time_tests[i]); + for (card = card_list; card != NULL; card = card->next) { + ret = pthread_create(&card->thread, NULL, card_thread, card); + if (ret != 0) { + ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n", + card->card, ret, + strerror(errno)); + } + } + + for (card = card_list; card != NULL; card = card->next) { + ret = pthread_join(card->thread, &thread_ret); + if (ret != 0) { + ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n", + card->card, ret, + strerror(errno)); } } + snd_config_delete(global_config); conf_free(); ksft_exit_pass(); diff --git a/tools/testing/selftests/alsa/pcm-test.conf b/tools/testing/selftests/alsa/pcm-test.conf new file mode 100644 index 000000000000..71bd3f78a6f2 --- /dev/null +++ b/tools/testing/selftests/alsa/pcm-test.conf @@ -0,0 +1,63 @@ +pcm.test.time1 { + description "8kHz mono large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 8000 + channels 1 + period_size 8000 + buffer_size 32000 +} +pcm.test.time2 { + description "8kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 8000 + channels 2 + period_size 8000 + buffer_size 32000 +} +pcm.test.time3 { + description "44.1kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 44100 + channels 2 + period_size 22500 + buffer_size 192000 +} +pcm.test.time4 { + description "48kHz stereo small periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 512 + buffer_size 4096 +} +pcm.test.time5 { + description "48kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 24000 + buffer_size 192000 +} +pcm.test.time6 { + description "48kHz 6 channel large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 48000 + buffer_size 576000 +} +pcm.test.time7 { + description "96kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 96000 + channels 2 + period_size 48000 + buffer_size 192000 +} |