Merge branch 'for-next' into for-linus
Preparation for 4.17 merge. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
bc334cb61b
|
@ -34,14 +34,14 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
|
static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control)
|
||||||
{
|
{
|
||||||
return (bmControls >> (control * 2)) & 0x1;
|
return (bmControls >> ((control - 1) * 2)) & 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool uac2_control_is_writeable(u32 bmControls, u8 control)
|
static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control)
|
||||||
{
|
{
|
||||||
return (bmControls >> (control * 2)) & 0x2;
|
return (bmControls >> ((control - 1) * 2)) & 0x2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4.7.2 Class-Specific AC Interface Descriptor */
|
/* 4.7.2 Class-Specific AC Interface Descriptor */
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||||
|
*
|
||||||
|
* This file holds USB constants and structures defined
|
||||||
|
* by the USB DEVICE CLASS DEFINITION FOR AUDIO DEVICES Release 3.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LINUX_USB_AUDIO_V3_H
|
||||||
|
#define __LINUX_USB_AUDIO_V3_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* v1.0, v2.0 and v3.0 of this standard have many things in common. For the rest
|
||||||
|
* of the definitions, please refer to audio.h and audio-v2.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* All High Capability descriptors have these 2 fields at the beginning */
|
||||||
|
struct uac3_hc_descriptor_header {
|
||||||
|
__le16 wLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__le16 wDescriptorID;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.3.1 CLUSTER DESCRIPTOR HEADER */
|
||||||
|
struct uac3_cluster_header_descriptor {
|
||||||
|
__le16 wLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__le16 wDescriptorID;
|
||||||
|
__u8 bNrChannels;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.3.2.1 SEGMENTS */
|
||||||
|
struct uac3_cluster_segment_descriptor {
|
||||||
|
__le16 wLength;
|
||||||
|
__u8 bSegmentType;
|
||||||
|
/* __u8[0]; segment-specific data */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.3.2.1.1 END SEGMENT */
|
||||||
|
struct uac3_cluster_end_segment_descriptor {
|
||||||
|
__le16 wLength;
|
||||||
|
__u8 bSegmentType; /* Constant END_SEGMENT */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.3.2.1.3.1 INFORMATION SEGMENT */
|
||||||
|
struct uac3_cluster_information_segment_descriptor {
|
||||||
|
__le16 wLength;
|
||||||
|
__u8 bSegmentType;
|
||||||
|
__u8 bChPurpose;
|
||||||
|
__u8 bChRelationship;
|
||||||
|
__u8 bChGroupID;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.5.2 CLASS-SPECIFIC AC INTERFACE DESCRIPTOR */
|
||||||
|
struct uac3_ac_header_descriptor {
|
||||||
|
__u8 bLength; /* 10 */
|
||||||
|
__u8 bDescriptorType; /* CS_INTERFACE descriptor type */
|
||||||
|
__u8 bDescriptorSubtype; /* HEADER descriptor subtype */
|
||||||
|
__u8 bCategory;
|
||||||
|
|
||||||
|
/* includes Clock Source, Unit, Terminal, and Power Domain desc. */
|
||||||
|
__le16 wTotalLength;
|
||||||
|
|
||||||
|
__le32 bmControls;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* 4.5.2.1 INPUT TERMINAL DESCRIPTOR */
|
||||||
|
struct uac3_input_terminal_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bTerminalID;
|
||||||
|
__le16 wTerminalType;
|
||||||
|
__u8 bAssocTerminal;
|
||||||
|
__u8 bCSourceID;
|
||||||
|
__le32 bmControls;
|
||||||
|
__le16 wClusterDescrID;
|
||||||
|
__le16 wExTerminalDescrID;
|
||||||
|
__le16 wConnectorsDescrID;
|
||||||
|
__le16 wTerminalDescrStr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* 4.5.2.2 OUTPUT TERMINAL DESCRIPTOR */
|
||||||
|
struct uac3_output_terminal_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bTerminalID;
|
||||||
|
__le16 wTerminalType;
|
||||||
|
__u8 bAssocTerminal;
|
||||||
|
__u8 bSourceID;
|
||||||
|
__u8 bCSourceID;
|
||||||
|
__le32 bmControls;
|
||||||
|
__le16 wExTerminalDescrID;
|
||||||
|
__le16 wConnectorsDescrID;
|
||||||
|
__le16 wTerminalDescrStr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* 4.5.2.7 FEATURE UNIT DESCRIPTOR */
|
||||||
|
struct uac3_feature_unit_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bUnitID;
|
||||||
|
__u8 bSourceID;
|
||||||
|
/* bmaControls is actually u32,
|
||||||
|
* but u8 is needed for the hybrid parser */
|
||||||
|
__u8 bmaControls[0]; /* variable length */
|
||||||
|
/* wFeatureDescrStr omitted */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define UAC3_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 4)
|
||||||
|
|
||||||
|
/* As above, but more useful for defining your own descriptors */
|
||||||
|
#define DECLARE_UAC3_FEATURE_UNIT_DESCRIPTOR(ch) \
|
||||||
|
struct uac3_feature_unit_descriptor_##ch { \
|
||||||
|
__u8 bLength; \
|
||||||
|
__u8 bDescriptorType; \
|
||||||
|
__u8 bDescriptorSubtype; \
|
||||||
|
__u8 bUnitID; \
|
||||||
|
__u8 bSourceID; \
|
||||||
|
__le32 bmaControls[ch + 1]; \
|
||||||
|
__le16 wFeatureDescrStr; \
|
||||||
|
} __attribute__ ((packed))
|
||||||
|
|
||||||
|
/* 4.5.2.12 CLOCK SOURCE DESCRIPTOR */
|
||||||
|
struct uac3_clock_source_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bClockID;
|
||||||
|
__u8 bmAttributes;
|
||||||
|
__le32 bmControls;
|
||||||
|
__u8 bReferenceTerminal;
|
||||||
|
__le16 wClockSourceStr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* bmAttribute fields */
|
||||||
|
#define UAC3_CLOCK_SOURCE_TYPE_EXT 0x0
|
||||||
|
#define UAC3_CLOCK_SOURCE_TYPE_INT 0x1
|
||||||
|
#define UAC3_CLOCK_SOURCE_ASYNC (0 << 2)
|
||||||
|
#define UAC3_CLOCK_SOURCE_SYNCED_TO_SOF (1 << 1)
|
||||||
|
|
||||||
|
/* 4.5.2.13 CLOCK SELECTOR DESCRIPTOR */
|
||||||
|
struct uac3_clock_selector_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bClockID;
|
||||||
|
__u8 bNrInPins;
|
||||||
|
__u8 baCSourceID[];
|
||||||
|
/* bmControls and wCSelectorDescrStr omitted */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* 4.5.2.14 CLOCK MULTIPLIER DESCRIPTOR */
|
||||||
|
struct uac3_clock_multiplier_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bClockID;
|
||||||
|
__u8 bCSourceID;
|
||||||
|
__le32 bmControls;
|
||||||
|
__le16 wCMultiplierDescrStr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* 4.5.2.15 POWER DOMAIN DESCRIPTOR */
|
||||||
|
struct uac3_power_domain_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bPowerDomainID;
|
||||||
|
__le16 waRecoveryTime1;
|
||||||
|
__le16 waRecoveryTime2;
|
||||||
|
__u8 bNrEntities;
|
||||||
|
__u8 baEntityID[];
|
||||||
|
/* wPDomainDescrStr omitted */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* As above, but more useful for defining your own descriptors */
|
||||||
|
#define DECLARE_UAC3_POWER_DOMAIN_DESCRIPTOR(n) \
|
||||||
|
struct uac3_power_domain_descriptor_##n { \
|
||||||
|
__u8 bLength; \
|
||||||
|
__u8 bDescriptorType; \
|
||||||
|
__u8 bDescriptorSubtype; \
|
||||||
|
__u8 bPowerDomainID; \
|
||||||
|
__le16 waRecoveryTime1; \
|
||||||
|
__le16 waRecoveryTime2; \
|
||||||
|
__u8 bNrEntities; \
|
||||||
|
__u8 baEntityID[n]; \
|
||||||
|
__le16 wPDomainDescrStr; \
|
||||||
|
} __attribute__ ((packed))
|
||||||
|
|
||||||
|
/* 4.7.2 CLASS-SPECIFIC AS INTERFACE DESCRIPTOR */
|
||||||
|
struct uac3_as_header_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__u8 bTerminalLink;
|
||||||
|
__le32 bmControls;
|
||||||
|
__le16 wClusterDescrID;
|
||||||
|
__le64 bmFormats;
|
||||||
|
__u8 bSubslotSize;
|
||||||
|
__u8 bBitResolution;
|
||||||
|
__le16 bmAuxProtocols;
|
||||||
|
__u8 bControlSize;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define UAC3_FORMAT_TYPE_I_RAW_DATA (1 << 6)
|
||||||
|
|
||||||
|
/* 4.8.1.2 CLASS-SPECIFIC AS ISOCHRONOUS AUDIO DATA ENDPOINT DESCRIPTOR */
|
||||||
|
struct uac3_iso_endpoint_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
__u8 bDescriptorSubtype;
|
||||||
|
__le32 bmControls;
|
||||||
|
__u8 bLockDelayUnits;
|
||||||
|
__le16 wLockDelay;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* 6.1 INTERRUPT DATA MESSAGE */
|
||||||
|
struct uac3_interrupt_data_msg {
|
||||||
|
__u8 bInfo;
|
||||||
|
__u8 bSourceType;
|
||||||
|
__le16 wValue;
|
||||||
|
__le16 wIndex;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* A.2 AUDIO AUDIO FUNCTION SUBCLASS CODES */
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 0x01
|
||||||
|
/* BADD profiles */
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_GENERIC_IO 0x20
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_HEADPHONE 0x21
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_SPEAKER 0x22
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_MICROPHONE 0x23
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_HEADSET 0x24
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER 0x25
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE 0x26
|
||||||
|
|
||||||
|
/* A.7 AUDIO FUNCTION CATEGORY CODES */
|
||||||
|
#define UAC3_FUNCTION_SUBCLASS_UNDEFINED 0x00
|
||||||
|
#define UAC3_FUNCTION_DESKTOP_SPEAKER 0x01
|
||||||
|
#define UAC3_FUNCTION_HOME_THEATER 0x02
|
||||||
|
#define UAC3_FUNCTION_MICROPHONE 0x03
|
||||||
|
#define UAC3_FUNCTION_HEADSET 0x04
|
||||||
|
#define UAC3_FUNCTION_TELEPHONE 0x05
|
||||||
|
#define UAC3_FUNCTION_CONVERTER 0x06
|
||||||
|
#define UAC3_FUNCTION_SOUND_RECORDER 0x07
|
||||||
|
#define UAC3_FUNCTION_IO_BOX 0x08
|
||||||
|
#define UAC3_FUNCTION_MUSICAL_INSTRUMENT 0x09
|
||||||
|
#define UAC3_FUNCTION_PRO_AUDIO 0x0a
|
||||||
|
#define UAC3_FUNCTION_AUDIO_VIDEO 0x0b
|
||||||
|
#define UAC3_FUNCTION_CONTROL_PANEL 0x0c
|
||||||
|
#define UAC3_FUNCTION_HEADPHONE 0x0d
|
||||||
|
#define UAC3_FUNCTION_GENERIC_SPEAKER 0x0e
|
||||||
|
#define UAC3_FUNCTION_HEADSET_ADAPTER 0x0f
|
||||||
|
#define UAC3_FUNCTION_SPEAKERPHONE 0x10
|
||||||
|
#define UAC3_FUNCTION_OTHER 0xff
|
||||||
|
|
||||||
|
/* A.8 AUDIO CLASS-SPECIFIC DESCRIPTOR TYPES */
|
||||||
|
#define UAC3_CS_UNDEFINED 0x20
|
||||||
|
#define UAC3_CS_DEVICE 0x21
|
||||||
|
#define UAC3_CS_CONFIGURATION 0x22
|
||||||
|
#define UAC3_CS_STRING 0x23
|
||||||
|
#define UAC3_CS_INTERFACE 0x24
|
||||||
|
#define UAC3_CS_ENDPOINT 0x25
|
||||||
|
#define UAC3_CS_CLUSTER 0x26
|
||||||
|
|
||||||
|
/* A.10 CLUSTER DESCRIPTOR SEGMENT TYPES */
|
||||||
|
#define UAC3_SEGMENT_UNDEFINED 0x00
|
||||||
|
#define UAC3_CLUSTER_DESCRIPTION 0x01
|
||||||
|
#define UAC3_CLUSTER_VENDOR_DEFINED 0x1F
|
||||||
|
#define UAC3_CHANNEL_INFORMATION 0x20
|
||||||
|
#define UAC3_CHANNEL_AMBISONIC 0x21
|
||||||
|
#define UAC3_CHANNEL_DESCRIPTION 0x22
|
||||||
|
#define UAC3_CHANNEL_VENDOR_DEFINED 0xFE
|
||||||
|
#define UAC3_END_SEGMENT 0xFF
|
||||||
|
|
||||||
|
/* A.11 CHANNEL PURPOSE DEFINITIONS */
|
||||||
|
#define UAC3_PURPOSE_UNDEFINED 0x00
|
||||||
|
#define UAC3_PURPOSE_GENERIC_AUDIO 0x01
|
||||||
|
#define UAC3_PURPOSE_VOICE 0x02
|
||||||
|
#define UAC3_PURPOSE_SPEECH 0x03
|
||||||
|
#define UAC3_PURPOSE_AMBIENT 0x04
|
||||||
|
#define UAC3_PURPOSE_REFERENCE 0x05
|
||||||
|
#define UAC3_PURPOSE_ULTRASONIC 0x06
|
||||||
|
#define UAC3_PURPOSE_VIBROKINETIC 0x07
|
||||||
|
#define UAC3_PURPOSE_NON_AUDIO 0xFF
|
||||||
|
|
||||||
|
/* A.12 CHANNEL RELATIONSHIP DEFINITIONS */
|
||||||
|
#define UAC3_CH_RELATIONSHIP_UNDEFINED 0x00
|
||||||
|
#define UAC3_CH_MONO 0x01
|
||||||
|
#define UAC3_CH_LEFT 0x02
|
||||||
|
#define UAC3_CH_RIGHT 0x03
|
||||||
|
#define UAC3_CH_ARRAY 0x04
|
||||||
|
#define UAC3_CH_PATTERN_X 0x20
|
||||||
|
#define UAC3_CH_PATTERN_Y 0x21
|
||||||
|
#define UAC3_CH_PATTERN_A 0x22
|
||||||
|
#define UAC3_CH_PATTERN_B 0x23
|
||||||
|
#define UAC3_CH_PATTERN_M 0x24
|
||||||
|
#define UAC3_CH_PATTERN_S 0x25
|
||||||
|
#define UAC3_CH_FRONT_LEFT 0x80
|
||||||
|
#define UAC3_CH_FRONT_RIGHT 0x81
|
||||||
|
#define UAC3_CH_FRONT_CENTER 0x82
|
||||||
|
#define UAC3_CH_FRONT_LEFT_OF_CENTER 0x83
|
||||||
|
#define UAC3_CH_FRONT_RIGHT_OF_CENTER 0x84
|
||||||
|
#define UAC3_CH_FRONT_WIDE_LEFT 0x85
|
||||||
|
#define UAC3_CH_FRONT_WIDE_RIGHT 0x86
|
||||||
|
#define UAC3_CH_SIDE_LEFT 0x87
|
||||||
|
#define UAC3_CH_SIDE_RIGHT 0x88
|
||||||
|
#define UAC3_CH_SURROUND_ARRAY_LEFT 0x89
|
||||||
|
#define UAC3_CH_SURROUND_ARRAY_RIGHT 0x8A
|
||||||
|
#define UAC3_CH_BACK_LEFT 0x8B
|
||||||
|
#define UAC3_CH_BACK_RIGHT 0x8C
|
||||||
|
#define UAC3_CH_BACK_CENTER 0x8D
|
||||||
|
#define UAC3_CH_BACK_LEFT_OF_CENTER 0x8E
|
||||||
|
#define UAC3_CH_BACK_RIGHT_OF_CENTER 0x8F
|
||||||
|
#define UAC3_CH_BACK_WIDE_LEFT 0x90
|
||||||
|
#define UAC3_CH_BACK_WIDE_RIGHT 0x91
|
||||||
|
#define UAC3_CH_TOP_CENTER 0x92
|
||||||
|
#define UAC3_CH_TOP_FRONT_LEFT 0x93
|
||||||
|
#define UAC3_CH_TOP_FRONT_RIGHT 0x94
|
||||||
|
#define UAC3_CH_TOP_FRONT_CENTER 0x95
|
||||||
|
#define UAC3_CH_TOP_FRONT_LOC 0x96
|
||||||
|
#define UAC3_CH_TOP_FRONT_ROC 0x97
|
||||||
|
#define UAC3_CH_TOP_FRONT_WIDE_LEFT 0x98
|
||||||
|
#define UAC3_CH_TOP_FRONT_WIDE_RIGHT 0x99
|
||||||
|
#define UAC3_CH_TOP_SIDE_LEFT 0x9A
|
||||||
|
#define UAC3_CH_TOP_SIDE_RIGHT 0x9B
|
||||||
|
#define UAC3_CH_TOP_SURR_ARRAY_LEFT 0x9C
|
||||||
|
#define UAC3_CH_TOP_SURR_ARRAY_RIGHT 0x9D
|
||||||
|
#define UAC3_CH_TOP_BACK_LEFT 0x9E
|
||||||
|
#define UAC3_CH_TOP_BACK_RIGHT 0x9F
|
||||||
|
#define UAC3_CH_TOP_BACK_CENTER 0xA0
|
||||||
|
#define UAC3_CH_TOP_BACK_LOC 0xA1
|
||||||
|
#define UAC3_CH_TOP_BACK_ROC 0xA2
|
||||||
|
#define UAC3_CH_TOP_BACK_WIDE_LEFT 0xA3
|
||||||
|
#define UAC3_CH_TOP_BACK_WIDE_RIGHT 0xA4
|
||||||
|
#define UAC3_CH_BOTTOM_CENTER 0xA5
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_LEFT 0xA6
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_RIGHT 0xA7
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_CENTER 0xA8
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_LOC 0xA9
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_ROC 0xAA
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_WIDE_LEFT 0xAB
|
||||||
|
#define UAC3_CH_BOTTOM_FRONT_WIDE_RIGHT 0xAC
|
||||||
|
#define UAC3_CH_BOTTOM_SIDE_LEFT 0xAD
|
||||||
|
#define UAC3_CH_BOTTOM_SIDE_RIGHT 0xAE
|
||||||
|
#define UAC3_CH_BOTTOM_SURR_ARRAY_LEFT 0xAF
|
||||||
|
#define UAC3_CH_BOTTOM_SURR_ARRAY_RIGHT 0xB0
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_LEFT 0xB1
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_RIGHT 0xB2
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_CENTER 0xB3
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_LOC 0xB4
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_ROC 0xB5
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_WIDE_LEFT 0xB6
|
||||||
|
#define UAC3_CH_BOTTOM_BACK_WIDE_RIGHT 0xB7
|
||||||
|
#define UAC3_CH_LOW_FREQUENCY_EFFECTS 0xB8
|
||||||
|
#define UAC3_CH_LFE_LEFT 0xB9
|
||||||
|
#define UAC3_CH_LFE_RIGHT 0xBA
|
||||||
|
#define UAC3_CH_HEADPHONE_LEFT 0xBB
|
||||||
|
#define UAC3_CH_HEADPHONE_RIGHT 0xBC
|
||||||
|
|
||||||
|
/* A.15 AUDIO CLASS-SPECIFIC AC INTERFACE DESCRIPTOR SUBTYPES */
|
||||||
|
/* see audio.h for the rest, which is identical to v1 */
|
||||||
|
#define UAC3_EXTENDED_TERMINAL 0x04
|
||||||
|
#define UAC3_MIXER_UNIT 0x05
|
||||||
|
#define UAC3_SELECTOR_UNIT 0x06
|
||||||
|
#define UAC3_FEATURE_UNIT 0x07
|
||||||
|
#define UAC3_EFFECT_UNIT 0x08
|
||||||
|
#define UAC3_PROCESSING_UNIT 0x09
|
||||||
|
#define UAC3_EXTENSION_UNIT 0x0a
|
||||||
|
#define UAC3_CLOCK_SOURCE 0x0b
|
||||||
|
#define UAC3_CLOCK_SELECTOR 0x0c
|
||||||
|
#define UAC3_CLOCK_MULTIPLIER 0x0d
|
||||||
|
#define UAC3_SAMPLE_RATE_CONVERTER 0x0e
|
||||||
|
#define UAC3_CONNECTORS 0x0f
|
||||||
|
#define UAC3_POWER_DOMAIN 0x10
|
||||||
|
|
||||||
|
/* A.22 AUDIO CLASS-SPECIFIC REQUEST CODES */
|
||||||
|
/* see audio-v2.h for the rest, which is identical to v2 */
|
||||||
|
#define UAC3_CS_REQ_INTEN 0x04
|
||||||
|
#define UAC3_CS_REQ_STRING 0x05
|
||||||
|
#define UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR 0x06
|
||||||
|
|
||||||
|
/* A.23.1 AUDIOCONTROL INTERFACE CONTROL SELECTORS */
|
||||||
|
#define UAC3_AC_CONTROL_UNDEFINED 0x00
|
||||||
|
#define UAC3_AC_ACTIVE_INTERFACE_CONTROL 0x01
|
||||||
|
#define UAC3_AC_POWER_DOMAIN_CONTROL 0x02
|
||||||
|
|
||||||
|
#endif /* __LINUX_USB_AUDIO_V3_H */
|
|
@ -1710,6 +1710,7 @@ struct snd_emu10k1 {
|
||||||
unsigned int ecard_ctrl; /* ecard control bits */
|
unsigned int ecard_ctrl; /* ecard control bits */
|
||||||
unsigned int address_mode; /* address mode */
|
unsigned int address_mode; /* address mode */
|
||||||
unsigned long dma_mask; /* PCI DMA mask */
|
unsigned long dma_mask; /* PCI DMA mask */
|
||||||
|
bool iommu_workaround; /* IOMMU workaround needed */
|
||||||
unsigned int delay_pcm_irq; /* in samples */
|
unsigned int delay_pcm_irq; /* in samples */
|
||||||
int max_cache_pages; /* max memory size / PAGE_SIZE */
|
int max_cache_pages; /* max memory size / PAGE_SIZE */
|
||||||
struct snd_dma_buffer silent_page; /* silent page */
|
struct snd_dma_buffer silent_page; /* silent page */
|
||||||
|
@ -1718,7 +1719,6 @@ struct snd_emu10k1 {
|
||||||
struct snd_dma_buffer p16v_buffer;
|
struct snd_dma_buffer p16v_buffer;
|
||||||
|
|
||||||
struct snd_util_memhdr *memhdr; /* page allocation list */
|
struct snd_util_memhdr *memhdr; /* page allocation list */
|
||||||
struct snd_emu10k1_memblk *reserved_page; /* reserved page */
|
|
||||||
|
|
||||||
struct list_head mapped_link_head;
|
struct list_head mapped_link_head;
|
||||||
struct list_head mapped_order_link_head;
|
struct list_head mapped_order_link_head;
|
||||||
|
@ -1878,6 +1878,8 @@ void snd_p16v_resume(struct snd_emu10k1 *emu);
|
||||||
/* memory allocation */
|
/* memory allocation */
|
||||||
struct snd_util_memblk *snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream);
|
struct snd_util_memblk *snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream);
|
||||||
int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
|
int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
|
||||||
|
int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
|
||||||
|
struct snd_dma_buffer *dmab);
|
||||||
struct snd_util_memblk *snd_emu10k1_synth_alloc(struct snd_emu10k1 *emu, unsigned int size);
|
struct snd_util_memblk *snd_emu10k1_synth_alloc(struct snd_emu10k1 *emu, unsigned int size);
|
||||||
int snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
|
int snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
|
||||||
int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, int size);
|
int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, int size);
|
||||||
|
|
|
@ -146,6 +146,8 @@ int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
|
||||||
int flags, unsigned int verb, unsigned int parm);
|
int flags, unsigned int verb, unsigned int parm);
|
||||||
bool snd_hdac_check_power_state(struct hdac_device *hdac,
|
bool snd_hdac_check_power_state(struct hdac_device *hdac,
|
||||||
hda_nid_t nid, unsigned int target_state);
|
hda_nid_t nid, unsigned int target_state);
|
||||||
|
unsigned int snd_hdac_sync_power_state(struct hdac_device *hdac,
|
||||||
|
hda_nid_t nid, unsigned int target_state);
|
||||||
/**
|
/**
|
||||||
* snd_hdac_read_parm - read a codec parameter
|
* snd_hdac_read_parm - read a codec parameter
|
||||||
* @codec: the codec object
|
* @codec: the codec object
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime {
|
||||||
char *buffer; /* vmallocated period */
|
char *buffer; /* vmallocated period */
|
||||||
size_t buffer_used; /* used length from period buffer */
|
size_t buffer_used; /* used length from period buffer */
|
||||||
struct mutex params_lock;
|
struct mutex params_lock;
|
||||||
|
atomic_t rw_ref; /* concurrent read/write accesses */
|
||||||
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
|
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
|
||||||
struct snd_pcm_plugin *plugin_first;
|
struct snd_pcm_plugin *plugin_first;
|
||||||
struct snd_pcm_plugin *plugin_last;
|
struct snd_pcm_plugin *plugin_last;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
/* bInterfaceProtocol values to denote the version of the standard used */
|
/* bInterfaceProtocol values to denote the version of the standard used */
|
||||||
#define UAC_VERSION_1 0x00
|
#define UAC_VERSION_1 0x00
|
||||||
#define UAC_VERSION_2 0x20
|
#define UAC_VERSION_2 0x20
|
||||||
|
#define UAC_VERSION_3 0x30
|
||||||
|
|
||||||
/* A.2 Audio Interface Subclass Codes */
|
/* A.2 Audio Interface Subclass Codes */
|
||||||
#define USB_SUBCLASS_AUDIOCONTROL 0x01
|
#define USB_SUBCLASS_AUDIOCONTROL 0x01
|
||||||
|
|
|
@ -670,7 +670,7 @@ card_id_show_attr(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
||||||
return snprintf(buf, PAGE_SIZE, "%s\n", card->id);
|
return scnprintf(buf, PAGE_SIZE, "%s\n", card->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
@ -710,7 +710,7 @@ card_number_show_attr(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
||||||
return snprintf(buf, PAGE_SIZE, "%i\n", card->number);
|
return scnprintf(buf, PAGE_SIZE, "%i\n", card->number);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL);
|
static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL);
|
||||||
|
|
|
@ -823,8 +823,25 @@ static int choose_rate(struct snd_pcm_substream *substream,
|
||||||
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
|
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
|
/* parameter locking: returns immediately if tried during streaming */
|
||||||
bool trylock)
|
static int lock_params(struct snd_pcm_runtime *runtime)
|
||||||
|
{
|
||||||
|
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
if (atomic_read(&runtime->oss.rw_ref)) {
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock_params(struct snd_pcm_runtime *runtime)
|
||||||
|
{
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call with params_lock held */
|
||||||
|
static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
struct snd_pcm_hw_params *params, *sparams;
|
struct snd_pcm_hw_params *params, *sparams;
|
||||||
|
@ -838,11 +855,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
|
||||||
const struct snd_mask *sformat_mask;
|
const struct snd_mask *sformat_mask;
|
||||||
struct snd_mask mask;
|
struct snd_mask mask;
|
||||||
|
|
||||||
if (trylock) {
|
if (!runtime->oss.params)
|
||||||
if (!(mutex_trylock(&runtime->oss.params_lock)))
|
return 0;
|
||||||
return -EAGAIN;
|
|
||||||
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
|
||||||
return -ERESTARTSYS;
|
|
||||||
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
|
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
|
||||||
params = kmalloc(sizeof(*params), GFP_KERNEL);
|
params = kmalloc(sizeof(*params), GFP_KERNEL);
|
||||||
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
|
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
|
||||||
|
@ -1068,6 +1082,23 @@ failure:
|
||||||
kfree(sw_params);
|
kfree(sw_params);
|
||||||
kfree(params);
|
kfree(params);
|
||||||
kfree(sparams);
|
kfree(sparams);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this one takes the lock by itself */
|
||||||
|
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
|
||||||
|
bool trylock)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (trylock) {
|
||||||
|
if (!(mutex_trylock(&runtime->oss.params_lock)))
|
||||||
|
return -EAGAIN;
|
||||||
|
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
|
||||||
|
err = snd_pcm_oss_change_params_locked(substream);
|
||||||
mutex_unlock(&runtime->oss.params_lock);
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -1096,11 +1127,14 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* call with params_lock held */
|
||||||
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
|
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
|
||||||
|
if (!runtime->oss.prepare)
|
||||||
|
return 0;
|
||||||
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
|
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
pcm_dbg(substream->pcm,
|
pcm_dbg(substream->pcm,
|
||||||
|
@ -1120,14 +1154,35 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (substream == NULL)
|
|
||||||
return 0;
|
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
if (runtime->oss.params) {
|
if (runtime->oss.params) {
|
||||||
err = snd_pcm_oss_change_params(substream, false);
|
err = snd_pcm_oss_change_params(substream, false);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
if (runtime->oss.prepare) {
|
||||||
|
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
err = snd_pcm_oss_prepare(substream);
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call with params_lock held */
|
||||||
|
static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
runtime = substream->runtime;
|
||||||
|
if (runtime->oss.params) {
|
||||||
|
err = snd_pcm_oss_change_params_locked(substream);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
if (runtime->oss.prepare) {
|
if (runtime->oss.prepare) {
|
||||||
err = snd_pcm_oss_prepare(substream);
|
err = snd_pcm_oss_prepare(substream);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -1332,13 +1387,15 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
|
||||||
if (atomic_read(&substream->mmap_count))
|
if (atomic_read(&substream->mmap_count))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
|
atomic_inc(&runtime->oss.rw_ref);
|
||||||
return tmp;
|
|
||||||
while (bytes > 0) {
|
while (bytes > 0) {
|
||||||
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
||||||
tmp = -ERESTARTSYS;
|
tmp = -ERESTARTSYS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tmp = snd_pcm_oss_make_ready_locked(substream);
|
||||||
|
if (tmp < 0)
|
||||||
|
goto err;
|
||||||
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
|
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
|
||||||
tmp = bytes;
|
tmp = bytes;
|
||||||
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
|
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
|
||||||
|
@ -1394,6 +1451,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
|
||||||
}
|
}
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
}
|
}
|
||||||
|
atomic_dec(&runtime->oss.rw_ref);
|
||||||
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,13 +1497,15 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
|
||||||
if (atomic_read(&substream->mmap_count))
|
if (atomic_read(&substream->mmap_count))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
|
atomic_inc(&runtime->oss.rw_ref);
|
||||||
return tmp;
|
|
||||||
while (bytes > 0) {
|
while (bytes > 0) {
|
||||||
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
||||||
tmp = -ERESTARTSYS;
|
tmp = -ERESTARTSYS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tmp = snd_pcm_oss_make_ready_locked(substream);
|
||||||
|
if (tmp < 0)
|
||||||
|
goto err;
|
||||||
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
|
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
|
||||||
if (runtime->oss.buffer_used == 0) {
|
if (runtime->oss.buffer_used == 0) {
|
||||||
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
|
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
|
||||||
|
@ -1486,6 +1546,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
|
||||||
}
|
}
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
}
|
}
|
||||||
|
atomic_dec(&runtime->oss.rw_ref);
|
||||||
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,10 +1562,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
continue;
|
continue;
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||||
|
mutex_lock(&runtime->oss.params_lock);
|
||||||
runtime->oss.prepare = 1;
|
runtime->oss.prepare = 1;
|
||||||
runtime->oss.buffer_used = 0;
|
runtime->oss.buffer_used = 0;
|
||||||
runtime->oss.prev_hw_ptr_period = 0;
|
runtime->oss.prev_hw_ptr_period = 0;
|
||||||
runtime->oss.period_ptr = 0;
|
runtime->oss.period_ptr = 0;
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1590,9 +1653,13 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
goto __direct;
|
goto __direct;
|
||||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||||
return err;
|
return err;
|
||||||
|
atomic_inc(&runtime->oss.rw_ref);
|
||||||
|
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
||||||
|
atomic_dec(&runtime->oss.rw_ref);
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
}
|
||||||
format = snd_pcm_oss_format_from(runtime->oss.format);
|
format = snd_pcm_oss_format_from(runtime->oss.format);
|
||||||
width = snd_pcm_format_physical_width(format);
|
width = snd_pcm_format_physical_width(format);
|
||||||
mutex_lock(&runtime->oss.params_lock);
|
|
||||||
if (runtime->oss.buffer_used > 0) {
|
if (runtime->oss.buffer_used > 0) {
|
||||||
#ifdef OSS_DEBUG
|
#ifdef OSS_DEBUG
|
||||||
pcm_dbg(substream->pcm, "sync: buffer_used\n");
|
pcm_dbg(substream->pcm, "sync: buffer_used\n");
|
||||||
|
@ -1602,10 +1669,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
runtime->oss.buffer + runtime->oss.buffer_used,
|
runtime->oss.buffer + runtime->oss.buffer_used,
|
||||||
size);
|
size);
|
||||||
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
|
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
mutex_unlock(&runtime->oss.params_lock);
|
goto unlock;
|
||||||
return err;
|
|
||||||
}
|
|
||||||
} else if (runtime->oss.period_ptr > 0) {
|
} else if (runtime->oss.period_ptr > 0) {
|
||||||
#ifdef OSS_DEBUG
|
#ifdef OSS_DEBUG
|
||||||
pcm_dbg(substream->pcm, "sync: period_ptr\n");
|
pcm_dbg(substream->pcm, "sync: period_ptr\n");
|
||||||
|
@ -1615,10 +1680,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
runtime->oss.buffer,
|
runtime->oss.buffer,
|
||||||
size * 8 / width);
|
size * 8 / width);
|
||||||
err = snd_pcm_oss_sync1(substream, size);
|
err = snd_pcm_oss_sync1(substream, size);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
mutex_unlock(&runtime->oss.params_lock);
|
goto unlock;
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The ALSA's period might be a bit large than OSS one.
|
* The ALSA's period might be a bit large than OSS one.
|
||||||
|
@ -1632,7 +1695,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
|
else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
|
||||||
snd_pcm_lib_writev(substream, NULL, size);
|
snd_pcm_lib_writev(substream, NULL, size);
|
||||||
}
|
}
|
||||||
|
unlock:
|
||||||
mutex_unlock(&runtime->oss.params_lock);
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
atomic_dec(&runtime->oss.rw_ref);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
/*
|
/*
|
||||||
* finish sync: drain the buffer
|
* finish sync: drain the buffer
|
||||||
*/
|
*/
|
||||||
|
@ -1643,7 +1710,9 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
substream->f_flags = saved_f_flags;
|
substream->f_flags = saved_f_flags;
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
mutex_lock(&runtime->oss.params_lock);
|
||||||
runtime->oss.prepare = 1;
|
runtime->oss.prepare = 1;
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
|
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
|
||||||
|
@ -1654,8 +1723,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
mutex_lock(&runtime->oss.params_lock);
|
||||||
runtime->oss.buffer_used = 0;
|
runtime->oss.buffer_used = 0;
|
||||||
runtime->oss.prepare = 1;
|
runtime->oss.prepare = 1;
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1667,6 +1738,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
|
||||||
for (idx = 1; idx >= 0; --idx) {
|
for (idx = 1; idx >= 0; --idx) {
|
||||||
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (substream == NULL)
|
if (substream == NULL)
|
||||||
continue;
|
continue;
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
|
@ -1674,10 +1747,14 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
|
||||||
rate = 1000;
|
rate = 1000;
|
||||||
else if (rate > 192000)
|
else if (rate > 192000)
|
||||||
rate = 192000;
|
rate = 192000;
|
||||||
|
err = lock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
if (runtime->oss.rate != rate) {
|
if (runtime->oss.rate != rate) {
|
||||||
runtime->oss.params = 1;
|
runtime->oss.params = 1;
|
||||||
runtime->oss.rate = rate;
|
runtime->oss.rate = rate;
|
||||||
}
|
}
|
||||||
|
unlock_params(runtime);
|
||||||
}
|
}
|
||||||
return snd_pcm_oss_get_rate(pcm_oss_file);
|
return snd_pcm_oss_get_rate(pcm_oss_file);
|
||||||
}
|
}
|
||||||
|
@ -1702,13 +1779,19 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
|
||||||
for (idx = 1; idx >= 0; --idx) {
|
for (idx = 1; idx >= 0; --idx) {
|
||||||
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (substream == NULL)
|
if (substream == NULL)
|
||||||
continue;
|
continue;
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
|
err = lock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
if (runtime->oss.channels != channels) {
|
if (runtime->oss.channels != channels) {
|
||||||
runtime->oss.params = 1;
|
runtime->oss.params = 1;
|
||||||
runtime->oss.channels = channels;
|
runtime->oss.channels = channels;
|
||||||
}
|
}
|
||||||
|
unlock_params(runtime);
|
||||||
}
|
}
|
||||||
return snd_pcm_oss_get_channels(pcm_oss_file);
|
return snd_pcm_oss_get_channels(pcm_oss_file);
|
||||||
}
|
}
|
||||||
|
@ -1781,6 +1864,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
|
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
|
||||||
{
|
{
|
||||||
int formats, idx;
|
int formats, idx;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (format != AFMT_QUERY) {
|
if (format != AFMT_QUERY) {
|
||||||
formats = snd_pcm_oss_get_formats(pcm_oss_file);
|
formats = snd_pcm_oss_get_formats(pcm_oss_file);
|
||||||
|
@ -1794,10 +1878,14 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
|
||||||
if (substream == NULL)
|
if (substream == NULL)
|
||||||
continue;
|
continue;
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
|
err = lock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
if (runtime->oss.format != format) {
|
if (runtime->oss.format != format) {
|
||||||
runtime->oss.params = 1;
|
runtime->oss.params = 1;
|
||||||
runtime->oss.format = format;
|
runtime->oss.format = format;
|
||||||
}
|
}
|
||||||
|
unlock_params(runtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return snd_pcm_oss_get_format(pcm_oss_file);
|
return snd_pcm_oss_get_format(pcm_oss_file);
|
||||||
|
@ -1817,8 +1905,6 @@ static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int s
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
|
|
||||||
if (substream == NULL)
|
|
||||||
return 0;
|
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
if (subdivide == 0) {
|
if (subdivide == 0) {
|
||||||
subdivide = runtime->oss.subdivision;
|
subdivide = runtime->oss.subdivision;
|
||||||
|
@ -1842,9 +1928,17 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
|
||||||
|
|
||||||
for (idx = 1; idx >= 0; --idx) {
|
for (idx = 1; idx >= 0; --idx) {
|
||||||
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
||||||
|
struct snd_pcm_runtime *runtime;
|
||||||
|
|
||||||
if (substream == NULL)
|
if (substream == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
|
runtime = substream->runtime;
|
||||||
|
err = lock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_pcm_oss_set_subdivide1(substream, subdivide);
|
||||||
|
unlock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -1854,8 +1948,6 @@ static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsign
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
|
|
||||||
if (substream == NULL)
|
|
||||||
return 0;
|
|
||||||
runtime = substream->runtime;
|
runtime = substream->runtime;
|
||||||
if (runtime->oss.subdivision || runtime->oss.fragshift)
|
if (runtime->oss.subdivision || runtime->oss.fragshift)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1875,9 +1967,17 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
|
||||||
|
|
||||||
for (idx = 1; idx >= 0; --idx) {
|
for (idx = 1; idx >= 0; --idx) {
|
||||||
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
|
||||||
|
struct snd_pcm_runtime *runtime;
|
||||||
|
|
||||||
if (substream == NULL)
|
if (substream == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
|
runtime = substream->runtime;
|
||||||
|
err = lock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_pcm_oss_set_fragment1(substream, val);
|
||||||
|
unlock_params(runtime);
|
||||||
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -1961,6 +2061,9 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
|
||||||
}
|
}
|
||||||
if (psubstream) {
|
if (psubstream) {
|
||||||
runtime = psubstream->runtime;
|
runtime = psubstream->runtime;
|
||||||
|
cmd = 0;
|
||||||
|
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||||
|
return -ERESTARTSYS;
|
||||||
if (trigger & PCM_ENABLE_OUTPUT) {
|
if (trigger & PCM_ENABLE_OUTPUT) {
|
||||||
if (runtime->oss.trigger)
|
if (runtime->oss.trigger)
|
||||||
goto _skip1;
|
goto _skip1;
|
||||||
|
@ -1978,13 +2081,19 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
|
||||||
cmd = SNDRV_PCM_IOCTL_DROP;
|
cmd = SNDRV_PCM_IOCTL_DROP;
|
||||||
runtime->oss.prepare = 1;
|
runtime->oss.prepare = 1;
|
||||||
}
|
}
|
||||||
|
_skip1:
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
if (cmd) {
|
||||||
err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
|
err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
_skip1:
|
}
|
||||||
if (csubstream) {
|
if (csubstream) {
|
||||||
runtime = csubstream->runtime;
|
runtime = csubstream->runtime;
|
||||||
|
cmd = 0;
|
||||||
|
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||||
|
return -ERESTARTSYS;
|
||||||
if (trigger & PCM_ENABLE_INPUT) {
|
if (trigger & PCM_ENABLE_INPUT) {
|
||||||
if (runtime->oss.trigger)
|
if (runtime->oss.trigger)
|
||||||
goto _skip2;
|
goto _skip2;
|
||||||
|
@ -1999,11 +2108,14 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
|
||||||
cmd = SNDRV_PCM_IOCTL_DROP;
|
cmd = SNDRV_PCM_IOCTL_DROP;
|
||||||
runtime->oss.prepare = 1;
|
runtime->oss.prepare = 1;
|
||||||
}
|
}
|
||||||
|
_skip2:
|
||||||
|
mutex_unlock(&runtime->oss.params_lock);
|
||||||
|
if (cmd) {
|
||||||
err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
|
err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
_skip2:
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2255,6 +2367,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
|
||||||
runtime->oss.maxfrags = 0;
|
runtime->oss.maxfrags = 0;
|
||||||
runtime->oss.subdivision = 0;
|
runtime->oss.subdivision = 0;
|
||||||
substream->pcm_release = snd_pcm_oss_release_substream;
|
substream->pcm_release = snd_pcm_oss_release_substream;
|
||||||
|
atomic_set(&runtime->oss.rw_ref, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
|
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
|
||||||
|
|
|
@ -1129,16 +1129,12 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
|
||||||
if (constrs->rules_num >= constrs->rules_all) {
|
if (constrs->rules_num >= constrs->rules_all) {
|
||||||
struct snd_pcm_hw_rule *new;
|
struct snd_pcm_hw_rule *new;
|
||||||
unsigned int new_rules = constrs->rules_all + 16;
|
unsigned int new_rules = constrs->rules_all + 16;
|
||||||
new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
|
new = krealloc(constrs->rules, new_rules * sizeof(*c),
|
||||||
|
GFP_KERNEL);
|
||||||
if (!new) {
|
if (!new) {
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
if (constrs->rules) {
|
|
||||||
memcpy(new, constrs->rules,
|
|
||||||
constrs->rules_num * sizeof(*c));
|
|
||||||
kfree(constrs->rules);
|
|
||||||
}
|
|
||||||
constrs->rules = new;
|
constrs->rules = new;
|
||||||
constrs->rules_all = new_rules;
|
constrs->rules_all = new_rules;
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,7 +323,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
|
||||||
struct snd_pcm_hw_constraints *constrs =
|
struct snd_pcm_hw_constraints *constrs =
|
||||||
&substream->runtime->hw_constraints;
|
&substream->runtime->hw_constraints;
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
unsigned int rstamps[constrs->rules_num];
|
unsigned int *rstamps;
|
||||||
unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
|
unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
|
||||||
unsigned int stamp;
|
unsigned int stamp;
|
||||||
struct snd_pcm_hw_rule *r;
|
struct snd_pcm_hw_rule *r;
|
||||||
|
@ -331,7 +331,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
|
||||||
struct snd_mask old_mask;
|
struct snd_mask old_mask;
|
||||||
struct snd_interval old_interval;
|
struct snd_interval old_interval;
|
||||||
bool again;
|
bool again;
|
||||||
int changed;
|
int changed, err = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each application of rule has own sequence number.
|
* Each application of rule has own sequence number.
|
||||||
|
@ -339,8 +339,9 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream,
|
||||||
* Each member of 'rstamps' array represents the sequence number of
|
* Each member of 'rstamps' array represents the sequence number of
|
||||||
* recent application of corresponding rule.
|
* recent application of corresponding rule.
|
||||||
*/
|
*/
|
||||||
for (k = 0; k < constrs->rules_num; k++)
|
rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
|
||||||
rstamps[k] = 0;
|
if (!rstamps)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each member of 'vstamps' array represents the sequence number of
|
* Each member of 'vstamps' array represents the sequence number of
|
||||||
|
@ -398,8 +399,10 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
changed = r->func(params, r);
|
changed = r->func(params, r);
|
||||||
if (changed < 0)
|
if (changed < 0) {
|
||||||
return changed;
|
err = changed;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When the parameter is changed, notify it to the caller
|
* When the parameter is changed, notify it to the caller
|
||||||
|
@ -430,7 +433,9 @@ retry:
|
||||||
if (again)
|
if (again)
|
||||||
goto retry;
|
goto retry;
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
kfree(rstamps);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
|
static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
|
||||||
|
|
|
@ -63,15 +63,18 @@ static int slave_update(struct link_slave *slave)
|
||||||
struct snd_ctl_elem_value *uctl;
|
struct snd_ctl_elem_value *uctl;
|
||||||
int err, ch;
|
int err, ch;
|
||||||
|
|
||||||
uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
|
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
|
||||||
if (!uctl)
|
if (!uctl)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
uctl->id = slave->slave.id;
|
uctl->id = slave->slave.id;
|
||||||
err = slave->slave.get(&slave->slave, uctl);
|
err = slave->slave.get(&slave->slave, uctl);
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
for (ch = 0; ch < slave->info.count; ch++)
|
for (ch = 0; ch < slave->info.count; ch++)
|
||||||
slave->vals[ch] = uctl->value.integer.value[ch];
|
slave->vals[ch] = uctl->value.integer.value[ch];
|
||||||
|
error:
|
||||||
kfree(uctl);
|
kfree(uctl);
|
||||||
return 0;
|
return err < 0 ? err : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get the slave ctl info and save the initial values */
|
/* get the slave ctl info and save the initial values */
|
||||||
|
|
|
@ -296,6 +296,8 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
cable->pause |= stream;
|
cable->pause |= stream;
|
||||||
loopback_timer_stop(dpcm);
|
loopback_timer_stop(dpcm);
|
||||||
spin_unlock(&cable->lock);
|
spin_unlock(&cable->lock);
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
loopback_active_notify(dpcm);
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
|
@ -304,6 +306,8 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
cable->pause &= ~stream;
|
cable->pause &= ~stream;
|
||||||
loopback_timer_start(dpcm);
|
loopback_timer_start(dpcm);
|
||||||
spin_unlock(&cable->lock);
|
spin_unlock(&cable->lock);
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
loopback_active_notify(dpcm);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -892,9 +896,11 @@ static int loopback_active_get(struct snd_kcontrol *kcontrol,
|
||||||
[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
|
[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
|
||||||
unsigned int val = 0;
|
unsigned int val = 0;
|
||||||
|
|
||||||
if (cable != NULL)
|
if (cable != NULL) {
|
||||||
val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
|
unsigned int running = cable->running ^ cable->pause;
|
||||||
1 : 0;
|
|
||||||
|
val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
|
||||||
|
}
|
||||||
ucontrol->value.integer.value[0] = val;
|
ucontrol->value.integer.value[0] = val;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -1064,3 +1065,37 @@ bool snd_hdac_check_power_state(struct hdac_device *hdac,
|
||||||
return (state == target_state);
|
return (state == target_state);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
|
EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
|
||||||
|
/**
|
||||||
|
* snd_hdac_sync_power_state - wait until actual power state matches
|
||||||
|
* with the target state
|
||||||
|
*
|
||||||
|
* @hdac: the HDAC device
|
||||||
|
* @nid: NID to send the command
|
||||||
|
* @target_state: target state to check for
|
||||||
|
*
|
||||||
|
* Return power state or PS_ERROR if codec rejects GET verb.
|
||||||
|
*/
|
||||||
|
unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
|
||||||
|
hda_nid_t nid, unsigned int power_state)
|
||||||
|
{
|
||||||
|
unsigned long end_time = jiffies + msecs_to_jiffies(500);
|
||||||
|
unsigned int state, actual_state, count;
|
||||||
|
|
||||||
|
for (count = 0; count < 500; count++) {
|
||||||
|
state = snd_hdac_codec_read(codec, nid, 0,
|
||||||
|
AC_VERB_GET_POWER_STATE, 0);
|
||||||
|
if (state & AC_PWRST_ERROR) {
|
||||||
|
msleep(20);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
actual_state = (state >> 4) & 0x0f;
|
||||||
|
if (actual_state == power_state)
|
||||||
|
break;
|
||||||
|
if (time_after_eq(jiffies, end_time))
|
||||||
|
break;
|
||||||
|
/* wait until the codec reachs to the target state */
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
|
||||||
|
|
|
@ -736,8 +736,7 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
|
||||||
static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
{
|
{
|
||||||
struct echoaudio *chip = snd_pcm_substream_chip(substream);
|
struct echoaudio *chip = snd_pcm_substream_chip(substream);
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct audiopipe *pipe;
|
||||||
struct audiopipe *pipe = runtime->private_data;
|
|
||||||
int i, err;
|
int i, err;
|
||||||
u32 channelmask = 0;
|
u32 channelmask = 0;
|
||||||
struct snd_pcm_substream *s;
|
struct snd_pcm_substream *s;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/iommu.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
@ -1272,12 +1273,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
|
||||||
release_firmware(emu->dock_fw);
|
release_firmware(emu->dock_fw);
|
||||||
if (emu->irq >= 0)
|
if (emu->irq >= 0)
|
||||||
free_irq(emu->irq, emu);
|
free_irq(emu->irq, emu);
|
||||||
/* remove reserved page */
|
|
||||||
if (emu->reserved_page) {
|
|
||||||
snd_emu10k1_synth_free(emu,
|
|
||||||
(struct snd_util_memblk *)emu->reserved_page);
|
|
||||||
emu->reserved_page = NULL;
|
|
||||||
}
|
|
||||||
snd_util_memhdr_free(emu->memhdr);
|
snd_util_memhdr_free(emu->memhdr);
|
||||||
if (emu->silent_page.area)
|
if (emu->silent_page.area)
|
||||||
snd_dma_free_pages(&emu->silent_page);
|
snd_dma_free_pages(&emu->silent_page);
|
||||||
|
@ -1764,6 +1759,38 @@ static struct snd_emu_chip_details emu_chip_details[] = {
|
||||||
{ } /* terminator */
|
{ } /* terminator */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The chip (at least the Audigy 2 CA0102 chip, but most likely others, too)
|
||||||
|
* has a problem that from time to time it likes to do few DMA reads a bit
|
||||||
|
* beyond its normal allocation and gets very confused if these reads get
|
||||||
|
* blocked by a IOMMU.
|
||||||
|
*
|
||||||
|
* This behaviour has been observed for the first (reserved) page
|
||||||
|
* (for which it happens multiple times at every playback), often for various
|
||||||
|
* synth pages and sometimes for PCM playback buffers and the page table
|
||||||
|
* memory itself.
|
||||||
|
*
|
||||||
|
* As a workaround let's widen these DMA allocations by an extra page if we
|
||||||
|
* detect that the device is behind a non-passthrough IOMMU.
|
||||||
|
*/
|
||||||
|
static void snd_emu10k1_detect_iommu(struct snd_emu10k1 *emu)
|
||||||
|
{
|
||||||
|
struct iommu_domain *domain;
|
||||||
|
|
||||||
|
emu->iommu_workaround = false;
|
||||||
|
|
||||||
|
if (!iommu_present(emu->card->dev->bus))
|
||||||
|
return;
|
||||||
|
|
||||||
|
domain = iommu_get_domain_for_dev(emu->card->dev);
|
||||||
|
if (domain && domain->type == IOMMU_DOMAIN_IDENTITY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev_notice(emu->card->dev,
|
||||||
|
"non-passthrough IOMMU detected, widening DMA allocations");
|
||||||
|
emu->iommu_workaround = true;
|
||||||
|
}
|
||||||
|
|
||||||
int snd_emu10k1_create(struct snd_card *card,
|
int snd_emu10k1_create(struct snd_card *card,
|
||||||
struct pci_dev *pci,
|
struct pci_dev *pci,
|
||||||
unsigned short extin_mask,
|
unsigned short extin_mask,
|
||||||
|
@ -1776,6 +1803,7 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||||
struct snd_emu10k1 *emu;
|
struct snd_emu10k1 *emu;
|
||||||
int idx, err;
|
int idx, err;
|
||||||
int is_audigy;
|
int is_audigy;
|
||||||
|
size_t page_table_size;
|
||||||
unsigned int silent_page;
|
unsigned int silent_page;
|
||||||
const struct snd_emu_chip_details *c;
|
const struct snd_emu_chip_details *c;
|
||||||
static struct snd_device_ops ops = {
|
static struct snd_device_ops ops = {
|
||||||
|
@ -1873,12 +1901,13 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||||
|
|
||||||
is_audigy = emu->audigy = c->emu10k2_chip;
|
is_audigy = emu->audigy = c->emu10k2_chip;
|
||||||
|
|
||||||
|
snd_emu10k1_detect_iommu(emu);
|
||||||
|
|
||||||
/* set addressing mode */
|
/* set addressing mode */
|
||||||
emu->address_mode = is_audigy ? 0 : 1;
|
emu->address_mode = is_audigy ? 0 : 1;
|
||||||
/* set the DMA transfer mask */
|
/* set the DMA transfer mask */
|
||||||
emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
|
emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
|
||||||
if (dma_set_mask(&pci->dev, emu->dma_mask) < 0 ||
|
if (dma_set_mask_and_coherent(&pci->dev, emu->dma_mask) < 0) {
|
||||||
dma_set_coherent_mask(&pci->dev, emu->dma_mask) < 0) {
|
|
||||||
dev_err(card->dev,
|
dev_err(card->dev,
|
||||||
"architecture does not support PCI busmaster DMA with mask 0x%lx\n",
|
"architecture does not support PCI busmaster DMA with mask 0x%lx\n",
|
||||||
emu->dma_mask);
|
emu->dma_mask);
|
||||||
|
@ -1900,11 +1929,17 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||||
emu->port = pci_resource_start(pci, 0);
|
emu->port = pci_resource_start(pci, 0);
|
||||||
|
|
||||||
emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
|
emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
|
||||||
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
|
|
||||||
(emu->address_mode ? 32 : 16) * 1024, &emu->ptb_pages) < 0) {
|
page_table_size = sizeof(u32) * (emu->address_mode ? MAXPAGES1 :
|
||||||
|
MAXPAGES0);
|
||||||
|
if (snd_emu10k1_alloc_pages_maybe_wider(emu, page_table_size,
|
||||||
|
&emu->ptb_pages) < 0) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
dev_dbg(card->dev, "page table address range is %.8lx:%.8lx\n",
|
||||||
|
(unsigned long)emu->ptb_pages.addr,
|
||||||
|
(unsigned long)(emu->ptb_pages.addr + emu->ptb_pages.bytes));
|
||||||
|
|
||||||
emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
|
emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
|
||||||
emu->page_addr_table = vmalloc(emu->max_cache_pages *
|
emu->page_addr_table = vmalloc(emu->max_cache_pages *
|
||||||
|
@ -1914,11 +1949,16 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
|
if (snd_emu10k1_alloc_pages_maybe_wider(emu, EMUPAGESIZE,
|
||||||
EMUPAGESIZE, &emu->silent_page) < 0) {
|
&emu->silent_page) < 0) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
dev_dbg(card->dev, "silent page range is %.8lx:%.8lx\n",
|
||||||
|
(unsigned long)emu->silent_page.addr,
|
||||||
|
(unsigned long)(emu->silent_page.addr +
|
||||||
|
emu->silent_page.bytes));
|
||||||
|
|
||||||
emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
|
emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
|
||||||
if (emu->memhdr == NULL) {
|
if (emu->memhdr == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -1993,13 +2033,8 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||||
SPCS_GENERATIONSTATUS | 0x00001200 |
|
SPCS_GENERATIONSTATUS | 0x00001200 |
|
||||||
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
|
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
|
||||||
|
|
||||||
emu->reserved_page = (struct snd_emu10k1_memblk *)
|
|
||||||
snd_emu10k1_synth_alloc(emu, 4096);
|
|
||||||
if (emu->reserved_page)
|
|
||||||
emu->reserved_page->map_locked = 1;
|
|
||||||
|
|
||||||
/* Clear silent pages and set up pointers */
|
/* Clear silent pages and set up pointers */
|
||||||
memset(emu->silent_page.area, 0, PAGE_SIZE);
|
memset(emu->silent_page.area, 0, emu->silent_page.bytes);
|
||||||
silent_page = emu->silent_page.addr << emu->address_mode;
|
silent_page = emu->silent_page.addr << emu->address_mode;
|
||||||
for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
|
for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
|
||||||
((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
|
((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
|
||||||
|
|
|
@ -411,12 +411,20 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||||
|
size_t alloc_size;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
|
if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
|
||||||
return err;
|
return err;
|
||||||
if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
|
|
||||||
|
alloc_size = params_buffer_bytes(hw_params);
|
||||||
|
if (emu->iommu_workaround)
|
||||||
|
alloc_size += EMUPAGESIZE;
|
||||||
|
err = snd_pcm_lib_malloc_pages(substream, alloc_size);
|
||||||
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
if (emu->iommu_workaround && runtime->dma_bytes >= EMUPAGESIZE)
|
||||||
|
runtime->dma_bytes -= EMUPAGESIZE;
|
||||||
if (err > 0) { /* change */
|
if (err > 0) { /* change */
|
||||||
int mapped;
|
int mapped;
|
||||||
if (epcm->memblk != NULL)
|
if (epcm->memblk != NULL)
|
||||||
|
|
|
@ -34,7 +34,10 @@
|
||||||
* aligned pages in others
|
* aligned pages in others
|
||||||
*/
|
*/
|
||||||
#define __set_ptb_entry(emu,page,addr) \
|
#define __set_ptb_entry(emu,page,addr) \
|
||||||
(((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
|
(((__le32 *)(emu)->ptb_pages.area)[page] = \
|
||||||
|
cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
|
||||||
|
#define __get_ptb_entry(emu, page) \
|
||||||
|
(le32_to_cpu(((__le32 *)(emu)->ptb_pages.area)[page]))
|
||||||
|
|
||||||
#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
|
#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
|
||||||
#define MAX_ALIGN_PAGES0 (MAXPAGES0 / UNIT_PAGES)
|
#define MAX_ALIGN_PAGES0 (MAXPAGES0 / UNIT_PAGES)
|
||||||
|
@ -44,8 +47,7 @@
|
||||||
/* get offset address from aligned page */
|
/* get offset address from aligned page */
|
||||||
#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
|
#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
|
||||||
|
|
||||||
#if PAGE_SIZE == 4096
|
#if PAGE_SIZE == EMUPAGESIZE && !IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
|
||||||
/* page size == EMUPAGESIZE */
|
|
||||||
/* fill PTB entrie(s) corresponding to page with addr */
|
/* fill PTB entrie(s) corresponding to page with addr */
|
||||||
#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
|
#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
|
||||||
/* fill PTB entrie(s) corresponding to page with silence pointer */
|
/* fill PTB entrie(s) corresponding to page with silence pointer */
|
||||||
|
@ -58,6 +60,8 @@ static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t a
|
||||||
page *= UNIT_PAGES;
|
page *= UNIT_PAGES;
|
||||||
for (i = 0; i < UNIT_PAGES; i++, page++) {
|
for (i = 0; i < UNIT_PAGES; i++, page++) {
|
||||||
__set_ptb_entry(emu, page, addr);
|
__set_ptb_entry(emu, page, addr);
|
||||||
|
dev_dbg(emu->card->dev, "mapped page %d to entry %.8x\n", page,
|
||||||
|
(unsigned int)__get_ptb_entry(emu, page));
|
||||||
addr += EMUPAGESIZE;
|
addr += EMUPAGESIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,9 +69,12 @@ static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
page *= UNIT_PAGES;
|
page *= UNIT_PAGES;
|
||||||
for (i = 0; i < UNIT_PAGES; i++, page++)
|
for (i = 0; i < UNIT_PAGES; i++, page++) {
|
||||||
/* do not increment ptr */
|
/* do not increment ptr */
|
||||||
__set_ptb_entry(emu, page, emu->silent_page.addr);
|
__set_ptb_entry(emu, page, emu->silent_page.addr);
|
||||||
|
dev_dbg(emu->card->dev, "mapped silent page %d to entry %.8x\n",
|
||||||
|
page, (unsigned int)__get_ptb_entry(emu, page));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* PAGE_SIZE */
|
#endif /* PAGE_SIZE */
|
||||||
|
|
||||||
|
@ -102,7 +109,7 @@ static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
|
||||||
*/
|
*/
|
||||||
static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
|
static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
|
||||||
{
|
{
|
||||||
int page = 0, found_page = -ENOMEM;
|
int page = 1, found_page = -ENOMEM;
|
||||||
int max_size = npages;
|
int max_size = npages;
|
||||||
int size;
|
int size;
|
||||||
struct list_head *candidate = &emu->mapped_link_head;
|
struct list_head *candidate = &emu->mapped_link_head;
|
||||||
|
@ -147,6 +154,10 @@ static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||||
page = search_empty_map_area(emu, blk->pages, &next);
|
page = search_empty_map_area(emu, blk->pages, &next);
|
||||||
if (page < 0) /* not found */
|
if (page < 0) /* not found */
|
||||||
return page;
|
return page;
|
||||||
|
if (page == 0) {
|
||||||
|
dev_err(emu->card->dev, "trying to map zero (reserved) page\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
/* insert this block in the proper position of mapped list */
|
/* insert this block in the proper position of mapped list */
|
||||||
list_add_tail(&blk->mapped_link, next);
|
list_add_tail(&blk->mapped_link, next);
|
||||||
/* append this as a newest block in order list */
|
/* append this as a newest block in order list */
|
||||||
|
@ -177,7 +188,7 @@ static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||||
q = get_emu10k1_memblk(p, mapped_link);
|
q = get_emu10k1_memblk(p, mapped_link);
|
||||||
start_page = q->mapped_page + q->pages;
|
start_page = q->mapped_page + q->pages;
|
||||||
} else
|
} else
|
||||||
start_page = 0;
|
start_page = 1;
|
||||||
if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
|
if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
|
||||||
q = get_emu10k1_memblk(p, mapped_link);
|
q = get_emu10k1_memblk(p, mapped_link);
|
||||||
end_page = q->mapped_page;
|
end_page = q->mapped_page;
|
||||||
|
@ -366,6 +377,33 @@ int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
|
||||||
return snd_emu10k1_synth_free(emu, blk);
|
return snd_emu10k1_synth_free(emu, blk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate DMA pages, widening the allocation if necessary
|
||||||
|
*
|
||||||
|
* See the comment above snd_emu10k1_detect_iommu() in emu10k1_main.c why
|
||||||
|
* this might be needed.
|
||||||
|
*
|
||||||
|
* If you modify this function check whether __synth_free_pages() also needs
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
|
||||||
|
struct snd_dma_buffer *dmab)
|
||||||
|
{
|
||||||
|
if (emu->iommu_workaround) {
|
||||||
|
size_t npages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||||
|
size_t size_real = npages * PAGE_SIZE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The device has been observed to accesses up to 256 extra
|
||||||
|
* bytes, but use 1k to be safe.
|
||||||
|
*/
|
||||||
|
if (size_real < size + 1024)
|
||||||
|
size += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
|
||||||
|
snd_dma_pci_data(emu->pci), size, dmab);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* memory allocation using multiple pages (for synth)
|
* memory allocation using multiple pages (for synth)
|
||||||
|
@ -450,10 +488,27 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
|
||||||
static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
|
static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
|
||||||
int last_page)
|
int last_page)
|
||||||
{
|
{
|
||||||
|
struct snd_dma_buffer dmab;
|
||||||
int page;
|
int page;
|
||||||
|
|
||||||
|
dmab.dev.type = SNDRV_DMA_TYPE_DEV;
|
||||||
|
dmab.dev.dev = snd_dma_pci_data(emu->pci);
|
||||||
|
|
||||||
for (page = first_page; page <= last_page; page++) {
|
for (page = first_page; page <= last_page; page++) {
|
||||||
free_page((unsigned long)emu->page_ptr_table[page]);
|
if (emu->page_ptr_table[page] == NULL)
|
||||||
|
continue;
|
||||||
|
dmab.area = emu->page_ptr_table[page];
|
||||||
|
dmab.addr = emu->page_addr_table[page];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* please keep me in sync with logic in
|
||||||
|
* snd_emu10k1_alloc_pages_maybe_wider()
|
||||||
|
*/
|
||||||
|
dmab.bytes = PAGE_SIZE;
|
||||||
|
if (emu->iommu_workaround)
|
||||||
|
dmab.bytes *= 2;
|
||||||
|
|
||||||
|
snd_dma_free_pages(&dmab);
|
||||||
emu->page_addr_table[page] = 0;
|
emu->page_addr_table[page] = 0;
|
||||||
emu->page_ptr_table[page] = NULL;
|
emu->page_ptr_table[page] = NULL;
|
||||||
}
|
}
|
||||||
|
@ -465,30 +520,30 @@ static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
|
||||||
static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||||
{
|
{
|
||||||
int page, first_page, last_page;
|
int page, first_page, last_page;
|
||||||
|
struct snd_dma_buffer dmab;
|
||||||
|
|
||||||
emu10k1_memblk_init(blk);
|
emu10k1_memblk_init(blk);
|
||||||
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
|
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
|
||||||
/* allocate kernel pages */
|
/* allocate kernel pages */
|
||||||
for (page = first_page; page <= last_page; page++) {
|
for (page = first_page; page <= last_page; page++) {
|
||||||
/* first try to allocate from <4GB zone */
|
if (snd_emu10k1_alloc_pages_maybe_wider(emu, PAGE_SIZE,
|
||||||
struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
|
&dmab) < 0)
|
||||||
__GFP_NOWARN);
|
goto __fail;
|
||||||
if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
|
if (!is_valid_page(emu, dmab.addr)) {
|
||||||
if (p)
|
snd_dma_free_pages(&dmab);
|
||||||
__free_page(p);
|
goto __fail;
|
||||||
/* try to allocate from <16MB zone */
|
|
||||||
p = alloc_page(GFP_ATOMIC | GFP_DMA |
|
|
||||||
__GFP_NORETRY | /* no OOM-killer */
|
|
||||||
__GFP_NOWARN);
|
|
||||||
}
|
}
|
||||||
if (!p) {
|
emu->page_addr_table[page] = dmab.addr;
|
||||||
__synth_free_pages(emu, first_page, page - 1);
|
emu->page_ptr_table[page] = dmab.area;
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
emu->page_addr_table[page] = page_to_phys(p);
|
|
||||||
emu->page_ptr_table[page] = page_address(p);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
__fail:
|
||||||
|
/* release allocated pages */
|
||||||
|
last_page = page - 1;
|
||||||
|
__synth_free_pages(emu, first_page, last_page);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
/*
|
/*
|
||||||
* Digital Beep Input Interface for HD-audio codec
|
* Digital Beep Input Interface for HD-audio codec
|
||||||
*
|
*
|
||||||
* Author: Matt Ranostay <mranostay@gmail.com>
|
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||||
* Copyright (c) 2008 Embedded Alley Solutions Inc
|
* Copyright (c) 2008 Embedded Alley Solutions Inc
|
||||||
*
|
|
||||||
* This driver is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This driver is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
/*
|
/*
|
||||||
* Digital Beep Input Interface for HD-audio codec
|
* Digital Beep Input Interface for HD-audio codec
|
||||||
*
|
*
|
||||||
* Author: Matt Ranostay <mranostay@gmail.com>
|
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||||
* Copyright (c) 2008 Embedded Alley Solutions Inc
|
* Copyright (c) 2008 Embedded Alley Solutions Inc
|
||||||
*
|
|
||||||
* This driver is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This driver is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SOUND_HDA_BEEP_H
|
#ifndef __SOUND_HDA_BEEP_H
|
||||||
|
|
|
@ -2702,32 +2702,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
|
EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
|
||||||
|
|
||||||
/*
|
|
||||||
* wait until the state is reached, returns the current state
|
|
||||||
*/
|
|
||||||
static unsigned int hda_sync_power_state(struct hda_codec *codec,
|
|
||||||
hda_nid_t fg,
|
|
||||||
unsigned int power_state)
|
|
||||||
{
|
|
||||||
unsigned long end_time = jiffies + msecs_to_jiffies(500);
|
|
||||||
unsigned int state, actual_state;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
state = snd_hda_codec_read(codec, fg, 0,
|
|
||||||
AC_VERB_GET_POWER_STATE, 0);
|
|
||||||
if (state & AC_PWRST_ERROR)
|
|
||||||
break;
|
|
||||||
actual_state = (state >> 4) & 0x0f;
|
|
||||||
if (actual_state == power_state)
|
|
||||||
break;
|
|
||||||
if (time_after_eq(jiffies, end_time))
|
|
||||||
break;
|
|
||||||
/* wait until the codec reachs to the target state */
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
|
* snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
|
||||||
* @codec: the HDA codec
|
* @codec: the HDA codec
|
||||||
|
@ -2790,7 +2764,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
|
||||||
state);
|
state);
|
||||||
snd_hda_codec_set_power_to_all(codec, fg, power_state);
|
snd_hda_codec_set_power_to_all(codec, fg, power_state);
|
||||||
}
|
}
|
||||||
state = hda_sync_power_state(codec, fg, power_state);
|
state = snd_hda_sync_power_state(codec, fg, power_state);
|
||||||
if (!(state & AC_PWRST_ERROR))
|
if (!(state & AC_PWRST_ERROR))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2434,6 +2434,9 @@ static const struct pci_device_id azx_ids[] = {
|
||||||
/* Cannonlake */
|
/* Cannonlake */
|
||||||
{ PCI_DEVICE(0x8086, 0x9dc8),
|
{ PCI_DEVICE(0x8086, 0x9dc8),
|
||||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||||
|
/* Icelake */
|
||||||
|
{ PCI_DEVICE(0x8086, 0x34c8),
|
||||||
|
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||||
/* Broxton-P(Apollolake) */
|
/* Broxton-P(Apollolake) */
|
||||||
{ PCI_DEVICE(0x8086, 0x5a98),
|
{ PCI_DEVICE(0x8086, 0x5a98),
|
||||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
|
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
|
||||||
|
|
|
@ -622,7 +622,11 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
|
||||||
{
|
{
|
||||||
return snd_hdac_check_power_state(&codec->core, nid, target_state);
|
return snd_hdac_check_power_state(&codec->core, nid, target_state);
|
||||||
}
|
}
|
||||||
|
static inline bool snd_hda_sync_power_state(struct hda_codec *codec,
|
||||||
|
hda_nid_t nid, unsigned int target_state)
|
||||||
|
{
|
||||||
|
return snd_hdac_sync_power_state(&codec->core, nid, target_state);
|
||||||
|
}
|
||||||
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
|
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
|
||||||
hda_nid_t nid,
|
hda_nid_t nid,
|
||||||
unsigned int power_state);
|
unsigned int power_state);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/tlv.h>
|
#include <sound/tlv.h>
|
||||||
|
|
||||||
|
@ -425,10 +426,9 @@ DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
|
||||||
static struct snd_kcontrol *ctl_find(struct snd_card *card,
|
static struct snd_kcontrol *ctl_find(struct snd_card *card,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct snd_ctl_elem_id sid;
|
struct snd_ctl_elem_id sid = {0};
|
||||||
memset(&sid, 0, sizeof(sid));
|
|
||||||
/* FIXME: strcpy is bad. */
|
strlcpy(sid.name, name, sizeof(sid.name));
|
||||||
strcpy(sid.name, name);
|
|
||||||
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||||
return snd_ctl_find_id(card, &sid);
|
return snd_ctl_find_id(card, &sid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/tlv.h>
|
#include <sound/tlv.h>
|
||||||
#include <sound/info.h>
|
#include <sound/info.h>
|
||||||
|
@ -785,10 +786,9 @@ DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
|
||||||
static struct snd_kcontrol *ctl_find(struct snd_card *card,
|
static struct snd_kcontrol *ctl_find(struct snd_card *card,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct snd_ctl_elem_id sid;
|
struct snd_ctl_elem_id sid = {0};
|
||||||
memset(&sid, 0, sizeof(sid));
|
|
||||||
/* FIXME: strcpy is bad. */
|
strlcpy(sid.name, name, sizeof(sid.name));
|
||||||
strcpy(sid.name, name);
|
|
||||||
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||||
return snd_ctl_find_id(card, &sid);
|
return snd_ctl_find_id(card, &sid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
* Alan Cox (alan@lxorguk.ukuu.org.uk)
|
* Alan Cox (alan@lxorguk.ukuu.org.uk)
|
||||||
* Thomas Sailer (sailer@ife.ee.ethz.ch)
|
* Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||||
*
|
*
|
||||||
|
* Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
|
@ -281,7 +283,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case UAC_VERSION_2: {
|
case UAC_VERSION_2:
|
||||||
|
case UAC_VERSION_3: {
|
||||||
struct usb_interface_assoc_descriptor *assoc =
|
struct usb_interface_assoc_descriptor *assoc =
|
||||||
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
||||||
|
|
||||||
|
@ -301,7 +304,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!assoc) {
|
if (!assoc) {
|
||||||
dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
|
dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct audioformat {
|
||||||
unsigned char endpoint; /* endpoint */
|
unsigned char endpoint; /* endpoint */
|
||||||
unsigned char ep_attr; /* endpoint attributes */
|
unsigned char ep_attr; /* endpoint attributes */
|
||||||
unsigned char datainterval; /* log_2 of data packet interval */
|
unsigned char datainterval; /* log_2 of data packet interval */
|
||||||
unsigned char protocol; /* UAC_VERSION_1/2 */
|
unsigned char protocol; /* UAC_VERSION_1/2/3 */
|
||||||
unsigned int maxpacksize; /* max. packet size */
|
unsigned int maxpacksize; /* max. packet size */
|
||||||
unsigned int rates; /* rate bitmasks */
|
unsigned int rates; /* rate bitmasks */
|
||||||
unsigned int rate_min, rate_max; /* min/max rates */
|
unsigned int rate_min, rate_max; /* min/max rates */
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
|
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/info.h>
|
#include <sound/info.h>
|
||||||
|
@ -50,6 +51,22 @@ static struct uac_clock_source_descriptor *
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct uac3_clock_source_descriptor *
|
||||||
|
snd_usb_find_clock_source_v3(struct usb_host_interface *ctrl_iface,
|
||||||
|
int clock_id)
|
||||||
|
{
|
||||||
|
struct uac3_clock_source_descriptor *cs = NULL;
|
||||||
|
|
||||||
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||||
|
ctrl_iface->extralen,
|
||||||
|
cs, UAC3_CLOCK_SOURCE))) {
|
||||||
|
if (cs->bClockID == clock_id)
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct uac_clock_selector_descriptor *
|
static struct uac_clock_selector_descriptor *
|
||||||
snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
|
snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
|
||||||
int clock_id)
|
int clock_id)
|
||||||
|
@ -69,6 +86,22 @@ static struct uac_clock_selector_descriptor *
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct uac3_clock_selector_descriptor *
|
||||||
|
snd_usb_find_clock_selector_v3(struct usb_host_interface *ctrl_iface,
|
||||||
|
int clock_id)
|
||||||
|
{
|
||||||
|
struct uac3_clock_selector_descriptor *cs = NULL;
|
||||||
|
|
||||||
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||||
|
ctrl_iface->extralen,
|
||||||
|
cs, UAC3_CLOCK_SELECTOR))) {
|
||||||
|
if (cs->bClockID == clock_id)
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct uac_clock_multiplier_descriptor *
|
static struct uac_clock_multiplier_descriptor *
|
||||||
snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
|
snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
|
||||||
int clock_id)
|
int clock_id)
|
||||||
|
@ -85,6 +118,22 @@ static struct uac_clock_multiplier_descriptor *
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct uac3_clock_multiplier_descriptor *
|
||||||
|
snd_usb_find_clock_multiplier_v3(struct usb_host_interface *ctrl_iface,
|
||||||
|
int clock_id)
|
||||||
|
{
|
||||||
|
struct uac3_clock_multiplier_descriptor *cs = NULL;
|
||||||
|
|
||||||
|
while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||||
|
ctrl_iface->extralen,
|
||||||
|
cs, UAC3_CLOCK_MULTIPLIER))) {
|
||||||
|
if (cs->bClockID == clock_id)
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
|
static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
|
||||||
{
|
{
|
||||||
unsigned char buf;
|
unsigned char buf;
|
||||||
|
@ -138,20 +187,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
|
||||||
|
int protocol,
|
||||||
|
int source_id)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
unsigned char data;
|
unsigned char data;
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
|
u32 bmControls;
|
||||||
|
|
||||||
|
if (protocol == UAC_VERSION_3) {
|
||||||
|
struct uac3_clock_source_descriptor *cs_desc =
|
||||||
|
snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
|
||||||
|
|
||||||
|
if (!cs_desc)
|
||||||
|
return 0;
|
||||||
|
bmControls = le32_to_cpu(cs_desc->bmControls);
|
||||||
|
} else { /* UAC_VERSION_1/2 */
|
||||||
struct uac_clock_source_descriptor *cs_desc =
|
struct uac_clock_source_descriptor *cs_desc =
|
||||||
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
|
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
|
||||||
|
|
||||||
if (!cs_desc)
|
if (!cs_desc)
|
||||||
return 0;
|
return 0;
|
||||||
|
bmControls = cs_desc->bmControls;
|
||||||
|
}
|
||||||
|
|
||||||
/* If a clock source can't tell us whether it's valid, we assume it is */
|
/* If a clock source can't tell us whether it's valid, we assume it is */
|
||||||
if (!uac2_control_is_readable(cs_desc->bmControls,
|
if (!uac_v2v3_control_is_readable(bmControls,
|
||||||
UAC2_CS_CONTROL_CLOCK_VALID - 1))
|
UAC2_CS_CONTROL_CLOCK_VALID))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||||
|
@ -170,9 +233,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
||||||
return !!data;
|
return !!data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
||||||
int entity_id, unsigned long *visited,
|
unsigned long *visited, bool validate)
|
||||||
bool validate)
|
|
||||||
{
|
{
|
||||||
struct uac_clock_source_descriptor *source;
|
struct uac_clock_source_descriptor *source;
|
||||||
struct uac_clock_selector_descriptor *selector;
|
struct uac_clock_selector_descriptor *selector;
|
||||||
|
@ -191,7 +253,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
||||||
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
|
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
|
||||||
if (source) {
|
if (source) {
|
||||||
entity_id = source->bClockID;
|
entity_id = source->bClockID;
|
||||||
if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
|
if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2,
|
||||||
|
entity_id)) {
|
||||||
usb_audio_err(chip,
|
usb_audio_err(chip,
|
||||||
"clock source %d is not valid, cannot use\n",
|
"clock source %d is not valid, cannot use\n",
|
||||||
entity_id);
|
entity_id);
|
||||||
|
@ -260,6 +323,97 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
||||||
|
unsigned long *visited, bool validate)
|
||||||
|
{
|
||||||
|
struct uac3_clock_source_descriptor *source;
|
||||||
|
struct uac3_clock_selector_descriptor *selector;
|
||||||
|
struct uac3_clock_multiplier_descriptor *multiplier;
|
||||||
|
|
||||||
|
entity_id &= 0xff;
|
||||||
|
|
||||||
|
if (test_and_set_bit(entity_id, visited)) {
|
||||||
|
usb_audio_warn(chip,
|
||||||
|
"%s(): recursive clock topology detected, id %d.\n",
|
||||||
|
__func__, entity_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* first, see if the ID we're looking for is a clock source already */
|
||||||
|
source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
|
||||||
|
if (source) {
|
||||||
|
entity_id = source->bClockID;
|
||||||
|
if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
|
||||||
|
entity_id)) {
|
||||||
|
usb_audio_err(chip,
|
||||||
|
"clock source %d is not valid, cannot use\n",
|
||||||
|
entity_id);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
return entity_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
selector = snd_usb_find_clock_selector_v3(chip->ctrl_intf, entity_id);
|
||||||
|
if (selector) {
|
||||||
|
int ret, i, cur;
|
||||||
|
|
||||||
|
/* the entity ID we are looking for is a selector.
|
||||||
|
* find out what it currently selects */
|
||||||
|
ret = uac_clock_selector_get_val(chip, selector->bClockID);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Selector values are one-based */
|
||||||
|
|
||||||
|
if (ret > selector->bNrInPins || ret < 1) {
|
||||||
|
usb_audio_err(chip,
|
||||||
|
"%s(): selector reported illegal value, id %d, ret %d\n",
|
||||||
|
__func__, selector->bClockID, ret);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = ret;
|
||||||
|
ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
|
||||||
|
visited, validate);
|
||||||
|
if (!validate || ret > 0 || !chip->autoclock)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* The current clock source is invalid, try others. */
|
||||||
|
for (i = 1; i <= selector->bNrInPins; i++) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (i == cur)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
|
||||||
|
visited, true);
|
||||||
|
if (ret < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = uac_clock_selector_set_val(chip, entity_id, i);
|
||||||
|
if (err < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
usb_audio_info(chip,
|
||||||
|
"found and selected valid clock source %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: multipliers only act as pass-thru element for now */
|
||||||
|
multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
|
||||||
|
entity_id);
|
||||||
|
if (multiplier)
|
||||||
|
return __uac3_clock_find_source(chip, multiplier->bCSourceID,
|
||||||
|
visited, validate);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For all kinds of sample rate settings and other device queries,
|
* For all kinds of sample rate settings and other device queries,
|
||||||
* the clock source (end-leaf) must be used. However, clock selectors,
|
* the clock source (end-leaf) must be used. However, clock selectors,
|
||||||
|
@ -271,12 +425,22 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
||||||
*
|
*
|
||||||
* Returns the clock source UnitID (>=0) on success, or an error.
|
* Returns the clock source UnitID (>=0) on success, or an error.
|
||||||
*/
|
*/
|
||||||
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
|
||||||
bool validate)
|
int entity_id, bool validate)
|
||||||
{
|
{
|
||||||
DECLARE_BITMAP(visited, 256);
|
DECLARE_BITMAP(visited, 256);
|
||||||
memset(visited, 0, sizeof(visited));
|
memset(visited, 0, sizeof(visited));
|
||||||
return __uac_clock_find_source(chip, entity_id, visited, validate);
|
|
||||||
|
switch (protocol) {
|
||||||
|
case UAC_VERSION_2:
|
||||||
|
return __uac_clock_find_source(chip, entity_id, visited,
|
||||||
|
validate);
|
||||||
|
case UAC_VERSION_3:
|
||||||
|
return __uac3_clock_find_source(chip, entity_id, visited,
|
||||||
|
validate);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
||||||
|
@ -335,7 +499,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
|
||||||
int altsetting, int clock)
|
int altsetting, int clock)
|
||||||
{
|
{
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
|
@ -348,7 +512,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
||||||
snd_usb_ctrl_intf(chip) | (clock << 8),
|
snd_usb_ctrl_intf(chip) | (clock << 8),
|
||||||
&data, sizeof(data));
|
&data, sizeof(data));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
|
dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
|
||||||
iface, altsetting, err);
|
iface, altsetting, err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -356,7 +520,7 @@ static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
||||||
return le32_to_cpu(data);
|
return le32_to_cpu(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt, int rate)
|
struct audioformat *fmt, int rate)
|
||||||
{
|
{
|
||||||
|
@ -365,18 +529,31 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
||||||
int err, cur_rate, prev_rate;
|
int err, cur_rate, prev_rate;
|
||||||
int clock;
|
int clock;
|
||||||
bool writeable;
|
bool writeable;
|
||||||
struct uac_clock_source_descriptor *cs_desc;
|
u32 bmControls;
|
||||||
|
|
||||||
clock = snd_usb_clock_find_source(chip, fmt->clock, true);
|
clock = snd_usb_clock_find_source(chip, fmt->protocol,
|
||||||
|
fmt->clock, true);
|
||||||
if (clock < 0)
|
if (clock < 0)
|
||||||
return clock;
|
return clock;
|
||||||
|
|
||||||
prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
|
prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock);
|
||||||
if (prev_rate == rate)
|
if (prev_rate == rate)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (fmt->protocol == UAC_VERSION_3) {
|
||||||
|
struct uac3_clock_source_descriptor *cs_desc;
|
||||||
|
|
||||||
|
cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock);
|
||||||
|
bmControls = le32_to_cpu(cs_desc->bmControls);
|
||||||
|
} else {
|
||||||
|
struct uac_clock_source_descriptor *cs_desc;
|
||||||
|
|
||||||
cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
|
cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
|
||||||
writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
|
bmControls = cs_desc->bmControls;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeable = uac_v2v3_control_is_writeable(bmControls,
|
||||||
|
UAC2_CS_CONTROL_SAM_FREQ);
|
||||||
if (writeable) {
|
if (writeable) {
|
||||||
data = cpu_to_le32(rate);
|
data = cpu_to_le32(rate);
|
||||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||||
|
@ -386,12 +563,13 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
||||||
&data, sizeof(data));
|
&data, sizeof(data));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
usb_audio_err(chip,
|
usb_audio_err(chip,
|
||||||
"%d:%d: cannot set freq %d (v2): err %d\n",
|
"%d:%d: cannot set freq %d (v2/v3): err %d\n",
|
||||||
iface, fmt->altsetting, rate, err);
|
iface, fmt->altsetting, rate, err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
|
cur_rate = get_sample_rate_v2v3(chip, iface,
|
||||||
|
fmt->altsetting, clock);
|
||||||
} else {
|
} else {
|
||||||
cur_rate = prev_rate;
|
cur_rate = prev_rate;
|
||||||
}
|
}
|
||||||
|
@ -430,7 +608,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||||
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
||||||
|
|
||||||
case UAC_VERSION_2:
|
case UAC_VERSION_2:
|
||||||
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
|
case UAC_VERSION_3:
|
||||||
|
return set_sample_rate_v2v3(chip, iface, alts, fmt, rate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt, int rate);
|
struct audioformat *fmt, int rate);
|
||||||
|
|
||||||
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
|
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
|
||||||
bool validate);
|
int entity_id, bool validate);
|
||||||
|
|
||||||
#endif /* __USBAUDIO_CLOCK_H */
|
#endif /* __USBAUDIO_CLOCK_H */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
|
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
|
@ -39,11 +40,11 @@
|
||||||
* @dev: usb device
|
* @dev: usb device
|
||||||
* @fp: audioformat record
|
* @fp: audioformat record
|
||||||
* @format: the format tag (wFormatTag)
|
* @format: the format tag (wFormatTag)
|
||||||
* @fmt: the format type descriptor
|
* @fmt: the format type descriptor (v1/v2) or AudioStreaming descriptor (v3)
|
||||||
*/
|
*/
|
||||||
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp,
|
struct audioformat *fp,
|
||||||
unsigned int format, void *_fmt)
|
u64 format, void *_fmt)
|
||||||
{
|
{
|
||||||
int sample_width, sample_bytes;
|
int sample_width, sample_bytes;
|
||||||
u64 pcm_formats = 0;
|
u64 pcm_formats = 0;
|
||||||
|
@ -54,7 +55,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
||||||
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
|
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
|
||||||
sample_width = fmt->bBitResolution;
|
sample_width = fmt->bBitResolution;
|
||||||
sample_bytes = fmt->bSubframeSize;
|
sample_bytes = fmt->bSubframeSize;
|
||||||
format = 1 << format;
|
format = 1ULL << format;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
||||||
format <<= 1;
|
format <<= 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case UAC_VERSION_3: {
|
||||||
|
struct uac3_as_header_descriptor *as = _fmt;
|
||||||
|
|
||||||
|
sample_width = as->bBitResolution;
|
||||||
|
sample_bytes = as->bSubslotSize;
|
||||||
|
|
||||||
|
if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
|
||||||
|
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
|
||||||
|
|
||||||
|
format <<= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pcm_formats == 0) &&
|
if ((pcm_formats == 0) &&
|
||||||
|
@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
||||||
}
|
}
|
||||||
if (format & ~0x3f) {
|
if (format & ~0x3f) {
|
||||||
usb_audio_info(chip,
|
usb_audio_info(chip,
|
||||||
"%u:%d : unsupported format bits %#x\n",
|
"%u:%d : unsupported format bits %#llx\n",
|
||||||
fp->iface, fp->altsetting, format);
|
fp->iface, fp->altsetting, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse the format descriptor and stores the possible sample rates
|
* parse the format descriptor and stores the possible sample rates
|
||||||
* on the audioformat table (audio class v2).
|
* on the audioformat table (audio class v2 and v3).
|
||||||
*/
|
*/
|
||||||
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp)
|
struct audioformat *fp)
|
||||||
{
|
{
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
unsigned char tmp[2], *data;
|
unsigned char tmp[2], *data;
|
||||||
int nr_triplets, data_size, ret = 0;
|
int nr_triplets, data_size, ret = 0;
|
||||||
int clock = snd_usb_clock_find_source(chip, fp->clock, false);
|
int clock = snd_usb_clock_find_source(chip, fp->protocol,
|
||||||
|
fp->clock, false);
|
||||||
|
|
||||||
if (clock < 0) {
|
if (clock < 0) {
|
||||||
dev_err(&dev->dev,
|
dev_err(&dev->dev,
|
||||||
|
@ -368,13 +382,30 @@ err:
|
||||||
* parse the format type I and III descriptors
|
* parse the format type I and III descriptors
|
||||||
*/
|
*/
|
||||||
static int parse_audio_format_i(struct snd_usb_audio *chip,
|
static int parse_audio_format_i(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp, unsigned int format,
|
struct audioformat *fp, u64 format,
|
||||||
struct uac_format_type_i_continuous_descriptor *fmt)
|
void *_fmt)
|
||||||
{
|
{
|
||||||
snd_pcm_format_t pcm_format;
|
snd_pcm_format_t pcm_format;
|
||||||
|
unsigned int fmt_type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
|
switch (fp->protocol) {
|
||||||
|
default:
|
||||||
|
case UAC_VERSION_1:
|
||||||
|
case UAC_VERSION_2: {
|
||||||
|
struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
|
||||||
|
|
||||||
|
fmt_type = fmt->bFormatType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UAC_VERSION_3: {
|
||||||
|
/* fp->fmt_type is already set in this case */
|
||||||
|
fmt_type = fp->fmt_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt_type == UAC_FORMAT_TYPE_III) {
|
||||||
/* FIXME: the format type is really IECxxx
|
/* FIXME: the format type is really IECxxx
|
||||||
* but we give normal PCM format to get the existing
|
* but we give normal PCM format to get the existing
|
||||||
* apps working...
|
* apps working...
|
||||||
|
@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
|
||||||
}
|
}
|
||||||
fp->formats = pcm_format_to_bits(pcm_format);
|
fp->formats = pcm_format_to_bits(pcm_format);
|
||||||
} else {
|
} else {
|
||||||
fp->formats = parse_audio_format_i_type(chip, fp, format, fmt);
|
fp->formats = parse_audio_format_i_type(chip, fp, format, _fmt);
|
||||||
if (!fp->formats)
|
if (!fp->formats)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
|
||||||
*/
|
*/
|
||||||
switch (fp->protocol) {
|
switch (fp->protocol) {
|
||||||
default:
|
default:
|
||||||
case UAC_VERSION_1:
|
case UAC_VERSION_1: {
|
||||||
|
struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
|
||||||
|
|
||||||
fp->channels = fmt->bNrChannels;
|
fp->channels = fmt->bNrChannels;
|
||||||
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
|
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case UAC_VERSION_2:
|
case UAC_VERSION_2:
|
||||||
|
case UAC_VERSION_3: {
|
||||||
/* fp->channels is already set in this case */
|
/* fp->channels is already set in this case */
|
||||||
ret = parse_audio_format_rates_v2(chip, fp);
|
ret = parse_audio_format_rates_v2v3(chip, fp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fp->channels < 1) {
|
if (fp->channels < 1) {
|
||||||
usb_audio_err(chip,
|
usb_audio_err(chip,
|
||||||
|
@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
|
||||||
*/
|
*/
|
||||||
static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp,
|
struct audioformat *fp,
|
||||||
int format, void *_fmt)
|
u64 format, void *_fmt)
|
||||||
{
|
{
|
||||||
int brate, framesize, ret;
|
int brate, framesize, ret;
|
||||||
|
|
||||||
|
@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usb_audio_info(chip,
|
usb_audio_info(chip,
|
||||||
"%u:%d : unknown format tag %#x is detected. processed as MPEG.\n",
|
"%u:%d : unknown format tag %#llx is detected. processed as MPEG.\n",
|
||||||
fp->iface, fp->altsetting, format);
|
fp->iface, fp->altsetting, format);
|
||||||
fp->formats = SNDRV_PCM_FMTBIT_MPEG;
|
fp->formats = SNDRV_PCM_FMTBIT_MPEG;
|
||||||
break;
|
break;
|
||||||
|
@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
||||||
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
|
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
|
||||||
usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
|
usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
|
||||||
fp->frame_size = framesize;
|
fp->frame_size = framesize;
|
||||||
ret = parse_audio_format_rates_v2(chip, fp);
|
ret = parse_audio_format_rates_v2v3(chip, fp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
|
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp, unsigned int format,
|
struct audioformat *fp, u64 format,
|
||||||
struct uac_format_type_i_continuous_descriptor *fmt,
|
struct uac_format_type_i_continuous_descriptor *fmt,
|
||||||
int stream)
|
int stream)
|
||||||
{
|
{
|
||||||
|
@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
|
||||||
|
struct audioformat *fp,
|
||||||
|
struct uac3_as_header_descriptor *as,
|
||||||
|
int stream)
|
||||||
|
{
|
||||||
|
u64 format = le64_to_cpu(as->bmFormats);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type I format bits are D0..D6
|
||||||
|
* This test works because type IV is not supported
|
||||||
|
*/
|
||||||
|
if (format & 0x7f)
|
||||||
|
fp->fmt_type = UAC_FORMAT_TYPE_I;
|
||||||
|
else
|
||||||
|
fp->fmt_type = UAC_FORMAT_TYPE_III;
|
||||||
|
|
||||||
|
err = parse_audio_format_i(chip, fp, format, as);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
#define __USBAUDIO_FORMAT_H
|
#define __USBAUDIO_FORMAT_H
|
||||||
|
|
||||||
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
|
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp, unsigned int format,
|
struct audioformat *fp, u64 format,
|
||||||
struct uac_format_type_i_continuous_descriptor *fmt,
|
struct uac_format_type_i_continuous_descriptor *fmt,
|
||||||
int stream);
|
int stream);
|
||||||
|
|
||||||
|
int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
|
||||||
|
struct audioformat *fp,
|
||||||
|
struct uac3_as_header_descriptor *as,
|
||||||
|
int stream);
|
||||||
#endif /* __USBAUDIO_FORMAT_H */
|
#endif /* __USBAUDIO_FORMAT_H */
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
|
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
|
@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state,
|
||||||
USB_DT_CS_INTERFACE)) != NULL) {
|
USB_DT_CS_INTERFACE)) != NULL) {
|
||||||
if (hdr->bLength >= 4 &&
|
if (hdr->bLength >= 4 &&
|
||||||
hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
|
hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
|
||||||
hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
|
hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER &&
|
||||||
hdr->bUnitID == unit)
|
hdr->bUnitID == unit)
|
||||||
return hdr;
|
return hdr;
|
||||||
}
|
}
|
||||||
|
@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
|
||||||
|
|
||||||
validx += cval->idx_off;
|
validx += cval->idx_off;
|
||||||
|
|
||||||
|
|
||||||
if (cval->head.mixer->protocol == UAC_VERSION_1) {
|
if (cval->head.mixer->protocol == UAC_VERSION_1) {
|
||||||
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
|
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
|
||||||
} else { /* UAC_VERSION_2 */
|
} else { /* UAC_VERSION_2/3 */
|
||||||
val_len = uac2_ctl_value_size(cval->val_type);
|
val_len = uac2_ctl_value_size(cval->val_type);
|
||||||
|
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
|
@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
|
||||||
static int check_input_term(struct mixer_build *state, int id,
|
static int check_input_term(struct mixer_build *state, int id,
|
||||||
struct usb_audio_term *term)
|
struct usb_audio_term *term)
|
||||||
{
|
{
|
||||||
|
int protocol = state->mixer->protocol;
|
||||||
int err;
|
int err;
|
||||||
void *p1;
|
void *p1;
|
||||||
|
|
||||||
|
@ -730,10 +733,13 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
|
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
|
||||||
unsigned char *hdr = p1;
|
unsigned char *hdr = p1;
|
||||||
term->id = id;
|
term->id = id;
|
||||||
|
|
||||||
|
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||||
switch (hdr[2]) {
|
switch (hdr[2]) {
|
||||||
case UAC_INPUT_TERMINAL:
|
case UAC_INPUT_TERMINAL:
|
||||||
if (state->mixer->protocol == UAC_VERSION_1) {
|
if (protocol == UAC_VERSION_1) {
|
||||||
struct uac_input_terminal_descriptor *d = p1;
|
struct uac_input_terminal_descriptor *d = p1;
|
||||||
|
|
||||||
term->type = le16_to_cpu(d->wTerminalType);
|
term->type = le16_to_cpu(d->wTerminalType);
|
||||||
term->channels = d->bNrChannels;
|
term->channels = d->bNrChannels;
|
||||||
term->chconfig = le16_to_cpu(d->wChannelConfig);
|
term->chconfig = le16_to_cpu(d->wChannelConfig);
|
||||||
|
@ -760,14 +766,16 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
case UAC_FEATURE_UNIT: {
|
case UAC_FEATURE_UNIT: {
|
||||||
/* the header is the same for v1 and v2 */
|
/* the header is the same for v1 and v2 */
|
||||||
struct uac_feature_unit_descriptor *d = p1;
|
struct uac_feature_unit_descriptor *d = p1;
|
||||||
|
|
||||||
id = d->bSourceID;
|
id = d->bSourceID;
|
||||||
break; /* continue to parse */
|
break; /* continue to parse */
|
||||||
}
|
}
|
||||||
case UAC_MIXER_UNIT: {
|
case UAC_MIXER_UNIT: {
|
||||||
struct uac_mixer_unit_descriptor *d = p1;
|
struct uac_mixer_unit_descriptor *d = p1;
|
||||||
|
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
term->channels = uac_mixer_unit_bNrChannels(d);
|
term->channels = uac_mixer_unit_bNrChannels(d);
|
||||||
term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
|
term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
|
||||||
term->name = uac_mixer_unit_iMixer(d);
|
term->name = uac_mixer_unit_iMixer(d);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -790,7 +798,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
case UAC2_EXTENSION_UNIT_V2: {
|
case UAC2_EXTENSION_UNIT_V2: {
|
||||||
struct uac_processing_unit_descriptor *d = p1;
|
struct uac_processing_unit_descriptor *d = p1;
|
||||||
|
|
||||||
if (state->mixer->protocol == UAC_VERSION_2 &&
|
if (protocol == UAC_VERSION_2 &&
|
||||||
hdr[2] == UAC2_EFFECT_UNIT) {
|
hdr[2] == UAC2_EFFECT_UNIT) {
|
||||||
/* UAC2/UAC1 unit IDs overlap here in an
|
/* UAC2/UAC1 unit IDs overlap here in an
|
||||||
* uncompatible way. Ignore this unit for now.
|
* uncompatible way. Ignore this unit for now.
|
||||||
|
@ -804,12 +812,13 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
}
|
}
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
term->channels = uac_processing_unit_bNrChannels(d);
|
term->channels = uac_processing_unit_bNrChannels(d);
|
||||||
term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
|
term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
|
||||||
term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
|
term->name = uac_processing_unit_iProcessing(d, protocol);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case UAC2_CLOCK_SOURCE: {
|
case UAC2_CLOCK_SOURCE: {
|
||||||
struct uac_clock_source_descriptor *d = p1;
|
struct uac_clock_source_descriptor *d = p1;
|
||||||
|
|
||||||
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
term->id = id;
|
term->id = id;
|
||||||
term->name = d->iClockSource;
|
term->name = d->iClockSource;
|
||||||
|
@ -818,6 +827,48 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
default:
|
default:
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
} else { /* UAC_VERSION_3 */
|
||||||
|
switch (hdr[2]) {
|
||||||
|
case UAC_INPUT_TERMINAL: {
|
||||||
|
struct uac3_input_terminal_descriptor *d = p1;
|
||||||
|
|
||||||
|
/* call recursively to verify that the
|
||||||
|
* referenced clock entity is valid */
|
||||||
|
err = check_input_term(state, d->bCSourceID, term);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* save input term properties after recursion,
|
||||||
|
* to ensure they are not overriden by the
|
||||||
|
* recursion calls */
|
||||||
|
term->id = id;
|
||||||
|
term->type = le16_to_cpu(d->wTerminalType);
|
||||||
|
|
||||||
|
/* REVISIT: UAC3 IT doesn't have channels/cfg */
|
||||||
|
term->channels = 0;
|
||||||
|
term->chconfig = 0;
|
||||||
|
|
||||||
|
term->name = le16_to_cpu(d->wTerminalDescrStr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case UAC3_FEATURE_UNIT: {
|
||||||
|
struct uac3_feature_unit_descriptor *d = p1;
|
||||||
|
|
||||||
|
id = d->bSourceID;
|
||||||
|
break; /* continue to parse */
|
||||||
|
}
|
||||||
|
case UAC3_CLOCK_SOURCE: {
|
||||||
|
struct uac3_clock_source_descriptor *d = p1;
|
||||||
|
|
||||||
|
term->type = d->bDescriptorSubtype << 16; /* virtual type */
|
||||||
|
term->id = id;
|
||||||
|
term->name = le16_to_cpu(d->wClockSourceStr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -828,26 +879,27 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||||
|
|
||||||
/* feature unit control information */
|
/* feature unit control information */
|
||||||
struct usb_feature_control_info {
|
struct usb_feature_control_info {
|
||||||
|
int control;
|
||||||
const char *name;
|
const char *name;
|
||||||
int type; /* data type for uac1 */
|
int type; /* data type for uac1 */
|
||||||
int type_uac2; /* data type for uac2 if different from uac1, else -1 */
|
int type_uac2; /* data type for uac2 if different from uac1, else -1 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_feature_control_info audio_feature_info[] = {
|
static struct usb_feature_control_info audio_feature_info[] = {
|
||||||
{ "Mute", USB_MIXER_INV_BOOLEAN, -1 },
|
{ UAC_FU_MUTE, "Mute", USB_MIXER_INV_BOOLEAN, -1 },
|
||||||
{ "Volume", USB_MIXER_S16, -1 },
|
{ UAC_FU_VOLUME, "Volume", USB_MIXER_S16, -1 },
|
||||||
{ "Tone Control - Bass", USB_MIXER_S8, -1 },
|
{ UAC_FU_BASS, "Tone Control - Bass", USB_MIXER_S8, -1 },
|
||||||
{ "Tone Control - Mid", USB_MIXER_S8, -1 },
|
{ UAC_FU_MID, "Tone Control - Mid", USB_MIXER_S8, -1 },
|
||||||
{ "Tone Control - Treble", USB_MIXER_S8, -1 },
|
{ UAC_FU_TREBLE, "Tone Control - Treble", USB_MIXER_S8, -1 },
|
||||||
{ "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemeted yet */
|
{ UAC_FU_GRAPHIC_EQUALIZER, "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemented yet */
|
||||||
{ "Auto Gain Control", USB_MIXER_BOOLEAN, -1 },
|
{ UAC_FU_AUTOMATIC_GAIN, "Auto Gain Control", USB_MIXER_BOOLEAN, -1 },
|
||||||
{ "Delay Control", USB_MIXER_U16, USB_MIXER_U32 },
|
{ UAC_FU_DELAY, "Delay Control", USB_MIXER_U16, USB_MIXER_U32 },
|
||||||
{ "Bass Boost", USB_MIXER_BOOLEAN, -1 },
|
{ UAC_FU_BASS_BOOST, "Bass Boost", USB_MIXER_BOOLEAN, -1 },
|
||||||
{ "Loudness", USB_MIXER_BOOLEAN, -1 },
|
{ UAC_FU_LOUDNESS, "Loudness", USB_MIXER_BOOLEAN, -1 },
|
||||||
/* UAC2 specific */
|
/* UAC2 specific */
|
||||||
{ "Input Gain Control", USB_MIXER_S16, -1 },
|
{ UAC2_FU_INPUT_GAIN, "Input Gain Control", USB_MIXER_S16, -1 },
|
||||||
{ "Input Gain Pad Control", USB_MIXER_S16, -1 },
|
{ UAC2_FU_INPUT_GAIN_PAD, "Input Gain Pad Control", USB_MIXER_S16, -1 },
|
||||||
{ "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
|
{ UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* private_free callback */
|
/* private_free callback */
|
||||||
|
@ -1183,6 +1235,21 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get the boolean value from the master channel of a UAC control */
|
||||||
|
static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
||||||
|
int val, err;
|
||||||
|
|
||||||
|
err = snd_usb_get_cur_mix_value(cval, 0, 0, &val);
|
||||||
|
if (err < 0)
|
||||||
|
return filter_error(cval, err);
|
||||||
|
val = (val != 0);
|
||||||
|
ucontrol->value.integer.value[0] = val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct snd_kcontrol_new usb_feature_unit_ctl = {
|
static struct snd_kcontrol_new usb_feature_unit_ctl = {
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
.name = "", /* will be filled later manually */
|
.name = "", /* will be filled later manually */
|
||||||
|
@ -1200,6 +1267,19 @@ static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
|
||||||
.put = NULL,
|
.put = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A control which shows the boolean value from reading a UAC control on
|
||||||
|
* the master channel.
|
||||||
|
*/
|
||||||
|
static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
|
||||||
|
.name = "", /* will be filled later manually */
|
||||||
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||||
|
.info = snd_ctl_boolean_mono_info,
|
||||||
|
.get = mixer_ctl_master_bool_get,
|
||||||
|
.put = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This symbol is exported in order to allow the mixer quirks to
|
* This symbol is exported in order to allow the mixer quirks to
|
||||||
* hook up to the standard feature unit control mechanism
|
* hook up to the standard feature unit control mechanism
|
||||||
|
@ -1242,6 +1322,17 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
|
||||||
strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name));
|
strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct usb_feature_control_info *get_feature_control_info(int control)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(audio_feature_info); ++i) {
|
||||||
|
if (audio_feature_info[i].control == control)
|
||||||
|
return &audio_feature_info[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
unsigned int ctl_mask, int control,
|
unsigned int ctl_mask, int control,
|
||||||
struct usb_audio_term *iterm, int unitid,
|
struct usb_audio_term *iterm, int unitid,
|
||||||
|
@ -1257,8 +1348,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
const struct usbmix_name_map *map;
|
const struct usbmix_name_map *map;
|
||||||
unsigned int range;
|
unsigned int range;
|
||||||
|
|
||||||
control++; /* change from zero-based to 1-based value */
|
|
||||||
|
|
||||||
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
||||||
/* FIXME: not supported yet */
|
/* FIXME: not supported yet */
|
||||||
return;
|
return;
|
||||||
|
@ -1274,7 +1363,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
|
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
|
||||||
cval->control = control;
|
cval->control = control;
|
||||||
cval->cmask = ctl_mask;
|
cval->cmask = ctl_mask;
|
||||||
ctl_info = &audio_feature_info[control-1];
|
|
||||||
|
ctl_info = get_feature_control_info(control);
|
||||||
|
if (!ctl_info) {
|
||||||
|
kfree(cval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (state->mixer->protocol == UAC_VERSION_1)
|
||||||
cval->val_type = ctl_info->type;
|
cval->val_type = ctl_info->type;
|
||||||
else /* UAC_VERSION_2 */
|
else /* UAC_VERSION_2 */
|
||||||
|
@ -1400,6 +1494,60 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
snd_usb_mixer_add_control(&cval->head, kctl);
|
snd_usb_mixer_add_control(&cval->head, kctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_connector_control_name(struct mixer_build *state,
|
||||||
|
struct usb_audio_term *term,
|
||||||
|
bool is_input, char *name, int name_size)
|
||||||
|
{
|
||||||
|
int name_len = get_term_name(state, term, name, name_size, 0);
|
||||||
|
|
||||||
|
if (name_len == 0)
|
||||||
|
strlcpy(name, "Unknown", name_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sound/core/ctljack.c has a convention of naming jack controls
|
||||||
|
* by ending in " Jack". Make it slightly more useful by
|
||||||
|
* indicating Input or Output after the terminal name.
|
||||||
|
*/
|
||||||
|
if (is_input)
|
||||||
|
strlcat(name, " - Input Jack", name_size);
|
||||||
|
else
|
||||||
|
strlcat(name, " - Output Jack", name_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build a mixer control for a UAC connector control (jack-detect) */
|
||||||
|
static void build_connector_control(struct mixer_build *state,
|
||||||
|
struct usb_audio_term *term, bool is_input)
|
||||||
|
{
|
||||||
|
struct snd_kcontrol *kctl;
|
||||||
|
struct usb_mixer_elem_info *cval;
|
||||||
|
|
||||||
|
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
|
||||||
|
if (!cval)
|
||||||
|
return;
|
||||||
|
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id);
|
||||||
|
/*
|
||||||
|
* The first byte from reading the UAC2_TE_CONNECTOR control returns the
|
||||||
|
* number of channels connected. This boolean ctl will simply report
|
||||||
|
* if any channels are connected or not.
|
||||||
|
* (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block)
|
||||||
|
*/
|
||||||
|
cval->control = UAC2_TE_CONNECTOR;
|
||||||
|
cval->val_type = USB_MIXER_BOOLEAN;
|
||||||
|
cval->channels = 1; /* report true if any channel is connected */
|
||||||
|
cval->min = 0;
|
||||||
|
cval->max = 1;
|
||||||
|
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
|
||||||
|
if (!kctl) {
|
||||||
|
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
||||||
|
kfree(cval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_connector_control_name(state, term, is_input, kctl->id.name,
|
||||||
|
sizeof(kctl->id.name));
|
||||||
|
kctl->private_free = snd_usb_mixer_elem_free;
|
||||||
|
snd_usb_mixer_add_control(&cval->head, kctl);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
||||||
void *_ftr)
|
void *_ftr)
|
||||||
{
|
{
|
||||||
|
@ -1423,8 +1571,8 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
||||||
* The only property of this unit we are interested in is the
|
* The only property of this unit we are interested in is the
|
||||||
* clock source validity. If that isn't readable, just bail out.
|
* clock source validity. If that isn't readable, just bail out.
|
||||||
*/
|
*/
|
||||||
if (!uac2_control_is_readable(hdr->bmControls,
|
if (!uac_v2v3_control_is_readable(hdr->bmControls,
|
||||||
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
|
UAC2_CS_CONTROL_CLOCK_VALID))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
|
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
|
||||||
|
@ -1439,13 +1587,9 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
||||||
cval->val_type = USB_MIXER_BOOLEAN;
|
cval->val_type = USB_MIXER_BOOLEAN;
|
||||||
cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
|
cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
|
||||||
|
|
||||||
if (uac2_control_is_writeable(hdr->bmControls,
|
|
||||||
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
|
|
||||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
|
||||||
else {
|
|
||||||
cval->master_readonly = 1;
|
cval->master_readonly = 1;
|
||||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
|
/* From UAC2 5.2.5.1.2 "Only the get request is supported." */
|
||||||
}
|
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
|
||||||
|
|
||||||
if (!kctl) {
|
if (!kctl) {
|
||||||
kfree(cval);
|
kfree(cval);
|
||||||
|
@ -1502,7 +1646,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
unitid);
|
unitid);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (state->mixer->protocol == UAC_VERSION_2) {
|
||||||
struct uac2_feature_unit_descriptor *ftr = _ftr;
|
struct uac2_feature_unit_descriptor *ftr = _ftr;
|
||||||
if (hdr->bLength < 6) {
|
if (hdr->bLength < 6) {
|
||||||
usb_audio_err(state->chip,
|
usb_audio_err(state->chip,
|
||||||
|
@ -1519,6 +1663,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
unitid);
|
unitid);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
} else { /* UAC_VERSION_3 */
|
||||||
|
struct uac3_feature_unit_descriptor *ftr = _ftr;
|
||||||
|
|
||||||
|
if (hdr->bLength < 7) {
|
||||||
|
usb_audio_err(state->chip,
|
||||||
|
"unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
|
||||||
|
unitid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
csize = 4;
|
||||||
|
channels = (ftr->bLength - 7) / 4 - 1;
|
||||||
|
bmaControls = ftr->bmaControls;
|
||||||
|
if (hdr->bLength < 7 + csize) {
|
||||||
|
usb_audio_err(state->chip,
|
||||||
|
"unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
|
||||||
|
unitid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parse the source unit */
|
/* parse the source unit */
|
||||||
|
@ -1556,6 +1718,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
/* check all control types */
|
/* check all control types */
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
unsigned int ch_bits = 0;
|
unsigned int ch_bits = 0;
|
||||||
|
int control = audio_feature_info[i].control;
|
||||||
|
|
||||||
for (j = 0; j < channels; j++) {
|
for (j = 0; j < channels; j++) {
|
||||||
unsigned int mask;
|
unsigned int mask;
|
||||||
|
|
||||||
|
@ -1571,25 +1735,26 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
* (for ease of programming).
|
* (for ease of programming).
|
||||||
*/
|
*/
|
||||||
if (ch_bits & 1)
|
if (ch_bits & 1)
|
||||||
build_feature_ctl(state, _ftr, ch_bits, i,
|
build_feature_ctl(state, _ftr, ch_bits, control,
|
||||||
&iterm, unitid, 0);
|
&iterm, unitid, 0);
|
||||||
if (master_bits & (1 << i))
|
if (master_bits & (1 << i))
|
||||||
build_feature_ctl(state, _ftr, 0, i, &iterm,
|
build_feature_ctl(state, _ftr, 0, control,
|
||||||
unitid, 0);
|
&iterm, unitid, 0);
|
||||||
}
|
}
|
||||||
} else { /* UAC_VERSION_2 */
|
} else { /* UAC_VERSION_2/3 */
|
||||||
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
||||||
unsigned int ch_bits = 0;
|
unsigned int ch_bits = 0;
|
||||||
unsigned int ch_read_only = 0;
|
unsigned int ch_read_only = 0;
|
||||||
|
int control = audio_feature_info[i].control;
|
||||||
|
|
||||||
for (j = 0; j < channels; j++) {
|
for (j = 0; j < channels; j++) {
|
||||||
unsigned int mask;
|
unsigned int mask;
|
||||||
|
|
||||||
mask = snd_usb_combine_bytes(bmaControls +
|
mask = snd_usb_combine_bytes(bmaControls +
|
||||||
csize * (j+1), csize);
|
csize * (j+1), csize);
|
||||||
if (uac2_control_is_readable(mask, i)) {
|
if (uac_v2v3_control_is_readable(mask, control)) {
|
||||||
ch_bits |= (1 << j);
|
ch_bits |= (1 << j);
|
||||||
if (!uac2_control_is_writeable(mask, i))
|
if (!uac_v2v3_control_is_writeable(mask, control))
|
||||||
ch_read_only |= (1 << j);
|
ch_read_only |= (1 << j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1608,11 +1773,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||||
* (for ease of programming).
|
* (for ease of programming).
|
||||||
*/
|
*/
|
||||||
if (ch_bits & 1)
|
if (ch_bits & 1)
|
||||||
build_feature_ctl(state, _ftr, ch_bits, i,
|
build_feature_ctl(state, _ftr, ch_bits, control,
|
||||||
&iterm, unitid, ch_read_only);
|
&iterm, unitid, ch_read_only);
|
||||||
if (uac2_control_is_readable(master_bits, i))
|
if (uac_v2v3_control_is_readable(master_bits, control))
|
||||||
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
|
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
|
||||||
!uac2_control_is_writeable(master_bits, i));
|
!uac_v2v3_control_is_writeable(master_bits,
|
||||||
|
control));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1684,6 +1850,23 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
|
||||||
snd_usb_mixer_add_control(&cval->head, kctl);
|
snd_usb_mixer_add_control(&cval->head, kctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
|
||||||
|
void *raw_desc)
|
||||||
|
{
|
||||||
|
struct usb_audio_term iterm;
|
||||||
|
struct uac2_input_terminal_descriptor *d = raw_desc;
|
||||||
|
|
||||||
|
check_input_term(state, d->bTerminalID, &iterm);
|
||||||
|
if (state->mixer->protocol == UAC_VERSION_2) {
|
||||||
|
/* Check for jack detection. */
|
||||||
|
if (uac_v2v3_control_is_readable(d->bmControls,
|
||||||
|
UAC2_TE_CONNECTOR)) {
|
||||||
|
build_connector_control(state, &iterm, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse a mixer unit
|
* parse a mixer unit
|
||||||
*/
|
*/
|
||||||
|
@ -2220,6 +2403,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||||
static int parse_audio_unit(struct mixer_build *state, int unitid)
|
static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||||
{
|
{
|
||||||
unsigned char *p1;
|
unsigned char *p1;
|
||||||
|
int protocol = state->mixer->protocol;
|
||||||
|
|
||||||
if (test_and_set_bit(unitid, state->unitbitmap))
|
if (test_and_set_bit(unitid, state->unitbitmap))
|
||||||
return 0; /* the unit already visited */
|
return 0; /* the unit already visited */
|
||||||
|
@ -2230,9 +2414,10 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||||
switch (p1[2]) {
|
switch (p1[2]) {
|
||||||
case UAC_INPUT_TERMINAL:
|
case UAC_INPUT_TERMINAL:
|
||||||
return 0; /* NOP */
|
return parse_audio_input_terminal(state, unitid, p1);
|
||||||
case UAC_MIXER_UNIT:
|
case UAC_MIXER_UNIT:
|
||||||
return parse_audio_mixer_unit(state, unitid, p1);
|
return parse_audio_mixer_unit(state, unitid, p1);
|
||||||
case UAC2_CLOCK_SOURCE:
|
case UAC2_CLOCK_SOURCE:
|
||||||
|
@ -2244,13 +2429,13 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||||
return parse_audio_feature_unit(state, unitid, p1);
|
return parse_audio_feature_unit(state, unitid, p1);
|
||||||
case UAC1_PROCESSING_UNIT:
|
case UAC1_PROCESSING_UNIT:
|
||||||
/* UAC2_EFFECT_UNIT has the same value */
|
/* UAC2_EFFECT_UNIT has the same value */
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (protocol == UAC_VERSION_1)
|
||||||
return parse_audio_processing_unit(state, unitid, p1);
|
return parse_audio_processing_unit(state, unitid, p1);
|
||||||
else
|
else
|
||||||
return 0; /* FIXME - effect units not implemented yet */
|
return 0; /* FIXME - effect units not implemented yet */
|
||||||
case UAC1_EXTENSION_UNIT:
|
case UAC1_EXTENSION_UNIT:
|
||||||
/* UAC2_PROCESSING_UNIT_V2 has the same value */
|
/* UAC2_PROCESSING_UNIT_V2 has the same value */
|
||||||
if (state->mixer->protocol == UAC_VERSION_1)
|
if (protocol == UAC_VERSION_1)
|
||||||
return parse_audio_extension_unit(state, unitid, p1);
|
return parse_audio_extension_unit(state, unitid, p1);
|
||||||
else /* UAC_VERSION_2 */
|
else /* UAC_VERSION_2 */
|
||||||
return parse_audio_processing_unit(state, unitid, p1);
|
return parse_audio_processing_unit(state, unitid, p1);
|
||||||
|
@ -2261,6 +2446,30 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||||
"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
|
"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
} else { /* UAC_VERSION_3 */
|
||||||
|
switch (p1[2]) {
|
||||||
|
case UAC_INPUT_TERMINAL:
|
||||||
|
return 0; /* NOP */
|
||||||
|
case UAC3_MIXER_UNIT:
|
||||||
|
return parse_audio_mixer_unit(state, unitid, p1);
|
||||||
|
case UAC3_CLOCK_SOURCE:
|
||||||
|
return parse_clock_source_unit(state, unitid, p1);
|
||||||
|
case UAC3_CLOCK_SELECTOR:
|
||||||
|
return parse_audio_selector_unit(state, unitid, p1);
|
||||||
|
case UAC3_FEATURE_UNIT:
|
||||||
|
return parse_audio_feature_unit(state, unitid, p1);
|
||||||
|
case UAC3_EFFECT_UNIT:
|
||||||
|
return 0; /* FIXME - effect units not implemented yet */
|
||||||
|
case UAC3_PROCESSING_UNIT:
|
||||||
|
return parse_audio_processing_unit(state, unitid, p1);
|
||||||
|
case UAC3_EXTENSION_UNIT:
|
||||||
|
return parse_audio_extension_unit(state, unitid, p1);
|
||||||
|
default:
|
||||||
|
usb_audio_err(state->chip,
|
||||||
|
"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
|
static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
|
||||||
|
@ -2330,7 +2539,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||||
err = parse_audio_unit(&state, desc->bSourceID);
|
err = parse_audio_unit(&state, desc->bSourceID);
|
||||||
if (err < 0 && err != -EINVAL)
|
if (err < 0 && err != -EINVAL)
|
||||||
return err;
|
return err;
|
||||||
} else { /* UAC_VERSION_2 */
|
} else if (mixer->protocol == UAC_VERSION_2) {
|
||||||
struct uac2_output_terminal_descriptor *desc = p;
|
struct uac2_output_terminal_descriptor *desc = p;
|
||||||
|
|
||||||
if (desc->bLength < sizeof(*desc))
|
if (desc->bLength < sizeof(*desc))
|
||||||
|
@ -2351,6 +2560,33 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||||
err = parse_audio_unit(&state, desc->bCSourceID);
|
err = parse_audio_unit(&state, desc->bCSourceID);
|
||||||
if (err < 0 && err != -EINVAL)
|
if (err < 0 && err != -EINVAL)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (uac_v2v3_control_is_readable(desc->bmControls,
|
||||||
|
UAC2_TE_CONNECTOR)) {
|
||||||
|
build_connector_control(&state, &state.oterm,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
} else { /* UAC_VERSION_3 */
|
||||||
|
struct uac3_output_terminal_descriptor *desc = p;
|
||||||
|
|
||||||
|
if (desc->bLength < sizeof(*desc))
|
||||||
|
continue; /* invalid descriptor? */
|
||||||
|
/* mark terminal ID as visited */
|
||||||
|
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||||
|
state.oterm.id = desc->bTerminalID;
|
||||||
|
state.oterm.type = le16_to_cpu(desc->wTerminalType);
|
||||||
|
state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr);
|
||||||
|
err = parse_audio_unit(&state, desc->bSourceID);
|
||||||
|
if (err < 0 && err != -EINVAL)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For UAC3, use the same approach to also add the
|
||||||
|
* clock selectors
|
||||||
|
*/
|
||||||
|
err = parse_audio_unit(&state, desc->bCSourceID);
|
||||||
|
if (err < 0 && err != -EINVAL)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2597,6 +2833,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
||||||
case UAC_VERSION_2:
|
case UAC_VERSION_2:
|
||||||
mixer->protocol = UAC_VERSION_2;
|
mixer->protocol = UAC_VERSION_2;
|
||||||
break;
|
break;
|
||||||
|
case UAC_VERSION_3:
|
||||||
|
mixer->protocol = UAC_VERSION_3;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
|
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
|
||||||
|
|
|
@ -1149,27 +1149,17 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Marantz/Denon USB DACs need a vendor cmd to switch
|
/* ITF-USB DSD based DACs need a vendor cmd to switch
|
||||||
* between PCM and native DSD mode
|
* between PCM and native DSD mode
|
||||||
*/
|
*/
|
||||||
static bool is_marantz_denon_dac(unsigned int id)
|
static bool is_itf_usb_dsd_dac(unsigned int id)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */
|
case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */
|
||||||
case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */
|
case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */
|
||||||
case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */
|
case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */
|
||||||
return true;
|
case USB_ID(0x1852, 0x5065): /* Luxman DA-06 */
|
||||||
}
|
case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-501V2/UD-503/NT-503 */
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch
|
|
||||||
* between PCM/DOP and native DSD mode
|
|
||||||
*/
|
|
||||||
static bool is_teac_dsd_dac(unsigned int id)
|
|
||||||
{
|
|
||||||
switch (id) {
|
|
||||||
case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */
|
|
||||||
case USB_ID(0x0644, 0x8044): /* Esoteric D-05X */
|
case USB_ID(0x0644, 0x8044): /* Esoteric D-05X */
|
||||||
case USB_ID(0x0644, 0x804a): /* TEAC UD-301 */
|
case USB_ID(0x0644, 0x804a): /* TEAC UD-301 */
|
||||||
return true;
|
return true;
|
||||||
|
@ -1183,7 +1173,7 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
|
||||||
struct usb_device *dev = subs->dev;
|
struct usb_device *dev = subs->dev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (is_marantz_denon_dac(subs->stream->chip->usb_id)) {
|
if (is_itf_usb_dsd_dac(subs->stream->chip->usb_id)) {
|
||||||
/* First switch to alt set 0, otherwise the mode switch cmd
|
/* First switch to alt set 0, otherwise the mode switch cmd
|
||||||
* will not be accepted by the DAC
|
* will not be accepted by the DAC
|
||||||
*/
|
*/
|
||||||
|
@ -1193,37 +1183,26 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
|
||||||
|
|
||||||
mdelay(20); /* Delay needed after setting the interface */
|
mdelay(20); /* Delay needed after setting the interface */
|
||||||
|
|
||||||
switch (fmt->altsetting) {
|
|
||||||
case 2: /* DSD mode requested */
|
|
||||||
case 1: /* PCM mode requested */
|
|
||||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
|
||||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
|
||||||
fmt->altsetting - 1, 1, NULL, 0);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mdelay(20);
|
|
||||||
} else if (is_teac_dsd_dac(subs->stream->chip->usb_id)) {
|
|
||||||
/* Vendor mode switch cmd is required. */
|
/* Vendor mode switch cmd is required. */
|
||||||
switch (fmt->altsetting) {
|
if (fmt->formats & SNDRV_PCM_FMTBIT_DSD_U32_BE) {
|
||||||
case 3: /* DSD mode (DSD_U32) requested */
|
/* DSD mode (DSD_U32) requested */
|
||||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
||||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||||
1, 1, NULL, 0);
|
1, 1, NULL, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: /* PCM or DOP mode (S32) requested */
|
} else {
|
||||||
case 1: /* PCM mode (S16) requested */
|
/* PCM or DOP mode (S32) requested */
|
||||||
|
/* PCM mode (S16) requested */
|
||||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
||||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||||
0, 1, NULL, 0);
|
0, 1, NULL, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
mdelay(20);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1300,10 +1279,10 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
|
||||||
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
||||||
mdelay(20);
|
mdelay(20);
|
||||||
|
|
||||||
/* Marantz/Denon devices with USB DAC functionality need a delay
|
/* ITF-USB DSD based DACs functionality need a delay
|
||||||
* after each class compliant request
|
* after each class compliant request
|
||||||
*/
|
*/
|
||||||
if (is_marantz_denon_dac(chip->usb_id)
|
if (is_itf_usb_dsd_dac(chip->usb_id)
|
||||||
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
|
||||||
mdelay(20);
|
mdelay(20);
|
||||||
|
|
||||||
|
@ -1329,6 +1308,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp,
|
struct audioformat *fp,
|
||||||
unsigned int sample_bytes)
|
unsigned int sample_bytes)
|
||||||
{
|
{
|
||||||
|
struct usb_interface *iface;
|
||||||
|
|
||||||
/* Playback Designs */
|
/* Playback Designs */
|
||||||
if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
|
if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
|
||||||
switch (fp->altsetting) {
|
switch (fp->altsetting) {
|
||||||
|
@ -1390,17 +1371,52 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Denon/Marantz devices with USB DAC functionality */
|
/* ITF-USB DSD based DACs */
|
||||||
if (is_marantz_denon_dac(chip->usb_id)) {
|
if (is_itf_usb_dsd_dac(chip->usb_id)) {
|
||||||
if (fp->altsetting == 2)
|
iface = usb_ifnum_to_if(chip->dev, fp->iface);
|
||||||
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TEAC devices with USB DAC functionality */
|
/* Altsetting 2 support native DSD if the num of altsets is
|
||||||
if (is_teac_dsd_dac(chip->usb_id)) {
|
* three (0-2),
|
||||||
if (fp->altsetting == 3)
|
* Altsetting 3 support native DSD if the num of altsets is
|
||||||
|
* four (0-3).
|
||||||
|
*/
|
||||||
|
if (fp->altsetting == iface->num_altsetting - 1)
|
||||||
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
||||||
|
struct audioformat *fp,
|
||||||
|
int stream)
|
||||||
|
{
|
||||||
|
switch (chip->usb_id) {
|
||||||
|
case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
|
||||||
|
/* Optoplay sets the sample rate attribute although
|
||||||
|
* it seems not supporting it in fact.
|
||||||
|
*/
|
||||||
|
fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
|
||||||
|
break;
|
||||||
|
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
|
||||||
|
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
||||||
|
/* doesn't set the sample rate attribute, but supports it */
|
||||||
|
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
|
||||||
|
break;
|
||||||
|
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
|
||||||
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
||||||
|
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
|
||||||
|
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
|
||||||
|
an older model 77d:223) */
|
||||||
|
/*
|
||||||
|
* plantronics headset and Griffin iMic have set adaptive-in
|
||||||
|
* although it's really not...
|
||||||
|
*/
|
||||||
|
fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
|
||||||
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
|
||||||
|
else
|
||||||
|
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,4 +42,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
||||||
struct audioformat *fp,
|
struct audioformat *fp,
|
||||||
unsigned int sample_bytes);
|
unsigned int sample_bytes);
|
||||||
|
|
||||||
|
void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
||||||
|
struct audioformat *fp,
|
||||||
|
int stream);
|
||||||
|
|
||||||
#endif /* __USBAUDIO_QUIRKS_H */
|
#endif /* __USBAUDIO_QUIRKS_H */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/audio.h>
|
#include <linux/usb/audio.h>
|
||||||
#include <linux/usb/audio-v2.h>
|
#include <linux/usb/audio-v2.h>
|
||||||
|
#include <linux/usb/audio-v3.h>
|
||||||
|
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
|
@ -311,6 +312,153 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
|
||||||
return chmap;
|
return chmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UAC3 device stores channels information in Cluster Descriptors */
|
||||||
|
static struct
|
||||||
|
snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
|
||||||
|
*cluster)
|
||||||
|
{
|
||||||
|
unsigned int channels = cluster->bNrChannels;
|
||||||
|
struct snd_pcm_chmap_elem *chmap;
|
||||||
|
void *p = cluster;
|
||||||
|
int len, c;
|
||||||
|
|
||||||
|
if (channels > ARRAY_SIZE(chmap->map))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
|
||||||
|
if (!chmap)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = le16_to_cpu(cluster->wLength);
|
||||||
|
c = 0;
|
||||||
|
p += sizeof(struct uac3_cluster_header_descriptor);
|
||||||
|
|
||||||
|
while (((p - (void *)cluster) < len) && (c < channels)) {
|
||||||
|
struct uac3_cluster_segment_descriptor *cs_desc = p;
|
||||||
|
u16 cs_len;
|
||||||
|
u8 cs_type;
|
||||||
|
|
||||||
|
cs_len = le16_to_cpu(cs_desc->wLength);
|
||||||
|
cs_type = cs_desc->bSegmentType;
|
||||||
|
|
||||||
|
if (cs_type == UAC3_CHANNEL_INFORMATION) {
|
||||||
|
struct uac3_cluster_information_segment_descriptor *is = p;
|
||||||
|
unsigned char map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: this conversion is not complete, update it
|
||||||
|
* after adding UAC3 values to asound.h
|
||||||
|
*/
|
||||||
|
switch (is->bChPurpose) {
|
||||||
|
case UAC3_CH_MONO:
|
||||||
|
map = SNDRV_CHMAP_MONO;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_LEFT:
|
||||||
|
case UAC3_CH_FRONT_LEFT:
|
||||||
|
case UAC3_CH_HEADPHONE_LEFT:
|
||||||
|
map = SNDRV_CHMAP_FL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_RIGHT:
|
||||||
|
case UAC3_CH_FRONT_RIGHT:
|
||||||
|
case UAC3_CH_HEADPHONE_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_FR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_FRONT_CENTER:
|
||||||
|
map = SNDRV_CHMAP_FC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_FRONT_LEFT_OF_CENTER:
|
||||||
|
map = SNDRV_CHMAP_FLC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_FRONT_RIGHT_OF_CENTER:
|
||||||
|
map = SNDRV_CHMAP_FRC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_SIDE_LEFT:
|
||||||
|
map = SNDRV_CHMAP_SL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_SIDE_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_SR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BACK_LEFT:
|
||||||
|
map = SNDRV_CHMAP_RL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BACK_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_RR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BACK_CENTER:
|
||||||
|
map = SNDRV_CHMAP_RC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BACK_LEFT_OF_CENTER:
|
||||||
|
map = SNDRV_CHMAP_RLC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BACK_RIGHT_OF_CENTER:
|
||||||
|
map = SNDRV_CHMAP_RRC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_CENTER:
|
||||||
|
map = SNDRV_CHMAP_TC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_FRONT_LEFT:
|
||||||
|
map = SNDRV_CHMAP_TFL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_FRONT_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_TFR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_FRONT_CENTER:
|
||||||
|
map = SNDRV_CHMAP_TFC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_FRONT_LOC:
|
||||||
|
map = SNDRV_CHMAP_TFLC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_FRONT_ROC:
|
||||||
|
map = SNDRV_CHMAP_TFRC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_SIDE_LEFT:
|
||||||
|
map = SNDRV_CHMAP_TSL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_SIDE_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_TSR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_BACK_LEFT:
|
||||||
|
map = SNDRV_CHMAP_TRL;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_BACK_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_TRR;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_TOP_BACK_CENTER:
|
||||||
|
map = SNDRV_CHMAP_TRC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_BOTTOM_CENTER:
|
||||||
|
map = SNDRV_CHMAP_BC;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_LOW_FREQUENCY_EFFECTS:
|
||||||
|
map = SNDRV_CHMAP_LFE;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_LFE_LEFT:
|
||||||
|
map = SNDRV_CHMAP_LLFE;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_LFE_RIGHT:
|
||||||
|
map = SNDRV_CHMAP_RLFE;
|
||||||
|
break;
|
||||||
|
case UAC3_CH_RELATIONSHIP_UNDEFINED:
|
||||||
|
default:
|
||||||
|
map = SNDRV_CHMAP_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chmap->map[c++] = map;
|
||||||
|
}
|
||||||
|
p += cs_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channels < c)
|
||||||
|
pr_err("%s: channel number mismatch\n", __func__);
|
||||||
|
|
||||||
|
chmap->channels = channels;
|
||||||
|
|
||||||
|
for (; c < channels; c++)
|
||||||
|
chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
|
||||||
|
|
||||||
|
return chmap;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* add this endpoint to the chip instance.
|
* add this endpoint to the chip instance.
|
||||||
* if a stream with the same endpoint already exists, append to it.
|
* if a stream with the same endpoint already exists, append to it.
|
||||||
|
@ -461,10 +609,11 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct uac2_output_terminal_descriptor *
|
static void *
|
||||||
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||||
int terminal_id)
|
int terminal_id)
|
||||||
{
|
{
|
||||||
|
/* OK to use with both UAC2 and UAC3 */
|
||||||
struct uac2_output_terminal_descriptor *term = NULL;
|
struct uac2_output_terminal_descriptor *term = NULL;
|
||||||
|
|
||||||
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||||
|
@ -484,10 +633,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||||
struct usb_host_interface *alts;
|
struct usb_host_interface *alts;
|
||||||
struct usb_interface_descriptor *altsd;
|
struct usb_interface_descriptor *altsd;
|
||||||
int i, altno, err, stream;
|
int i, altno, err, stream;
|
||||||
unsigned int format = 0, num_channels = 0;
|
u64 format = 0;
|
||||||
|
unsigned int num_channels = 0;
|
||||||
struct audioformat *fp = NULL;
|
struct audioformat *fp = NULL;
|
||||||
int num, protocol, clock = 0;
|
int num, protocol, clock = 0;
|
||||||
struct uac_format_type_i_continuous_descriptor *fmt;
|
struct uac_format_type_i_continuous_descriptor *fmt = NULL;
|
||||||
|
struct snd_pcm_chmap_elem *chmap_v3 = NULL;
|
||||||
unsigned int chconfig;
|
unsigned int chconfig;
|
||||||
|
|
||||||
dev = chip->dev;
|
dev = chip->dev;
|
||||||
|
@ -624,18 +775,136 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||||
iface_no, altno, as->bTerminalLink);
|
iface_no, altno, as->bTerminalLink);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case UAC_VERSION_3: {
|
||||||
|
struct uac3_input_terminal_descriptor *input_term;
|
||||||
|
struct uac3_output_terminal_descriptor *output_term;
|
||||||
|
struct uac3_as_header_descriptor *as;
|
||||||
|
struct uac3_cluster_header_descriptor *cluster;
|
||||||
|
struct uac3_hc_descriptor_header hc_header;
|
||||||
|
u16 cluster_id, wLength;
|
||||||
|
|
||||||
|
as = snd_usb_find_csint_desc(alts->extra,
|
||||||
|
alts->extralen,
|
||||||
|
NULL, UAC_AS_GENERAL);
|
||||||
|
|
||||||
|
if (!as) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : UAC_AS_GENERAL descriptor not found\n",
|
||||||
|
iface_no, altno);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (as->bLength < sizeof(*as)) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : invalid UAC_AS_GENERAL desc\n",
|
||||||
|
iface_no, altno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster_id = le16_to_cpu(as->wClusterDescrID);
|
||||||
|
if (!cluster_id) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : no cluster descriptor\n",
|
||||||
|
iface_no, altno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get number of channels and channel map through
|
||||||
|
* High Capability Cluster Descriptor
|
||||||
|
*
|
||||||
|
* First step: get High Capability header and
|
||||||
|
* read size of Cluster Descriptor
|
||||||
|
*/
|
||||||
|
err = snd_usb_ctl_msg(chip->dev,
|
||||||
|
usb_rcvctrlpipe(chip->dev, 0),
|
||||||
|
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||||
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||||
|
cluster_id,
|
||||||
|
snd_usb_ctrl_intf(chip),
|
||||||
|
&hc_header, sizeof(hc_header));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
else if (err != sizeof(hc_header)) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : can't get High Capability descriptor\n",
|
||||||
|
iface_no, altno);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second step: allocate needed amount of memory
|
||||||
|
* and request Cluster Descriptor
|
||||||
|
*/
|
||||||
|
wLength = le16_to_cpu(hc_header.wLength);
|
||||||
|
cluster = kzalloc(wLength, GFP_KERNEL);
|
||||||
|
if (!cluster)
|
||||||
|
return -ENOMEM;
|
||||||
|
err = snd_usb_ctl_msg(chip->dev,
|
||||||
|
usb_rcvctrlpipe(chip->dev, 0),
|
||||||
|
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||||
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||||
|
cluster_id,
|
||||||
|
snd_usb_ctrl_intf(chip),
|
||||||
|
cluster, wLength);
|
||||||
|
if (err < 0) {
|
||||||
|
kfree(cluster);
|
||||||
|
return err;
|
||||||
|
} else if (err != wLength) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : can't get Cluster Descriptor\n",
|
||||||
|
iface_no, altno);
|
||||||
|
kfree(cluster);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_channels = cluster->bNrChannels;
|
||||||
|
chmap_v3 = convert_chmap_v3(cluster);
|
||||||
|
|
||||||
|
kfree(cluster);
|
||||||
|
|
||||||
|
format = le64_to_cpu(as->bmFormats);
|
||||||
|
|
||||||
|
/* lookup the terminal associated to this interface
|
||||||
|
* to extract the clock */
|
||||||
|
input_term = snd_usb_find_input_terminal_descriptor(
|
||||||
|
chip->ctrl_intf,
|
||||||
|
as->bTerminalLink);
|
||||||
|
|
||||||
|
if (input_term) {
|
||||||
|
clock = input_term->bCSourceID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
|
||||||
|
as->bTerminalLink);
|
||||||
|
if (output_term) {
|
||||||
|
clock = output_term->bCSourceID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"%u:%d : bogus bTerminalLink %d\n",
|
||||||
|
iface_no, altno, as->bTerminalLink);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||||
/* get format type */
|
/* get format type */
|
||||||
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
|
fmt = snd_usb_find_csint_desc(alts->extra,
|
||||||
|
alts->extralen,
|
||||||
|
NULL, UAC_FORMAT_TYPE);
|
||||||
if (!fmt) {
|
if (!fmt) {
|
||||||
dev_err(&dev->dev,
|
dev_err(&dev->dev,
|
||||||
"%u:%d : no UAC_FORMAT_TYPE desc\n",
|
"%u:%d : no UAC_FORMAT_TYPE desc\n",
|
||||||
iface_no, altno);
|
iface_no, altno);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
|
if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
|
||||||
((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
|
|| ((protocol == UAC_VERSION_2) &&
|
||||||
|
(fmt->bLength < 6))) {
|
||||||
dev_err(&dev->dev,
|
dev_err(&dev->dev,
|
||||||
"%u:%d : invalid UAC_FORMAT_TYPE desc\n",
|
"%u:%d : invalid UAC_FORMAT_TYPE desc\n",
|
||||||
iface_no, altno);
|
iface_no, altno);
|
||||||
|
@ -643,9 +912,10 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Blue Microphones workaround: The last altsetting is identical
|
* Blue Microphones workaround: The last altsetting is
|
||||||
* with the previous one, except for a larger packet size, but
|
* identical with the previous one, except for a larger
|
||||||
* is actually a mislabeled two-channel setting; ignore it.
|
* packet size, but is actually a mislabeled two-channel
|
||||||
|
* setting; ignore it.
|
||||||
*/
|
*/
|
||||||
if (fmt->bNrChannels == 1 &&
|
if (fmt->bNrChannels == 1 &&
|
||||||
fmt->bSubframeSize == 2 &&
|
fmt->bSubframeSize == 2 &&
|
||||||
|
@ -656,6 +926,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
|
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
|
||||||
fp->maxpacksize * 2)
|
fp->maxpacksize * 2)
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
|
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
|
@ -678,48 +949,42 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||||
INIT_LIST_HEAD(&fp->list);
|
INIT_LIST_HEAD(&fp->list);
|
||||||
|
|
||||||
/* some quirks for attributes here */
|
/* some quirks for attributes here */
|
||||||
|
snd_usb_audioformat_attributes_quirk(chip, fp, stream);
|
||||||
switch (chip->usb_id) {
|
|
||||||
case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
|
|
||||||
/* Optoplay sets the sample rate attribute although
|
|
||||||
* it seems not supporting it in fact.
|
|
||||||
*/
|
|
||||||
fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
||||||
break;
|
|
||||||
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
|
|
||||||
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
|
||||||
/* doesn't set the sample rate attribute, but supports it */
|
|
||||||
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
||||||
break;
|
|
||||||
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
|
|
||||||
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
|
||||||
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
|
|
||||||
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
|
|
||||||
an older model 77d:223) */
|
|
||||||
/*
|
|
||||||
* plantronics headset and Griffin iMic have set adaptive-in
|
|
||||||
* although it's really not...
|
|
||||||
*/
|
|
||||||
fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
|
|
||||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
||||||
fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
|
|
||||||
else
|
|
||||||
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ok, let's parse further... */
|
/* ok, let's parse further... */
|
||||||
if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
|
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||||
|
if (snd_usb_parse_audio_format(chip, fp, format,
|
||||||
|
fmt, stream) < 0) {
|
||||||
kfree(fp->rate_table);
|
kfree(fp->rate_table);
|
||||||
kfree(fp);
|
kfree(fp);
|
||||||
fp = NULL;
|
fp = NULL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct uac3_as_header_descriptor *as;
|
||||||
|
|
||||||
|
as = snd_usb_find_csint_desc(alts->extra,
|
||||||
|
alts->extralen,
|
||||||
|
NULL, UAC_AS_GENERAL);
|
||||||
|
|
||||||
|
if (snd_usb_parse_audio_format_v3(chip, fp, as,
|
||||||
|
stream) < 0) {
|
||||||
|
kfree(fp->rate_table);
|
||||||
|
kfree(fp);
|
||||||
|
fp = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Create chmap */
|
/* Create chmap */
|
||||||
if (fp->channels != num_channels)
|
if (fp->channels != num_channels)
|
||||||
chconfig = 0;
|
chconfig = 0;
|
||||||
fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
|
|
||||||
|
if (protocol == UAC_VERSION_3)
|
||||||
|
fp->chmap = chmap_v3;
|
||||||
|
else
|
||||||
|
fp->chmap = convert_chmap(fp->channels, chconfig,
|
||||||
|
protocol);
|
||||||
|
|
||||||
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
|
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
|
||||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||||
|
|
Loading…
Reference in New Issue