diff options
Diffstat (limited to 'sound')
69 files changed, 1881 insertions, 1500 deletions
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 1eddf8fa188f..8797d42e2b76 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -776,7 +776,7 @@ static int check_codec(struct aoa_codec *codec, struct codec_connection *cc; /* if the codec has a 'codec' node, we require a reference */ - if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { + if (of_node_name_eq(codec->node, "codec")) { snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name); ref = of_get_property(ldev->sound, propname, NULL); @@ -1008,8 +1008,8 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) return -ENODEV; /* by breaking out we keep a reference */ - while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) { - if (sound->type && strcasecmp(sound->type, "soundchip") == 0) + for_each_child_of_node(sdev->ofdev.dev.of_node, sound) { + if (of_node_is_type(sound, "soundchip")) break; } if (!sound) diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 70bcaa7f93dd..065d3a55725e 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -74,11 +74,11 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env) of = &soundbus_dev->ofdev; /* stuff we want to pass to /sbin/hotplug */ - retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name); + retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node); if (retval) return retval; - retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type); + retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node)); if (retval) return retval; diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index bd7c5029fc59..c3f57a3fb1a5 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -154,7 +154,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, struct device_node *np) { struct i2sbus_dev *dev; - struct device_node *child = NULL, *sound = NULL; + struct device_node *child, *sound = NULL; struct resource *r; int i, layout = 0, rlen, ok = force; char node_name[6]; @@ -177,8 +177,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, return 0; i = 0; - while ((child = of_get_next_child(np, child))) { - if (strcmp(child->name, "sound") == 0) { + for_each_child_of_node(np, child) { + if (of_node_name_eq(child, "sound")) { i++; sound = child; } diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c index 81da020bddef..a2d55e15afbb 100644 --- a/sound/aoa/soundbus/sysfs.c +++ b/sound/aoa/soundbus/sysfs.c @@ -1,18 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/kernel.h> +#include <linux/of.h> #include <linux/stat.h> /* FIX UP */ #include "soundbus.h" -#define soundbus_config_of_attr(field, format_string) \ -static ssize_t \ -field##_show (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct soundbus_dev *mdev = to_soundbus_device (dev); \ - return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \ -} - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -25,17 +17,33 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, strcat(buf, "\n"); length = strlen(buf); } else { - length = sprintf(buf, "of:N%sT%s\n", - of->dev.of_node->name, of->dev.of_node->type); + length = sprintf(buf, "of:N%pOFn%c%s\n", + of->dev.of_node, 'T', + of_node_get_device_type(of->dev.of_node)); } return length; } static DEVICE_ATTR_RO(modalias); -soundbus_config_of_attr (name, "%s\n"); +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct soundbus_dev *sdev = to_soundbus_device(dev); + struct platform_device *of = &sdev->ofdev; + + return sprintf(buf, "%pOFn\n", of->dev.of_node); +} static DEVICE_ATTR_RO(name); -soundbus_config_of_attr (type, "%s\n"); + +static ssize_t type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct soundbus_dev *sdev = to_soundbus_device(dev); + struct platform_device *of = &sdev->ofdev; + + return sprintf(buf, "%s\n", of_node_get_device_type(of->dev.of_node)); +} static DEVICE_ATTR_RO(type); struct attribute *soundbus_dev_attrs[] = { diff --git a/sound/core/control.c b/sound/core/control.c index 649d3217590e..fad7db402443 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -348,22 +348,41 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) return 0; } -/* add a new kcontrol object; call with card->controls_rwsem locked */ -static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +enum snd_ctl_add_mode { + CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE, +}; + +/* add/replace a new kcontrol object; call with card->controls_rwsem locked */ +static int __snd_ctl_add_replace(struct snd_card *card, + struct snd_kcontrol *kcontrol, + enum snd_ctl_add_mode mode) { struct snd_ctl_elem_id id; unsigned int idx; unsigned int count; + struct snd_kcontrol *old; + int err; id = kcontrol->id; if (id.index > UINT_MAX - kcontrol->count) return -EINVAL; - if (snd_ctl_find_id(card, &id)) { - dev_err(card->dev, - "control %i:%i:%i:%s:%i is already present\n", - id.iface, id.device, id.subdevice, id.name, id.index); - return -EBUSY; + old = snd_ctl_find_id(card, &id); + if (!old) { + if (mode == CTL_REPLACE) + return -EINVAL; + } else { + if (mode == CTL_ADD_EXCLUSIVE) { + dev_err(card->dev, + "control %i:%i:%i:%s:%i is already present\n", + id.iface, id.device, id.subdevice, id.name, + id.index); + return -EBUSY; + } + + err = snd_ctl_remove(card, old); + if (err < 0) + return err; } if (snd_ctl_find_hole(card, kcontrol->count) < 0) @@ -382,21 +401,9 @@ static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) return 0; } -/** - * snd_ctl_add - add the control instance to the card - * @card: the card instance - * @kcontrol: the control instance to add - * - * Adds the control instance created via snd_ctl_new() or - * snd_ctl_new1() to the given card. Assigns also an unique - * numid used for fast search. - * - * It frees automatically the control which cannot be added. - * - * Return: Zero if successful, or a negative error code on failure. - * - */ -int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +static int snd_ctl_add_replace(struct snd_card *card, + struct snd_kcontrol *kcontrol, + enum snd_ctl_add_mode mode) { int err = -EINVAL; @@ -406,7 +413,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) goto error; down_write(&card->controls_rwsem); - err = __snd_ctl_add(card, kcontrol); + err = __snd_ctl_add_replace(card, kcontrol, mode); up_write(&card->controls_rwsem); if (err < 0) goto error; @@ -416,6 +423,25 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return err; } + +/** + * snd_ctl_add - add the control instance to the card + * @card: the card instance + * @kcontrol: the control instance to add + * + * Adds the control instance created via snd_ctl_new() or + * snd_ctl_new1() to the given card. Assigns also an unique + * numid used for fast search. + * + * It frees automatically the control which cannot be added. + * + * Return: Zero if successful, or a negative error code on failure. + * + */ +int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) +{ + return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE); +} EXPORT_SYMBOL(snd_ctl_add); /** @@ -435,53 +461,8 @@ EXPORT_SYMBOL(snd_ctl_add); int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace) { - struct snd_ctl_elem_id id; - unsigned int count; - unsigned int idx; - struct snd_kcontrol *old; - int ret; - - if (!kcontrol) - return -EINVAL; - if (snd_BUG_ON(!card || !kcontrol->info)) { - ret = -EINVAL; - goto error; - } - id = kcontrol->id; - down_write(&card->controls_rwsem); - old = snd_ctl_find_id(card, &id); - if (!old) { - if (add_on_replace) - goto add; - up_write(&card->controls_rwsem); - ret = -EINVAL; - goto error; - } - ret = snd_ctl_remove(card, old); - if (ret < 0) { - up_write(&card->controls_rwsem); - goto error; - } -add: - if (snd_ctl_find_hole(card, kcontrol->count) < 0) { - up_write(&card->controls_rwsem); - ret = -ENOMEM; - goto error; - } - list_add_tail(&kcontrol->list, &card->controls); - card->controls_count += kcontrol->count; - kcontrol->id.numid = card->last_numid + 1; - card->last_numid += kcontrol->count; - id = kcontrol->id; - count = kcontrol->count; - up_write(&card->controls_rwsem); - for (idx = 0; idx < count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); - return 0; - -error: - snd_ctl_free_one(kcontrol); - return ret; + return snd_ctl_add_replace(card, kcontrol, + add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE); } EXPORT_SYMBOL(snd_ctl_replace); @@ -1369,7 +1350,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, /* This function manage to free the instance on failure. */ down_write(&card->controls_rwsem); - err = __snd_ctl_add(card, kctl); + err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE); if (err < 0) { snd_ctl_free_one(kctl); goto unlock; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index fdb9b92fc8d6..01b9d62eef14 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -25,6 +25,7 @@ #include <linux/time.h> #include <linux/mutex.h> #include <linux/device.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/pcm.h> @@ -129,6 +130,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, return -EFAULT; if (stream < 0 || stream > 1) return -EINVAL; + stream = array_index_nospec(stream, 2); if (get_user(subdevice, &info->subdevice)) return -EFAULT; mutex_lock(®ister_mutex); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 40013b26f671..6c0b30391ba9 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2177,16 +2177,11 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (!is_playback && - runtime->status->state == SNDRV_PCM_STATE_PREPARED) { - if (size >= runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } else { - /* nothing to do */ - err = 0; + runtime->status->state == SNDRV_PCM_STATE_PREPARED && + size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) goto _end_unlock; - } } avail = snd_pcm_avail(substream); diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 92e6524a3a9d..7d4640d1fe9f 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -393,7 +393,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT)) return -ENXIO; - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; /* check client structures are in place */ diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 8a146b039276..052e00590259 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -41,6 +41,7 @@ config SND_OXFW * Mackie(Loud) U.420/U.420d * TASCAM FireOne * Stanton Controllers & Systems 1 Deck/Mixer + * APOGEE duet FireWire To compile this driver as a module, choose M here: the module will be called snd-oxfw. @@ -161,5 +162,6 @@ config SND_FIREFACE help Say Y here to include support for RME fireface series. * Fireface 400 + * Fireface 800 endif # SND_FIREWIRE diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 54cdd4ffa9ce..ac20acf48fc6 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -131,7 +131,7 @@ TRACE_EVENT(in_packet_without_header, __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %03u %3u %3u %02u %01u %02u", + "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u", __entry->second, __entry->cycle, __entry->src, @@ -169,7 +169,7 @@ TRACE_EVENT(out_packet_without_header, __entry->dest = fw_parent_device(s->unit)->node_id; __entry->payload_quadlets = payload_length / 4; __entry->data_blocks = data_blocks, - __entry->data_blocks = s->data_block_counter, + __entry->data_block_counter = s->data_block_counter, __entry->packet_index = s->packet_index; __entry->irq = !!in_interrupt(); __entry->index = index; diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9be76c808fcc..3ada55ed5381 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -654,15 +654,17 @@ end: } static int handle_in_packet_without_header(struct amdtp_stream *s, - unsigned int payload_quadlets, unsigned int cycle, + unsigned int payload_length, unsigned int cycle, unsigned int index) { __be32 *buffer; + unsigned int payload_quadlets; unsigned int data_blocks; struct snd_pcm_substream *pcm; unsigned int pcm_frames; buffer = s->buffer.packets[s->packet_index].buffer; + payload_quadlets = payload_length / 4; data_blocks = payload_quadlets / s->data_block_quadlets; trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks, diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 672d13488454..d91874275d2c 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -408,7 +408,7 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal), /* Apogee Electronics, Ensemble */ - SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal), + SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal), /* ESI, Quatafire610 */ SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal), /* AcousticReality, eARMasterOne */ diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 8f807284ba54..79a7d6d99d72 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,3 +1,4 @@ snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ - ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o \ + ff-protocol-ff800.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index bf47f9ec8703..d0bc96b20a65 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -8,11 +8,6 @@ #include "ff.h" -static inline unsigned int get_multiplier_mode_with_index(unsigned int index) -{ - return ((int)index - 1) / 2; -} - static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -24,10 +19,16 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(c, pcm_channels[mode])) continue; @@ -49,10 +50,16 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; + if (!snd_interval_test(r, amdtp_rate_table[i])) continue; @@ -66,7 +73,6 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { - unsigned int mode; unsigned int rate, channels; int i; @@ -76,7 +82,12 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw, hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { - mode = get_multiplier_mode_with_index(i); + enum snd_ff_stream_mode mode; + int err; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) @@ -141,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock; - err = ff->spec->protocol->get_clock(ff, &rate, &src); + err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 40ccbfd8ef89..a0c550dabe9a 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -12,16 +12,205 @@ static void proc_dump_clock_config(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + unsigned int rate; + const char *src; + int err; - ff->spec->protocol->dump_clock_config(ff, buffer); + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", + (data & 0x20) ? "Professional" : "Consumer", + (data & 0x40) ? "on" : "off"); + + snd_iprintf(buffer, "Optical output interface format: %s\n", + ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); + + snd_iprintf(buffer, "Word output single speed: %s\n", + ((data >> 8) & 0x20) ? "on" : "off"); + + snd_iprintf(buffer, "S/PDIF input interface: %s\n", + ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); + + switch ((data >> 1) & 0x03) { + case 0x01: + rate = 32000; + break; + case 0x00: + rate = 44100; + break; + case 0x03: + rate = 48000; + break; + case 0x02: + default: + return; + } + + if (data & 0x08) + rate *= 2; + else if (data & 0x10) + rate *= 4; + + snd_iprintf(buffer, "Sampling rate: %d\n", rate); + + if (data & 0x01) { + src = "Internal"; + } else { + switch ((data >> 10) & 0x07) { + case 0x00: + src = "ADAT1"; + break; + case 0x01: + src = "ADAT2"; + break; + case 0x03: + src = "S/PDIF"; + break; + case 0x04: + src = "Word"; + break; + case 0x05: + src = "LTC"; + break; + default: + return; + } + } + + snd_iprintf(buffer, "Sync to clock source: %s\n", src); } static void proc_dump_sync_status(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "External source detection:\n"); + + snd_iprintf(buffer, "Word Clock:"); + if ((data >> 24) & 0x20) { + if ((data >> 24) & 0x40) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "S/PDIF:"); + if ((data >> 16) & 0x10) { + if ((data >> 16) & 0x04) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "ADAT1:"); + if ((data >> 8) & 0x04) { + if ((data >> 8) & 0x10) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "ADAT2:"); + if ((data >> 8) & 0x08) { + if ((data >> 8) & 0x20) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "\nUsed external source:\n"); + + if (((data >> 22) & 0x07) == 0x07) { + snd_iprintf(buffer, "None\n"); + } else { + switch ((data >> 22) & 0x07) { + case 0x00: + snd_iprintf(buffer, "ADAT1:"); + break; + case 0x01: + snd_iprintf(buffer, "ADAT2:"); + break; + case 0x03: + snd_iprintf(buffer, "S/PDIF:"); + break; + case 0x04: + snd_iprintf(buffer, "Word:"); + break; + case 0x07: + snd_iprintf(buffer, "Nothing:"); + break; + case 0x02: + case 0x05: + case 0x06: + default: + snd_iprintf(buffer, "unknown:"); + break; + } + + if ((data >> 25) & 0x07) { + switch ((data >> 25) & 0x07) { + case 0x01: + snd_iprintf(buffer, "32000\n"); + break; + case 0x02: + snd_iprintf(buffer, "44100\n"); + break; + case 0x03: + snd_iprintf(buffer, "48000\n"); + break; + case 0x04: + snd_iprintf(buffer, "64000\n"); + break; + case 0x05: + snd_iprintf(buffer, "88200\n"); + break; + case 0x06: + snd_iprintf(buffer, "96000\n"); + break; + case 0x07: + snd_iprintf(buffer, "128000\n"); + break; + case 0x08: + snd_iprintf(buffer, "176400\n"); + break; + case 0x09: + snd_iprintf(buffer, "192000\n"); + break; + case 0x00: + snd_iprintf(buffer, "unknown\n"); + break; + } + } + } - ff->spec->protocol->dump_sync_status(ff, buffer); + snd_iprintf(buffer, "Multiplied:"); + snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); } static void add_node(struct snd_ff *ff, struct snd_info_entry *root, diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 654a50319198..2280fab9b3c7 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -14,85 +14,60 @@ #define FF400_ISOC_COMM_START 0x000080100508ull #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull -#define FF400_SYNC_STATUS 0x0000801c0000ull -#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */ -#define FF400_CLOCK_CONFIG 0x0000801c0004ull -#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull -#define FF400_MIDI_RX_PORT_0 0x000080180000ull -#define FF400_MIDI_RX_PORT_1 0x000080190000ull - -static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src) +/* + * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, + * we can allocate between 0 and 7 channel. + */ +static int keep_resources(struct snd_ff *ff, unsigned int rate) { - __le32 reg; - u32 data; + enum snd_ff_stream_mode mode; + int i; int err; - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - data = le32_to_cpu(reg); - /* Calculate sampling rate. */ - switch ((data >> 1) & 0x03) { - case 0x01: - *rate = 32000; - break; - case 0x00: - *rate = 44100; - break; - case 0x03: - *rate = 48000; - break; - case 0x02: - default: - return -EIO; - } - - if (data & 0x08) - *rate *= 2; - else if (data & 0x10) - *rate *= 4; + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; - /* Calculate source of clock. */ - if (data & 0x01) { - *src = SND_FF_CLOCK_SRC_INTERNAL; - } else { - /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ - switch ((data >> 10) & 0x07) { - case 0x03: - *src = SND_FF_CLOCK_SRC_SPDIF; - break; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - case 0x00: - default: - *src = SND_FF_CLOCK_SRC_ADAT; - break; - } - } + /* Keep resources for out-stream. */ + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, + ff->spec->pcm_playback_channels[mode]); + if (err < 0) + return err; + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); - return 0; + return err; } static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { __le32 reg; - int i, err; + int err; - /* Check whether the given value is supported or not. */ - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - if (i == CIP_SFC_COUNT) - return -EINVAL; + err = keep_resources(ff, rate); + if (err < 0) + return err; /* Set the number of data blocks transferred in a second. */ reg = cpu_to_le32(rate); @@ -142,233 +117,45 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); } -static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) +static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) { - __le32 *reg; int i; - int err; - reg = kcalloc(18, sizeof(__le32), GFP_KERNEL); - if (reg == NULL) - return -ENOMEM; + for (i = 0; i < length / 4; i++) { + u32 quad = le32_to_cpu(buf[i]); + u8 byte; + unsigned int index; + struct snd_rawmidi_substream *substream; - if (enable) { + /* Message in first port. */ /* - * Each quadlet is corresponding to data channels in a data - * blocks in reverse order. Precisely, quadlets for available - * data channels should be enabled. Here, I take second best - * to fetch PCM frames from all of data channels regardless of - * stf. + * 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. */ - for (i = 0; i < 18; ++i) - reg[i] = cpu_to_le32(0x00000001); - } - - err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, - FF400_FETCH_PCM_FRAMES, reg, - sizeof(__le32) * 18, 0); - kfree(reg); - return err; -} - -static void ff400_dump_sync_status(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_SYNC_STATUS, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "External source detection:\n"); - - snd_iprintf(buffer, "Word Clock:"); - if ((data >> 24) & 0x20) { - if ((data >> 24) & 0x40) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "S/PDIF:"); - if ((data >> 16) & 0x10) { - if ((data >> 16) & 0x04) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "ADAT:"); - if ((data >> 8) & 0x04) { - if ((data >> 8) & 0x10) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "\nUsed external source:\n"); - - if (((data >> 22) & 0x07) == 0x07) { - snd_iprintf(buffer, "None\n"); - } else { - switch ((data >> 22) & 0x07) { - case 0x00: - snd_iprintf(buffer, "ADAT:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - case 0x01: - case 0x02: - case 0x05: - case 0x06: - default: - snd_iprintf(buffer, "unknown:"); - break; - } - - if ((data >> 25) & 0x07) { - switch ((data >> 25) & 0x07) { - case 0x01: - snd_iprintf(buffer, "32000\n"); - break; - case 0x02: - snd_iprintf(buffer, "44100\n"); - break; - case 0x03: - snd_iprintf(buffer, "48000\n"); - break; - case 0x04: - snd_iprintf(buffer, "64000\n"); - break; - case 0x05: - snd_iprintf(buffer, "88200\n"); - break; - case 0x06: - snd_iprintf(buffer, "96000\n"); - break; - case 0x07: - snd_iprintf(buffer, "128000\n"); - break; - case 0x08: - snd_iprintf(buffer, "176400\n"); - break; - case 0x09: - snd_iprintf(buffer, "192000\n"); - break; - case 0x00: - snd_iprintf(buffer, "unknown\n"); - break; + 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); } } - } - - snd_iprintf(buffer, "Multiplied:"); - snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); -} -static void ff400_dump_clock_config(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - unsigned int rate; - const char *src; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", - (data & 0x20) ? "Professional" : "Consumer", - (data & 0x40) ? "on" : "off"); - - snd_iprintf(buffer, "Optical output interface format: %s\n", - ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); - - snd_iprintf(buffer, "Word output single speed: %s\n", - ((data >> 8) & 0x20) ? "on" : "off"); - - snd_iprintf(buffer, "S/PDIF input interface: %s\n", - ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); - - switch ((data >> 1) & 0x03) { - case 0x01: - rate = 32000; - break; - case 0x00: - rate = 44100; - break; - case 0x03: - rate = 48000; - break; - case 0x02: - default: - return; - } - - if (data & 0x08) - rate *= 2; - else if (data & 0x10) - rate *= 4; - - snd_iprintf(buffer, "Sampling rate: %d\n", rate); - - if (data & 0x01) { - src = "Internal"; - } else { - switch ((data >> 10) & 0x07) { - case 0x00: - src = "ADAT"; - break; - case 0x03: - src = "S/PDIF"; - break; - case 0x04: - src = "Word"; - break; - case 0x05: - src = "LTC"; - break; - default: - return; + /* 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); + } } } - - snd_iprintf(buffer, "Sync to clock source: %s\n", src); } const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .get_clock = ff400_get_clock, + .handle_midi_msg = ff400_handle_midi_msg, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, - .switch_fetching_mode = ff400_switch_fetching_mode, - - .dump_sync_status = ff400_dump_sync_status, - .dump_clock_config = ff400_dump_clock_config, - - .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, - .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0, - .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1, }; diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c new file mode 100644 index 000000000000..2acbf6039770 --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-ff800.c @@ -0,0 +1,143 @@ +/* + * ff-protocol-ff800.c - a part of driver for RME Fireface series + * + * Copyright (c) 2018 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/delay.h> + +#include "ff.h" + +#define FF800_STF 0x0000fc88f000 +#define FF800_RX_PACKET_FORMAT 0x0000fc88f004 +#define FF800_ALLOC_TX_STREAM 0x0000fc88f008 +#define FF800_ISOC_COMM_START 0x0000fc88f00c +#define FF800_TX_S800_FLAG 0x00000800 +#define FF800_ISOC_COMM_STOP 0x0000fc88f010 + +#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 + +static int allocate_rx_resources(struct snd_ff *ff) +{ + u32 data; + __le32 reg; + int err; + + // Controllers should allocate isochronous resources for rx stream. + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + // Set isochronous channel and the number of quadlets of rx packets. + data = ff->rx_stream.data_block_quadlets << 3; + data = (data << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); +} + +static int allocate_tx_resources(struct snd_ff *ff) +{ + __le32 reg; + unsigned int count; + unsigned int tx_isoc_channel; + int err; + + reg = cpu_to_le32(ff->tx_stream.data_block_quadlets); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ALLOC_TX_STREAM, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Wait till the format of tx packet is available. + count = 0; + while (count++ < 10) { + u32 data; + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + FF800_TX_PACKET_ISOC_CH, ®, sizeof(reg), 0); + if (err < 0) + return err; + + data = le32_to_cpu(reg); + if (data != 0xffffffff) { + tx_isoc_channel = data; + break; + } + + msleep(50); + } + if (count >= 10) + return -ETIMEDOUT; + + // NOTE: this is a makeshift to start OHCI 1394 IR context in the + // channel. On the other hand, 'struct fw_iso_resources.allocated' is + // not true and it's not deallocated at stop. + ff->tx_resources.channel = tx_isoc_channel; + + return 0; +} + +static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +{ + __le32 reg; + int err; + + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // If starting isochronous communication immediately, change of STF has + // no effect. In this case, the communication runs based on former STF. + // Let's sleep for a bit. + msleep(100); + + err = allocate_rx_resources(ff); + if (err < 0) + return err; + + err = allocate_tx_resources(ff); + if (err < 0) + return err; + + reg = cpu_to_le32(0x80000000); + reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); + if (fw_parent_device(ff->unit)->max_speed == SCODE_800) + reg |= cpu_to_le32(FF800_TX_S800_FLAG); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_START, ®, sizeof(reg), 0); +} + +static void ff800_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x80000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_ISOC_COMM_STOP, ®, sizeof(reg), 0); +} + +static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u8 byte = le32_to_cpu(buf[i]) & 0xff; + struct snd_rawmidi_substream *substream; + + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream) + snd_rawmidi_receive(substream, &byte, 1); + } +} + +const struct snd_ff_protocol snd_ff_protocol_ff800 = { + .handle_midi_msg = ff800_handle_midi_msg, + .begin_session = ff800_begin_session, + .finish_session = ff800_finish_session, +}; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 78880922120e..a490e4553721 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -10,73 +10,71 @@ #define CALLBACK_TIMEOUT_MS 200 -static int get_rate_mode(unsigned int rate, unsigned int *mode) +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode) { - int i; - - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - - if (i == CIP_SFC_COUNT) + static const enum snd_ff_stream_mode modes[] = { + [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW, + [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID, + [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH, + [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH, + }; + + if (sfc >= CIP_SFC_COUNT) return -EINVAL; - *mode = ((int)i - 1) / 2; + *mode = modes[sfc]; return 0; } -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) +static void release_resources(struct snd_ff *ff) { - int mode; - int err; - - err = get_rate_mode(rate, &mode); - if (err < 0) - return err; + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); +} - /* Keep resources for in-stream. */ - err = amdtp_ff_set_parameters(&ff->tx_stream, rate, - ff->spec->pcm_capture_channels[mode]); - if (err < 0) - return err; - ff->tx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->tx_resources, - amdtp_stream_get_max_payload(&ff->tx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; +static int switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + unsigned int count; + __le32 *reg; + int i; + int err; - /* Keep resources for out-stream. */ - err = amdtp_ff_set_parameters(&ff->rx_stream, rate, - ff->spec->pcm_playback_channels[mode]); - if (err < 0) - return err; - ff->rx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - fw_iso_resources_free(&ff->tx_resources); + count = 0; + for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) + count = max(count, ff->spec->pcm_playback_channels[i]); + + reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + if (!enable) { + /* + * Each quadlet is corresponding to data channels in a data + * blocks in reverse order. Precisely, quadlets for available + * data channels should be enabled. Here, I take second best + * to fetch PCM frames from all of data channels regardless of + * stf. + */ + for (i = 0; i < count; ++i) + reg[i] = cpu_to_le32(0x00000001); + } + err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, + SND_FF_REG_FETCH_PCM_FRAMES, reg, + sizeof(__le32) * count, 0); + kfree(reg); return err; } -static void release_resources(struct snd_ff *ff) -{ - fw_iso_resources_free(&ff->tx_resources); - fw_iso_resources_free(&ff->rx_resources); -} - static inline void finish_session(struct snd_ff *ff) { ff->spec->protocol->finish_session(ff); - ff->spec->protocol->switch_fetching_mode(ff, false); + switch_fetching_mode(ff, false); } static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -149,7 +147,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0; - err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); + err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); if (err < 0) return err; if (curr_rate != rate || @@ -168,9 +166,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) * packets. Then, the device transfers packets. */ if (!amdtp_stream_running(&ff->rx_stream)) { - err = keep_resources(ff, rate); + enum snd_ff_stream_mode mode; + int i; + + for (i = 0; i < CIP_SFC_COUNT; ++i) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) - goto error; + return err; + + err = amdtp_ff_set_parameters(&ff->tx_stream, rate, + ff->spec->pcm_capture_channels[mode]); + if (err < 0) + return err; + + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, + ff->spec->pcm_playback_channels[mode]); + if (err < 0) + return err; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -188,7 +206,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; } - err = ff->spec->protocol->switch_fetching_mode(ff, true); + err = switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 332b29f8ed75..5f4ddfd55403 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -8,6 +8,72 @@ #include "ff.h" +#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull +#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull + +int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + /* Calculate sampling rate. */ + switch ((data >> 1) & 0x03) { + case 0x01: + *rate = 32000; + break; + case 0x00: + *rate = 44100; + break; + case 0x03: + *rate = 48000; + break; + case 0x02: + default: + return -EIO; + } + + if (data & 0x08) + *rate *= 2; + else if (data & 0x10) + *rate *= 4; + + /* Calculate source of clock. */ + if (data & 0x01) { + *src = SND_FF_CLOCK_SRC_INTERNAL; + } else { + /* TODO: 0x02, 0x06, 0x07? */ + switch ((data >> 10) & 0x07) { + case 0x00: + *src = SND_FF_CLOCK_SRC_ADAT1; + break; + case 0x01: + *src = SND_FF_CLOCK_SRC_ADAT2; + break; + case 0x03: + *src = SND_FF_CLOCK_SRC_SPDIF; + break; + case 0x04: + *src = SND_FF_CLOCK_SRC_WORD; + break; + case 0x05: + *src = SND_FF_CLOCK_SRC_LTC; + break; + default: + return -EIO; + } + } + + return 0; +} + static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, int rcode) { @@ -90,10 +156,10 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) fill_midi_buf(ff, port, i, buf[i]); if (port == 0) { - addr = ff->spec->protocol->midi_rx_port_0_reg; + addr = SND_FF_REG_MIDI_RX_PORT_0; callback = finish_transmit_midi0_msg; } else { - addr = ff->spec->protocol->midi_rx_port_1_reg; + addr = SND_FF_REG_MIDI_RX_PORT_1; callback = finish_transmit_midi1_msg; } @@ -140,42 +206,10 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, { struct snd_ff *ff = callback_data; __le32 *buf = data; - u32 quad; - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - int i; fw_send_response(card, request, RCODE_COMPLETE); - for (i = 0; i < length / 4; i++) { - quad = le32_to_cpu(buf[i]); - - /* 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); - } - } - - /* 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); - } - } - } + ff->spec->protocol->handle_midi_msg(ff, buf, length); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -203,36 +237,33 @@ static int allocate_own_address(struct snd_ff *ff, int i) } /* - * The configuration to start asynchronous transactions for MIDI messages is in - * 0x'0000'8010'051c. This register includes the other options, thus this driver - * doesn't touch it and leaves the decision to userspace. The userspace MUST add - * 0x04000000 to write transactions to the register to receive any MIDI - * messages. - * - * Here, I just describe MIDI-related offsets of the register, in little-endian - * order. - * * Controllers are allowed to register higher 4 bytes of address to receive - * the transactions. The register is 0x'0000'8010'03f4. On the other hand, the - * controllers are not allowed to register lower 4 bytes of the address. They - * are forced to select from 4 options by writing corresponding bits to - * 0x'0000'8010'051c. + * the transactions. Different models have different registers for this purpose; + * e.g. 0x'0000'8010'03f4 for Fireface 400. + * The controllers are not allowed to register lower 4 bytes of the address. + * They are forced to select one of 4 options for the part of address by writing + * corresponding bits to 0x'0000'8010'051f. + * + * The 3rd-6th bits of this register are flags to indicate lower 4 bytes of + * address to which the device transferrs the transactions. In short: + * - 0x20: 0x'....'....'0000'0180 + * - 0x10: 0x'....'....'0000'0100 + * - 0x08: 0x'....'....'0000'0080 + * - 0x04: 0x'....'....'0000'0000 * - * The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes - * of address to which the device transferrs the transactions. - * - 6th: 0x'....'....'0000'0180 - * - 5th: 0x'....'....'0000'0100 - * - 4th: 0x'....'....'0000'0080 - * - 3rd: 0x'....'....'0000'0000 + * This driver configure 0x'....'....'0000'0000 to receive MIDI messages from + * units. The 3rd bit of the register should be configured, however this driver + * deligates this task to userspace applications due to a restriction that this + * register is write-only and the other bits have own effects. * - * This driver configure 0x'....'....'0000'0000 for units to receive MIDI - * messages. 3rd bit of the register should be configured, however this driver - * deligates this task to user space applications due to a restriction that - * this register is write-only and the other bits have own effects. + * Unlike Fireface 800, Fireface 400 cancels transferring asynchronous + * transactions when the 1st and 2nd of the register stand. These two bits have + * the same effect. + * - 0x02, 0x01: cancel transferring * - * The 1st and 2nd bits in LSB of this register are used to cancel transferring - * asynchronous transactions. These two bits have the same effect. - * - 1st/2nd: cancel transferring + * On the other hand, the bits have no effect on Fireface 800. This model + * cancels asynchronous transactions when the higher 4 bytes of address is + * overwritten with zero. */ int snd_ff_transaction_reregister(struct snd_ff *ff) { @@ -247,7 +278,7 @@ int snd_ff_transaction_reregister(struct snd_ff *ff) addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32); reg = cpu_to_le32(addr); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->midi_high_addr, ®, sizeof(reg), 0); } @@ -288,7 +319,7 @@ void snd_ff_transaction_unregister(struct snd_ff *ff) /* Release higher 4 bytes of address. */ reg = cpu_to_le32(0x00000000); snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->midi_high_addr, ®, sizeof(reg), 0); fw_core_remove_address_handler(&ff->async_handler); diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 3f61cfeace69..36575f4159d1 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -145,6 +145,16 @@ static void snd_ff_remove(struct fw_unit *unit) fw_unit_put(ff->unit); } +static const struct snd_ff_spec spec_ff800 = { + .name = "Fireface800", + .pcm_capture_channels = {28, 20, 12}, + .pcm_playback_channels = {28, 20, 12}, + .midi_in_ports = 1, + .midi_out_ports = 1, + .protocol = &snd_ff_protocol_ff800, + .midi_high_addr = 0x000200000320ull, +}; + static const struct snd_ff_spec spec_ff400 = { .name = "Fireface400", .pcm_capture_channels = {18, 14, 10}, @@ -152,9 +162,22 @@ static const struct snd_ff_spec spec_ff400 = { .midi_in_ports = 2, .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, + .midi_high_addr = 0x0000801003f4ull, }; static const struct ieee1394_device_id snd_ff_id_table[] = { + /* Fireface 800 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = 0x000001, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ff800, + }, /* Fireface 400 */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -162,7 +185,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_VERSION | IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, - .specifier_id = 0x000a35, + .specifier_id = OUI_RME, .version = 0x000002, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 64df44beb950..7dfc7745a914 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -31,23 +31,34 @@ #include "../amdtp-stream.h" #include "../iso-resources.h" -#define SND_FF_STREAM_MODES 3 - #define SND_FF_MAXIMIM_MIDI_QUADS 9 #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 +#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull +/* For block write request. */ +#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull +#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull + +enum snd_ff_stream_mode { + SND_FF_STREAM_MODE_LOW = 0, + SND_FF_STREAM_MODE_MID, + SND_FF_STREAM_MODE_HIGH, + SND_FF_STREAM_MODE_COUNT, +}; + struct snd_ff_protocol; struct snd_ff_spec { const char *const name; - const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES]; - const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES]; + const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT]; + const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT]; unsigned int midi_in_ports; unsigned int midi_out_ports; const struct snd_ff_protocol *protocol; + u64 midi_high_addr; }; struct snd_ff { @@ -89,31 +100,24 @@ struct snd_ff { enum snd_ff_clock_src { SND_FF_CLOCK_SRC_INTERNAL, SND_FF_CLOCK_SRC_SPDIF, - SND_FF_CLOCK_SRC_ADAT, + SND_FF_CLOCK_SRC_ADAT1, + SND_FF_CLOCK_SRC_ADAT2, SND_FF_CLOCK_SRC_WORD, SND_FF_CLOCK_SRC_LTC, - /* TODO: perhaps ADAT2 and TCO exists. */ + /* TODO: perhaps TCO exists. */ }; struct snd_ff_protocol { - int (*get_clock)(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src); + void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); - int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); - - void (*dump_sync_status)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - void (*dump_clock_config)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - - u64 midi_high_addr_reg; - u64 midi_rx_port_0_reg; - u64 midi_rx_port_1_reg; }; +extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400; +int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff); @@ -125,6 +129,8 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, + enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index afb78d90384b..3d27f3378d5d 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -20,6 +20,7 @@ #define VENDOR_LACIE 0x00d04b #define VENDOR_TASCAM 0x00022e #define OUI_STANTON 0x001260 +#define OUI_APOGEE 0x0003db #define MODEL_SATELLITE 0x00200f @@ -397,6 +398,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = { .vendor_id = OUI_STANTON, .model_id = 0x002000, }, + // APOGEE, duet FireWire + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_APOGEE, + .model_id = 0x01dddd, + }, { } }; MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index ab482423c165..a52d1f76c610 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -117,6 +117,55 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } +static void read_status_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int data_blocks) +{ + struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream); + bool used = READ_ONCE(tscm->hwdep->used); + int i; + + for (i = 0; i < data_blocks; i++) { + unsigned int index; + __be32 before; + __be32 after; + + index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT; + before = tscm->state[index]; + after = buffer[s->data_block_quadlets - 1]; + + if (used && index > 4 && index < 16) { + __be32 mask; + + if (index == 5) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 6) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 8) + mask = cpu_to_be32(~0x000f0f00); + else + mask = cpu_to_be32(~0x00000000); + + if ((before ^ after) & mask) { + struct snd_firewire_tascam_change *entry = + &tscm->queue[tscm->push_pos]; + + spin_lock_irq(&tscm->lock); + entry->index = index; + entry->before = before; + entry->after = after; + if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT) + tscm->push_pos = 0; + spin_unlock_irq(&tscm->lock); + + wake_up(&tscm->hwdep_wait); + } + } + + tscm->state[index] = after; + buffer += s->data_block_quadlets; + } +} + static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, @@ -128,7 +177,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, if (data_blocks > 0 && pcm) read_pcm_s32(s, pcm, buffer, data_blocks); - /* A place holder for control messages. */ + read_status_messages(s, buffer, data_blocks); return data_blocks; } diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 4e4c1e9020e8..0414abf5daa8 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -16,18 +16,93 @@ #include "tascam.h" +static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf, + long count, loff_t *offset) +{ + struct snd_firewire_event_lock_status event = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + }; + + event.status = (tscm->dev_lock_count > 0); + tscm->dev_lock_changed = false; + count = min_t(long, count, sizeof(event)); + + spin_unlock_irq(&tscm->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf, + long remained, loff_t *offset) +{ + char __user *pos = buf; + unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL; + struct snd_firewire_tascam_change *entries = tscm->queue; + long count; + + // At least, one control event can be copied. + if (remained < sizeof(type) + sizeof(*entries)) { + spin_unlock_irq(&tscm->lock); + return -EINVAL; + } + + // Copy the type field later. + count = sizeof(type); + remained -= sizeof(type); + pos += sizeof(type); + + while (true) { + unsigned int head_pos; + unsigned int tail_pos; + unsigned int length; + + if (tscm->pull_pos == tscm->push_pos) + break; + else if (tscm->pull_pos < tscm->push_pos) + tail_pos = tscm->push_pos; + else + tail_pos = SND_TSCM_QUEUE_COUNT; + head_pos = tscm->pull_pos; + + length = (tail_pos - head_pos) * sizeof(*entries); + if (remained < length) + length = rounddown(remained, sizeof(*entries)); + if (length == 0) + break; + + spin_unlock_irq(&tscm->lock); + if (copy_to_user(pos, &entries[head_pos], length)) + return -EFAULT; + + spin_lock_irq(&tscm->lock); + + tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT; + + count += length; + remained -= length; + pos += length; + } + + spin_unlock_irq(&tscm->lock); + + if (copy_to_user(buf, &type, sizeof(type))) + return -EFAULT; + + return count; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_tscm *tscm = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event = { - .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, - }; spin_lock_irq(&tscm->lock); - while (!tscm->dev_lock_changed) { + while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) { prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&tscm->lock); schedule(); @@ -37,15 +112,15 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&tscm->lock); } - event.lock_status.status = (tscm->dev_lock_count > 0); - tscm->dev_lock_changed = false; - - spin_unlock_irq(&tscm->lock); - - count = min_t(long, count, sizeof(event.lock_status)); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; + // NOTE: The acquired lock should be released in callee side. + if (tscm->dev_lock_changed) { + count = tscm_hwdep_read_locked(tscm, buf, count, offset); + } else if (tscm->push_pos != tscm->pull_pos) { + count = tscm_hwdep_read_queue(tscm, buf, count, offset); + } else { + spin_unlock_irq(&tscm->lock); + count = 0; + } return count; } @@ -59,7 +134,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &tscm->hwdep_wait, wait); spin_lock_irq(&tscm->lock); - if (tscm->dev_lock_changed) + if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos) events = EPOLLIN | EPOLLRDNORM; else events = 0; @@ -123,6 +198,14 @@ static int hwdep_unlock(struct snd_tscm *tscm) return err; } +static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg) +{ + if (copy_to_user(arg, tscm->state, sizeof(tscm->state))) + return -EFAULT; + + return 0; +} + static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) { struct snd_tscm *tscm = hwdep->private_data; @@ -147,6 +230,8 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, return hwdep_lock(tscm); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return hwdep_unlock(tscm); + case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE: + return tscm_hwdep_state(tscm, (void __user *)arg); default: return -ENOIOCTLCMD; } @@ -185,5 +270,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) hwdep->private_data = tscm; hwdep->exclusive = true; + tscm->hwdep = hwdep; + return err; } diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index a5bd167eb5d9..6a411ee0dcf1 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -62,6 +62,8 @@ struct snd_fw_async_midi_port { int consume_bytes; }; +#define SND_TSCM_QUEUE_COUNT 16 + struct snd_tscm { struct snd_card *card; struct fw_unit *unit; @@ -89,6 +91,13 @@ struct snd_tscm { /* For MIDI message outgoing transactions. */ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; + + // A cache of status information in tx isoc packets. + __be32 state[SNDRV_FIREWIRE_TASCAM_STATE_COUNT]; + struct snd_hwdep *hwdep; + struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT]; + unsigned int pull_pos; + unsigned int push_pos; }; #define TSCM_ADDR_BASE 0xffff00000000ull diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 714a51721a31..012305177f68 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -9,8 +9,6 @@ #include <sound/hdaudio.h> #include "trace.h" -static void process_unsol_events(struct work_struct *work); - static const struct hdac_bus_ops default_ops = { .command = snd_hdac_bus_send_cmd, .get_response = snd_hdac_bus_get_response, @@ -37,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, bus->io_ops = io_ops; INIT_LIST_HEAD(&bus->stream_list); INIT_LIST_HEAD(&bus->codec_list); - INIT_WORK(&bus->unsol_work, process_unsol_events); + INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events); spin_lock_init(&bus->reg_lock); mutex_init(&bus->cmd_mutex); bus->irq = -1; @@ -148,7 +146,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); /* * process queued unsolicited events */ -static void process_unsol_events(struct work_struct *work) +void snd_hdac_bus_process_unsol_events(struct work_struct *work) { struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); struct hdac_device *codec; @@ -171,6 +169,7 @@ static void process_unsol_events(struct work_struct *work) drv->unsol_event(codec, res); } } +EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events); /** * snd_hdac_bus_add_device - Add a codec to bus diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c index 6e46a9c73aed..a6d37b9d6413 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -54,41 +54,44 @@ EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); /** * snd_hdac_display_power - Power up / down the power refcount * @bus: HDA core bus + * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller * @enable: power up or down * - * This function is supposed to be used only by a HD-audio controller - * driver that needs the interaction with graphics driver. + * This function is used by either HD-audio controller or codec driver that + * needs the interaction with graphics driver. * - * This function manages a refcount and calls the get_power() and + * This function updates the power status, and calls the get_power() and * put_power() ops accordingly, toggling the codec wakeup, too. - * - * Returns zero for success or a negative error code. */ -int snd_hdac_display_power(struct hdac_bus *bus, bool enable) +void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) { struct drm_audio_component *acomp = bus->audio_component; - if (!acomp || !acomp->ops) - return -ENODEV; - dev_dbg(bus->dev, "display power %s\n", enable ? "enable" : "disable"); + if (enable) + set_bit(idx, &bus->display_power_status); + else + clear_bit(idx, &bus->display_power_status); - if (enable) { - if (!bus->drm_power_refcount++) { + if (!acomp || !acomp->ops) + return; + + if (bus->display_power_status) { + if (!bus->display_power_active) { if (acomp->ops->get_power) acomp->ops->get_power(acomp->dev); snd_hdac_set_codec_wakeup(bus, true); snd_hdac_set_codec_wakeup(bus, false); + bus->display_power_active = true; } } else { - WARN_ON(!bus->drm_power_refcount); - if (!--bus->drm_power_refcount) + if (bus->display_power_active) { if (acomp->ops->put_power) acomp->ops->put_power(acomp->dev); + bus->display_power_active = false; + } } - - return 0; } EXPORT_SYMBOL_GPL(snd_hdac_display_power); @@ -321,10 +324,12 @@ int snd_hdac_acomp_exit(struct hdac_bus *bus) if (!acomp) return 0; - WARN_ON(bus->drm_power_refcount); - if (bus->drm_power_refcount > 0 && acomp->ops) + if (WARN_ON(bus->display_power_active) && acomp->ops) acomp->ops->put_power(acomp->dev); + bus->display_power_active = false; + bus->display_power_status = 0; + component_master_del(dev, &hdac_component_master_ops); bus->audio_component = NULL; diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index dbf02a3a8d2f..95b073ee4b32 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -622,23 +622,6 @@ int snd_hdac_power_down_pm(struct hdac_device *codec) EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif -/** - * snd_hdac_link_power - Enable/disable the link power for a codec - * @codec: the codec object - * @bool: enable or disable the link power - */ -int snd_hdac_link_power(struct hdac_device *codec, bool enable) -{ - if (!codec->link_power_control) - return 0; - - if (codec->bus->ops->link_power) - return codec->bus->ops->link_power(codec->bus, enable); - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_hdac_link_power); - /* codec vendor labels */ struct hda_vendor_id { unsigned int id; diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index d45a6b9d6437..3d44c358c4b3 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -183,10 +183,10 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, } if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { - if (!access_ok(VERIFY_READ, data, sp->v.size)) + if (!access_ok(data, sp->v.size)) return -EFAULT; } else { - if (!access_ok(VERIFY_READ, data, sp->v.size * 2)) + if (!access_ok(data, sp->v.size * 2)) return -EFAULT; } diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index a31fe1550903..aad74e809797 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1183,7 +1183,7 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, u32 h_stream) { - struct hpi_format hpi_format; + struct hpi_format hpi_format; u16 format; u16 err; u32 h_control; diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 598d140bb7cb..5fc497c6d738 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -903,6 +903,9 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip) struct dsp_spos_instance * ins = chip->dsp_spos_instance; int i; + if (!ins) + return 0; + snd_info_free_entry(ins->proc_sym_info_entry); ins->proc_sym_info_entry = NULL; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 6ebe817801ea..1f25e6d029d8 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -36,6 +36,7 @@ #include <linux/init.h> #include <linux/mutex.h> #include <linux/moduleparam.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/tlv.h> @@ -1026,6 +1027,8 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu, if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) return -EINVAL; + ipcm->substream = array_index_nospec(ipcm->substream, + EMU10K1_FX8010_PCM_COUNT); if (ipcm->channels > 32) return -EINVAL; pcm = &emu->fx8010.pcm[ipcm->substream]; @@ -1072,6 +1075,8 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu, if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) return -EINVAL; + ipcm->substream = array_index_nospec(ipcm->substream, + EMU10K1_FX8010_PCM_COUNT); pcm = &emu->fx8010.pcm[ipcm->substream]; mutex_lock(&emu->fx8010.lock); spin_lock_irq(&emu->reg_lock); diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c deleted file mode 100644 index bbd6c87a4ed6..000000000000 --- a/sound/pci/hda/dell_wmi_helper.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Helper functions for Dell Mic Mute LED control; - * to be included from codec driver - */ - -#if IS_ENABLED(CONFIG_DELL_LAPTOP) -#include <linux/dell-led.h> - -static int (*dell_micmute_led_set_func)(int); - -static void dell_micmute_update(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - dell_micmute_led_set_func(spec->micmute_led.led_value); -} - -static void alc_fixup_dell_wmi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - bool removefunc = false; - - if (action == HDA_FIXUP_ACT_PROBE) { - if (!dell_micmute_led_set_func) - dell_micmute_led_set_func = symbol_request(dell_micmute_led_set); - if (!dell_micmute_led_set_func) { - codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n"); - return; - } - - removefunc = (dell_micmute_led_set_func(false) < 0) || - (snd_hda_gen_add_micmute_led(codec, - dell_micmute_update) < 0); - } - - if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { - symbol_put(dell_micmute_led_set); - dell_micmute_led_set_func = NULL; - } -} - -#else /* CONFIG_DELL_LAPTOP */ -static void alc_fixup_dell_wmi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ -} - -#endif /* CONFIG_DELL_LAPTOP */ diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 9174f1b3a987..1ec706ced75c 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -115,7 +115,8 @@ static int hda_codec_driver_probe(struct device *dev) err = snd_hda_codec_build_controls(codec); if (err < 0) goto error_module; - if (codec->card->registered) { + /* only register after the bus probe finished; otherwise it's racy */ + if (!codec->bus->bus_probing && codec->card->registered) { err = snd_card_register(codec->card); if (err < 0) goto error_module; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0957813939e5..9f8d59e7e89f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -36,6 +36,7 @@ #include "hda_beep.h" #include "hda_jack.h" #include <sound/hda_hwdep.h> +#include <sound/hda_component.h> #define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core) #define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core) @@ -799,6 +800,13 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); +/* enable/disable display power per codec */ +static void codec_display_power(struct hda_codec *codec, bool enable) +{ + if (codec->display_power_control) + snd_hdac_display_power(&codec->bus->core, codec->addr, enable); +} + /* also called from hda_bind.c */ void snd_hda_codec_register(struct hda_codec *codec) { @@ -806,7 +814,7 @@ void snd_hda_codec_register(struct hda_codec *codec) return; if (device_is_registered(hda_codec_dev(codec))) { snd_hda_register_beep_device(codec); - snd_hdac_link_power(&codec->core, true); + codec_display_power(codec, true); pm_runtime_enable(hda_codec_dev(codec)); /* it was powered up in snd_hda_codec_new(), now all done */ snd_hda_power_down(codec); @@ -834,7 +842,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device) codec->in_freeing = 1; snd_hdac_device_unregister(&codec->core); - snd_hdac_link_power(&codec->core, false); + codec_display_power(codec, false); put_device(hda_codec_dev(codec)); return 0; } @@ -2926,7 +2934,7 @@ static int hda_codec_runtime_suspend(struct device *dev) (codec_has_clkstop(codec) && codec_has_epss(codec) && (state & AC_PWRST_CLK_STOP_OK))) snd_hdac_codec_link_down(&codec->core); - snd_hdac_link_power(&codec->core, false); + codec_display_power(codec, false); return 0; } @@ -2934,7 +2942,7 @@ static int hda_codec_runtime_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - snd_hdac_link_power(&codec->core, true); + codec_display_power(codec, true); snd_hdac_codec_link_up(&codec->core); hda_call_codec_resume(codec); pm_runtime_mark_last_busy(dev); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index fe2506672a72..532e081f8b8a 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -989,20 +989,9 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr, return azx_rirb_get_response(bus, addr, res); } -static int azx_link_power(struct hdac_bus *bus, bool enable) -{ - struct azx *chip = bus_to_azx(bus); - - if (chip->ops->link_power) - return chip->ops->link_power(chip, enable); - else - return -EINVAL; -} - static const struct hdac_bus_ops bus_core_ops = { .command = azx_send_cmd, .get_response = azx_get_response, - .link_power = azx_link_power, }; #ifdef CONFIG_SND_HDA_DSP_LOADER diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index c95097bb5a0c..7185ed574b41 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -50,11 +50,7 @@ /* 24 unused */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ -#ifdef CONFIG_SND_HDA_I915 -#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ -#else -#define AZX_DCAPS_I915_POWERWELL 0 /* NOP */ -#endif +/* 27 unused */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ #define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 276150f29cda..4095cd7c56c6 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -29,6 +29,7 @@ #include <linux/string.h> #include <linux/bitops.h> #include <linux/module.h> +#include <linux/leds.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/tlv.h> @@ -4035,6 +4036,36 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) +static void call_ledtrig_micmute(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + ledtrig_audio_set(LED_AUDIO_MICMUTE, + spec->micmute_led.led_value ? LED_ON : LED_OFF); +} +#endif + +/** + * snd_hda_gen_fixup_micmute_led - A fixup for mic-mute LED trigger + * + * Pass this function to the quirk entry if another driver supports the + * audio mic-mute LED trigger. Then this will bind the mixer capture switch + * change with the LED. + * + * Note that this fixup has to be called after other fixup that sets + * cap_sync_hook. Otherwise the chaining wouldn't work. + */ +void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_gen_add_micmute_led(codec, call_ledtrig_micmute); +#endif +} +EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); + /* * parse digital I/Os and set up NIDs in BIOS auto-parse mode */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 10123664fa61..78d77042b05a 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -357,5 +357,7 @@ int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); int snd_hda_gen_add_micmute_led(struct hda_codec *codec, void (*hook)(struct hda_codec *)); +void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 76f03abd15ab..e5c49003e75f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -310,31 +310,28 @@ enum { #define AZX_DCAPS_INTEL_HASWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_BAYTRAIL \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_BRASWELL \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_SKYLAKE \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) #define AZX_DCAPS_INTEL_BROXTON \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_I915_POWERWELL) + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -591,8 +588,7 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) struct pci_dev *pci = chip->pci; u32 val; - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_set_codec_wakeup(bus, true); + snd_hdac_set_codec_wakeup(bus, true); if (chip->driver_type == AZX_DRIVER_SKL) { pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); val = val & ~INTEL_HDA_CGCTL_MISCBDCGE; @@ -604,8 +600,8 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) val = val | INTEL_HDA_CGCTL_MISCBDCGE; pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); } - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_set_codec_wakeup(bus, false); + + snd_hdac_set_codec_wakeup(bus, false); /* reduce dma latency to avoid noise */ if (IS_BXT(pci)) @@ -667,13 +663,8 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) return 0; } -/* Enable/disable i915 display power for the link */ -static int azx_intel_link_power(struct azx *chip, bool enable) -{ - struct hdac_bus *bus = azx_bus(chip); - - return snd_hdac_display_power(bus, enable); -} +#define display_power(chip, enable) \ + snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable) /* * Check whether the current DMA position is acceptable for updating @@ -930,35 +921,75 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) mutex_unlock(&card_list_lock); return 0; } -#else -#define azx_add_card_list(chip) /* NOP */ -#define azx_del_card_list(chip) /* NOP */ -#endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP /* * power management */ -static int azx_suspend(struct device *dev) +static bool azx_is_pm_ready(struct snd_card *card) { - struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct hdac_bus *bus; if (!card) - return 0; - + return false; chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); if (chip->disabled || hda->init_failed || !chip->running) + return false; + return true; +} + +static void __azx_runtime_suspend(struct azx *chip) +{ + azx_stop_chip(chip); + azx_enter_link_reset(chip); + azx_clear_irq_pending(chip); + display_power(chip, false); +} + +static void __azx_runtime_resume(struct azx *chip) +{ + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip); + struct hda_codec *codec; + int status; + + display_power(chip, true); + if (hda->need_i915_power) + snd_hdac_i915_set_bclk(bus); + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + + azx_init_pci(chip); + hda_intel_init_chip(chip, true); + + if (status) { + list_for_each_codec(codec, &chip->bus) + if (status & (1 << codec->addr)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + } + + /* power down again for link-controlled chips */ + if (!hda->need_i915_power) + display_power(chip, false); +} + +#ifdef CONFIG_PM_SLEEP +static int azx_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip; + struct hdac_bus *bus; + + if (!azx_is_pm_ready(card)) return 0; + chip = card->private_data; bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - azx_clear_irq_pending(chip); - azx_stop_chip(chip); - azx_enter_link_reset(chip); + __azx_runtime_suspend(chip); if (bus->irq >= 0) { free_irq(bus->irq, chip); bus->irq = -1; @@ -966,9 +997,6 @@ static int azx_suspend(struct device *dev) if (chip->msi) pci_disable_msi(chip->pci); - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && hda->need_i915_power) - snd_hdac_display_power(bus, false); trace_azx_suspend(chip); return 0; @@ -976,41 +1004,19 @@ static int azx_suspend(struct device *dev) static int azx_resume(struct device *dev) { - struct pci_dev *pci = to_pci_dev(dev); struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - struct hdac_bus *bus; - if (!card) + if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - bus = azx_bus(chip); - if (chip->disabled || hda->init_failed || !chip->running) - return 0; - - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - snd_hdac_display_power(bus, true); - if (hda->need_i915_power) - snd_hdac_i915_set_bclk(bus); - } - if (chip->msi) - if (pci_enable_msi(pci) < 0) + if (pci_enable_msi(chip->pci) < 0) chip->msi = 0; if (azx_acquire_irq(chip, 1) < 0) return -EIO; - azx_init_pci(chip); - - hda_intel_init_chip(chip, true); - - /* power down again for link-controlled chips */ - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && - !hda->need_i915_power) - snd_hdac_display_power(bus, false); - + __azx_runtime_resume(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); trace_azx_resume(chip); @@ -1045,21 +1051,14 @@ static int azx_thaw_noirq(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int azx_runtime_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - if (!card) + if (!azx_is_pm_ready(card)) return 0; - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed) - return 0; - if (!azx_has_pm_runtime(chip)) return 0; @@ -1067,13 +1066,7 @@ static int azx_runtime_suspend(struct device *dev) azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK); - azx_stop_chip(chip); - azx_enter_link_reset(chip); - azx_clear_irq_pending(chip); - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && hda->need_i915_power) - snd_hdac_display_power(azx_bus(chip), false); - + __azx_runtime_suspend(chip); trace_azx_runtime_suspend(chip); return 0; } @@ -1082,51 +1075,18 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; - struct hda_intel *hda; - struct hdac_bus *bus; - struct hda_codec *codec; - int status; - if (!card) + if (!azx_is_pm_ready(card)) return 0; - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - bus = azx_bus(chip); - if (chip->disabled || hda->init_failed) - return 0; - if (!azx_has_pm_runtime(chip)) return 0; - - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - snd_hdac_display_power(bus, true); - if (hda->need_i915_power) - snd_hdac_i915_set_bclk(bus); - } - - /* Read STATESTS before controller reset */ - status = azx_readw(chip, STATESTS); - - azx_init_pci(chip); - hda_intel_init_chip(chip, true); - - if (status) { - list_for_each_codec(codec, &chip->bus) - if (status & (1 << codec->addr)) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); - } + __azx_runtime_resume(chip); /* disable controller Wake Up event*/ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK); - /* power down again for link-controlled chips */ - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && - !hda->need_i915_power) - snd_hdac_display_power(bus, false); - trace_azx_runtime_resume(chip); return 0; } @@ -1167,6 +1127,8 @@ static const struct dev_pm_ops azx_pm = { #define AZX_PM_OPS &azx_pm #else +#define azx_add_card_list(chip) /* NOP */ +#define azx_del_card_list(chip) /* NOP */ #define AZX_PM_OPS NULL #endif /* CONFIG_PM */ @@ -1374,11 +1336,8 @@ static int azx_free(struct azx *chip) #ifdef CONFIG_SND_HDA_PATCH_LOADER release_firmware(chip->fw); #endif + display_power(chip, false); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - if (hda->need_i915_power) - snd_hdac_display_power(bus, false); - } if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) snd_hdac_i915_exit(bus); kfree(hda); @@ -1935,8 +1894,7 @@ static int azx_first_init(struct azx *chip) /* initialize chip */ azx_init_pci(chip); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - snd_hdac_i915_set_bclk(bus); + snd_hdac_i915_set_bclk(bus); hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0); @@ -2078,7 +2036,6 @@ static const struct hda_controller_ops pci_hda_ops = { .disable_msi_reset_irq = disable_msi_reset_irq, .pcm_mmap_prepare = pcm_mmap_prepare, .position_check = azx_position_check, - .link_power = azx_intel_link_power, }; static int azx_probe(struct pci_dev *pci, @@ -2228,6 +2185,7 @@ static int azx_probe_continue(struct azx *chip) int dev = chip->dev_index; int err; + to_hda_bus(bus)->bus_probing = 1; hda->probe_continued = 1; /* bind with i915 if needed */ @@ -2245,10 +2203,13 @@ static int azx_probe_continue(struct azx *chip) goto out_free; } else { /* don't bother any longer */ - chip->driver_caps &= - ~(AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL); + chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT; } } + + /* HSW/BDW controllers need this power */ + if (CONTROLLER_IN_GPU(pci)) + hda->need_i915_power = 1; } /* Request display power well for the HDA controller or codec. For @@ -2256,18 +2217,7 @@ static int azx_probe_continue(struct azx *chip) * this power. For other platforms, like Baytrail/Braswell, only the * display codec needs the power and it can be released after probe. */ - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - /* HSW/BDW controllers need this power */ - if (CONTROLLER_IN_GPU(pci)) - hda->need_i915_power = 1; - - err = snd_hdac_display_power(bus, true); - if (err < 0) { - dev_err(chip->card->dev, - "Cannot turn on display power on i915\n"); - goto i915_power_fail; - } - } + display_power(chip, true); err = azx_first_init(chip); if (err < 0) @@ -2315,14 +2265,12 @@ static int azx_probe_continue(struct azx *chip) pm_runtime_put_autosuspend(&pci->dev); out_free: - if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - && !hda->need_i915_power) - snd_hdac_display_power(bus, false); - -i915_power_fail: + if (err < 0 || !hda->need_i915_power) + display_power(chip, false); if (err < 0) hda->init_failed = 1; complete_all(&hda->probe_wait); + to_hda_bus(bus)->bus_probing = 0; return err; } diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index c499727920e6..74b46952fc98 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -339,9 +339,15 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (jack->nid) { if (!jack->jack || jack->block_report) continue; - state = get_jack_plug_state(jack->pin_sense); - snd_jack_report(jack->jack, - state ? jack->type : 0); + state = jack->button_state; + if (get_jack_plug_state(jack->pin_sense)) + state |= jack->type; + snd_jack_report(jack->jack, state); + if (jack->button_state) { + snd_jack_report(jack->jack, + state & ~jack->button_state); + jack->button_state = 0; /* button released */ + } } } EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync); @@ -379,15 +385,19 @@ static void hda_free_jack_priv(struct snd_jack *jack) * @nid: pin NID to assign * @name: string name for the jack * @phantom_jack: flag to deal as a phantom jack + * @type: jack type bits to be reported, 0 for guessing from pincfg + * @keymap: optional jack / key mapping * * This assigns a jack-detection kctl to the given pin. The kcontrol * will have the given name and index. */ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, bool phantom_jack) + const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap) { struct hda_jack_tbl *jack; - int err, state, type; + const struct hda_jack_keymap *map; + int err, state, buttons; jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) @@ -395,16 +405,30 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, if (jack->jack) return 0; /* already created */ - type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->card, name, type, + if (!type) + type = get_input_jack_type(codec, nid); + + buttons = 0; + if (keymap) { + for (map = keymap; map->type; map++) + buttons |= map->type; + } + + err = snd_jack_new(codec->card, name, type | buttons, &jack->jack, true, phantom_jack); if (err < 0) return err; jack->phantom_jack = !!phantom_jack; jack->type = type; + jack->button_state = 0; jack->jack->private_data = jack; jack->jack->private_free = hda_free_jack_priv; + if (keymap) { + for (map = keymap; map->type; map++) + snd_jack_set_key(jack->jack, map->type, map->key); + } + state = snd_hda_jack_detect(codec, nid); snd_jack_report(jack->jack, state ? jack->type : 0); @@ -437,7 +461,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); - err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack); + err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; @@ -508,19 +532,25 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls); -static void call_jack_callback(struct hda_codec *codec, +static void call_jack_callback(struct hda_codec *codec, unsigned int res, struct hda_jack_tbl *jack) { struct hda_jack_callback *cb; - for (cb = jack->callback; cb; cb = cb->next) + for (cb = jack->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; cb->func(codec, cb); + } if (jack->gated_jack) { struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, jack->gated_jack); if (gated) { - for (cb = gated->callback; cb; cb = cb->next) + for (cb = gated->callback; cb; cb = cb->next) { + cb->jack = gated; + cb->unsol_res = res; cb->func(codec, cb); + } } } } @@ -540,7 +570,7 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) return; event->jack_dirty = 1; - call_jack_callback(codec, event); + call_jack_callback(codec, res, event); snd_hda_jack_report_sync(codec); } EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); @@ -566,7 +596,7 @@ void snd_hda_jack_poll_all(struct hda_codec *codec) if (old_sense == get_jack_plug_state(jack->pin_sense)) continue; changes = 1; - call_jack_callback(codec, jack); + call_jack_callback(codec, 0, jack); } if (changes) snd_hda_jack_report_sync(codec); diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index e9814c0168ea..1d713201c160 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -13,6 +13,7 @@ #define __SOUND_HDA_JACK_H #include <linux/err.h> +#include <sound/jack.h> struct auto_pin_cfg; struct hda_jack_tbl; @@ -24,6 +25,8 @@ struct hda_jack_callback { hda_nid_t nid; hda_jack_callback_fn func; unsigned int private_data; /* arbitrary data */ + unsigned int unsol_res; /* unsolicited event bits */ + struct hda_jack_tbl *jack; /* associated jack entry */ struct hda_jack_callback *next; }; @@ -40,9 +43,15 @@ struct hda_jack_tbl { hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gated_jack; /* gated is dependent on this jack */ int type; + int button_state; struct snd_jack *jack; }; +struct hda_jack_keymap { + enum snd_jack_types type; + int key; +}; + struct hda_jack_tbl * snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid); struct hda_jack_tbl * @@ -82,7 +91,8 @@ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, bool phantom_jack); + const char *name, bool phantom_jack, + int type, const struct hda_jack_keymap *keymap); int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index dd7d4242d6d2..97a176d817a0 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -31,6 +31,7 @@ #include <linux/of_device.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/string.h> #include <sound/core.h> #include <sound/initval.h> @@ -233,10 +234,12 @@ static int hda_tegra_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + struct hdac_bus *bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_stop_chip(chip); + synchronize_irq(bus->irq); azx_enter_link_reset(chip); hda_tegra_disable_clocks(hda); @@ -344,6 +347,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) int err; unsigned short gcap; int irq_id = platform_get_irq(pdev, 0); + const char *sname; + struct device_node *root; err = hda_tegra_init_chip(chip, pdev); if (err) @@ -401,8 +406,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) return -ENODEV; } + /* driver name */ strcpy(card->driver, "tegra-hda"); - strcpy(card->shortname, "tegra-hda"); + + root = of_find_node_by_path("/"); + sname = of_get_property(root, "compatible", NULL); + of_node_put(root); + if (!sname) { + dev_err(card->dev, + "failed to get compatible property from root node\n"); + return -ENODEV; + } + /* shortname for card */ + if (strlen(sname) > sizeof(card->shortname)) + dev_info(card->dev, "truncating shortname for card\n"); + strncpy(card->shortname, sname, sizeof(card->shortname)); + + /* longname for card */ snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, bus->addr, bus->irq); @@ -513,7 +533,7 @@ static void hda_tegra_probe_work(struct work_struct *work) goto out_free; /* create codec instances */ - err = azx_probe_codecs(chip, 0); + err = azx_probe_codecs(chip, 8); if (err < 0) goto out_free; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0a567634e5fa..29882bda7632 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1081,6 +1081,18 @@ enum { QUIRK_AE5, }; +#ifdef CONFIG_PCI +#define ca0132_quirk(spec) ((spec)->quirk) +#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio) +#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions) +#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls) +#else +#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; }) +#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; }) +#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; }) +#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; }) +#endif + static const struct hda_pintbl alienware_pincfgs[] = { { 0x0b, 0x90170110 }, /* Builtin Speaker */ { 0x0c, 0x411111f0 }, /* N/A */ @@ -3101,7 +3113,7 @@ static void dspload_post_setup(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; codec_dbg(codec, "---- dspload_post_setup ------\n"); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { /*set DSP speaker to 2.0 configuration*/ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); @@ -3333,7 +3345,7 @@ static void ca0132_gpio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); @@ -3344,6 +3356,8 @@ static void ca0132_gpio_init(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); break; + default: + break; } } @@ -3353,7 +3367,7 @@ static void ca0132_gpio_setup(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, 0x07); @@ -3372,6 +3386,8 @@ static void ca0132_gpio_setup(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x0C); break; + default: + break; } } @@ -4172,7 +4188,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) switch (spec->cur_out_type) { case SPEAKER_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); @@ -4203,10 +4219,12 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa4); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; case HEADPHONE_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, true); ca0113_mmio_gpio_set(codec, 4, true); @@ -4238,10 +4256,12 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa1); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; case SURROUND_OUT: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); @@ -4272,6 +4292,8 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) chipio_set_control_param(codec, 0x0d, 0xa4); chipio_write(codec, 0x18b03c, 0x00000012); break; + default: + break; } break; } @@ -4446,7 +4468,7 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) to_delayed_work(work), struct ca0132_spec, unsol_hp_work); struct hda_jack_tbl *jack; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(spec->codec); else ca0132_select_out(spec->codec); @@ -4530,14 +4552,14 @@ static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); if (spec->in_enum_val == REAR_LINE_IN) tmp = FLOAT_ZERO; else { - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) tmp = FLOAT_THREE; else tmp = FLOAT_ONE; @@ -4549,7 +4571,7 @@ static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) codec_dbg(codec, "%s: on.", __func__); chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_16_000); if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID]) @@ -4645,7 +4667,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->cur_mic_type) { case REAR_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, false); @@ -4669,14 +4691,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); dspio_set_uint_param(codec, 0x80, 0x00, tmp); chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000000C); @@ -4689,12 +4711,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000004C); break; + default: + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; case REAR_LINE_IN: ca0132_mic_boost_set(codec, 0); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, false); @@ -4705,28 +4729,32 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case QUIRK_AE5: ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); break; + default: + break; } chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: chipio_write(codec, 0x18B098, 0x00000000); chipio_write(codec, 0x18B09C, 0x00000000); break; + default: + break; } chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); break; case FRONT_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 0, true); @@ -4748,7 +4776,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); dspio_set_uint_param(codec, 0x80, 0x00, tmp); @@ -4756,7 +4784,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x000000CC); @@ -4765,6 +4793,8 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000004C); break; + default: + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4859,7 +4889,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) val = 0; /* If Voice Focus on SBZ, set to two channel. */ - if ((nid == VOICE_FOCUS) && (spec->use_pci_mmio) + if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4878,7 +4908,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) * For SBZ noise reduction, there's an extra command * to module ID 0x47. No clue why. */ - if ((nid == NOISE_REDUCTION) && (spec->use_pci_mmio) + if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4894,7 +4924,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) } /* If rear line in disable effects. */ - if (spec->use_alt_functions && + if (ca0132_use_alt_functions(spec) && spec->in_enum_val == REAR_LINE_IN) val = 0; } @@ -4924,7 +4954,7 @@ static int ca0132_pe_switch_set(struct hda_codec *codec) codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n", spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); i = OUT_EFFECT_START_NID - EFFECT_START_NID; @@ -4984,7 +5014,7 @@ static int ca0132_cvoice_switch_set(struct hda_codec *codec) /* set correct vipsource */ oldval = stop_mic1(codec); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ret |= ca0132_alt_set_vipsource(codec, 1); else ret |= ca0132_set_vipsource(codec, 1); @@ -5053,7 +5083,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; if (!auto_jack) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -5070,7 +5100,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, } if (nid == VNID_HP_ASEL) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -5784,7 +5814,7 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, /* mic boost */ if (nid == spec->input_pins[0]) { spec->cur_mic_boost = *valp; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { if (spec->in_enum_val != REAR_LINE_IN) changed = ca0132_mic_boost_set(codec, *valp); } else { @@ -6080,7 +6110,7 @@ static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, /* If using alt_controls, add FX: prefix. But, don't add FX: * prefix to OutFX or InFX enable controls. */ - if ((spec->use_alt_controls) && (nid <= IN_EFFECT_END_NID)) + if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); else sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); @@ -6357,7 +6387,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } /* Setup vmaster with surround slaves for desktop ca0132 devices */ - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT, spec->tlv); snd_hda_add_vmaster(codec, "Master Playback Volume", @@ -6377,7 +6407,7 @@ static int ca0132_build_controls(struct hda_codec *codec) num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { /* Desktop cards break if Echo Cancellation is used. */ - if (spec->use_pci_mmio) { + if (ca0132_use_pci_mmio(spec)) { if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + OUT_EFFECTS_COUNT)) continue; @@ -6394,7 +6424,7 @@ static int ca0132_build_controls(struct hda_codec *codec) * EQ presets, and Smart Volume presets. Also, change names to add FX * prefix, and change PlayEnhancement and CrystalVoice to match. */ - if (spec->use_alt_controls) { + if (ca0132_use_alt_controls(spec)) { err = ca0132_alt_add_svm_enum(codec); if (err < 0) return err; @@ -6448,7 +6478,7 @@ static int ca0132_build_controls(struct hda_codec *codec) * to select the new outputs and inputs, plus add the new mic boost * setting control. */ - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { err = ca0132_alt_add_output_enum(codec); if (err < 0) return err; @@ -6459,14 +6489,14 @@ static int ca0132_build_controls(struct hda_codec *codec) * ZxR only has microphone input, there is no front panel * header on the card, and aux-in is handled by the DBPro board. */ - if (spec->quirk != QUIRK_ZXR) { + if (ca0132_quirk(spec) != QUIRK_ZXR) { err = ca0132_alt_add_input_enum(codec); if (err < 0) return err; } } - if (spec->quirk == QUIRK_AE5) { + if (ca0132_quirk(spec) == QUIRK_AE5) { err = ae5_add_headphone_gain_enum(codec); if (err < 0) return err; @@ -6475,7 +6505,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } - if (spec->quirk == QUIRK_ZXR) { + if (ca0132_quirk(spec) == QUIRK_ZXR) { err = zxr_add_headphone_gain_switch(codec); if (err < 0) return err; @@ -6505,7 +6535,7 @@ static int ca0132_build_controls(struct hda_codec *codec) return err; } - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_add_chmap_ctls(codec); return 0; @@ -6583,7 +6613,7 @@ static int ca0132_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); if (!info) return -ENOMEM; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { info->own_chmap = true; info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = ca0132_alt_chmaps; @@ -6597,7 +6627,7 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; /* With the DSP enabled, desktops don't use this ADC. */ - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); if (!info) return -ENOMEM; @@ -6795,7 +6825,7 @@ static void ca0132_init_dmic(struct hda_codec *codec) * Bit 6: set to select Data2, clear for Data1 * Bit 7: set to enable DMic, clear for AMic */ - if (spec->quirk == QUIRK_ALIENWARE_M17XR4) + if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4) val = 0x33; else val = 0x23; @@ -6877,7 +6907,7 @@ static void ca0132_alt_init_analog_mics(struct hda_codec *codec) /* Mic 1 Setup */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->quirk == QUIRK_R3DI) { + if (ca0132_quirk(spec) == QUIRK_R3DI) { chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ONE; } else @@ -6887,7 +6917,7 @@ static void ca0132_alt_init_analog_mics(struct hda_codec *codec) /* Mic 2 setup (not present on desktop cards) */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x01, tmp); @@ -6949,7 +6979,7 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) chipio_set_stream_channels(codec, 0x0C, 6); chipio_set_stream_control(codec, 0x0C, 1); /* No clue what these control */ - if (spec->quirk == QUIRK_SBZ) { + if (ca0132_quirk(spec) == QUIRK_SBZ) { chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); @@ -6962,7 +6992,7 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); - } else if (spec->quirk == QUIRK_ZXR) { + } else if (ca0132_quirk(spec) == QUIRK_ZXR) { chipio_write_no_mutex(codec, 0x190038, 0x000140c2); chipio_write_no_mutex(codec, 0x19003c, 0x000141c3); chipio_write_no_mutex(codec, 0x190040, 0x000150c4); @@ -6992,7 +7022,7 @@ static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) * why this is, but multiple tests have confirmed it. */ for (i = 0; i < 2; i++) { - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: tmp = 0x00000003; @@ -7021,6 +7051,8 @@ static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) tmp = 0x00000000; dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); break; + default: + break; } msleep(100); } @@ -7043,7 +7075,7 @@ static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: chipio_write(codec, 0x18b098, 0x0000000c); chipio_write(codec, 0x18b09C, 0x0000000c); @@ -7052,6 +7084,8 @@ static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) chipio_write(codec, 0x18b098, 0x0000000c); chipio_write(codec, 0x18b09c, 0x0000004c); break; + default: + break; } } @@ -7273,7 +7307,7 @@ static void r3d_setup_defaults(struct hda_codec *codec) /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); /* Setup effect defaults */ @@ -7420,7 +7454,7 @@ static void ca0132_init_flags(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1); chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1); chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1); @@ -7453,7 +7487,7 @@ static void ca0132_init_params(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); chipio_set_conn_rate(codec, 0x0B, SR_48_000); chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0); @@ -7490,7 +7524,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) * can use the default firmware, but I'll leave the option in case * it needs it again. */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: case QUIRK_AE5: @@ -7564,7 +7598,7 @@ static void ca0132_download_dsp(struct hda_codec *codec) } /* For codecs using alt functions, this is already done earlier */ - if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions)) + if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec)) ca0132_set_dsp_msr(codec, true); } @@ -7601,7 +7635,7 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_in(codec); else ca0132_select_mic(codec); @@ -7616,7 +7650,7 @@ static void ca0132_init_unsol(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, ca0132_process_dsp_response); /* Front headphone jack detection */ - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_front_hp, hp_callback); } @@ -7706,7 +7740,7 @@ static void ca0132_init_chip(struct hda_codec *codec) mutex_init(&spec->chipio_mutex); spec->cur_out_type = SPEAKER_OUT; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->cur_mic_type = DIGITAL_MIC; else spec->cur_mic_type = REAR_MIC; @@ -7732,7 +7766,7 @@ static void ca0132_init_chip(struct hda_codec *codec) * Sets defaults for the effect slider controls, only for alternative * ca0132 codecs. Also sets x-bass crossover frequency to 80hz. */ - if (spec->use_alt_controls) { + if (ca0132_use_alt_controls(spec)) { spec->xbass_xover_freq = 8; for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) spec->fx_ctl_val[i] = effect_slider_defaults[i]; @@ -7747,7 +7781,7 @@ static void ca0132_init_chip(struct hda_codec *codec) * the daughter board. So, there is no input enum control, and we need * to make sure that spec->in_enum_val is set properly. */ - if (spec->quirk == QUIRK_ZXR) + if (ca0132_quirk(spec) == QUIRK_ZXR) spec->in_enum_val = REAR_MIC; #ifdef ENABLE_TUNING_CONTROLS @@ -8088,27 +8122,27 @@ static void ca0132_mmio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x400); else writel(0x00000000, spec->mem_base + 0x400); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x408); else writel(0x00000000, spec->mem_base + 0x408); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0x40c); else writel(0x00000000, spec->mem_base + 0x40C); - if (spec->quirk == QUIRK_ZXR) + if (ca0132_quirk(spec) == QUIRK_ZXR) writel(0x00880640, spec->mem_base + 0x01C); else writel(0x00880680, spec->mem_base + 0x01C); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000080, spec->mem_base + 0xC0C); else writel(0x00000083, spec->mem_base + 0xC0C); @@ -8116,7 +8150,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x00000030, spec->mem_base + 0xC00); writel(0x00000000, spec->mem_base + 0xC04); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000000, spec->mem_base + 0xC0C); else writel(0x00000003, spec->mem_base + 0xC0C); @@ -8125,7 +8159,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x00000003, spec->mem_base + 0xC0C); writel(0x00000003, spec->mem_base + 0xC0C); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) writel(0x00000001, spec->mem_base + 0xC08); else writel(0x000000C1, spec->mem_base + 0xC08); @@ -8136,7 +8170,7 @@ static void ca0132_mmio_init(struct hda_codec *codec) writel(0x000000C1, spec->mem_base + 0xC08); writel(0x00000080, spec->mem_base + 0xC04); - if (spec->quirk == QUIRK_AE5) { + if (ca0132_quirk(spec) == QUIRK_AE5) { writel(0x00000000, spec->mem_base + 0x42c); writel(0x00000000, spec->mem_base + 0x46c); writel(0x00000000, spec->mem_base + 0x4ac); @@ -8211,7 +8245,7 @@ static void ca0132_alt_init(struct hda_codec *codec) ca0132_alt_vol_setup(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: codec_dbg(codec, "SBZ alt_init"); ca0132_gpio_init(codec); @@ -8248,6 +8282,8 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); break; + default: + break; } } @@ -8274,7 +8310,7 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_reload = true; spec->dsp_state = DSP_DOWNLOAD_INIT; } else { - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_dsp_startup_check(codec); return 0; } @@ -8284,12 +8320,12 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - if (spec->use_pci_mmio) + if (ca0132_use_pci_mmio(spec)) ca0132_mmio_init(codec); snd_hda_power_up_pm(codec); - if (spec->quirk == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5) ae5_register_set(codec); ca0132_init_unsol(codec); @@ -8298,14 +8334,14 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->base_init_verbs); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_init(codec); ca0132_download_dsp(codec); ca0132_refresh_widget_caps(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_R3DI: case QUIRK_R3D: r3d_setup_defaults(codec); @@ -8334,7 +8370,7 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D); @@ -8342,11 +8378,11 @@ static int ca0132_init(struct hda_codec *codec) VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); } - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) ca0132_gpio_setup(codec); snd_hda_sequence_write(codec, spec->spec_init_verbs); - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { ca0132_alt_select_out(codec); ca0132_alt_select_in(codec); } else { @@ -8391,7 +8427,7 @@ static void ca0132_free(struct hda_codec *codec) cancel_delayed_work_sync(&spec->unsol_hp_work); snd_hda_power_up(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: sbz_exit_chip(codec); break; @@ -8407,14 +8443,18 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_R3DI: r3di_gpio_shutdown(codec); break; + default: + break; } snd_hda_sequence_write(codec, spec->base_exit_verbs); ca0132_exit_chip(codec); snd_hda_power_down(codec); +#ifdef CONFIG_PCI if (spec->mem_base) pci_iounmap(codec->bus->pci, spec->mem_base); +#endif kfree(spec->spec_init_verbs); kfree(codec->spec); } @@ -8461,12 +8501,12 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.dac_nids = spec->dacs; spec->multiout.num_dacs = 3; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->multiout.max_channels = 2; else spec->multiout.max_channels = 6; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_ALIENWARE: codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__); snd_hda_apply_pincfgs(codec, alienware_pincfgs); @@ -8491,9 +8531,11 @@ static void ca0132_config(struct hda_codec *codec) codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); snd_hda_apply_pincfgs(codec, ae5_pincfgs); break; + default: + break; } - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_ALIENWARE: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ @@ -8654,7 +8696,7 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) * Since desktop cards use pci_mmio, this can be used to determine * whether or not to use these verbs instead of a separate bool. */ - if (spec->use_pci_mmio) + if (ca0132_use_pci_mmio(spec)) spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), @@ -8729,11 +8771,10 @@ static int patch_ca0132(struct hda_codec *codec) spec->quirk = quirk->value; else spec->quirk = QUIRK_NONE; - - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_detect_quirk(codec); - if (spec->quirk == QUIRK_ZXR_DBPRO) + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) codec->patch_ops = dbpro_patch_ops; else codec->patch_ops = ca0132_patch_ops; @@ -8746,7 +8787,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->num_mixers = 1; /* Set which mixers each quirk uses. */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound Blaster Z"); @@ -8775,7 +8816,7 @@ static int patch_ca0132(struct hda_codec *codec) } /* Setup whether or not to use alt functions/controls/pci_mmio */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: case QUIRK_AE5: @@ -8796,6 +8837,7 @@ static int patch_ca0132(struct hda_codec *codec) break; } +#ifdef CONFIG_PCI if (spec->use_pci_mmio) { spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); if (spec->mem_base == NULL) { @@ -8803,6 +8845,7 @@ static int patch_ca0132(struct hda_codec *codec) spec->quirk = QUIRK_NONE; } } +#endif spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 950e02e71766..a4ee7656d9ee 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -923,6 +923,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), + SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), @@ -930,6 +932,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 67099cbb6be2..46f88dc7b7e8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2142,7 +2142,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) strncat(hdmi_str, " Phantom", sizeof(hdmi_str) - strlen(hdmi_str) - 1); ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, - phantom_jack); + phantom_jack, 0, NULL); if (ret < 0) return ret; jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); @@ -2616,11 +2616,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid) intel_haswell_enable_all_pins(codec, true); intel_haswell_fixup_enable_dp12(codec); - /* For Haswell/Broadwell, the controller is also in the power well and - * can cover the codec power request, and so need not set this flag. - */ - if (!is_haswell(codec) && !is_broadwell(codec)) - codec->core.link_power_control = 1; + codec->display_power_control = 1; codec->patch_ops.set_power_state = haswell_set_power_state; codec->depop_delay = 0; @@ -2656,7 +2652,7 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) /* For Valleyview/Cherryview, only the display codec is in the display * power well and can use link_power ops to request/release the power. */ - codec->core.link_power_control = 1; + codec->display_power_control = 1; codec->depop_delay = 0; codec->auto_runtime_pm = 1; @@ -3834,6 +3830,10 @@ HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 15021c839372..1ffa36e987b4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -117,6 +117,7 @@ struct alc_spec { int codec_variant; /* flag for other variants */ unsigned int has_alc5505_dsp:1; unsigned int no_depop_delay:1; + unsigned int done_hp_init:1; /* for PLL fix */ hda_nid_t pll_nid; @@ -514,6 +515,15 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } } +/* get a primary headphone pin if available */ +static hda_nid_t alc_get_hp_pin(struct alc_spec *spec) +{ + if (spec->gen.autocfg.hp_pins[0]) + return spec->gen.autocfg.hp_pins[0]; + if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) + return spec->gen.autocfg.line_out_pins[0]; + return 0; +} /* * Realtek SSID verification @@ -724,9 +734,7 @@ do_sku: * 15 : 1 --> enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!spec->gen.autocfg.hp_pins[0] && - !(spec->gen.autocfg.line_out_pins[0] && - spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) { + if (!alc_get_hp_pin(spec)) { hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ nid = ports[tmp]; @@ -1847,6 +1855,8 @@ enum { ALC887_FIXUP_BASS_CHMAP, ALC1220_FIXUP_GB_DUAL_CODECS, ALC1220_FIXUP_CLEVO_P950, + ALC1220_FIXUP_SYSTEM76_ORYP5, + ALC1220_FIXUP_SYSTEM76_ORYP5_PINS, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -2048,6 +2058,17 @@ static void alc1220_fixup_clevo_p950(struct hda_codec *codec, snd_hda_override_conn_list(codec, 0x1b, 1, conn1); } +static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +static void alc1220_fixup_system76_oryp5(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc1220_fixup_clevo_p950(codec, fix, action); + alc_fixup_headset_mode_no_hp_mic(codec, fix, action); +} + static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = HDA_FIXUP_PINS, @@ -2292,6 +2313,19 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_clevo_p950, }, + [ALC1220_FIXUP_SYSTEM76_ORYP5] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_system76_oryp5, + }, + [ALC1220_FIXUP_SYSTEM76_ORYP5_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + {} + }, + .chained = true, + .chain_id = ALC1220_FIXUP_SYSTEM76_ORYP5, + }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -2368,6 +2402,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x96e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_SYSTEM76_ORYP5_PINS), + SND_PCI_QUIRK(0x1558, 0x97e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_SYSTEM76_ORYP5_PINS), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), @@ -2958,7 +2994,7 @@ static void alc282_restore_default_value(struct hda_codec *codec) static void alc282_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; int coef78; @@ -2995,7 +3031,7 @@ static void alc282_init(struct hda_codec *codec) static void alc282_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; int coef78; @@ -3073,14 +3109,9 @@ static void alc283_restore_default_value(struct hda_codec *codec) static void alc283_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; - if (!spec->gen.autocfg.hp_outs) { - if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) - hp_pin = spec->gen.autocfg.line_out_pins[0]; - } - alc283_restore_default_value(codec); if (!hp_pin) @@ -3114,14 +3145,9 @@ static void alc283_init(struct hda_codec *codec) static void alc283_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; - if (!spec->gen.autocfg.hp_outs) { - if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) - hp_pin = spec->gen.autocfg.line_out_pins[0]; - } - if (!hp_pin) { alc269_shutup(codec); return; @@ -3155,7 +3181,7 @@ static void alc283_shutup(struct hda_codec *codec) static void alc256_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; if (!hp_pin) @@ -3191,7 +3217,7 @@ static void alc256_init(struct hda_codec *codec) static void alc256_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; if (!hp_pin) { @@ -3227,7 +3253,7 @@ static void alc256_shutup(struct hda_codec *codec) static void alc225_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp1_pin_sense, hp2_pin_sense; if (!hp_pin) @@ -3270,7 +3296,7 @@ static void alc225_init(struct hda_codec *codec) static void alc225_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp1_pin_sense, hp2_pin_sense; if (!hp_pin) { @@ -3314,7 +3340,7 @@ static void alc225_shutup(struct hda_codec *codec) static void alc_default_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; if (!hp_pin) @@ -3343,7 +3369,7 @@ static void alc_default_init(struct hda_codec *codec) static void alc_default_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); bool hp_pin_sense; if (!hp_pin) { @@ -3372,6 +3398,48 @@ static void alc_default_shutup(struct hda_codec *codec) snd_hda_shutup_pins(codec); } +static void alc294_hp_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + int i, val; + + if (!hp_pin) + return; + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(100); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ + alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ + + /* Wait for depop procedure finish */ + val = alc_read_coefex_idx(codec, 0x58, 0x01); + for (i = 0; i < 20 && val & 0x0080; i++) { + msleep(50); + val = alc_read_coefex_idx(codec, 0x58, 0x01); + } + /* Set HP depop to auto mode */ + alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); + msleep(50); +} + +static void alc294_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->done_hp_init) { + alc294_hp_init(codec); + spec->done_hp_init = true; + } + alc_default_init(codec); +} + static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, unsigned int val) { @@ -4102,6 +4170,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) case 0x10ec0295: case 0x10ec0289: case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); alc_process_coef_fw(codec, coef0225); break; case 0x10ec0867: @@ -4736,7 +4805,7 @@ static void alc_update_headset_mode(struct hda_codec *codec) struct alc_spec *spec = codec->spec; hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); int new_headset_mode; @@ -5015,7 +5084,7 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, static void alc_shutup_dell_xps13(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int hp_pin = spec->gen.autocfg.hp_pins[0]; + int hp_pin = alc_get_hp_pin(spec); /* Prevent pop noises when headphones are plugged in */ snd_hda_codec_write(codec, hp_pin, 0, @@ -5108,7 +5177,7 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PROBE) { int mic_pin = find_ext_mic_pin(codec); - int hp_pin = spec->gen.autocfg.hp_pins[0]; + int hp_pin = alc_get_hp_pin(spec); if (snd_BUG_ON(!mic_pin || !hp_pin)) return; @@ -5380,6 +5449,73 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, snd_hda_override_wcaps(codec, 0x03, 0); } +static const struct hda_jack_keymap alc_headset_btn_keymap[] = { + { SND_JACK_BTN_0, KEY_PLAYPAUSE }, + { SND_JACK_BTN_1, KEY_VOICECOMMAND }, + { SND_JACK_BTN_2, KEY_VOLUMEUP }, + { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, + {} +}; + +static void alc_headset_btn_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + int report = 0; + + if (jack->unsol_res & (7 << 13)) + report |= SND_JACK_BTN_0; + + if (jack->unsol_res & (1 << 16 | 3 << 8)) + report |= SND_JACK_BTN_1; + + /* Volume up key */ + if (jack->unsol_res & (7 << 23)) + report |= SND_JACK_BTN_2; + + /* Volume down key */ + if (jack->unsol_res & (7 << 10)) + report |= SND_JACK_BTN_3; + + jack->jack->button_state = report; +} + +static void alc_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x55, + alc_headset_btn_callback); + snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false, + SND_JACK_HEADSET, alc_headset_btn_keymap); + break; + case HDA_FIXUP_ACT_INIT: + switch (codec->core.vendor_id) { + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); + break; + case 0x10ec0236: + case 0x10ec0256: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + break; + } + break; + } +} + +static void alc_fixup_disable_mic_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -5390,9 +5526,6 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, hda_fixup_thinkpad_acpi(codec, fix, action); } -/* for dell wmi mic mute led */ -#include "dell_wmi_helper.c" - /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -5470,7 +5603,7 @@ enum { ALC292_FIXUP_TPT440_DOCK, ALC292_FIXUP_TPT440, ALC283_FIXUP_HEADSET_MIC, - ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, + ALC255_FIXUP_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, ALC280_FIXUP_HP_GPIO4, ALC286_FIXUP_HP_GPIO_LED, @@ -5492,6 +5625,7 @@ enum { ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, ALC255_FIXUP_DELL_SPK_NOISE, + ALC225_FIXUP_DISABLE_MIC_VREF, ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC280_FIXUP_HP_HEADSET_MIC, @@ -5515,6 +5649,7 @@ enum { ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, + ALC256_FIXUP_HUAWEI_MBXP_PINS, ALC295_FIXUP_HP_X360, ALC221_FIXUP_HP_HEADSET_MIC, ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, @@ -5523,6 +5658,9 @@ enum { ALC294_FIXUP_ASUS_MIC, ALC294_FIXUP_ASUS_HEADSET_MIC, ALC294_FIXUP_ASUS_SPK, + ALC225_FIXUP_HEADSET_JACK, + ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE, + ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, }; static const struct hda_fixup alc269_fixups[] = { @@ -5770,7 +5908,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { .type = HDA_FIXUP_FUNC, @@ -5794,6 +5932,24 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC256_FIXUP_HUAWEI_MBXP_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x90a60130}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x18, 0x411111f0}, + {0x19, 0x04a11040}, + {0x1a, 0x411111f0}, + {0x1b, 0x90170112}, + {0x1d, 0x40759a05}, + {0x1e, 0x411111f0}, + {0x21, 0x04211020}, + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, [ALC269_FIXUP_ASUS_X101_FUNC] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_x101_headset_mic, @@ -5996,7 +6152,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_alc255, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = { .type = HDA_FIXUP_FUNC, @@ -6031,9 +6187,9 @@ static const struct hda_fixup alc269_fixups[] = { { }, }, }, - [ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = { + [ALC255_FIXUP_MIC_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_wmi, + .v.func = snd_hda_gen_fixup_micmute_led, }, [ALC282_FIXUP_ASPIRE_V5_PINS] = { .type = HDA_FIXUP_PINS, @@ -6092,7 +6248,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_dell_alc288, .chained = true, - .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, @@ -6191,6 +6347,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE }, + [ALC225_FIXUP_DISABLE_MIC_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_mic_vref, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -6200,7 +6362,7 @@ static const struct hda_fixup alc269_fixups[] = { {} }, .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF }, [ALC280_FIXUP_HP_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, @@ -6424,7 +6586,7 @@ static const struct hda_fixup alc269_fixups[] = { [ALC294_FIXUP_ASUS_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1113c }, /* use as headset mic, without its own jack detect */ + { 0x19, 0x01a1103c }, /* use as headset mic */ { } }, .chained = true, @@ -6441,6 +6603,30 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC225_FIXUP_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + }, + [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Disable PCBEEP-IN passthrough */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6503,6 +6689,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -6573,6 +6760,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -6617,6 +6805,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x1325, "System76 Darter Pro (darp5)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), @@ -6680,6 +6869,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", ALC256_FIXUP_HUAWEI_MBXP_PINS), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ #if 0 @@ -6805,7 +6997,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, - {.id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, .name = "alc255-dell-mute"}, + {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"}, {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, @@ -6825,7 +7017,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, - {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, + {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"}, {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"}, {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"}, {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"}, @@ -6844,6 +7036,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, + {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-sense-combo"}, {} }; #define ALC225_STANDARD_PINS \ @@ -7119,7 +7312,7 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x19, 0x03a11020}, {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, + SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, {0x12, 0x90a60130}, {0x14, 0x90170110}, {0x19, 0x04a11040}, @@ -7271,37 +7464,6 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4, 0, 1<<11); } -static void alc294_hp_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - int i, val; - - if (!hp_pin) - return; - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(100); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ - alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ - - /* Wait for depop procedure finish */ - val = alc_read_coefex_idx(codec, 0x58, 0x01); - for (i = 0; i < 20 && val & 0x0080; i++) { - msleep(50); - val = alc_read_coefex_idx(codec, 0x58, 0x01); - } - /* Set HP depop to auto mode */ - alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); - msleep(50); -} - /* */ static int patch_alc269(struct hda_codec *codec) @@ -7427,7 +7589,7 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC294; spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ - alc294_hp_init(codec); + spec->init_hook = alc294_init; break; case 0x10ec0300: spec->codec_variant = ALC269_TYPE_ALC300; @@ -7439,7 +7601,7 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC700; spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */ - alc294_hp_init(codec); + spec->init_hook = alc294_init; break; } diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 568575b72f2f..4089feb8c68e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -3,12 +3,11 @@ * to be included from codec driver */ -#if IS_ENABLED(CONFIG_THINKPAD_ACPI) +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) && IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) #include <linux/acpi.h> -#include <linux/thinkpad_acpi.h> +#include <linux/leds.h> -static int (*led_set_func)(int, bool); static void (*old_vmaster_hook)(void *, int); static bool is_thinkpad(struct hda_codec *codec) @@ -23,50 +22,20 @@ static void update_tpacpi_mute_led(void *private_data, int enabled) if (old_vmaster_hook) old_vmaster_hook(private_data, enabled); - if (led_set_func) - led_set_func(TPACPI_LED_MUTE, !enabled); -} - -static void update_tpacpi_micmute(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - led_set_func(TPACPI_LED_MICMUTE, spec->micmute_led.led_value); + ledtrig_audio_set(LED_AUDIO_MUTE, enabled ? LED_OFF : LED_ON); } static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct hda_gen_spec *spec = codec->spec; - bool removefunc = false; if (action == HDA_FIXUP_ACT_PROBE) { if (!is_thinkpad(codec)) return; - if (!led_set_func) - led_set_func = symbol_request(tpacpi_led_set); - if (!led_set_func) { - codec_warn(codec, - "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); - return; - } - - removefunc = true; - if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { - old_vmaster_hook = spec->vmaster_mute.hook; - spec->vmaster_mute.hook = update_tpacpi_mute_led; - removefunc = false; - } - if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0 && - !snd_hda_gen_add_micmute_led(codec, - update_tpacpi_micmute)) - removefunc = false; - } - - if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { - symbol_put(tpacpi_led_set); - led_set_func = NULL; - old_vmaster_hook = NULL; + old_vmaster_hook = spec->vmaster_mute.hook; + spec->vmaster_mute.hook = update_tpacpi_mute_led; + snd_hda_gen_fixup_micmute_led(codec, fix, action); } } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1bff4b1b39cd..ba99ff0e93e0 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -30,6 +30,7 @@ #include <linux/math64.h> #include <linux/vmalloc.h> #include <linux/io.h> +#include <linux/nospec.h> #include <sound/core.h> #include <sound/control.h> @@ -4092,15 +4093,16 @@ static int snd_hdsp_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_channel_info *info) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); - int mapped_channel; + unsigned int channel = info->channel; - if (snd_BUG_ON(info->channel >= hdsp->max_channels)) + if (snd_BUG_ON(channel >= hdsp->max_channels)) return -EINVAL; + channel = array_index_nospec(channel, hdsp->max_channels); - if ((mapped_channel = hdsp->channel_map[info->channel]) < 0) + if (hdsp->channel_map[channel] < 0) return -EINVAL; - info->offset = mapped_channel * HDSP_CHANNEL_BUFFER_BYTES; + info->offset = hdsp->channel_map[channel] * HDSP_CHANNEL_BUFFER_BYTES; info->first = 0; info->step = 32; return 0; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 48dd44f8e914..d692e4070167 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -908,7 +908,7 @@ static void detect_byte_swap(struct snd_pmac *chip) /* if seems that Keylargo can't byte-swap */ for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0) { + if (of_node_name_eq(mio, "mac-io")) { if (of_device_is_compatible(mio, "Keylargo")) chip->can_byte_swap = 0; break; @@ -1313,7 +1313,7 @@ int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) } else if (chip->is_pbook_G3) { struct device_node* mio; for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0) { + if (of_node_name_eq(mio, "mac-io")) { struct resource r; if (of_address_to_resource(mio, 0, &r) == 0) chip->macio_base = diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 0779a2912237..6d7ffffcce95 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1365,8 +1365,8 @@ int snd_pmac_tumbler_init(struct snd_pmac *chip) mix->anded_reset = 0; mix->reset_on_sleep = 1; - for (np = chip->node->child; np; np = np->sibling) { - if (!strcmp(np->name, "sound")) { + for_each_child_of_node(chip->node, np) { + if (of_node_name_eq(np, "sound")) { if (of_get_property(np, "has-anded-reset", NULL)) mix->anded_reset = 1; if (of_get_property(np, "layout-id", NULL)) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index db709eeb019c..b19d7a3e7a2c 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1890,51 +1890,31 @@ static void hdmi_codec_remove(struct snd_soc_component *component) pm_runtime_disable(&hdev->dev); } -#ifdef CONFIG_PM -static int hdmi_codec_prepare(struct device *dev) -{ - struct hdac_device *hdev = dev_to_hdac_dev(dev); - - pm_runtime_get_sync(&hdev->dev); - - /* - * Power down afg. - * codec_read is preferred over codec_write to set the power state. - * This way verb is send to set the power state and response - * is received. So setting power state is ensured without using loop - * to read the state. - */ - snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - - return 0; -} - -static void hdmi_codec_complete(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int hdmi_codec_resume(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + int ret; - /* Power up afg */ - snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - - hdac_hdmi_skl_enable_all_pins(hdev); - hdac_hdmi_skl_enable_dp12(hdev); - + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; /* * As the ELD notify callback request is not entertained while the * device is in suspend state. Need to manually check detection of * all pins here. pin capablity change is not support, so use the * already set pin caps. + * + * NOTE: this is safe to call even if the codec doesn't actually resume. + * The pin check involves only with DRM audio component hooks, so it + * works even if the HD-audio side is still dreaming peacefully. */ hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); - - pm_runtime_put_sync(&hdev->dev); + return 0; } #else -#define hdmi_codec_prepare NULL -#define hdmi_codec_complete NULL +#define hdmi_codec_resume NULL #endif static const struct snd_soc_component_driver hdmi_hda_codec = { @@ -2074,13 +2054,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) * Turned off in the runtime_suspend during the first explicit * pm_runtime_suspend call. */ - ret = snd_hdac_display_power(hdev->bus, true); - if (ret < 0) { - dev_err(&hdev->dev, - "Cannot turn on display power on i915 err: %d\n", - ret); - return ret; - } + snd_hdac_display_power(hdev->bus, hdev->addr, true); ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); if (ret < 0) { @@ -2108,6 +2082,8 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) struct hdac_hdmi_port *port, *port_next; int i; + snd_hdac_display_power(hdev->bus, hdev->addr, false); + list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { pcm->cvt = NULL; if (list_empty(&pcm->port_list)) @@ -2139,81 +2115,11 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) } #ifdef CONFIG_PM -/* - * Power management sequences - * ========================== - * - * The following explains the PM handling of HDAC HDMI with its parent - * device SKL and display power usage - * - * Probe - * ----- - * In SKL probe, - * 1. skl_probe_work() powers up the display (refcount++ -> 1) - * 2. enumerates the codecs on the link - * 3. powers down the display (refcount-- -> 0) - * - * In HDAC HDMI probe, - * 1. hdac_hdmi_dev_probe() powers up the display (refcount++ -> 1) - * 2. probe the codec - * 3. put the HDAC HDMI device to runtime suspend - * 4. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) - * - * Once children are runtime suspended, SKL device also goes to runtime - * suspend - * - * HDMI Playback - * ------------- - * Open HDMI device, - * 1. skl_runtime_resume() invoked - * 2. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) - * - * Close HDMI device, - * 1. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) - * 2. skl_runtime_suspend() invoked - * - * S0/S3 Cycle with playback in progress - * ------------------------------------- - * When the device is opened for playback, the device is runtime active - * already and the display refcount is 1 as explained above. - * - * Entering to S3, - * 1. hdmi_codec_prepare() invoke the runtime resume of codec which just - * increments the PM runtime usage count of the codec since the device - * is in use already - * 2. skl_suspend() powers down the display (refcount-- -> 0) - * - * Wakeup from S3, - * 1. skl_resume() powers up the display (refcount++ -> 1) - * 2. hdmi_codec_complete() invokes the runtime suspend of codec which just - * decrements the PM runtime usage count of the codec since the device - * is in use already - * - * Once playback is stopped, the display refcount is set to 0 as explained - * above in the HDMI playback sequence. The PM handlings are designed in - * such way that to balance the refcount of display power when the codec - * device put to S3 while playback is going on. - * - * S0/S3 Cycle without playback in progress - * ---------------------------------------- - * Entering to S3, - * 1. hdmi_codec_prepare() invoke the runtime resume of codec - * 2. skl_runtime_resume() invoked - * 3. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) - * 4. skl_suspend() powers down the display (refcount-- -> 0) - * - * Wakeup from S3, - * 1. skl_resume() powers up the display (refcount++ -> 1) - * 2. hdmi_codec_complete() invokes the runtime suspend of codec - * 3. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) - * 4. skl_runtime_suspend() invoked - */ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink = NULL; - int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2239,11 +2145,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) snd_hdac_ext_bus_link_put(bus, hlink); - err = snd_hdac_display_power(bus, false); - if (err < 0) - dev_err(dev, "Cannot turn off display power on i915\n"); + snd_hdac_display_power(bus, hdev->addr, false); - return err; + return 0; } static int hdac_hdmi_runtime_resume(struct device *dev) @@ -2251,7 +2155,6 @@ static int hdac_hdmi_runtime_resume(struct device *dev) struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink = NULL; - int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -2267,11 +2170,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev) snd_hdac_ext_bus_link_get(bus, hlink); - err = snd_hdac_display_power(bus, true); - if (err < 0) { - dev_err(dev, "Cannot turn on display power on i915\n"); - return err; - } + snd_hdac_display_power(bus, hdev->addr, true); hdac_hdmi_skl_enable_all_pins(hdev); hdac_hdmi_skl_enable_dp12(hdev); @@ -2289,8 +2188,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev) static const struct dev_pm_ops hdac_hdmi_pm = { SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) - .prepare = hdmi_codec_prepare, - .complete = hdmi_codec_complete, + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) }; static const struct hda_device_id hdmi_list[] = { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5abd35ca4e41..4ed5b7e17d44 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -40,6 +40,9 @@ #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC) #include "../../../soc/codecs/hdac_hda.h" #endif +static int skl_pci_binding; +module_param_named(pci_binding, skl_pci_binding, int, 0444); +MODULE_PARM_DESC(pci_binding, "PCI binding (0=auto, 1=only legacy, 2=only asoc"); /* * initialize the PCI registers @@ -311,7 +314,7 @@ static int skl_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); - int ret = 0; + int ret; /* * Do not suspend if streams which are marked ignore suspend are @@ -333,14 +336,7 @@ static int skl_suspend(struct device *dev) skl->skl_sst->fw_loaded = false; } - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = snd_hdac_display_power(bus, false); - if (ret < 0) - dev_err(bus->dev, - "Cannot turn OFF display power on i915\n"); - } - - return ret; + return 0; } static int skl_resume(struct device *dev) @@ -351,16 +347,6 @@ static int skl_resume(struct device *dev) struct hdac_ext_link *hlink = NULL; int ret; - /* Turned OFF in HDMI codec driver after codec reconfiguration */ - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = snd_hdac_display_power(bus, true); - if (ret < 0) { - dev_err(bus->dev, - "Cannot turn on display power on i915\n"); - return ret; - } - } - /* * resume only when we are not in suspend active, otherwise need to * restore the device @@ -453,8 +439,10 @@ static int skl_free(struct hdac_bus *bus) snd_hdac_ext_bus_exit(bus); cancel_work_sync(&skl->probe_work); - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); snd_hdac_i915_exit(bus); + } return 0; } @@ -786,11 +774,9 @@ static int skl_i915_init(struct hdac_bus *bus) if (err < 0) return err; - err = snd_hdac_display_power(bus, true); - if (err < 0) - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); - return err; + return 0; } static void skl_probe_work(struct work_struct *work) @@ -823,7 +809,7 @@ static void skl_probe_work(struct work_struct *work) err = skl_platform_register(bus->dev); if (err < 0) { dev_err(bus->dev, "platform register failed: %d\n", err); - return; + goto out_err; } err = skl_machine_device_register(skl); @@ -838,14 +824,8 @@ static void skl_probe_work(struct work_struct *work) list_for_each_entry(hlink, &bus->hlink_list, list) snd_hdac_ext_bus_link_put(bus, hlink); - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - err = snd_hdac_display_power(bus, false); - if (err < 0) { - dev_err(bus->dev, "Cannot turn off display power on i915\n"); - skl_machine_device_unregister(skl); - return; - } - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); /* configure PM */ pm_runtime_put_noidle(bus->dev); @@ -856,7 +836,7 @@ static void skl_probe_work(struct work_struct *work) out_err: if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - err = snd_hdac_display_power(bus, false); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); } /* @@ -914,21 +894,6 @@ static int skl_first_init(struct hdac_bus *bus) unsigned short gcap; int cp_streams, pb_streams, start_idx; - /* - * detect DSP by checking class/subclass/prog-id information - * class=04 subclass 03 prog-if 00: no DSP, legacy driver needs to be used - * class=04 subclass 01 prog-if 00: DSP is present (and may be required e.g. for DMIC or SSP support) - * class=04 subclass 03 prog-if 80: either of DSP or legacy mode can be used - */ - if (pci->class == 0x040300) { - dev_err(bus->dev, "The DSP is not enabled on this platform, aborting probe\n"); - return -ENODEV; - } else if (pci->class != 0x040100 && pci->class != 0x040380) { - dev_err(bus->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, aborting probe\n", pci->class); - return -ENODEV; - } - dev_info(bus->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); - err = pci_request_regions(pci, "Skylake HD audio"); if (err < 0) return err; @@ -1002,6 +967,36 @@ static int skl_probe(struct pci_dev *pci, struct hdac_bus *bus = NULL; int err; + switch (skl_pci_binding) { + case SND_SKL_PCI_BIND_AUTO: + /* + * detect DSP by checking class/subclass/prog-id information + * class=04 subclass 03 prog-if 00: no DSP, use legacy driver + * class=04 subclass 01 prog-if 00: DSP is present + * (and may be required e.g. for DMIC or SSP support) + * class=04 subclass 03 prog-if 80: use DSP or legacy mode + */ + if (pci->class == 0x040300) { + dev_info(&pci->dev, "The DSP is not enabled on this platform, aborting probe\n"); + return -ENODEV; + } + if (pci->class != 0x040100 && pci->class != 0x040380) { + dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, aborting probe\n", pci->class); + return -ENODEV; + } + dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); + break; + case SND_SKL_PCI_BIND_LEGACY: + dev_info(&pci->dev, "Module parameter forced binding with HDaudio legacy, aborting probe\n"); + return -ENODEV; + case SND_SKL_PCI_BIND_ASOC: + dev_info(&pci->dev, "Module parameter forced binding with SKL driver, bypassed detection logic\n"); + break; + default: + dev_err(&pci->dev, "invalid value for skl_pci_binding module parameter, ignored\n"); + break; + } + /* we use ext core ops, so provide NULL for ops here */ err = skl_create(pci, NULL, &skl); if (err < 0) diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 079063d8038d..883678ee971c 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -2071,12 +2071,12 @@ static int cs4231_ebus_probe(struct platform_device *op) static int cs4231_probe(struct platform_device *op) { #ifdef EBUS_SUPPORT - if (!strcmp(op->dev.of_node->parent->name, "ebus")) + if (of_node_name_eq(op->dev.of_node->parent, "ebus")) return cs4231_ebus_probe(op); #endif #ifdef SBUS_SUPPORT - if (!strcmp(op->dev.of_node->parent->name, "sbus") || - !strcmp(op->dev.of_node->parent->name, "sbi")) + if (of_node_name_eq(op->dev.of_node->parent, "sbus") || + of_node_name_eq(op->dev.of_node->parent, "sbi")) return cs4231_sbus_probe(op); #endif return -ENODEV; diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index e557946718a9..d9fcae071b47 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -22,9 +22,9 @@ #include <sound/core.h> #include <sound/hwdep.h> #include <linux/uaccess.h> +#include <linux/nospec.h> #include "emux_voice.h" - #define TMP_CLIENT_ID 0x1001 /* @@ -66,13 +66,16 @@ snd_emux_hwdep_misc_mode(struct snd_emux *emu, void __user *arg) return -EFAULT; if (info.mode < 0 || info.mode >= EMUX_MD_END) return -EINVAL; + info.mode = array_index_nospec(info.mode, EMUX_MD_END); if (info.port < 0) { for (i = 0; i < emu->num_ports; i++) emu->portptrs[i]->ctrls[info.mode] = info.value; } else { - if (info.port < emu->num_ports) + if (info.port < emu->num_ports) { + info.port = array_index_nospec(info.port, emu->num_ports); emu->portptrs[info.port]->ctrls[info.mode] = info.value; + } } return 0; } diff --git a/sound/usb/card.c b/sound/usb/card.c index a105947eaf55..746a72e23cf9 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -246,7 +246,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) h1 = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, NULL, UAC_HEADER); - if (!h1) { + if (!h1 || h1->bLength < sizeof(*h1)) { dev_err(&dev->dev, "cannot find UAC_HEADER\n"); return -EINVAL; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c63c84b54969..e7d441d0e839 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -753,8 +753,9 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc) { int mu_channels; + void *c; - if (desc->bLength < 11) + if (desc->bLength < sizeof(*desc)) return -EINVAL; if (!desc->bNrInPins) return -EINVAL; @@ -763,6 +764,8 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, case UAC_VERSION_1: case UAC_VERSION_2: default: + if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1) + return 0; /* no bmControls -> skip */ mu_channels = uac_mixer_unit_bNrChannels(desc); break; case UAC_VERSION_3: @@ -772,7 +775,11 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, } if (!mu_channels) - return -EINVAL; + return 0; + + c = uac_mixer_unit_bmControls(desc, state->mixer->protocol); + if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength) + return 0; /* no bmControls -> skip */ return mu_channels; } @@ -944,7 +951,7 @@ static int check_input_term(struct mixer_build *state, int id, struct uac_mixer_unit_descriptor *d = p1; err = uac_mixer_unit_get_channels(state, d); - if (err < 0) + if (err <= 0) return err; term->channels = err; @@ -2068,11 +2075,15 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_input_terminal_descriptor *d_v2 = raw_desc; + if (d_v2->bLength < sizeof(*d_v2)) + return -EINVAL; control = UAC2_TE_CONNECTOR; term_id = d_v2->bTerminalID; bmctls = le16_to_cpu(d_v2->bmControls); } else if (state->mixer->protocol == UAC_VERSION_3) { struct uac3_input_terminal_descriptor *d_v3 = raw_desc; + if (d_v3->bLength < sizeof(*d_v3)) + return -EINVAL; control = UAC3_TE_INSERTION; term_id = d_v3->bTerminalID; bmctls = le32_to_cpu(d_v3->bmControls); @@ -2118,7 +2129,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, if (err < 0) continue; /* no bmControls field (e.g. Maya44) -> ignore */ - if (desc->bLength <= 10 + input_pins) + if (!num_outs) continue; err = check_input_term(state, desc->baSourceID[pin], &iterm); if (err < 0) @@ -2314,7 +2325,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, char *name) { struct uac_processing_unit_descriptor *desc = raw_desc; - int num_ins = desc->bNrInPins; + int num_ins; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; int i, err, nameid, type, len; @@ -2329,7 +2340,13 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, 0, NULL, default_value_info }; - if (desc->bLength < 13 || desc->bLength < 13 + num_ins || + if (desc->bLength < 13) { + usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); + return -EINVAL; + } + + num_ins = desc->bNrInPins; + if (desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) { usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); return -EINVAL; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 382847154227..db114f3977e0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -314,6 +314,9 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, return 0; } +/* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk + * applies. Returns 1 if a quirk was found. + */ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, struct usb_device *dev, struct usb_interface_descriptor *altsd, @@ -384,7 +387,7 @@ add_sync_ep: subs->data_endpoint->sync_master = subs->sync_endpoint; - return 0; + return 1; } static int set_sync_endpoint(struct snd_usb_substream *subs, @@ -423,6 +426,10 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (err < 0) return err; + /* endpoint set by quirk */ + if (err > 0) + return 0; + if (altsd->bNumEndpoints < 2) return 0; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 37fc0447c071..b345beb447bd 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3326,6 +3326,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, + { + .ifnum = -1 + }, } } }, @@ -3369,6 +3372,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, + { + .ifnum = -1 + }, } } }, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6623cafc94f2..7e65fe853ee3 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> +#include <linux/bits.h> #include <sound/control.h> #include <sound/core.h> @@ -668,15 +669,133 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev) } /* - * C-Media CM6206 is based on CM106 with two additional - * registers that are not documented in the data sheet. - * Values here are chosen based on sniffing USB traffic - * under Windows. + * CM6206 registers from the CM6206 datasheet rev 2.1 */ +#define CM6206_REG0_DMA_MASTER BIT(15) +#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12) +#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12) +/* Bit 4 thru 11 is the S/PDIF category code */ +#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4) +#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3) +#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2) +#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1) +#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0) + +#define CM6206_REG1_TEST_SEL_CLK BIT(14) +#define CM6206_REG1_PLLBIN_EN BIT(13) +#define CM6206_REG1_SOFT_MUTE_EN BIT(12) +#define CM6206_REG1_GPIO4_OUT BIT(11) +#define CM6206_REG1_GPIO4_OE BIT(10) +#define CM6206_REG1_GPIO3_OUT BIT(9) +#define CM6206_REG1_GPIO3_OE BIT(8) +#define CM6206_REG1_GPIO2_OUT BIT(7) +#define CM6206_REG1_GPIO2_OE BIT(6) +#define CM6206_REG1_GPIO1_OUT BIT(5) +#define CM6206_REG1_GPIO1_OE BIT(4) +#define CM6206_REG1_SPDIFO_INVALID BIT(3) +#define CM6206_REG1_SPDIF_LOOP_EN BIT(2) +#define CM6206_REG1_SPDIFO_DIS BIT(1) +#define CM6206_REG1_SPDIFI_MIX BIT(0) + +#define CM6206_REG2_DRIVER_ON BIT(15) +#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13) +#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13) +#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13) +#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13) +#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12) +#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11) +#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10) +#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9) +#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8) +#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7) +#define CM6206_REG2_MUTE_SUBWOOFER BIT(6) +#define CM6206_REG2_MUTE_CENTER BIT(5) +#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(3) +#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3) +#define CM6206_REG2_EN_BTL BIT(2) +#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0) +#define CM6206_REG2_MCUCLKSEL_3_MHZ (1) +#define CM6206_REG2_MCUCLKSEL_6_MHZ (2) +#define CM6206_REG2_MCUCLKSEL_12_MHZ (3) + +/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */ +#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11) +#define CM6206_REG3_VRAP25EN BIT(10) +#define CM6206_REG3_MSEL1 BIT(9) +#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7) +#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7) +#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7) +#define CM6206_REG3_PINSEL BIT(6) +#define CM6206_REG3_FOE BIT(5) +#define CM6206_REG3_ROE BIT(4) +#define CM6206_REG3_CBOE BIT(3) +#define CM6206_REG3_LOSE BIT(2) +#define CM6206_REG3_HPOE BIT(1) +#define CM6206_REG3_SPDIFI_CANREC BIT(0) + +#define CM6206_REG5_DA_RSTN BIT(13) +#define CM6206_REG5_AD_RSTN BIT(12) +#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12) +#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9) +#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9) +#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9) +#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9) +#define CM6206_REG5_CODECM BIT(8) +#define CM6206_REG5_EN_HPF BIT(7) +#define CM6206_REG5_T_SEL_DSDA4 BIT(6) +#define CM6206_REG5_T_SEL_DSDA3 BIT(5) +#define CM6206_REG5_T_SEL_DSDA2 BIT(4) +#define CM6206_REG5_T_SEL_DSDA1 BIT(3) +#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0 +#define CM6206_REG5_T_SEL_DSDAD_FRONT 4 +#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5 +#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6 +#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7 + static int snd_usb_cm6206_boot_quirk(struct usb_device *dev) { int err = 0, reg; - int val[] = {0x2004, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000}; + int val[] = { + /* + * Values here are chosen based on sniffing USB traffic + * under Windows. + * + * REG0: DAC is master, sample rate 48kHz, no copyright + */ + CM6206_REG0_SPDIFO_RATE_48K | + CM6206_REG0_SPDIFO_COPYRIGHT_NA, + /* + * REG1: PLL binary search enable, soft mute enable. + */ + CM6206_REG1_PLLBIN_EN | + CM6206_REG1_SOFT_MUTE_EN, + /* + * REG2: enable output drivers, + * select front channels to the headphone output, + * then mute the headphone channels, run the MCU + * at 1.5 MHz. + */ + CM6206_REG2_DRIVER_ON | + CM6206_REG2_HEADP_SEL_FRONT_CHANNELS | + CM6206_REG2_MUTE_HEADPHONE_RIGHT | + CM6206_REG2_MUTE_HEADPHONE_LEFT, + /* + * REG3: default flyspeed, set 2.5V mic bias + * enable all line out ports and enable SPDIF + */ + CM6206_REG3_FLYSPEED_DEFAULT | + CM6206_REG3_VRAP25EN | + CM6206_REG3_FOE | + CM6206_REG3_ROE | + CM6206_REG3_CBOE | + CM6206_REG3_LOSE | + CM6206_REG3_HPOE | + CM6206_REG3_SPDIFI_CANREC, + /* REG4 is just a bunch of GPIO lines */ + 0x0000, + /* REG5: de-assert AD/DA reset signals */ + CM6206_REG5_DA_RSTN | + CM6206_REG5_AD_RSTN }; for (reg = 0; reg < ARRAY_SIZE(val); reg++) { err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]); @@ -1373,6 +1492,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */ case USB_ID(0x152a, 0x85de): /* SMSL D1 DAC */ case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */ case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */ @@ -1447,6 +1567,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case 0x20b1: /* XMOS based devices */ case 0x152a: /* Thesycon devices */ case 0x25ce: /* Mytek devices */ + case 0x2ab6: /* T+A devices */ if (fp->dsd_raw) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 67cf849aa16b..d9e3de495c16 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -596,12 +596,8 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, csep = snd_usb_find_desc(alts->extra, alts->extralen, NULL, USB_DT_CS_ENDPOINT); if (!csep || csep->bLength < 7 || - csep->bDescriptorSubtype != UAC_EP_GENERAL) { - usb_audio_warn(chip, - "%u:%d : no or invalid class specific endpoint descriptor\n", - iface_no, altsd->bAlternateSetting); - return 0; - } + csep->bDescriptorSubtype != UAC_EP_GENERAL) + goto error; if (protocol == UAC_VERSION_1) { attributes = csep->bmAttributes; @@ -609,6 +605,8 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct uac2_iso_endpoint_descriptor *csep2 = (struct uac2_iso_endpoint_descriptor *) csep; + if (csep2->bLength < sizeof(*csep2)) + goto error; attributes = csep->bmAttributes & UAC_EP_CS_ATTR_FILL_MAX; /* emulate the endpoint attributes of a v1 device */ @@ -618,12 +616,20 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct uac3_iso_endpoint_descriptor *csep3 = (struct uac3_iso_endpoint_descriptor *) csep; + if (csep3->bLength < sizeof(*csep3)) + goto error; /* emulate the endpoint attributes of a v1 device */ if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH) attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; } return attributes; + + error: + usb_audio_warn(chip, + "%u:%d : no or invalid class specific endpoint descriptor\n", + iface_no, altsd->bAlternateSetting); + return 0; } /* find an input terminal descriptor (either UAC1 or UAC2) with the given @@ -631,13 +637,17 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, */ static void * snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, - int terminal_id) + int terminal_id, bool uac23) { struct uac2_input_terminal_descriptor *term = NULL; + size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) : + sizeof(struct uac_input_terminal_descriptor); while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, term, UAC_INPUT_TERMINAL))) { + if (term->bLength < minlen) + continue; if (term->bTerminalID == terminal_id) return term; } @@ -655,7 +665,8 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, term, UAC_OUTPUT_TERMINAL))) { - if (term->bTerminalID == terminal_id) + if (term->bLength >= sizeof(*term) && + term->bTerminalID == terminal_id) return term; } @@ -729,7 +740,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, format = le16_to_cpu(as->wFormatTag); /* remember the format value */ iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); + as->bTerminalLink, + false); if (iterm) { num_channels = iterm->bNrChannels; chconfig = le16_to_cpu(iterm->wChannelConfig); @@ -764,7 +776,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, * to extract the clock */ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); + as->bTerminalLink, + true); if (input_term) { clock = input_term->bCSourceID; if (!chconfig && (num_channels == input_term->bNrChannels)) @@ -998,7 +1011,8 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, * to extract the clock */ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); + as->bTerminalLink, + true); if (input_term) { clock = input_term->bCSourceID; goto found_clock; diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 83d76c345940..00c92eb854ce 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1648,7 +1648,7 @@ static int had_create_jack(struct snd_intelhad *ctx, * PM callbacks */ -static int hdmi_lpe_audio_runtime_suspend(struct device *dev) +static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); int port; @@ -1664,23 +1664,8 @@ static int hdmi_lpe_audio_runtime_suspend(struct device *dev) } } - return 0; -} - -static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) -{ - struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); - int err; + snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot); - err = hdmi_lpe_audio_runtime_suspend(dev); - if (!err) - snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot); - return err; -} - -static int hdmi_lpe_audio_runtime_resume(struct device *dev) -{ - pm_runtime_mark_last_busy(dev); return 0; } @@ -1688,8 +1673,10 @@ static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); - hdmi_lpe_audio_runtime_resume(dev); + pm_runtime_mark_last_busy(dev); + snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D0); + return 0; } @@ -1877,7 +1864,6 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev); - pm_runtime_set_active(&pdev->dev); dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); for_each_port(card_ctx, port) { @@ -1908,8 +1894,6 @@ static int hdmi_lpe_audio_remove(struct platform_device *pdev) static const struct dev_pm_ops hdmi_lpe_audio_pm = { SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) - SET_RUNTIME_PM_OPS(hdmi_lpe_audio_runtime_suspend, - hdmi_lpe_audio_runtime_resume, NULL) }; static struct platform_driver hdmi_lpe_audio_driver = { diff --git a/sound/xen/Kconfig b/sound/xen/Kconfig index 4f1fceea82d2..e4d7beb4df1c 100644 --- a/sound/xen/Kconfig +++ b/sound/xen/Kconfig @@ -5,6 +5,7 @@ config SND_XEN_FRONTEND depends on XEN select SND_PCM select XEN_XENBUS_FRONTEND + select XEN_FRONT_PGDIR_SHBUF help Choose this option if you want to enable a para-virtualized frontend sound driver for Xen guest OSes. diff --git a/sound/xen/Makefile b/sound/xen/Makefile index 1e6470ecc2f2..24031775b715 100644 --- a/sound/xen/Makefile +++ b/sound/xen/Makefile @@ -3,7 +3,6 @@ snd_xen_front-objs := xen_snd_front.o \ xen_snd_front_cfg.o \ xen_snd_front_evtchnl.o \ - xen_snd_front_shbuf.o \ xen_snd_front_alsa.o obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index b089b13b5160..a9e5c2cd7698 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -16,12 +16,12 @@ #include <xen/xen.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include <xen/interface/io/sndif.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" static struct xensnd_req * be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) @@ -82,7 +82,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, } int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz) @@ -99,7 +99,8 @@ int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, req->op.open.pcm_rate = rate; req->op.open.buffer_sz = buffer_sz; req->op.open.period_sz = period_sz; - req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); + req->op.open.gref_directory = + xen_front_pgdir_shbuf_get_dir_start(shbuf); mutex_unlock(&evtchnl->ring_io_lock); ret = be_stream_do_io(evtchnl); diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h index a2ea2463bcc5..05611f113b94 100644 --- a/sound/xen/xen_snd_front.h +++ b/sound/xen/xen_snd_front.h @@ -16,7 +16,7 @@ struct xen_snd_front_card_info; struct xen_snd_front_evtchnl; struct xen_snd_front_evtchnl_pair; -struct xen_snd_front_shbuf; +struct xen_front_pgdir_shbuf; struct xensnd_query_hw_param; struct xen_snd_front_info { @@ -35,7 +35,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, struct xensnd_query_hw_param *hw_param_resp); int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz); diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index 2cbd9679aca1..a7f413cb704d 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -15,17 +15,24 @@ #include <sound/pcm_params.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_cfg.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" struct xen_snd_front_pcm_stream_info { struct xen_snd_front_info *front_info; struct xen_snd_front_evtchnl_pair *evt_pair; - struct xen_snd_front_shbuf sh_buf; + + /* This is the shared buffer with its backing storage. */ + struct xen_front_pgdir_shbuf shbuf; + u8 *buffer; + size_t buffer_sz; + int num_pages; + struct page **pages; + int index; bool is_open; @@ -214,12 +221,20 @@ static void stream_clear(struct xen_snd_front_pcm_stream_info *stream) stream->out_frames = 0; atomic_set(&stream->hw_ptr, 0); xen_snd_front_evtchnl_pair_clear(stream->evt_pair); - xen_snd_front_shbuf_clear(&stream->sh_buf); + memset(&stream->shbuf, 0, sizeof(stream->shbuf)); + stream->buffer = NULL; + stream->buffer_sz = 0; + stream->pages = NULL; + stream->num_pages = 0; } static void stream_free(struct xen_snd_front_pcm_stream_info *stream) { - xen_snd_front_shbuf_free(&stream->sh_buf); + xen_front_pgdir_shbuf_unmap(&stream->shbuf); + xen_front_pgdir_shbuf_free(&stream->shbuf); + if (stream->buffer) + free_pages_exact(stream->buffer, stream->buffer_sz); + kfree(stream->pages); stream_clear(stream); } @@ -421,10 +436,34 @@ static int alsa_close(struct snd_pcm_substream *substream) return 0; } +static int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream, + size_t buffer_sz) +{ + int i; + + stream->buffer = alloc_pages_exact(stream->buffer_sz, GFP_KERNEL); + if (!stream->buffer) + return -ENOMEM; + + stream->buffer_sz = buffer_sz; + stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE); + stream->pages = kcalloc(stream->num_pages, sizeof(struct page *), + GFP_KERNEL); + if (!stream->pages) + return -ENOMEM; + + for (i = 0; i < stream->num_pages; i++) + stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE); + + return 0; +} + static int alsa_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct xen_snd_front_info *front_info = stream->front_info; + struct xen_front_pgdir_shbuf_cfg buf_cfg; int ret; /* @@ -432,19 +471,32 @@ static int alsa_hw_params(struct snd_pcm_substream *substream, * so free the previously allocated shared buffer if any. */ stream_free(stream); + ret = shbuf_setup_backstore(stream, params_buffer_bytes(params)); + if (ret < 0) + goto fail; - ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev, - &stream->sh_buf, - params_buffer_bytes(params)); - if (ret < 0) { - stream_free(stream); - dev_err(&stream->front_info->xb_dev->dev, - "Failed to allocate buffers for stream with index %d\n", - stream->index); - return ret; - } + memset(&buf_cfg, 0, sizeof(buf_cfg)); + buf_cfg.xb_dev = front_info->xb_dev; + buf_cfg.pgdir = &stream->shbuf; + buf_cfg.num_pages = stream->num_pages; + buf_cfg.pages = stream->pages; + + ret = xen_front_pgdir_shbuf_alloc(&buf_cfg); + if (ret < 0) + goto fail; + + ret = xen_front_pgdir_shbuf_map(&stream->shbuf); + if (ret < 0) + goto fail; return 0; + +fail: + stream_free(stream); + dev_err(&front_info->xb_dev->dev, + "Failed to allocate buffers for stream with index %d\n", + stream->index); + return ret; } static int alsa_hw_free(struct snd_pcm_substream *substream) @@ -476,7 +528,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream) sndif_format = ret; ret = xen_snd_front_stream_prepare(&stream->evt_pair->req, - &stream->sh_buf, + &stream->shbuf, sndif_format, runtime->channels, runtime->rate, @@ -556,10 +608,10 @@ static int alsa_pb_copy_user(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - if (copy_from_user(stream->sh_buf.buffer + pos, src, count)) + if (copy_from_user(stream->buffer + pos, src, count)) return -EFAULT; return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); @@ -571,10 +623,10 @@ static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memcpy(stream->sh_buf.buffer + pos, src, count); + memcpy(stream->buffer + pos, src, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } @@ -586,14 +638,14 @@ static int alsa_cap_copy_user(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ? + return copy_to_user(dst, stream->buffer + pos, count) ? -EFAULT : 0; } @@ -604,14 +656,14 @@ static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - memcpy(dst, stream->sh_buf.buffer + pos, count); + memcpy(dst, stream->buffer + pos, count); return 0; } @@ -622,10 +674,10 @@ static int alsa_pb_fill_silence(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memset(stream->sh_buf.buffer + pos, 0, count); + memset(stream->buffer + pos, 0, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } diff --git a/sound/xen/xen_snd_front_shbuf.c b/sound/xen/xen_snd_front_shbuf.c deleted file mode 100644 index 07ac176a41ba..000000000000 --- a/sound/xen/xen_snd_front_shbuf.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR MIT - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#include <linux/kernel.h> -#include <xen/xen.h> -#include <xen/xenbus.h> - -#include "xen_snd_front_shbuf.h" - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf) -{ - if (!buf->grefs) - return GRANT_INVALID_REF; - - return buf->grefs[0]; -} - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf) -{ - memset(buf, 0, sizeof(*buf)); -} - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf) -{ - int i; - - if (buf->grefs) { - for (i = 0; i < buf->num_grefs; i++) - if (buf->grefs[i] != GRANT_INVALID_REF) - gnttab_end_foreign_access(buf->grefs[i], - 0, 0UL); - kfree(buf->grefs); - } - kfree(buf->directory); - free_pages_exact(buf->buffer, buf->buffer_sz); - xen_snd_front_shbuf_clear(buf); -} - -/* - * number of grant references a page can hold with respect to the - * xensnd_page_directory header - */ -#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \ - offsetof(struct xensnd_page_directory, gref)) / \ - sizeof(grant_ref_t)) - -static void fill_page_dir(struct xen_snd_front_shbuf *buf, - int num_pages_dir) -{ - struct xensnd_page_directory *page_dir; - unsigned char *ptr; - int i, cur_gref, grefs_left, to_copy; - - ptr = buf->directory; - grefs_left = buf->num_grefs - num_pages_dir; - /* - * skip grant references at the beginning, they are for pages granted - * for the page directory itself - */ - cur_gref = num_pages_dir; - for (i = 0; i < num_pages_dir; i++) { - page_dir = (struct xensnd_page_directory *)ptr; - if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) { - to_copy = grefs_left; - page_dir->gref_dir_next_page = GRANT_INVALID_REF; - } else { - to_copy = XENSND_NUM_GREFS_PER_PAGE; - page_dir->gref_dir_next_page = buf->grefs[i + 1]; - } - - memcpy(&page_dir->gref, &buf->grefs[cur_gref], - to_copy * sizeof(grant_ref_t)); - - ptr += XEN_PAGE_SIZE; - grefs_left -= to_copy; - cur_gref += to_copy; - } -} - -static int grant_references(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - grant_ref_t priv_gref_head; - unsigned long frame; - int ret, i, j, cur_ref; - int otherend_id; - - ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head); - if (ret) - return ret; - - buf->num_grefs = num_grefs; - otherend_id = xb_dev->otherend_id; - j = 0; - - for (i = 0; i < num_pages_dir; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->directory + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - for (i = 0; i < num_pages_buffer; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->buffer + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - gnttab_free_grant_references(priv_gref_head); - fill_page_dir(buf, num_pages_dir); - return 0; - -fail: - gnttab_free_grant_references(priv_gref_head); - return ret; -} - -static int alloc_int_buffers(struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL); - if (!buf->grefs) - return -ENOMEM; - - buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL); - if (!buf->directory) - goto fail; - - buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE; - buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL); - if (!buf->buffer) - goto fail; - - return 0; - -fail: - kfree(buf->grefs); - buf->grefs = NULL; - kfree(buf->directory); - buf->directory = NULL; - return -ENOMEM; -} - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz) -{ - int num_pages_buffer, num_pages_dir, num_grefs; - int ret; - - xen_snd_front_shbuf_clear(buf); - - num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE); - /* number of pages the page directory consumes itself */ - num_pages_dir = DIV_ROUND_UP(num_pages_buffer, - XENSND_NUM_GREFS_PER_PAGE); - num_grefs = num_pages_buffer + num_pages_dir; - - ret = alloc_int_buffers(buf, num_pages_dir, - num_pages_buffer, num_grefs); - if (ret < 0) - return ret; - - ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer, - num_grefs); - if (ret < 0) - return ret; - - fill_page_dir(buf, num_pages_dir); - return 0; -} diff --git a/sound/xen/xen_snd_front_shbuf.h b/sound/xen/xen_snd_front_shbuf.h deleted file mode 100644 index d28e97c47b2c..000000000000 --- a/sound/xen/xen_snd_front_shbuf.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR MIT */ - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#ifndef __XEN_SND_FRONT_SHBUF_H -#define __XEN_SND_FRONT_SHBUF_H - -#include <xen/grant_table.h> - -#include "xen_snd_front_evtchnl.h" - -struct xen_snd_front_shbuf { - int num_grefs; - grant_ref_t *grefs; - u8 *directory; - u8 *buffer; - size_t buffer_sz; -}; - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf); - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz); - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf); - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf); - -#endif /* __XEN_SND_FRONT_SHBUF_H */ |