diff options
Diffstat (limited to 'sound')
46 files changed, 1504 insertions, 989 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/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/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_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/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/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_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..e784130ea4e0 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, @@ -2245,10 +2202,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 +2216,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,11 +2264,8 @@ 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); 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..83befd8d43e8 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> @@ -344,6 +345,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 +404,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 +531,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..e5bdbc245682 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,13 +8443,15 @@ 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); - if (spec->mem_base) + if (IS_ENABLED(CONFIG_PCI) && spec->mem_base) pci_iounmap(codec->bus->pci, spec->mem_base); kfree(spec->spec_init_verbs); kfree(codec->spec); @@ -8461,12 +8499,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 +8529,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 +8694,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 +8769,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 +8785,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 +8814,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 +8835,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 +8843,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..51cc6589443f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -923,6 +923,7 @@ 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, 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), 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..129ebd857892 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5380,6 +5380,66 @@ 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; + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -5390,9 +5450,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 +5527,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, @@ -5515,6 +5572,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 +5581,7 @@ enum { ALC294_FIXUP_ASUS_MIC, ALC294_FIXUP_ASUS_HEADSET_MIC, ALC294_FIXUP_ASUS_SPK, + ALC225_FIXUP_HEADSET_JACK, }; static const struct hda_fixup alc269_fixups[] = { @@ -5770,7 +5829,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 +5853,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 +6073,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 +6108,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 +6169,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, @@ -6441,6 +6518,10 @@ 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, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6680,6 +6761,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 +6889,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"}, @@ -6844,6 +6928,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 \ 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/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..3ab2949c1dfa 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2074,13 +2074,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 +2102,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)) @@ -2213,7 +2209,6 @@ 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 +2234,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 +2244,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 +2259,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); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5abd35ca4e41..29d9b0eb83ea 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -311,7 +311,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 +333,10 @@ 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"); - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); - return ret; + return 0; } static int skl_resume(struct device *dev) @@ -352,14 +348,8 @@ static int skl_resume(struct device *dev) 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; - } - } + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); /* * resume only when we are not in suspend active, otherwise need to @@ -786,11 +776,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) @@ -838,14 +826,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 +838,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); } /* 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/quirks.c b/sound/usb/quirks.c index 6623cafc94f2..96340f23f86d 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]); |