diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/control.c | 2 | ||||
-rw-r--r-- | sound/core/rawmidi.c | 2 | ||||
-rw-r--r-- | sound/core/seq/seq_clientmgr.c | 2 | ||||
-rw-r--r-- | sound/core/timer.c | 2 | ||||
-rw-r--r-- | sound/hda/hdac_component.c | 18 | ||||
-rw-r--r-- | sound/soc/cirrus/edb93xx.c | 2 | ||||
-rw-r--r-- | sound/soc/cirrus/ep93xx-ac97.c | 1 | ||||
-rw-r--r-- | sound/soc/cirrus/ep93xx-i2s.c | 3 | ||||
-rw-r--r-- | sound/soc/cirrus/simone.c | 2 | ||||
-rw-r--r-- | sound/soc/cirrus/snappercl15.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ab8500-codec.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/adau1977-spi.c | 11 | ||||
-rw-r--r-- | sound/soc/mxs/mxs-saif.c | 1 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc-ac97.c | 1 | ||||
-rw-r--r-- | sound/usb/Kconfig | 4 | ||||
-rw-r--r-- | sound/usb/Makefile | 2 | ||||
-rw-r--r-- | sound/usb/card.c | 14 | ||||
-rw-r--r-- | sound/usb/card.h | 3 | ||||
-rw-r--r-- | sound/usb/line6/toneport.c | 8 | ||||
-rw-r--r-- | sound/usb/media.c | 327 | ||||
-rw-r--r-- | sound/usb/media.h | 74 | ||||
-rw-r--r-- | sound/usb/mixer.h | 3 | ||||
-rw-r--r-- | sound/usb/pcm.c | 29 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 1 | ||||
-rw-r--r-- | sound/usb/stream.c | 2 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 6 |
26 files changed, 500 insertions, 26 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index fad7db402443..a5cc9a874062 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -54,7 +54,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) struct snd_ctl_file *ctl; int i, err; - err = nonseekable_open(inode, file); + err = stream_open(inode, file); if (err < 0) return err; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index c0690d1ecd55..4666bb366c0c 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -382,7 +382,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) return -EINVAL; /* invalid combination */ - err = nonseekable_open(inode, file); + err = stream_open(inode, file); if (err < 0) return err; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index c0227a672442..b3f593ee752e 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -339,7 +339,7 @@ static int snd_seq_open(struct inode *inode, struct file *file) struct snd_seq_user_client *user; int err; - err = nonseekable_open(inode, file); + err = stream_open(inode, file); if (err < 0) return err; diff --git a/sound/core/timer.c b/sound/core/timer.c index d23efec35660..e3973957b392 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1452,7 +1452,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) struct snd_timer_user *tu; int err; - err = nonseekable_open(inode, file); + err = stream_open(inode, file); if (err < 0) return err; diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c index 1ea51e3b942a..dfe7e755f594 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -81,17 +81,23 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) if (bus->display_power_status) { if (!bus->display_power_active) { + unsigned long cookie = -1; + if (acomp->ops->get_power) - acomp->ops->get_power(acomp->dev); + cookie = acomp->ops->get_power(acomp->dev); + snd_hdac_set_codec_wakeup(bus, true); snd_hdac_set_codec_wakeup(bus, false); - bus->display_power_active = true; + bus->display_power_active = cookie; } } else { if (bus->display_power_active) { + unsigned long cookie = bus->display_power_active; + if (acomp->ops->put_power) - acomp->ops->put_power(acomp->dev); - bus->display_power_active = false; + acomp->ops->put_power(acomp->dev, cookie); + + bus->display_power_active = 0; } } unlock: @@ -329,9 +335,9 @@ int snd_hdac_acomp_exit(struct hdac_bus *bus) return 0; if (WARN_ON(bus->display_power_active) && acomp->ops) - acomp->ops->put_power(acomp->dev); + acomp->ops->put_power(acomp->dev, bus->display_power_active); - bus->display_power_active = false; + bus->display_power_active = 0; bus->display_power_status = 0; component_master_del(dev, &hdac_component_master_ops); diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c index 3d011abaa266..f678b4c1514a 100644 --- a/sound/soc/cirrus/edb93xx.c +++ b/sound/soc/cirrus/edb93xx.c @@ -22,11 +22,11 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/soc/cirrus/ep93xx.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <asm/mach-types.h> -#include <mach/hardware.h> static int edb93xx_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index cd5a939ad608..c6bc447429af 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -24,6 +24,7 @@ #include <sound/soc.h> #include <linux/platform_data/dma-ep93xx.h> +#include <linux/soc/cirrus/ep93xx.h> #include "ep93xx-pcm.h" diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 0918c5da575a..beab7c516855 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -27,9 +27,8 @@ #include <sound/initval.h> #include <sound/soc.h> -#include <mach/hardware.h> -#include <mach/ep93xx-regs.h> #include <linux/platform_data/dma-ep93xx.h> +#include <linux/soc/cirrus/ep93xx.h> #include "ep93xx-pcm.h" diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c index 1ec661834e5a..cb850530331b 100644 --- a/sound/soc/cirrus/simone.c +++ b/sound/soc/cirrus/simone.c @@ -13,13 +13,13 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/soc/cirrus/ep93xx.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <asm/mach-types.h> -#include <mach/hardware.h> static struct snd_soc_dai_link simone_dai = { .name = "AC97", diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c index 11ff7b2672b2..dea4909154c8 100644 --- a/sound/soc/cirrus/snappercl15.c +++ b/sound/soc/cirrus/snappercl15.c @@ -13,12 +13,12 @@ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/soc/cirrus/ep93xx.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <asm/mach-types.h> -#include <mach/hardware.h> #include "../codecs/tlv320aic23.h" diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 87616b126018..19e7f0333c2a 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1062,10 +1062,10 @@ static void anc_iir(struct snd_soc_component *component, unsigned int bnk, snd_soc_component_update_bits(component, AB8500_ANCCONF1, BIT(AB8500_ANCCONF1_ANCIIRINIT), BIT(AB8500_ANCCONF1_ANCIIRINIT)); - usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); snd_soc_component_update_bits(component, AB8500_ANCCONF1, BIT(AB8500_ANCCONF1_ANCIIRINIT), 0); - usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); } else { snd_soc_component_update_bits(component, AB8500_ANCCONF1, BIT(AB8500_ANCCONF1_ANCIIRUPDATE), diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c index 84ffbde9583f..2baf61567b59 100644 --- a/sound/soc/codecs/adau1977-spi.c +++ b/sound/soc/codecs/adau1977-spi.c @@ -10,6 +10,8 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/spi/spi.h> #include <sound/soc.h> @@ -54,9 +56,18 @@ static const struct spi_device_id adau1977_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); +static const struct of_device_id adau1977_spi_of_match[] = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); + static struct spi_driver adau1977_spi_driver = { .driver = { .name = "adau1977", + .of_match_table = of_match_ptr(adau1977_spi_of_match), }, .probe = adau1977_spi_probe, .id_table = adau1977_spi_ids, diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 156aa7c00787..2bbb92ed96c8 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -26,6 +26,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> +#include <linux/io.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 1cfca698ae4b..b0fa285c7ba2 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -102,7 +102,6 @@ static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97) u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY; __raw_writel(ACCTL_ENLINK, base + ACCTLDIS); - mmiowb(); udelay(1); __raw_writel(ACCTL_ENLINK, base + ACCTLEN); /* wait for primary codec ready status */ diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index f61b5662bb89..6319b544ba3a 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -15,6 +15,7 @@ config SND_USB_AUDIO select SND_RAWMIDI select SND_PCM select BITREVERSE + select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO) help Say Y here to include support for USB audio and USB MIDI devices. @@ -22,6 +23,9 @@ config SND_USB_AUDIO To compile this driver as a module, choose M here: the module will be called snd-usb-audio. +config SND_USB_AUDIO_USE_MEDIA_CONTROLLER + bool + config SND_USB_UA101 tristate "Edirol UA-101/UA-1000 driver" select SND_PCM diff --git a/sound/usb/Makefile b/sound/usb/Makefile index d330f74c90e6..e1ce257ab705 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -18,6 +18,8 @@ snd-usb-audio-objs := card.o \ quirks.o \ stream.o +snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o + snd-usbmidi-lib-objs := midi.o # Toplevel Module Dependency diff --git a/sound/usb/card.c b/sound/usb/card.c index 719e10034553..04465d581204 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -68,6 +68,7 @@ #include "format.h" #include "power.h" #include "stream.h" +#include "media.h" MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("USB Audio"); @@ -673,6 +674,11 @@ static int usb_audio_probe(struct usb_interface *intf, if (err < 0) goto __error; + if (quirk && quirk->shares_media_device) { + /* don't want to fail when snd_media_device_create() fails */ + snd_media_device_create(chip, intf); + } + usb_chip[chip->index] = chip; chip->num_interfaces++; usb_set_intfdata(intf, chip); @@ -732,6 +738,14 @@ static void usb_audio_disconnect(struct usb_interface *intf) list_for_each(p, &chip->midi_list) { snd_usbmidi_disconnect(p); } + /* + * Nice to check quirk && quirk->shares_media_device and + * then call the snd_media_device_delete(). Don't have + * access to the quirk here. snd_media_device_delete() + * accesses mixer_list + */ + snd_media_device_delete(chip); + /* release mixer resources */ list_for_each_entry(mixer, &chip->mixer_list, list) { snd_usb_mixer_disconnect(mixer); diff --git a/sound/usb/card.h b/sound/usb/card.h index 79fa2a19fb7b..2991b9986f66 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -109,6 +109,8 @@ struct snd_usb_endpoint { struct list_head list; }; +struct media_ctl; + struct snd_usb_substream { struct snd_usb_stream *stream; struct usb_device *dev; @@ -161,6 +163,7 @@ struct snd_usb_substream { } dsd_dop; bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */ + struct media_ctl *media_ctl; }; struct snd_usb_stream { diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index 325b07b98b3c..e28368d8eba2 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -292,8 +292,8 @@ static bool toneport_has_led(struct usb_line6_toneport *toneport) } } -static const char * const led_colors[2] = { "red", "green" }; -static const int led_init_vals[2] = { 0x00, 0x26 }; +static const char * const toneport_led_colors[2] = { "red", "green" }; +static const int toneport_led_init_vals[2] = { 0x00, 0x26 }; static void toneport_update_led(struct usb_line6_toneport *toneport) { @@ -321,9 +321,9 @@ static int toneport_init_leds(struct usb_line6_toneport *toneport) led->toneport = toneport; snprintf(led->name, sizeof(led->name), "%s::%s", - dev_name(dev), led_colors[i]); + dev_name(dev), toneport_led_colors[i]); leddev->name = led->name; - leddev->brightness = led_init_vals[i]; + leddev->brightness = toneport_led_init_vals[i]; leddev->max_brightness = 0x26; leddev->brightness_set = toneport_led_brightness_set; err = led_classdev_register(dev, leddev); diff --git a/sound/usb/media.c b/sound/usb/media.c new file mode 100644 index 000000000000..812017eacbcf --- /dev/null +++ b/sound/usb/media.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * media.c - Media Controller specific ALSA driver code + * + * Copyright (c) 2019 Shuah Khan <shuah@kernel.org> + * + */ + +/* + * This file adds Media Controller support to the ALSA driver + * to use the Media Controller API to share the tuner with DVB + * and V4L2 drivers that control the media device. + * + * The media device is created based on the existing quirks framework. + * Using this approach, the media controller API usage can be added for + * a specific device. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <sound/pcm.h> +#include <sound/core.h> + +#include "usbaudio.h" +#include "card.h" +#include "mixer.h" +#include "media.h" + +int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm, + int stream) +{ + struct media_device *mdev; + struct media_ctl *mctl; + struct device *pcm_dev = &pcm->streams[stream].dev; + u32 intf_type; + int ret = 0; + u16 mixer_pad; + struct media_entity *entity; + + mdev = subs->stream->chip->media_dev; + if (!mdev) + return 0; + + if (subs->media_ctl) + return 0; + + /* allocate media_ctl */ + mctl = kzalloc(sizeof(*mctl), GFP_KERNEL); + if (!mctl) + return -ENOMEM; + + mctl->media_dev = mdev; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + intf_type = MEDIA_INTF_T_ALSA_PCM_PLAYBACK; + mctl->media_entity.function = MEDIA_ENT_F_AUDIO_PLAYBACK; + mctl->media_pad.flags = MEDIA_PAD_FL_SOURCE; + mixer_pad = 1; + } else { + intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE; + mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE; + mctl->media_pad.flags = MEDIA_PAD_FL_SINK; + mixer_pad = 2; + } + mctl->media_entity.name = pcm->name; + media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad); + ret = media_device_register_entity(mctl->media_dev, + &mctl->media_entity); + if (ret) + goto free_mctl; + + mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0, + MAJOR(pcm_dev->devt), + MINOR(pcm_dev->devt)); + if (!mctl->intf_devnode) { + ret = -ENOMEM; + goto unregister_entity; + } + mctl->intf_link = media_create_intf_link(&mctl->media_entity, + &mctl->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!mctl->intf_link) { + ret = -ENOMEM; + goto devnode_remove; + } + + /* create link between mixer and audio */ + media_device_for_each_entity(entity, mdev) { + switch (entity->function) { + case MEDIA_ENT_F_AUDIO_MIXER: + ret = media_create_pad_link(entity, mixer_pad, + &mctl->media_entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + goto remove_intf_link; + break; + } + } + + subs->media_ctl = mctl; + return 0; + +remove_intf_link: + media_remove_intf_link(mctl->intf_link); +devnode_remove: + media_devnode_remove(mctl->intf_devnode); +unregister_entity: + media_device_unregister_entity(&mctl->media_entity); +free_mctl: + kfree(mctl); + return ret; +} + +void snd_media_stream_delete(struct snd_usb_substream *subs) +{ + struct media_ctl *mctl = subs->media_ctl; + + if (mctl) { + struct media_device *mdev; + + mdev = mctl->media_dev; + if (mdev && media_devnode_is_registered(mdev->devnode)) { + media_devnode_remove(mctl->intf_devnode); + media_device_unregister_entity(&mctl->media_entity); + media_entity_cleanup(&mctl->media_entity); + } + kfree(mctl); + subs->media_ctl = NULL; + } +} + +int snd_media_start_pipeline(struct snd_usb_substream *subs) +{ + struct media_ctl *mctl = subs->media_ctl; + int ret = 0; + + if (!mctl) + return 0; + + mutex_lock(&mctl->media_dev->graph_mutex); + if (mctl->media_dev->enable_source) + ret = mctl->media_dev->enable_source(&mctl->media_entity, + &mctl->media_pipe); + mutex_unlock(&mctl->media_dev->graph_mutex); + return ret; +} + +void snd_media_stop_pipeline(struct snd_usb_substream *subs) +{ + struct media_ctl *mctl = subs->media_ctl; + + if (!mctl) + return; + + mutex_lock(&mctl->media_dev->graph_mutex); + if (mctl->media_dev->disable_source) + mctl->media_dev->disable_source(&mctl->media_entity); + mutex_unlock(&mctl->media_dev->graph_mutex); +} + +static int snd_media_mixer_init(struct snd_usb_audio *chip) +{ + struct device *ctl_dev = &chip->card->ctl_dev; + struct media_intf_devnode *ctl_intf; + struct usb_mixer_interface *mixer; + struct media_device *mdev = chip->media_dev; + struct media_mixer_ctl *mctl; + u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL; + int ret; + + if (!mdev) + return -ENODEV; + + ctl_intf = chip->ctl_intf_media_devnode; + if (!ctl_intf) { + ctl_intf = media_devnode_create(mdev, intf_type, 0, + MAJOR(ctl_dev->devt), + MINOR(ctl_dev->devt)); + if (!ctl_intf) + return -ENOMEM; + chip->ctl_intf_media_devnode = ctl_intf; + } + + list_for_each_entry(mixer, &chip->mixer_list, list) { + + if (mixer->media_mixer_ctl) + continue; + + /* allocate media_mixer_ctl */ + mctl = kzalloc(sizeof(*mctl), GFP_KERNEL); + if (!mctl) + return -ENOMEM; + + mctl->media_dev = mdev; + mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER; + mctl->media_entity.name = chip->card->mixername; + mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK; + mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE; + mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE; + media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX, + mctl->media_pad); + ret = media_device_register_entity(mctl->media_dev, + &mctl->media_entity); + if (ret) { + kfree(mctl); + return ret; + } + + mctl->intf_link = media_create_intf_link(&mctl->media_entity, + &ctl_intf->intf, + MEDIA_LNK_FL_ENABLED); + if (!mctl->intf_link) { + media_device_unregister_entity(&mctl->media_entity); + media_entity_cleanup(&mctl->media_entity); + kfree(mctl); + return -ENOMEM; + } + mctl->intf_devnode = ctl_intf; + mixer->media_mixer_ctl = mctl; + } + return 0; +} + +static void snd_media_mixer_delete(struct snd_usb_audio *chip) +{ + struct usb_mixer_interface *mixer; + struct media_device *mdev = chip->media_dev; + + if (!mdev) + return; + + list_for_each_entry(mixer, &chip->mixer_list, list) { + struct media_mixer_ctl *mctl; + + mctl = mixer->media_mixer_ctl; + if (!mixer->media_mixer_ctl) + continue; + + if (media_devnode_is_registered(mdev->devnode)) { + media_device_unregister_entity(&mctl->media_entity); + media_entity_cleanup(&mctl->media_entity); + } + kfree(mctl); + mixer->media_mixer_ctl = NULL; + } + if (media_devnode_is_registered(mdev->devnode)) + media_devnode_remove(chip->ctl_intf_media_devnode); + chip->ctl_intf_media_devnode = NULL; +} + +int snd_media_device_create(struct snd_usb_audio *chip, + struct usb_interface *iface) +{ + struct media_device *mdev; + struct usb_device *usbdev = interface_to_usbdev(iface); + int ret = 0; + + /* usb-audio driver is probed for each usb interface, and + * there are multiple interfaces per device. Avoid calling + * media_device_usb_allocate() each time usb_audio_probe() + * is called. Do it only once. + */ + if (chip->media_dev) { + mdev = chip->media_dev; + goto snd_mixer_init; + } + + mdev = media_device_usb_allocate(usbdev, KBUILD_MODNAME, THIS_MODULE); + if (IS_ERR(mdev)) + return -ENOMEM; + + /* save media device - avoid lookups */ + chip->media_dev = mdev; + +snd_mixer_init: + /* Create media entities for mixer and control dev */ + ret = snd_media_mixer_init(chip); + /* media_device might be registered, print error and continue */ + if (ret) + dev_err(&usbdev->dev, + "Couldn't create media mixer entities. Error: %d\n", + ret); + + if (!media_devnode_is_registered(mdev->devnode)) { + /* dont'register if snd_media_mixer_init() failed */ + if (ret) + goto create_fail; + + /* register media_device */ + ret = media_device_register(mdev); +create_fail: + if (ret) { + snd_media_mixer_delete(chip); + media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE); + /* clear saved media_dev */ + chip->media_dev = NULL; + dev_err(&usbdev->dev, + "Couldn't register media device. Error: %d\n", + ret); + return ret; + } + } + + return ret; +} + +void snd_media_device_delete(struct snd_usb_audio *chip) +{ + struct media_device *mdev = chip->media_dev; + struct snd_usb_stream *stream; + + /* release resources */ + list_for_each_entry(stream, &chip->pcm_list, list) { + snd_media_stream_delete(&stream->substream[0]); + snd_media_stream_delete(&stream->substream[1]); + } + + snd_media_mixer_delete(chip); + + if (mdev) { + media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE); + chip->media_dev = NULL; + } +} diff --git a/sound/usb/media.h b/sound/usb/media.h new file mode 100644 index 000000000000..f5bdec1d602f --- /dev/null +++ b/sound/usb/media.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * media.h - Media Controller specific ALSA driver code + * + * Copyright (c) 2019 Shuah Khan <shuah@kernel.org> + * + */ + +/* + * This file adds Media Controller support to the ALSA driver + * to use the Media Controller API to share the tuner with DVB + * and V4L2 drivers that control the media device. + * + * The media device is created based on the existing quirks framework. + * Using this approach, the media controller API usage can be added for + * a specific device. + */ +#ifndef __MEDIA_H + +#ifdef CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER + +#include <linux/media.h> +#include <media/media-device.h> +#include <media/media-entity.h> +#include <media/media-dev-allocator.h> +#include <sound/asound.h> + +struct media_ctl { + struct media_device *media_dev; + struct media_entity media_entity; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct media_pad media_pad; + struct media_pipeline media_pipe; +}; + +/* + * One source pad each for SNDRV_PCM_STREAM_CAPTURE and + * SNDRV_PCM_STREAM_PLAYBACK. One for sink pad to link + * to AUDIO Source + */ +#define MEDIA_MIXER_PAD_MAX (SNDRV_PCM_STREAM_LAST + 2) + +struct media_mixer_ctl { + struct media_device *media_dev; + struct media_entity media_entity; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct media_pad media_pad[MEDIA_MIXER_PAD_MAX]; + struct media_pipeline media_pipe; +}; + +int snd_media_device_create(struct snd_usb_audio *chip, + struct usb_interface *iface); +void snd_media_device_delete(struct snd_usb_audio *chip); +int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm, + int stream); +void snd_media_stream_delete(struct snd_usb_substream *subs); +int snd_media_start_pipeline(struct snd_usb_substream *subs); +void snd_media_stop_pipeline(struct snd_usb_substream *subs); +#else +static inline int snd_media_device_create(struct snd_usb_audio *chip, + struct usb_interface *iface) + { return 0; } +static inline void snd_media_device_delete(struct snd_usb_audio *chip) { } +static inline int snd_media_stream_init(struct snd_usb_substream *subs, + struct snd_pcm *pcm, int stream) + { return 0; } +static inline void snd_media_stream_delete(struct snd_usb_substream *subs) { } +static inline int snd_media_start_pipeline(struct snd_usb_substream *subs) + { return 0; } +static inline void snd_media_stop_pipeline(struct snd_usb_substream *subs) { } +#endif +#endif /* __MEDIA_H */ diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 3d12af8bf191..394cd9107507 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -4,6 +4,8 @@ #include <sound/info.h> +struct media_mixer_ctl; + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -23,6 +25,7 @@ struct usb_mixer_interface { struct urb *rc_urb; struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; + struct media_mixer_ctl *media_mixer_ctl; bool disconnected; }; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 056af0a57b22..5d8494b2a026 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -35,6 +35,7 @@ #include "pcm.h" #include "clock.h" #include "power.h" +#include "media.h" #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 @@ -787,6 +788,10 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct audioformat *fmt; int ret; + ret = snd_media_start_pipeline(subs); + if (ret) + return ret; + if (snd_usb_use_vmalloc) ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); @@ -794,7 +799,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) - return ret; + goto stop_pipeline; subs->pcm_format = params_format(hw_params); subs->period_bytes = params_period_bytes(hw_params); @@ -808,12 +813,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, dev_dbg(&subs->dev->dev, "cannot set format: format = %#x, rate = %d, channels = %d\n", subs->pcm_format, subs->cur_rate, subs->channels); - return -EINVAL; + ret = -EINVAL; + goto stop_pipeline; } ret = snd_usb_lock_shutdown(subs->stream->chip); if (ret < 0) - return ret; + goto stop_pipeline; ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); if (ret < 0) @@ -829,6 +835,12 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, unlock: snd_usb_unlock_shutdown(subs->stream->chip); + if (ret < 0) + goto stop_pipeline; + return ret; + + stop_pipeline: + snd_media_stop_pipeline(subs); return ret; } @@ -841,6 +853,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) { struct snd_usb_substream *subs = substream->runtime->private_data; + snd_media_stop_pipeline(subs); subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; @@ -1313,6 +1326,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = &as->substream[direction]; + int ret; subs->interface = -1; subs->altset_idx = 0; @@ -1326,7 +1340,13 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) subs->dsd_dop.channel = 0; subs->dsd_dop.marker = 1; - return setup_hw_info(runtime, subs); + ret = setup_hw_info(runtime, subs); + if (ret == 0) { + ret = snd_media_stream_init(subs, as->pcm, direction); + if (ret) + snd_usb_autosuspend(subs->stream->chip); + } + return ret; } static int snd_usb_pcm_close(struct snd_pcm_substream *substream) @@ -1337,6 +1357,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) int ret; stop_endpoints(subs, true); + snd_media_stop_pipeline(subs); if (!as->chip->keep_iface && subs->interface >= 0 && diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 629b84532648..5600143ff660 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2971,6 +2971,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .product_name = pname, \ .ifnum = QUIRK_ANY_INTERFACE, \ .type = QUIRK_AUDIO_ALIGN_TRANSFER, \ + .shares_media_device = 1, \ } \ } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index d9e3de495c16..9f1623e37fb3 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -38,6 +38,7 @@ #include "clock.h" #include "stream.h" #include "power.h" +#include "media.h" /* * free a substream @@ -55,6 +56,7 @@ static void free_substream(struct snd_usb_substream *subs) } kfree(subs->rate_list.list); kfree(subs->str_pd); + snd_media_stream_delete(subs); } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b9faeca645fd..0968a45c8925 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -30,6 +30,9 @@ * */ +struct media_device; +struct media_intf_devnode; + struct snd_usb_audio { int index; struct usb_device *dev; @@ -66,6 +69,8 @@ struct snd_usb_audio { */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ + struct media_device *media_dev; + struct media_intf_devnode *ctl_intf_media_devnode; }; #define usb_audio_err(chip, fmt, args...) \ @@ -117,6 +122,7 @@ struct snd_usb_audio_quirk { const char *profile_name; /* override the card->longname */ int16_t ifnum; uint16_t type; + bool shares_media_device; const void *data; }; |