From patchwork Wed Apr 9 11:07:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044460 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73D4325DCE0 for ; Wed, 9 Apr 2025 10:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195860; cv=none; b=fZwuTUglLUbc3d9cjDS8CiDXpEYqy4h9fA623DUPWFThyHDvzU7j6pjJudp31ZKUyr6mxTEIux5SiqKdtpRARIMLu76SHgFGE9yxD5q2Da0aQZ8EMbT9oCw21ZR9NIB6/XQY31OqG6Vxpegu7iBo/GZpXJkpKIMWEvhAOWZ+vPI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195860; c=relaxed/simple; bh=9pmlo3y5/4VGmU6ZyeKvsRxtyqRaqYJlB0yozqK/4jM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KAaFODqYeeIouKv+bUhCNdxRGP78tA44XgWjj/APbgF5S+90yNU47xPHMPFTlXmT7wRj6VrAehkT2gt4JrwP2+tKkIC9G1R1ATkgtDkvxS92r+9pmXyqfBCIGUySzjmOW/gEiicJfKs76V6YKwJnyfSr+EzxmZSqgiLkdX63EEo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=actnbfVp; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="actnbfVp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195858; x=1775731858; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=9pmlo3y5/4VGmU6ZyeKvsRxtyqRaqYJlB0yozqK/4jM=; b=actnbfVpX9Sjv+3YGWaU2DdQh8fpd2l8EKfdw5oGypLCXu6ewT2I6NW3 nNX8V6KBbN5ylxg7kBcZhZ/PZrtQp79q4CLc/liel1hfWOoGCK+6aMHlt U0UgAgFGkg26SuOONR01DOR2J350/gQNj111Y/PP9MWADHJspimvp33lY WlMQoIBmk1l8tIinV8S0bnhgrseF8FLBahScgp6AZhJvuEqPWrs5M6VJf U6zmo9Q/HBt/SmilLrlpGOqh+SWiglbI6Jdw8NteJkOMgNiZyDKzq+X/6 eYtBuYRlJ3BTRBaIb73sE3C/b/WWKDm8CBjJb2ohKwaDnVVdBNtjHTYRL Q==; X-CSE-ConnectionGUID: qfqR1t6wT+Owi2DKyxsOjg== X-CSE-MsgGUID: MWV+OMEDSUeIOtYFtgTVbw== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380015" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380015" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:50:58 -0700 X-CSE-ConnectionGUID: PUtMS0b6SwK5unGVJYpk7Q== X-CSE-MsgGUID: fhuM1yDDReuwHtJAqwmkPg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426178" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:50:56 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 01/15] ALSA: usb: Move media-filters to the media code Date: Wed, 9 Apr 2025 13:07:16 +0200 Message-Id: <20250409110731.3752332-2-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Creating and deleting process of media devices in the common code can be simplified if quirk_flags are checked in callee. Signed-off-by: Cezary Rojewski --- sound/usb/card.c | 13 +++---------- sound/usb/media.c | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 9c411b82a218..03694295491f 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -909,10 +909,8 @@ static int usb_audio_probe(struct usb_interface *intf, if (err < 0) goto __error_no_register; - if (chip->quirk_flags & QUIRK_FLAG_SHARE_MEDIA_DEVICE) { - /* don't want to fail when snd_media_device_create() fails */ - snd_media_device_create(chip, intf); - } + /* don't want to fail when snd_media_device_create() fails */ + snd_media_device_create(chip, intf); if (quirk) chip->quirk_type = quirk->type; @@ -983,12 +981,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) snd_usbmidi_disconnect(p); } snd_usb_midi_v2_disconnect_all(chip); - /* - * 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() accesses mixer_list */ snd_media_device_delete(chip); /* release mixer resources */ diff --git a/sound/usb/media.c b/sound/usb/media.c index d48db6f3ae65..b175fa820345 100644 --- a/sound/usb/media.c +++ b/sound/usb/media.c @@ -258,6 +258,9 @@ int snd_media_device_create(struct snd_usb_audio *chip, struct usb_device *usbdev = interface_to_usbdev(iface); int ret = 0; + if (!(chip->quirk_flags & QUIRK_FLAG_SHARE_MEDIA_DEVICE)) + return 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() @@ -312,6 +315,9 @@ void snd_media_device_delete(struct snd_usb_audio *chip) struct media_device *mdev = chip->media_dev; struct snd_usb_stream *stream; + if (!(chip->quirk_flags & QUIRK_FLAG_SHARE_MEDIA_DEVICE)) + return; + /* release resources */ list_for_each_entry(stream, &chip->pcm_list, list) { snd_media_stream_delete(&stream->substream[0]); From patchwork Wed Apr 9 11:07:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044461 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF2BB25D8E7 for ; Wed, 9 Apr 2025 10:51:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195862; cv=none; b=krYrtz+Zjjmo/OFBl74ieZmfz6w02Ms2D+O0XwhzyMHlAjM2v7MxO1rvBVDrZYQb1nEiDN9MgfepueMV8sKJuAQVy87WJBuQGbQOGy0dN4hmlJRqYY8QfMdQqUsTpMV/CEO4mCbV7B0gvFT0zJzgRwsuqFaDk0E0dZY0Ufehn5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195862; c=relaxed/simple; bh=hjeM+9DgD9nUByAQbkl92cVc5223EI8wFxHAwurcPPg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EApRnfc5k3bj/1vzIiv1IGiR69w7e0La7AKRK/KuYUvAL4geuoU89f7odfbIeqBgfsAU6Rv9Pt60SIKeGbxNiqN+TSZRcp/dHMI38i7vzQQ8AzRl3Xv2f6Epvb3j7Y/Po7AZdIL4wZ8IPJIn+c4XpZ3Z6fYO5zmiIM/Wl5dnHM4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=R0W1vSnT; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="R0W1vSnT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195861; x=1775731861; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=hjeM+9DgD9nUByAQbkl92cVc5223EI8wFxHAwurcPPg=; b=R0W1vSnTDsOUJJ5l8WmgGWpsGjbH2k+UbmxeYZxgsn1X5TytDCfPArw5 st3egJtxoYx4WEJ1PcH7s4XjfOGipmsDUA+inWhHhUO1KvJgrrXBBNCZG 91FOMk0NbRDUi/AoSgLdpyHiG5KSIdv8KQkoifEuBB/2a2NMzIVazjonD 3ZueilM8zk7ljgxEqA8okQOu1Lba+DyFN6h2C+LXtUD2nweGrQBVl4OJ5 rmDYPGXgTd2hW/2Be5OPJw8BcN7HRYHMAFz92AlfcyTmPIkXOLISsVD3P FG7uCmwslW859B/nd320cFqzOiA5yAVlw6F+Yt/DIQS4MunimjbAoCMv1 A==; X-CSE-ConnectionGUID: mg+vskCvRuy0HIwYJ1Cu+g== X-CSE-MsgGUID: 2JkTG6kjQTWtbIV7IR7/Gg== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380030" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380030" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:00 -0700 X-CSE-ConnectionGUID: 4sI6bRSITDqYqQ3+ABi0XA== X-CSE-MsgGUID: pxT0xWMZTK6jFZfw2R2YxA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426183" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:50:58 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 02/15] ALSA: usb: Drop private_free() usage for card and pcms Date: Wed, 9 Apr 2025 13:07:17 +0200 Message-Id: <20250409110731.3752332-3-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC On ASoC side, drivers operate on instance of struct snd_soc_card instead of struct snd_card. The latter is owned by the ASoC framework. To make the existing sound/usb code ASoC-friendly and avoid freeing-order problems when soc_cleanup_card_resources() is called, resign from cardd->private_free() and extra-size when allocating new card with snd_card_new(). Similar pattern applies to PCMs. Signed-off-by: Cezary Rojewski --- sound/usb/card.c | 50 ++++++++++++++++++++++------------------------ sound/usb/stream.c | 14 ++++++------- sound/usb/stream.h | 1 + 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 03694295491f..65de8e7854b6 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -485,25 +485,6 @@ lookup_device_name(u32 id) return NULL; } -/* - * free the chip instance - * - * here we have to do not much, since pcm and controls are already freed - * - */ - -static void snd_usb_audio_free(struct snd_card *card) -{ - struct snd_usb_audio *chip = card->private_data; - - snd_usb_endpoint_free_all(chip); - snd_usb_midi_v2_free_all(chip); - - mutex_destroy(&chip->mutex); - if (!atomic_read(&chip->shutdown)) - dev_set_drvdata(&chip->dev->dev, NULL); -} - static void usb_audio_make_shortname(struct usb_device *dev, struct snd_usb_audio *chip, const struct snd_usb_audio_quirk *quirk) @@ -630,14 +611,17 @@ static int snd_usb_audio_create(struct usb_interface *intf, return -ENXIO; } - err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, - sizeof(*chip), &card); + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, 0, &card); if (err < 0) { dev_err(&dev->dev, "cannot create card instance %d\n", idx); + kfree(chip); return err; } - chip = card->private_data; mutex_init(&chip->mutex); init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; @@ -665,8 +649,6 @@ static int snd_usb_audio_create(struct usb_interface *intf, else snd_usb_init_quirk_flags(chip); - card->private_free = snd_usb_audio_free; - strcpy(card->driver, "USB-Audio"); sprintf(component, "USB%04x:%04x", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); @@ -755,6 +737,19 @@ get_alias_quirk(struct usb_device *dev, unsigned int id) return NULL; } +static void snd_usb_chip_free(struct snd_usb_audio *chip) +{ + snd_usb_free_pcms(chip); + snd_usb_endpoint_free_all(chip); + snd_usb_midi_v2_free_all(chip); + /* mixers and midi1.0 freed with snd_card_free(). */ + + mutex_destroy(&chip->mutex); + if (!atomic_read(&chip->shutdown)) + dev_set_drvdata(&chip->dev->dev, NULL); + kfree(chip); +} + /* register card if we reach to the last interface or to the specified * one given via option */ @@ -934,8 +929,10 @@ static int usb_audio_probe(struct usb_interface *intf, * decrement before memory is possibly returned. */ atomic_dec(&chip->active); - if (!chip->num_interfaces) + if (!chip->num_interfaces) { snd_card_free(chip->card); + snd_usb_chip_free(chip); + } } mutex_unlock(®ister_mutex); return err; @@ -997,7 +994,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->num_interfaces <= 0) { usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); - snd_card_free_when_closed(card); + snd_card_free(card); + snd_usb_chip_free(chip); } else { mutex_unlock(®ister_mutex); } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index c1ea8844a46f..4d83c95bd9a8 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -55,7 +55,7 @@ static void free_substream(struct snd_usb_substream *subs) /* * free a usb stream instance */ -static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) +static void snd_usb_pcm_free(struct snd_usb_stream *stream) { free_substream(&stream->substream[0]); free_substream(&stream->substream[1]); @@ -63,13 +63,12 @@ static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) kfree(stream); } -static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) +void snd_usb_free_pcms(struct snd_usb_audio *chip) { - struct snd_usb_stream *stream = pcm->private_data; - if (stream) { - stream->pcm = NULL; - snd_usb_audio_stream_free(stream); - } + struct snd_usb_stream *as, *save; + + list_for_each_entry_safe(as, save, &chip->pcm_list, list) + snd_usb_pcm_free(as); } /* @@ -533,7 +532,6 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, } as->pcm = pcm; pcm->private_data = as; - pcm->private_free = snd_usb_audio_pcm_free; pcm->info_flags = 0; if (chip->pcm_devs > 0) sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); diff --git a/sound/usb/stream.h b/sound/usb/stream.h index d92e18d5818f..f70693502281 100644 --- a/sound/usb/stream.h +++ b/sound/usb/stream.h @@ -8,6 +8,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct audioformat *fp); +void snd_usb_free_pcms(struct snd_usb_audio *chip); #endif /* __USBAUDIO_STREAM_H */ From patchwork Wed Apr 9 11:07:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044462 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FA9C25D8F6 for ; Wed, 9 Apr 2025 10:51:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195865; cv=none; b=m2FdysfG6IcHCvs+8Vr882XbhaQom2IVukOYeA0kkPC/epi1wPuTqN2m3lhwi/mtpXQ2+hgtHuQGEX+0dhV/FShnZHTPR5cjGZq0JfXoxvPGqQ7HttYcGOB8cs+ACE2q5n0yIr4Xzac4WzS1kCJcP4VjvXwJ+VzeN90i4AFj5z0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195865; c=relaxed/simple; bh=y3D+xY5AicFp52/KRIcFbt0Ng3QFmhpvHu4leKLRReQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MUqVYwEgKidVrbTCUQifQI44B/pIxOyDQe7kmMRaBBHt0qBai08gj8GFUgKIxT+E8y1E/er7B9ntgy3l4J8MsA+QZ0cmVmHPPJq7PfUScc6egN4qeeZ3ZlDImjuruFC2zbCV8DY5cOOIMpMZ6artq8qTrRo6l3PUYY4WMR88ebE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=icyxISyC; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="icyxISyC" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195863; x=1775731863; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=y3D+xY5AicFp52/KRIcFbt0Ng3QFmhpvHu4leKLRReQ=; b=icyxISyCPT2OJT2RJfn6ZgPX55OSeWWRMWjMP4KxkG049J7Opy6lcH6j U5RPamyXLCdNUYIzAF39UJ+jOVr2R+raB0Kei4WtUDGY2XuTbJB03mrUN jXAGxQVmB5g4O17twRmg7WiIxx6Hzu34BIpxlxoybPC1+NLmhriYv2OgW JtoPr3o7/mcJfyq7EApnlRFweGezGXEwTjNoLjHy+2jP4PbrARsBtOw6x F2HmpTCKg+D4etireIZzGj+9G43sGStCHqEwzQMfdrelUclxu8xJidhes 6Bt7QiYciptLn9LeUghv7QPAXt0VRGf/Q+zElNJ2t5jyq1XH6LETdhHC/ w==; X-CSE-ConnectionGUID: awU5u3uQQh612DXP8EPJcQ== X-CSE-MsgGUID: e5IlSC9DQLufxKZm6UR04A== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380040" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380040" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:03 -0700 X-CSE-ConnectionGUID: /mEr5S69T1KgdMbrvnFwfQ== X-CSE-MsgGUID: TJsWCBu+TLenmatsyb0gIQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426193" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:01 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 03/15] ALSA: usb: Relocate the usbaudio header file Date: Wed, 9 Apr 2025 13:07:18 +0200 Message-Id: <20250409110731.3752332-4-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC For ALSA and ASoC to share the symbols, common header shall be defined. Existing usbaudio.h holds most of important stuff, with few adjustments it can be simply relocated to include/ to satisfy needs of both frameworks. The adjstments are: - rename quirk_type enumeration to snd_usb_quirk_type to avoid naming conflits - add quirk pointer so that quirks applied for a USB device can later be referred to Signed-off-by: Cezary Rojewski --- sound/usb/usbaudio.h => include/sound/usb.h | 24 +++++++++++---------- sound/usb/caiaq/device.h | 2 +- sound/usb/card.c | 2 +- sound/usb/clock.c | 2 +- sound/usb/endpoint.c | 2 +- sound/usb/format.c | 2 +- sound/usb/helper.c | 2 +- sound/usb/implicit.c | 2 +- sound/usb/media.c | 2 +- sound/usb/midi.c | 2 +- sound/usb/midi.h | 2 ++ sound/usb/midi2.c | 2 +- sound/usb/midi2.h | 1 + sound/usb/misc/ua101.c | 2 +- sound/usb/mixer.c | 2 +- sound/usb/mixer_quirks.c | 2 +- sound/usb/mixer_s1810c.c | 2 +- sound/usb/mixer_scarlett.c | 2 +- sound/usb/mixer_scarlett2.c | 2 +- sound/usb/mixer_us16x08.c | 2 +- sound/usb/pcm.c | 2 +- sound/usb/power.c | 2 +- sound/usb/proc.c | 2 +- sound/usb/quirks.c | 2 +- sound/usb/quirks.h | 2 ++ sound/usb/stream.c | 2 +- sound/usb/usx2y/us122l.c | 2 +- sound/usb/usx2y/usX2Yhwdep.c | 1 + sound/usb/usx2y/usbusx2y.h | 2 +- sound/usb/validate.c | 2 +- 30 files changed, 44 insertions(+), 36 deletions(-) rename sound/usb/usbaudio.h => include/sound/usb.h (98%) diff --git a/sound/usb/usbaudio.h b/include/sound/usb.h similarity index 98% rename from sound/usb/usbaudio.h rename to include/sound/usb.h index 158ec053dc44..7e7a86b5c9c4 100644 --- a/sound/usb/usbaudio.h +++ b/include/sound/usb.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef __USBAUDIO_H -#define __USBAUDIO_H +#ifndef __SOUND_USB_H +#define __SOUND_USB_H + /* * (Tentative) USB Audio Driver for ALSA * @@ -21,6 +22,14 @@ struct media_intf_devnode; #define MAX_CARD_INTERFACES 16 +struct snd_usb_audio_quirk { + const char *vendor_name; + const char *product_name; + int16_t ifnum; + uint16_t type; + const void *data; +}; + /* * Structure holding assosiation between Audio Control Interface * and given Streaming or Midi Interface. @@ -37,6 +46,7 @@ struct snd_usb_audio { struct usb_interface *intf[MAX_CARD_INTERFACES]; u32 usb_id; uint16_t quirk_type; + const struct snd_usb_audio_quirk *quirk; struct mutex mutex; unsigned int system_suspend; atomic_t active; @@ -99,7 +109,7 @@ struct snd_usb_audio { #define QUIRK_NO_INTERFACE -2 #define QUIRK_ANY_INTERFACE -1 -enum quirk_type { +enum snd_usb_quirk_type { QUIRK_IGNORE_INTERFACE, QUIRK_COMPOSITE, QUIRK_AUTODETECT, @@ -124,14 +134,6 @@ enum quirk_type { QUIRK_TYPE_COUNT }; -struct snd_usb_audio_quirk { - const char *vendor_name; - const char *product_name; - int16_t ifnum; - uint16_t type; - const void *data; -}; - #define combine_word(s) ((*(s)) | ((unsigned int)(s)[1] << 8)) #define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16)) #define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24)) diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index 743eb0387b5f..2346dc8209c9 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -2,7 +2,7 @@ #ifndef CAIAQ_DEVICE_H #define CAIAQ_DEVICE_H -#include "../usbaudio.h" +#include #define USB_VID_NATIVEINSTRUMENTS 0x17cc diff --git a/sound/usb/card.c b/sound/usb/card.c index 65de8e7854b6..d8794adb7a88 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -40,8 +40,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "midi.h" #include "midi2.h" diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 842ba5b801ea..fa5bc7b8c79c 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -14,8 +14,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "helper.h" #include "clock.h" diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a29f28eb7d0c..bdd2b75ebfed 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -12,8 +12,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "helper.h" #include "card.h" #include "endpoint.h" diff --git a/sound/usb/format.c b/sound/usb/format.c index 6049d957694c..1fa1711980ad 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -11,8 +11,8 @@ #include #include +#include -#include "usbaudio.h" #include "card.h" #include "quirks.h" #include "helper.h" diff --git a/sound/usb/helper.c b/sound/usb/helper.c index 72b671fb2c84..2e4da52f8d8e 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -5,8 +5,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "helper.h" #include "quirks.h" diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 4727043fd745..fd0b23e7d92e 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -11,8 +11,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "helper.h" #include "pcm.h" diff --git a/sound/usb/media.c b/sound/usb/media.c index b175fa820345..7060410fd1c6 100644 --- a/sound/usb/media.c +++ b/sound/usb/media.c @@ -24,8 +24,8 @@ #include #include +#include -#include "usbaudio.h" #include "card.h" #include "mixer.h" #include "media.h" diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 767f1948cc5a..53d327ee8014 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -54,7 +54,7 @@ #include #include #include -#include "usbaudio.h" +#include #include "midi.h" #include "power.h" #include "helper.h" diff --git a/sound/usb/midi.h b/sound/usb/midi.h index 2100f1486b03..bf3561652ba8 100644 --- a/sound/usb/midi.h +++ b/sound/usb/midi.h @@ -2,6 +2,8 @@ #ifndef __USBMIDI_H #define __USBMIDI_H +#include + /* maximum number of endpoints per interface */ #define MIDI_MAX_ENDPOINTS 2 diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 692dfc3c182f..f1d925b74692 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -18,7 +18,7 @@ #include #include #include -#include "usbaudio.h" +#include #include "midi.h" #include "midi2.h" #include "helper.h" diff --git a/sound/usb/midi2.h b/sound/usb/midi2.h index 94a65fcbd58b..3e66531d5e80 100644 --- a/sound/usb/midi2.h +++ b/sound/usb/midi2.h @@ -2,6 +2,7 @@ #ifndef __USB_AUDIO_MIDI2_H #define __USB_AUDIO_MIDI2_H +#include #include "midi.h" #if IS_ENABLED(CONFIG_SND_USB_AUDIO_MIDI_V2) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 4f6b20ed29dd..454587cdcad7 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -13,7 +13,7 @@ #include #include #include -#include "../usbaudio.h" +#include #include "../midi.h" MODULE_DESCRIPTION("Edirol UA-101/1000 driver"); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e2e0ddf1957d..f95da9368921 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -43,8 +43,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "mixer.h" #include "helper.h" #include "mixer_quirks.h" diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 23fcd680167d..54d16e0f8ac4 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -29,8 +29,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "mixer.h" #include "mixer_quirks.h" #include "mixer_scarlett.h" diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index fac4bbc6b275..d95edb1dc4d0 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -18,8 +18,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "mixer.h" #include "mixer_quirks.h" #include "helper.h" diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index ff548041679b..f31718caa679 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -124,8 +124,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "mixer.h" #include "helper.h" #include "power.h" diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 7f595c1752a5..a3866e9dd1ac 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -158,10 +158,10 @@ #include #include #include +#include #include -#include "usbaudio.h" #include "mixer.h" #include "helper.h" diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index 20ac32635f1f..bdceb3b38689 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -11,8 +11,8 @@ #include #include +#include -#include "usbaudio.h" #include "mixer.h" #include "helper.h" diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 08bf535ed163..ea698f061af9 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -13,8 +13,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "quirks.h" #include "endpoint.h" diff --git a/sound/usb/power.c b/sound/usb/power.c index 66bd4daa68fd..ad847aadb4fb 100644 --- a/sound/usb/power.c +++ b/sound/usb/power.c @@ -8,8 +8,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "helper.h" #include "power.h" diff --git a/sound/usb/proc.c b/sound/usb/proc.c index e9bbaea7b2fa..b7e898722ff4 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -8,8 +8,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "helper.h" #include "card.h" #include "endpoint.h" diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 8ba0aff8be2e..2a9470ef8b5f 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -13,8 +13,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "mixer.h" #include "mixer_quirks.h" diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index f9bfd5ac7bab..0ea079688261 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -2,6 +2,8 @@ #ifndef __USBAUDIO_QUIRKS_H #define __USBAUDIO_QUIRKS_H +#include + struct audioformat; struct snd_usb_endpoint; struct snd_usb_substream; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 4d83c95bd9a8..6beeff03d711 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -14,8 +14,8 @@ #include #include #include +#include -#include "usbaudio.h" #include "card.h" #include "proc.h" #include "quirks.h" diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 6bcf8b859ebb..3d00541a602c 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -11,9 +11,9 @@ #include #include #include +#include #define MODNAME "US122L" #include "usb_stream.c" -#include "../usbaudio.h" #include "../midi.h" #include "us122l.h" diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index 9fd6a86cc08e..2d6a9c80e5bf 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "usx2y.h" #include "usbusx2y.h" #include "usX2Yhwdep.h" diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index 391fd7b4ed5e..689b565fb225 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef USBUSX2Y_H #define USBUSX2Y_H -#include "../usbaudio.h" +#include #include "../midi.h" #include "usbus428ctldefs.h" diff --git a/sound/usb/validate.c b/sound/usb/validate.c index 6fe206f6e911..8a7e8fc8197e 100644 --- a/sound/usb/validate.c +++ b/sound/usb/validate.c @@ -9,7 +9,7 @@ #include #include #include -#include "usbaudio.h" +#include #include "helper.h" struct usb_desc_validator { From patchwork Wed Apr 9 11:07:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044463 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E98E25E441 for ; Wed, 9 Apr 2025 10:51:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195868; cv=none; b=lox1WtGm92DkG78pElPHALMTADzvrEPwQA7ovzKk0lUhFuLN8RylsHHYI++yDXoIwGGbvNK3wFB5eDjyBqtBQi6gHYGxVz6YIod7FWZ2KQBEwcAf3ThpPAGOtg3CxLVGyBizfW8mnJwjdSQSerC79hzOmNs1QWp1FMMDpNIlHKI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195868; c=relaxed/simple; bh=m5qAflhwIe44jRryTmRvCNbU5P54YAc94YTF92NjS+c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZBBJ4miKcJP1hOznj2mEfgZ5E/rjESMMretvu4Shp35S/lBk3giZUImPROBuZTFcJxz6XAxTVHkuXzN375qv852DDB2DPyZLT8vHBG61IeRBvsRJHyeFLwY5zXBWfo27xxMAV5NS8mSCnO94htMqrm+wNr4Vm3Wx82nnK/g8Kbs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=DxHwcWRn; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="DxHwcWRn" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195866; x=1775731866; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=m5qAflhwIe44jRryTmRvCNbU5P54YAc94YTF92NjS+c=; b=DxHwcWRn2GUQ0b8vG3q5FiZ21M8S/QWfiPUFAOdmMVNFg4eK+n59TTz2 nHUgH5Pgj3LWWXrTyHEl3VWoydF16rtY9VDtzKfvEIvMgOs5vlrvqyNoT HKPEfSGdSUKkzN03iurORZKvojgpNcQgUZsVW04Ziq2iQOgqAGEj35nAQ fLAjmvXNE1qBKUXlJ2eMo5mjkzIY/fZnsX6ZZYF23007jZFcaria9So2I Oq6GDm83G9wQxYRE0nBsU4EsdxbvtFBcjyPOEmxmFS0WWJ1U7Ds82lrEt dwm0gFqeAt/g7MAkpCsXvcm1SOu4Ksf0fKkEermMdXMFXUIIWdQpr/RLR A==; X-CSE-ConnectionGUID: O8A5ynJQT2+r20Ur6bPI9w== X-CSE-MsgGUID: YhHGcqxJSkG7AtuGnPpOVw== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380049" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380049" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:06 -0700 X-CSE-ConnectionGUID: 2/fZRr4VQLasS2ca0EqEVw== X-CSE-MsgGUID: +yP3sNT9T0+Pud7CqfpCWg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426202" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:03 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 04/15] ALSA: usb: Implement two-stage quirk applying mechanism Date: Wed, 9 Apr 2025 13:07:19 +0200 Message-Id: <20250409110731.3752332-5-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC The USB Audio-Class (AC) device driver on ASoC side needs to know the number of available PCM streams when creating list of DAI and DAI Links before these objects are actually probed. To achieve that split existing USB stream enumeration procedure from one-stage to two-stage process: 1) parse the descriptors and obtain information about PCMs but do not create them yet 2) create all PCMs based on the previously obtained information and follow that up with MIDIs, mixers and Media resources On quirks side this translates to splitting all handlers that do combine pcm/midi/mixer together into 'pcm-only' and 'others'. A top level handler snd_usb_parse_pcms_quirk() is provided to invoke all the necessary parse-pcm quirks depending on the quirk type. It mimics behavior of snd_usb_create_quirk() but limits itself to just parsing the PCMs. Signed-off-by: Cezary Rojewski --- sound/usb/quirks.c | 217 ++++++++++++++++++++++++++++++++++++--------- sound/usb/quirks.h | 4 + 2 files changed, 179 insertions(+), 42 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2a9470ef8b5f..4e402c6406a9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -28,7 +28,36 @@ #include "stream.h" /* - * handle the quirks for the contained interfaces + * First run of composite quirk. Parses all PCM interfaces. + * These can be later created with snd_usb_add_pcms(). + */ +static int create_composite_pcm_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk_comp) +{ + int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + const struct snd_usb_audio_quirk *quirk; + int err; + + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { + iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); + if (!iface) + continue; + if (quirk->ifnum != probed_ifnum && + usb_interface_claimed(iface)) + continue; + err = snd_usb_parse_pcms_quirk(chip, iface, driver, quirk); + if (err < 0) + return err; + } + + return 0; +} + +/* + * Second run of composite quirk. Creates all non-PCM interfaces and + * claims the usb_interface @iface. */ static int create_composite_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, @@ -375,6 +404,25 @@ static int create_auto_midi_quirk(struct snd_usb_audio *chip, return create_std_midi_quirk(chip, iface, driver, alts); } +static int create_autodetect_pcm_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + return create_auto_pcm_quirk(chip, iface, driver); +} + +__maybe_unused +static int create_autodetect_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + if (list_empty(&chip->pcm_list)) + return create_auto_midi_quirk(chip, iface, driver); + return 1; +} + static int create_autodetect_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, @@ -388,14 +436,10 @@ static int create_autodetect_quirk(struct snd_usb_audio *chip, return err; } -/* - * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface. - * The only way to detect the sample rate is by looking at wMaxPacketSize. - */ -static int create_uaxx_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) +static int create_uaxx_pcm_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) { static const struct audioformat ua_format = { .formats = SNDRV_PCM_FMTBIT_S24_3LE, @@ -405,8 +449,8 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, .altset_idx = 1, .rates = SNDRV_PCM_RATE_CONTINUOUS, }; - struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; + struct usb_host_interface *alts; struct audioformat *fp; int err; @@ -416,35 +460,15 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, alts = &iface->altsetting[1]; altsd = get_iface_desc(alts); - if (altsd->bNumEndpoints == 2) { - static const struct snd_usb_midi_endpoint_info ua700_ep = { - .out_cables = 0x0003, - .in_cables = 0x0003 - }; - static const struct snd_usb_audio_quirk ua700_quirk = { - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &ua700_ep - }; - static const struct snd_usb_midi_endpoint_info uaxx_ep = { - .out_cables = 0x0001, - .in_cables = 0x0001 - }; - static const struct snd_usb_audio_quirk uaxx_quirk = { - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &uaxx_ep - }; - const struct snd_usb_audio_quirk *quirk = - chip->usb_id == USB_ID(0x0582, 0x002b) - ? &ua700_quirk : &uaxx_quirk; - return __snd_usbmidi_create(chip->card, iface, - &chip->midi_list, quirk, - chip->usb_id, - &chip->num_rawmidis); + switch (altsd->bNumEndpoints) { + case 2: + return 2; /* Leave this to midi. */ + case 1: + break; + default: + return -ENXIO; } - if (altsd->bNumEndpoints != 1) - return -ENXIO; - fp = kmemdup(&ua_format, sizeof(*fp), GFP_KERNEL); if (!fp) return -ENOMEM; @@ -484,6 +508,78 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, return 0; } +/* + * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface. + * The only way to detect the sample rate is by looking at wMaxPacketSize. + */ +static int create_uaxx_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + static const struct snd_usb_midi_endpoint_info ua700_ep = { + .out_cables = 0x0003, + .in_cables = 0x0003 + }; + static const struct snd_usb_audio_quirk ua700_quirk = { + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &ua700_ep + }; + static const struct snd_usb_midi_endpoint_info uaxx_ep = { + .out_cables = 0x0001, + .in_cables = 0x0001 + }; + static const struct snd_usb_audio_quirk uaxx_quirk = { + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &uaxx_ep + }; + const struct snd_usb_audio_quirk *spec; + struct usb_host_interface *alt; + + spec = chip->usb_id == USB_ID(0x0582, 0x002b) ? &ua700_quirk : &uaxx_quirk; + + /* both PCM and MIDI interfaces have 2 or more altsettings */ + if (iface->num_altsetting < 2) + return -ENXIO; + alt = &iface->altsetting[1]; + + switch (alt->desc.bNumEndpoints) { + case 2: + break; + case 1: + return 1; /* Leave this to pcm. */ + default: + return -ENXIO; + } + + return __snd_usbmidi_create(chip->card, iface, &chip->midi_list, spec, chip->usb_id, + &chip->num_rawmidis); +} + +static int create_uaxx_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + struct usb_interface_descriptor *altsd; + struct usb_host_interface *alts; + + /* both PCM and MIDI interfaces have 2 or more altsettings */ + if (iface->num_altsetting < 2) + return -ENXIO; + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + + switch (altsd->bNumEndpoints) { + case 2: + return create_uaxx_midi_quirk(chip, iface, driver, quirk); + case 1: + return create_uaxx_pcm_quirk(chip, iface, driver, quirk); + default: + return -ENXIO; + } +} + /* * Create a standard mixer for the specified interface. */ @@ -498,6 +594,44 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip, return snd_usb_create_mixer(chip, quirk->ifnum); } +int snd_usb_parse_pcms_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + typedef int (*quirk_func_t)(struct snd_usb_audio *, + struct usb_interface *, + struct usb_driver *, + const struct snd_usb_audio_quirk *); + static const quirk_func_t quirk_funcs[] = { + [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, + [QUIRK_COMPOSITE] = create_composite_pcm_quirk, + [QUIRK_AUTODETECT] = create_autodetect_pcm_quirk, + [QUIRK_MIDI_STANDARD_INTERFACE] = ignore_interface_quirk, + [QUIRK_MIDI_FIXED_ENDPOINT] = ignore_interface_quirk, + [QUIRK_MIDI_YAMAHA] = ignore_interface_quirk, + [QUIRK_MIDI_ROLAND] = ignore_interface_quirk, + [QUIRK_MIDI_MIDIMAN] = ignore_interface_quirk, + [QUIRK_MIDI_NOVATION] = ignore_interface_quirk, + [QUIRK_MIDI_RAW_BYTES] = ignore_interface_quirk, + [QUIRK_MIDI_EMAGIC] = ignore_interface_quirk, + [QUIRK_MIDI_CME] = ignore_interface_quirk, + [QUIRK_MIDI_AKAI] = ignore_interface_quirk, + [QUIRK_MIDI_FTDI] = ignore_interface_quirk, + [QUIRK_MIDI_CH345] = ignore_interface_quirk, + [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, + [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, + [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_pcm_quirk, + [QUIRK_AUDIO_STANDARD_MIXER] = ignore_interface_quirk, + }; + + if (quirk->type < QUIRK_TYPE_COUNT) + return quirk_funcs[quirk->type](chip, iface, driver, quirk); + + usb_audio_err(chip, "invalid quirk type %d\n", quirk->type); + return -ENXIO; +} + /* * audio-interface quirks * @@ -537,12 +671,11 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, }; - if (quirk->type < QUIRK_TYPE_COUNT) { + if (quirk->type < QUIRK_TYPE_COUNT) return quirk_funcs[quirk->type](chip, iface, driver, quirk); - } else { - usb_audio_err(chip, "invalid quirk type %d\n", quirk->type); - return -ENXIO; - } + + usb_audio_err(chip, "invalid quirk type %d\n", quirk->type); + return -ENXIO; } /* diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 0ea079688261..1344e4e635d8 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -8,6 +8,10 @@ struct audioformat; struct snd_usb_endpoint; struct snd_usb_substream; +int snd_usb_parse_pcms_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk); int snd_usb_create_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, From patchwork Wed Apr 9 11:07:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044464 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D538C25DAFB for ; Wed, 9 Apr 2025 10:51:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195870; cv=none; b=DmWhEz0vca/aKSfgzX3I/lYHDQHmc8xq6rnOJZ7oa7ZyrMlnYfULzR3P/ilsg914van9WfAXjlRqp5U0Ha1anxE0DQT2V0wKx6P8QCrZFF1BmDd/A2cjvyr4b9SziQGYnG1byjD21jbhSRUhFGzn7gAZcZB21DyuG+aETR4Ret4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195870; c=relaxed/simple; bh=Aar8iCZEtXLBuKdUvVNF4NhEj8Ra64yf6DSThIwfkHw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=BfQ2OokMBx/hVr5u7IjRspYwa9ZWGmH4D3szBN0iW4nK3bonnOl7OgiKWxd0wwja0O9/+lZMGDWt/ePwsOVZBOxLgeYGCOd6dPci4tMssB41Y4X3owD8HOGswBFtL34l1gGwNczpFXtsP9ABDjpf0atuJFy2Z6t4UaTPQbFWI4g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=MB8s0Ae7; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="MB8s0Ae7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195869; x=1775731869; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Aar8iCZEtXLBuKdUvVNF4NhEj8Ra64yf6DSThIwfkHw=; b=MB8s0Ae7r5t8axkrmaixMp1fuNi/z2NsVEDLgtcP9OhO0Rx1F1CoAW8/ xCyHJkOKPrza/azSTdNlcgnujTQMQIdJI+ztWGFE5U9ao+KRJychWTYjX DcrF/8q/4pX9tXPmw4BsKKGAK9qpTlq58i2f3dAtvq5rWwvhv0DGLBbDS +8GI5Yghcg3hTPAvAhIkj3/Zmq7DBY+T7iM3ks7loWIpo9lPJefZ+wAE2 7/M9RtjrUcLE6DujS/ghbBXTglXs+D/NFajaaTY16iydu/E9rrz0DTTNC GrfPDeICmsZwEBo7s5RaDF/RK9CBumg4eL6PAj0EUl8B7/HMvV/H46luz A==; X-CSE-ConnectionGUID: 6MIPeCk8RCqAWD1oasaV8A== X-CSE-MsgGUID: NJ659pSgRJCXOimD1GzWCg== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380058" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380058" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:08 -0700 X-CSE-ConnectionGUID: ZS4pZXDnTNaJ0mI163ivhA== X-CSE-MsgGUID: MqM3yASwQ9qY3iwFlffsXg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426210" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:06 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 05/15] ALSA: usb: Implement two-stage stream creation mechanism Date: Wed, 9 Apr 2025 13:07:20 +0200 Message-Id: <20250409110731.3752332-6-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC The USB Audio-Class (AC) device driver on ASoC side needs to know the number of available PCM streams when creating list of DAI and DAI Links before these objects are actually probed. To achieve that split existing USB stream enumeration procedure from one-stage to two-stage process: 1) parse the descriptors and obtain information about PCMs but do not create them yet 2) create all PCMs based on the previously obtained information and follow that up with MIDIs, mixers and Media resources On the streaming side, this translates to replacing existing functions that do: create_all() with new ones that: parse_pcms() create_nonpcms() As number of changes is large, the process is not part of a single patch. The switch is done in follow ups. The implementation does try to align with existing equivalent as closely as possible - no quirks and magic if-statements are skipped. Any kind of cleanup in that area is outside of the scope of this change. Existing snd_usb_create_stream() is represented by: snd_usb_streaming_iface() snd_usb_create_claim_pcm() snd_usb_create_claim_midi() Part of existing usb_audio_probe(), responsible for stream creation is represented by: snd_usb_parse_pcms() snd_usb_create_nonpcms() Existing __snd_usb_add_audio_stream() is represented by: snd_usb_parse_pcms() snd_usb_pcm_add() snd_usb_add_pcms() As ASoC is responsible for calling snd_pcm_new(), the component driver has to bind the PCMs to USB streams manually. Helper snd_usb_bind_pcm() addresses that need. Signed-off-by: Cezary Rojewski --- include/sound/usb.h | 5 ++ sound/usb/card.c | 168 ++++++++++++++++++++++++++++++++++++++++++++ sound/usb/stream.c | 145 ++++++++++++++++++++++++++++++++++++++ sound/usb/stream.h | 1 + 4 files changed, 319 insertions(+) diff --git a/include/sound/usb.h b/include/sound/usb.h index 7e7a86b5c9c4..c50fc61f357c 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -8,6 +8,9 @@ * Copyright (c) 2002 by Takashi Iwai */ +#include +#include + /* handling of USB vendor/product ID pairs as 32-bit numbers */ #define USB_ID(vendor, product) (((unsigned int)(vendor) << 16) | (product)) #define USB_ID_VENDOR(id) ((id) >> 16) @@ -87,6 +90,8 @@ struct snd_usb_audio { struct snd_intf_to_ctrl intf_to_ctrl[MAX_CARD_INTERFACES]; }; +int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); + #define USB_AUDIO_IFACE_UNUSED ((void *)-1L) #define usb_audio_err(chip, fmt, args...) \ diff --git a/sound/usb/card.c b/sound/usb/card.c index d8794adb7a88..4121ad03acd1 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -217,6 +217,110 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int return 0; } +__maybe_unused +static struct usb_interface *snd_usb_streaming_iface(struct snd_usb_audio *chip, int ctrlif, + int interface, u8 subclass) +{ + struct usb_interface_descriptor *altsd; + struct usb_device *udev = chip->dev; + struct usb_host_interface *alts; + struct usb_interface *iface; + + iface = usb_ifnum_to_if(udev, interface); + if (!iface) { + dev_err(&udev->dev, "%u:%d : does not exist\n", ctrlif, interface); + return ERR_PTR(-EINVAL); + } + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + + /* + * Android with both accessory and audio interfaces enabled gets the + * interface numbers wrong. + */ + if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) || + chip->usb_id == USB_ID(0x18d1, 0x2d05)) && + interface == 0 && + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && + altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) { + interface = 2; + iface = usb_ifnum_to_if(udev, interface); + if (!iface) + return ERR_PTR(-EINVAL); + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + } + + if (usb_interface_claimed(iface)) { + dev_dbg(&udev->dev, "%d:%d: skipping, already claimed\n", ctrlif, interface); + return ERR_PTR(-EINVAL); + } + + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING && + altsd->bInterfaceSubClass != USB_SUBCLASS_MIDISTREAMING)) { + dev_dbg(&udev->dev, "%u:%d: skipping non-supported interface %d\n", + ctrlif, interface, altsd->bInterfaceClass); + /* skip non-supported classes */ + return ERR_PTR(-EINVAL); + } + + if (altsd->bInterfaceSubClass == subclass) + return iface; + return NULL; +} + +__maybe_unused +static int snd_usb_parse_claim_pcm(struct snd_usb_audio *chip, int ctrlif, + struct usb_interface *iface, + struct usb_driver *iface_driver) +{ + struct usb_device *udev = chip->dev; + struct usb_host_interface *alt; + int ifnum; + + alt = &iface->altsetting[0]; + ifnum = alt->desc.bInterfaceNumber; + + if (snd_usb_get_speed(udev) == USB_SPEED_LOW) { + dev_err(&udev->dev, "low speed audio streaming not supported\n"); + return -EINVAL; + } + + snd_usb_add_ctrl_interface_link(chip, ifnum, ctrlif); + + if (!snd_usb_parse_audio_interface(chip, ifnum)) { + usb_set_interface(udev, ifnum, 0); /* reset the current interface */ + return usb_driver_claim_interface(iface_driver, iface, USB_AUDIO_IFACE_UNUSED); + } + + return 0; +} + +__maybe_unused +static int snd_usb_create_claim_midi(struct snd_usb_audio *chip, int ctrlif, + struct usb_interface *iface, + struct usb_driver *iface_driver) +{ + struct usb_device *udev = chip->dev; + struct usb_host_interface *alt; + int ifnum, ret; + + alt = &iface->altsetting[0]; + ifnum = alt->desc.bInterfaceNumber; + + ret = snd_usb_midi_v2_create(chip, iface, NULL, chip->usb_id); + if (ret < 0) { + dev_err(&udev->dev, "%u:%d: cannot create sequencer device\n", ctrlif, ifnum); + return -EINVAL; + } + + return usb_driver_claim_interface(iface_driver, iface, USB_AUDIO_IFACE_UNUSED); +} + /* * parse audio control descriptor and create pcm/midi streams */ @@ -737,6 +841,70 @@ get_alias_quirk(struct usb_device *dev, unsigned int id) return NULL; } +__maybe_unused +static int snd_usb_parse_pcms(struct snd_usb_audio *chip, struct usb_interface *iface, + struct usb_driver *driver) +{ + const struct snd_usb_audio_quirk *quirk = chip->quirk; + struct usb_host_interface *alt; + int ifnum, ret; + + alt = &iface->altsetting[0]; + ifnum = alt->desc.bInterfaceNumber; + + /* Check for custom parsing behavior, favor it over the generic one. */ + if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { + ret = snd_usb_parse_pcms_quirk(chip, iface, driver, quirk); + + /* Were custom pcm interfaces parsed? */ + if (ret == 1) + return 0; + if (ret) + return ret; + } + + /* Parse pcm interfaces in generic fashion. */ + ret = snd_usb_create_streams(chip, ifnum); + if (ret < 0) + return ret; + + return 0; +} + +__maybe_unused +static int snd_usb_create_nonpcms(struct snd_usb_audio *chip, struct usb_interface *iface, + struct usb_driver *driver) +{ + const struct snd_usb_audio_quirk *quirk = chip->quirk; + struct usb_host_interface *alt; + int ifnum, ret; + + alt = &iface->altsetting[0]; + ifnum = alt->desc.bInterfaceNumber; + + /* Check for custom create behavior, favor it over the generic one. */ + if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { + ret = snd_usb_create_quirk(chip, iface, driver, quirk); + + /* Were custom midi/mixer interfaces created? */ + if (ret == 1) + return 0; + if (ret) + return ret; + } + + /* Create midi/mixer interfaces in generic fashion. */ + ret = snd_usb_create_streams(chip, ifnum); + if (ret < 0) + return ret; + + ret = snd_usb_create_mixer(chip, ifnum); + if (ret < 0) + return ret; + + return 0; +} + static void snd_usb_chip_free(struct snd_usb_audio *chip) { snd_usb_free_pcms(chip); diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 6beeff03d711..1cf59e6d8ce6 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -556,6 +556,151 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, return add_chmap(pcm, stream, &as->substream[stream]); } +/* Parse and build ->pcm_list but do not create PCMs yet. */ +__maybe_unused +static int snd_usb_parse_pcm(struct snd_usb_audio *chip, int stream, struct audioformat *fp, + struct snd_usb_power_domain *pd) +{ + struct snd_usb_substream *subs; + struct snd_usb_stream *as; + + /* Is there anything to do? */ + list_for_each_entry(as, &chip->pcm_list, list) { + if (as->fmt_type != fp->fmt_type) + continue; + subs = &as->substream[stream]; + if (subs->ep_num == fp->endpoint) { + list_add_tail(&fp->list, &subs->fmt_list); + subs->num_formats++; + subs->formats |= fp->formats; + return 0; + } + } + + if (chip->card && chip->card->registered) + chip->need_delayed_register = true; + + /* + * Check for empty substreams (holes in the list) to minimize + * the number of new snd_pcms created. + */ + list_for_each_entry(as, &chip->pcm_list, list) { + if (as->fmt_type != fp->fmt_type) + continue; + subs = &as->substream[stream]; + if (!subs->num_formats) { + snd_usb_init_substream(as, stream, fp, pd); + return 0; + } + } + + /* Otherwise create a new stream. */ + as = kzalloc(sizeof(*as), GFP_KERNEL); + if (!as) + return -ENOMEM; + + as->pcm_index = chip->pcm_devs; + as->chip = chip; + as->fmt_type = fp->fmt_type; + chip->pcm_devs++; + snd_usb_init_substream(as, stream, fp, pd); + + /* + * Keep using head insertion for M-Audio Audiophile USB (tm) which has a + * fix to swap capture stream order in conf/cards/USB-audio.conf + */ + if (chip->usb_id == USB_ID(0x0763, 0x2003)) + list_add(&as->list, &chip->pcm_list); + else + list_add_tail(&as->list, &chip->pcm_list); + return 0; +} + +static int snd_usb_pcm_add(struct snd_usb_audio *chip, struct snd_usb_stream *as) +{ + struct snd_pcm *pcm; + unsigned int stream; + int ret; + + ret = snd_pcm_new(chip->card, "USB Audio", as->pcm_index, + as->substream[SNDRV_PCM_STREAM_PLAYBACK].ep_num ? 1 : 0, + as->substream[SNDRV_PCM_STREAM_CAPTURE].ep_num ? 1 : 0, + &pcm); + if (ret < 0) + return ret; + + as->pcm = pcm; + /* ->private_data taken for 'rtd' on ASoC side. */ + pcm->private_data = as; + pcm->info_flags = 0; + + if (as->pcm_index) + sprintf(pcm->name, "USB Audio #%d", as->pcm_index); + else + strscpy(pcm->name, "USB Audio", sizeof(pcm->name)); + + snd_usb_proc_pcm_format_add(as); + + for_each_pcm_streams(stream) { + if (as->substream[stream].num_formats) { + snd_usb_set_pcm_ops(pcm, stream); + snd_usb_preallocate_buffer(&as->substream[stream]); + + ret = add_chmap(pcm, stream, &as->substream[stream]); + if (ret) + return ret; + } + } + + return 0; +} + +/** + * snd_usb_bind_pcm - Bind USB stream with a PCM object. + * + * @stream_entry: USB stream, one of chip->pcm_list. + * @pcm: the PCM given USB stream shall be attached to. + * + * Note: Designed for ASoC users where snd_pcm_new() and pcm->private_data + * is owned by the framework. + */ +int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm) +{ + struct snd_usb_stream *as; + unsigned int dir; + int ret; + + as = list_entry(stream_entry, struct snd_usb_stream, list); + as->pcm = pcm; + + snd_usb_proc_pcm_format_add(as); + + for_each_pcm_streams(dir) { + if (as->substream[dir].num_formats) { + /* Buffer preallocation and PCM ops controlled by ASoC component. */ + ret = add_chmap(pcm, dir, &as->substream[dir]); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_usb_bind_pcm); + +int snd_usb_add_pcms(struct snd_usb_audio *chip) +{ + struct snd_usb_stream *as; + int ret; + + list_for_each_entry(as, &chip->pcm_list, list) { + ret = snd_usb_pcm_add(chip, as); + if (ret) + return ret; + } + return 0; +} + int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct audioformat *fp) diff --git a/sound/usb/stream.h b/sound/usb/stream.h index f70693502281..98cff9922cf1 100644 --- a/sound/usb/stream.h +++ b/sound/usb/stream.h @@ -9,6 +9,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct audioformat *fp); void snd_usb_free_pcms(struct snd_usb_audio *chip); +int snd_usb_add_pcms(struct snd_usb_audio *chip); #endif /* __USBAUDIO_STREAM_H */ From patchwork Wed Apr 9 11:07:21 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044465 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76C2225DAF8 for ; Wed, 9 Apr 2025 10:51:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195873; cv=none; b=mE3TC1bTwezwhUHL4UPHIrt2atpVg+xJDQ1CNfPUEY7nML8nqnktxdHBTWRw7lxMzJkHf2IQJkr/ODC2kvYCgJ4IFU0BN3aFFCNX/fa8P0AbNhNlVB8STPlzlxAgKjqUfKn0ZouBG1nBW20aevDjp+B11qGC4LnLRjK4RZuf88k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195873; c=relaxed/simple; bh=2h+mbAr2po2eTpLJlihR/AbRMGHhU+8myptAZ6qwy5U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=GmWwQKh814jdZOxYtHwE2kL5jy9/8jbPlKUL6n27A4+GWFHNRsEF+P8QRDPERdBy8lXvgItWW8ltGKuvro+KBFBSgQqY5CSLxMB2OND+WCZ41MtTZ1UyMwV+kXnph/RCYKmVhqScppEUzDETVQbmIgviIu1Go0tsxDIQJ9gguNA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=bfK8MCtL; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="bfK8MCtL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195871; x=1775731871; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=2h+mbAr2po2eTpLJlihR/AbRMGHhU+8myptAZ6qwy5U=; b=bfK8MCtLcNjtmaG0/UDUxeiexAZ7lJY2GLswDfLCNuMGMLPyMYLzpegQ XV3JaGIQ74fm7bgI/ix3+U8FtTuyyYe3LY/z5x1X6YB5UD2mlHuBK0iaA lVPNc8MSEAsKZxmjCwIbpRxvyTqd86iao9pxuY4KzHd51KyErcwTH+a0J sEdGE0KeMNoTPWXN0/qdiquJOKUETx9Nl5O8as7E3ac+R1uhNqiUUugW8 gEwth37C5XiYnz6QD5QXcgLsBMVwjfsuiCHdt6PcC3NyhhkLwLYYjlk+5 eRLV/mJyoTBtTPXeIBv9Kd77oU278SssBnECU4QIbRn+vU+u9qNohPCeK w==; X-CSE-ConnectionGUID: msP7T8XGQPCwXsjMMmPnRA== X-CSE-MsgGUID: 5lpcc4PETNqMAZKjIx8r/w== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380065" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380065" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:11 -0700 X-CSE-ConnectionGUID: I8S1BIFFS6eUjLMz7NDUlQ== X-CSE-MsgGUID: tvNkM+odTq6+4mNnc1SCQw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426216" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:09 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 06/15] ALSA: usb: Implement two-stage chip probing mechanism Date: Wed, 9 Apr 2025 13:07:21 +0200 Message-Id: <20250409110731.3752332-7-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC The USB Audio-Class (AC) device driver on ASoC side needs to know the number of available PCM streams when creating list of DAI and DAI Links before these objects are actually probed. To achieve that split existing USB stream enumeration procedure from one-stage to two-stage process: 1) parse the descriptors and obtain information about PCMs but do not create them yet 2) create all PCMs based on the previously obtained information and follow that up with MIDIs, mixers and Media resources As number of changes is large, the process is not part of a single patch. The switch is done in follow ups. Existing usb_audio_probe() is a non-atomic function that performs a large number of operations. These can be represented with: snd_usb_request_chip() snd_usb_find_chip() snd_usb_alloc_slot() snd_usb_new_chip() snd_usb_chip_init() snd_usb_chip_release() snd_usb_sanity_check() snd_usb_create_card() All the steps done before stream creation are performed in first-stage probing function - snd_usb_probe_unlocked(). As ASoC is responsible for calling snd_card_new(), the component driver has to bind the card to USB device manually. Helper snd_usb_bind_card() addresses that need. Signed-off-by: Cezary Rojewski --- include/linux/usb.h | 15 +++ include/linux/usb/ch9.h | 11 ++ include/sound/usb.h | 2 + sound/usb/card.c | 289 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 316 insertions(+), 1 deletion(-) diff --git a/include/linux/usb.h b/include/linux/usb.h index cfa8005e24f9..8e2f9eb93823 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -749,6 +749,21 @@ static inline const struct usb_device *__intf_to_usbdev_const(const struct usb_i const struct usb_interface *: __intf_to_usbdev_const, \ struct usb_interface *: __intf_to_usbdev)(intf) +static inline u16 usb_device_vid(struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.idVendor); +} + +static inline u16 usb_device_pid(struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.idProduct); +} + +static inline u32 usb_device_vpid(struct usb_device *udev) +{ + return ((u32)usb_device_vid(udev) << 16) | usb_device_pid(udev); +} + extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); extern struct usb_device *usb_hub_find_child(struct usb_device *hdev, diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index c93b410b314a..e0f4d9bbc8fa 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -57,4 +57,15 @@ extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, __u16 wLength); #endif +/** + * usb_epaddr_num - extract endpoint's number from its address + * @epaddr: bEndpointAddress of the USB endpoint + * + * Returns endpoint number: 0 to 15. + */ +static inline int usb_epaddr_num(unsigned char epaddr) +{ + return epaddr & USB_ENDPOINT_NUMBER_MASK; +} + #endif /* __LINUX_USB_CH9_H */ diff --git a/include/sound/usb.h b/include/sound/usb.h index c50fc61f357c..f30a8a96c49e 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -90,6 +90,8 @@ struct snd_usb_audio { struct snd_intf_to_ctrl intf_to_ctrl[MAX_CARD_INTERFACES]; }; +int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, + struct usb_driver *driver); int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); #define USB_AUDIO_IFACE_UNUSED ((void *)-1L) diff --git a/sound/usb/card.c b/sound/usb/card.c index 4121ad03acd1..a9e37fe0d29e 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -841,7 +841,6 @@ get_alias_quirk(struct usb_device *dev, unsigned int id) return NULL; } -__maybe_unused static int snd_usb_parse_pcms(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver) { @@ -918,6 +917,294 @@ static void snd_usb_chip_free(struct snd_usb_audio *chip) kfree(chip); } +/* Must be called under register_mutex due to usb_chip[] manipulation. */ +static void snd_usb_chip_release(struct snd_usb_audio *chip, struct usb_interface *iface) +{ + chip->num_interfaces--; + chip->intf[chip->num_interfaces] = NULL; + usb_set_intfdata(iface, NULL); + + if (!chip->num_interfaces) { + usb_chip[chip->index] = NULL; + if (chip->card) + snd_card_free(chip->card); + snd_usb_chip_free(chip); + } +} + +/* + * Search usb_chip[] for a chip instance matching the specified @udev. + * If found, increase chip->active before returning to block autosuspend. + */ +static struct snd_usb_audio *snd_usb_find_chip(struct usb_device *udev) +{ + struct snd_usb_audio *chip; + int i; + + for (i = 0; i < SNDRV_CARDS; i++) { + chip = usb_chip[i]; + + if (!chip || chip->dev != udev) + continue; + if (atomic_read(&chip->shutdown)) { + dev_err(&udev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); + return ERR_PTR(-EIO); + } + + /* Avoid autosuspend during probe. */ + atomic_inc(&chip->active); + return chip; + } + + return NULL; +} + +static int snd_usb_alloc_slot(struct usb_device *udev) +{ + u16 product = usb_device_pid(udev); + u16 vendor = usb_device_vid(udev); + int i; + + for (i = 0; i < SNDRV_CARDS; i++) { + if (usb_chip[i]) + continue; + if ((vid[i] == -1 || vid[i] == vendor) && + (pid[i] == -1 || pid[i] == product)) { + if (enable[i]) + return i; + if (vid[i] == vendor || pid[i] == product) { + dev_info(&udev->dev, "device (%04x:%04x) is disabled\n", + vendor, product); + return -ENOENT; + } + } + } + + return -EBUSY; +} + +static void snd_usb_chip_init(struct snd_usb_audio *chip, struct usb_interface *iface, int idx, + const struct snd_usb_audio_quirk *quirk, u32 id) +{ + struct usb_device *udev = interface_to_usbdev(iface); + + chip->index = idx; + chip->dev = udev; + chip->setup = device_setup[idx]; + chip->generic_implicit_fb = implicit_fb[idx]; + chip->autoclock = autoclock; + chip->lowlatency = lowlatency; + chip->usb_id = id; + mutex_init(&chip->mutex); + init_waitqueue_head(&chip->shutdown_wait); + + /* Avoid autosuspend during probe. */ + atomic_set(&chip->active, 1); + atomic_set(&chip->usage_count, 0); + atomic_set(&chip->shutdown, 0); + INIT_LIST_HEAD(&chip->pcm_list); + INIT_LIST_HEAD(&chip->ep_list); + INIT_LIST_HEAD(&chip->iface_ref_list); + INIT_LIST_HEAD(&chip->clock_ref_list); + INIT_LIST_HEAD(&chip->midi_list); + INIT_LIST_HEAD(&chip->midi_v2_list); + INIT_LIST_HEAD(&chip->mixer_list); + + /* Assign quirk-related information. */ + chip->quirk = quirk; + if (quirk) + chip->quirk_type = quirk->type; + if (quirk_flags[idx]) + chip->quirk_flags = quirk_flags[idx]; + else + snd_usb_init_quirk_flags(chip); + if (ignore_ctl_error) + chip->quirk_flags |= QUIRK_FLAG_IGNORE_CTL_ERROR; + + find_last_interface(chip); +} + +/* Leaves chip->active at 1. */ +static struct snd_usb_audio *snd_usb_new_chip(struct usb_interface *iface, int idx, + const struct snd_usb_audio_quirk *quirk, u32 id) +{ + struct snd_usb_audio *chip; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return NULL; + + snd_usb_chip_init(chip, iface, idx, quirk, id); + return chip; +} + +/* + * Must be called under register_mutex due to usb_chip[] manipulation. + * + * Regardless if existing chip is found or new one is created, chip->active + * is updated to avoid autosuspend. + */ +static struct snd_usb_audio *snd_usb_request_chip(struct usb_interface *iface, + const struct snd_usb_audio_quirk *quirk, u32 id) +{ + struct usb_device *udev = interface_to_usbdev(iface); + struct snd_usb_audio *chip; + int slot, ret; + + chip = snd_usb_find_chip(udev); + if (IS_ERR(chip)) + return chip; + + /* If not registered yet, create new chip. */ + if (!chip) { + ret = snd_usb_apply_boot_quirk_once(udev, iface, quirk, id); + if (ret < 0) + return ERR_PTR(ret); + + slot = snd_usb_alloc_slot(udev); + if (slot < 0) { + dev_err(&udev->dev, "no available usb audio device\n"); + return ERR_PTR(slot); + } + + chip = snd_usb_new_chip(iface, slot, quirk, id); + if (!chip) + return ERR_PTR(-ENOMEM); + + usb_chip[slot] = chip; + dev_set_drvdata(&udev->dev, chip); + } else if (chip->num_interfaces >= MAX_CARD_INTERFACES) { + dev_info(&udev->dev, "Too many interfaces assigned to the single USB-audio card\n"); + + atomic_dec(&chip->active); + return ERR_PTR(-EINVAL); + } + + chip->intf[chip->num_interfaces] = iface; + chip->num_interfaces++; + usb_set_intfdata(iface, chip); + + return chip; +} + +static int snd_usb_sanity_check(struct usb_interface *iface, + const struct snd_usb_audio_quirk *quirk) +{ + struct usb_host_interface *alts; + struct usb_device *udev; + int ifnum; + + udev = interface_to_usbdev(iface); + alts = &iface->altsetting[0]; + ifnum = get_iface_desc(alts)->bInterfaceNumber; + + if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) + return -ENXIO; + if (quirk && quirk->ifnum == QUIRK_NODEV_INTERFACE) + return -ENODEV; + + switch (snd_usb_get_speed(udev)) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + break; + default: + dev_err(&udev->dev, "unknown device speed %d\n", snd_usb_get_speed(udev)); + return -ENXIO; + } + + return 0; +} + +__maybe_unused +static int snd_usb_probe_unlocked(struct usb_interface *iface, const struct usb_device_id *usb_id, + struct usb_driver *driver) +{ + const struct snd_usb_audio_quirk *quirk; + struct usb_host_interface *alts; + struct snd_usb_audio *chip; + struct usb_device *udev; + int ret; + u32 id; + + quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; + alts = &iface->altsetting[0]; + udev = interface_to_usbdev(iface); + id = usb_device_vpid(udev); + if (get_alias_id(udev, &id)) + quirk = get_alias_quirk(udev, id); + + ret = snd_usb_sanity_check(iface, quirk); + if (ret) + return ret; + + ret = snd_usb_apply_boot_quirk(udev, iface, quirk, id); + if (ret < 0) + return ret; + + chip = snd_usb_request_chip(iface, quirk, id); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) + usb_disable_autosuspend(interface_to_usbdev(iface)); + + /* + * For devices with more than one control interface, we assume the + * first contains the audio controls. We might need a more specific + * check here in the future. + */ + if (!chip->ctrl_intf) + chip->ctrl_intf = alts; + + ret = snd_usb_parse_pcms(chip, iface, driver); + if (ret) + goto err; + return 0; +err: + snd_usb_chip_release(chip, iface); + return ret; +} + +int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, + struct usb_driver *driver) +{ + chip->card = card; + snd_usb_audio_create_proc(chip); + + atomic_sub(chip->num_interfaces, &chip->active); + return 0; +} +EXPORT_SYMBOL_GPL(snd_usb_bind_card); + +__maybe_unused +static int snd_usb_create_card(struct snd_usb_audio *chip, struct usb_interface *iface) +{ + struct usb_device *udev = chip->dev; + struct snd_card *card; + int idx = chip->index; + char component[14]; + int ret; + + ret = snd_card_new(&iface->dev, index[idx], id[idx], THIS_MODULE, 0, &card); + if (ret) { + dev_err(&udev->dev, "cannot create card instance %d\n", idx); + return ret; + } + + chip->card = card; + strscpy(card->driver, "USB-Audio", sizeof(card->driver)); + usb_audio_make_shortname(udev, chip, chip->quirk); + usb_audio_make_longname(udev, chip, chip->quirk); + sprintf(component, "USB%04x:%04x", usb_device_vid(udev), usb_device_pid(udev)); + snd_component_add(card, component); + snd_usb_audio_create_proc(chip); + + return 0; +} + /* register card if we reach to the last interface or to the specified * one given via option */ From patchwork Wed Apr 9 11:07:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044466 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1AD3025DB10 for ; Wed, 9 Apr 2025 10:51:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195875; cv=none; b=neD1wgE1lEUi4/IraVVQvwsD7lncUTxQWk7AwtqWMLKZ8ZXOp762mAFcDFST32Kr15K3f58gKKAhl9BCq6ezMaceD21Og/9sAkciNsbs+IuaKUgphbhsSK3fNsFu225wAyXB0UbkCpGiAP2G+3Uql0Y+marRi3cV14qs5Bw66HM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195875; c=relaxed/simple; bh=mQlPnCSJSEKphVGT8JzCUXd35BzymHfEWVg8bShwmj0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=C1TZuGweTnwK6JH2UcFTMOZPWNEaNYzm5glgHNePMfKjU+hB3oCgjqhpJ7Vyvg7m9HCBe9HtTLyEBfBnyiD09JIGphK2QbxGNMwItzWpmSqaF0+iUT8TJeS6HFrqJlRdYj5UaGzEu4OFCO/iqV8yuTsUnZ8H+Lhy4GrQNRr5t88= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=LoT5JhHF; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="LoT5JhHF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195874; x=1775731874; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=mQlPnCSJSEKphVGT8JzCUXd35BzymHfEWVg8bShwmj0=; b=LoT5JhHFQ1Y7JOUSE60C7QRbgJcLkpmafXE80KHfzZuHwO5nr4s1Vj+B QIuLMOr230PGbT4RmeWIdA445ym/JdR8HSR13IjX8f4ZaLGheD0bs5tL5 Boa4aooeidMF8AsxoshzvFmJddkaj4dQhpwxlc8om2SiAudRWYXGgSXbf 623PZaHLO2pecTCBOz9WiaXgcZJ78jymZ3ZO9mlBy2r63Gae3RnCSptp9 TjvQ/WUZ6egq9jrO4Z0jUoYEfB3V/D3NCAHP763LguAFsQCrcdzAIZWXB t9cUUMO2aQpByDTMfBbfvU4Y+klg8BuQ/BZ/RtjIu2xx2fhz2EpQg3tj5 Q==; X-CSE-ConnectionGUID: kTqjklgMQHq2CLQ+TINVdA== X-CSE-MsgGUID: YzUsbn8xRMGFn45/6s2a9Q== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380073" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380073" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:14 -0700 X-CSE-ConnectionGUID: 2BIqQ2NPTkCbOe/EyhKgJA== X-CSE-MsgGUID: VAHrH5UsR9SW9OOtpXcfbQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426222" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:11 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 07/15] ALSA: usb: Switch to the two-stage chip probing Date: Wed, 9 Apr 2025 13:07:22 +0200 Message-Id: <20250409110731.3752332-8-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Utilize newly added functions and update existing probing procedure of USB Audio-Class (AC) device from one-stage into two-stage process. In consequence, drop snd_usb_audio_create() and rewrite usb_audio_probe() to first do snd_usb_probe_unlocked() which is intended to be shared by both ALSA and ASoC driver before continuing with creating all the streams. While at it, do not forget about snd_audio_disconnect(). Use snd_usb_chip_release() to keep both probe() and disconnect() synchronized. Signed-off-by: Cezary Rojewski --- sound/usb/card.c | 281 ++++++++--------------------------------------- 1 file changed, 45 insertions(+), 236 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index a9e37fe0d29e..4af69a69b5ce 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -687,86 +688,6 @@ static void usb_audio_make_longname(struct usb_device *dev, } } -/* - * create a chip instance and set its names. - */ -static int snd_usb_audio_create(struct usb_interface *intf, - struct usb_device *dev, int idx, - const struct snd_usb_audio_quirk *quirk, - unsigned int usb_id, - struct snd_usb_audio **rchip) -{ - struct snd_card *card; - struct snd_usb_audio *chip; - int err; - char component[14]; - - *rchip = NULL; - - switch (snd_usb_get_speed(dev)) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - break; - default: - dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev)); - return -ENXIO; - } - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, 0, &card); - if (err < 0) { - dev_err(&dev->dev, "cannot create card instance %d\n", idx); - kfree(chip); - return err; - } - - mutex_init(&chip->mutex); - init_waitqueue_head(&chip->shutdown_wait); - chip->index = idx; - chip->dev = dev; - chip->card = card; - chip->setup = device_setup[idx]; - chip->generic_implicit_fb = implicit_fb[idx]; - chip->autoclock = autoclock; - chip->lowlatency = lowlatency; - atomic_set(&chip->active, 1); /* avoid autopm during probing */ - atomic_set(&chip->usage_count, 0); - atomic_set(&chip->shutdown, 0); - - chip->usb_id = usb_id; - INIT_LIST_HEAD(&chip->pcm_list); - INIT_LIST_HEAD(&chip->ep_list); - INIT_LIST_HEAD(&chip->iface_ref_list); - INIT_LIST_HEAD(&chip->clock_ref_list); - INIT_LIST_HEAD(&chip->midi_list); - INIT_LIST_HEAD(&chip->midi_v2_list); - INIT_LIST_HEAD(&chip->mixer_list); - - if (quirk_flags[idx]) - chip->quirk_flags = quirk_flags[idx]; - else - snd_usb_init_quirk_flags(chip); - - strcpy(card->driver, "USB-Audio"); - sprintf(component, "USB%04x:%04x", - USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); - snd_component_add(card, component); - - usb_audio_make_shortname(dev, chip, quirk); - usb_audio_make_longname(dev, chip, quirk); - - snd_usb_audio_create_proc(chip); - - *rchip = chip; - return 0; -} - /* look for a matching quirk alias id */ static bool get_alias_id(struct usb_device *dev, unsigned int *id) { @@ -870,7 +791,6 @@ static int snd_usb_parse_pcms(struct snd_usb_audio *chip, struct usb_interface * return 0; } -__maybe_unused static int snd_usb_create_nonpcms(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver) { @@ -1118,7 +1038,6 @@ static int snd_usb_sanity_check(struct usb_interface *iface, return 0; } -__maybe_unused static int snd_usb_probe_unlocked(struct usb_interface *iface, const struct usb_device_id *usb_id, struct usb_driver *driver) { @@ -1179,7 +1098,6 @@ int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, } EXPORT_SYMBOL_GPL(snd_usb_bind_card); -__maybe_unused static int snd_usb_create_card(struct snd_usb_audio *chip, struct usb_interface *iface) { struct usb_device *udev = chip->dev; @@ -1227,170 +1145,68 @@ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb_audio_probe(struct usb_interface *iface, const struct usb_device_id *usb_id) { - struct usb_device *dev = interface_to_usbdev(intf); - const struct snd_usb_audio_quirk *quirk = - (const struct snd_usb_audio_quirk *)usb_id->driver_info; - struct snd_usb_audio *chip; - int i, err; struct usb_host_interface *alts; - int ifnum; - u32 id; + struct snd_usb_audio *chip; + struct usb_device *udev; + int ifnum, ret; - alts = &intf->altsetting[0]; - ifnum = get_iface_desc(alts)->bInterfaceNumber; - id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - if (get_alias_id(dev, &id)) - quirk = get_alias_quirk(dev, id); - if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) - return -ENXIO; - if (quirk && quirk->ifnum == QUIRK_NODEV_INTERFACE) + alts = &iface->altsetting[0]; + ifnum = alts->desc.bInterfaceNumber; + udev = interface_to_usbdev(iface); + + udev = interface_to_usbdev(iface); + if (udev->audsb_capable) return -ENODEV; - err = snd_usb_apply_boot_quirk(dev, intf, quirk, id); - if (err < 0) - return err; - - /* - * found a config. now register to ALSA - */ - - /* check whether it's already registered */ - chip = NULL; - mutex_lock(®ister_mutex); - for (i = 0; i < SNDRV_CARDS; i++) { - if (usb_chip[i] && usb_chip[i]->dev == dev) { - if (atomic_read(&usb_chip[i]->shutdown)) { - dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); - err = -EIO; - goto __error; - } - chip = usb_chip[i]; - atomic_inc(&chip->active); /* avoid autopm */ - break; - } - } - if (! chip) { - err = snd_usb_apply_boot_quirk_once(dev, intf, quirk, id); - if (err < 0) - goto __error; - - /* it's a fresh one. - * now look for an empty slot and create a new card instance - */ - for (i = 0; i < SNDRV_CARDS; i++) - if (!usb_chip[i] && - (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && - (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { - if (enable[i]) { - err = snd_usb_audio_create(intf, dev, i, quirk, - id, &chip); - if (err < 0) - goto __error; - break; - } else if (vid[i] != -1 || pid[i] != -1) { - dev_info(&dev->dev, - "device (%04x:%04x) is disabled\n", - USB_ID_VENDOR(id), - USB_ID_PRODUCT(id)); - err = -ENOENT; - goto __error; - } - } - if (!chip) { - dev_err(&dev->dev, "no available usb audio device\n"); - err = -ENODEV; - goto __error; - } - find_last_interface(chip); - } - - if (chip->num_interfaces >= MAX_CARD_INTERFACES) { - dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n"); - err = -EINVAL; - goto __error; + guard(mutex)(®ister_mutex); + + ret = snd_usb_probe_unlocked(iface, usb_id, &usb_audio_driver); + chip = dev_get_drvdata(&udev->dev); + /* If chip isn't there, probe failed at the first interface. */ + if (!chip) + return ret; + if (ret) + goto err_try_register; + if (!chip->card) { + ret = snd_usb_create_card(chip, iface); + if (ret) + goto err_no_register; } - dev_set_drvdata(&dev->dev, chip); + ret = snd_usb_add_pcms(chip); + if (ret) + goto err_try_register; - if (ignore_ctl_error) - chip->quirk_flags |= QUIRK_FLAG_IGNORE_CTL_ERROR; - - if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) - usb_disable_autosuspend(interface_to_usbdev(intf)); - - /* - * For devices with more than one control interface, we assume the - * first contains the audio controls. We might need a more specific - * check here in the future. - */ - if (!chip->ctrl_intf) - chip->ctrl_intf = alts; - - err = 1; /* continue */ - if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { - /* need some special handlings */ - err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk); - if (err < 0) - goto __error; - } - - if (err > 0) { - /* create normal USB audio interfaces */ - err = snd_usb_create_streams(chip, ifnum); - if (err < 0) - goto __error; - err = snd_usb_create_mixer(chip, ifnum); - if (err < 0) - goto __error; - } + ret = snd_usb_create_nonpcms(chip, iface, &usb_audio_driver); + if (ret) + goto err_try_register; if (chip->need_delayed_register) { - dev_info(&dev->dev, - "Found post-registration device assignment: %08x:%02x\n", + dev_info(&udev->dev, "Found post-registration device assignment: %08x:%02x\n", chip->usb_id, ifnum); chip->need_delayed_register = false; /* clear again */ } - err = try_to_register_card(chip, ifnum); - if (err < 0) - goto __error_no_register; + ret = try_to_register_card(chip, ifnum); + if (ret < 0) + goto err_no_register; - /* don't want to fail when snd_media_device_create() fails */ - snd_media_device_create(chip, intf); + /* Media device is optional, ignore error checks. */ + snd_media_device_create(chip, iface); - if (quirk) - chip->quirk_type = quirk->type; - - usb_chip[chip->index] = chip; - chip->intf[chip->num_interfaces] = intf; - chip->num_interfaces++; - usb_set_intfdata(intf, chip); atomic_dec(&chip->active); - mutex_unlock(®ister_mutex); return 0; - __error: - /* in the case of error in secondary interface, still try to register */ - if (chip) - try_to_register_card(chip, ifnum); +err_try_register: + /* In the case of error in secondary interface, still try to register. */ + try_to_register_card(chip, ifnum); - __error_no_register: - if (chip) { - /* chip->active is inside the chip->card object, - * decrement before memory is possibly returned. - */ - atomic_dec(&chip->active); - if (!chip->num_interfaces) { - snd_card_free(chip->card); - snd_usb_chip_free(chip); - } - } - mutex_unlock(®ister_mutex); - return err; +err_no_register: + atomic_dec(&chip->active); + snd_usb_chip_release(chip, iface); + return ret; } /* @@ -1445,15 +1261,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) usb_enable_autosuspend(interface_to_usbdev(intf)); - chip->num_interfaces--; - if (chip->num_interfaces <= 0) { - usb_chip[chip->index] = NULL; - mutex_unlock(®ister_mutex); - snd_card_free(card); - snd_usb_chip_free(chip); - } else { - mutex_unlock(®ister_mutex); - } + snd_usb_chip_release(chip, intf); + mutex_unlock(®ister_mutex); } /* lock the shutdown (disconnect) task and autoresume */ From patchwork Wed Apr 9 11:07:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044467 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F01ED25DB0F for ; Wed, 9 Apr 2025 10:51:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195878; cv=none; b=hrj8Scfs1og71FqKR60zVBW33MLFqPW55rBzmX4V7SKOJ+713YqzLyctOC9Sl4l9rQR9mVXkfAPDoD0tnqtX3Ugybd4fpVjf+5e/1wWVpVJ8uivxSifUbO6oZj5Sg2OWlvDN7H+HYnkkswkTKE8sx6jtOrW3Z0rGGO16sr3crbc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195878; c=relaxed/simple; bh=W2wZjTHSGDkn6hahtiKEQLpFMYjPEQsk1hEJklz/eHU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YTLQNHi+P2CwzlczY3Vl+60xQHxapWVGoClQO/k3MLp8kmw6JkE+7yS2Fh9os+pqGtb6RhWf/Z/Ri2KHA7JBNiCR6qkJprBGr1fEXaO+AqPvTxonnrelWrlEUYWu8pw8W/8bY7YeYBqoAwFymtddo9HzQdXr1R7MvycWthZ/0tw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=aloMUvqc; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="aloMUvqc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195877; x=1775731877; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=W2wZjTHSGDkn6hahtiKEQLpFMYjPEQsk1hEJklz/eHU=; b=aloMUvqcDavECHehHFgNhzuILZ1enXv2otfi7togJHDO16tMK3eLDF9e qK5bFn/sq4AHpyyPDrc0et4MZZ6mkg41sjhVsp5E9QWtuwIxCJaUDHhl8 peYjANZqB5ZIk0LFJbd0eS9SR2YvJyPYtjfqMpwKc0Y1tCZ4aCkK6GHD4 9QECQRHU4w4K5ARCMbBTU4cRiOLQp/95fleTaH64R+avePtBDjQ/U7Pqz oryFEGSjL64QpXaH4L7056C3+qiTvO7Kj0EMU/cjYj9icbLWE4yaQIUke qSROzDGHkC6Gp8KhDEqCdtVG0ZY6yUInsBR3ZFJm6TYXj6r07kopcyvnY A==; X-CSE-ConnectionGUID: JfKx3oswRNi5ZBL5a1G0gg== X-CSE-MsgGUID: yXTqC/7YRr+AzLrO3oA3Gw== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380081" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380081" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:17 -0700 X-CSE-ConnectionGUID: NdX5UzbrTfKTFNIiEuXBmA== X-CSE-MsgGUID: Whkw/dzaSJmS5vxmXZrUAg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426231" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:14 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 08/15] ALSA: usb: Switch to the two-stage stream creation Date: Wed, 9 Apr 2025 13:07:23 +0200 Message-Id: <20250409110731.3752332-9-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Utilize newly added functions and update existing stream craetion procedure for USB Audio-Class (AC) device from one-stage into two-stage process. In consequence, snd_usb_create_stream() and __snd_usb_add_audio_stream(). To complete the process, update snd_usb_create_streams() so that it performs correct operation: parse pcms -or- create non-pcms based on the SubClass value of the USB interface. Signed-off-by: Cezary Rojewski --- sound/usb/card.c | 119 ++++++++++----------------------------------- sound/usb/stream.c | 97 +----------------------------------- 2 files changed, 29 insertions(+), 187 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 4af69a69b5ce..354bf76988ab 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -138,87 +138,6 @@ static void snd_usb_stream_disconnect(struct snd_usb_stream *as) } } -static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface) -{ - struct usb_device *dev = chip->dev; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_interface *iface = usb_ifnum_to_if(dev, interface); - - if (!iface) { - dev_err(&dev->dev, "%u:%d : does not exist\n", - ctrlif, interface); - return -EINVAL; - } - - alts = &iface->altsetting[0]; - altsd = get_iface_desc(alts); - - /* - * Android with both accessory and audio interfaces enabled gets the - * interface numbers wrong. - */ - if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) || - chip->usb_id == USB_ID(0x18d1, 0x2d05)) && - interface == 0 && - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && - altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) { - interface = 2; - iface = usb_ifnum_to_if(dev, interface); - if (!iface) - return -EINVAL; - alts = &iface->altsetting[0]; - altsd = get_iface_desc(alts); - } - - if (usb_interface_claimed(iface)) { - dev_dbg(&dev->dev, "%d:%d: skipping, already claimed\n", - ctrlif, interface); - return -EINVAL; - } - - if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && - altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = snd_usb_midi_v2_create(chip, iface, NULL, - chip->usb_id); - if (err < 0) { - dev_err(&dev->dev, - "%u:%d: cannot create sequencer device\n", - ctrlif, interface); - return -EINVAL; - } - return usb_driver_claim_interface(&usb_audio_driver, iface, - USB_AUDIO_IFACE_UNUSED); - } - - if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && - altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || - altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) { - dev_dbg(&dev->dev, - "%u:%d: skipping non-supported interface %d\n", - ctrlif, interface, altsd->bInterfaceClass); - /* skip non-supported classes */ - return -EINVAL; - } - - if (snd_usb_get_speed(dev) == USB_SPEED_LOW) { - dev_err(&dev->dev, "low speed audio streaming not supported\n"); - return -EINVAL; - } - - snd_usb_add_ctrl_interface_link(chip, interface, ctrlif); - - if (! snd_usb_parse_audio_interface(chip, interface)) { - usb_set_interface(dev, interface, 0); /* reset the current interface */ - return usb_driver_claim_interface(&usb_audio_driver, iface, - USB_AUDIO_IFACE_UNUSED); - } - - return 0; -} - -__maybe_unused static struct usb_interface *snd_usb_streaming_iface(struct snd_usb_audio *chip, int ctrlif, int interface, u8 subclass) { @@ -274,7 +193,6 @@ static struct usb_interface *snd_usb_streaming_iface(struct snd_usb_audio *chip, return NULL; } -__maybe_unused static int snd_usb_parse_claim_pcm(struct snd_usb_audio *chip, int ctrlif, struct usb_interface *iface, struct usb_driver *iface_driver) @@ -301,7 +219,6 @@ static int snd_usb_parse_claim_pcm(struct snd_usb_audio *chip, int ctrlif, return 0; } -__maybe_unused static int snd_usb_create_claim_midi(struct snd_usb_audio *chip, int ctrlif, struct usb_interface *iface, struct usb_driver *iface_driver) @@ -325,18 +242,30 @@ static int snd_usb_create_claim_midi(struct snd_usb_audio *chip, int ctrlif, /* * parse audio control descriptor and create pcm/midi streams */ -static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) +static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif, + struct usb_driver *iface_driver, u8 subclass) { + int (*create_stream)(struct snd_usb_audio *, int, + struct usb_interface *, struct usb_driver *); struct usb_device *dev = chip->dev; struct usb_host_interface *host_iface; struct usb_interface_descriptor *altsd; + struct usb_interface *iface; int i, protocol; + int ifnum; /* find audiocontrol interface */ host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; altsd = get_iface_desc(host_iface); protocol = altsd->bInterfaceProtocol; + if (subclass == USB_SUBCLASS_AUDIOSTREAMING) + create_stream = snd_usb_parse_claim_pcm; + else if (subclass == USB_SUBCLASS_MIDISTREAMING) + create_stream = snd_usb_create_claim_midi; + else + return -EINVAL; + switch (protocol) { default: dev_warn(&dev->dev, @@ -385,8 +314,12 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } - for (i = 0; i < h1->bInCollection; i++) - snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); + for (i = 0; i < h1->bInCollection; i++) { + ifnum = h1->baInterfaceNr[i]; + iface = snd_usb_streaming_iface(chip, ctrlif, ifnum, subclass); + if (!IS_ERR_OR_NULL(iface)) + create_stream(chip, ctrlif, iface, iface_driver); + } break; } @@ -431,10 +364,12 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } for (i = 0; i < assoc->bInterfaceCount; i++) { - int intf = assoc->bFirstInterface + i; - - if (intf != ctrlif) - snd_usb_create_stream(chip, ctrlif, intf); + ifnum = assoc->bFirstInterface + i; + if (ifnum == ctrlif) + continue; + iface = snd_usb_streaming_iface(chip, ctrlif, ifnum, subclass); + if (!IS_ERR_OR_NULL(iface)) + create_stream(chip, ctrlif, iface, iface_driver); } break; @@ -784,7 +719,7 @@ static int snd_usb_parse_pcms(struct snd_usb_audio *chip, struct usb_interface * } /* Parse pcm interfaces in generic fashion. */ - ret = snd_usb_create_streams(chip, ifnum); + ret = snd_usb_create_streams(chip, ifnum, driver, USB_SUBCLASS_AUDIOSTREAMING); if (ret < 0) return ret; @@ -813,7 +748,7 @@ static int snd_usb_create_nonpcms(struct snd_usb_audio *chip, struct usb_interfa } /* Create midi/mixer interfaces in generic fashion. */ - ret = snd_usb_create_streams(chip, ifnum); + ret = snd_usb_create_streams(chip, ifnum, driver, USB_SUBCLASS_MIDISTREAMING); if (ret < 0) return ret; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 1cf59e6d8ce6..601dc9ed020a 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -94,8 +94,6 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, subs->pkt_offset_adj = 0; subs->stream_offset_adj = 0; - snd_usb_set_pcm_ops(as->pcm, stream); - list_add_tail(&fp->list, &subs->fmt_list); subs->formats |= fp->formats; subs->num_formats++; @@ -110,8 +108,6 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, snd_usb_power_domain_set(subs->stream->chip, pd, UAC3_PD_STATE_D1); } - - snd_usb_preallocate_buffer(subs); } /* kctl callbacks for usb-audio channel maps */ @@ -468,96 +464,7 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor return chmap; } -/* - * add this endpoint to the chip instance. - * if a stream with the same endpoint already exists, append to it. - * if not, create a new pcm stream. note, fp is added to the substream - * fmt_list and will be freed on the chip instance release. do not free - * fp or do remove it from the substream fmt_list to avoid double-free. - */ -static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, - int stream, - struct audioformat *fp, - struct snd_usb_power_domain *pd) - -{ - struct snd_usb_stream *as; - struct snd_usb_substream *subs; - struct snd_pcm *pcm; - int err; - - list_for_each_entry(as, &chip->pcm_list, list) { - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (subs->ep_num == fp->endpoint) { - list_add_tail(&fp->list, &subs->fmt_list); - subs->num_formats++; - subs->formats |= fp->formats; - return 0; - } - } - - if (chip->card->registered) - chip->need_delayed_register = true; - - /* look for an empty stream */ - list_for_each_entry(as, &chip->pcm_list, list) { - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (subs->ep_num) - continue; - err = snd_pcm_new_stream(as->pcm, stream, 1); - if (err < 0) - return err; - snd_usb_init_substream(as, stream, fp, pd); - return add_chmap(as->pcm, stream, subs); - } - - /* create a new pcm */ - as = kzalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return -ENOMEM; - as->pcm_index = chip->pcm_devs; - as->chip = chip; - as->fmt_type = fp->fmt_type; - err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, - &pcm); - if (err < 0) { - kfree(as); - return err; - } - as->pcm = pcm; - pcm->private_data = as; - pcm->info_flags = 0; - if (chip->pcm_devs > 0) - sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); - else - strcpy(pcm->name, "USB Audio"); - - snd_usb_init_substream(as, stream, fp, pd); - - /* - * Keep using head insertion for M-Audio Audiophile USB (tm) which has a - * fix to swap capture stream order in conf/cards/USB-audio.conf - */ - if (chip->usb_id == USB_ID(0x0763, 0x2003)) - list_add(&as->list, &chip->pcm_list); - else - list_add_tail(&as->list, &chip->pcm_list); - - chip->pcm_devs++; - - snd_usb_proc_pcm_format_add(as); - - return add_chmap(pcm, stream, &as->substream[stream]); -} - /* Parse and build ->pcm_list but do not create PCMs yet. */ -__maybe_unused static int snd_usb_parse_pcm(struct snd_usb_audio *chip, int stream, struct audioformat *fp, struct snd_usb_power_domain *pd) { @@ -705,7 +612,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct audioformat *fp) { - return __snd_usb_add_audio_stream(chip, stream, fp, NULL); + return snd_usb_parse_pcm(chip, stream, fp, NULL); } static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip, @@ -713,7 +620,7 @@ static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip, struct audioformat *fp, struct snd_usb_power_domain *pd) { - return __snd_usb_add_audio_stream(chip, stream, fp, pd); + return snd_usb_parse_pcm(chip, stream, fp, pd); } static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, From patchwork Wed Apr 9 11:07:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044468 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6925525DB0F for ; Wed, 9 Apr 2025 10:51:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195880; cv=none; b=k5CmMBpfQ2i3grt0f9sgjeFLzHDJVEcPZcyBaq6DHNrdplpwoyZvfLdXXNfRpJtrph/KfwfSYx4HtAJ4VOX+AecSlaHccGZfXv8TsDg1oX2FhRkwS/NDQ9LARsBGZOuE2iBZswhpFftVSHDez69xkCXNy/jJwzNsYaxASkUyquU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195880; c=relaxed/simple; bh=lbymKk2ajwterb4kUk0MAqFiA6Az++OGnOfHl4m1vig=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=P9qgS4OOPw+zs8lFZ697ALjy5cAM7FzFNMjXwKna1Vt143PSH4vBONtgtBoDQgLfKK3cmtivU6D9Jhvm7knKLWuk81dmrg45VI+trKOyirUVMlLDu0w9Jt4T/DlnJgsPr3P1C79zancqmpeuX1ztdvchXjzVwCx33jQI3R2/FSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=iC7XEcSf; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="iC7XEcSf" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195879; x=1775731879; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=lbymKk2ajwterb4kUk0MAqFiA6Az++OGnOfHl4m1vig=; b=iC7XEcSfDG/tsUXWoZy692IflLfMUYGgAJL2zviU2Drfl1DOYdormKR2 yA4MWPh3uUk+QCwKTGkQpNRdPxlNJJ0LeF6M3sPs/mMDg5YSEl5e3yNdG h/HD02Gydkq1CqVs6TUjXEROEJ4r1TgPQ+btV1tb66BWH4YskHiixg91N UM4c/Pmyob93oj4vddGpYgR+nciX7DTinY5Sa3G1dJaHgc9eNiY3Av9jR 6+v8ZY+iX587ct2FHaY1h9Fyboqpz5M8bsBNZ3KxN6JepplRO8J5o+Hyw bLBJx6YML4UnEwKTrTIp4aMjNW9/j4N1l4/mA/NtnaSoU9LmDY/IWQhHP g==; X-CSE-ConnectionGUID: yru8glubRoy6quWwEhjEGQ== X-CSE-MsgGUID: lc5b3n+DQPyC8r68WIel4Q== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380092" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380092" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:19 -0700 X-CSE-ConnectionGUID: YSObAZF2Q0WJp4iWFwUdDg== X-CSE-MsgGUID: l9oUkYzgSISBhIvFlQzAgg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426239" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:17 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 09/15] ALSA: usb: Switch to the two-stage quirk applying Date: Wed, 9 Apr 2025 13:07:24 +0200 Message-Id: <20250409110731.3752332-10-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Utilize newly added functions and update existing apply-quirks procedure for USB Audio-Class (AC) device from one-stage into two-stage process. In consequence, drop create_uaxx_quirk() and create_autodetect_quirk() and update existing snd_usb_create_quirk() so that it only creates new streams, leaving the PCM parsing process to snd_usb_parse_pcms_quirk(). Signed-off-by: Cezary Rojewski --- sound/usb/quirks.c | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4e402c6406a9..037a45c79fbe 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -412,7 +412,6 @@ static int create_autodetect_pcm_quirk(struct snd_usb_audio *chip, return create_auto_pcm_quirk(chip, iface, driver); } -__maybe_unused static int create_autodetect_midi_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, @@ -423,19 +422,6 @@ static int create_autodetect_midi_quirk(struct snd_usb_audio *chip, return 1; } -static int create_autodetect_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) -{ - int err; - - err = create_auto_pcm_quirk(chip, iface, driver); - if (err == -ENODEV) - err = create_auto_midi_quirk(chip, iface, driver); - return err; -} - static int create_uaxx_pcm_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, @@ -556,30 +542,6 @@ static int create_uaxx_midi_quirk(struct snd_usb_audio *chip, &chip->num_rawmidis); } -static int create_uaxx_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) -{ - struct usb_interface_descriptor *altsd; - struct usb_host_interface *alts; - - /* both PCM and MIDI interfaces have 2 or more altsettings */ - if (iface->num_altsetting < 2) - return -ENXIO; - alts = &iface->altsetting[1]; - altsd = get_iface_desc(alts); - - switch (altsd->bNumEndpoints) { - case 2: - return create_uaxx_midi_quirk(chip, iface, driver, quirk); - case 1: - return create_uaxx_pcm_quirk(chip, iface, driver, quirk); - default: - return -ENXIO; - } -} - /* * Create a standard mixer for the specified interface. */ @@ -652,7 +614,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, static const quirk_func_t quirk_funcs[] = { [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, [QUIRK_COMPOSITE] = create_composite_quirk, - [QUIRK_AUTODETECT] = create_autodetect_quirk, + [QUIRK_AUTODETECT] = create_autodetect_midi_quirk, [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk, [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk, [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk, @@ -667,7 +629,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_MIDI_CH345] = create_any_midi_quirk, [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, - [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, + [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_midi_quirk, [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, }; From patchwork Wed Apr 9 11:07:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044469 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3DD9F25DB0F for ; Wed, 9 Apr 2025 10:51:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195884; cv=none; b=JUf4HbTDl10m4Ox4W/94BaMijQQ3C0bsR1ixgiTCxRJ1rwy804Wsg69dll1lvnKNraATQItiiKxDbMOmYktXzEMB2rcCaDJBXV7p7nzL+wEvWpbstyuIPSPmAb4WUTEyL0cWEvptTtgEQ2k/ukPU3MooJY4gk8eddkq8/uuhyJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195884; c=relaxed/simple; bh=6FkBpWVnrOGogjTK4mDjWYFjYSsXNGDoRjAoHJqwrzQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mGG9yZDsO7vuE7i8iHkDdRX2wSi9CJmt4u5Ja3UoT6whQLHi4QDh1p/sfzlKMdSAR7tGBddHynhsU046WriHETRIsTEPODvNIjjMbz3iGcTCu22j+GPM0kwpOXWdnuNllK4iLaw3jS4qZou1CwCdaqXRAvJ2GP5cwF0qnCNnQy4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=CsW9e0RV; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="CsW9e0RV" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195882; x=1775731882; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=6FkBpWVnrOGogjTK4mDjWYFjYSsXNGDoRjAoHJqwrzQ=; b=CsW9e0RVnifGV79BNnK8kfvBmRAYZOeR80UsDwRDm7Xb7/GEMuBhSkWn yJ0rv5Y73gXzCRm5aui1RXM7Ir09rmGb1OKU+M+4AC1g0VCyPQbU5THe4 06kbSW+vH5LP/wWXZsj2qZ07WMESsQQLAPz1JLOR9gYVtS1j6iQiFsGLo CToeABlxdB8NgHHceI5vu40cXL8Ze6+XHECNvPALnBRrKrN2zIMbYOgW1 kMorJjzW1FQ8GzhSViZSQzXezYsv+6DefemcTWQYT/rGexODAFHFU86h0 OWGtU7x0/QSnMeABA5QK2Gj2f/vtRg9cv/5X1AfvACutG8KfTxVp292mE Q==; X-CSE-ConnectionGUID: pYZApMysSwuAIamObA60JA== X-CSE-MsgGUID: Jiic3rJjQvmy+UDWaeexvA== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380099" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380099" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:22 -0700 X-CSE-ConnectionGUID: xIZo3bj3SNSkWemXmeZkwA== X-CSE-MsgGUID: Y1Tye1K2TuSnjosRS4qMog== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426244" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:19 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 10/15] ALSA: usb: Export PCM operations Date: Wed, 9 Apr 2025 13:07:25 +0200 Message-Id: <20250409110731.3752332-11-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Existing PCM operations can be reused by DAIs on the ASoC side. Not all of them are exported as endpoints of the offloaded USB device are entirely controlled by the Audio Sideband and AudioDSP hardware. As ASoC occupies substream->private_data, update existing open() and close() to utilize runtime->private_data for obtaining USB stream instead. Provide a wrapper for ASoC driver i.e.: snd_usb_pcm_open() as they are unaware of details of USB streams and do not access USB substreams directly. Signed-off-by: Cezary Rojewski --- include/sound/usb.h | 9 +++ sound/usb/pcm.c | 159 +++++++++++++++++++++++++++++++------------- 2 files changed, 120 insertions(+), 48 deletions(-) diff --git a/include/sound/usb.h b/include/sound/usb.h index f30a8a96c49e..daba868fb6e0 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -93,6 +93,15 @@ struct snd_usb_audio { int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, struct usb_driver *driver); int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); +void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw); + +/* PCM operations, see struct snd_pcm_ops. */ +int snd_usb_pcm_open(struct snd_pcm_substream *substream); +int snd_usb_pcm_close(struct snd_pcm_substream *substream); +int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +int snd_usb_pcm_hw_free(struct snd_pcm_substream *substream); +int snd_usb_pcm_prepare(struct snd_pcm_substream *substream); #define USB_AUDIO_IFACE_UNUSED ((void *)-1L) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ea698f061af9..e04e47c0a35e 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -471,8 +471,8 @@ static void close_endpoints(struct snd_usb_audio *chip, * if sg buffer is supported on the later version of alsa, we'll follow * that. */ -static int snd_usb_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_usb_substream *subs = substream->runtime->private_data; struct snd_usb_audio *chip = subs->stream->chip; @@ -579,13 +579,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, return ret; } +EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_params); /* * hw_free callback * * reset the audio format and release the buffer */ -static int snd_usb_hw_free(struct snd_pcm_substream *substream) +int snd_usb_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_usb_substream *subs = substream->runtime->private_data; struct snd_usb_audio *chip = subs->stream->chip; @@ -603,6 +604,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) return 0; } +EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_free); /* free-wheeling mode? (e.g. dmix) */ static int in_free_wheeling_mode(struct snd_pcm_runtime *runtime) @@ -634,7 +636,7 @@ static int lowlatency_playback_available(struct snd_pcm_runtime *runtime, * * only a few subtle things... */ -static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) +int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; @@ -693,6 +695,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) snd_usb_unlock_shutdown(chip); return ret; } +EXPORT_SYMBOL_GPL(snd_usb_pcm_prepare); /* * h/w constraints @@ -1075,6 +1078,47 @@ static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params, return apply_hw_params_minmax(it, rmin, rmax); } +static void usb_pcm_hw_init(struct snd_usb_substream *subs, struct snd_pcm_hardware *hw) +{ + const struct audioformat *fp; + + hw->formats = subs->formats; + hw->rate_min = 0x7fffffff; + hw->rate_max = 0; + hw->channels_min = 256; + hw->channels_max = 0; + hw->rates = 0; + + /* Now reduce the scope based on substream capabilities. */ + list_for_each_entry(fp, &subs->fmt_list, list) { + hw->rates |= fp->rates; + if (hw->rate_min > fp->rate_min) + hw->rate_min = fp->rate_min; + if (hw->rate_max < fp->rate_max) + hw->rate_max = fp->rate_max; + if (hw->channels_min > fp->channels) + hw->channels_min = fp->channels; + if (hw->channels_max < fp->channels) + hw->channels_max = fp->channels; + if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) { + /* FIXME: there might be more than one audio formats... */ + hw->period_bytes_min = fp->frame_size; + hw->period_bytes_max = fp->frame_size; + } + } +} + +void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw) +{ + struct snd_usb_stream *as; + + if (dir <= SNDRV_PCM_STREAM_LAST) { + as = list_entry(stream_entry, struct snd_usb_stream, list); + usb_pcm_hw_init(&as->substream[dir], hw); + } +} +EXPORT_SYMBOL_GPL(snd_usb_pcm_hw_init); + /* * set up the runtime hardware information. */ @@ -1086,30 +1130,10 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre int param_period_time_if_needed = -1; int err; - runtime->hw.formats = subs->formats; - - runtime->hw.rate_min = 0x7fffffff; - runtime->hw.rate_max = 0; - runtime->hw.channels_min = 256; - runtime->hw.channels_max = 0; - runtime->hw.rates = 0; ptmin = UINT_MAX; - /* check min/max rates and channels */ + usb_pcm_hw_init(subs, &runtime->hw); + list_for_each_entry(fp, &subs->fmt_list, list) { - runtime->hw.rates |= fp->rates; - if (runtime->hw.rate_min > fp->rate_min) - runtime->hw.rate_min = fp->rate_min; - if (runtime->hw.rate_max < fp->rate_max) - runtime->hw.rate_max = fp->rate_max; - if (runtime->hw.channels_min > fp->channels) - runtime->hw.channels_min = fp->channels; - if (runtime->hw.channels_max < fp->channels) - runtime->hw.channels_max = fp->channels; - if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) { - /* FIXME: there might be more than one audio formats... */ - runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = - fp->frame_size; - } pt = 125 * (1 << fp->datainterval); ptmin = min(ptmin, pt); } @@ -1202,12 +1226,12 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre return 0; } -static int snd_usb_pcm_open(struct snd_pcm_substream *substream) +static int __usb_pcm_open(struct snd_pcm_substream *substream) { - int direction = substream->stream; - 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]; + struct snd_usb_substream *subs = runtime->private_data; + struct snd_usb_stream *as = subs->stream; + int direction = substream->stream; int ret; runtime->hw = snd_usb_hardware; @@ -1215,7 +1239,6 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) if (direction == SNDRV_PCM_STREAM_PLAYBACK && as->chip->lowlatency) runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR; - runtime->private_data = subs; subs->pcm_substream = substream; /* runtime PM is also done there */ @@ -1227,36 +1250,76 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) ret = setup_hw_info(runtime, subs); if (ret < 0) return ret; - ret = snd_usb_autoresume(subs->stream->chip); + ret = snd_usb_autoresume(as->chip); if (ret < 0) return ret; - ret = snd_media_stream_init(subs, as->pcm, direction); + ret = snd_media_stream_init(subs, substream->pcm, direction); if (ret < 0) - snd_usb_autosuspend(subs->stream->chip); + snd_usb_autosuspend(as->chip); return ret; } -static int snd_usb_pcm_close(struct snd_pcm_substream *substream) +static int usb_pcm_open(struct snd_pcm_substream *substream) { - int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); - struct snd_usb_substream *subs = &as->substream[direction]; + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On ASoC side, substream->private_data is occupied by the + * framework. For ALSA and ASoC to share USB PCM operations + * runtime->private_data shall be utilized instead. + */ + runtime->private_data = &as->substream[substream->stream]; + + return __usb_pcm_open(substream); +} + +int snd_usb_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_usb_stream *as; + + /* + * On ASoC side, substream->private_data is occupied by the + * framework. For ALSA and ASoC to share USB PCM operations + * runtime->private_data shall be utilized instead. + */ + as = list_entry(runtime->private_data, struct snd_usb_stream, list); + runtime->private_data = &as->substream[substream->stream]; + + return __usb_pcm_open(substream); +} +EXPORT_SYMBOL_GPL(snd_usb_pcm_open); + +int snd_usb_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + struct snd_usb_stream *as = subs->stream; int ret; snd_media_stop_pipeline(subs); - if (!snd_usb_lock_shutdown(subs->stream->chip)) { + if (!snd_usb_lock_shutdown(as->chip)) { ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1); - snd_usb_unlock_shutdown(subs->stream->chip); + snd_usb_unlock_shutdown(as->chip); if (ret < 0) return ret; } subs->pcm_substream = NULL; - snd_usb_autosuspend(subs->stream->chip); + snd_usb_autosuspend(as->chip); return 0; } +EXPORT_SYMBOL_GPL(snd_usb_pcm_close); + +static int usb_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = snd_usb_pcm_close(substream); + + substream->runtime->private_data = NULL; + return ret; +} /* Since a URB can handle only a single linear buffer, we must use double * buffering when the data to be transferred overflows the buffer boundary. @@ -1744,10 +1807,10 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream } static const struct snd_pcm_ops snd_usb_playback_ops = { - .open = snd_usb_pcm_open, - .close = snd_usb_pcm_close, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, + .open = usb_pcm_open, + .close = usb_pcm_close, + .hw_params = snd_usb_pcm_hw_params, + .hw_free = snd_usb_pcm_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_substream_playback_trigger, .sync_stop = snd_usb_pcm_sync_stop, @@ -1756,10 +1819,10 @@ static const struct snd_pcm_ops snd_usb_playback_ops = { }; static const struct snd_pcm_ops snd_usb_capture_ops = { - .open = snd_usb_pcm_open, - .close = snd_usb_pcm_close, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, + .open = usb_pcm_open, + .close = usb_pcm_close, + .hw_params = snd_usb_pcm_hw_params, + .hw_free = snd_usb_pcm_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_substream_capture_trigger, .sync_stop = snd_usb_pcm_sync_stop, From patchwork Wed Apr 9 11:07:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044470 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 82A3425DAE3 for ; Wed, 9 Apr 2025 10:51:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195886; cv=none; b=NRTjNjECoDtjapg1s0/2ONAbPSmivAaBy9uYqgP/ziMDRAJPhGFZJzMN2Thz4a/ajRFECPo8aZ6Rz4t/jG0fbsQwb1/skfuynwcDV4JEF0+aLdo644+DAgZ9EHgRWdi8cIx7ZOU4uxKGM519pyRanHAKulUjGmV1nhi7GyIBfCo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195886; c=relaxed/simple; bh=zCc1I/rsU18c5TSxaIaapvgK+iNm2ZW99PbX2aByn6k=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XimIsyJl3UpLZeAsp/K9RBg0JhdL9AEiY+wLUZd0v0X8BcGf7JghHZEutocQXppLUZKdNaLRrDUvXfigXQFl9KWAmOJC95BuW98apkRHSDLw07gByqwmlXAK/xcmLgbXYJzFvEE4xJpLASWv+qZNv+1PfGJZmGW/brz9QAIn7q4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=UStQwVx/; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="UStQwVx/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195884; x=1775731884; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zCc1I/rsU18c5TSxaIaapvgK+iNm2ZW99PbX2aByn6k=; b=UStQwVx/d7YkM8I9TxQ03YkcfprYGdJU4EUwZXFmmSeXdy9EA1BR8Ret ToS0jIvR/FSbyb+/HcUVT3g7TuWi2/zkol6sQhtIzQBywq20B+XmEjHdj N2cehPeQYXrPwX7KJLMcjSb0kHFX3Z2mYmPH/VGuXDRc033TjNpTMgLya EPyYD4A9gcNdPlKQNs6h106uWx/EIk2Q6Qe8G/YpOcsP+32u+K/NJnF6V ZRBxZLKHZ6Mf8pdt/9Tr38TrtpOwv8VQcOa+mu7ZyWx3lkpBY7yp6PVjm 2I+gMj+Fjt1A8U0B5dIwNzxOROHxtoocdmqYDr9qAs/ziedQodN2YzQ/8 A==; X-CSE-ConnectionGUID: d/iEP9zWTaCLrDRWokBfkA== X-CSE-MsgGUID: 3PkzllXVR4WKRsgoTGaB2A== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380107" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380107" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:24 -0700 X-CSE-ConnectionGUID: YG/ZEH1CRLWeu1920aSLkQ== X-CSE-MsgGUID: HIt+Fuv0QGG+SdipEavfPg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426248" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:22 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 11/15] ALSA: usb: Export usb_interface driver operations Date: Wed, 9 Apr 2025 13:07:26 +0200 Message-Id: <20250409110731.3752332-12-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Existing usb_driver operations can be reused by component-drivers on the ASoC side. ASoC embraces modular approach and it is entirely acceptable for component to be unbound while the USB device is still plugged in. In such case, PCM list built when scanning USB interfaces can be kept intact to avoid scanning the device a second time when the component is probed again. Add snd_usb_release_resources() to cover that scenario. Signed-off-by: Cezary Rojewski --- include/sound/usb.h | 8 +++++ sound/usb/card.c | 88 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/include/sound/usb.h b/include/sound/usb.h index daba868fb6e0..fbcfa2b985f7 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -90,11 +90,19 @@ struct snd_usb_audio { struct snd_intf_to_ctrl intf_to_ctrl[MAX_CARD_INTERFACES]; }; +void snd_usb_release_resources(struct snd_usb_audio *chip); int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, struct usb_driver *driver); int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw); +/* USB interface operations, see struct usb_driver. */ +int snd_usb_probe(struct usb_interface *iface, const struct usb_device_id *usb_id, + struct usb_driver *driver); +void snd_usb_disconnect(struct usb_interface *iface); +int snd_usb_suspend(struct usb_interface *intf, pm_message_t message); +int snd_usb_resume(struct usb_interface *intf); + /* PCM operations, see struct snd_pcm_ops. */ int snd_usb_pcm_open(struct snd_pcm_substream *substream); int snd_usb_pcm_close(struct snd_pcm_substream *substream); diff --git a/sound/usb/card.c b/sound/usb/card.c index 354bf76988ab..a4bd8ba6a26c 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -773,7 +773,8 @@ static void snd_usb_chip_free(struct snd_usb_audio *chip) } /* Must be called under register_mutex due to usb_chip[] manipulation. */ -static void snd_usb_chip_release(struct snd_usb_audio *chip, struct usb_interface *iface) +static void snd_usb_chip_release(struct snd_usb_audio *chip, struct usb_interface *iface, + bool free_card) { chip->num_interfaces--; chip->intf[chip->num_interfaces] = NULL; @@ -781,7 +782,7 @@ static void snd_usb_chip_release(struct snd_usb_audio *chip, struct usb_interfac if (!chip->num_interfaces) { usb_chip[chip->index] = NULL; - if (chip->card) + if (free_card && chip->card) snd_card_free(chip->card); snd_usb_chip_free(chip); } @@ -1018,10 +1019,18 @@ static int snd_usb_probe_unlocked(struct usb_interface *iface, const struct usb_ goto err; return 0; err: - snd_usb_chip_release(chip, iface); + snd_usb_chip_release(chip, iface, false); return ret; } +int snd_usb_probe(struct usb_interface *iface, const struct usb_device_id *usb_id, + struct usb_driver *driver) +{ + guard(mutex)(®ister_mutex); + return snd_usb_probe_unlocked(iface, usb_id, driver); +} +EXPORT_SYMBOL_GPL(snd_usb_probe); + int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, struct usb_driver *driver) { @@ -1140,15 +1149,59 @@ static int usb_audio_probe(struct usb_interface *iface, const struct usb_device_ err_no_register: atomic_dec(&chip->active); - snd_usb_chip_release(chip, iface); + snd_usb_chip_release(chip, iface, true); return ret; } +/** + * snd_usb_release_resources - Release resources occupied by USB AC device + * + * @chip: Driver context for the USB AC device + * + * Note: PCMs and endpoints created when scanning USB interface descriptors + * are not freed here. That information is static and can be used to re-create + * PCMs without the need to scan the interfaces again. + * + * Compared to usb_disconnect(), this does free midi2.0 resources. + */ +void snd_usb_release_resources(struct snd_usb_audio *chip) +{ + struct usb_mixer_interface *mixer; + struct list_head *midi_entry; + struct snd_usb_endpoint *ep; + struct snd_usb_stream *as; + + /* release the pcm resources */ + list_for_each_entry(as, &chip->pcm_list, list) + snd_usb_stream_disconnect(as); + + /* release the endpoint resources */ + list_for_each_entry(ep, &chip->ep_list, list) + snd_usb_endpoint_release(ep); + + /* release the midi resources */ + list_for_each(midi_entry, &chip->midi_list) + snd_usbmidi_disconnect(midi_entry); + + snd_usb_midi_v2_disconnect_all(chip); + snd_usb_midi_v2_free_all(chip); + /* + * snd_media_device_delete() accesses mixer_list, do it + * before releasing mixers. + */ + snd_media_device_delete(chip); + + /* release mixer resources */ + list_for_each_entry(mixer, &chip->mixer_list, list) + snd_usb_mixer_disconnect(mixer); +} +EXPORT_SYMBOL_GPL(snd_usb_release_resources); + /* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -static void usb_audio_disconnect(struct usb_interface *intf) +static void usb_disconnect(struct usb_interface *intf, bool free_card) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_card *card; @@ -1196,10 +1249,21 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) usb_enable_autosuspend(interface_to_usbdev(intf)); - snd_usb_chip_release(chip, intf); + snd_usb_chip_release(chip, intf, free_card); mutex_unlock(®ister_mutex); } +static void usb_audio_disconnect(struct usb_interface *iface) +{ + usb_disconnect(iface, true); +} + +void snd_usb_disconnect(struct usb_interface *iface) +{ + usb_disconnect(iface, false); +} +EXPORT_SYMBOL_GPL(snd_usb_disconnect); + /* lock the shutdown (disconnect) task and autoresume */ int snd_usb_lock_shutdown(struct snd_usb_audio *chip) { @@ -1264,7 +1328,7 @@ void snd_usb_autosuspend(struct snd_usb_audio *chip) usb_autopm_put_interface(chip->intf[i]); } -static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) +int snd_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_usb_stream *as; @@ -1294,8 +1358,9 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) return 0; } +EXPORT_SYMBOL_GPL(snd_usb_suspend); -static int usb_audio_resume(struct usb_interface *intf) +int snd_usb_resume(struct usb_interface *intf) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_usb_stream *as; @@ -1343,6 +1408,7 @@ static int usb_audio_resume(struct usb_interface *intf) atomic_dec(&chip->active); /* allow autopm after this point */ return err; } +EXPORT_SYMBOL_GPL(snd_usb_resume); static const struct usb_device_id usb_audio_ids [] = { #include "quirks-table.h" @@ -1361,9 +1427,9 @@ static struct usb_driver usb_audio_driver = { .name = "snd-usb-audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, - .suspend = usb_audio_suspend, - .resume = usb_audio_resume, - .reset_resume = usb_audio_resume, + .suspend = snd_usb_suspend, + .resume = snd_usb_resume, + .reset_resume = snd_usb_resume, .id_table = usb_audio_ids, .supports_autosuspend = 1, }; From patchwork Wed Apr 9 11:07:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044471 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E08F25DAEF for ; Wed, 9 Apr 2025 10:51:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195888; cv=none; b=m+3Bcb5WSUuNmPqRCFwuKhejO+wwnKL3OkQnKWvL58ZQP1DPMXioKimoqMDjfoZE5gJy0RjKxzMXC8jSxuh23usAOkPr0aGn6cx1KAY7XVPx30rlvnJ8jnmT/HxFL9xjT5x9B7OKcpU7ran40KsGO12Ot5KT6J9FLb4YuEi5q90= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195888; c=relaxed/simple; bh=05EWgN9JvZt5KZ6BNyQfi9CE29UQOn5kp7B83WEMT4w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nyX5W5h5K4Vd5AAEceYvxSMboInLXD//+gNnxeUfn7zyY6KCrJWLDQcxokvUFuI/baImn7dED3HekGzJ46XHZHk3aJl6JCY/oR1dg6z5y5gUqhG885cLHFcqkjoyDNsE0gw6K9MkrkntwYblSHT8hUeSxD3g5bTDHsfZxYkXzoo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=hp6VF3JP; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="hp6VF3JP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195887; x=1775731887; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=05EWgN9JvZt5KZ6BNyQfi9CE29UQOn5kp7B83WEMT4w=; b=hp6VF3JPjZnh+FiekgXKh6vWC8YJYCmYISROorwNwmeSB+lVFxgkINen KMxoDDX1p+f/BDvLvSc9sm/ICWPKR4wBdQnVgTSc785h6bB+e9PQPkXs+ r0PyopLbKr4YoPFVDdpdYr4BQkphqIuPlcHEnwoGcm8u76AIeBnmm3tHh o9g57m81SB+Vi/nL4xkzdAFJ/TPZeQCVxt7fKojhJLjhnoH4Soum1BCyb yZLadKtiXiYJBnQUNuvKYG3BQWXKyZFgYw38sF6HJ2QO2Uuf57SFYv8NG KfKMiZMfxwF1LakLEKCZjylE5eRa3j8bnH8f0cBGjo0MBbvWVZAPNLJbb A==; X-CSE-ConnectionGUID: WoNgVsBqSXWwHFsp76gI+A== X-CSE-MsgGUID: RlEyAJVUQgqsxZIU6qoSig== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380115" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380115" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:27 -0700 X-CSE-ConnectionGUID: jsppskMcRXSjTrYYzGFDBQ== X-CSE-MsgGUID: qUHT+kJ9RFa4Qonu0aoG9w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426252" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:25 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 12/15] ALSA: usb: Export card-naming procedure Date: Wed, 9 Apr 2025 13:07:27 +0200 Message-Id: <20250409110731.3752332-13-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC USB sound card features customized naming procedure. For ALSA and ASoC drivers to share UseCaseManagement (UCM) configuration files, it is best if the naming is aligned between the two. Signed-off-by: Cezary Rojewski --- include/sound/usb.h | 1 + sound/usb/card.c | 78 ++++++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/include/sound/usb.h b/include/sound/usb.h index fbcfa2b985f7..b20badfda6a6 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -91,6 +91,7 @@ struct snd_usb_audio { }; void snd_usb_release_resources(struct snd_usb_audio *chip); +void snd_usb_make_card_names(struct snd_usb_audio *chip, char *shortname, char *longname); int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, struct usb_driver *driver); int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); diff --git a/sound/usb/card.c b/sound/usb/card.c index a4bd8ba6a26c..20a77e63a7eb 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -525,103 +525,102 @@ lookup_device_name(u32 id) return NULL; } -static void usb_audio_make_shortname(struct usb_device *dev, - struct snd_usb_audio *chip, - const struct snd_usb_audio_quirk *quirk) +static void snd_usb_make_card_shortname(struct snd_usb_audio *chip, char *shortname) { - struct snd_card *card = chip->card; const struct usb_audio_device_name *preset; + struct usb_device *udev = chip->dev; const char *s = NULL; preset = lookup_device_name(chip->usb_id); if (preset && preset->product_name) s = preset->product_name; - else if (quirk && quirk->product_name) - s = quirk->product_name; + else if (chip->quirk && chip->quirk->product_name) + s = chip->quirk->product_name; if (s && *s) { - strscpy(card->shortname, s, sizeof(card->shortname)); + strscpy(shortname, s, sizeof_field(struct snd_card, shortname)); return; } /* retrieve the device string as shortname */ - if (!dev->descriptor.iProduct || - usb_string(dev, dev->descriptor.iProduct, - card->shortname, sizeof(card->shortname)) <= 0) { + if (!udev->descriptor.iProduct || + usb_string(udev, udev->descriptor.iProduct, + shortname, sizeof_field(struct snd_card, shortname)) <= 0) { /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", + sprintf(shortname, "USB Device %#04x:%#04x", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); } - strim(card->shortname); + strim(shortname); } -static void usb_audio_make_longname(struct usb_device *dev, - struct snd_usb_audio *chip, - const struct snd_usb_audio_quirk *quirk) +void snd_usb_make_card_names(struct snd_usb_audio *chip, char *shortname, char *longname) { - struct snd_card *card = chip->card; const struct usb_audio_device_name *preset; + struct usb_device *udev = chip->dev; const char *s = NULL; int len; + snd_usb_make_card_shortname(chip, shortname); + preset = lookup_device_name(chip->usb_id); /* shortcut - if any pre-defined string is given, use it */ if (preset && preset->profile_name) s = preset->profile_name; if (s && *s) { - strscpy(card->longname, s, sizeof(card->longname)); + strscpy(longname, s, sizeof_field(struct snd_card, longname)); return; } if (preset && preset->vendor_name) s = preset->vendor_name; - else if (quirk && quirk->vendor_name) - s = quirk->vendor_name; - *card->longname = 0; + else if (chip->quirk && chip->quirk->vendor_name) + s = chip->quirk->vendor_name; + *longname = 0; if (s && *s) { - strscpy(card->longname, s, sizeof(card->longname)); + strscpy(longname, s, sizeof_field(struct snd_card, longname)); } else { /* retrieve the vendor and device strings as longname */ - if (dev->descriptor.iManufacturer) - usb_string(dev, dev->descriptor.iManufacturer, - card->longname, sizeof(card->longname)); + if (udev->descriptor.iManufacturer) + usb_string(udev, udev->descriptor.iManufacturer, + longname, sizeof_field(struct snd_card, longname)); /* we don't really care if there isn't any vendor string */ } - if (*card->longname) { - strim(card->longname); - if (*card->longname) - strlcat(card->longname, " ", sizeof(card->longname)); + if (*longname) { + strim(longname); + if (*longname) + strlcat(longname, " ", sizeof_field(struct snd_card, longname)); } - strlcat(card->longname, card->shortname, sizeof(card->longname)); + strlcat(longname, shortname, sizeof_field(struct snd_card, longname)); - len = strlcat(card->longname, " at ", sizeof(card->longname)); + len = strlcat(longname, " at ", sizeof_field(struct snd_card, longname)); - if (len < sizeof(card->longname)) - usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); + if (len < sizeof_field(struct snd_card, longname)) + usb_make_path(udev, longname + len, sizeof_field(struct snd_card, longname) - len); - switch (snd_usb_get_speed(dev)) { + switch (snd_usb_get_speed(udev)) { case USB_SPEED_LOW: - strlcat(card->longname, ", low speed", sizeof(card->longname)); + strlcat(longname, ", low speed", sizeof_field(struct snd_card, longname)); break; case USB_SPEED_FULL: - strlcat(card->longname, ", full speed", sizeof(card->longname)); + strlcat(longname, ", full speed", sizeof_field(struct snd_card, longname)); break; case USB_SPEED_HIGH: - strlcat(card->longname, ", high speed", sizeof(card->longname)); + strlcat(longname, ", high speed", sizeof_field(struct snd_card, longname)); break; case USB_SPEED_SUPER: - strlcat(card->longname, ", super speed", sizeof(card->longname)); + strlcat(longname, ", super speed", sizeof_field(struct snd_card, longname)); break; case USB_SPEED_SUPER_PLUS: - strlcat(card->longname, ", super speed plus", sizeof(card->longname)); + strlcat(longname, ", super speed plus", sizeof_field(struct snd_card, longname)); break; default: break; } } +EXPORT_SYMBOL_GPL(snd_usb_make_card_names); /* look for a matching quirk alias id */ static bool get_alias_id(struct usb_device *dev, unsigned int *id) @@ -1058,8 +1057,7 @@ static int snd_usb_create_card(struct snd_usb_audio *chip, struct usb_interface chip->card = card; strscpy(card->driver, "USB-Audio", sizeof(card->driver)); - usb_audio_make_shortname(udev, chip, chip->quirk); - usb_audio_make_longname(udev, chip, chip->quirk); + snd_usb_make_card_names(chip, card->shortname, card->longname); sprintf(component, "USB%04x:%04x", usb_device_vid(udev), usb_device_pid(udev)); snd_component_add(card, component); snd_usb_audio_create_proc(chip); From patchwork Wed Apr 9 11:07:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044472 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5386F25D911 for ; Wed, 9 Apr 2025 10:51:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195895; cv=none; b=dDXB8zOK46xEgzg/t7Yj4cPInkaazxWm83ZLOrzYyLixs8FOEUV2SISp4AEDJ8dwsFV3P/JZlSEvjqPHvGe59Jr2KPa39sPRVi0fOvLu9aoxJ0G65V2O+JBlzXHqnY4AXWb3GQcBeji0ZhUIU5KMz5wyu3J8LaU/UmxzNJRKR/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195895; c=relaxed/simple; bh=UIm2xgMMzbCgGrUeyg/CFqfru4d+TTJow4fKf4Fflyw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iL+mj44Cvmb2JHXzm1Urr//UgPfJycK3EjIus17KfAt0LHfiHoQjJFOHmTwmBwIw6ehQZ5gW4zj3bm398NnfgWAQ7N1P7AQrrdz8b8YZw8GCEj499epqMlhofJHZD43CyoTj0anF3e0LtHjuzV5MiOhPg6qpqBzF67CB65J1mOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=JxiYMT2E; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="JxiYMT2E" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195893; x=1775731893; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=UIm2xgMMzbCgGrUeyg/CFqfru4d+TTJow4fKf4Fflyw=; b=JxiYMT2Ew1blE0uNsvfi2YiWJZQR5gjzdgLwmEx7j/+5X7FgCLuNTt2X q350498vVHVU2hz2KSUsglHzPVUdTp1ADn8s+42BFqjEP+S/ZSjHDOsH+ yBawlVcjX0+K/YsA+DCpWowPr7M4PNcydTtOr3Yu9IU/a9AYxUjiZFrre 2QiswsEsO14W5hgSQjoCppBg806tw0aKzGrJxlH0Uh6GRmksf+5Oik7QS xytiIGDOCsQTiEvRpNgZ+nxrhFwQU5GuQ3C5Cba/3ymcFFiC7Zha+d30n s5Kakghgf8zbaXQKwlbigdMo8GzrE4j2R8MvtZoeZ1ebsXKMb3zPGyis7 Q==; X-CSE-ConnectionGUID: U4d2cz/zRRq/Lt1M5HMhdA== X-CSE-MsgGUID: OhMXMKRgRKqrmFwWN7CuDg== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380133" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380133" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:32 -0700 X-CSE-ConnectionGUID: jVRXJcwdSl+WwHTwJYeGMg== X-CSE-MsgGUID: mAlclXiXSDakM2FmUqbRWQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426257" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:27 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 13/15] ALSA: usb: Add getters to obtain endpoint information Date: Wed, 9 Apr 2025 13:07:28 +0200 Message-Id: <20250409110731.3752332-14-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Intel's Audio Link Hub (ALH) requires information about the data and feedback endpoints to manage the offloaded PCM stream properly. Signed-off-by: Cezary Rojewski --- include/sound/usb.h | 4 ++++ sound/usb/card.h | 4 ++-- sound/usb/stream.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/include/sound/usb.h b/include/sound/usb.h index b20badfda6a6..8f6e26d4de48 100644 --- a/include/sound/usb.h +++ b/include/sound/usb.h @@ -96,6 +96,10 @@ int snd_usb_bind_card(struct snd_usb_audio *chip, struct snd_card *card, struct usb_driver *driver); int snd_usb_bind_pcm(struct list_head *stream_entry, struct snd_pcm *pcm); void snd_usb_pcm_hw_init(struct list_head *stream_entry, int dir, struct snd_pcm_hardware *hw); +void snd_usb_pcm_get_epaddr(struct list_head *stream_entry, int dir, u8 *data_epaddr, + u8 *fb_epaddr); +void snd_usb_pcm_get_epinfo(struct list_head *stream_entry, int dir, u16 *data_maxp, u16 *fb_maxp, + u8 *fb_syncinterval); /* USB interface operations, see struct usb_driver. */ int snd_usb_probe(struct usb_interface *iface, const struct usb_device_id *usb_id, diff --git a/sound/usb/card.h b/sound/usb/card.h index 6ec95b2edf86..00c6e9046296 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -21,10 +21,10 @@ struct audioformat { unsigned char ep_idx; /* endpoint array index */ unsigned char altset_idx; /* array index of alternate setting */ unsigned char attributes; /* corresponding attributes of cs endpoint */ - unsigned char endpoint; /* endpoint */ + unsigned char endpoint; /* bEndpointAddress of data endpoint */ unsigned char ep_attr; /* endpoint attributes */ bool implicit_fb; /* implicit feedback endpoint */ - unsigned char sync_ep; /* sync endpoint number */ + unsigned char sync_ep; /* bEndpointAddress of feedback endpoint */ unsigned char sync_iface; /* sync EP interface */ unsigned char sync_altsetting; /* sync EP alternate setting */ unsigned char sync_ep_idx; /* sync EP array index */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 601dc9ed020a..8f8a049f5b43 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -623,6 +623,57 @@ static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip, return snd_usb_parse_pcm(chip, stream, fp, pd); } +/** + * snd_usb_pcm_get_epaddr - Obtain addresses of the stream endpoints. + * + * @stream_entry: The USB stream + * @dir: Substream's direction + * @data_epaddr: returned bEndpointAddress of the data endpoint + * @fb_epaddr: returned bEndpointAddress of the feedback endpoint + */ +void snd_usb_pcm_get_epaddr(struct list_head *stream_entry, int dir, u8 *data_epaddr, + u8 *fb_epaddr) +{ + struct snd_usb_substream *subs; + struct snd_usb_stream *as; + struct audioformat *fp; + + as = list_entry(stream_entry, struct snd_usb_stream, list); + subs = &as->substream[dir]; + fp = list_first_entry(&subs->fmt_list, struct audioformat, list); + + *data_epaddr = fp->endpoint; + *fb_epaddr = fp->sync_ep; +} +EXPORT_SYMBOL_GPL(snd_usb_pcm_get_epaddr); + +/** + * snd_usb_pcm_get_epinfo - Obtain runtime information of the stream endpoints. + * + * @stream_entry: The USB stream + * @dir: Substream's direction + * @data_maxp: returned wMaxPacketSize of the data endpoint + * @fb_maxp: returned wMaxPacketSize of the feedback endpoint + * @fb_syncinterval: returned synchronization interval of the feedback endpoint + */ +void snd_usb_pcm_get_epinfo(struct list_head *stream_entry, int dir, u16 *data_maxp, u16 *fb_maxp, + u8 *fb_syncinterval) +{ + struct snd_usb_substream *subs; + struct snd_usb_stream *as; + + as = list_entry(stream_entry, struct snd_usb_stream, list); + subs = &as->substream[dir]; + + if (subs->data_endpoint) + *data_maxp = subs->data_endpoint->maxpacksize; + if (subs->sync_endpoint) { + *fb_maxp = subs->sync_endpoint->maxpacksize; + *fb_syncinterval = subs->sync_endpoint->syncinterval; + } +} +EXPORT_SYMBOL_GPL(snd_usb_pcm_get_epinfo); + static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct usb_host_interface *alts, int protocol, int iface_no) From patchwork Wed Apr 9 11:07:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044473 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BCD6C25DAF2 for ; Wed, 9 Apr 2025 10:51:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195895; cv=none; b=UEM/pgT9KgnLGsHyAaiVIFCi8t1QLAd3My/m77v2GrGnlzRhTaoFv5bC38HyZ9KtNIZ2lQDo+RHw4rO4589TI5dUPQQK/NI6JwrVDiRiW18QNwTr160XzIVCI2FI/7NbsYUGeUthKwnLcJaDDAjhBNWWNs5kkUWXEp8njPGQh5g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195895; c=relaxed/simple; bh=0BTa3c6CTQMXmyxTjtIbHgajzaRHg7CFdalM7EuOnzI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=WAWoXzJNnu/AQlZCN0ETkaaGtjxK4ur+Capjwgbf5aEoD+uCknuIaT8JphnOOD/2vL0XQ2w3DgpLgv6RdKjfxYcDwX1r89y/DA/75wgm3gHevQq4ftTbdJREdvkehmN6yE2XMZ3n9D7V/SLAQsPAKU+hHtpgybxHFJfcYhlJYd8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=EVuOT9yz; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="EVuOT9yz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195894; x=1775731894; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=0BTa3c6CTQMXmyxTjtIbHgajzaRHg7CFdalM7EuOnzI=; b=EVuOT9yz8c2AuMznRvEb1JJpTOoLTBMy1fKuBruu1TQORnrUPIYhGNe5 F+bExLzC4P7tKcisO/R31pVuz2/kNLarijMsy+8LJWWPsYfMKL3fhNsGi 9uB67LJ3Ol3po0/O1H1799CAaE7vbm4WlozFMjINDcWT9GzGJ6R3uMBiA YQVkRwUCJ2zHc3Iqt1Sz8IWZtfiR32ZnzA9+EcDeJV7ptOgbA6uwqLrcJ EqT1UqeRSyP1mw2e/sYUOOfd7yffIuCqW8koBT/5s7BpcATr27eud2LIn BitufnBntsZd2HqxqBi1Zcf8YC6XikGezsyb8UmtEbuRK1zRolAHngrDZ g==; X-CSE-ConnectionGUID: qadScKNYS2i60M4BHZQJWQ== X-CSE-MsgGUID: nVZElz9URAyUhrGVoWQs7Q== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380141" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380141" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:33 -0700 X-CSE-ConnectionGUID: GUz+kxYvRS2ubiSHguWQag== X-CSE-MsgGUID: jZoeu7C9R3yah3izB1kRpA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426261" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:30 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 14/15] ASoC: codecs: Add USB-Audio driver Date: Wed, 9 Apr 2025 13:07:29 +0200 Message-Id: <20250409110731.3752332-15-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Sound card on the ASoC side is typically a marriage of two major components: codec and platform. Platform usually covers DSP subject so the USB Audio-Class (AC) driver fits the codec role better. The driver takes heavily from the 'classic' driver defined in sound/usb/card.c. The difference worth noting is lack of tolerance for quirks - given the limited number of Audio Link Hub (ALH) streams, only candidates (USB devices) we can be sure of have green light for offloading. There are two important responsibilities of the driver on top of PCM/component operations: - register platform device for machine-board driver to hook onto - allocation and free of Audio Sideband resources and storing up-to-date information about USB endpoints engaged in the streaming. The platform component can leverage the latter to configure their hardware accordingly to what is present on xHCI side. Signed-off-by: Cezary Rojewski --- include/sound/usb_offload.h | 46 ++++ sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/usb.c | 441 ++++++++++++++++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 include/sound/usb_offload.h create mode 100644 sound/soc/codecs/usb.c diff --git a/include/sound/usb_offload.h b/include/sound/usb_offload.h new file mode 100644 index 000000000000..029a8f22d32f --- /dev/null +++ b/include/sound/usb_offload.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USB Audio Offload(ed) stream information + * + * Copyright(c) 2025 Intel Corporation + */ +#ifndef __SOUND_USB_OFFLOAD_H +#define __SOUND_USB_OFFLOAD_H + +#include /* enum usb_device_speed */ + +/** + * struct uao_stream_info - USB Audio Offload stream information + * + * @bus_devid: Device ID of the owning xHCI controller, see pci_dev_id(). + * @slot_id: slot_id of the USB device. + * @tt: true if behind USB2.0 Transaction Translator (TT) + * @speed: USB_SPEED_XXX of the USB device. + * + * @data_epaddr: bEndpointAddress of the data endpoint. + * @data_audsb_res: Audio Sideband resource assigned by xHCI. + * @data_maxp: wMaxPacketSize of the data endpoint. + * @fb_epaddr: bEndpointAddress of the feedback endpoint. + * @fb_audsb_res: Audio Sideband resource assigned by xHCI. + * @fb_maxp: wMaxPacketSize of the feedback endpoint. + * @fb_interval: Synchornization interval of the feedback endpoint. + * + * If the owning xHCI controller is not a PCI device, @bus_devid is 0. + * If no feedback endpoint exists for a given stream, all fb_xxx are 0. + */ +struct uao_stream_info { + u16 bus_devid; + u8 slot_id; + u8 tt; + enum usb_device_speed speed; + + u8 data_epaddr; + u8 data_audsb_res; + u16 data_maxp; + u8 fb_epaddr; + u8 fb_audsb_res; + u16 fb_maxp; + u8 fb_syncinterval; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f8a21c4b74c2..eda9268cf0b4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2177,6 +2177,12 @@ config SND_SOC_UDA1380 tristate depends on I2C +config SND_SOC_USB_CODEC + tristate "USB \"codec\" representative in ASoC" + depends on SND_USB_AUDIO + help + TODO + config SND_SOC_WCD_CLASSH tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 578d6aedc69a..cc1206057aae 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -331,6 +331,7 @@ snd-soc-twl6040-y := twl6040.o snd-soc-uda1334-y := uda1334.o snd-soc-uda1342-y := uda1342.o snd-soc-uda1380-y := uda1380.o +snd-soc-usb-codec-y := usb.o snd-soc-wcd-classh-y := wcd-clsh-v2.o snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o snd-soc-wcd9335-y := wcd9335.o @@ -746,6 +747,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_USB_CODEC) += snd-soc-usb-codec.o obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o diff --git a/sound/soc/codecs/usb.c b/sound/soc/codecs/usb.c new file mode 100644 index 000000000000..1013147d66b4 --- /dev/null +++ b/sound/soc/codecs/usb.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2025 Intel Corporation +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include +#include /* pm_message_t */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UAO_COMPONENT_NAME "usb-codec" + +static struct usb_driver uaol_driver; + +struct uao_dai_data { + struct uao_stream_info uao; + struct list_head *stream_entry; /* see struct snd_usb_stream. */ +}; + +static int uaol_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct uao_dai_data *data = snd_soc_dai_dma_data_get(dai, 0); + + /* Complete a USB stream initialization by binding it with a PCM. */ + return snd_usb_bind_pcm(data->stream_entry, rtd->pcm); +} + +static int uaol_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_usb_audio *chip = dev_get_drvdata(dai->component->dev); + struct usb_device *udev = chip->dev; + struct uao_dai_data *data; + u8 data_res, fb_res; + int ret; + + fb_res = 0; + data = snd_soc_dai_get_dma_data(dai, substream); + + ret = usb_alloc_audsb_resource(udev, data->uao.data_epaddr, &data_res); + if (ret) + return ret; + if (data->uao.fb_epaddr) { + ret = usb_alloc_audsb_resource(udev, data->uao.fb_epaddr, &fb_res); + if (ret) + goto err_alloc_fb; + } + + /* sound/usb/pcm.c operations expect valid USB stream in ->private_data. */ + substream->runtime->private_data = data->stream_entry; + + ret = snd_usb_pcm_open(substream); + if (ret) + goto err_pcm_open; + + data->uao.data_audsb_res = data_res; + data->uao.fb_audsb_res = fb_res; + return 0; + +err_pcm_open: + substream->runtime->private_data = NULL; + if (data->uao.fb_epaddr) + usb_free_audsb_resource(udev, data->uao.fb_epaddr); +err_alloc_fb: + usb_free_audsb_resource(udev, data->uao.data_epaddr); + return ret; +} + +static void uaol_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_usb_audio *chip = dev_get_drvdata(dai->component->dev); + struct usb_device *udev = chip->dev; + struct uao_dai_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + data->uao.data_audsb_res = 0; + data->uao.fb_audsb_res = 0; + + snd_usb_pcm_close(substream); + substream->runtime->private_data = NULL; + + if (data->uao.fb_epaddr) + usb_free_audsb_resource(udev, data->uao.fb_epaddr); + usb_free_audsb_resource(udev, data->uao.data_epaddr); +} + +static int uaol_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + struct uao_dai_data *data = snd_soc_dai_get_dma_data(dai, substream); + int ret; + + ret = snd_usb_pcm_hw_params(substream, hw_params); + if (ret) + return ret; + + /* With USB endpoints configured, assign the dynamic information. */ + snd_usb_pcm_get_epinfo(data->stream_entry, substream->stream, &data->uao.data_maxp, + &data->uao.fb_maxp, &data->uao.fb_syncinterval); + return 0; +} + +static int uaol_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct uao_dai_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + data->uao.data_maxp = 0; + data->uao.fb_maxp = 0; + data->uao.fb_syncinterval = 0; + + return snd_usb_pcm_hw_free(substream); +} + +static int uaol_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return snd_usb_pcm_prepare(substream); +} + +static const struct snd_soc_dai_ops uaol_dai_ops = { + .pcm_new = uaol_dai_pcm_new, + .startup = uaol_dai_startup, + .shutdown = uaol_dai_shutdown, + .hw_params = uaol_dai_hw_params, + .hw_free = uaol_dai_hw_free, + .prepare = uaol_dai_prepare, +}; + +static const struct snd_soc_dapm_route dapm_routes[] = { + { "AIF1TX", NULL, "Codec Input Pin1" }, + { "Codec Output Pin1", NULL, "AIF1RX" }, +}; + +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("Codec Input Pin1"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), +}; + +static int uaol_init_dai_driver(struct snd_usb_audio *chip, struct snd_soc_dai_driver *driver, + struct list_head *stream_entry, int idx) +{ + struct device *dev = &chip->dev->dev; + struct snd_soc_pcm_stream *stream; + struct snd_pcm_hardware hw; + int dir; + + driver->id = idx; + driver->ops = &uaol_dai_ops; + driver->name = devm_kasprintf(dev, GFP_KERNEL, "usb-codec-dai%d", idx); + if (!driver->name) + return -ENOMEM; + + for_each_pcm_streams(dir) { + if (dir) + stream = &driver->capture; + else + stream = &driver->playback; + + /* If the direction is not supported, skip it. */ + memset(&hw, 0, sizeof(hw)); + snd_usb_pcm_hw_init(stream_entry, dir, &hw); + if (!hw.formats) + continue; + + stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, "UAO Audio #%i %s", + idx, snd_pcm_direction_name(dir)); + if (!stream->stream_name) + return -ENOMEM; + stream->rates = hw.rates; + stream->rate_min = hw.rate_min; + stream->rate_max = hw.rate_max; + stream->channels_min = hw.channels_min; + stream->channels_max = hw.channels_max; + stream->formats = hw.formats; + } + + if (!driver->playback.formats && !driver->capture.formats) + return -EINVAL; + return 0; +} + +static int uaol_init_dai_dma_data(struct snd_usb_audio *chip, struct snd_soc_dai *dai, + struct list_head *stream_entry, u16 bus_devid) +{ + struct usb_device *udev = chip->dev; + struct uao_dai_data *data; + int dir; + + for_each_pcm_streams(dir) { + if (!snd_soc_dai_get_pcm_stream(dai, dir)->formats) + continue; + + data = devm_kzalloc(&udev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* The dynamic information is assigned in hw_params(). */ + data->stream_entry = stream_entry; + data->uao.bus_devid = bus_devid; + data->uao.slot_id = udev->slot_id; + if (udev->tt) + data->uao.tt = true; + data->uao.speed = udev->speed; + snd_usb_pcm_get_epaddr(stream_entry, dir, &data->uao.data_epaddr, + &data->uao.fb_epaddr); + + snd_soc_dai_dma_data_set(dai, dir, data); + } + + return 0; +} + +static void uaol_unregister_dais(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai, *save; + + for_each_component_dais_safe(component, dai, save) + if (strstr(dai->driver->name, UAO_COMPONENT_NAME)) + snd_soc_unregister_dai(dai); +} + +static int uaol_register_dais(struct snd_soc_component *component) +{ + struct snd_usb_audio *chip = dev_get_drvdata(component->dev); + struct usb_device *udev = chip->dev; + struct snd_soc_dai_driver *drivers; + struct list_head *stream_entry; + struct snd_soc_dai *dai; + u16 bus_devid = 0; + int ret, i = 0; + + if (list_empty(&chip->pcm_list)) + return -EINVAL; + + drivers = devm_kcalloc(&udev->dev, chip->pcm_devs, sizeof(*drivers), GFP_KERNEL); + if (!drivers) + return -ENOMEM; + + if (dev_is_pci(udev->bus->controller)) { + struct pci_dev *pci = to_pci_dev(udev->bus->controller); + + bus_devid = pci_dev_id(pci); + } + + list_for_each(stream_entry, &chip->pcm_list) { + ret = uaol_init_dai_driver(chip, &drivers[i], stream_entry, i); + if (ret) + goto err; + + dai = snd_soc_register_dai(component, &drivers[i], false); + if (!dai) { + ret = -EINVAL; + goto err; + } + + ret = uaol_init_dai_dma_data(chip, dai, stream_entry, bus_devid); + if (ret) + goto err; + i++; + } + + return 0; +err: + uaol_unregister_dais(component); + return ret; +} + +static int uaol_component_probe(struct snd_soc_component *component) +{ + struct snd_usb_audio *chip = dev_get_drvdata(component->dev); + + return snd_usb_bind_card(chip, component->card->snd_card, &uaol_driver); +} + +static void uaol_component_remove(struct snd_soc_component *component) +{ + struct snd_usb_audio *chip = dev_get_drvdata(component->dev); + + /* Prevent autosuspend of the chip. */ + atomic_add(chip->num_interfaces, &chip->active); + + /* + * Unregister DAIs that were created manually in uaol_register_dais() + * and release all (audio) resources attached tt the chip. The parsed + * information about PCMs (chip->pcm_list) remains intact so the card + * can be re-bound without the need to re-parse USB interface again. + * + * Nothing for proc as ASoC did snd_card_disconnect() already, it is gone. + */ + snd_usb_release_resources(chip); + chip->card = NULL; +} + +static const struct snd_soc_component_driver uaol_codec_driver = { + .name = "usb-codec-driver", + .probe = uaol_component_probe, + .remove = uaol_component_remove, + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = dapm_routes, + .num_dapm_routes = ARRAY_SIZE(dapm_routes), +}; + +static int uaol_register_component(struct snd_usb_audio *chip) +{ + struct snd_soc_component *component; + struct usb_device *udev = chip->dev; + int ret; + + component = devm_kzalloc(&udev->dev, sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + component->name = UAO_COMPONENT_NAME; + + ret = snd_soc_component_initialize(component, &uaol_codec_driver, &udev->dev); + if (ret) + return ret; + + ret = uaol_register_dais(component); + if (ret) + return ret; + + return snd_soc_add_component(component, NULL, 0); +} + +static void uaol_unregister_board(void *pdev) +{ + platform_device_unregister(pdev); +} + +static int uaol_register_chip(struct snd_usb_audio *chip) +{ + struct snd_soc_acpi_mach mach = {{0}}; + struct device *dev = &chip->dev->dev; + struct platform_device *pdev; + int ret; + + mach.tplg_filename = "uao-tplg.bin"; + + ret = uaol_register_component(chip); + if (ret) + return ret; + + pdev = platform_device_register_data(dev, "uao_board", PLATFORM_DEVID_AUTO, + &mach, sizeof(mach)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + ret = devm_add_action(dev, uaol_unregister_board, pdev); + if (ret) { + platform_device_unregister(pdev); + return ret; + } + + return 0; +} + +static int uaol_probe(struct usb_interface *iface, const struct usb_device_id *usb_id) +{ + struct snd_usb_audio *chip; + struct usb_device *udev; + int ret; + + udev = interface_to_usbdev(iface); + if (!udev->audsb_capable) + return -ENODEV; + + /* UAO is optional, leave the device to the classic driver if probe fails. */ + ret = snd_usb_probe(iface, usb_id, &uaol_driver); + if (ret) { + dev_warn(&udev->dev, "USB Audio Offload driver probe failed: %d\n", ret); + udev->audsb_capable = false; + return -EPROBE_DEFER; + } + + chip = usb_get_intfdata(iface); + return uaol_register_chip(chip); +} + +static void uaol_disconnect(struct usb_interface *iface) +{ + struct snd_usb_audio *chip = usb_get_intfdata(iface); + + if (chip == USB_AUDIO_IFACE_UNUSED) + return; + + snd_soc_unregister_component(&chip->dev->dev); + return snd_usb_disconnect(iface); +} + +static int uaol_suspend(struct usb_interface *iface, pm_message_t message) +{ + return snd_usb_suspend(iface, message); +} + +static int uaol_resume(struct usb_interface *iface) +{ + return snd_usb_resume(iface); +} + +static const struct usb_device_id uaol_device_ids[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL + }, + { } +}; +MODULE_DEVICE_TABLE(usb, uaol_device_ids); + +static struct usb_driver uaol_driver = { + .name = "snd-soc-usb-codec", + .probe = uaol_probe, + .disconnect = uaol_disconnect, + .suspend = uaol_suspend, + .resume = uaol_resume, + .reset_resume = uaol_resume, + .id_table = uaol_device_ids, + .supports_autosuspend = 1, +}; + +module_usb_driver(uaol_driver); + +MODULE_DESCRIPTION("USB Audio driver for ASoC"); +MODULE_LICENSE("GPL"); From patchwork Wed Apr 9 11:07:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cezary Rojewski X-Patchwork-Id: 14044474 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FBEC25D911 for ; Wed, 9 Apr 2025 10:51:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195903; cv=none; b=O5ZVfzqOs1skcbdxygoLagidnPwB48XqxNhTMOqiDZYRHOGrVi8X8u8JGgLCPlbXExufTcISPgYkUoxcGnXVcvji5RECpQXV+8io7qwK8AUfx+8T7clxmhVqDwIn+x0xQhJGfinTR+zzjsjMyCHjkBBzCdVdjWrGEArLAUAB14c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744195903; c=relaxed/simple; bh=c1khOfXuuo4V16BQRyB2YzmAnkjoflB5TM7TMkaF2/8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NPgTVHs/tuwmGPdlRSMyFsMwG3j3315tNvhaRtDyzr8GQ/cNPt7K8x7dciOC/azhnLAJTQDj1jW9zV4T/2arHQGa62NhpXQBewJVtDVAJLAkwiMy6ygChZn6nkcMYNPjEZYjGSzeqxt5bR3Cs2UaT8x41dpYEtcYgBIjCD3t3mw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=eVIsfd+v; arc=none smtp.client-ip=198.175.65.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="eVIsfd+v" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1744195902; x=1775731902; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=c1khOfXuuo4V16BQRyB2YzmAnkjoflB5TM7TMkaF2/8=; b=eVIsfd+v7QgLKuNA9ZrAuMaJwX1JKLIrOHxaDkMcf8AlZQpaUhtnc08R 97ummEX4DIx1+g7Oy2MU5gwx/Xrbtw4YF3J5IIkiKmhncyAiYZPfbHMMw SNumBQpTzIopIybcjPKXNjotn2EUi1mt3cwDaYuYzhf8xeAqTYRhw1TCo f17eiHATCus7DWVgcj2z5k3BVJnrMKEd9f/GjYaUm17njQM4kiCgJAXCC xph5J1G2soxb6lY3IKvOXWFQbH7i8RHXQC6hq7Y8xZE+LsSXNWdN17DDk IUSf1fCC4RGK6LaEdcLPY5nEV7nzwmGxrOoimOOzvUs1EAEPI2wvbQu7V w==; X-CSE-ConnectionGUID: rZnJTtS/QR+H1qBx2j71/A== X-CSE-MsgGUID: 063rTMN+TMmbzr7/8zZ60A== X-IronPort-AV: E=McAfee;i="6700,10204,11397"; a="45380147" X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="45380147" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Apr 2025 03:51:34 -0700 X-CSE-ConnectionGUID: YYGfXlmlQ1Occ85RkqI7Wg== X-CSE-MsgGUID: Js74u3szRomiq+frocqS5w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.15,200,1739865600"; d="scan'208";a="133426274" Received: from crojewsk-ctrl.igk.intel.com ([10.237.149.0]) by orviesa003.jf.intel.com with ESMTP; 09 Apr 2025 03:51:32 -0700 From: Cezary Rojewski To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org, gregkh@linuxfoundation.org, quic_wcheng@quicinc.com, mathias.nyman@linux.intel.com, Cezary Rojewski Subject: [RFC 15/15] ASoC: Intel: avs: Add USB machine board Date: Wed, 9 Apr 2025 13:07:30 +0200 Message-Id: <20250409110731.3752332-16-cezary.rojewski@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com> References: <20250409110731.3752332-1-cezary.rojewski@intel.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Provide machine board driver that represents USB Audio sound card. The card connects ASoC USB codec driver with Intel AudioDSP avs-driver driver so that user can enjoy streaming over USB device without engaging their CPU - transfer is offloaded onto DSP instead. Signed-off-by: Cezary Rojewski --- sound/soc/intel/avs/boards/Kconfig | 8 ++ sound/soc/intel/avs/boards/Makefile | 2 + sound/soc/intel/avs/boards/usb.c | 115 ++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 sound/soc/intel/avs/boards/usb.c diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 7938e6d6e44d..f036cb96c48f 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -244,4 +244,12 @@ config SND_SOC_INTEL_AVS_MACH_TDF8532 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_USB + tristate "USB-Audio board" + depends on USB_XHCI_PCI || COMPILE_TEST + select SND_SOC_USB_CODEC + help + This allows Intel AVS driver to pair with xHCI Audio Sideband. + If unsure select "N". + endmenu diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index 7c718c0ce120..4fdea026a216 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -24,6 +24,7 @@ snd-soc-avs-rt5663-y := rt5663.o snd-soc-avs-rt5682-y := rt5682.o snd-soc-avs-ssm4567-y := ssm4567.o snd-soc-avs-tdf8532-y := tdf8532.o +snd-soc-avs-usb-y := usb.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o @@ -49,3 +50,4 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_TDF8532) += snd-soc-avs-tdf8532.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_USB) += snd-soc-avs-usb.o diff --git a/sound/soc/intel/avs/boards/usb.c b/sound/soc/intel/avs/boards/usb.c new file mode 100644 index 000000000000..d103a8a5474b --- /dev/null +++ b/sound/soc/intel/avs/boards/usb.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2025 Intel Corporation +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include +#include +#include + +static int uao_create_dai_links(struct device *dev, struct snd_usb_audio *chip, + struct snd_soc_dai_link **links, int *num_links) +{ + struct snd_soc_dai_link *dl; + int i; + + dl = devm_kcalloc(dev, chip->pcm_devs, sizeof(*dl), GFP_KERNEL); + if (!dl) + return -ENOMEM; + + for (i = 0; i < chip->pcm_devs; i++) { + dl[i].name = devm_kasprintf(dev, GFP_KERNEL, "uao-be-link%d", i); + if (!dl[i].name) + return -ENOMEM; + dl[i].stream_name = dl[i].name; + + dl[i].no_pcm = 1; + dl[i].num_cpus = 1; + dl[i].num_codecs = 1; + dl[i].nonatomic = 1; + dl[i].codecs = devm_kzalloc(dev, sizeof(*dl[i].codecs), GFP_KERNEL); + dl[i].cpus = devm_kzalloc(dev, sizeof(*dl[i].cpus), GFP_KERNEL); + if (!dl[i].codecs || !dl[i].cpus) + return -ENOMEM; + dl[i].codecs->name = "usb-codec"; + dl[i].codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "usb-codec-dai%d", i); + if (!dl[i].codecs->dai_name) + return -ENOMEM; + dl[i].cpus->dai_name = "uaol-cpu0"; + } + + *links = dl; + *num_links = chip->pcm_devs; + return 0; +} + +static int uao_board_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_usb_audio *chip; + struct snd_soc_card *card; + struct usb_device *udev; + char shortname[32]; + char longname[80]; + int ret; + + chip = dev_get_drvdata(dev->parent); + udev = chip->dev; + + switch (udev->speed) { + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + return -EOPNOTSUPP; + } + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + ret = uao_create_dai_links(dev, chip, &card->dai_link, &card->num_links); + if (ret) + return ret; + + card->dev = dev; + card->owner = THIS_MODULE; + card->fully_routed = true; + card->driver_name = "USB-Audio"; + + snd_usb_make_card_names(chip, shortname, longname); + /* card->driver/shortname/longname set by ASoC. */ + /* card->private_data/free redundant for ASoC-based. */ + card->name = devm_kstrdup(dev, shortname, GFP_KERNEL); + card->long_name = devm_kstrdup(dev, longname, GFP_KERNEL); + card->components = devm_kasprintf(dev, GFP_KERNEL, "USB%04x:%04x", + usb_device_vid(udev), usb_device_pid(udev)); + if (!card->name || !card->long_name || !card->components) + return -ENOMEM; + + return devm_snd_soc_register_card(dev, card); +} + +static const struct platform_device_id uao_board_driver_ids[] = { + { .name = "uao_board", }, + { }, +}; + +static struct platform_driver uao_board_driver = { + .probe = uao_board_probe, + .driver = { + .name = "uao_board", + .pm = &snd_soc_pm_ops, + }, + .id_table = uao_board_driver_ids, +}; + +module_platform_driver(uao_board_driver); + +MODULE_DESCRIPTION("USB Audio Offload machine driver"); +MODULE_LICENSE("GPL");