[alsa-devel] [PATCH 2/2] sound/usb: Update ALSA driver to use media controller API

Shuah Khan shuahkh at osg.samsung.com
Mon Jun 1 17:51:47 CEST 2015


On 05/27/2015 09:24 AM, Takashi Iwai wrote:
> At Fri,  8 May 2015 13:31:31 -0600,
> Shuah Khan wrote:
>>
>> Change ALSA driver to use media 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_dr() interface.
>> media_device_get_dr() will allocate a new media device device
>> resource or return an existing one if it exists. During probe,
>> media usb driver could have created the media device. It will
>> then register the media device if it isn't already registered.
>> Media device unregister is done from usb_audio_disconnect().
>>
>> New fields to add support for media entity and links are
>> added to struct snd_usb_substream. A new media entity for
>> ALSA and a link from tuner entity to the newly registered
>> ALSA entity are created from snd_usb_init_substream() and
>> removed from free_substream(). The state is kept to indicate
>> if tuner is linked. This is to account for case when tuner
>> entity doesn't exist. Media pipeline gets started to mark
>> the tuner busy from snd_usb_substream_capture_trigger in
>> response to SNDRV_PCM_TRIGGER_START and pipeline is stopped
>> in response to SNDRV_PCM_TRIGGER_STOP. snd_usb_pcm_close()
>> stops pipeline to cover the case when SNDRV_PCM_TRIGGER_STOP
>> isn't issued. Pipeline start and stop are done only when
>> tuner_linked is set.
>>
>> Tested with and without CONFIG_MEDIA_CONTROLLER enabled.
>> Tested tuner entity doesn't exist case as au0828 v4l2
>> driver is the one that will create the tuner when it gets
>> updated to use media controller API.
> 
> I guess it gets broken when CONFIG_MEDIA_SUPPORT=m while
> CONFIG_SND*=y.  So, it should be compiled in only when the media
> support is built-in or both sound and media are module, i.e.
> 
> #ifdef CONFIG_MEDIA_CONTROLLER
> #if defined(CONFIG_MEDIA_SUPPORT) || \
>     (defined(CONFIG_MEDIA_SUPPORT_MODULE) && defined(MODULE))
> #define I_CAN_USE_MEDIA_CONTROLLER
> #endif
> #endif
> 
>> Signed-off-by: Shuah Khan <shuahkh at osg.samsung.com>
>> ---
>>  sound/usb/card.c         |  5 +++++
>>  sound/usb/card.h         | 12 ++++++++++
>>  sound/usb/pcm.c          | 23 ++++++++++++++++++-
>>  sound/usb/quirks-table.h |  1 +
>>  sound/usb/quirks.c       | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
>>  sound/usb/quirks.h       |  6 +++++
>>  sound/usb/stream.c       | 40 +++++++++++++++++++++++++++++++++
>>  sound/usb/usbaudio.h     |  1 +
>>  8 files changed, 144 insertions(+), 2 deletions(-)
>>
>> diff --git a/sound/usb/card.c b/sound/usb/card.c
>> index 1fab977..587fc24 100644
>> --- a/sound/usb/card.c
>> +++ b/sound/usb/card.c
>> @@ -621,6 +621,11 @@ static void usb_audio_disconnect(struct usb_interface *intf)
>>  		}
>>  	}
>>  
>> +	/* 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);
> 
> This should be called once, so better in if (!was_shutdown) block, I
> guess.

Moved it under if (!was_shutdown) block.

> 
> Apart from that, yes, a good way to call an optional destructor for
> quirks would be better.

Maybe something to look into as an enhancement for quirk handling.

> 
> 
>>  	chip->num_interfaces--;
>>  	if (chip->num_interfaces <= 0) {
>>  		usb_chip[chip->index] = NULL;
>> diff --git a/sound/usb/card.h b/sound/usb/card.h
>> index ef580b4..477bdcd 100644
>> --- a/sound/usb/card.h
>> +++ b/sound/usb/card.h
>> @@ -1,6 +1,11 @@
>>  #ifndef __USBAUDIO_CARD_H
>>  #define __USBAUDIO_CARD_H
>>  
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +#include <media/media-device.h>
>> +#include <media/media-entity.h>
>> +#endif
>> +
>>  #define MAX_NR_RATES	1024
>>  #define MAX_PACKS	6		/* per URB */
>>  #define MAX_PACKS_HS	(MAX_PACKS * 8)	/* in high speed mode */
>> @@ -155,6 +160,13 @@ struct snd_usb_substream {
>>  	} dsd_dop;
>>  
>>  	bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +	struct media_device *media_dev;
>> +	struct media_entity media_entity;
>> +	struct media_pad media_pads;
>> +	struct media_pipeline media_pipe;
>> +	bool   tuner_linked;
>> +#endif
> 
> Maybe a slightly better idea is to allocate these in a new record and
> stores the pointer in stream->media_ctl or whatever new field.
> Then, a check like
> 	if (subs->tuner_linked)
> can be replaced with
> 	if (subs->media_ctl)
> 
> The whole media-specific stuff can be gathered in a single file,
> e.g. sound/usb/media.c, and there you can define the struct.
> 
> 
>>  };
>>  
>>  struct snd_usb_stream {
>> diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
>> index b4ef410..c2a40a9 100644
>> --- a/sound/usb/pcm.c
>> +++ b/sound/usb/pcm.c
>> @@ -1225,6 +1225,10 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
>>  
>>  	subs->pcm_substream = NULL;
>>  	snd_usb_autosuspend(subs->stream->chip);
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +	if (subs->tuner_linked)
>> +		media_entity_pipeline_stop(&subs->media_entity);
>> +#endif
> 
> Let's define each of such call properly and drop ifdef at each place.
> 
> 
>>  	return 0;
>>  }
>> @@ -1587,9 +1591,22 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
>>  
>>  	switch (cmd) {
>>  	case SNDRV_PCM_TRIGGER_START:
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +		if (subs->tuner_linked) {
>> +			err = media_entity_pipeline_start(&subs->media_entity,
>> +							  &subs->media_pipe);
>> +			if (err)
>> +				return err;
>> +		}
>> +#endif
>>  		err = start_endpoints(subs, false);
>> -		if (err < 0)
>> +		if (err < 0) {
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +			if (subs->tuner_linked)
>> +				media_entity_pipeline_stop(&subs->media_entity);
>> +#endif
>>  			return err;
>> +		}
>>  
>>  		subs->data_endpoint->retire_data_urb = retire_capture_urb;
>>  		subs->running = 1;
>> @@ -1597,6 +1614,10 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
>>  	case SNDRV_PCM_TRIGGER_STOP:
>>  		stop_endpoints(subs, false);
>>  		subs->running = 0;
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +		if (subs->tuner_linked)
>> +			media_entity_pipeline_stop(&subs->media_entity);
>> +#endif
>>  		return 0;
>>  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>  		subs->data_endpoint->retire_data_urb = NULL;
>> diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
>> index 2f6d3e9..51c544f 100644
>> --- a/sound/usb/quirks-table.h
>> +++ b/sound/usb/quirks-table.h
>> @@ -2798,6 +2798,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
>>  		.product_name = pname, \
>>  		.ifnum = QUIRK_ANY_INTERFACE, \
>>  		.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
>> +		.media_device = 1, \
>>  	} \
> 
> I guess this could be implemented with QUIRK_COMPOSITE, too, without
> adding a new field.  But it might result in too complex code.  Let's
> judge it later.
> 

