[alsa-devel] [PATCH v2 26/31] sound/usb: Update ALSA driver to use Managed Media Controller API

Takashi Iwai tiwai at suse.de
Fri Jan 15 14:38:08 CET 2016


On Thu, 14 Jan 2016 20:52:28 +0100,
Shuah Khan wrote:
> 
> Change ALSA driver to use Managed Media Managed Controller
> API to share tuner with DVB and V4L2 drivers that control
> AU0828 media device.  Media device is created based on a
> newly added field value in the struct snd_usb_audio_quirk.
> Using this approach, the media controller API usage can be
> added for a specific device. In this patch, Media Controller
> API is enabled for AU0828 hw. snd_usb_create_quirk() will
> check this new field, if set will create a media device using
> media_device_get_devres() interface.
> 
> media_device_get_devres() will allocate a new media device
> devres or return an existing one, if it finds one.
> 
> During probe, media usb driver could have created the media
> device devres. It will then initialze (if necessary) and
> register the media device if it isn't already initialized
> and registered. Media device unregister is done from
> usb_audio_disconnect().
> 
> During probe, media usb driver could have created the
> media device devres. It will then register the media
> device if it isn't already registered. Media device
> unregister is done from usb_audio_disconnect().
> 
> New structure media_ctl is added to group the new
> fields to support media entity and links. This new
> structure is added to struct snd_usb_substream.
> 
> A new entity_notify hook and a new ALSA capture media
> entity are registered from snd_usb_pcm_open() after
> setting up hardware information for the PCM device.
> 
> When a new entity is registered, Media Controller API
> interface media_device_register_entity() invokes all
> registered entity_notify hooks for the media device.
> ALSA entity_notify hook parses all the entity list to
> find a link from decoder it ALSA entity. This indicates
> that the bridge driver created a link from decoder to
> ALSA capture entity.
> 
> ALSA will attempt to enable the tuner to link the tuner
> to the decoder calling enable_source handler if one is
> provided by the bridge driver prior to starting Media
> pipeline from snd_usb_hw_params(). If enable_source returns
> with tuner busy condition, then snd_usb_hw_params() will fail
> with -EBUSY. Media pipeline is stopped from snd_usb_hw_free().
> 
> Signed-off-by: Shuah Khan <shuahkh at osg.samsung.com>
> ---
> Changes since v1: Addressed Takasi Iwai's comments on v1
> - Move config defines to Kconfig and add logic
>   to Makefile to conditionally compile media.c
> - Removed extra includes from media.c
> - Added snd_usb_autosuspend() in error leg
> - Removed debug related code that was missed in v1
> 
>  sound/usb/Kconfig        |   7 ++
>  sound/usb/Makefile       |   4 +
>  sound/usb/card.c         |   7 ++
>  sound/usb/card.h         |   1 +
>  sound/usb/media.c        | 190 +++++++++++++++++++++++++++++++++++++++++++++++
>  sound/usb/media.h        |  56 ++++++++++++++
>  sound/usb/pcm.c          |  28 +++++--
>  sound/usb/quirks-table.h |   1 +
>  sound/usb/quirks.c       |   5 ++
>  sound/usb/stream.c       |   2 +
>  sound/usb/usbaudio.h     |   1 +
>  11 files changed, 297 insertions(+), 5 deletions(-)
>  create mode 100644 sound/usb/media.c
>  create mode 100644 sound/usb/media.h
> 
> diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
> index a452ad7..8d5cdab 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 || MEDIA_SUPPORT_MODULE)

This looks wrong as a Kconfig syntax.  "... if MEDIA_CONTROLLER"
should work, I suppose?

Above all, can MEDIA_CONTROLLER be tristate?  It's currently a bool.
If it's a tristate, it causes a problem wrt dependency.  Imagine
USB-audio is built-in while MC is a module.

>  	help
>  	  Say Y here to include support for USB audio and USB MIDI
>  	  devices.
> @@ -22,6 +23,12 @@ 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 "USB Audio/MIDI driver with Media Controller Support"
> +	depends on SND_USB_AUDIO && MEDIA_CONTROLLER
> +	depends on MEDIA_SUPPORT || MEDIA_SUPPORT_MODULE

Hmm, it's superfluous to have both this depends and the reverse-select
of the above.  (And again MEDIA_SUPPORT_MODULE looks bogus.)

> +	default y
> +
>  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 2d2d122..3113c45 100644
> --- a/sound/usb/Makefile
> +++ b/sound/usb/Makefile
> @@ -15,6 +15,10 @@ snd-usb-audio-objs := 	card.o \
>  			quirks.o \
>  			stream.o
>  
> +ifeq ($(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER),y)
> +  snd-usb-audio-objs += media.o
> +endif

