diff options
79 files changed, 1686 insertions, 596 deletions
diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index cd421856409e..2d2998faff62 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -3864,14 +3864,16 @@ corresponding destructor. And next, set suspend/resume callbacks to the pci_driver:: - static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume); + static DEFINE_SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume); static struct pci_driver driver = { .name = KBUILD_MODNAME, .id_table = snd_my_ids, .probe = snd_my_probe, .remove = snd_my_remove, - .driver.pm = &snd_my_pm_ops, + .driver = { + .pm = &snd_my_pm_ops, + }, }; Module Parameters diff --git a/MAINTAINERS b/MAINTAINERS index 8999497011a2..5f9b4fbcc32c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5014,6 +5014,7 @@ F: include/linux/mfd/cs42l43* F: include/sound/cs* F: sound/pci/hda/cirrus* F: sound/pci/hda/cs* +F: sound/pci/hda/hda_component* F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/soc/codecs/cs* @@ -20485,6 +20486,12 @@ F: include/uapi/sound/compress_* F: sound/core/compress_offload.c F: sound/soc/soc-compress.c +SOUND - CORE KUNIT TEST +M: Ivan Orlov <ivan.orlov0322@gmail.com> +L: linux-sound@vger.kernel.org +S: Supported +F: sound/core/sound_kunit.c + SOUND - DMAENGINE HELPERS M: Lars-Peter Clausen <lars@metafoo.de> S: Supported diff --git a/include/sound/ak4531_codec.h b/include/sound/ak4531_codec.h index 9a4429970d92..64402347d7a2 100644 --- a/include/sound/ak4531_codec.h +++ b/include/sound/ak4531_codec.h @@ -65,6 +65,9 @@ int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531, #ifdef CONFIG_PM void snd_ak4531_suspend(struct snd_ak4531 *ak4531); void snd_ak4531_resume(struct snd_ak4531 *ak4531); +#else +static inline void snd_ak4531_suspend(struct snd_ak4531 *ak4531) {} +static inline void snd_ak4531_resume(struct snd_ak4531 *ak4531) {} #endif #endif /* __SOUND_AK4531_CODEC_H */ diff --git a/include/sound/emux_synth.h b/include/sound/emux_synth.h index 1cc530434b97..3f7f365ed248 100644 --- a/include/sound/emux_synth.h +++ b/include/sound/emux_synth.h @@ -103,7 +103,7 @@ struct snd_emux { int ports[SNDRV_EMUX_MAX_PORTS]; /* The ports for this device */ struct snd_emux_port *portptrs[SNDRV_EMUX_MAX_PORTS]; int used; /* use counter */ - char *name; /* name of the device (internal) */ + const char *name; /* name of the device (internal) */ struct snd_rawmidi **vmidi; struct timer_list tlist; /* for pending note-offs */ int timer_active; diff --git a/include/sound/sb.h b/include/sound/sb.h index f540339fb0c7..24970f36c38a 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -290,6 +290,9 @@ int snd_sbmixer_new(struct snd_sb *chip); #ifdef CONFIG_PM void snd_sbmixer_suspend(struct snd_sb *chip); void snd_sbmixer_resume(struct snd_sb *chip); +#else +static inline void snd_sbmixer_suspend(struct snd_sb *chip) {} +static inline void snd_sbmixer_resume(struct snd_sb *chip) {} #endif /* sb8_init.c */ diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h index dfe49547a7b0..5f4100c2cf04 100644 --- a/include/uapi/linux/virtio_snd.h +++ b/include/uapi/linux/virtio_snd.h @@ -8,6 +8,14 @@ #include <linux/virtio_types.h> /******************************************************************************* + * FEATURE BITS + */ +enum { + /* device supports control elements */ + VIRTIO_SND_F_CTLS = 0 +}; + +/******************************************************************************* * CONFIGURATION SPACE */ struct virtio_snd_config { @@ -17,6 +25,8 @@ struct virtio_snd_config { __le32 streams; /* # of available channel maps */ __le32 chmaps; + /* # of available control elements */ + __le32 controls; }; enum { @@ -55,6 +65,15 @@ enum { /* channel map control request types */ VIRTIO_SND_R_CHMAP_INFO = 0x0200, + /* control element request types */ + VIRTIO_SND_R_CTL_INFO = 0x0300, + VIRTIO_SND_R_CTL_ENUM_ITEMS, + VIRTIO_SND_R_CTL_READ, + VIRTIO_SND_R_CTL_WRITE, + VIRTIO_SND_R_CTL_TLV_READ, + VIRTIO_SND_R_CTL_TLV_WRITE, + VIRTIO_SND_R_CTL_TLV_COMMAND, + /* jack event types */ VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, VIRTIO_SND_EVT_JACK_DISCONNECTED, @@ -63,6 +82,9 @@ enum { VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, VIRTIO_SND_EVT_PCM_XRUN, + /* control element event types */ + VIRTIO_SND_EVT_CTL_NOTIFY = 0x1200, + /* common status codes */ VIRTIO_SND_S_OK = 0x8000, VIRTIO_SND_S_BAD_MSG, @@ -331,4 +353,136 @@ struct virtio_snd_chmap_info { __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; }; +/******************************************************************************* + * CONTROL ELEMENTS MESSAGES + */ +struct virtio_snd_ctl_hdr { + /* VIRTIO_SND_R_CTL_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + __le32 control_id; +}; + +/* supported roles for control elements */ +enum { + VIRTIO_SND_CTL_ROLE_UNDEFINED = 0, + VIRTIO_SND_CTL_ROLE_VOLUME, + VIRTIO_SND_CTL_ROLE_MUTE, + VIRTIO_SND_CTL_ROLE_GAIN +}; + +/* supported value types for control elements */ +enum { + VIRTIO_SND_CTL_TYPE_BOOLEAN = 0, + VIRTIO_SND_CTL_TYPE_INTEGER, + VIRTIO_SND_CTL_TYPE_INTEGER64, + VIRTIO_SND_CTL_TYPE_ENUMERATED, + VIRTIO_SND_CTL_TYPE_BYTES, + VIRTIO_SND_CTL_TYPE_IEC958 +}; + +/* supported access rights for control elements */ +enum { + VIRTIO_SND_CTL_ACCESS_READ = 0, + VIRTIO_SND_CTL_ACCESS_WRITE, + VIRTIO_SND_CTL_ACCESS_VOLATILE, + VIRTIO_SND_CTL_ACCESS_INACTIVE, + VIRTIO_SND_CTL_ACCESS_TLV_READ, + VIRTIO_SND_CTL_ACCESS_TLV_WRITE, + VIRTIO_SND_CTL_ACCESS_TLV_COMMAND +}; + +struct virtio_snd_ctl_info { + /* common header */ + struct virtio_snd_info hdr; + /* element role (VIRTIO_SND_CTL_ROLE_XXX) */ + __le32 role; + /* element value type (VIRTIO_SND_CTL_TYPE_XXX) */ + __le32 type; + /* element access right bit map (1 << VIRTIO_SND_CTL_ACCESS_XXX) */ + __le32 access; + /* # of members in the element value */ + __le32 count; + /* index for an element with a non-unique name */ + __le32 index; + /* name identifier string for the element */ + __u8 name[44]; + /* additional information about the element's value */ + union { + /* VIRTIO_SND_CTL_TYPE_INTEGER */ + struct { + /* minimum supported value */ + __le32 min; + /* maximum supported value */ + __le32 max; + /* fixed step size for value (0 = variable size) */ + __le32 step; + } integer; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 */ + struct { + /* minimum supported value */ + __le64 min; + /* maximum supported value */ + __le64 max; + /* fixed step size for value (0 = variable size) */ + __le64 step; + } integer64; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED */ + struct { + /* # of options supported for value */ + __le32 items; + } enumerated; + } value; +}; + +struct virtio_snd_ctl_enum_item { + /* option name */ + __u8 item[64]; +}; + +struct virtio_snd_ctl_iec958 { + /* AES/IEC958 channel status bits */ + __u8 status[24]; + /* AES/IEC958 subcode bits */ + __u8 subcode[147]; + /* nothing */ + __u8 pad; + /* AES/IEC958 subframe bits */ + __u8 dig_subframe[4]; +}; + +struct virtio_snd_ctl_value { + union { + /* VIRTIO_SND_CTL_TYPE_BOOLEAN|INTEGER value */ + __le32 integer[128]; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 value */ + __le64 integer64[64]; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED value (option indexes) */ + __le32 enumerated[128]; + /* VIRTIO_SND_CTL_TYPE_BYTES value */ + __u8 bytes[512]; + /* VIRTIO_SND_CTL_TYPE_IEC958 value */ + struct virtio_snd_ctl_iec958 iec958; + } value; +}; + +/* supported event reason types */ +enum { + /* element's value has changed */ + VIRTIO_SND_CTL_EVT_MASK_VALUE = 0, + /* element's information has changed */ + VIRTIO_SND_CTL_EVT_MASK_INFO, + /* element's metadata has changed */ + VIRTIO_SND_CTL_EVT_MASK_TLV +}; + +struct virtio_snd_ctl_event { + /* VIRTIO_SND_EVT_CTL_NOTIFY */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + __le16 control_id; + /* event reason bit map (1 << VIRTIO_SND_CTL_EVT_MASK_XXX) */ + __le16 mask; +}; + #endif /* VIRTIO_SND_IF_H */ diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 0cd19a05db19..e68b4cb4df29 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1126,7 +1126,6 @@ static void aoa_fabric_layout_remove(struct soundbus_dev *sdev) sdev->pcmname = NULL; } -#ifdef CONFIG_PM_SLEEP static int aoa_fabric_layout_suspend(struct device *dev) { struct layout_dev *ldev = dev_get_drvdata(dev); @@ -1147,11 +1146,9 @@ static int aoa_fabric_layout_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, aoa_fabric_layout_suspend, aoa_fabric_layout_resume); -#endif - static struct soundbus_driver aoa_soundbus_driver = { .name = "snd_aoa_soundbus_drv", .owner = THIS_MODULE, @@ -1159,9 +1156,7 @@ static struct soundbus_driver aoa_soundbus_driver = { .remove = aoa_fabric_layout_remove, .driver = { .owner = THIS_MODULE, -#ifdef CONFIG_PM_SLEEP .pm = &aoa_fabric_layout_pm_ops, -#endif } }; diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 8f24a3eea16b..2a295f610594 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -127,7 +127,7 @@ static void soundbus_device_shutdown(struct device *dev) /* soundbus_dev_attrs is declared in sysfs.c */ ATTRIBUTE_GROUPS(soundbus_dev); -static struct bus_type soundbus_bus_type = { +static const struct bus_type soundbus_bus_type = { .name = "aoa-soundbus", .probe = soundbus_probe, .uevent = soundbus_uevent, diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 0817ad21af74..f64896564728 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -737,7 +737,6 @@ static const struct snd_pcm_ops aaci_capture_ops = { /* * Power Management. */ -#ifdef CONFIG_PM static int aaci_do_suspend(struct snd_card *card) { struct aaci *aaci = card->private_data; @@ -763,12 +762,7 @@ static int aaci_resume(struct device *dev) return card ? aaci_do_resume(card) : 0; } -static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); -#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops) -#else -#define AACI_DEV_PM_OPS NULL -#endif - +static DEFINE_SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); static const struct ac97_pcm ac97_defs[] = { [0] = { /* Front PCM */ @@ -1081,7 +1075,7 @@ MODULE_DEVICE_TABLE(amba, aaci_ids); static struct amba_driver aaci_driver = { .drv = { .name = DRIVER_NAME, - .pm = AACI_DEV_PM_OPS, + .pm = &aaci_dev_pm_ops, }, .probe = aaci_probe, .remove = aaci_remove, diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 2d83ad91f968..4c367e73b2c9 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -111,8 +111,6 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); } -#ifdef CONFIG_PM_SLEEP - static int pxa2xx_ac97_do_suspend(struct snd_card *card) { pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; @@ -164,8 +162,7 @@ static int pxa2xx_ac97_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = { .open = pxa2xx_ac97_pcm_open, @@ -277,9 +274,7 @@ static struct platform_driver pxa2xx_ac97_driver = { .remove_new = pxa2xx_ac97_remove, .driver = { .name = "pxa2xx-ac97", -#ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, -#endif }, }; diff --git a/sound/core/Kconfig b/sound/core/Kconfig index e41818e59a15..8077f481d84f 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -39,6 +39,23 @@ config SND_UMP_LEGACY_RAWMIDI legacy MIDI 1.0 byte streams is created for each UMP Endpoint. The device contains 16 substreams corresponding to UMP groups. +config SND_CORE_TEST + tristate "Sound core KUnit test" + depends on KUNIT + select SND_PCM + default KUNIT_ALL_TESTS + help + This options enables the sound core functions KUnit test. + + KUnit tests run during boot and output the results to the debug + log in TAP format (https://testanything.org/). Only useful for + kernel devs running KUnit test harness and are not for inclusion + into a production build. + + For more information on KUnit and unit tests in general, refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + config SND_COMPRESS_OFFLOAD tristate diff --git a/sound/core/Makefile b/sound/core/Makefile index f6526b337137..b8aa886198ab 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_UMP) += snd-ump.o +obj-$(CONFIG_SND_CORE_TEST) += sound_kunit.o + obj-$(CONFIG_SND_OSSEMUL) += oss/ obj-$(CONFIG_SND_SEQUENCER) += seq/ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index d0788126cbab..d9b338088d10 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -225,9 +225,11 @@ static const char * const snd_pcm_format_names[] = { */ const char *snd_pcm_format_name(snd_pcm_format_t format) { - if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) + unsigned int format_num = (__force unsigned int)format; + + if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num]) return "Unknown"; - return snd_pcm_format_names[(__force unsigned int)format]; + return snd_pcm_format_names[format_num]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index c14981daf943..0374bbf51cd4 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -71,7 +71,6 @@ config SND_SEQ_UMP among legacy and UMP clients. config SND_SEQ_UMP_CLIENT - tristate def_tristate SND_UMP endif # SND_SEQUENCER diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index 6c2c4fb9b753..f0e964b19af7 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -163,6 +163,6 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, /* misc. functions for proc interface */ -char *enabled_str(int bool); +char *enabled_str(bool b); #endif /* __SEQ_OSS_DEVICE_H */ diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 42d4e7535a82..76bf41c26acd 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -455,9 +455,9 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp) * misc. functions for proc interface */ char * -enabled_str(int bool) +enabled_str(bool b) { - return bool ? "enabled" : "disabled"; + return b ? "enabled" : "disabled"; } static const char * diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 18320a248aa7..78dcb0ea1558 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -113,6 +113,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i return 0; } +/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */ +static int __dump_midi(void *ptr, void *buf, int count) +{ + return dump_midi(ptr, buf, count); +} + static int event_process_midi(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop) { @@ -132,7 +138,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); return 0; } - snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_seq_dump_var_event(ev, __dump_midi, substream); snd_midi_event_reset_decode(msynth->parser); } else { if (msynth->parser == NULL) diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 1b9260108e48..1678737f11be 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -62,6 +62,13 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi, /* * decode input event and put to read buffer of each opened file */ + +/* callback for snd_seq_dump_var_event(), bridging to snd_rawmidi_receive() */ +static int dump_to_rawmidi(void *ptr, void *buf, int count) +{ + return snd_rawmidi_receive(ptr, buf, count); +} + static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, struct snd_seq_event *ev, bool atomic) @@ -80,7 +87,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) continue; - snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream); + snd_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream); snd_midi_event_reset_decode(vmidi->parser); } else { len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev); diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index 7f3fd8eb016f..654d620d0199 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -49,7 +49,7 @@ static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) sdrv->argsize == sdev->argsize; } -static struct bus_type snd_seq_bus_type = { +static const struct bus_type snd_seq_bus_type = { .name = "snd_seq", .match = snd_seq_bus_match, }; diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c new file mode 100644 index 000000000000..4212c4a20697 --- /dev/null +++ b/sound/core/sound_kunit.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sound core KUnit test + * Author: Ivan Orlov <ivan.orlov0322@gmail.com> + */ + +#include <kunit/test.h> +#include <sound/core.h> +#include <sound/pcm.h> + +#define SILENCE_BUFFER_MAX_FRAMES 260 +#define SILENCE_BUFFER_SIZE (sizeof(u64) * SILENCE_BUFFER_MAX_FRAMES) +#define SILENCE(...) { __VA_ARGS__ } +#define DEFINE_FORMAT(fmt, pbits, wd, endianness, signd, silence_arr) { \ + .format = SNDRV_PCM_FORMAT_##fmt, .physical_bits = pbits, \ + .width = wd, .le = endianness, .sd = signd, .silence = silence_arr, \ + .name = #fmt, \ +} + +#define WRONG_FORMAT (SNDRV_PCM_FORMAT_LAST + 1) + +#define VALID_NAME "ValidName" +#define NAME_W_SPEC_CHARS "In%v@1id name" +#define NAME_W_SPACE "Test name" +#define NAME_W_SPACE_REMOVED "Testname" + +#define TEST_FIRST_COMPONENT "Component1" +#define TEST_SECOND_COMPONENT "Component2" + +struct snd_format_test_data { + snd_pcm_format_t format; + int physical_bits; + int width; + int le; + int sd; + unsigned char silence[8]; + unsigned char *name; +}; + +struct avail_test_data { + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t hw_ptr; + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t expected_avail; +}; + +static struct snd_format_test_data valid_fmt[] = { + DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()), + DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)), + DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()), + DEFINE_FORMAT(S16_BE, 16, 16, 0, 1, SILENCE()), + DEFINE_FORMAT(U16_LE, 16, 16, 1, 0, SILENCE(0x00, 0x80)), + DEFINE_FORMAT(U16_BE, 16, 16, 0, 0, SILENCE(0x80, 0x00)), + DEFINE_FORMAT(S24_LE, 32, 24, 1, 1, SILENCE()), + DEFINE_FORMAT(S24_BE, 32, 24, 0, 1, SILENCE()), + DEFINE_FORMAT(U24_LE, 32, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)), + DEFINE_FORMAT(U24_BE, 32, 24, 0, 0, SILENCE(0x00, 0x80, 0x00, 0x00)), + DEFINE_FORMAT(S32_LE, 32, 32, 1, 1, SILENCE()), + DEFINE_FORMAT(S32_BE, 32, 32, 0, 1, SILENCE()), + DEFINE_FORMAT(U32_LE, 32, 32, 1, 0, SILENCE(0x00, 0x00, 0x00, 0x80)), + DEFINE_FORMAT(U32_BE, 32, 32, 0, 0, SILENCE(0x80, 0x00, 0x00, 0x00)), + DEFINE_FORMAT(FLOAT_LE, 32, 32, 1, -1, SILENCE()), + DEFINE_FORMAT(FLOAT_BE, 32, 32, 0, -1, SILENCE()), + DEFINE_FORMAT(FLOAT64_LE, 64, 64, 1, -1, SILENCE()), + DEFINE_FORMAT(FLOAT64_BE, 64, 64, 0, -1, SILENCE()), + DEFINE_FORMAT(IEC958_SUBFRAME_LE, 32, 32, 1, -1, SILENCE()), + DEFINE_FORMAT(IEC958_SUBFRAME_BE, 32, 32, 0, -1, SILENCE()), + DEFINE_FORMAT(MU_LAW, 8, 8, -1, -1, SILENCE(0x7f)), + DEFINE_FORMAT(A_LAW, 8, 8, -1, -1, SILENCE(0x55)), + DEFINE_FORMAT(IMA_ADPCM, 4, 4, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_24, 3, 3, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_40, 5, 5, -1, -1, SILENCE()), + DEFINE_FORMAT(DSD_U8, 8, 8, 1, 0, SILENCE(0x69)), + DEFINE_FORMAT(DSD_U16_LE, 16, 16, 1, 0, SILENCE(0x69, 0x69)), + DEFINE_FORMAT(DSD_U32_LE, 32, 32, 1, 0, SILENCE(0x69, 0x69, 0x69, 0x69)), + DEFINE_FORMAT(DSD_U16_BE, 16, 16, 0, 0, SILENCE(0x69, 0x69)), + DEFINE_FORMAT(DSD_U32_BE, 32, 32, 0, 0, SILENCE(0x69, 0x69, 0x69, 0x69)), + DEFINE_FORMAT(S20_LE, 32, 20, 1, 1, SILENCE()), + DEFINE_FORMAT(S20_BE, 32, 20, 0, 1, SILENCE()), + DEFINE_FORMAT(U20_LE, 32, 20, 1, 0, SILENCE(0x00, 0x00, 0x08, 0x00)), + DEFINE_FORMAT(U20_BE, 32, 20, 0, 0, SILENCE(0x00, 0x08, 0x00, 0x00)), + DEFINE_FORMAT(S24_3LE, 24, 24, 1, 1, SILENCE()), + DEFINE_FORMAT(S24_3BE, 24, 24, 0, 1, SILENCE()), + DEFINE_FORMAT(U24_3LE, 24, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)), + DEFINE_FORMAT(U24_3BE, 24, 24, 0, 0, SILENCE(0x80, 0x00, 0x00)), + DEFINE_FORMAT(S20_3LE, 24, 20, 1, 1, SILENCE()), + DEFINE_FORMAT(S20_3BE, 24, 20, 0, 1, SILENCE()), + DEFINE_FORMAT(U20_3LE, 24, 20, 1, 0, SILENCE(0x00, 0x00, 0x08)), + DEFINE_FORMAT(U20_3BE, 24, 20, 0, 0, SILENCE(0x08, 0x00, 0x00)), + DEFINE_FORMAT(S18_3LE, 24, 18, 1, 1, SILENCE()), + DEFINE_FORMAT(S18_3BE, 24, 18, 0, 1, SILENCE()), + DEFINE_FORMAT(U18_3LE, 24, 18, 1, 0, SILENCE(0x00, 0x00, 0x02)), + DEFINE_FORMAT(U18_3BE, 24, 18, 0, 0, SILENCE(0x02, 0x00, 0x00)), + DEFINE_FORMAT(G723_24_1B, 8, 3, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_40_1B, 8, 5, -1, -1, SILENCE()), +}; + +static void test_phys_format_size(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(valid_fmt[i].format), + valid_fmt[i].physical_bits); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(-1), -EINVAL); +} + +static void test_format_width(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(valid_fmt[i].format), + valid_fmt[i].width); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(-1), -EINVAL); +} + +static void test_format_signed(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_signed(valid_fmt[i].format), + valid_fmt[i].sd < 0 ? -EINVAL : valid_fmt[i].sd); + KUNIT_EXPECT_EQ(test, snd_pcm_format_unsigned(valid_fmt[i].format), + valid_fmt[i].sd < 0 ? -EINVAL : 1 - valid_fmt[i].sd); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(-1), -EINVAL); +} + +static void test_format_endianness(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(valid_fmt[i].format), + valid_fmt[i].le < 0 ? -EINVAL : valid_fmt[i].le); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(valid_fmt[i].format), + valid_fmt[i].le < 0 ? -EINVAL : 1 - valid_fmt[i].le); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(-1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(-1), -EINVAL); +} + +static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *data, + u8 *buffer, size_t samples_count) +{ + size_t sample_bytes = data->physical_bits >> 3; + u32 i; + + KUNIT_ASSERT_EQ(test, snd_pcm_format_set_silence(data->format, buffer, samples_count), 0); + for (i = 0; i < samples_count * sample_bytes; i++) + KUNIT_EXPECT_EQ(test, buffer[i], data->silence[i % sample_bytes]); +} + +static void test_format_fill_silence(struct kunit *test) +{ + u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES }; + u8 *buffer; + u32 i, j; + + buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(buf_samples); i++) { + for (j = 0; j < ARRAY_SIZE(valid_fmt); j++) + _test_fill_silence(test, &valid_fmt[j], buffer, buf_samples[i]); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(WRONG_FORMAT, buffer, 20), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_LAST, buffer, 0), 0); +} + +static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size) +{ + snd_pcm_uframes_t boundary = buffer_size; + + while (boundary * 2 <= 0x7fffffffUL - buffer_size) + boundary *= 2; + return boundary; +} + +static struct avail_test_data p_avail_data[] = { + /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */ + { 128, 1000, 1129, 1073741824UL - 1 }, + /* + * buf_size + hw_ptr - appl_ptr >= boundary => + * => avail = buf_size + hw_ptr - appl_ptr - boundary + */ + { 128, 1073741824UL, 10, 118 }, + /* standard case: avail = buf_size + hw_ptr - appl_ptr */ + { 128, 1000, 1001, 127 }, +}; + +static void test_playback_avail(struct kunit *test) +{ + struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); + u32 i; + + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); + r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) { + r->buffer_size = p_avail_data[i].buffer_size; + r->boundary = calculate_boundary(r->buffer_size); + r->status->hw_ptr = p_avail_data[i].hw_ptr; + r->control->appl_ptr = p_avail_data[i].appl_ptr; + KUNIT_EXPECT_EQ(test, snd_pcm_playback_avail(r), p_avail_data[i].expected_avail); + } +} + +static struct avail_test_data c_avail_data[] = { + /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */ + { 128, 1000, 1001, 1073741824UL - 1 }, + /* standard case: avail = hw_ptr - appl_ptr */ + { 128, 1001, 1000, 1 }, +}; + +static void test_capture_avail(struct kunit *test) +{ + struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); + u32 i; + + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); + r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) { + r->buffer_size = c_avail_data[i].buffer_size; + r->boundary = calculate_boundary(r->buffer_size); + r->status->hw_ptr = c_avail_data[i].hw_ptr; + r->control->appl_ptr = c_avail_data[i].appl_ptr; + KUNIT_EXPECT_EQ(test, snd_pcm_capture_avail(r), c_avail_data[i].expected_avail); + } +} + +static void test_card_set_id(struct kunit *test) +{ + struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + + snd_card_set_id(card, VALID_NAME); + KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME); + + /* clear the first id character so we can set it again */ + card->id[0] = '\0'; + snd_card_set_id(card, NAME_W_SPEC_CHARS); + KUNIT_EXPECT_STRNEQ(test, card->id, NAME_W_SPEC_CHARS); + + card->id[0] = '\0'; + snd_card_set_id(card, NAME_W_SPACE); + kunit_info(test, "%s", card->id); + KUNIT_EXPECT_STREQ(test, card->id, NAME_W_SPACE_REMOVED); +} + +static void test_pcm_format_name(struct kunit *test) +{ + u32 i; + const char *name; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + name = snd_pcm_format_name(valid_fmt[i].format); + KUNIT_ASSERT_NOT_NULL_MSG(test, name, "Don't have name for %s", valid_fmt[i].name); + KUNIT_EXPECT_STREQ(test, name, valid_fmt[i].name); + } + + KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT), "Unknown"); + KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(-1), "Unknown"); +} + +static void test_card_add_component(struct kunit *test) +{ + struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + + snd_component_add(card, TEST_FIRST_COMPONENT); + KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT); + + snd_component_add(card, TEST_SECOND_COMPONENT); + KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT " " TEST_SECOND_COMPONENT); +} + +static struct kunit_case sound_utils_cases[] = { + KUNIT_CASE(test_phys_format_size), + KUNIT_CASE(test_format_width), + KUNIT_CASE(test_format_endianness), + KUNIT_CASE(test_format_signed), + KUNIT_CASE(test_format_fill_silence), + KUNIT_CASE(test_playback_avail), + KUNIT_CASE(test_capture_avail), + KUNIT_CASE(test_card_set_id), + KUNIT_CASE(test_pcm_format_name), + KUNIT_CASE(test_card_add_component), + {}, +}; + +static struct kunit_suite sound_utils_suite = { + .name = "sound-core-test", + .test_cases = sound_utils_cases, +}; + +kunit_test_suite(sound_utils_suite); +MODULE_AUTHOR("Ivan Orlov"); +MODULE_LICENSE("GPL"); diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 1c65e0a3b13c..892c4e29c0a3 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1830,7 +1830,6 @@ static int loopback_probe(struct platform_device *devptr) return 0; } -#ifdef CONFIG_PM_SLEEP static int loopback_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); @@ -1847,11 +1846,7 @@ static int loopback_resume(struct device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); -#define LOOPBACK_PM_OPS &loopback_pm -#else -#define LOOPBACK_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); #define SND_LOOPBACK_DRIVER "snd_aloop" @@ -1859,7 +1854,7 @@ static struct platform_driver loopback_driver = { .probe = loopback_probe, .driver = { .name = SND_LOOPBACK_DRIVER, - .pm = LOOPBACK_PM_OPS, + .pm = &loopback_pm, }, }; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 4317677ba24a..52ff6ac3f743 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1098,7 +1098,6 @@ static int snd_dummy_probe(struct platform_device *devptr) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_dummy_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); @@ -1115,11 +1114,7 @@ static int snd_dummy_resume(struct device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume); -#define SND_DUMMY_PM_OPS &snd_dummy_pm -#else -#define SND_DUMMY_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume); #define SND_DUMMY_DRIVER "snd_dummy" @@ -1127,7 +1122,7 @@ static struct platform_driver snd_dummy_driver = { .probe = snd_dummy_probe, .driver = { .name = SND_DUMMY_DRIVER, - .pm = SND_DUMMY_PM_OPS, + .pm = &snd_dummy_pm, }, }; diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index c7be1c395bcb..7195cb49e00f 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -176,7 +176,6 @@ static void pcsp_stop_beep(struct snd_pcsp *chip) pcspkr_stop_sound(); } -#ifdef CONFIG_PM_SLEEP static int pcsp_suspend(struct device *dev) { struct snd_pcsp *chip = dev_get_drvdata(dev); @@ -184,11 +183,7 @@ static int pcsp_suspend(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL); -#define PCSP_PM_OPS &pcsp_pm -#else -#define PCSP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL); static void pcsp_shutdown(struct platform_device *dev) { @@ -199,7 +194,7 @@ static void pcsp_shutdown(struct platform_device *dev) static struct platform_driver pcsp_platform_driver = { .driver = { .name = "pcspkr", - .pm = PCSP_PM_OPS, + .pm = &pcsp_pm, }, .probe = pcsp_probe, .shutdown = pcsp_shutdown, diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 22b6c779682a..5973c25c2add 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -175,6 +175,8 @@ config SND_FIREWIRE_MOTU * 8pre * 828mk3 (FireWire only) * 828mk3 (Hybrid) + * 896mk3 (FireWire only) + * 896mk3 (Hybrid) * Ultralite mk3 (FireWire only) * Ultralite mk3 (Hybrid) * Traveler mk3 diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 7be17bca257f..c9f153f85ae6 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -773,10 +773,14 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, } else { unsigned int dbc_interval; - if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) - dbc_interval = s->ctx_data.tx.dbc_interval; - else - dbc_interval = *data_blocks; + if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) { + if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) + dbc_interval = s->ctx_data.tx.dbc_interval; + else + dbc_interval = *data_blocks; + } else { + dbc_interval = payload_length / sizeof(__be32); + } lost = dbc != ((*data_block_counter + dbc_interval) & 0xff); } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index b7ff44751ab9..a1ed2e80f91a 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -37,6 +37,9 @@ * the value of current SYT_INTERVAL; e.g. initial value is not zero. * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff. * For incoming packet, the value in SYT field of CIP is not handled. + * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with + * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets + * in CIP payload, instead of the number of accumulated data blocks. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -51,6 +54,7 @@ enum cip_flags { CIP_NO_HEADER = 0x100, CIP_UNALIGHED_DBC = 0x200, CIP_UNAWARE_SYT = 0x400, + CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800, }; /** diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index 8a0426920a76..7254fdfe046a 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -261,6 +261,7 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) if (motu->spec == &snd_motu_spec_828mk3_fw || motu->spec == &snd_motu_spec_828mk3_hybrid || + motu->spec == &snd_motu_spec_896mk3 || motu->spec == &snd_motu_spec_traveler_mk3 || motu->spec == &snd_motu_spec_track16) return detect_packet_formats_with_opt_ifaces(motu, data); @@ -288,6 +289,14 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. }; +const struct snd_motu_spec snd_motu_spec_896mk3 = { + .name = "896mk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_COMMAND_DSP, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {18, 14, 10}, +}; + const struct snd_motu_spec snd_motu_spec_traveler_mk3 = { .name = "TravelerMk3", .protocol_version = SND_MOTU_PROTOCOL_V3, diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index d73599eb7d5a..d14ab5dd5bea 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -168,10 +168,12 @@ static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3), SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid. + SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16), SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 3b1dc98a7be0..c66be0a89ccf 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -138,6 +138,7 @@ extern const struct snd_motu_spec snd_motu_spec_8pre; extern const struct snd_motu_spec snd_motu_spec_828mk3_fw; extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid; +extern const struct snd_motu_spec snd_motu_spec_896mk3; extern const struct snd_motu_spec snd_motu_spec_traveler_mk3; extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; extern const struct snd_motu_spec snd_motu_spec_audio_express; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index f4a702def397..00f7feb91f92 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -177,6 +177,8 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) flags |= CIP_JUMBO_PAYLOAD; if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS) flags |= CIP_WRONG_DBS; + if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS) + flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS; } else { conn = &oxfw->in_conn; c_dir = CMP_INPUT; @@ -486,26 +488,57 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, struct snd_oxfw_stream_formation *formation) { - u8 *format; - unsigned int len; int err; - len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; - format = kmalloc(len, GFP_KERNEL); - if (format == NULL) - return -ENOMEM; + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + u8 *format; + unsigned int len; - err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); - if (err < 0) - goto end; - if (len < 3) { - err = -EIO; - goto end; + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + format = kmalloc(len, GFP_KERNEL); + if (format == NULL) + return -ENOMEM; + + err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); + if (err >= 0) { + if (len < 3) + err = -EIO; + else + err = snd_oxfw_stream_parse_format(format, formation); + } + + kfree(format); + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the duplicated hard-coded format, instead. + unsigned int rate; + u8 *const *formats; + int i; + + err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0); + if (err < 0) + return err; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + formats = oxfw->rx_stream_formats; + else + formats = oxfw->tx_stream_formats; + + for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) { + if (!formats[i]) + continue; + + err = snd_oxfw_stream_parse_format(formats[i], formation); + if (err < 0) + continue; + + if (formation->rate == rate) + break; + } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EIO; } - err = snd_oxfw_stream_parse_format(format, formation); -end: - kfree(format); return err; } @@ -515,7 +548,7 @@ end: * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 */ -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation) { unsigned int i, e, channels, type; @@ -600,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, unsigned int i, eid; int err; - /* get format at current sampling rate */ - err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); - if (err < 0) { - dev_err(&oxfw->unit->device, - "fail to get current stream format for isoc %s plug %d:%d\n", - (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", - pid, err); - goto end; + // get format at current sampling rate. + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get current stream format for isoc %s plug %d:%d\n", + (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", + pid, err); + goto end; + } + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the hard-coded format, instead. + buf[0] = 0x90; + buf[1] = 0x40; + buf[2] = avc_stream_rate_table[0]; + buf[3] = 0x00; + buf[4] = 0x01; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[5] = 0x08; + else + buf[5] = 0x02; + + buf[6] = 0x06; + + *len = 7; } /* parse and set stream format */ diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 241a697ce26b..98ae0e8cba87 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -21,6 +21,7 @@ #define VENDOR_TASCAM 0x00022e #define OUI_STANTON 0x001260 #define OUI_APOGEE 0x0003db +#define OUI_OXFORD 0x0030e0 #define MODEL_SATELLITE 0x00200f #define MODEL_SCS1M 0x001000 @@ -232,6 +233,11 @@ static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent if (err < 0) goto error; + if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) { + oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED | + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS; + } + err = snd_oxfw_stream_discover(oxfw); if (err < 0) goto error; @@ -330,6 +336,9 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave), OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers), + // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the + // default value of ASIC. + OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL), // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all. OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL), // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff. @@ -337,7 +346,6 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is // installed to avoid the postpone, the value of SYT field is always 0xffff. OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL), - // Miglia HarmonyAudio. Not yet identified. // // OXFW971 devices: diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index d728e451a25c..39ea9a6dde33 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -52,6 +52,11 @@ enum snd_oxfw_quirk { // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO // are ignored, thus driver should transfer packets with timestamp. SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20, + // Miglia Harmony Audio does not support AV/C Stream Format Information command. + SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40, + // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number + // of accumulated payload quadlets including the packet. + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80, }; /* This is an arbitrary number for convinience. */ @@ -136,7 +141,7 @@ struct snd_oxfw_stream_formation { unsigned int pcm; unsigned int midi; }; -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation); int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 610ea7a33cd8..b53de020309f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -567,7 +567,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) return 0; error: - dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", + dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", azx_dev->bufsize, period_bytes); return -EINVAL; } diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index b57d72ea4503..5f60658c6051 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -23,6 +23,10 @@ static int ctrl_link_mask; module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); +static ulong ctrl_addr = 0x40000000; +module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444); +MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR"); + static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx) { struct fwnode_handle *link; @@ -141,6 +145,9 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE) return AE_OK; /* keep going */ + if (adr != ctrl_addr) + return AE_OK; /* keep going */ + /* found the correct SoundWire controller */ info->handle = handle; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 2378a39abaeb..31e51e2df655 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -243,9 +243,7 @@ struct snd_ali { spinlock_t reg_lock; spinlock_t voice_alloc; -#ifdef CONFIG_PM_SLEEP - struct snd_ali_image *image; -#endif + struct snd_ali_image image; }; static const struct pci_device_id snd_ali_ids[] = { @@ -1824,18 +1822,13 @@ static int snd_ali_mixer(struct snd_ali *codec) return 0; } -#ifdef CONFIG_PM_SLEEP static int ali_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < chip->num_of_codecs; i++) snd_ac97_suspend(chip->ac97[i]); @@ -1872,13 +1865,9 @@ static int ali_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - spin_lock_irq(&chip->reg_lock); for (i = 0; i < ALI_CHANNELS; i++) { @@ -1908,11 +1897,7 @@ static int ali_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); -#define ALI_PM_OPS &ali_pm -#else -#define ALI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); static void snd_ali_free(struct snd_card *card) { @@ -2112,13 +2097,6 @@ static int snd_ali_create(struct snd_card *card, return err; } -#ifdef CONFIG_PM_SLEEP - codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image), - GFP_KERNEL); - if (!codec->image) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif - snd_ali_enable_address_interrupt(codec); codec->hw_initialized = 1; return 0; @@ -2181,7 +2159,7 @@ static struct pci_driver ali5451_driver = { .id_table = snd_ali_ids, .probe = snd_ali_probe, .driver = { - .pm = ALI_PM_OPS, + .pm = &ali_pm, }, }; diff --git a/sound/pci/als300.c b/sound/pci/als300.c index c70aff060120..c7c481203ef8 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -654,7 +654,6 @@ static int snd_als300_create(struct snd_card *card, return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_als300_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -677,11 +676,7 @@ static int snd_als300_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); -#define SND_ALS300_PM_OPS &snd_als300_pm -#else -#define SND_ALS300_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); static int snd_als300_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) @@ -739,7 +734,7 @@ static struct pci_driver als300_driver = { .id_table = snd_als300_ids, .probe = snd_als300_probe, .driver = { - .pm = SND_ALS300_PM_OPS, + .pm = &snd_als300_pm, }, }; diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index f33aeb692a11..022473594c73 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -936,7 +936,6 @@ static int snd_card_als4000_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static int snd_als4000_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -968,18 +967,14 @@ static int snd_als4000_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); -#define SND_ALS4000_PM_OPS &snd_als4000_pm -#else -#define SND_ALS4000_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); static struct pci_driver als4000_driver = { .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .driver = { - .pm = SND_ALS4000_PM_OPS, + .pm = &snd_als4000_pm, }, }; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 43d01f1847ed..df2fef726d60 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -520,7 +520,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -530,7 +529,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1454,7 +1452,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock, } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1499,12 +1496,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1634,7 +1626,7 @@ static struct pci_driver atiixp_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8864c4c3c7e1..eb569539f322 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -496,7 +496,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp_modem *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -506,7 +505,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1094,7 +1092,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1128,11 +1125,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1258,7 +1251,7 @@ static struct pci_driver atiixp_modem_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h index b5c5a71c0ac3..3a3de56b9b07 100644 --- a/sound/pci/aw2/aw2-saa7146.h +++ b/sound/pci/aw2/aw2-saa7146.h @@ -19,11 +19,12 @@ #define NUM_STREAM_CAPTURE_ANA 0 -typedef void (*snd_aw2_saa7146_it_cb) (void *); +struct snd_pcm_substream; +typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *); struct snd_aw2_saa7146_cb_param { snd_aw2_saa7146_it_cb p_it_callback; - void *p_callback_param; + struct snd_pcm_substream *p_callback_param; }; /* definition of the chip-specific record */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 431f0026b507..84989c291cd7 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -295,7 +295,6 @@ struct snd_azf3328 { * CONFIG_PM register storage below, but that's slightly difficult. */ u16 shadow_reg_ctrl_6AH; -#ifdef CONFIG_PM_SLEEP /* register value containers for power management * Note: not always full I/O range preserved (similar to Win driver!) */ u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4]; @@ -303,7 +302,6 @@ struct snd_azf3328 { u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4]; u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4]; u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4]; -#endif }; static const struct pci_device_id snd_azf3328_ids[] = { @@ -2517,7 +2515,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static inline void snd_azf3328_suspend_regs(const struct snd_azf3328 *chip, unsigned long io_addr, unsigned count, u32 *saved_regs) @@ -2633,18 +2630,14 @@ snd_azf3328_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); -#define SND_AZF3328_PM_OPS &snd_azf3328_pm -#else -#define SND_AZF3328_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); static struct pci_driver azf3328_driver = { .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .driver = { - .pm = SND_AZF3328_PM_OPS, + .pm = &snd_azf3328_pm, }, }; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 08e34b184780..36014501f7ed 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -486,10 +486,8 @@ struct cmipci { spinlock_t reg_lock; -#ifdef CONFIG_PM_SLEEP unsigned int saved_regs[0x20]; unsigned char saved_mixers[0x20]; -#endif }; @@ -3260,7 +3258,6 @@ static int snd_cmipci_probe(struct pci_dev *pci, return err; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -3324,18 +3321,14 @@ static int snd_cmipci_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); -#define SND_CMIPCI_PM_OPS &snd_cmipci_pm -#else -#define SND_CMIPCI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); static struct pci_driver cmipci_driver = { .name = KBUILD_MODNAME, .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .driver = { - .pm = SND_CMIPCI_PM_OPS, + .pm = &snd_cmipci_pm, }, }; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 0c9cadf7b3b8..0cc86e73cc62 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -470,10 +470,7 @@ struct cs4281 { struct gameport *gameport; -#ifdef CONFIG_PM_SLEEP u32 suspend_regs[SUSPEND_REGISTERS]; -#endif - }; static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); @@ -1897,8 +1894,6 @@ static int snd_cs4281_probe(struct pci_dev *pci, /* * Power Management */ -#ifdef CONFIG_PM_SLEEP - static const int saved_regs[SUSPEND_REGISTERS] = { BA0_JSCTL, BA0_GPIOR, @@ -1987,18 +1982,14 @@ static int cs4281_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); -#define CS4281_PM_OPS &cs4281_pm -#else -#define CS4281_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); static struct pci_driver cs4281_driver = { .name = KBUILD_MODNAME, .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .driver = { - .pm = CS4281_PM_OPS, + .pm = &cs4281_pm, }, }; diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index d074727c3e21..397900929aa6 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -292,7 +292,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) return 0; } -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr) +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr) { int err; struct amixer_mgr *amixer_mgr; @@ -321,8 +321,9 @@ error: return err; } -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) +int amixer_mgr_destroy(void *ptr) { + struct amixer_mgr *amixer_mgr = ptr; rsc_mgr_uninit(&amixer_mgr->mgr); kfree(amixer_mgr); return 0; @@ -446,7 +447,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) return 0; } -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr) +int sum_mgr_create(struct hw *hw, void **rsum_mgr) { int err; struct sum_mgr *sum_mgr; @@ -475,8 +476,9 @@ error: return err; } -int sum_mgr_destroy(struct sum_mgr *sum_mgr) +int sum_mgr_destroy(void *ptr) { + struct sum_mgr *sum_mgr = ptr; rsc_mgr_uninit(&sum_mgr->mgr); kfree(sum_mgr); return 0; diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h index 4498e6139d0e..8fc017da6bda 100644 --- a/sound/pci/ctxfi/ctamixer.h +++ b/sound/pci/ctxfi/ctamixer.h @@ -43,8 +43,8 @@ struct sum_mgr { }; /* Constructor and destructor of daio resource manager */ -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr); -int sum_mgr_destroy(struct sum_mgr *sum_mgr); +int sum_mgr_create(struct hw *hw, void **ptr); +int sum_mgr_destroy(void *ptr); /* Define the descriptor of a amixer resource */ struct amixer_rsc_ops; @@ -89,7 +89,7 @@ struct amixer_mgr { }; /* Constructor and destructor of amixer resource manager */ -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr); -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr); +int amixer_mgr_destroy(void *amixer_mgr); #endif /* CTAMIXER_H */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index fbdb8a3d5b8e..2a3e9d8ba7db 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -105,23 +105,20 @@ static struct { .public_name = "Mixer"} }; -typedef int (*create_t)(struct hw *, void **); -typedef int (*destroy_t)(void *); - static struct { int (*create)(struct hw *hw, void **rmgr); int (*destroy)(void *mgr); } rsc_mgr_funcs[NUM_RSCTYP] = { - [SRC] = { .create = (create_t)src_mgr_create, - .destroy = (destroy_t)src_mgr_destroy }, - [SRCIMP] = { .create = (create_t)srcimp_mgr_create, - .destroy = (destroy_t)srcimp_mgr_destroy }, - [AMIXER] = { .create = (create_t)amixer_mgr_create, - .destroy = (destroy_t)amixer_mgr_destroy }, - [SUM] = { .create = (create_t)sum_mgr_create, - .destroy = (destroy_t)sum_mgr_destroy }, - [DAIO] = { .create = (create_t)daio_mgr_create, - .destroy = (destroy_t)daio_mgr_destroy } + [SRC] = { .create = src_mgr_create, + .destroy = src_mgr_destroy }, + [SRCIMP] = { .create = srcimp_mgr_create, + .destroy = srcimp_mgr_destroy }, + [AMIXER] = { .create = amixer_mgr_create, + .destroy = amixer_mgr_destroy }, + [SUM] = { .create = sum_mgr_create, + .destroy = sum_mgr_destroy }, + [DAIO] = { .create = daio_mgr_create, + .destroy = daio_mgr_destroy } }; static int diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 7fc720046ce2..83aaf9441ef3 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -684,7 +684,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) return 0; } -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr) +int daio_mgr_create(struct hw *hw, void **rdaio_mgr) { int err, i; struct daio_mgr *daio_mgr; @@ -738,8 +738,9 @@ error1: return err; } -int daio_mgr_destroy(struct daio_mgr *daio_mgr) +int daio_mgr_destroy(void *ptr) { + struct daio_mgr *daio_mgr = ptr; unsigned long flags; /* free daio input mapper list */ diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index bd6310f48013..15147fe5f74a 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -115,7 +115,7 @@ struct daio_mgr { }; /* Constructor and destructor of daio resource manager */ -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr); -int daio_mgr_destroy(struct daio_mgr *daio_mgr); +int daio_mgr_create(struct hw *hw, void **ptr); +int daio_mgr_destroy(void *ptr); #endif /* CTDAIO_H */ diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 4a94b4708a77..159bd4008069 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -540,7 +540,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) return 0; } -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr) +int src_mgr_create(struct hw *hw, void **rsrc_mgr) { int err, i; struct src_mgr *src_mgr; @@ -580,8 +580,9 @@ error1: return err; } -int src_mgr_destroy(struct src_mgr *src_mgr) +int src_mgr_destroy(void *ptr) { + struct src_mgr *src_mgr = ptr; rsc_mgr_uninit(&src_mgr->mgr); kfree(src_mgr); @@ -821,7 +822,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) return err; } -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr) +int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr) { int err; struct srcimp_mgr *srcimp_mgr; @@ -866,8 +867,9 @@ error1: return err; } -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr) +int srcimp_mgr_destroy(void *ptr) { + struct srcimp_mgr *srcimp_mgr = ptr; unsigned long flags; /* free src input mapper list */ diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h index 1124daf50c9b..e6366cc6a7ae 100644 --- a/sound/pci/ctxfi/ctsrc.h +++ b/sound/pci/ctxfi/ctsrc.h @@ -139,10 +139,10 @@ struct srcimp_mgr { }; /* Constructor and destructor of SRC resource manager */ -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr); -int src_mgr_destroy(struct src_mgr *src_mgr); +int src_mgr_create(struct hw *hw, void **ptr); +int src_mgr_destroy(void *ptr); /* Constructor and destructor of SRCIMP resource manager */ -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr); -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); +int srcimp_mgr_create(struct hw *hw, void **ptr); +int srcimp_mgr_destroy(void *ptr); #endif /* CTSRC_H */ diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index c70c3ac4e99a..7484de255a3e 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -34,7 +34,6 @@ static int get_firmware(const struct firmware **fw_entry, int err; char name[30]; -#ifdef CONFIG_PM_SLEEP if (chip->fw_cache[fw_index]) { dev_dbg(chip->card->dev, "firmware requested: %s is cached\n", @@ -42,7 +41,6 @@ static int get_firmware(const struct firmware **fw_entry, *fw_entry = chip->fw_cache[fw_index]; return 0; } -#endif dev_dbg(chip->card->dev, "firmware requested: %s\n", card_fw[fw_index].data); @@ -51,10 +49,8 @@ static int get_firmware(const struct firmware **fw_entry, if (err < 0) dev_err(chip->card->dev, "get_firmware(): Firmware not available (%d)\n", err); -#ifdef CONFIG_PM_SLEEP else chip->fw_cache[fw_index] = *fw_entry; -#endif return err; } @@ -63,18 +59,13 @@ static int get_firmware(const struct firmware **fw_entry, static void free_firmware(const struct firmware *fw_entry, struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); -#else - release_firmware(fw_entry); -#endif } static void free_firmware_cache(struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP int i; for (i = 0; i < 8 ; i++) @@ -82,8 +73,6 @@ static void free_firmware_cache(struct echoaudio *chip) release_firmware(chip->fw_cache[i]); dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - -#endif } @@ -2146,8 +2135,6 @@ static int snd_echo_probe(struct pci_dev *pci, } -#if defined(CONFIG_PM_SLEEP) - static int snd_echo_suspend(struct device *dev) { struct echoaudio *chip = dev_get_drvdata(dev); @@ -2237,11 +2224,7 @@ static int snd_echo_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); -#define SND_ECHO_PM_OPS &snd_echo_pm -#else -#define SND_ECHO_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); /****************************************************************************** Everything starts and ends here @@ -2253,7 +2236,7 @@ static struct pci_driver echo_driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .driver = { - .pm = SND_ECHO_PM_OPS, + .pm = &snd_echo_pm, }, }; diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index d51de3e4edfa..511f2fcc0fb9 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -422,9 +422,7 @@ struct echoaudio { u32 __iomem *dsp_registers; /* DSP's register base */ u32 active_mask; /* Chs. active mask or * punks out */ -#ifdef CONFIG_PM_SLEEP const struct firmware *fw_cache[8]; /* Cached firmwares */ -#endif #ifdef ECHOCARD_HAS_MIDI u16 mtc_state; /* State for MIDI input parsing state machine */ diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 89210b2c7342..18928b905939 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1968,7 +1968,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); } -#ifdef CONFIG_PM_SLEEP static int snd_ensoniq_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2007,11 +2006,7 @@ static int snd_ensoniq_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); -#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm -#else -#define SND_ENSONIQ_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); static int snd_ensoniq_create(struct snd_card *card, struct pci_dev *pci) @@ -2380,7 +2375,7 @@ static struct pci_driver ens137x_driver = { .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .driver = { - .pm = SND_ENSONIQ_PM_OPS, + .pm = &snd_ensoniq_pm, }, }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index ec598ba1a883..018a8d53ca53 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -216,9 +216,7 @@ struct es1938 { #ifdef SUPPORT_JOYSTICK struct gameport *gameport; #endif -#ifdef CONFIG_PM_SLEEP unsigned char saved_regs[SAVED_REG_SIZE]; -#endif }; static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id); @@ -1395,7 +1393,6 @@ static void snd_es1938_chip_init(struct es1938 *chip) outb(0, SLDM_REG(chip, DMACLEAR)); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -1461,11 +1458,7 @@ static int es1938_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); -#define ES1938_PM_OPS &es1938_pm -#else -#define ES1938_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); #ifdef SUPPORT_JOYSTICK static int snd_es1938_create_gameport(struct es1938 *chip) @@ -1787,7 +1780,7 @@ static struct pci_driver es1938_driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .driver = { - .pm = ES1938_PM_OPS, + .pm = &es1938_pm, }, }; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 4bc0f53c223b..c6c018b40c69 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -473,9 +473,7 @@ struct esschan { /* linked list */ struct list_head list; -#ifdef CONFIG_PM_SLEEP u16 wc_map[4]; -#endif }; struct es1968 { @@ -526,9 +524,7 @@ struct es1968 { struct list_head substream_list; spinlock_t substream_lock; -#ifdef CONFIG_PM_SLEEP u16 apu_map[NR_APUS][NR_APU_REGS]; -#endif #ifdef SUPPORT_JOYSTICK struct gameport *gameport; @@ -689,9 +685,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat { if (snd_BUG_ON(channel >= NR_APUS)) return; -#ifdef CONFIG_PM_SLEEP chip->apu_map[channel][reg] = data; -#endif reg |= (channel << 4); apu_index_set(chip, reg); apu_data_set(chip, data); @@ -976,9 +970,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es /* set the wavecache control reg */ wave_set_register(chip, es->apu[channel] << 3, tmpval); -#ifdef CONFIG_PM_SLEEP es->wc_map[channel] = tmpval; -#endif } @@ -2356,7 +2348,6 @@ static void snd_es1968_start_irq(struct es1968 *chip) outw(w, chip->io_port + ESM_PORT_HOST_IRQ); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -2418,11 +2409,7 @@ static int es1968_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); -#define ES1968_PM_OPS &es1968_pm -#else -#define ES1968_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); #ifdef SUPPORT_JOYSTICK #define JOYSTICK_ADDR 0x200 @@ -2852,7 +2839,7 @@ static struct pci_driver es1968_driver = { .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .driver = { - .pm = ES1968_PM_OPS, + .pm = &es1968_pm, }, }; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 62b3cb126c6d..7f4834c2d5e6 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -222,9 +222,7 @@ struct fm801 { struct snd_tea575x tea; #endif -#ifdef CONFIG_PM_SLEEP u16 saved_regs[0x20]; -#endif }; /* @@ -1339,7 +1337,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static const unsigned char saved_regs[] = { FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, @@ -1396,18 +1393,14 @@ static int snd_fm801_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); -#define SND_FM801_PM_OPS &snd_fm801_pm -#else -#define SND_FM801_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); static struct pci_driver fm801_driver = { .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .driver = { - .pm = SND_FM801_PM_OPS, + .pm = &snd_fm801_pm, }, }; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 8e0ff70fb610..26da739eea82 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -116,6 +116,9 @@ config SND_HDA_CS_DSP_CONTROLS tristate select FW_CS_DSP +config SND_HDA_SCODEC_COMPONENT + tristate + config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C @@ -201,6 +204,7 @@ config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC select SND_HDA_GENERIC_LEDS + select SND_HDA_SCODEC_COMPONENT help Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 793e296c3f64..13e04e1f65de 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -37,6 +37,7 @@ snd-hda-scodec-cs35l56-objs := cs35l56_hda.o snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o +snd-hda-scodec-component-objs := hda_component.o snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o # common driver @@ -67,6 +68,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o +obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o # this must be the last entry after codec drivers; diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index 87dcb367e239..b1a5c0ec2b55 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -51,19 +51,30 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 }, { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, @@ -207,6 +218,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde struct spi_device *spi; bool dsd_found; int ret; + int i; for (cfg = cs35l41_config_table; cfg->ssid; cfg++) { if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id)) @@ -292,16 +304,6 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde cs35l41->index = id == 0x40 ? 0 : 1; } - if (cfg->num_amps == 3) - /* 3 amps means a center channel, so no duplicate channels */ - cs35l41->channel_index = 0; - else - /* - * if 4 amps, there are duplicate channels, so they need different indexes - * if 2 amps, no duplicate channels, channel_index would be 0 - */ - cs35l41->channel_index = cs35l41->index / 2; - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", cs35l41->index, GPIOD_OUT_LOW, "cs35l41-reset"); @@ -309,6 +311,11 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde hw_cfg->spk_pos = cfg->channel[cs35l41->index]; + cs35l41->channel_index = 0; + for (i = 0; i < cs35l41->index; i++) + if (cfg->channel[i] == hw_cfg->spk_pos) + cs35l41->channel_index++; + if (cfg->boost_type == INTERNAL) { hw_cfg->bst_type = CS35L41_INT_BOOST; hw_cfg->bst_ind = cfg->boost_ind_nanohenry; @@ -333,6 +340,42 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde } /* + * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has + * its own speaker id. + */ +static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* If _DSD exists for this laptop, we cannot support it through here */ + if (acpi_dev_has_props(cs35l41->dacpi)) + return -ENOENT; + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + if (cs35l41->index == 0) + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1); + else + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->valid = true; + + hw_cfg->bst_type = CS35L41_INT_BOOST; + hw_cfg->bst_ind = 1000; + hw_cfg->bst_ipk = 4100; + hw_cfg->bst_cap = 24; + hw_cfg->gpio1.func = CS35L41_NOT_USED; + hw_cfg->gpio1.valid = true; + + return 0; +} + +/* * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. * And devices created by serial-multi-instantiate don't have their device struct * pointing to the correct fwnode, so acpi_dev must be used here. @@ -389,19 +432,34 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "103C8A2E", generic_dsd_config }, { "CSC3551", "103C8A30", generic_dsd_config }, { "CSC3551", "103C8A31", generic_dsd_config }, + { "CSC3551", "103C8A6E", generic_dsd_config }, { "CSC3551", "103C8BB3", generic_dsd_config }, { "CSC3551", "103C8BB4", generic_dsd_config }, + { "CSC3551", "103C8BDD", generic_dsd_config }, + { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BDF", generic_dsd_config }, { "CSC3551", "103C8BE0", generic_dsd_config }, { "CSC3551", "103C8BE1", generic_dsd_config }, { "CSC3551", "103C8BE2", generic_dsd_config }, - { "CSC3551", "103C8BE9", generic_dsd_config }, - { "CSC3551", "103C8BDD", generic_dsd_config }, - { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BE3", generic_dsd_config }, { "CSC3551", "103C8BE5", generic_dsd_config }, { "CSC3551", "103C8BE6", generic_dsd_config }, + { "CSC3551", "103C8BE7", generic_dsd_config }, + { "CSC3551", "103C8BE8", generic_dsd_config }, + { "CSC3551", "103C8BE9", generic_dsd_config }, { "CSC3551", "103C8B3A", generic_dsd_config }, + { "CSC3551", "103C8C15", generic_dsd_config }, + { "CSC3551", "103C8C16", generic_dsd_config }, + { "CSC3551", "103C8C17", generic_dsd_config }, + { "CSC3551", "103C8C4F", generic_dsd_config }, + { "CSC3551", "103C8C50", generic_dsd_config }, + { "CSC3551", "103C8C51", generic_dsd_config }, + { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8CDD", generic_dsd_config }, + { "CSC3551", "103C8CDE", generic_dsd_config }, { "CSC3551", "104312AF", generic_dsd_config }, { "CSC3551", "10431433", generic_dsd_config }, { "CSC3551", "10431463", generic_dsd_config }, diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c new file mode 100644 index 000000000000..cd299d7d84ba --- /dev/null +++ b/sound/pci/hda/hda_component.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD audio Component Binding Interface + * + * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/acpi.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/hda_codec.h> +#include "hda_component.h" +#include "hda_local.h" + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps, + acpi_handle handle, u32 event, void *data) +{ + int i; + + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].acpi_notify) + comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event, + comps[i].dev); + } +} +EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT); + +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, int num_comps, + acpi_notify_handler handler, void *data) +{ + bool support_notifications = false; + struct acpi_device *adev; + int ret; + int i; + + adev = comps[0].adev; + if (!acpi_device_handle(adev)) + return 0; + + for (i = 0; i < num_comps; i++) + support_notifications = support_notifications || + comps[i].acpi_notifications_supported; + + if (support_notifications) { + ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + handler, data); + if (ret < 0) { + codec_warn(cdc, "Failed to install notify handler: %d\n", ret); + return 0; + } + + codec_dbg(cdc, "Notify handler installed\n"); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); + +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler) +{ + struct acpi_device *adev; + int ret; + + adev = comps[0].adev; + if (!acpi_device_handle(adev)) + return; + + ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler); + if (ret < 0) + codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action) +{ + int i; + + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].pre_playback_hook) + comps[i].pre_playback_hook(comps[i].dev, action); + } + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].playback_hook) + comps[i].playback_hook(comps[i].dev, action); + } + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].post_playback_hook) + comps[i].post_playback_hook(comps[i].dev, action); + } +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT); + +struct hda_scodec_match { + const char *bus; + const char *hid; + const char *match_str; + int index; +}; + +/* match the device name in a slightly relaxed manner */ +static int hda_comp_match_dev_name(struct device *dev, void *data) +{ + struct hda_scodec_match *p = data; + const char *d = dev_name(dev); + int n = strlen(p->bus); + char tmp[32]; + + /* check the bus name */ + if (strncmp(d, p->bus, n)) + return 0; + /* skip the bus number */ + if (isdigit(d[n])) + n++; + /* the rest must be exact matching */ + snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index); + return !strcmp(d + n, tmp); +} + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component *comps, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops) +{ + struct device *dev = hda_codec_dev(cdc); + struct component_match *match = NULL; + struct hda_scodec_match *sm; + int ret, i; + + for (i = 0; i < count; i++) { + sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); + if (!sm) + return -ENOMEM; + + sm->bus = bus; + sm->hid = hid; + sm->match_str = match_str; + sm->index = i; + comps[i].codec = cdc; + component_match_add(dev, &match, hda_comp_match_dev_name, sm); + } + + ret = component_master_add_with_match(dev, ops, match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT); + +void hda_component_manager_free(struct hda_codec *cdc, + const struct component_master_ops *ops) +{ + struct device *dev = hda_codec_dev(cdc); + + component_master_del(dev, ops); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT); + +MODULE_DESCRIPTION("HD Audio component binding library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h index bbd6f0ed16c1..deae9dea01b4 100644 --- a/sound/pci/hda/hda_component.h +++ b/sound/pci/hda/hda_component.h @@ -23,3 +23,62 @@ struct hda_component { void (*playback_hook)(struct device *dev, int action); void (*post_playback_hook)(struct device *dev, int action); }; + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps, + acpi_handle handle, u32 event, void *data); +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, int num_comps, + acpi_notify_handler handler, void *data); +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler); +#else +static inline void hda_component_acpi_device_notify(struct hda_component *comps, + int num_comps, + acpi_handle handle, + u32 event, + void *data) +{ +} + +static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + int num_comps, + acpi_notify_handler handler, + void *data) + +{ + return 0; +} + +static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler) +{ +} +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, + int action); + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component *comps, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops); + +void hda_component_manager_free(struct hda_codec *cdc, + const struct component_master_ops *ops); + +static inline int hda_component_manager_bind(struct hda_codec *cdc, + struct hda_component *comps) +{ + return component_bind_all(hda_codec_dev(cdc), comps); +} + +static inline void hda_component_manager_unbind(struct hda_codec *cdc, + struct hda_component *comps) +{ + component_unbind_all(hda_codec_dev(cdc), comps); +} diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 3e7bfeee84fd..29eae7244fe7 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -24,6 +24,7 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/pcm_params.h> #include "hda_controller.h" #include "hda_local.h" @@ -108,6 +109,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); + struct hdac_stream *hdas = azx_stream(azx_dev); int ret = 0; trace_azx_pcm_hw_params(chip, azx_dev); @@ -117,9 +119,15 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, goto unlock; } - azx_dev->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; + /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ + hdas->bufsize = params_buffer_bytes(hw_params); + hdas->period_bytes = params_period_bytes(hw_params); + hdas->format_val = 0; + hdas->no_period_wakeup = + (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + if (snd_hdac_stream_setup_periods(hdas) < 0) + ret = -ENOMEM; unlock: dsp_unlock(azx_dev); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d9a689ed424c..9f1147af030c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -133,7 +133,6 @@ struct alc_spec { u8 alc_mute_keycode_map[1]; /* component binding */ - struct component_match *match; struct hda_component comps[HDA_MAX_COMPONENTS]; }; @@ -6719,91 +6718,30 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } -#ifdef CONFIG_ACPI static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) { struct hda_codec *cdc = data; struct alc_spec *spec = cdc->spec; - int i; codec_info(cdc, "ACPI Notification %d\n", event); - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].acpi_notify) - spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), event, - spec->comps[i].dev); - } -} - -static int comp_bind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - bool support_notifications = false; - struct acpi_device *adev; - int ret; - int i; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return 0; - - for (i = 0; i < HDA_MAX_COMPONENTS; i++) - support_notifications = support_notifications || - spec->comps[i].acpi_notifications_supported; - - if (support_notifications) { - ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify, cdc); - if (ret < 0) { - codec_warn(cdc, "Failed to install notify handler: %d\n", ret); - return 0; - } - - codec_dbg(cdc, "Notify handler installed\n"); - } - - return 0; + hda_component_acpi_device_notify(spec->comps, ARRAY_SIZE(spec->comps), + handle, event, data); } -static void comp_unbind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - struct acpi_device *adev; - int ret; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return; - - ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify); - if (ret < 0) - codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); -} -#else -static int comp_bind_acpi(struct device *dev) -{ - return 0; -} - -static void comp_unbind_acpi(struct device *dev) -{ -} -#endif - static int comp_bind(struct device *dev) { struct hda_codec *cdc = dev_to_hda_codec(dev); struct alc_spec *spec = cdc->spec; int ret; - ret = component_bind_all(dev, spec->comps); + ret = hda_component_manager_bind(cdc, spec->comps); if (ret) return ret; - return comp_bind_acpi(dev); + return hda_component_manager_bind_acpi_notifications(cdc, + spec->comps, ARRAY_SIZE(spec->comps), + comp_acpi_device_notify, cdc); } static void comp_unbind(struct device *dev) @@ -6811,8 +6749,8 @@ static void comp_unbind(struct device *dev) struct hda_codec *cdc = dev_to_hda_codec(dev); struct alc_spec *spec = cdc->spec; - comp_unbind_acpi(dev); - component_unbind_all(dev, spec->comps); + hda_component_manager_unbind_acpi_notifications(cdc, spec->comps, comp_acpi_device_notify); + hda_component_manager_unbind(cdc, spec->comps); } static const struct component_master_ops comp_master_ops = { @@ -6824,177 +6762,78 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_ struct snd_pcm_substream *sub, int action) { struct alc_spec *spec = cdc->spec; - int i; - - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].pre_playback_hook) - spec->comps[i].pre_playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].playback_hook) - spec->comps[i].playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].post_playback_hook) - spec->comps[i].post_playback_hook(spec->comps[i].dev, action); - } -} - -struct scodec_dev_name { - const char *bus; - const char *hid; - int index; -}; - -/* match the device name in a slightly relaxed manner */ -static int comp_match_cs35l41_dev_name(struct device *dev, void *data) -{ - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00-cs35l41-hda.%d", p->hid, p->index); - return !strcmp(d + n, tmp); -} - -static int comp_match_tas2781_dev_name(struct device *dev, - void *data) -{ - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00", p->hid); - - return !strcmp(d + n, tmp); -} - -static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, - const char *hid, int count) -{ - struct device *dev = hda_codec_dev(cdc); - struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; - int ret, i; - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - for (i = 0; i < count; i++) { - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = i; - spec->comps[i].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_cs35l41_dev_name, rec); - } - ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); - if (ret) - codec_err(cdc, "Fail to register component aggregator %d\n", ret); - else - spec->gen.pcm_playback_hook = comp_generic_playback_hook; - break; - case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); - break; - } + hda_component_manager_playback_hook(spec->comps, ARRAY_SIZE(spec->comps), action); } -static void tas2781_generic_fixup(struct hda_codec *cdc, int action, - const char *bus, const char *hid) +static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, const char *match_str, int count) { - struct device *dev = hda_codec_dev(cdc); struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; int ret; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = 0; - spec->comps[0].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_tas2781_dev_name, rec); - ret = component_master_add_with_match(dev, &comp_master_ops, - spec->match); + ret = hda_component_manager_init(cdc, spec->comps, count, bus, hid, + match_str, &comp_master_ops); if (ret) - codec_err(cdc, - "Fail to register component aggregator %d\n", - ret); - else - spec->gen.pcm_playback_hook = - comp_generic_playback_hook; + return; + + spec->gen.pcm_playback_hook = comp_generic_playback_hook; break; case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); + hda_component_manager_free(cdc, &comp_master_ops); break; } } static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 4); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 4); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); } static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); +} + +static void cs35l56_fixup_spi_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "spi", "CSC3556", "-%s:00-cs35l56-hda.%d", 4); } static void tas2781_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781"); + comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); } static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "INT8866"); + comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); } /* for alc295_fixup_hp_top_speakers */ @@ -7476,6 +7315,7 @@ enum { ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, ALC294_FIXUP_CS35L41_I2C_2, + ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9629,6 +9469,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l56_fixup_spi_four, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -9921,9 +9767,21 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), @@ -9933,8 +9791,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9962,11 +9822,35 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), @@ -9976,6 +9860,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8cde, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), @@ -10097,6 +9983,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), @@ -12637,6 +12524,7 @@ MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); +MODULE_IMPORT_NS(SND_HDA_SCODEC_COMPONENT); static struct hda_codec_driver realtek_driver = { .id = snd_hda_id_realtek, diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index ae285c0a629c..dae3e15ba534 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2555,7 +2555,6 @@ static void snd_intel8x0_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2628,11 +2627,7 @@ static int intel8x0_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); -#define INTEL8X0_PM_OPS &intel8x0_pm -#else -#define INTEL8X0_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ @@ -3200,7 +3195,7 @@ static struct pci_driver intel8x0_driver = { .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .driver = { - .pm = INTEL8X0_PM_OPS, + .pm = &intel8x0_pm, }, }; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 653ecca78238..3d6f5b3cc73e 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -965,7 +965,6 @@ static void snd_intel8x0m_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1006,11 +1005,7 @@ static int intel8x0m_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); -#define INTEL8X0M_PM_OPS &intel8x0m_pm -#else -#define INTEL8X0M_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); static void snd_intel8x0m_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) @@ -1236,7 +1231,7 @@ static struct pci_driver intel8x0m_driver = { .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .driver = { - .pm = INTEL8X0M_PM_OPS, + .pm = &intel8x0m_pm, }, }; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 305cbd24a391..f4d211970d7e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -769,9 +769,7 @@ struct snd_m3 { unsigned int in_suspend; -#ifdef CONFIG_PM_SLEEP u16 *suspend_mem; -#endif const struct firmware *assp_kernel_image; const struct firmware *assp_minisrc_image; @@ -2354,9 +2352,7 @@ static void snd_m3_free(struct snd_card *card) outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */ } -#ifdef CONFIG_PM_SLEEP vfree(chip->suspend_mem); -#endif release_firmware(chip->assp_kernel_image); release_firmware(chip->assp_minisrc_image); } @@ -2365,7 +2361,6 @@ static void snd_m3_free(struct snd_card *card) /* * APM support */ -#ifdef CONFIG_PM_SLEEP static int m3_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2439,11 +2434,7 @@ static int m3_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); -#define M3_PM_OPS &m3_pm -#else -#define M3_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); #ifdef CONFIG_SND_MAESTRO3_INPUT static int snd_m3_input_register(struct snd_m3 *chip) @@ -2587,14 +2578,14 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, chip->irq = pci->irq; card->sync_irq = chip->irq; -#ifdef CONFIG_PM_SLEEP - chip->suspend_mem = - vmalloc(array_size(sizeof(u16), - REV_B_CODE_MEMORY_LENGTH + - REV_B_DATA_MEMORY_LENGTH)); - if (chip->suspend_mem == NULL) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + chip->suspend_mem = + vmalloc(array_size(sizeof(u16), + REV_B_CODE_MEMORY_LENGTH + + REV_B_DATA_MEMORY_LENGTH)); + if (!chip->suspend_mem) + dev_warn(card->dev, "can't allocate apm buffer\n"); + } err = snd_m3_mixer(chip); if (err < 0) @@ -2706,7 +2697,7 @@ static struct pci_driver m3_driver = { .id_table = snd_m3_ids, .probe = snd_m3_probe, .driver = { - .pm = M3_PM_OPS, + .pm = &m3_pm, }, }; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 34f90829e656..11ba7d4eac2a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1356,7 +1356,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * APM event handler, so the card is properly reinitialized after a power * event. @@ -1400,11 +1399,7 @@ static int nm256_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); -#define NM256_PM_OPS &nm256_pm -#else -#define NM256_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); static void snd_nm256_free(struct snd_card *card) { @@ -1660,7 +1655,7 @@ static struct pci_driver nm256_driver = { .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .driver = { - .pm = NM256_PM_OPS, + .pm = &nm256_pm, }, }; diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9dee0345f22c..7e80686fb41a 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -448,9 +448,7 @@ struct snd_riptide { unsigned long received_irqs; unsigned long handled_irqs; -#ifdef CONFIG_PM_SLEEP int in_suspend; -#endif }; struct sgd { /* scatter gather desriptor */ @@ -1142,7 +1140,6 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP static int riptide_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1166,11 +1163,7 @@ static int riptide_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); -#define RIPTIDE_PM_OPS &riptide_pm -#else -#define RIPTIDE_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) { @@ -2135,7 +2128,7 @@ static struct pci_driver driver = { .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .driver = { - .pm = RIPTIDE_PM_OPS, + .pm = &riptide_pm, }, }; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 6b5ffb18197b..d50ad25574ad 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -220,12 +220,10 @@ struct rme96 { u8 rev; /* card revision number */ -#ifdef CONFIG_PM_SLEEP u32 playback_pointer; u32 capture_pointer; void *playback_suspend_buffer; void *capture_suspend_buffer; -#endif struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -1543,10 +1541,8 @@ snd_rme96_free(struct rme96 *rme96) rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); } -#ifdef CONFIG_PM_SLEEP vfree(rme96->playback_suspend_buffer); vfree(rme96->capture_suspend_buffer); -#endif } static void @@ -2329,8 +2325,6 @@ snd_rme96_create_switches(struct snd_card *card, * Card initialisation */ -#ifdef CONFIG_PM_SLEEP - static int rme96_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2392,11 +2386,7 @@ static int rme96_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); -#define RME96_PM_OPS &rme96_pm -#else -#define RME96_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); static void snd_rme96_card_free(struct snd_card *card) { @@ -2432,14 +2422,14 @@ __snd_rme96_probe(struct pci_dev *pci, if (err) return err; -#ifdef CONFIG_PM_SLEEP - rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->playback_suspend_buffer) - return -ENOMEM; - rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->capture_suspend_buffer) - return -ENOMEM; -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->playback_suspend_buffer) + return -ENOMEM; + rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->capture_suspend_buffer) + return -ENOMEM; + } strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { @@ -2483,7 +2473,7 @@ static struct pci_driver rme96_driver = { .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .driver = { - .pm = RME96_PM_OPS, + .pm = &rme96_pm, }, }; diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index fabe393607f8..53206beb2cb5 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -90,11 +90,7 @@ struct voice { * we're not doing power management, we still need to allocate a page * for the silence buffer. */ -#ifdef CONFIG_PM_SLEEP #define SIS_SUSPEND_PAGES 4 -#else -#define SIS_SUSPEND_PAGES 1 -#endif struct sis7019 { unsigned long ioport; @@ -1152,7 +1148,6 @@ static int sis_chip_init(struct sis7019 *sis) return 0; } -#ifdef CONFIG_PM_SLEEP static int sis_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1231,11 +1226,7 @@ error: return -EIO; } -static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); -#define SIS_PM_OPS &sis_pm -#else -#define SIS_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); static int sis_alloc_suspend(struct sis7019 *sis) { @@ -1397,7 +1388,7 @@ static struct pci_driver sis7019_driver = { .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .driver = { - .pm = SIS_PM_OPS, + .pm = &sis_pm, }, }; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index d8666ff7bdfa..89838b4fb118 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -347,13 +347,11 @@ struct via82xx { unsigned char old_legacy; unsigned char old_legacy_cfg; -#ifdef CONFIG_PM_SLEEP unsigned char legacy_saved; unsigned char legacy_cfg_saved; unsigned char spdif_ctrl_saved; unsigned char capture_src_saved[2]; unsigned int mpu_port_saved; -#endif unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */ @@ -2031,9 +2029,7 @@ static int snd_via686_init_misc(struct via82xx *chip) if (mpu_port >= 0x200) { /* force MIDI */ mpu_port &= 0xfffc; pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01); -#ifdef CONFIG_PM_SLEEP chip->mpu_port_saved = mpu_port; -#endif } else { mpu_port = pci_resource_start(chip->pci, 2); } @@ -2085,10 +2081,8 @@ static int snd_via686_init_misc(struct via82xx *chip) snd_via686_create_gameport(chip, &legacy); -#ifdef CONFIG_PM_SLEEP chip->legacy_saved = legacy; chip->legacy_cfg_saved = legacy_cfg; -#endif return 0; } @@ -2234,7 +2228,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2287,11 +2280,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -2576,7 +2565,7 @@ static struct pci_driver via82xx_driver = { .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ca7f024bf8ec..a0a49b8d1511 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1008,7 +1008,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1042,11 +1041,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -1168,7 +1163,7 @@ static struct pci_driver via82xx_modem_driver = { .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index e73bd62c033c..80e0ea0ec9fb 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -271,7 +271,6 @@ static void pxa2xx_ac97_dev_remove(struct platform_device *pdev) pxa2xx_ac97_hw_remove(pdev); } -#ifdef CONFIG_PM_SLEEP static int pxa2xx_ac97_dev_suspend(struct device *dev) { return pxa2xx_ac97_hw_suspend(); @@ -282,18 +281,15 @@ static int pxa2xx_ac97_dev_resume(struct device *dev) return pxa2xx_ac97_hw_resume(); } -static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); -#endif static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_dev_probe, .remove_new = pxa2xx_ac97_dev_remove, .driver = { .name = "pxa2xx-ac97", -#ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, -#endif .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), }, }; diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 1e8765d75d8f..5648d744aa79 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -1076,8 +1076,6 @@ out: snd_card_free(card); } -#ifdef CONFIG_PM_SLEEP - static int snd_at73c213_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1109,18 +1107,13 @@ static int snd_at73c213_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend, snd_at73c213_resume); -#define AT73C213_PM_OPS (&at73c213_pm_ops) - -#else -#define AT73C213_PM_OPS NULL -#endif static struct spi_driver at73c213_driver = { .driver = { .name = "at73c213", - .pm = AT73C213_PM_OPS, + .pm = &at73c213_pm_ops, }, .probe = snd_at73c213_probe, .remove = snd_at73c213_remove, diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 0006c3ddb51d..a82af9374852 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -85,7 +85,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch return -EINVAL; emu->card = card; - emu->name = kstrdup(name, GFP_KERNEL); + emu->name = kstrdup_const(name, GFP_KERNEL); emu->voices = kcalloc(emu->max_voices, sizeof(struct snd_emux_voice), GFP_KERNEL); if (emu->name == NULL || emu->voices == NULL) @@ -140,7 +140,7 @@ int snd_emux_free(struct snd_emux *emu) snd_emux_delete_hwdep(emu); snd_sf_free(emu->sflist); kfree(emu->voices); - kfree(emu->name); + kfree_const(emu->name); kfree(emu); return 0; } diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile index 2742bddb8874..a839f8c8b5e6 100644 --- a/sound/virtio/Makefile +++ b/sound/virtio/Makefile @@ -7,6 +7,7 @@ virtio_snd-objs := \ virtio_chmap.o \ virtio_ctl_msg.o \ virtio_jack.o \ + virtio_kctl.o \ virtio_pcm.o \ virtio_pcm_msg.o \ virtio_pcm_ops.o diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index b158c3cb8e5f..2da20c625247 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -64,6 +64,9 @@ static void virtsnd_event_dispatch(struct virtio_snd *snd, case VIRTIO_SND_EVT_PCM_XRUN: virtsnd_pcm_event(snd, event); break; + case VIRTIO_SND_EVT_CTL_NOTIFY: + virtsnd_kctl_event(snd, event); + break; } } @@ -233,6 +236,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd) if (rc) return rc; + if (virtio_has_feature(vdev, VIRTIO_SND_F_CTLS)) { + rc = virtsnd_kctl_parse_cfg(snd); + if (rc) + return rc; + } + if (snd->njacks) { rc = virtsnd_jack_build_devs(snd); if (rc) @@ -251,6 +260,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd) return rc; } + if (snd->nkctls) { + rc = virtsnd_kctl_build_devs(snd); + if (rc) + return rc; + } + return snd_card_register(snd->card); } @@ -417,10 +432,16 @@ static const struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_SND_F_CTLS +}; + static struct virtio_driver virtsnd_driver = { .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .validate = virtsnd_validate, .probe = virtsnd_probe, .remove = virtsnd_remove, diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index 86ef3941895e..3ceee4e416fc 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -32,6 +32,16 @@ struct virtio_snd_queue { }; /** + * struct virtio_kctl - VirtIO control element. + * @kctl: ALSA control element. + * @items: Items for the ENUMERATED element type. + */ +struct virtio_kctl { + struct snd_kcontrol *kctl; + struct virtio_snd_ctl_enum_item *items; +}; + +/** * struct virtio_snd - VirtIO sound card device. * @vdev: Underlying virtio device. * @queues: Virtqueue wrappers. @@ -45,6 +55,9 @@ struct virtio_snd_queue { * @nsubstreams: Number of PCM substreams. * @chmaps: VirtIO channel maps. * @nchmaps: Number of channel maps. + * @kctl_infos: VirtIO control element information. + * @kctls: VirtIO control elements. + * @nkctls: Number of control elements. */ struct virtio_snd { struct virtio_device *vdev; @@ -59,6 +72,9 @@ struct virtio_snd { u32 nsubstreams; struct virtio_snd_chmap_info *chmaps; u32 nchmaps; + struct virtio_snd_ctl_info *kctl_infos; + struct virtio_kctl *kctls; + u32 nkctls; }; /* Message completion timeout in milliseconds (module parameter). */ @@ -108,4 +124,10 @@ int virtsnd_chmap_parse_cfg(struct virtio_snd *snd); int virtsnd_chmap_build_devs(struct virtio_snd *snd); +int virtsnd_kctl_parse_cfg(struct virtio_snd *snd); + +int virtsnd_kctl_build_devs(struct virtio_snd *snd); + +void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event); + #endif /* VIRTIO_SND_CARD_H */ diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c new file mode 100644 index 000000000000..7aa79c05b464 --- /dev/null +++ b/sound/virtio/virtio_kctl.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2022 OpenSynergy GmbH + */ +#include <sound/control.h> +#include <linux/virtio_config.h> + +#include "virtio_card.h" + +/* Map for converting VirtIO types to ALSA types. */ +static const snd_ctl_elem_type_t g_v2a_type_map[] = { + [VIRTIO_SND_CTL_TYPE_BOOLEAN] = SNDRV_CTL_ELEM_TYPE_BOOLEAN, + [VIRTIO_SND_CTL_TYPE_INTEGER] = SNDRV_CTL_ELEM_TYPE_INTEGER, + [VIRTIO_SND_CTL_TYPE_INTEGER64] = SNDRV_CTL_ELEM_TYPE_INTEGER64, + [VIRTIO_SND_CTL_TYPE_ENUMERATED] = SNDRV_CTL_ELEM_TYPE_ENUMERATED, + [VIRTIO_SND_CTL_TYPE_BYTES] = SNDRV_CTL_ELEM_TYPE_BYTES, + [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 +}; + +/* Map for converting VirtIO access rights to ALSA access rights. */ +static const unsigned int g_v2a_access_map[] = { + [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, + [VIRTIO_SND_CTL_ACCESS_WRITE] = SNDRV_CTL_ELEM_ACCESS_WRITE, + [VIRTIO_SND_CTL_ACCESS_VOLATILE] = SNDRV_CTL_ELEM_ACCESS_VOLATILE, + [VIRTIO_SND_CTL_ACCESS_INACTIVE] = SNDRV_CTL_ELEM_ACCESS_INACTIVE, + [VIRTIO_SND_CTL_ACCESS_TLV_READ] = SNDRV_CTL_ELEM_ACCESS_TLV_READ, + [VIRTIO_SND_CTL_ACCESS_TLV_WRITE] = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE, + [VIRTIO_SND_CTL_ACCESS_TLV_COMMAND] = SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND +}; + +/* Map for converting VirtIO event masks to ALSA event masks. */ +static const unsigned int g_v2a_mask_map[] = { + [VIRTIO_SND_CTL_EVT_MASK_VALUE] = SNDRV_CTL_EVENT_MASK_VALUE, + [VIRTIO_SND_CTL_EVT_MASK_INFO] = SNDRV_CTL_EVENT_MASK_INFO, + [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV +}; + +/** + * virtsnd_kctl_info() - Returns information about the control. + * @kcontrol: ALSA control element. + * @uinfo: Element information. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_kctl *kctl = &snd->kctls[kcontrol->private_value]; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int i; + + uinfo->type = g_v2a_type_map[le32_to_cpu(kinfo->type)]; + uinfo->count = le32_to_cpu(kinfo->count); + + switch (uinfo->type) { + case SNDRV_CTL_ELEM_TYPE_INTEGER: + uinfo->value.integer.min = + le32_to_cpu(kinfo->value.integer.min); + uinfo->value.integer.max = + le32_to_cpu(kinfo->value.integer.max); + uinfo->value.integer.step = + le32_to_cpu(kinfo->value.integer.step); + + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + uinfo->value.integer64.min = + le64_to_cpu(kinfo->value.integer64.min); + uinfo->value.integer64.max = + le64_to_cpu(kinfo->value.integer64.max); + uinfo->value.integer64.step = + le64_to_cpu(kinfo->value.integer64.step); + + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + uinfo->value.enumerated.items = + le32_to_cpu(kinfo->value.enumerated.items); + i = uinfo->value.enumerated.item; + if (i >= uinfo->value.enumerated.items) + return -EINVAL; + + strscpy(uinfo->value.enumerated.name, kctl->items[i].item, + sizeof(uinfo->value.enumerated.name)); + + break; + } + + return 0; +} + +/** + * virtsnd_kctl_get() - Read the value from the control. + * @kcontrol: ALSA control element. + * @uvalue: Element value. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + struct virtio_snd_ctl_value *kvalue; + size_t request_size = sizeof(*hdr); + size_t response_size = sizeof(struct virtio_snd_hdr) + sizeof(*kvalue); + unsigned int i; + int rc; + + msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + virtsnd_ctl_msg_ref(msg); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_READ); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + rc = virtsnd_ctl_msg_send_sync(snd, msg); + if (rc) + goto on_failure; + + kvalue = (void *)((u8 *)virtsnd_ctl_msg_response(msg) + + sizeof(struct virtio_snd_hdr)); + + switch (type) { + case VIRTIO_SND_CTL_TYPE_BOOLEAN: + case VIRTIO_SND_CTL_TYPE_INTEGER: + for (i = 0; i < count; ++i) + uvalue->value.integer.value[i] = + le32_to_cpu(kvalue->value.integer[i]); + break; + case VIRTIO_SND_CTL_TYPE_INTEGER64: + for (i = 0; i < count; ++i) + uvalue->value.integer64.value[i] = + le64_to_cpu(kvalue->value.integer64[i]); + break; + case VIRTIO_SND_CTL_TYPE_ENUMERATED: + for (i = 0; i < count; ++i) + uvalue->value.enumerated.item[i] = + le32_to_cpu(kvalue->value.enumerated[i]); + break; + case VIRTIO_SND_CTL_TYPE_BYTES: + memcpy(uvalue->value.bytes.data, kvalue->value.bytes, count); + break; + case VIRTIO_SND_CTL_TYPE_IEC958: + memcpy(&uvalue->value.iec958, &kvalue->value.iec958, + sizeof(uvalue->value.iec958)); + break; + } + +on_failure: + virtsnd_ctl_msg_unref(msg); + + return rc; +} + +/** + * virtsnd_kctl_put() - Write the value to the control. + * @kcontrol: ALSA control element. + * @uvalue: Element value. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + struct virtio_snd_ctl_value *kvalue; + size_t request_size = sizeof(*hdr) + sizeof(*kvalue); + size_t response_size = sizeof(struct virtio_snd_hdr); + unsigned int i; + + msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_WRITE); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + kvalue = (void *)((u8 *)hdr + sizeof(*hdr)); + + switch (type) { + case VIRTIO_SND_CTL_TYPE_BOOLEAN: + case VIRTIO_SND_CTL_TYPE_INTEGER: + for (i = 0; i < count; ++i) + kvalue->value.integer[i] = + cpu_to_le32(uvalue->value.integer.value[i]); + break; + case VIRTIO_SND_CTL_TYPE_INTEGER64: + for (i = 0; i < count; ++i) + kvalue->value.integer64[i] = + cpu_to_le64(uvalue->value.integer64.value[i]); + break; + case VIRTIO_SND_CTL_TYPE_ENUMERATED: + for (i = 0; i < count; ++i) + kvalue->value.enumerated[i] = + cpu_to_le32(uvalue->value.enumerated.item[i]); + break; + case VIRTIO_SND_CTL_TYPE_BYTES: + memcpy(kvalue->value.bytes, uvalue->value.bytes.data, count); + break; + case VIRTIO_SND_CTL_TYPE_IEC958: + memcpy(&kvalue->value.iec958, &uvalue->value.iec958, + sizeof(kvalue->value.iec958)); + break; + } + + return virtsnd_ctl_msg_send_sync(snd, msg); +} + +/** + * virtsnd_kctl_tlv_op() - Perform an operation on the control's metadata. + * @kcontrol: ALSA control element. + * @op_flag: Operation code (SNDRV_CTL_TLV_OP_XXX). + * @size: Size of the TLV data in bytes. + * @utlv: TLV data. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_tlv_op(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *utlv) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + unsigned int *tlv; + struct scatterlist sg; + int rc; + + msg = virtsnd_ctl_msg_alloc(sizeof(*hdr), sizeof(struct virtio_snd_hdr), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + + tlv = kzalloc(size, GFP_KERNEL); + if (!tlv) { + rc = -ENOMEM; + goto on_msg_unref; + } + + sg_init_one(&sg, tlv, size); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_READ: + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_READ); + + rc = virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false); + if (!rc) { + if (copy_to_user(utlv, tlv, size)) + rc = -EFAULT; + } + + break; + case SNDRV_CTL_TLV_OP_WRITE: + case SNDRV_CTL_TLV_OP_CMD: + if (op_flag == SNDRV_CTL_TLV_OP_WRITE) + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_WRITE); + else + hdr->hdr.code = + cpu_to_le32(VIRTIO_SND_R_CTL_TLV_COMMAND); + + if (copy_from_user(tlv, utlv, size)) { + rc = -EFAULT; + goto on_msg_unref; + } else { + rc = virtsnd_ctl_msg_send(snd, msg, &sg, NULL, false); + } + + break; + default: + rc = -EINVAL; + /* We never get here - we listed all values for op_flag */ + WARN_ON(1); + goto on_msg_unref; + } + kfree(tlv); + return rc; + +on_msg_unref: + virtsnd_ctl_msg_unref(msg); + kfree(tlv); + + return rc; +} + +/** + * virtsnd_kctl_get_enum_items() - Query items for the ENUMERATED element type. + * @snd: VirtIO sound device. + * @cid: Control element ID. + * + * This function is called during initial device initialization. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_get_enum_items(struct virtio_snd *snd, unsigned int cid) +{ + struct virtio_device *vdev = snd->vdev; + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; + struct virtio_kctl *kctl = &snd->kctls[cid]; + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + unsigned int n = le32_to_cpu(kinfo->value.enumerated.items); + struct scatterlist sg; + + msg = virtsnd_ctl_msg_alloc(sizeof(*hdr), + sizeof(struct virtio_snd_hdr), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + kctl->items = devm_kcalloc(&vdev->dev, n, sizeof(*kctl->items), + GFP_KERNEL); + if (!kctl->items) { + virtsnd_ctl_msg_unref(msg); + return -ENOMEM; + } + + sg_init_one(&sg, kctl->items, n * sizeof(*kctl->items)); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_ENUM_ITEMS); + hdr->control_id = cpu_to_le32(cid); + + return virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false); +} + +/** + * virtsnd_kctl_parse_cfg() - Parse the control element configuration. + * @snd: VirtIO sound device. + * + * This function is called during initial device initialization. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + u32 i; + int rc; + + virtio_cread_le(vdev, struct virtio_snd_config, controls, + &snd->nkctls); + if (!snd->nkctls) + return 0; + + snd->kctl_infos = devm_kcalloc(&vdev->dev, snd->nkctls, + sizeof(*snd->kctl_infos), GFP_KERNEL); + if (!snd->kctl_infos) + return -ENOMEM; + + snd->kctls = devm_kcalloc(&vdev->dev, snd->nkctls, sizeof(*snd->kctls), + GFP_KERNEL); + if (!snd->kctls) + return -ENOMEM; + + rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CTL_INFO, 0, snd->nkctls, + sizeof(*snd->kctl_infos), snd->kctl_infos); + if (rc) + return rc; + + for (i = 0; i < snd->nkctls; ++i) { + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; + unsigned int type = le32_to_cpu(kinfo->type); + + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { + rc = virtsnd_kctl_get_enum_items(snd, i); + if (rc) + return rc; + } + } + + return 0; +} + +/** + * virtsnd_kctl_build_devs() - Build ALSA control elements. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_kctl_build_devs(struct virtio_snd *snd) +{ + unsigned int cid; + + for (cid = 0; cid < snd->nkctls; ++cid) { + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; + struct virtio_kctl *kctl = &snd->kctls[cid]; + struct snd_kcontrol_new kctl_new; + unsigned int i; + int rc; + + memset(&kctl_new, 0, sizeof(kctl_new)); + + kctl_new.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kctl_new.name = kinfo->name; + kctl_new.index = le32_to_cpu(kinfo->index); + + for (i = 0; i < ARRAY_SIZE(g_v2a_access_map); ++i) + if (le32_to_cpu(kinfo->access) & (1 << i)) + kctl_new.access |= g_v2a_access_map[i]; + + if (kctl_new.access & (SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_WRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND)) { + kctl_new.access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + kctl_new.tlv.c = virtsnd_kctl_tlv_op; + } + + kctl_new.info = virtsnd_kctl_info; + kctl_new.get = virtsnd_kctl_get; + kctl_new.put = virtsnd_kctl_put; + kctl_new.private_value = cid; + + kctl->kctl = snd_ctl_new1(&kctl_new, snd); + if (!kctl->kctl) + return -ENOMEM; + + rc = snd_ctl_add(snd->card, kctl->kctl); + if (rc) + return rc; + } + + return 0; +} + +/** + * virtsnd_kctl_event() - Handle the control element event notification. + * @snd: VirtIO sound device. + * @event: VirtIO sound event. + * + * Context: Interrupt context. + */ +void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event) +{ + struct virtio_snd_ctl_event *kevent = + (struct virtio_snd_ctl_event *)event; + struct virtio_kctl *kctl; + unsigned int cid = le16_to_cpu(kevent->control_id); + unsigned int mask = 0; + unsigned int i; + + if (cid >= snd->nkctls) + return; + + for (i = 0; i < ARRAY_SIZE(g_v2a_mask_map); ++i) + if (le16_to_cpu(kevent->mask) & (1 << i)) + mask |= g_v2a_mask_map[i]; + + + kctl = &snd->kctls[cid]; + + snd_ctl_notify(snd->card, mask, &kctl->kctl->id); +} |