I looked into this. Looks like there is one quirk type allowed
at this time. My initial thought was to make .type an array of
quirk types so a device could have multiple quirk types. I opted
to go with the simpler route to add a new field instead.

> 
>>  }
>>  
>> diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
>> index 7c5a701..468e10f 100644
>> --- a/sound/usb/quirks.c
>> +++ b/sound/usb/quirks.c
>> @@ -503,6 +503,56 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
>>  	return snd_usb_create_mixer(chip, quirk->ifnum, 0);
>>  }
>>  
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +static int media_device_init(struct usb_interface *iface)
>> +{
>> +	struct media_device *mdev;
>> +	struct usb_device *usbdev = interface_to_usbdev(iface);
>> +	int ret;
>> +
>> +	mdev = media_device_get_dr(&usbdev->dev);
>> +	if (!mdev)
>> +		return -ENOMEM;
>> +	if (!media_devnode_is_registered(&mdev->devnode)) {
>> +		/* 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_register(mdev);
>> +		if (ret) {
>> +			dev_err(&usbdev->dev,
>> +				"Couldn't create a media device. Error: %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +	}
>> +	dev_info(&usbdev->dev, "Created media device.\n");
>> +	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_dr(&usbdev->dev);
>> +	if (mdev && media_devnode_is_registered(&mdev->devnode))
>> +		media_device_unregister(mdev);
>> +	dev_info(&usbdev->dev, "Deleted media device.\n");
>> +}
>> +#else
>> +static inline int media_device_init(struct usb_interface *iface)
>> +{
>> +	return 0;
>> +}
>> +#endif
> 
> Let's move all these media specific code into the single place.
> 

Moved all the media specific code into a new file. I will send
v2 that covers Hans's comments as well later on this week.

P.S: Entity part of the code needs updates once Property API
solidifies.

thanks,
-- Shuah


-- 
Shuah Khan
Sr. Linux Kernel Developer
Open Source Innovation Group
Samsung Research America (Silicon Valley)
shuahkh at osg.samsung.com | (970) 217-8978


More information about the Alsa-devel mailing list