A better form is like

snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.


> +
>  snd-usbmidi-lib-objs := midi.o
>  
>  # Toplevel Module Dependency
> diff --git a/sound/usb/card.c b/sound/usb/card.c
> index 18f5664..1a63851 100644
> --- a/sound/usb/card.c
> +++ b/sound/usb/card.c
> @@ -66,6 +66,7 @@
>  #include "format.h"
>  #include "power.h"
>  #include "stream.h"
> +#include "media.h"
>  
>  MODULE_AUTHOR("Takashi Iwai <tiwai at suse.de>");
>  MODULE_DESCRIPTION("USB Audio");
> @@ -621,6 +622,12 @@ static void usb_audio_disconnect(struct usb_interface *intf)
>  		list_for_each_entry(mixer, &chip->mixer_list, list) {
>  			snd_usb_mixer_disconnect(mixer);
>  		}
> +		/*
> +		 * Nice to check quirk && quirk->media_device
> +		 * need some special handlings. Doesn't look like
> +		 * we have access to quirk here
> +		*/
> +		media_device_delete(intf);
>  	}
>  
>  	chip->num_interfaces--;
> diff --git a/sound/usb/card.h b/sound/usb/card.h
> index 71778ca..c15a03c 100644
> --- a/sound/usb/card.h
> +++ b/sound/usb/card.h
> @@ -156,6 +156,7 @@ struct snd_usb_substream {
>  	} dsd_dop;
>  
>  	bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
> +	void *media_ctl;
>  };
>  
>  struct snd_usb_stream {
> diff --git a/sound/usb/media.c b/sound/usb/media.c
> new file mode 100644
> index 0000000..644b6e8
> --- /dev/null
> +++ b/sound/usb/media.c
> @@ -0,0 +1,190 @@
> +/*
> + * media.c - Media Controller specific ALSA driver code
> + *
> + * Copyright (c) 2015 Shuah Khan <shuahkh at osg.samsung.com>
> + * Copyright (c) 2015 Samsung Electronics Co., Ltd.
> + *
> + * This file is released under the GPLv2.
> + */
> +
> +/*
> + * This file adds Media Controller support to ALSA driver
> + * to use the Media Controller API to share tuner with DVB
> + * and V4L2 drivers that control media device. Media device
> + * is created based on 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 "usbaudio.h"
> +#include "card.h"
> +#include "media.h"
> +
> +int 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;
> +
> +	mdev = media_device_get_devres(&usbdev->dev);
> +	if (!mdev)
> +		return -ENOMEM;
> +	if (!mdev->dev) {
> +		/* register media device */
> +		mdev->dev = &usbdev->dev;
> +		if (usbdev->product)
> +			strlcpy(mdev->model, usbdev->product,
> +				sizeof(mdev->model));
> +		if (usbdev->serial)
> +			strlcpy(mdev->serial, usbdev->serial,
> +				sizeof(mdev->serial));
> +		strcpy(mdev->bus_info, usbdev->devpath);
> +		mdev->hw_revision = le16_to_cpu(usbdev->descriptor.bcdDevice);
> +		ret = media_device_init(mdev);
> +		if (ret) {
> +			dev_err(&usbdev->dev,
> +				"Couldn't create a media device. Error: %d\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +	if (!media_devnode_is_registered(&mdev->devnode)) {
> +		ret = media_device_register(mdev);
> +		if (ret) {
> +			dev_err(&usbdev->dev,
> +				"Couldn't register media device. Error: %d\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +void media_device_delete(struct usb_interface *iface)
> +{
> +	struct media_device *mdev;
> +	struct usb_device *usbdev = interface_to_usbdev(iface);
> +
> +	mdev = media_device_find_devres(&usbdev->dev);
> +	if (mdev && media_devnode_is_registered(&mdev->devnode))
> +		media_device_unregister(mdev);
> +}
> +
> +static int media_enable_source(struct media_ctl *mctl)
> +{
> +	if (mctl && mctl->media_dev->enable_source)
> +		return mctl->media_dev->enable_source(&mctl->media_entity,
> +						      &mctl->media_pipe);
> +	return 0;
> +}
> +
> +static void media_disable_source(struct media_ctl *mctl)
> +{
> +	if (mctl && mctl->media_dev->disable_source)
> +		mctl->media_dev->disable_source(&mctl->media_entity);
> +}
> +
> +int 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;
> +
> +	mdev = media_device_find_devres(&subs->dev->dev);
> +	if (!mdev)
> +		return -ENODEV;
> +
> +	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;
> +	} else {
> +		intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE;
> +		mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE;
> +	}
> +	mctl->media_entity.name = pcm->name;
> +	mctl->media_entity.info.dev.major = MAJOR(pcm_dev->devt);
> +	mctl->media_entity.info.dev.minor = MINOR(pcm_dev->devt);
> +	mctl->media_pad.flags = MEDIA_PAD_FL_SINK;
> +	media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad);
> +	ret =  media_device_register_entity(mctl->media_dev,
> +					    &mctl->media_entity);
> +	if (ret) {
> +		kfree(mctl);
> +		return ret;
> +	}
> +	mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0,
> +						  MAJOR(pcm_dev->devt),
> +						  MINOR(pcm_dev->devt));
> +	if (!mctl->intf_devnode) {
> +		media_device_unregister_entity(&mctl->media_entity);
> +		kfree(mctl);
> +		return -ENOMEM;
> +	}
> +	mctl->intf_link = media_create_intf_link(&mctl->media_entity,
> +						 &mctl->intf_devnode->intf,
> +						 MEDIA_LNK_FL_ENABLED);
> +	if (!mctl->intf_link) {
> +		media_devnode_remove(mctl->intf_devnode);
> +		media_device_unregister_entity(&mctl->media_entity);
> +		kfree(mctl);
> +		return -ENOMEM;
> +	}
> +	subs->media_ctl = (void *) mctl;

The cast to a void pointer is superfluous.


> +	return 0;
> +}
> +
> +void media_stream_delete(struct snd_usb_substream *subs)
> +{
> +	struct media_ctl *mctl = (struct media_ctl *) subs->media_ctl;

The cast from a void pointer is superfluous, too.

> +
> +	if (mctl && mctl->media_dev) {
> +		struct media_device *mdev;
> +
> +		mdev = media_device_find_devres(&subs->dev->dev);
> +		if (mdev) {
> +			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 media_start_pipeline(struct snd_usb_substream *subs)
> +{
> +	struct media_ctl *mctl = (struct media_ctl *) subs->media_ctl;
> +
> +	if (mctl)
> +		return media_enable_source(mctl);
> +	return 0;
> +}
> +
> +void media_stop_pipeline(struct snd_usb_substream *subs)
> +{
> +	struct media_ctl *mctl = (struct media_ctl *) subs->media_ctl;
> +
> +	if (mctl)
> +		media_disable_source(mctl);
> +}
> diff --git a/sound/usb/media.h b/sound/usb/media.h
> new file mode 100644
> index 0000000..00b77e6
> --- /dev/null
> +++ b/sound/usb/media.h
> @@ -0,0 +1,56 @@
> +/*
> + * media.h - Media Controller specific ALSA driver code
> + *
> + * Copyright (c) 2015 Shuah Khan <shuahkh at osg.samsung.com>
> + * Copyright (c) 2015 Samsung Electronics Co., Ltd.
> + *
> + * This file is released under the GPLv2.
> + */
> +
> +/*
> + * This file adds Media Controller support to ALSA driver
> + * to use the Media Controller API to share tuner with DVB
> + * and V4L2 drivers that control media device. Media device
> + * is created based on existing quirks framework. Using this
> + * approach, the media controller API usage can be added for
> + * a specific device.
> +*/
> +#ifndef __MEDIA_H
> +
> +#if defined(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) || \
> +	defined(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER_MODULE)

The kconfig is a bool.


thanks,

Takashi

> +#include <media/media-device.h>
> +#include <media/media-entity.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;
> +};
> +
> +int media_device_create(struct snd_usb_audio *chip,
> +			struct usb_interface *iface);
> +void media_device_delete(struct usb_interface *iface);
> +int media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
> +			int stream);
> +void media_stream_delete(struct snd_usb_substream *subs);
> +int media_start_pipeline(struct snd_usb_substream *subs);
> +void media_stop_pipeline(struct snd_usb_substream *subs);
> +#else
> +static inline int media_device_create(struct snd_usb_audio *chip,
> +				      struct usb_interface *iface)
> +						{ return 0; }
> +static inline void media_device_delete(struct usb_interface *iface) { }
> +static inline int media_stream_init(struct snd_usb_substream *subs,
> +					struct snd_pcm *pcm, int stream)
> +						{ return 0; }
> +static inline void media_stream_delete(struct snd_usb_substream *subs) { }
> +static inline int media_start_pipeline(struct snd_usb_substream *subs)
> +					{ return 0; }
> +static inline void media_stop_pipeline(struct snd_usb_substream *subs) { }
> +#endif
> +#endif /* __MEDIA_H */
> diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
> index 9245f52..c3b8486 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
> @@ -715,10 +716,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
>  	struct audioformat *fmt;
>  	int ret;
>  
> +	ret = media_start_pipeline(subs);
> +	if (ret)
> +		return ret;
> +
>  	ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
>  					       params_buffer_bytes(hw_params));
>  	if (ret < 0)
> -		return ret;
> +		goto err_ret;
>  
>  	subs->pcm_format = params_format(hw_params);
>  	subs->period_bytes = params_period_bytes(hw_params);
> @@ -732,22 +737,27 @@ 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 err_ret;
>  	}
>  
>  	ret = snd_usb_lock_shutdown(subs->stream->chip);
>  	if (ret < 0)
> -		return ret;
> +		goto err_ret;
>  	ret = set_format(subs, fmt);
>  	snd_usb_unlock_shutdown(subs->stream->chip);
>  	if (ret < 0)
> -		return ret;
> +		goto err_ret;
>  
>  	subs->interface = fmt->iface;
>  	subs->altset_idx = fmt->altset_idx;
>  	subs->need_setup_ep = true;
>  
>  	return 0;
> +
> +err_ret:
> +	media_stop_pipeline(subs);
> +	return ret;
>  }
>  
>  /*
> @@ -759,6 +769,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
>  {
>  	struct snd_usb_substream *subs = substream->runtime->private_data;
>  
> +	media_stop_pipeline(subs);
>  	subs->cur_audiofmt = NULL;
>  	subs->cur_rate = 0;
>  	subs->period_bytes = 0;
> @@ -1219,6 +1230,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
>  	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;
> @@ -1232,7 +1244,12 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
>  	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 = 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, int direction)
> @@ -1241,6 +1258,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
>  	struct snd_usb_substream *subs = &as->substream[direction];
>  
>  	stop_endpoints(subs, true);
> +	media_stop_pipeline(subs);
>  
>  	if (subs->interface >= 0 &&
>  	    !snd_usb_lock_shutdown(subs->stream->chip)) {
> diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
> index 1a1e2e4..8f7b71b 100644
> --- a/sound/usb/quirks-table.h
> +++ b/sound/usb/quirks-table.h
> @@ -2875,6 +2875,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
>  		.product_name = pname, \
>  		.ifnum = QUIRK_ANY_INTERFACE, \
>  		.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
> +		.media_device = 1, \
>  	} \
>  }
>  
> diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
> index 5ca80e7..b0d9d13 100644
> --- a/sound/usb/quirks.c
> +++ b/sound/usb/quirks.c
> @@ -36,6 +36,7 @@
>  #include "pcm.h"
>  #include "clock.h"
>  #include "stream.h"
> +#include "media.h"
>  
>  /*
>   * handle the quirks for the contained interfaces
> @@ -545,6 +546,10 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
>  		[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
>  	};
>  
> +	if (quirk->media_device) {
> +		/* don't want to fail when media_device_create() fails */
> +		media_device_create(chip, iface);
> +	}
>  	if (quirk->type < QUIRK_TYPE_COUNT) {
>  		return quirk_funcs[quirk->type](chip, iface, driver, quirk);
>  	} else {
> diff --git a/sound/usb/stream.c b/sound/usb/stream.c
> index 8ee14f2..789e515 100644
> --- a/sound/usb/stream.c
> +++ b/sound/usb/stream.c
> @@ -36,6 +36,7 @@
>  #include "format.h"
>  #include "clock.h"
>  #include "stream.h"
> +#include "media.h"
>  
>  /*
>   * free a substream
> @@ -52,6 +53,7 @@ static void free_substream(struct snd_usb_substream *subs)
>  		kfree(fp);
>  	}
>  	kfree(subs->rate_list.list);
> +	media_stream_delete(subs);
>  }
>  
>  
> diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
> index 15a1271..e3fac29 100644
> --- a/sound/usb/usbaudio.h
> +++ b/sound/usb/usbaudio.h
> @@ -109,6 +109,7 @@ struct snd_usb_audio_quirk {
>  	const char *product_name;
>  	int16_t ifnum;
>  	uint16_t type;
> +	bool media_device;
>  	const void *data;
>  };
>  
> -- 
> 2.5.0
> 
> 


More information about the Alsa-devel mailing list