From 7c424edd116e76eee6218a1e9a6ff6c4daaf2a4d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 6 Apr 2016 19:02:12 +0800 Subject: [PATCH 01/34] pcm_plugin: fix appl pointer not correct when mmap_commit() return error When snd_pcm_mmap_commit() return error, the appl pointer is also updated. which cause the avail_update()'s result wrong. This patch move the snd_pcm_mmap_appl_forward() to the place when snd_pcm_mmap_commit() is successfully returned. Signed-off-by: Shengjiu Wang Signed-off-by: Takashi Iwai --- src/pcm/pcm_plugin.c | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index d007e8c..940491d 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -279,18 +279,22 @@ static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, return -EPIPE; } snd_atomic_write_begin(&plugin->watom); - snd_pcm_mmap_appl_forward(pcm, frames); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { snd_pcm_sframes_t res; res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); - if (res < 0) + if (res < 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + } frames -= res; } - snd_atomic_write_end(&plugin->watom); - if (result <= 0) + if (result <= 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + } + snd_pcm_mmap_appl_forward(pcm, frames); + snd_atomic_write_end(&plugin->watom); offset += frames; xfer += frames; size -= frames; @@ -325,19 +329,23 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, return -EPIPE; } snd_atomic_write_begin(&plugin->watom); - snd_pcm_mmap_appl_forward(pcm, frames); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { snd_pcm_sframes_t res; res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result); - if (res < 0) + if (res < 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + } frames -= res; } - snd_atomic_write_end(&plugin->watom); - if (result <= 0) + if (result <= 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + } + snd_pcm_mmap_appl_forward(pcm, frames); + snd_atomic_write_end(&plugin->watom); offset += frames; xfer += frames; size -= frames; @@ -423,19 +431,23 @@ snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, frames = plugin->write(pcm, areas, appl_offset, frames, slave_areas, slave_offset, &slave_frames); snd_atomic_write_begin(&plugin->watom); - snd_pcm_mmap_appl_forward(pcm, frames); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); - snd_atomic_write_end(&plugin->watom); if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { snd_pcm_sframes_t res; res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); - if (res < 0) + if (res < 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? xfer : res; + } frames -= res; } - if (result <= 0) + if (result <= 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? xfer : result; + } + snd_pcm_mmap_appl_forward(pcm, frames); + snd_atomic_write_end(&plugin->watom); if (frames == cont) appl_offset = 0; else @@ -490,19 +502,23 @@ static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) frames = (plugin->read)(pcm, areas, hw_offset, frames, slave_areas, slave_offset, &slave_frames); snd_atomic_write_begin(&plugin->watom); - snd_pcm_mmap_hw_forward(pcm, frames); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); - snd_atomic_write_end(&plugin->watom); if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { snd_pcm_sframes_t res; res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result); - if (res < 0) + if (res < 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + } frames -= res; } - if (result <= 0) + if (result <= 0) { + snd_atomic_write_end(&plugin->watom); return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + } + snd_pcm_mmap_hw_forward(pcm, frames); + snd_atomic_write_end(&plugin->watom); if (frames == cont) hw_offset = 0; else -- 2.5.5 From 503a285ed60164d8c65c6ee9ba6f23631da753df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 Apr 2016 16:29:41 +0200 Subject: [PATCH 02/34] pcm: Clean up error paths in snd_pcm_plugin_*() helpers Minor code refactoring to unify the error return paths. Signed-off-by: Takashi Iwai --- src/pcm/pcm_plugin.c | 67 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index 940491d..8527783 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -276,7 +276,8 @@ static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) { SNDMSG("write overflow %ld > %ld", slave_frames, snd_pcm_mmap_playback_avail(slave)); - return -EPIPE; + err = -EPIPE; + goto error; } snd_atomic_write_begin(&plugin->watom); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); @@ -284,14 +285,14 @@ static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, snd_pcm_sframes_t res; res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); if (res < 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + err = res; + goto error_atomic; } frames -= res; } if (result <= 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + err = result; + goto error_atomic; } snd_pcm_mmap_appl_forward(pcm, frames); snd_atomic_write_end(&plugin->watom); @@ -300,6 +301,11 @@ static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm, size -= frames; } return (snd_pcm_sframes_t)xfer; + + error_atomic: + snd_atomic_write_end(&plugin->watom); + error: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, @@ -311,6 +317,7 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, snd_pcm_t *slave = plugin->gen.slave; snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t result; + int err; while (size > 0) { snd_pcm_uframes_t frames = size; @@ -326,7 +333,8 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) { SNDMSG("read overflow %ld > %ld", slave_frames, snd_pcm_mmap_playback_avail(slave)); - return -EPIPE; + err = -EPIPE; + goto error; } snd_atomic_write_begin(&plugin->watom); result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); @@ -335,14 +343,14 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result); if (res < 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + err = res; + goto error_atomic; } frames -= res; } if (result <= 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + err = result; + goto error_atomic; } snd_pcm_mmap_appl_forward(pcm, frames); snd_atomic_write_end(&plugin->watom); @@ -351,6 +359,11 @@ static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm, size -= frames; } return (snd_pcm_sframes_t)xfer; + + error_atomic: + snd_atomic_write_end(&plugin->watom); + error: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } @@ -401,6 +414,7 @@ snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset; snd_pcm_sframes_t slave_size; snd_pcm_sframes_t xfer; + int err; if (pcm->stream == SND_PCM_STREAM_CAPTURE) { snd_atomic_write_begin(&plugin->watom); @@ -421,11 +435,10 @@ snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t slave_offset; snd_pcm_uframes_t slave_frames = ULONG_MAX; snd_pcm_sframes_t result; - int err; err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); if (err < 0) - return xfer > 0 ? xfer : err; + goto error; if (frames > cont) frames = cont; frames = plugin->write(pcm, areas, appl_offset, frames, @@ -437,14 +450,14 @@ snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result); if (res < 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? xfer : res; + err = res; + goto error_atomic; } frames -= res; } if (result <= 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? xfer : result; + err = result; + goto error_atomic; } snd_pcm_mmap_appl_forward(pcm, frames); snd_atomic_write_end(&plugin->watom); @@ -461,6 +474,11 @@ snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, return -EPIPE; } return xfer; + + error_atomic: + snd_atomic_write_end(&plugin->watom); + error: + return xfer > 0 ? xfer : err; } static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) @@ -468,6 +486,7 @@ static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) snd_pcm_plugin_t *plugin = pcm->private_data; snd_pcm_t *slave = plugin->gen.slave; snd_pcm_sframes_t slave_size; + int err; slave_size = snd_pcm_avail_update(slave); if (pcm->stream == SND_PCM_STREAM_CAPTURE && @@ -492,11 +511,10 @@ static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) snd_pcm_uframes_t slave_offset; snd_pcm_uframes_t slave_frames = ULONG_MAX; snd_pcm_sframes_t result; - int err; err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); if (err < 0) - return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; + goto error; if (frames > cont) frames = cont; frames = (plugin->read)(pcm, areas, hw_offset, frames, @@ -508,14 +526,14 @@ static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result); if (res < 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : res; + err = res; + goto error_atomic; } frames -= res; } if (result <= 0) { - snd_atomic_write_end(&plugin->watom); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : result; + err = result; + goto error_atomic; } snd_pcm_mmap_hw_forward(pcm, frames); snd_atomic_write_end(&plugin->watom); @@ -528,6 +546,11 @@ static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) xfer += frames; } return (snd_pcm_sframes_t)xfer; + + error_atomic: + snd_atomic_write_end(&plugin->watom); + error: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } } -- 2.5.5 From 374c5fa9c5cb80efa41ef8a3afd215aa48b48436 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:28:42 +0800 Subject: [PATCH 03/34] topology: Use the generic pointer to free an element's object The element is a wrapper for different types of objects.So use the generic pointer 'obj' instead of the type-specific pointer to free the object. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/elem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/topology/elem.c b/src/topology/elem.c index 12d6a72..00f9eea 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -83,8 +83,8 @@ void tplg_elem_free(struct tplg_elem *elem) /* free struct snd_tplg_ object, * the union pointers share the same address */ - if (elem->mixer_ctrl) - free(elem->mixer_ctrl); + if (elem->obj) + free(elem->obj); free(elem); } -- 2.5.5 From 6b31bf8edb407f4c184576909f40c41bdc8439e4 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:01 +0800 Subject: [PATCH 04/34] topology: Define a free handler for the element This handler is defined for type-specific destruction of an element. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/elem.c | 6 +++++- src/topology/tplg_local.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/topology/elem.c b/src/topology/elem.c index 00f9eea..f2afaaf 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -83,8 +83,12 @@ void tplg_elem_free(struct tplg_elem *elem) /* free struct snd_tplg_ object, * the union pointers share the same address */ - if (elem->obj) + if (elem->obj) { + if (elem->free) + elem->free(elem->obj); + free(elem->obj); + } free(elem); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 4915b1a..7368a86 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -127,6 +127,8 @@ struct tplg_elem { */ struct list_head ref_list; struct list_head list; /* list of all elements with same type */ + + void (*free)(void *obj); }; struct map_elem { -- 2.5.5 From 2fd8d388f530b03872d8afb51b0f98c6474646c7 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:15 +0800 Subject: [PATCH 05/34] topology: Add doc for vendor tuples Describe how to define vendor tokens and tuples in the text conf file. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/topology.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/include/topology.h b/include/topology.h index 011f6ae..51d282f 100644 --- a/include/topology.h +++ b/include/topology.h @@ -203,12 +203,77 @@ extern "C" { * bytes "0x12,0x34,0x56,0x78" * shorts "0x1122,0x3344,0x5566,0x7788" * words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234" + * tuples "section id of the vendor tuples" * }; * - * The file, bytes, shorts and words keywords are all mutually exclusive as - * the private data should only be taken from one source. The private data can - * either be read from a separate file or defined in the topology file using - * the bytes, shorts or words keywords. + * The file, bytes, shorts, words and tuples keywords are all mutually + * exclusive as the private data should only be taken from one source. + * The private data can either be read from a separate file or defined in + * the topology file using the bytes, shorts, words or tuples keywords. + * The keyword tuples is to define vendor specific tuples. Please refer to + * section Vendor Tokens and Vendor tuples. + * + *
Vendor Tokens
+ * A vendor token list is defined as a new section. Each token element is + * a pair of string ID and integer value. And both the ID and value are + * vendor-specific. + * + *
+ * SectionVendorTokens."id of the vendor tokens" {
+ *	comment "optional comments"
+ *	VENDOR_TOKEN_ID1 "1"
+ *	VENDOR_TOKEN_ID2 "2"
+ *	VENDOR_TOKEN_ID3 "3"
+ *	...
+ * }
+ * 
+ * + *
Vendor Tuples
+ * Vendor tuples are defined as a new section. It contains a reference to + * a vendor token list and several tuple arrays. + * All arrays share a vendor token list, defined by the tokens keyword. + * Each tuple array is for a specific type, defined by the string following + * the tuples keyword. Supported types are: string, uuid, bool, byte, + * short and word. + * + *
+ * SectionVendorTuples."id of the vendor tuples" {
+ *	tokens "id of the vendor tokens"
+ *
+ *	tuples."string" {
+ *		VENDOR_TOKEN_ID1 "character string"
+ *		...
+ *	}
+ *
+ *	tuples."uuid" {
+ *		VENDOR_TOKEN_ID2 "16 character uuid"
+ *		...
+ *	}
+ *
+ *	tuples."bool" {
+ *		VENDOR_TOKEN_ID3 "true/false"
+ *		...
+ *	}
+ *
+ *	tuples."byte" {
+ *		VENDOR_TOKEN_ID4 "0x11"
+ *		VENDOR_TOKEN_ID5 "0x22"
+ *		...
+ *	}
+ *
+ *	tuples."short" {
+ *		VENDOR_TOKEN_ID6 "0x1122"
+ *		VENDOR_TOKEN_ID7 "0x3344"
+ *		...
+ *	}
+ *
+ *	tuples."word" {
+ *		VENDOR_TOKEN_ID8 "0x11223344"
+ *		VENDOR_TOKEN_ID9 "0x55667788"
+ *		...
+ *	}
+ * }
+ * 
* *
Mixer Controls
* A mixer control is defined as a new section that can include channel mapping, @@ -389,6 +454,10 @@ extern "C" { * fields are the same for widgets as they are for controls whilst the other * fields map on very closely to the driver widget fields. * + *
Widget Private Data
+ * Widget can have private data. For the format of the private data, please + * refer to section Control Private Data. + * *

PCM Capabilities

* Topology can also define the capabilities of FE and BE PCMs. Capabilities * can be defined with the following section :- -- 2.5.5 From 768a006089fab94d5aeffd9115da3fbe951a94f8 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:27 +0800 Subject: [PATCH 06/34] topology: ABI - Define types for vendor tuples Tuples, a pair of token and value, can be used to define vendor specific data, for controls and widgets. This can avoid importing binary data blob from other files. Vendor specific tuple arrays will be embeded in the private data buffer of a control or widget object. To be backward compatible, union is used to define the tuple arrays in the existing private data ABI object 'struct snd_soc_tplg_private'. Vendors need to make sure the token values defined by the topology conf file match those defined by their driver. Now supported tuple types are uuid, string, bool, byte, short and word. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/sound/asoc.h | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/include/sound/asoc.h b/include/sound/asoc.h index a29c05c..920c9e0 100644 --- a/include/sound/asoc.h +++ b/include/sound/asoc.h @@ -107,6 +107,14 @@ #define SND_SOC_TPLG_STREAM_PLAYBACK 0 #define SND_SOC_TPLG_STREAM_CAPTURE 1 +/* vendor tuple types */ +#define SND_SOC_TPLG_TUPLE_TYPE_UUID 0 +#define SND_SOC_TPLG_TUPLE_TYPE_STRING 1 +#define SND_SOC_TPLG_TUPLE_TYPE_BOOL 2 +#define SND_SOC_TPLG_TUPLE_TYPE_BYTE 3 +#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4 +#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5 + /* * Block Header. * This header precedes all object and object arrays below. @@ -123,6 +131,35 @@ struct snd_soc_tplg_hdr { __le32 count; /* number of elements in block */ } __attribute__((packed)); +/* vendor tuple for uuid */ +struct snd_soc_tplg_vendor_uuid_elem { + __le32 token; + char uuid[16]; +} __attribute__((packed)); + +/* vendor tuple for a bool/byte/short/word value */ +struct snd_soc_tplg_vendor_value_elem { + __le32 token; + __le32 value; +} __attribute__((packed)); + +/* vendor tuple for string */ +struct snd_soc_tplg_vendor_string_elem { + __le32 token; + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +} __attribute__((packed)); + +struct snd_soc_tplg_vendor_array { + __le32 size; /* size in bytes of the array, including all elements */ + __le32 type; /* SND_SOC_TPLG_TUPLE_TYPE_ */ + __le32 num_elems; /* number of elements in array */ + union { + struct snd_soc_tplg_vendor_uuid_elem uuid[0]; + struct snd_soc_tplg_vendor_value_elem value[0]; + struct snd_soc_tplg_vendor_string_elem string[0]; + }; +} __attribute__((packed)); + /* * Private data. * All topology objects may have private data that can be used by the driver or @@ -130,7 +167,10 @@ struct snd_soc_tplg_hdr { */ struct snd_soc_tplg_private { __le32 size; /* in bytes of private data */ - char data[0]; + union { + char data[0]; + struct snd_soc_tplg_vendor_array array[0]; + }; } __attribute__((packed)); /* -- 2.5.5 From 9b751b38cbb60bfb08ee796a59d72f40e3cd196d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:36 +0800 Subject: [PATCH 07/34] topology: Add support for vendor tokens Vendor can define a token list in SectionVendorTokens. Each token element is a pair of string ID and integer value. And both the ID and value are vendor-specific. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/topology.h | 1 + src/topology/data.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ src/topology/elem.c | 3 +++ src/topology/parser.c | 10 ++++++++++ src/topology/tplg_local.h | 15 ++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/include/topology.h b/include/topology.h index 51d282f..0df112c 100644 --- a/include/topology.h +++ b/include/topology.h @@ -578,6 +578,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_BE, /*!< BE DAI link */ SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ + SND_TPLG_TYPE_TOKEN, /*!< Vendor tokens */ }; /** diff --git a/src/topology/data.c b/src/topology/data.c index 370c0fa..8455c15 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,56 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; } +/* Parse vendor tokens + */ +int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_elem *elem; + struct tplg_vendor_tokens *tokens; + int num_tokens = 0; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TOKEN); + if (!elem) + return -ENOMEM; + + snd_config_for_each(i, next, cfg) { + num_tokens++; + } + + if (!num_tokens) + return 0; + + tplg_dbg(" Vendor tokens: %s, %d tokens\n", elem->id, num_tokens); + + tokens = calloc(1, sizeof(*tokens) + + num_tokens * sizeof(struct tplg_token)); + if (!tokens) + return -ENOMEM; + elem->tokens = tokens; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (snd_config_get_string(n, &value) < 0) + continue; + + elem_copy_text(tokens->token[tokens->num_tokens].id, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tokens->token[tokens->num_tokens].value = atoi(value); + tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id, + tokens->token[tokens->num_tokens].value); + tokens->num_tokens++; + } + + return 0; +} /* Parse Private data. * diff --git a/src/topology/elem.c b/src/topology/elem.c index f2afaaf..95e3fd4 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -193,6 +193,9 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, list_add_tail(&elem->list, &tplg->cc_list); obj_size = sizeof(struct snd_soc_tplg_link_config); break; + case SND_TPLG_TYPE_TOKEN: + list_add_tail(&elem->list, &tplg->token_list); + break; default: free(elem); return NULL; diff --git a/src/topology/parser.c b/src/topology/parser.c index 4546257..264abc8 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -173,6 +173,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) continue; } + if (strcmp(id, "SectionVendorTokens") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_tokens, + NULL); + if (err < 0) + return err; + continue; + } + SNDERR("error: unknown section %s\n", id); } return 0; @@ -407,6 +415,7 @@ snd_tplg_t *snd_tplg_new(void) INIT_LIST_HEAD(&tplg->mixer_list); INIT_LIST_HEAD(&tplg->enum_list); INIT_LIST_HEAD(&tplg->bytes_ext_list); + INIT_LIST_HEAD(&tplg->token_list); return tplg; } @@ -426,6 +435,7 @@ void snd_tplg_free(snd_tplg_t *tplg) tplg_elem_free_list(&tplg->mixer_list); tplg_elem_free_list(&tplg->enum_list); tplg_elem_free_list(&tplg->bytes_ext_list); + tplg_elem_free_list(&tplg->token_list); free(tplg); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 7368a86..679bfff 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -69,6 +69,7 @@ struct snd_tplg { struct list_head route_list; struct list_head text_list; struct list_head pdata_list; + struct list_head token_list; struct list_head pcm_config_list; struct list_head pcm_caps_list; @@ -86,6 +87,16 @@ struct tplg_ref { struct list_head list; }; +/* element for vendor tokens */ +struct tplg_token { + char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + unsigned int value; +}; + +struct tplg_vendor_tokens { + unsigned int num_tokens; + struct tplg_token token[0]; +}; /* topology element */ struct tplg_elem { @@ -118,6 +129,7 @@ struct tplg_elem { /* these do not map to UAPI structs but are internal only */ struct snd_soc_tplg_ctl_tlv *tlv; struct snd_soc_tplg_private *data; + struct tplg_vendor_tokens *tokens; }; /* an element may refer to other elements: @@ -151,6 +163,9 @@ int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED); + int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); -- 2.5.5 From fdb9a6d19f8b7cc8252d27e83907d8d1c5e4b53d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:43 +0800 Subject: [PATCH 08/34] topology: Add support for parsing vendor tuples Vendor can define several tuple arrays in 'SectionVendorTuples', as well as the reference to a vendor token list object. A later patche will copy vendor tuples in ABI format to the private buffer of its parent data object in the building phase. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/topology.h | 1 + src/topology/data.c | 240 +++++++++++++++++++++++++++++++++++++++++++++- src/topology/elem.c | 4 + src/topology/parser.c | 10 ++ src/topology/tplg_local.h | 29 ++++++ 5 files changed, 283 insertions(+), 1 deletion(-) diff --git a/include/topology.h b/include/topology.h index 0df112c..b47f422 100644 --- a/include/topology.h +++ b/include/topology.h @@ -579,6 +579,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ SND_TPLG_TYPE_TOKEN, /*!< Vendor tokens */ + SND_TPLG_TYPE_TUPLE, /*!< Vendor tuples */ }; /** diff --git a/src/topology/data.c b/src/topology/data.c index 8455c15..606fcd3 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,175 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; } +static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_tuple_set **s) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_tuple_set *set; + unsigned int type, num_tuples = 0; + struct tplg_tuple *tuple; + unsigned long int tuple_val; + int len; + + snd_config_get_id(cfg, &id); + + if (strcmp(id, "uuid") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_UUID; + else if (strcmp(id, "string") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_STRING; + else if (strcmp(id, "bool") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_BOOL; + else if (strcmp(id, "byte") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_BYTE; + else if (strcmp(id, "short") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_SHORT; + else if (strcmp(id, "word") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_WORD; + else { + SNDERR("error: invalid tuple type '%s'\n", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) + num_tuples++; + if (!num_tuples) + return 0; + + tplg_dbg("\t %d %s tuples:\n", num_tuples, id); + set = calloc(1, sizeof(*set) + num_tuples * sizeof(struct tplg_tuple)); + if (!set) + return -ENOMEM; + + set->type = type; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + + /* get id */ + if (snd_config_get_id(n, &id) < 0) + continue; + + /* get value */ + if (snd_config_get_string(n, &value) < 0) + continue; + + tuple = &set->tuple[set->num_tuples]; + elem_copy_text(tuple->token, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + switch (type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + len = strlen(value); + if (len > 16 || len == 0) { + SNDERR("error: tuple %s: invalid uuid\n", id); + goto err; + } + + memcpy(tuple->uuid, value, len); + tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->uuid); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + elem_copy_text(tuple->string, value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + if (strcmp(value, "true") == 0) + tuple->value = 1; + tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + errno = 0; + /* no support for negative value */ + tuple_val = strtoul(value, NULL, 0); + if ((errno == ERANGE && tuple_val == ULONG_MAX) + || (errno != 0 && tuple_val == 0)) { + SNDERR("error: tuple %s:strtoul fail\n", id); + goto err; + } + + if ((type == SND_SOC_TPLG_TUPLE_TYPE_WORD + && tuple_val > UINT_MAX) + || (type == SND_SOC_TPLG_TUPLE_TYPE_SHORT + && tuple_val > USHRT_MAX) + || (type == SND_SOC_TPLG_TUPLE_TYPE_BYTE + && tuple_val > UCHAR_MAX)) { + SNDERR("error: tuple %s: invalid value\n", id); + goto err; + } + + tuple->value = (unsigned int) tuple_val; + tplg_dbg("\t\t%s = 0x%x\n", tuple->token, tuple->value); + break; + + default: + break; + } + + set->num_tuples++; + } + + *s = set; + return 0; + +err: + free(set); + return -EINVAL; +} + +static int parse_tuple_sets(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_vendor_tuples *tuples) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + unsigned int num_tuple_sets = 0; + int err; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("error: compound type expected for %s", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) { + num_tuple_sets++; + } + + if (!num_tuple_sets) + return 0; + + tuples->set = calloc(1, num_tuple_sets * sizeof(void *)); + if (!tuples->set) + return -ENOMEM; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("error: compound type expected for %s, is %d", + id, snd_config_get_type(n)); + return -EINVAL; + } + + err = parse_tuple_set(tplg, n, &tuples->set[tuples->num_sets]); + if (err < 0) + return err; + + /* overlook empty tuple sets */ + if (tuples->set[tuples->num_sets]) + tuples->num_sets++; + } + + return 0; +} + /* Parse vendor tokens */ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, @@ -304,10 +473,71 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } +/* Parse vendor tuples. + */ +int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_elem *elem; + struct tplg_vendor_tuples *tuples; + int err; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TUPLE); + if (!elem) + return -ENOMEM; + + tplg_dbg(" Vendor Tuples: %s\n", elem->id); + + tuples = calloc(1, sizeof(*tuples)); + if (!tuples) + return -ENOMEM; + elem->tuples = tuples; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "tokens") == 0) { + if (snd_config_get_string(n, &value) < 0) + return -EINVAL; + tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, value); + tplg_dbg("\t refer to vendor tokens: %s\n", value); + } + + if (strcmp(id, "tuples") == 0) { + err = parse_tuple_sets(tplg, n, tuples); + if (err < 0) + return err; + } + } + + return 0; +} + +/* Free handler of tuples */ +void tplg_free_tuples(void *obj) +{ + struct tplg_vendor_tuples *tuples = (struct tplg_vendor_tuples *)obj; + int i; + + if (!tuples || !tuples->set) + return; + + for (i = 0; i < tuples->num_sets; i++) + free(tuples->set[i]); + + free(tuples->set); +} + /* Parse Private data. * * Object private data can either be from file or defined as bytes, shorts, - * words. + * words, tuples. */ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) @@ -365,6 +595,14 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "tuples") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + tplg_dbg(" Data: %s\n", val); + tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val); + continue; + } + if (strcmp(id, "index") == 0) { if (snd_config_get_string(n, &val) < 0) return -EINVAL; diff --git a/src/topology/elem.c b/src/topology/elem.c index 95e3fd4..50414f0 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -196,6 +196,10 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, case SND_TPLG_TYPE_TOKEN: list_add_tail(&elem->list, &tplg->token_list); break; + case SND_TPLG_TYPE_TUPLE: + list_add_tail(&elem->list, &tplg->tuple_list); + elem->free = tplg_free_tuples; + break; default: free(elem); return NULL; diff --git a/src/topology/parser.c b/src/topology/parser.c index 264abc8..0d967b4 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -181,6 +181,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) continue; } + if (strcmp(id, "SectionVendorTuples") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_tuples, + NULL); + if (err < 0) + return err; + continue; + } + SNDERR("error: unknown section %s\n", id); } return 0; @@ -416,6 +424,7 @@ snd_tplg_t *snd_tplg_new(void) INIT_LIST_HEAD(&tplg->enum_list); INIT_LIST_HEAD(&tplg->bytes_ext_list); INIT_LIST_HEAD(&tplg->token_list); + INIT_LIST_HEAD(&tplg->tuple_list); return tplg; } @@ -436,6 +445,7 @@ void snd_tplg_free(snd_tplg_t *tplg) tplg_elem_free_list(&tplg->enum_list); tplg_elem_free_list(&tplg->bytes_ext_list); tplg_elem_free_list(&tplg->token_list); + tplg_elem_free_list(&tplg->tuple_list); free(tplg); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 679bfff..4d59a1f 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -70,6 +70,7 @@ struct snd_tplg { struct list_head text_list; struct list_head pdata_list; struct list_head token_list; + struct list_head tuple_list; struct list_head pcm_config_list; struct list_head pcm_caps_list; @@ -97,6 +98,28 @@ struct tplg_vendor_tokens { unsigned int num_tokens; struct tplg_token token[0]; }; + +/* element for vendor tuples */ +struct tplg_tuple { + char token[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + union { + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char uuid[16]; + unsigned int value; + }; +}; + +struct tplg_tuple_set { + unsigned int type; /* uuid, bool, byte, short, word, string*/ + unsigned int num_tuples; + struct tplg_tuple tuple[0]; +}; + +struct tplg_vendor_tuples { + unsigned int num_sets; + struct tplg_tuple_set **set; +}; + /* topology element */ struct tplg_elem { @@ -130,6 +153,7 @@ struct tplg_elem { struct snd_soc_tplg_ctl_tlv *tlv; struct snd_soc_tplg_private *data; struct tplg_vendor_tokens *tokens; + struct tplg_vendor_tuples *tuples; }; /* an element may refer to other elements: @@ -166,6 +190,11 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED); + +void tplg_free_tuples(void *obj); + int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); -- 2.5.5 From 0c5e5c1801e5b1b55e46144ee29ca7a71f2812b0 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 7 Apr 2016 15:29:49 +0800 Subject: [PATCH 09/34] topology: Build data objects with tuples For data objects with tuples, the parser will bind the vendor tuples and tokens, copy the tuples to the private buffer of its parent data object. Then later the builder will export the vendor tuples as private binary data for the control or widgets objects. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/data.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++ src/topology/parser.c | 4 + src/topology/tplg_local.h | 1 + 3 files changed, 218 insertions(+) diff --git a/src/topology/data.c b/src/topology/data.c index 606fcd3..19c31bf 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,198 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; } +/* get the token integer value from its id */ +static int get_token_value(const char *token_id, + struct tplg_vendor_tokens *tokens) +{ + int i; + + for (i = 0; i < tokens->num_tokens; i++) { + if (strcmp(token_id, tokens->token[i].id) == 0) + return tokens->token[i].value; + } + + SNDERR("error: cannot find token id '%s'\n", token_id); + return -1; +} + +/* get the vendor tokens referred by the vendor tuples */ +static struct tplg_elem *get_tokens(snd_tplg_t *tplg, struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + int err = 0; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + + if (!ref->id || ref->type != SND_TPLG_TYPE_TOKEN) + continue; + + if (!ref->elem) { + ref->elem = tplg_elem_lookup(&tplg->token_list, + ref->id, SND_TPLG_TYPE_TOKEN); + } + + return ref->elem; + } + + return NULL; +} + +/* check if a data element has tuples */ +static bool has_tuples(struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + int err = 0; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + if (ref->id && ref->type == SND_TPLG_TYPE_TUPLE) + return true; + } + + return false; +} + +/* get size of a tuple element from its type */ +static unsigned int get_tuple_size(int type) +{ + switch (type) { + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + return sizeof(struct snd_soc_tplg_vendor_uuid_elem); + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + return sizeof(struct snd_soc_tplg_vendor_string_elem); + + default: + return sizeof(struct snd_soc_tplg_vendor_value_elem); + } +} + +/* fill a data element's private buffer with its tuples */ +static int copy_tuples(struct tplg_elem *elem, + struct tplg_vendor_tuples *tuples, struct tplg_vendor_tokens *tokens) +{ + struct snd_soc_tplg_private *priv = elem->data; + struct tplg_tuple_set *tuple_set; + struct tplg_tuple *tuple; + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_uuid_elem *uuid; + struct snd_soc_tplg_vendor_string_elem *string; + struct snd_soc_tplg_vendor_value_elem *value; + int set_size, size, off; + int i, j, token_val; + + if (priv) { + SNDERR("error: %s has more data than tuples\n", elem->id); + return -EINVAL; + } + + size = 0; + for (i = 0; i < tuples->num_sets ; i++) { + tuple_set = tuples->set[i]; + set_size = sizeof(struct snd_soc_tplg_vendor_array) + + get_tuple_size(tuple_set->type) + * tuple_set->num_tuples; + size += set_size; + if (size > TPLG_MAX_PRIV_SIZE) { + SNDERR("error: data too big %d\n", size); + return -EINVAL; + } + + if (priv != NULL) + priv = realloc(priv, sizeof(*priv) + size); + else + priv = calloc(1, sizeof(*priv) + size); + if (!priv) + return -ENOMEM; + + off = priv->size; + priv->size = size; + + array = (struct snd_soc_tplg_vendor_array *)(priv->data + off); + array->size = set_size; + array->type = tuple_set->type; + array->num_elems = tuple_set->num_tuples; + + /* fill the private data buffer */ + for (j = 0; j < tuple_set->num_tuples; j++) { + tuple = &tuple_set->tuple[j]; + token_val = get_token_value(tuple->token, tokens); + if (token_val < 0) + return -EINVAL; + + switch (tuple_set->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + uuid = &array->uuid[j]; + uuid->token = token_val; + memcpy(uuid->uuid, tuple->uuid, 16); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + string = &array->string[j]; + string->token = token_val; + elem_copy_text(string->string, tuple->string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + + default: + value = &array->value[j]; + value->token = token_val; + value->value = tuple->value; + break; + } + } + } + + elem->data = priv; + return 0; +} + +/* build a data element from its tuples */ +static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + struct tplg_elem *tuples, *tokens; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + + if (!ref->id || ref->type != SND_TPLG_TYPE_TUPLE) + continue; + + tplg_dbg("look up tuples %s\n", ref->id); + + if (!ref->elem) + ref->elem = tplg_elem_lookup(&tplg->tuple_list, + ref->id, SND_TPLG_TYPE_TUPLE); + tuples = ref->elem; + if (!tuples) + return -EINVAL; + + tplg_dbg("found tuples %s\n", tuples->id); + tokens = get_tokens(tplg, tuples); + if (!tokens) + return -EINVAL; + + tplg_dbg("found tokens %s\n", tokens->id); + /* a data object can only have one tuples object */ + return copy_tuples(elem, tuples->tuples, tokens->tokens); + } + + return 0; +} + static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_tuple_set **s) { @@ -683,3 +875,24 @@ int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref) memcpy(priv->data, ref->data->data, priv_data_size); return 0; } + +/* check data objects and build those with tuples */ +int tplg_build_data(snd_tplg_t *tplg) +{ + struct list_head *base, *pos; + struct tplg_elem *elem; + int err = 0; + + base = &tplg->pdata_list; + list_for_each(pos, base) { + + elem = list_entry(pos, struct tplg_elem, list); + if (has_tuples(elem)) { + err = build_tuples(tplg, elem); + if (err < 0) + return err; + } + } + + return 0; +} diff --git a/src/topology/parser.c b/src/topology/parser.c index 0d967b4..30d91f9 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -242,6 +242,10 @@ static int tplg_build_integ(snd_tplg_t *tplg) { int err; + err = tplg_build_data(tplg); + if (err < 0) + return err; + err = tplg_build_controls(tplg); if (err < 0) return err; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 4d59a1f..4c601d4 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -222,6 +222,7 @@ int tplg_parse_be(snd_tplg_t *tplg, int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +int tplg_build_data(snd_tplg_t *tplg); int tplg_build_controls(snd_tplg_t *tplg); int tplg_build_widgets(snd_tplg_t *tplg); int tplg_build_routes(snd_tplg_t *tplg); -- 2.5.5 From e57b521c61f0df14b660ce6ba8c5009a63f5b115 Mon Sep 17 00:00:00 2001 From: Hsin-Yu Chao Date: Wed, 13 Apr 2016 18:53:09 +0800 Subject: [PATCH 10/34] ucm: add cset-tlv This patch enables UCM to set a file in TLV format to kcontrol by: cset-tlv "name='' " This new 'cset-tlv' command will be used to write audio DSP to specific alsa control, where the driver expectes a file in TLV format. The TLV file to set to kcontrol will be checked first by file size not larger than 16 MB, and then examine if the length field reports correct number of bytes in the TLV file. Signed-off-by: Hsin-Yu Chao Reviewed-by: Vinod Koul Signed-off-by: Takashi Iwai --- src/ucm/main.c | 94 +++++++++++++++++++++++++++++++++++++++++++++-------- src/ucm/parser.c | 10 ++++++ src/ucm/ucm_local.h | 1 + 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index 7e44603..24d9510 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -161,6 +161,57 @@ static int open_ctl(snd_use_case_mgr_t *uc_mgr, return 0; } +static int read_tlv_file(unsigned int **res, + const char *filepath) +{ + int err = 0; + int fd; + struct stat st; + size_t sz; + ssize_t sz_read; + struct snd_ctl_tlv *tlv; + + fd = open(filepath, O_RDONLY); + if (fd < 0) { + err = -errno; + return err; + } + if (fstat(fd, &st) == -1) { + err = -errno; + goto __fail; + } + sz = st.st_size; + if (sz > 16 * 1024 * 1024 || sz < 8 || sz % 4) { + uc_error("File size should be less than 16 MB " + "and multiple of 4"); + err = -EINVAL; + goto __fail; + } + *res = malloc(sz); + if (res == NULL) { + err = -ENOMEM; + goto __fail; + } + sz_read = read(fd, *res, sz); + if (sz_read < 0 || (size_t)sz_read != sz) { + err = -EIO; + free(*res); + *res = NULL; + } + /* Check if the tlv file specifies valid size. */ + tlv = (struct snd_ctl_tlv *)(*res); + if (tlv->length + 2 * sizeof(unsigned int) != sz) { + uc_error("Invalid tlv size: %d", tlv->length); + err = -EINVAL; + free(*res); + *res = NULL; + } + +__fail: + close(fd); + return err; +} + static int binary_file_parse(snd_ctl_elem_value_t *dst, snd_ctl_elem_info_t *info, const char *filepath) @@ -226,6 +277,7 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *value; snd_ctl_elem_info_t *info; + unsigned int *res = NULL; snd_ctl_elem_id_malloc(&id); snd_ctl_elem_value_malloc(&value); @@ -241,23 +293,36 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) err = -EINVAL; goto __fail; } - snd_ctl_elem_value_set_id(value, id); snd_ctl_elem_info_set_id(info, id); - err = snd_ctl_elem_read(ctl, value); - if (err < 0) - goto __fail; err = snd_ctl_elem_info(ctl, info); if (err < 0) goto __fail; - if (type == SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE) - err = binary_file_parse(value, info, pos); - else - err = snd_ctl_ascii_value_parse(ctl, value, info, pos); - if (err < 0) - goto __fail; - err = snd_ctl_elem_write(ctl, value); - if (err < 0) - goto __fail; + if (type == SEQUENCE_ELEMENT_TYPE_CSET_TLV) { + if (!snd_ctl_elem_info_is_tlv_writable(info)) { + err = -EINVAL; + goto __fail; + } + err = read_tlv_file(&res, pos); + if (err < 0) + goto __fail; + err = snd_ctl_elem_tlv_write(ctl, id, res); + if (err < 0) + goto __fail; + } else { + snd_ctl_elem_value_set_id(value, id); + err = snd_ctl_elem_read(ctl, value); + if (err < 0) + goto __fail; + if (type == SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE) + err = binary_file_parse(value, info, pos); + else + err = snd_ctl_ascii_value_parse(ctl, value, info, pos); + if (err < 0) + goto __fail; + err = snd_ctl_elem_write(ctl, value); + if (err < 0) + goto __fail; + } err = 0; __fail: if (id != NULL) @@ -266,6 +331,8 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) free(value); if (info != NULL) free(info); + if (res != NULL) + free(res); return err; } @@ -298,6 +365,7 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr, break; case SEQUENCE_ELEMENT_TYPE_CSET: case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE: + case SEQUENCE_ELEMENT_TYPE_CSET_TLV: if (cdev == NULL) { char *playback_ctl = NULL; char *capture_ctl = NULL; diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 8405bd2..13f62d7 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -316,6 +316,16 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, continue; } + if (strcmp(cmd, "cset-tlv") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV; + err = parse_string(n, &curr->data.cset); + if (err < 0) { + uc_error("error: cset-tlv requires a string!"); + return err; + } + continue; + } + if (strcmp(cmd, "usleep") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP; err = snd_config_get_integer(n, &curr->data.sleep); diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 3a5d2c2..b89de2a 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -48,6 +48,7 @@ #define SEQUENCE_ELEMENT_TYPE_SLEEP 3 #define SEQUENCE_ELEMENT_TYPE_EXEC 4 #define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5 +#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6 struct ucm_value { struct list_head list; -- 2.5.5 From fdba9e1bad8f769a6137e565471f0227f23a3132 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Apr 2016 17:33:03 +0200 Subject: [PATCH 11/34] pcm: Fallback open as the first instance for dmix & co dmix and other PCM plugins tries to open a secondary stream with O_APPEND flag when the shmem was already attached by another. However, when another streams have been already closed after the shmem check, this open may return the error EBADFD, since the kernel accepts O_APPEND only for the secondary streams. This patch adds a workaround for such a case. It just retries opening the stream as the first instance (i.e. without O_APPEND flag). This is basically safe behavior (the kernel takes care of races), even we may do this even unconditionally. But it's bad from the performance POV, so we do it only when really needed. Reported-by: Lars Lindqvist Signed-off-by: Takashi Iwai --- src/pcm/pcm_dmix.c | 8 ++++++++ src/pcm/pcm_dshare.c | 8 ++++++++ src/pcm/pcm_dsnoop.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c index b26a5c7..007d356 100644 --- a/src/pcm/pcm_dmix.c +++ b/src/pcm/pcm_dmix.c @@ -1020,6 +1020,7 @@ int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name, dmix->max_periods = opts->max_periods; dmix->sync_ptr = snd_pcm_dmix_sync_ptr; + retry: if (first_instance) { /* recursion is already checked in snd_pcm_direct_get_slave_ipc_offset() */ @@ -1076,6 +1077,13 @@ int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name, SND_PCM_APPEND, NULL); if (ret < 0) { + /* all other streams have been closed; + * retry as the first instance + */ + if (ret == -EBADFD) { + first_instance = 1; + goto retry; + } SNDERR("unable to open slave"); goto _err; } diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index 58e47bb..adb3587 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -690,6 +690,7 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, break; } + retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -758,6 +759,13 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, SND_PCM_APPEND, NULL); if (ret < 0) { + /* all other streams have been closed; + * retry as the first instance + */ + if (ret == -EBADFD) { + first_instance = 1; + goto retry; + } SNDERR("unable to open slave"); goto _err; } diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index 576c35b..8ff0ba5 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -583,6 +583,7 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, break; } + retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -651,6 +652,13 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, SND_PCM_APPEND, NULL); if (ret < 0) { + /* all other streams have been closed; + * retry as the first instance + */ + if (ret == -EBADFD) { + first_instance = 1; + goto retry; + } SNDERR("unable to open slave"); goto _err; } -- 2.5.5 From f5c313eae5c26d6843a4f860743151f53b2f4041 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Thu, 28 Apr 2016 11:07:56 +0530 Subject: [PATCH 12/34] conf: topology: Add Skylake i2s conf The Skylake topology configuration for simple topology graph is provided. This exposes the PCM capabilities of the DSP. Signed-off-by: Shreyas NC Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- configure.ac | 1 + src/conf/topology/Makefile.am | 2 +- src/conf/topology/sklrt286/Makefile.am | 4 + src/conf/topology/sklrt286/codec0_in-cpr-1.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/codec0_in-mi.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/codec0_out-cpr-4.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/codec0_out-mo.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/codec1_out-cpr-5.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/codec1_out-mo.bin | Bin 0 -> 4244 bytes .../topology/sklrt286/dmic01_hifi_in-cpr-3.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/dmic01_hifi_in-mi.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi1_pt_out-cpr-7.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi1_pt_out-cpr-8.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi2_pt_out-cpr-10.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi2_pt_out-cpr-9.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi3_pt_out-cpr-11.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/hdmi3_pt_out-cpr-12.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/media0_in-cpr-0.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/media0_in-mi.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/media0_out-cpr-6.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/media0_out-mo.bin | Bin 0 -> 4244 bytes src/conf/topology/sklrt286/skl_i2s.conf | 342 +++++++++++++++++++++ 22 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 src/conf/topology/sklrt286/Makefile.am create mode 100644 src/conf/topology/sklrt286/codec0_in-cpr-1.bin create mode 100644 src/conf/topology/sklrt286/codec0_in-mi.bin create mode 100644 src/conf/topology/sklrt286/codec0_out-cpr-4.bin create mode 100644 src/conf/topology/sklrt286/codec0_out-mo.bin create mode 100644 src/conf/topology/sklrt286/codec1_out-cpr-5.bin create mode 100644 src/conf/topology/sklrt286/codec1_out-mo.bin create mode 100644 src/conf/topology/sklrt286/dmic01_hifi_in-cpr-3.bin create mode 100644 src/conf/topology/sklrt286/dmic01_hifi_in-mi.bin create mode 100644 src/conf/topology/sklrt286/hdmi1_pt_out-cpr-7.bin create mode 100644 src/conf/topology/sklrt286/hdmi1_pt_out-cpr-8.bin create mode 100644 src/conf/topology/sklrt286/hdmi2_pt_out-cpr-10.bin create mode 100644 src/conf/topology/sklrt286/hdmi2_pt_out-cpr-9.bin create mode 100644 src/conf/topology/sklrt286/hdmi3_pt_out-cpr-11.bin create mode 100644 src/conf/topology/sklrt286/hdmi3_pt_out-cpr-12.bin create mode 100644 src/conf/topology/sklrt286/media0_in-cpr-0.bin create mode 100644 src/conf/topology/sklrt286/media0_in-mi.bin create mode 100644 src/conf/topology/sklrt286/media0_out-cpr-6.bin create mode 100644 src/conf/topology/sklrt286/media0_out-mo.bin create mode 100644 src/conf/topology/sklrt286/skl_i2s.conf diff --git a/configure.ac b/configure.ac index c265ec9..1bf75e6 100644 --- a/configure.ac +++ b/configure.ac @@ -661,6 +661,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ src/conf/topology/Makefile \ src/conf/topology/broadwell/Makefile \ modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ + src/conf/topology/sklrt286/Makefile \ alsalisp/Makefile aserver/Makefile \ test/Makefile test/lsb/Makefile \ utils/Makefile utils/alsa-lib.spec utils/alsa.pc) diff --git a/src/conf/topology/Makefile.am b/src/conf/topology/Makefile.am index f56a96c..cbdb7cf 100644 --- a/src/conf/topology/Makefile.am +++ b/src/conf/topology/Makefile.am @@ -1 +1 @@ -SUBDIRS=broadwell +SUBDIRS=broadwell sklrt286 diff --git a/src/conf/topology/sklrt286/Makefile.am b/src/conf/topology/sklrt286/Makefile.am new file mode 100644 index 0000000..facc508 --- /dev/null +++ b/src/conf/topology/sklrt286/Makefile.am @@ -0,0 +1,4 @@ +alsaconfigdir = @ALSA_CONFIG_DIR@ +sklrt286dir = $(alsaconfigdir)/topology/sklrt286 +sklrt286_DATA = skl_i2s.conf media0_in-cpr-0.bin media0_in-mi.bin media0_out-mo.bin media0_out-cpr-6.bin codec0_out-mo.bin codec0_out-cpr-4.bin codec1_out-mo.bin codec1_out-cpr-5.bin codec0_in-cpr-1.bin codec0_in-mi.bin dmic01_hifi_in-cpr-3.bin dmic01_hifi_in-mi.bin hdmi1_pt_out-cpr-7.bin hdmi1_pt_out-cpr-8.bin hdmi2_pt_out-cpr-9.bin hdmi2_pt_out-cpr-10.bin hdmi3_pt_out-cpr-11.bin hdmi3_pt_out-cpr-12.bin +EXTRA_DIST = $(sklrt286_DATA) diff --git a/src/conf/topology/sklrt286/skl_i2s.conf b/src/conf/topology/sklrt286/skl_i2s.conf new file mode 100644 index 0000000..6da224f --- /dev/null +++ b/src/conf/topology/sklrt286/skl_i2s.conf @@ -0,0 +1,342 @@ +SectionData."media0_in cpr 0" { + file "sklrt286/media0_in-cpr-0.bin" +} +SectionData."media0_in mi" { + file "sklrt286/media0_in-mi.bin" +} +SectionData."media0_out mo" { + file "sklrt286/media0_out-mo.bin" +} +SectionData."media0_out cpr 6" { + file "sklrt286/media0_out-cpr-6.bin" +} +SectionData."codec0_out mo" { + file "sklrt286/codec0_out-mo.bin" +} +SectionData."codec0_out cpr 4" { + file "sklrt286/codec0_out-cpr-4.bin" +} +SectionData."codec1_out mo" { + file "sklrt286/codec1_out-mo.bin" +} +SectionData."codec1_out cpr 5" { + file "sklrt286/codec1_out-cpr-5.bin" +} +SectionData."codec0_in cpr 1" { + file "sklrt286/codec0_in-cpr-1.bin" +} +SectionData."codec0_in mi" { + file "sklrt286/codec0_in-mi.bin" +} +SectionData."dmic01_hifi_in cpr 3" { + file "sklrt286/dmic01_hifi_in-cpr-3.bin" +} +SectionData."dmic01_hifi_in mi" { + file "sklrt286/dmic01_hifi_in-mi.bin" +} +SectionData."hdmi1_pt_out cpr 7" { + file "sklrt286/hdmi1_pt_out-cpr-7.bin" +} +SectionData."hdmi1_pt_out cpr 8" { + file "sklrt286/hdmi1_pt_out-cpr-8.bin" +} +SectionData."hdmi2_pt_out cpr 9" { + file "sklrt286/hdmi2_pt_out-cpr-9.bin" +} +SectionData."hdmi2_pt_out cpr 10" { + file "sklrt286/hdmi2_pt_out-cpr-10.bin" +} +SectionData."hdmi3_pt_out cpr 11" { + file "sklrt286/hdmi3_pt_out-cpr-11.bin" +} +SectionData."hdmi3_pt_out cpr 12" { + file "sklrt286/hdmi3_pt_out-cpr-12.bin" +} + +SectionControlMixer."media0_in mi Switch" { + index"1" + invert "false" + max "1" + min"0" + no_pm "true" + channel."fl" { + reg "-1" + shift "0" + } + channel."fr" { + reg "-1" + shift "0" + } + ops."ctl" { + get "64" + put "64" + info "64" + } +} +SectionControlMixer."codec0_in mi Switch" { + index"1" + invert "false" + max "1" + min"0" + no_pm "true" + channel."fl" { + reg "-1" + shift "0" + } + channel."fr" { + reg "-1" + shift "0" + } + ops."ctl" { + get "64" + put "64" + info "64" + } +} +SectionControlMixer."dmic01_hifi_in mi Switch" { + index"1" + invert "false" + max "1" + min"0" + no_pm "true" + channel."fl" { + reg "-1" + shift "0" + } + channel."fr" { + reg "-1" + shift "0" + } + ops."ctl" { + get "64" + put "64" + info "64" + } +} + + +SectionWidget."media0_in cpr 0" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "media0_in cpr 0" +} +SectionWidget."media0_in mi" { + index"1" + type"pga" + no_pm "true" + event_type "4" + event_flags "9" + subseq "10" + data "media0_in mi" +} +SectionWidget."media0_out mo" { + index"1" + type"mixer" + no_pm "true" + event_type "1" + event_flags "15" + subseq "10" + data "media0_out mo" + mixer [ + "media0_in mi Switch" + "codec0_in mi Switch" + "dmic01_hifi_in mi Switch" + ] +} +SectionWidget."media0_out cpr 6" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "media0_out cpr 6" +} +SectionWidget."codec0_out mo" { + index"1" + type"mixer" + no_pm "true" + event_type "1" + event_flags "15" + subseq "10" + data "codec0_out mo" + mixer [ + "media0_in mi Switch" + "codec0_in mi Switch" + "dmic01_hifi_in mi Switch" + ] +} +SectionWidget."codec0_out cpr 4" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "codec0_out cpr 4" +} +SectionWidget."codec0_out" { + index"1" + type"aif_out" + no_pm "true" +} +SectionWidget."codec1_out mo" { + index"1" + type"mixer" + no_pm "true" + event_type "1" + event_flags "15" + subseq "10" + data "codec1_out mo" + mixer [ + "media0_in mi Switch" + "codec0_in mi Switch" + "dmic01_hifi_in mi Switch" + ] +} +SectionWidget."codec1_out cpr 5" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "codec1_out cpr 5" +} +SectionWidget."codec1_out" { + index"1" + type"aif_out" + no_pm "true" +} +SectionWidget."codec0_in cpr 1" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "codec0_in cpr 1" +} +SectionWidget."codec0_in mi" { + index"1" + type"pga" + no_pm "true" + event_type "4" + event_flags "9" + subseq "10" + data "codec0_in mi" +} +SectionWidget."codec0_in" { + index"1" + type"aif_in" + no_pm "true" +} +SectionWidget."dmic01_hifi_in cpr 3" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "dmic01_hifi_in cpr 3" +} +SectionWidget."dmic01_hifi_in mi" { + index"1" + type"pga" + no_pm "true" + event_type "4" + event_flags "9" + subseq "10" + data "dmic01_hifi_in mi" +} +SectionWidget."dmic01_hifi" { + index"1" + type"aif_in" + no_pm "true" +} +SectionWidget."hdmi1_pt_out cpr 7" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "hdmi1_pt_out cpr 7" +} +SectionWidget."hdmi1_pt_out cpr 8" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "hdmi1_pt_out cpr 8" +} +SectionWidget."iDisp1_out" { + index"1" + type"aif_out" + no_pm "true" +} +SectionWidget."hdmi2_pt_out cpr 9" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "hdmi2_pt_out cpr 9" +} +SectionWidget."hdmi2_pt_out cpr 10" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "hdmi2_pt_out cpr 10" +} +SectionWidget."iDisp2_out" { + index"1" + type"aif_out" + no_pm "true" +} +SectionWidget."hdmi3_pt_out cpr 11" { + index"1" + type"mixer" + no_pm "true" + event_type "3" + event_flags "9" + data "hdmi3_pt_out cpr 11" +} +SectionWidget."hdmi3_pt_out cpr 12" { + index"1" + type"pga" + no_pm "true" + event_type "4" + data "hdmi3_pt_out cpr 12" +} +SectionGraph."Pipeline 1 Graph" { + index"1" + lines [ + "media0_in mi, , media0_in cpr 0" + "media0_in cpr 0, , System Playback" + "media0_out mo, media0_in mi Switch, media0_in mi" + "media0_out mo, codec0_in mi Switch, codec0_in mi" + "media0_out mo, dmic01_hifi_in mi Switch, dmic01_hifi_in mi" + "media0_out cpr 6, , media0_out mo" + "System Capture, , media0_out cpr 6" + "codec0_out mo, media0_in mi Switch, media0_in mi" + "codec0_out mo, codec0_in mi Switch, codec0_in mi" + "codec0_out mo, dmic01_hifi_in mi Switch, dmic01_hifi_in mi" + "codec0_out cpr 4, , codec0_out mo" + "codec0_out, , codec0_out cpr 4" + "codec1_out mo, media0_in mi Switch, media0_in mi" + "codec1_out mo, codec0_in mi Switch, codec0_in mi" + "codec1_out mo, dmic01_hifi_in mi Switch, dmic01_hifi_in mi" + "codec1_out cpr 5, , codec1_out mo" + "codec1_out, , codec1_out cpr 5" + "codec0_in mi, , codec0_in cpr 1" + "codec0_in cpr 1, , codec0_in" + "dmic01_hifi_in mi, , dmic01_hifi_in cpr 3" + "dmic01_hifi_in cpr 3, , dmic01_hifi" + "hdmi1_pt_out cpr 8, , hdmi1_pt_out cpr 7" + "hdmi1_pt_out cpr 7, , HDMI1 Playback" + "iDisp1_out, , hdmi1_pt_out cpr 8" + "hdmi2_pt_out cpr 10, , hdmi2_pt_out cpr 9" + "hdmi2_pt_out cpr 9, , HDMI2 Playback" + "iDisp2_out, , hdmi2_pt_out cpr 10" + "hdmi3_pt_out cpr 12, , hdmi3_pt_out cpr 11" + "hdmi3_pt_out cpr 11, , HDMI3 Playback" + "iDisp1_out, , hdmi3_pt_out cpr 12" + ] +} + -- 2.5.5 From a8ca6d1c4b942616dad43d51d954c31894e3c608 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Thu, 28 Apr 2016 11:07:57 +0530 Subject: [PATCH 13/34] Add u8 in type_compat.h Skylake headers use u8 data types which were not present in type_compat so add them. Signed-off-by: Shreyas NC Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/type_compat.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/type_compat.h b/include/sound/type_compat.h index eec86e4..e973ff3 100644 --- a/include/sound/type_compat.h +++ b/include/sound/type_compat.h @@ -32,9 +32,11 @@ typedef int32_t __s32; #define __le64 __u64 #define __le32 __u32 #define __le16 __u16 +#define __le8 __u8 #define __be64 __u64 #define __be32 __u32 #define __be16 __u16 +#define __be8 __u8 #endif /* DOC_HIDDEN */ #endif /* __TYPE_COMPAT_H */ -- 2.5.5 From 65b271034a36580badc980b3ff0bd77e7b9837ce Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Thu, 28 Apr 2016 11:07:58 +0530 Subject: [PATCH 14/34] conf: topology: Generate Private data binary blobs The DSP modules need private data and that is provided as binary blob. These blobs are compiled from C structures which specify module configuration. Signed-off-by: Shreyas NC Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- configure.ac | 1 + src/conf/topology/sklrt286/Makefile.am | 1 + src/conf/topology/sklrt286/data/Makefile.am | 4 + src/conf/topology/sklrt286/data/pvt.c | 1815 ++++++++++++++++++++ src/conf/topology/sklrt286/data/pvt_data.c | 90 + src/conf/topology/sklrt286/data/pvt_local.h | 9 + .../topology/sklrt286/data/skl-tplg-interface.h | 232 +++ 7 files changed, 2152 insertions(+) create mode 100644 src/conf/topology/sklrt286/data/Makefile.am create mode 100644 src/conf/topology/sklrt286/data/pvt.c create mode 100644 src/conf/topology/sklrt286/data/pvt_data.c create mode 100644 src/conf/topology/sklrt286/data/pvt_local.h create mode 100644 src/conf/topology/sklrt286/data/skl-tplg-interface.h diff --git a/configure.ac b/configure.ac index 1bf75e6..28fcd24 100644 --- a/configure.ac +++ b/configure.ac @@ -661,6 +661,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ src/conf/topology/Makefile \ src/conf/topology/broadwell/Makefile \ modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ + src/conf/topology/sklrt286/data/Makefile \ src/conf/topology/sklrt286/Makefile \ alsalisp/Makefile aserver/Makefile \ test/Makefile test/lsb/Makefile \ diff --git a/src/conf/topology/sklrt286/Makefile.am b/src/conf/topology/sklrt286/Makefile.am index facc508..ed58b77 100644 --- a/src/conf/topology/sklrt286/Makefile.am +++ b/src/conf/topology/sklrt286/Makefile.am @@ -1,4 +1,5 @@ alsaconfigdir = @ALSA_CONFIG_DIR@ +SUBDIRS = data sklrt286dir = $(alsaconfigdir)/topology/sklrt286 sklrt286_DATA = skl_i2s.conf media0_in-cpr-0.bin media0_in-mi.bin media0_out-mo.bin media0_out-cpr-6.bin codec0_out-mo.bin codec0_out-cpr-4.bin codec1_out-mo.bin codec1_out-cpr-5.bin codec0_in-cpr-1.bin codec0_in-mi.bin dmic01_hifi_in-cpr-3.bin dmic01_hifi_in-mi.bin hdmi1_pt_out-cpr-7.bin hdmi1_pt_out-cpr-8.bin hdmi2_pt_out-cpr-9.bin hdmi2_pt_out-cpr-10.bin hdmi3_pt_out-cpr-11.bin hdmi3_pt_out-cpr-12.bin EXTRA_DIST = $(sklrt286_DATA) diff --git a/src/conf/topology/sklrt286/data/Makefile.am b/src/conf/topology/sklrt286/data/Makefile.am new file mode 100644 index 0000000..888ce16 --- /dev/null +++ b/src/conf/topology/sklrt286/data/Makefile.am @@ -0,0 +1,4 @@ +noinst_PROGRAMS = pvt_data +pvt_data_SOURCES = pvt_data.c +AM_CPPFLAGS = \ + -Wall -I$(top_srcdir)/include diff --git a/src/conf/topology/sklrt286/data/pvt.c b/src/conf/topology/sklrt286/data/pvt.c new file mode 100644 index 0000000..3447e3e --- /dev/null +++ b/src/conf/topology/sklrt286/data/pvt.c @@ -0,0 +1,1815 @@ +/* +* Copyright(c) 2014-2016 Intel Corporation +* All rights reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. + +* This library 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. +* +* Authors: Shreyas Nc +* +*/ +#include "pvt_local.h" + +struct skl_dfw_module_mod dfw_wrap[] = { +{ +.name = "media0_in cpr 0", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 0, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 5, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 1, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "media0_in mi", +.skl_dfw_mod = { + .uuid = {178, 110, 101, 57, 113, 59, 73, 64, 141, 63, 249, 44, 213, 196, 60, 9}, + .module_id = 1, + .instance_id = 0, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 1, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "media0_out mo", +.skl_dfw_mod = { + .uuid = {90, 80, 86, 60, 215, 36, 143, 65, 189, 220, 193, 245, 163, 172, 42, 224}, + .module_id = 2, + .instance_id = 2, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 8, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 2, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "media0_out cpr 6", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 6, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 0, + .dev_type = 5, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 2, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec0_out mo", +.skl_dfw_mod = { + .uuid = {90, 80, 86, 60, 215, 36, 143, 65, 189, 220, 193, 245, 163, 172, 42, 224}, + .module_id = 2, + .instance_id = 0, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 8, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 3, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x4, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec0_out cpr 4", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 4, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = 0, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 2, + .dev_type = 2, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 3, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x4, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec1_out mo", +.skl_dfw_mod = { + .uuid = {90, 80, 86, 60, 215, 36, 143, 65, 189, 220, 193, 245, 163, 172, 42, 224}, + .module_id = 2, + .instance_id = 1, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 8, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 4, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec1_out cpr 5", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 5, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = 0, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 2, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 2, + .dev_type = 2, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 4, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec0_in cpr 1", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 1, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = 0, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 2, + .dev_type = 2, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 5, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "codec0_in mi", +.skl_dfw_mod = { + .uuid = {178, 110, 101, 57, 113, 59, 73, 64, 141, 63, 249, 44, 213, 196, 60, 9}, + .module_id = 1, + .instance_id = 1, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 5, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "dmic01_hifi_in cpr 3", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 3, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = 0, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 2, + .dev_type = 1, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 4, + .converter = 4, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 6, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "dmic01_hifi_in mi", +.skl_dfw_mod = { + .uuid = {178, 110, 101, 57, 113, 59, 73, 64, 141, 63, 249, 44, 213, 196, 60, 9}, + .module_id = 1, + .instance_id = 3, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 1, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 0, + .conn_type = 0, + .dev_type = 6, + .hw_conn_type = 2, + .rsvd2 = 0, + .params_fixup = 0, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 6, + .pipe_priority = 0, + .conn_type = 2, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi1_pt_out cpr 7", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 7, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 5, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 7, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi1_pt_out cpr 8", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 8, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 4, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 7, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi2_pt_out cpr 9", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 9, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 5, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 8, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi2_pt_out cpr 10", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 10, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 4, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 8, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi3_pt_out cpr 11", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 11, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 5, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 9, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 32, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, +{ +.name = "hdmi3_pt_out cpr 12", +.skl_dfw_mod = { + .uuid = {131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218}, + .module_id = 3, + .instance_id = 12, + .max_mcps = 0x186a0, + .mem_pages = 0x1, + .obs = 384, + .ibs = 384, + .vbus_id = -1, + .max_in_queue = 1, + .max_out_queue = 2, + .time_slot = 0, + .core_id = 0, + .rsvd1 = 0, + .module_type = 1, + .conn_type = 1, + .dev_type = 4, + .hw_conn_type = 1, + .rsvd2 = 0, + .params_fixup = 7, + .converter = 0, + .input_pin_type = 0, + .output_pin_type = 0, + .is_dynamic_in_pin = 1, + .is_dynamic_out_pin = 1, + .is_loadable = 0, + .rsvd3 = 0, + .pipe = { + .pipe_id = 9, + .pipe_priority = 0, + .conn_type = 1, + .rsvd = 0, + .memory_pages = 0x2, + }, + .in_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .out_fmt = { + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + { + .channels = 2, + .freq = 48000, + .bit_depth = 32, + .valid_bit_depth = 24, + .ch_cfg = 1, + .interleaving_style = 0, + .sample_type = 0, + .ch_map = 0xffffff10, + }, + }, + .in_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + }, + .out_pin = { + { + .module_id = 0, + .instance_id = 0, + }, + { + .module_id = 0, + .instance_id = 0, + }, + }, + }, +}, + }; diff --git a/src/conf/topology/sklrt286/data/pvt_data.c b/src/conf/topology/sklrt286/data/pvt_data.c new file mode 100644 index 0000000..dd55c3a --- /dev/null +++ b/src/conf/topology/sklrt286/data/pvt_data.c @@ -0,0 +1,90 @@ +/* + * Copyright(c) 2014-2016 Intel Corporation + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * Authors: Shreyas Nc + * + */ +#include "pvt.c" +#include "stdio.h" +#include "fcntl.h" +#include +#include +#include +#include +#include "global.h" +#include "list.h" + +#include +#include + +int replace_space(char *path, char *newpath) +{ + char buffer[52]; + char *p; + + strcpy(buffer, path); + + while ((p = strchr(buffer, ' '))) + p[0] = '-'; + + strcpy(newpath, buffer); + return 0; +} + +/* + * The private data structures are written into a + * binary blob. These contain module private data + * information + */ +int main(void) +{ + unsigned int i; + FILE *fd; + char path[128]; + char new_path[128]; + struct snd_soc_tplg_private *priv = NULL; + + memset(path, 0, sizeof(path)); + memset(new_path, 0, sizeof(new_path)); + + priv = calloc(1, sizeof(dfw_wrap) + sizeof(uint32_t)); + + for (i = 0; i < ARRAY_SIZE(dfw_wrap); i++) { + strcat(path, "../"); + strcat(path, dfw_wrap[i].name); + strcat(path, ".bin"); + + replace_space(path, new_path); + + priv->size = (uint32_t)sizeof(dfw_wrap[i].skl_dfw_mod); + + memcpy(priv->data, &dfw_wrap[i].skl_dfw_mod, + priv->size); + + fd = fopen(new_path, "wb"); + + if (fd == NULL) + return -ENOENT; + + if (fwrite(priv->data, priv->size, 1, fd) != 1) { + fclose(fd); + return -1; + } + + memset(path, 0, sizeof(path)); + } + + free(priv); + return 0; +} diff --git a/src/conf/topology/sklrt286/data/pvt_local.h b/src/conf/topology/sklrt286/data/pvt_local.h new file mode 100644 index 0000000..5edf7bd --- /dev/null +++ b/src/conf/topology/sklrt286/data/pvt_local.h @@ -0,0 +1,9 @@ +#include +#include "skl-tplg-interface.h" + +struct skl_dfw_module_mod { + char name[100]; + struct skl_dfw_module skl_dfw_mod; +}; + + diff --git a/src/conf/topology/sklrt286/data/skl-tplg-interface.h b/src/conf/topology/sklrt286/data/skl-tplg-interface.h new file mode 100644 index 0000000..e7389bc --- /dev/null +++ b/src/conf/topology/sklrt286/data/skl-tplg-interface.h @@ -0,0 +1,232 @@ +/* + * skl-tplg-interface.h - Intel DSP FW private data interface + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP + * Nilofer, Samreen + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#ifndef __HDA_TPLG_INTERFACE_H__ +#define __HDA_TPLG_INTERFACE_H__ + +#include +/* + * Default types range from 0~12. type can range from 0 to 0xff + * SST types start at higher to avoid any overlapping in future + */ +#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 + +#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +#define MAX_IN_QUEUE 8 +#define MAX_OUT_QUEUE 8 + +#define SKL_UUID_STR_SZ 40 +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum skl_event_types { + SKL_EVENT_NONE = 0, + SKL_MIXER_EVENT, + SKL_MUX_EVENT, + SKL_VMIXER_EVENT, + SKL_PGA_EVENT +}; + +/** + * enum skl_ch_cfg - channel configuration + * + * @SKL_CH_CFG_MONO: One channel only + * @SKL_CH_CFG_STEREO: L & R + * @SKL_CH_CFG_2_1: L, R & LFE + * @SKL_CH_CFG_3_0: L, C & R + * @SKL_CH_CFG_3_1: L, C, R & LFE + * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs + * @SKL_CH_CFG_4_0: L, C, R & Cs + * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs + * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE + * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two + * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] + * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] + * @SKL_CH_CFG_INVALID: Invalid + */ +enum skl_ch_cfg { + SKL_CH_CFG_MONO = 0, + SKL_CH_CFG_STEREO = 1, + SKL_CH_CFG_2_1 = 2, + SKL_CH_CFG_3_0 = 3, + SKL_CH_CFG_3_1 = 4, + SKL_CH_CFG_QUATRO = 5, + SKL_CH_CFG_4_0 = 6, + SKL_CH_CFG_5_0 = 7, + SKL_CH_CFG_5_1 = 8, + SKL_CH_CFG_DUAL_MONO = 9, + SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, + SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, + SKL_CH_CFG_4_CHANNEL = 12, + SKL_CH_CFG_INVALID +}; + +enum skl_module_type { + SKL_MODULE_TYPE_MIXER = 0, + SKL_MODULE_TYPE_COPIER, + SKL_MODULE_TYPE_UPDWMIX, + SKL_MODULE_TYPE_SRCINT, + SKL_MODULE_TYPE_ALGO, + SKL_MODULE_TYPE_BASE_OUTFMT +}; + +enum skl_core_affinity { + SKL_AFFINITY_CORE_0 = 0, + SKL_AFFINITY_CORE_1, + SKL_AFFINITY_CORE_MAX +}; + +enum skl_pipe_conn_type { + SKL_PIPE_CONN_TYPE_NONE = 0, + SKL_PIPE_CONN_TYPE_FE, + SKL_PIPE_CONN_TYPE_BE +}; + +enum skl_hw_conn_type { + SKL_CONN_NONE = 0, + SKL_CONN_SOURCE = 1, + SKL_CONN_SINK = 2 +}; + +enum skl_dev_type { + SKL_DEVICE_BT = 0x0, + SKL_DEVICE_DMIC = 0x1, + SKL_DEVICE_I2S = 0x2, + SKL_DEVICE_SLIMBUS = 0x3, + SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_HDAHOST = 0x5, + SKL_DEVICE_NONE +}; + +/** + * enum skl_interleaving - interleaving style + * + * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] + * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] + */ +enum skl_interleaving { + SKL_INTERLEAVING_PER_CHANNEL = 0, + SKL_INTERLEAVING_PER_SAMPLE = 1, +}; + +enum skl_sample_type { + SKL_SAMPLE_TYPE_INT_MSB = 0, + SKL_SAMPLE_TYPE_INT_LSB = 1, + SKL_SAMPLE_TYPE_INT_SIGNED = 2, + SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, + SKL_SAMPLE_TYPE_FLOAT = 4 +}; + +enum module_pin_type { + /* All pins of the module takes same PCM inputs or outputs + * e.g. mixout + */ + SKL_PIN_TYPE_HOMOGENEOUS, + /* All pins of the module takes different PCM inputs or outputs + * e.g mux + */ + SKL_PIN_TYPE_HETEROGENEOUS, +}; + +enum skl_module_param_type { + SKL_PARAM_DEFAULT = 0, + SKL_PARAM_INIT, + SKL_PARAM_SET, + SKL_PARAM_BIND +}; + +struct skl_dfw_module_pin { + __le16 module_id; + __le16 instance_id; +} __attribute__((packed)); + +struct skl_dfw_module_fmt { + __le32 channels; + __le32 freq; + __le32 bit_depth; + __le32 valid_bit_depth; + __le32 ch_cfg; + __le32 interleaving_style; + __le32 sample_type; + __le32 ch_map; +} __attribute__((packed)); + +struct skl_dfw_module_caps { + __le32 set_params:2; + __le32 rsvd:30; + __le32 param_id; + __le32 caps_size; + __le32 caps[HDA_SST_CFG_MAX]; +}; + +struct skl_dfw_pipe { + __le8 pipe_id; + __le8 pipe_priority; + __le16 conn_type:4; + __le16 rsvd:4; + __le16 memory_pages:8; +} __attribute__((packed)); + +struct skl_dfw_module { + __le8 uuid[16]; + + __le16 module_id; + __le16 instance_id; + __le32 max_mcps; + __le32 mem_pages; + __le32 obs; + __le32 ibs; + __le32 vbus_id; + + __le32 max_in_queue:8; + __le32 max_out_queue:8; + __le32 time_slot:8; + __le32 core_id:4; + __le32 rsvd1:4; + + __le32 module_type:8; + __le32 conn_type:4; + __le32 dev_type:4; + __le32 hw_conn_type:4; + __le32 rsvd2:12; + + __le32 params_fixup:8; + __le32 converter:8; + __le32 input_pin_type:1; + __le32 output_pin_type:1; + __le32 is_dynamic_in_pin:1; + __le32 is_dynamic_out_pin:1; + __le32 is_loadable:1; + __le32 rsvd3:11; + + struct skl_dfw_pipe pipe; + struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; + struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; + struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; + struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; + struct skl_dfw_module_caps caps; +} __attribute__((packed)); + +struct skl_dfw_algo_data { + __le32 set_params:2; + __le32 rsvd:30; + __le32 param_id; + __le32 max; + char params[0]; +} __attribute__((packed)); + +#endif -- 2.5.5 From e64334df2b2fe6756539178d89133a0b56e83c9d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:02:57 +0800 Subject: [PATCH 16/34] topology: Set manifest size for ABI The topology kernel driver will check the size of manifest struct, and will stop loading topology info if size mismatch is detected. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/topology/parser.c b/src/topology/parser.c index 30d91f9..84117c3 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -414,6 +414,8 @@ snd_tplg_t *snd_tplg_new(void) if (!tplg) return NULL; + tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest); + INIT_LIST_HEAD(&tplg->tlv_list); INIT_LIST_HEAD(&tplg->widget_list); INIT_LIST_HEAD(&tplg->pcm_list); -- 2.5.5 From 86ec8b49008fbcfa45756f7ab1803392c3bb464e Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:03:04 +0800 Subject: [PATCH 17/34] topology: Refactor functions to parse and build streams Previously these functions are only used by pcm elements (front-end DAI & DAI link) to parse stream capablities. Now refactor them to be reused by back-end DAI elements later. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/parser.c | 2 +- src/topology/pcm.c | 60 +++++++++++++++++++++++++++-------------------- src/topology/tplg_local.h | 2 +- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/topology/parser.c b/src/topology/parser.c index 84117c3..f6fc944 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -119,7 +119,7 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) if (strcmp(id, "SectionPCMCapabilities") == 0) { err = tplg_parse_compound(tplg, n, - tplg_parse_pcm_caps, NULL); + tplg_parse_stream_caps, NULL); if (err < 0) return err; continue; diff --git a/src/topology/pcm.c b/src/topology/pcm.c index d75aad8..1df4f54 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -40,9 +40,9 @@ struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id) return NULL; } -/* copy referenced caps to the pcm */ -static void copy_pcm_caps(const char *id, struct snd_soc_tplg_stream_caps *caps, - struct tplg_elem *ref_elem) +/* copy referenced caps to the parent (pcm or be dai) */ +static void copy_stream_caps(const char *id, + struct snd_soc_tplg_stream_caps *caps, struct tplg_elem *ref_elem) { struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps; @@ -52,24 +52,19 @@ static void copy_pcm_caps(const char *id, struct snd_soc_tplg_stream_caps *caps, *caps = *ref_caps; } -/* check referenced config and caps for a pcm */ -static int tplg_build_pcm_caps(snd_tplg_t *tplg, struct tplg_elem *elem) +/* find and copy the referenced stream caps */ +static int tplg_build_stream_caps(snd_tplg_t *tplg, + const char *id, struct snd_soc_tplg_stream_caps *caps) { struct tplg_elem *ref_elem = NULL; - struct snd_soc_tplg_pcm *pcm; - struct snd_soc_tplg_stream_caps *caps; unsigned int i; - pcm = elem->pcm; - for (i = 0; i < 2; i++) { - caps = &pcm->caps[i]; - ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list, - caps->name, SND_TPLG_TYPE_STREAM_CAPS); + caps[i].name, SND_TPLG_TYPE_STREAM_CAPS); if (ref_elem != NULL) - copy_pcm_caps(elem->id, caps, ref_elem); + copy_stream_caps(id, &caps[i], ref_elem); } return 0; @@ -91,7 +86,7 @@ int tplg_build_pcm(snd_tplg_t *tplg, unsigned int type) return -EINVAL; } - err = tplg_build_pcm_caps(tplg, elem); + err = tplg_build_stream_caps(tplg, elem->id, elem->pcm->caps); if (err < 0) return err; @@ -184,8 +179,8 @@ static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str) return 0; } -/* Parse pcm Capabilities */ -int tplg_parse_pcm_caps(snd_tplg_t *tplg, +/* Parse pcm stream capabilities */ +int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) { struct snd_soc_tplg_stream_caps *sc; @@ -263,29 +258,40 @@ int tplg_parse_pcm_caps(snd_tplg_t *tplg, return 0; } -/* Parse the caps of a pcm stream */ -int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, +/* Parse the caps and config of a pcm stream */ +static int tplg_parse_streams(snd_tplg_t *tplg, snd_config_t *cfg, void *private) { snd_config_iterator_t i, next; snd_config_t *n; struct tplg_elem *elem = private; struct snd_soc_tplg_pcm *pcm; + unsigned int *playback, *capture; + struct snd_soc_tplg_stream_caps *caps; const char *id, *value; int stream; - pcm = elem->pcm; - snd_config_get_id(cfg, &id); tplg_dbg("\t%s:\n", id); + switch (elem->type) { + case SND_TPLG_TYPE_PCM: + pcm = elem->pcm; + playback = &pcm->playback; + capture = &pcm->capture; + caps = pcm->caps; + break; + default: + return -EINVAL; + } + if (strcmp(id, "playback") == 0) { stream = SND_SOC_TPLG_STREAM_PLAYBACK; - pcm->playback = 1; + *playback = 1; } else if (strcmp(id, "capture") == 0) { stream = SND_SOC_TPLG_STREAM_CAPTURE; - pcm->capture = 1; + *capture = 1; } else return -EINVAL; @@ -300,8 +306,10 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, if (strcmp(id, "capabilities") == 0) { if (snd_config_get_string(n, &value) < 0) continue; - - elem_copy_text(pcm->caps[stream].name, value, + /* store stream caps name, to find and merge + * the caps in building phase. + */ + elem_copy_text(caps[stream].name, value, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value); @@ -312,7 +320,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } -/* Parse pcm */ +/* Parse pcm (for front end DAI & DAI link) */ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) { @@ -365,7 +373,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, if (strcmp(id, "pcm") == 0) { err = tplg_parse_compound(tplg, n, - tplg_parse_stream_caps, elem); + tplg_parse_streams, elem); if (err < 0) return err; continue; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 4c601d4..9239aef 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -210,7 +210,7 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, int tplg_parse_dapm_widget(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); -int tplg_parse_pcm_caps(snd_tplg_t *tplg, +int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); int tplg_parse_pcm(snd_tplg_t *tplg, -- 2.5.5 From 0935e32d40e065e61255550ac4c5b7c6710dd028 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:03:22 +0800 Subject: [PATCH 18/34] topology: Use generic pointer to realloc buffer for private data Many element types have private data. So use the generic obj pointer instead of the type-specific pointer when reallocating the object to accommodate the private data. Empty private data will be overlooked. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/topology/data.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/topology/data.c b/src/topology/data.c index 19c31bf..9f8d5d0 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -822,44 +822,36 @@ int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref) { struct snd_soc_tplg_private *priv; int priv_data_size; + void *obj; if (!ref) return -EINVAL; tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id); + if (!ref->data || !ref->data->size) /* overlook empty private data */ + return 0; + priv_data_size = ref->data->size; + obj = realloc(elem->obj, + elem->size + priv_data_size); + if (!obj) + return -ENOMEM; + elem->obj = obj; switch (elem->type) { case SND_TPLG_TYPE_MIXER: - elem->mixer_ctrl = realloc(elem->mixer_ctrl, - elem->size + priv_data_size); - if (!elem->mixer_ctrl) - return -ENOMEM; priv = &elem->mixer_ctrl->priv; break; case SND_TPLG_TYPE_ENUM: - elem->enum_ctrl = realloc(elem->enum_ctrl, - elem->size + priv_data_size); - if (!elem->enum_ctrl) - return -ENOMEM; priv = &elem->enum_ctrl->priv; break; case SND_TPLG_TYPE_BYTES: - elem->bytes_ext = realloc(elem->bytes_ext, - elem->size + priv_data_size); - if (!elem->bytes_ext) - return -ENOMEM; priv = &elem->bytes_ext->priv; break; - case SND_TPLG_TYPE_DAPM_WIDGET: - elem->widget = realloc(elem->widget, - elem->size + priv_data_size); - if (!elem->widget) - return -ENOMEM; priv = &elem->widget->priv; break; -- 2.5.5 From 5d23c406d1757d1b65e1d17a71c69620d9af71d7 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:03:30 +0800 Subject: [PATCH 19/34] topology: Fix pcm ID & name parsing The name and ID of SectionPCM should be set to pcm_name and pcm_id, for a front-end DAI link in the kernel, not for the front-end DAI of the link. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/sound/asoc.h | 2 +- src/topology/pcm.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/sound/asoc.h b/include/sound/asoc.h index 920c9e0..abe49c5 100644 --- a/include/sound/asoc.h +++ b/include/sound/asoc.h @@ -414,7 +414,7 @@ struct snd_soc_tplg_pcm { __le32 size; /* in bytes of this structure */ char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 pcm_id; /* unique ID - used to match */ + __le32 pcm_id; /* unique ID - used to match with DAI link */ __le32 dai_id; /* unique ID - used to match */ __le32 playback; /* supports playback mode */ __le32 capture; /* supports capture mode */ diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 1df4f54..1661821 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -337,7 +337,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, pcm = elem->pcm; pcm->size = elem->size; - elem_copy_text(pcm->dai_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + elem_copy_text(pcm->pcm_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); tplg_dbg(" PCM: %s\n", elem->id); @@ -366,8 +366,8 @@ int tplg_parse_pcm(snd_tplg_t *tplg, if (snd_config_get_string(n, &val) < 0) return -EINVAL; - pcm->dai_id = atoi(val); - tplg_dbg("\t%s: %d\n", id, pcm->dai_id); + pcm->pcm_id = atoi(val); + tplg_dbg("\t%s: %d\n", id, pcm->pcm_id); continue; } -- 2.5.5 From 25d6f8e6a8e162259dcf84600496dc8ebd8196e7 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:03:37 +0800 Subject: [PATCH 20/34] topology: Parse front-end DAI name and ID for the PCM These two fields are necessary to create the front-end DAIs in kernel but the support is missing in text conf previously. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- include/topology.h | 4 ++++ src/topology/pcm.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/include/topology.h b/include/topology.h index b47f422..9d57ce3 100644 --- a/include/topology.h +++ b/include/topology.h @@ -533,6 +533,10 @@ extern "C" { * * id "0" # used for binding to the PCM * + * dai."name of front-end DAI" { + * id "0" # used for binding to the front-end DAI + * } + * * pcm."playback" { * capabilities "capabilities1" # capabilities for playback * diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 1661821..efee58b 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -320,6 +320,51 @@ static int tplg_parse_streams(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } +/* Parse name and id of a front-end DAI (ie. cpu dai of a FE DAI link) */ +static int tplg_parse_fe_dai(snd_tplg_t *tplg, snd_config_t *cfg, + void *private) +{ + struct tplg_elem *elem = private; + struct snd_soc_tplg_pcm *pcm = elem->pcm; + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value = NULL; + unsigned long int id_val; + int err; + + snd_config_get_id(cfg, &id); + tplg_dbg("\t\tFE DAI %s:\n", id); + elem_copy_text(pcm->dai_name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + + /* get id */ + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "id") == 0) { + if (snd_config_get_string(n, &value) < 0) + continue; + errno = 0; + /* no support for negative value */ + id_val = strtoul(value, NULL, 0); + if ((errno == ERANGE && id_val == ULONG_MAX) + || (errno != 0 && id_val == 0) + || id_val > UINT_MAX) { + SNDERR("error: invalid fe dai ID\n"); + return -EINVAL; + } + + pcm->dai_id = (int) id_val; + tplg_dbg("\t\t\tindex: %d\n", pcm->dai_id); + } + } + + return 0; +} + /* Parse pcm (for front end DAI & DAI link) */ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) @@ -378,6 +423,14 @@ int tplg_parse_pcm(snd_tplg_t *tplg, return err; continue; } + + if (strcmp(id, "dai") == 0) { + err = tplg_parse_compound(tplg, n, + tplg_parse_fe_dai, elem); + if (err < 0) + return err; + continue; + } } return 0; -- 2.5.5 From 76af5bf833323d8ae1caa23d592a959e74990932 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 29 Apr 2016 11:03:45 +0800 Subject: [PATCH 21/34] topology: Update PCM configurations in Broadwell text conf file To make this conf file a better example, update the name & ID of PCMs (front-end DAI link) and their cpu DAI (front-end DAI), same as those defined by Broadwell upstream driver. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai --- src/conf/topology/broadwell/broadwell.conf | 34 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/conf/topology/broadwell/broadwell.conf b/src/conf/topology/broadwell/broadwell.conf index 05b3889..eb89377 100644 --- a/src/conf/topology/broadwell/broadwell.conf +++ b/src/conf/topology/broadwell/broadwell.conf @@ -272,18 +272,22 @@ SectionPCMCapabilities."Offload0 Playback" { SectionPCMCapabilities."Offload1 Playback" { formats "S24_LE,S16_LE" rate_min "8000" - rate_max "48000" + rate_max "192000" channels_min "2" channels_max "2" } # PCM devices exported by Firmware -SectionPCM."System Pin" { +SectionPCM."System Playback/Capture" { index "1" # used for binding to the PCM - ID "0" + id "0" + + dai."System Pin" { + id "0" + } pcm."playback" { @@ -307,12 +311,16 @@ SectionPCM."System Pin" { } } -SectionPCM."Offload0 Pin" { +SectionPCM."Offload0 Playback" { index "1" # used for binding to the PCM - ID "1" + id "1" + + dai."Offload0 Pin" { + id "1" + } pcm."playback" { @@ -325,12 +333,16 @@ SectionPCM."Offload0 Pin" { } } -SectionPCM."Offload1 Pin" { +SectionPCM."Offload1 Playback" { index "1" # used for binding to the PCM - ID "2" + id "2" + + dai."Offload1 Pin" { + id "2" + } pcm."playback" { @@ -343,12 +355,16 @@ SectionPCM."Offload1 Pin" { } } -SectionPCM."Loopback Pin" { +SectionPCM."Loopback PCM" { index "1" # used for binding to the PCM - ID "3" + id "3" + + dai."Loopback Pin" { + id "3" + } pcm."capture" { -- 2.5.5 From 85bf9915989e0a338632739684c75192c1753239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Krause?= Date: Sun, 8 May 2016 20:48:42 +0200 Subject: [PATCH 22/34] pcm: softvol: fix conversion of TLVs min_db and max_dB value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both, min_dB and max_dB, are floating type whereas the TLV is (always) unsigned. The problem with the conversion of a negative floating-point number into an unsigned integer is, that the behavior is undefined. This may, depending on the platform, result in a wrong TLV, i.e. for the default values of min_dB (-51dB) and max_dB (0dB), alsactl generates the following state on an ARM cpu build with GCC: control.1 { iface MIXER name Master value.0 255 value.1 255 comment { access 'read write user' type INTEGER count 2 range '0 - 255' tlv '00000001000000080000000000000014' dbmin 0 dbmax 5100 dbvalue.0 5100 dbvalue.1 5100 } } With the fix applied, alsactl stores the correct TLV: control.1 { iface MIXER name Master value.0 255 value.1 255 comment { access 'read write user' type INTEGER count 2 range '0 - 255' tlv '0000000100000008ffffec1400000014' dbmin -5100 dbmax 0 dbvalue.0 0 dbvalue.1 0 } } Also tested for different combinations of min_dB and max_dB other than the default values. Replaces: http://mailman.alsa-project.org/pipermail/alsa-devel/2016-May/107733.html Fixes: http://mailman.alsa-project.org/pipermail/alsa-devel/2016-May/107628.html Cc: Clemens Ladisch Signed-off-by: Jörg Krause Signed-off-by: Takashi Iwai --- src/pcm/pcm_softvol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index 802aa4b..5492db8 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -658,8 +658,8 @@ static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo) unsigned int tlv[4]; tlv[0] = SND_CTL_TLVT_DB_SCALE; tlv[1] = 2 * sizeof(int); - tlv[2] = svol->min_dB * 100; - tlv[3] = (svol->max_dB - svol->min_dB) * 100 / svol->max_val; + tlv[2] = (int)(svol->min_dB * 100); + tlv[3] = (int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val); return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv); } -- 2.5.5 From a192f52fc63a86e1fbb9a09adb0bc2a6bbc8dab1 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Thu, 5 May 2016 08:32:06 +0200 Subject: [PATCH 23/34] conf/ucm: ROCKCHIP-I2S: add Rockchip I2S UCM config. Taken from the ChromeOS sources, this configuration was tested on Veyron Jerry based Chromebook from Google. [Added missing Makefile changes by tiwai] Signed-off-by: Enric Balletbo i Serra Signed-off-by: Takashi Iwai --- configure.ac | 1 + src/conf/ucm/Makefile.am | 2 +- src/conf/ucm/ROCKCHIP-I2S/HiFi.conf | 94 +++++++++++++++++++++++++++++ src/conf/ucm/ROCKCHIP-I2S/Makefile.am | 4 ++ src/conf/ucm/ROCKCHIP-I2S/ROCKCHIP-I2S.conf | 6 ++ 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/conf/ucm/ROCKCHIP-I2S/HiFi.conf create mode 100644 src/conf/ucm/ROCKCHIP-I2S/Makefile.am create mode 100644 src/conf/ucm/ROCKCHIP-I2S/ROCKCHIP-I2S.conf diff --git a/configure.ac b/configure.ac index 28fcd24..ff340a8 100644 --- a/configure.ac +++ b/configure.ac @@ -658,6 +658,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ src/conf/ucm/PAZ00/Makefile \ src/conf/ucm/GoogleNyan/Makefile \ src/conf/ucm/broadwell-rt286/Makefile \ + src/conf/ucm/ROCKCHIP-I2S/Makefile \ src/conf/topology/Makefile \ src/conf/topology/broadwell/Makefile \ modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ diff --git a/src/conf/ucm/Makefile.am b/src/conf/ucm/Makefile.am index e6a6325..d1d51e5 100644 --- a/src/conf/ucm/Makefile.am +++ b/src/conf/ucm/Makefile.am @@ -1 +1 @@ -SUBDIRS=DAISY-I2S PandaBoard PandaBoardES SDP4430 tegraalc5632 PAZ00 GoogleNyan broadwell-rt286 +SUBDIRS=DAISY-I2S PandaBoard PandaBoardES SDP4430 tegraalc5632 PAZ00 GoogleNyan broadwell-rt286 ROCKCHIP-I2S diff --git a/src/conf/ucm/ROCKCHIP-I2S/HiFi.conf b/src/conf/ucm/ROCKCHIP-I2S/HiFi.conf new file mode 100644 index 0000000..7bfe995 --- /dev/null +++ b/src/conf/ucm/ROCKCHIP-I2S/HiFi.conf @@ -0,0 +1,94 @@ +SectionVerb { + Value { + OutputDspName "speaker_eq" + MinBufferLevel "512" + } + + EnableSequence [ + cdev "hw:ROCKCHIPI2S" + + cset "name='Left Speaker Mixer Left DAC Switch' on" + cset "name='Right Speaker Mixer Right DAC Switch' on" + cset "name='Headphone Left Switch' off" + cset "name='Headphone Right Switch' off" + cset "name='Digital EQ 3 Band Switch' off" + cset "name='Digital EQ 5 Band Switch' off" + cset "name='Digital EQ 7 Band Switch' off" + cset "name='Biquad Switch' off" + cset "name='Filter Mode' Music" + cset "name='ADC Oversampling Rate' 0" + + cset "name='DMIC Mux' DMIC" + cset "name='MIC2 Mux' IN34" + cset "name='Right ADC Mixer MIC2 Switch' on" + cset "name='Left ADC Mixer MIC2 Switch' on" + cset "name='MIC2 Volume' 20" + cset "name='Headset Mic Switch' off" + cset "name='Int Mic Switch' on" + + cset "name='ADCR Boost Volume' 4" + cset "name='ADCL Boost Volume' 4" + cset "name='ADCR Volume' 11" + cset "name='ADCL Volume' 11" + + cset "name='Left Speaker Mixer Left DAC Switch' on" + cset "name='Right Speaker Mixer Right DAC Switch' on" + cset "name='Speaker Left Mixer Volume' 2" + cset "name='Speaker Right Mixer Volume' 2" + cset "name='Record Path DC Blocking' on" + cset "name='Playback Path DC Blocking' on" + + cset "name='Speaker Left Switch' on" + cset "name='Speaker Right Switch' on" + cset "name='Speaker Switch' on" + ] + + DisableSequence [ + ] +} + +SectionDevice."Headphone".0 { + Value { + JackName "ROCKCHIP-I2S Headset Jack" + OutputDspName "" + } + + EnableSequence [ + cdev "hw:ROCKCHIPI2S" + + cset "name='Speaker Switch' off" + cset "name='Headphone Left Switch' on" + cset "name='Headphone Right Switch' on" + ] + DisableSequence [ + cdev "hw:ROCKCHIPI2S" + + cset "name='Headphone Left Switch' off" + cset "name='Headphone Right Switch' off" + cset "name='Speaker Switch' on" + ] +} + +SectionDevice."Mic".0 { + Value { + JackName "ROCKCHIP-I2S Headset Jack" + } + + EnableSequence [ + cdev "hw:ROCKCHIPI2S" + + cset "name='Int Mic Switch' off" + cset "name='DMIC Mux' ADC" + cset "name='Headset Mic Switch' on" + cset "name='Record Path DC Blocking' on" + ] + + DisableSequence [ + cdev "hw:ROCKCHIPI2S" + + cset "name='Headset Mic Switch' off" + cset "name='DMIC Mux' DMIC" + cset "name='Int Mic Switch' on" + cset "name='Record Path DC Blocking' off" + ] +} diff --git a/src/conf/ucm/ROCKCHIP-I2S/Makefile.am b/src/conf/ucm/ROCKCHIP-I2S/Makefile.am new file mode 100644 index 0000000..485a740 --- /dev/null +++ b/src/conf/ucm/ROCKCHIP-I2S/Makefile.am @@ -0,0 +1,4 @@ +alsaconfigdir = @ALSA_CONFIG_DIR@ +ucmdir = $(alsaconfigdir)/ucm/ROCKCHIP-I2S +ucm_DATA = ROCKCHIP-I2S.conf HiFi.conf +EXTRA_DIST = $(ucm_DATA) diff --git a/src/conf/ucm/ROCKCHIP-I2S/ROCKCHIP-I2S.conf b/src/conf/ucm/ROCKCHIP-I2S/ROCKCHIP-I2S.conf new file mode 100644 index 0000000..0c399b0 --- /dev/null +++ b/src/conf/ucm/ROCKCHIP-I2S/ROCKCHIP-I2S.conf @@ -0,0 +1,6 @@ +Comment "Rockchip card" + +SectionUseCase."HiFi" { + File "HiFi.conf" + Comment "Default" +} -- 2.5.5 From c14b0a08f0bf58e4f62307c68f8ff0137b4dec19 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 May 2016 09:06:47 +0200 Subject: [PATCH 24/34] pcm: Fix suspend/resume regression with dmix & co The recent fix commit [8985742d91db: pcm: dmix: Handle slave PCM xrun and unexpected states properly] caused a regression in dmix and other plugins regarding suspend/resume. For example, aplay endlessly prints "Suspended. Trying resume. Done." message if suspend and resume are performed in the middle of playback. The reason is that the commit above changed the shadow PCM state (dmix->state) to SUSPENDED when the slave PCM is in suspend, while it doesn't restore the shadow state upon resume. Thus it appears as if it's always suspended even after the resume is invoked. The fix is just to add the proper update of the shadow state in snd_pcm_direct_resume(). Reported-by: Shengjiu Wang Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 14de734..e28738b 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -848,6 +848,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) snd_pcm_start(dmix->spcm); err = 0; } + dmix->state = snd_pcm_state(dmix->spcm); snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); return err; } -- 2.5.5 From 5610b356b5f110f7f8e586f56e5b74e0f0c2db38 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 May 2016 13:06:25 +0200 Subject: [PATCH 25/34] pcm: dmix: Fix doubly resume of slave PCM The dmix plugin and co may trigger the resume for each instance in snd_pcm_direct_resume(). It means that the slave PCM gets resumed or re-prepared/started by each opened dmix stream, and this may end up with the doubly triggers even though the slave PCM has been already resumed by another dmix stream. For avoiding this conflicts, check the slave PCM state and resume only when it's still in the suspended state. Meanwhile we keep the shadow state updated no matter whether the slave was triggered or not. Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index e28738b..ac082f1 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -841,6 +841,12 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) int err; snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); + /* resume only when the slave PCM is still in suspended state */ + if (snd_pcm_state(dmix->spcm) != SND_PCM_STATE_SUSPENDED) { + err = 0; + goto out; + } + err = snd_pcm_resume(dmix->spcm); if (err == -ENOSYS) { /* FIXME: error handling? */ @@ -848,6 +854,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) snd_pcm_start(dmix->spcm); err = 0; } + out: dmix->state = snd_pcm_state(dmix->spcm); snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); return err; -- 2.5.5 From 8cdbdae73109c901aec4984f6ba65e5b25722f13 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 May 2016 16:30:44 +0200 Subject: [PATCH 26/34] namehint: Don't enumerate as duplex if only a single direction is defined When a hint description has only either device_input or device_output, we shouldn't handle it as a full duplex but rather a single direction. In that way, we can avoid to list up a playback stream like dmix or surround51 as a capture stream in the namehint. Reported-by: Trent Reed Signed-off-by: Takashi Iwai --- src/control/namehint.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/control/namehint.c b/src/control/namehint.c index 856957c..ad8dda3 100644 --- a/src/control/namehint.c +++ b/src/control/namehint.c @@ -28,6 +28,7 @@ #include "local.h" #ifndef DOC_HIDDEN +#define DEV_SKIP 9999 /* some non-existing device number */ struct hint_list { char **list; unsigned int count; @@ -90,7 +91,7 @@ static int get_dev_name1(struct hint_list *list, char **res, int device, int stream) { *res = NULL; - if (device < 0) + if (device < 0 || device == DEV_SKIP) return 0; switch (list->iface) { #ifdef BUILD_HWDEP @@ -317,7 +318,9 @@ static int try_config(snd_config_t *config, err = -EINVAL; goto __cleanup; } - list->device_output = -1; + /* skip the counterpart if only a single direction is defined */ + if (list->device_output < 0) + list->device_output = DEV_SKIP; } if (snd_config_search(cfg, "device_output", &n) >= 0) { if (snd_config_get_integer(n, &list->device_output) < 0) { @@ -325,6 +328,9 @@ static int try_config(snd_config_t *config, err = -EINVAL; goto __cleanup; } + /* skip the counterpart if only a single direction is defined */ + if (list->device_input < 0) + list->device_input = DEV_SKIP; } } else if (level == 1 && !list->show_all) goto __skip_add; -- 2.5.5 From 5fb3fe17249c3fffb8b8e15108ff72f27ba5e81c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 May 2016 16:33:19 +0200 Subject: [PATCH 27/34] pcm: Define namehint for single directional PCM types The PCM namehint for some PCM types like dmix, dsnoop and surround51 should be defined as single directional. Reported-by: Trent Reed Signed-off-by: Takashi Iwai --- src/conf/pcm/dmix.conf | 2 +- src/conf/pcm/dsnoop.conf | 2 +- src/conf/pcm/surround21.conf | 2 +- src/conf/pcm/surround40.conf | 2 +- src/conf/pcm/surround41.conf | 2 +- src/conf/pcm/surround50.conf | 2 +- src/conf/pcm/surround51.conf | 2 +- src/conf/pcm/surround71.conf | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/conf/pcm/dmix.conf b/src/conf/pcm/dmix.conf index e62cb29..7d0aa01 100644 --- a/src/conf/pcm/dmix.conf +++ b/src/conf/pcm/dmix.conf @@ -110,6 +110,6 @@ pcm.!dmix { name defaults.namehint.extended } description "Direct sample mixing device" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/dsnoop.conf b/src/conf/pcm/dsnoop.conf index 49cfca9..abbd44f 100644 --- a/src/conf/pcm/dsnoop.conf +++ b/src/conf/pcm/dsnoop.conf @@ -110,6 +110,6 @@ pcm.!dsnoop { name defaults.namehint.extended } description "Direct sample snooping device" - device $DEV + device_input $DEV } } diff --git a/src/conf/pcm/surround21.conf b/src/conf/pcm/surround21.conf index 7f4676b..1cf1b7a 100644 --- a/src/conf/pcm/surround21.conf +++ b/src/conf/pcm/surround21.conf @@ -56,6 +56,6 @@ pcm.!surround21 { ttable.2.LFE 1 hint { description "2.1 Surround output to Front and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround40.conf b/src/conf/pcm/surround40.conf index 361ccaa..9788ad4 100644 --- a/src/conf/pcm/surround40.conf +++ b/src/conf/pcm/surround40.conf @@ -54,6 +54,6 @@ pcm.!surround40 { } hint { description "4.0 Surround output to Front and Rear speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround41.conf b/src/conf/pcm/surround41.conf index 2f82381..7b4ef3b 100644 --- a/src/conf/pcm/surround41.conf +++ b/src/conf/pcm/surround41.conf @@ -60,6 +60,6 @@ pcm.!surround41 { ttable.4.LFE 1 hint { description "4.1 Surround output to Front, Rear and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround50.conf b/src/conf/pcm/surround50.conf index dc95c17..7d9a9e7 100644 --- a/src/conf/pcm/surround50.conf +++ b/src/conf/pcm/surround50.conf @@ -60,6 +60,6 @@ pcm.!surround50 { ttable.4.FC 1 hint { description "5.0 Surround output to Front, Center and Rear speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround51.conf b/src/conf/pcm/surround51.conf index 3a7543f..e67f007 100644 --- a/src/conf/pcm/surround51.conf +++ b/src/conf/pcm/surround51.conf @@ -56,6 +56,6 @@ pcm.!surround51 { } hint { description "5.1 Surround output to Front, Center, Rear and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround71.conf b/src/conf/pcm/surround71.conf index 076a97d..a26c3f3 100644 --- a/src/conf/pcm/surround71.conf +++ b/src/conf/pcm/surround71.conf @@ -58,6 +58,6 @@ pcm.!surround71 { } hint { description "7.1 Surround output to Front, Center, Side, Rear and Woofer speakers" - device $DEV + device_output $DEV } } -- 2.5.5 From c9a0d7d601e8ab069f8745968c03c8470b24d20d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 May 2016 15:39:07 +0200 Subject: [PATCH 28/34] conf: Add thread-safe global tree reference Most of open functions in alsa-lib have the call pattern: snd_config_update(); return snd_xxx_open(x, snd_config, ...); This means that the toplevel config gets updated, and passed to a local open function. Although snd_config_update() itself has a pthread mutex to be thread safe, the whole procedure above isn't thread safe. Namely, the global snd_config tree may be deleted and recreated at any time while the open function is being processed. This may lead to a data corruption and crash of the program. For avoiding the corruption, this patch introduces a refcount to config tree object. A few new helper functions are introduced as well: - snd_config_update_ref() does update and take the refcount of the toplevel tree. The obtained config tree has to be freed via snd_config_unref() below. - snd_config_ref() and snd_config_unref() manage the refcount of the config object. The latter eventually deletes the object when all references are gone. Along with these additions, the caller of snd_config_update() and snd_config global tree in alsa-lib are replaced with the new helpers. Signed-off-by: Takashi Iwai --- include/conf.h | 4 +++ src/conf.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ src/control/control.c | 8 +++-- src/hwdep/hwdep.c | 8 +++-- src/pcm/pcm.c | 8 +++-- src/rawmidi/rawmidi.c | 8 +++-- src/seq/seq.c | 8 +++-- src/timer/timer.c | 8 +++-- src/timer/timer_query.c | 8 +++-- 9 files changed, 125 insertions(+), 14 deletions(-) diff --git a/include/conf.h b/include/conf.h index 087c05d..5d293d5 100644 --- a/include/conf.h +++ b/include/conf.h @@ -94,6 +94,10 @@ int snd_config_update_r(snd_config_t **top, snd_config_update_t **update, const int snd_config_update_free(snd_config_update_t *update); int snd_config_update_free_global(void); +int snd_config_update_ref(snd_config_t **top); +void snd_config_ref(snd_config_t *top); +void snd_config_unref(snd_config_t *top); + int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result); int snd_config_searchv(snd_config_t *config, diff --git a/src/conf.c b/src/conf.c index f8b7a66..a516611 100644 --- a/src/conf.c +++ b/src/conf.c @@ -434,6 +434,7 @@ static pthread_once_t snd_config_update_mutex_once = PTHREAD_ONCE_INIT; struct _snd_config { char *id; snd_config_type_t type; + int refcount; /* default = 0 */ union { long integer; long long integer64; @@ -1825,6 +1826,10 @@ int snd_config_remove(snd_config_t *config) * If the node is a compound node, its descendants (the whole subtree) * are deleted recursively. * + * The function is supposed to be called only for locally copied config + * trees. For the global tree, take the reference via #snd_config_update_ref + * and free it via #snd_config_unref. + * * \par Conforming to: * LSB 3.2 * @@ -1833,6 +1838,10 @@ int snd_config_remove(snd_config_t *config) int snd_config_delete(snd_config_t *config) { assert(config); + if (config->refcount > 0) { + config->refcount--; + return 0; + } switch (config->type) { case SND_CONFIG_TYPE_COMPOUND: { @@ -3833,6 +3842,8 @@ int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, cons * \warning Whenever #snd_config is updated, all string pointers and * configuration node handles previously obtained from it may become * invalid. + * For safer operations, use #snd_config_update_ref and release the config + * via #snd_config_unref. * * \par Errors: * Any errors encountered when parsing the input or returned by hooks or @@ -3851,6 +3862,74 @@ int snd_config_update(void) return err; } +/** + * \brief Updates #snd_config and takes its reference. + * \return 0 if #snd_config was up to date, 1 if #snd_config was + * updated, otherwise a negative error code. + * + * Unlike #snd_config_update, this function increases a reference counter + * so that the obtained tree won't be deleted until unreferenced by + * #snd_config_unref. + * + * This function is supposed to be thread-safe. + */ +int snd_config_update_ref(snd_config_t **top) +{ + int err; + + if (top) + *top = NULL; + snd_config_lock(); + err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL); + if (err >= 0) { + if (snd_config) { + if (top) { + snd_config->refcount++; + *top = snd_config; + } + } else { + err = -ENODEV; + } + } + snd_config_unlock(); + return err; +} + +/** + * \brief Take the reference of the config tree. + * + * Increases a reference counter of the given config tree. + * + * This function is supposed to be thread-safe. + */ +void snd_config_ref(snd_config_t *cfg) +{ + snd_config_lock(); + if (cfg) + cfg->refcount++; + snd_config_unlock(); +} + +/** + * \brief Unreference the config tree. + * + * Decreases a reference counter of the given config tree, and eventually + * deletes the tree if all references are gone. This is the counterpart of + * #snd_config_unref. + * + * Also, the config taken via #snd_config_update_ref must be unreferenced + * by this function, too. + * + * This function is supposed to be thread-safe. + */ +void snd_config_unref(snd_config_t *cfg) +{ + snd_config_lock(); + if (cfg) + snd_config_delete(cfg); + snd_config_unlock(); +} + /** * \brief Frees a private update structure. * \param[in] update The private update structure to free. diff --git a/src/control/control.c b/src/control/control.c index 8a5d530..ae78843 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -968,12 +968,16 @@ static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const cha */ int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) { + snd_config_t *top; int err; + assert(ctlp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_ctl_open_noupdate(ctlp, snd_config, name, mode); + err = snd_ctl_open_noupdate(ctlp, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index 5dc791c..bac634b 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -168,12 +168,16 @@ static int snd_hwdep_open_noupdate(snd_hwdep_t **hwdep, snd_config_t *root, cons */ int snd_hwdep_open(snd_hwdep_t **hwdep, const char *name, int mode) { + snd_config_t *top; int err; + assert(hwdep && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_hwdep_open_noupdate(hwdep, snd_config, name, mode); + err = snd_hwdep_open_noupdate(hwdep, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 203e7a5..0d0d093 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2288,12 +2288,16 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root, int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode) { + snd_config_t *top; int err; + assert(pcmp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0); + err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0); + snd_config_unref(top); + return err; } /** diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 0c89b8b..4701b43 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -305,12 +305,16 @@ static int snd_rawmidi_open_noupdate(snd_rawmidi_t **inputp, snd_rawmidi_t **out int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, const char *name, int mode) { + snd_config_t *top; int err; + assert((inputp || outputp) && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_rawmidi_open_noupdate(inputp, outputp, snd_config, name, mode); + err = snd_rawmidi_open_noupdate(inputp, outputp, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/seq/seq.c b/src/seq/seq.c index 4405e68..9279830 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -974,12 +974,16 @@ static int snd_seq_open_noupdate(snd_seq_t **seqp, snd_config_t *root, int snd_seq_open(snd_seq_t **seqp, const char *name, int streams, int mode) { + snd_config_t *top; int err; + assert(seqp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_seq_open_noupdate(seqp, snd_config, name, streams, mode, 0); + err = snd_seq_open_noupdate(seqp, top, name, streams, mode, 0); + snd_config_unref(top); + return err; } /** diff --git a/src/timer/timer.c b/src/timer/timer.c index a25e4f7..b253471 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -201,12 +201,16 @@ static int snd_timer_open_noupdate(snd_timer_t **timer, snd_config_t *root, cons */ int snd_timer_open(snd_timer_t **timer, const char *name, int mode) { + snd_config_t *top; int err; + assert(timer && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_timer_open_noupdate(timer, snd_config, name, mode); + err = snd_timer_open_noupdate(timer, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/timer/timer_query.c b/src/timer/timer_query.c index 93d2455..2072cea 100644 --- a/src/timer/timer_query.c +++ b/src/timer/timer_query.c @@ -159,12 +159,16 @@ static int snd_timer_query_open_noupdate(snd_timer_query_t **timer, snd_config_t */ int snd_timer_query_open(snd_timer_query_t **timer, const char *name, int mode) { + snd_config_t *top; int err; + assert(timer && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_timer_query_open_noupdate(timer, snd_config, name, mode); + err = snd_timer_query_open_noupdate(timer, top, name, mode); + snd_config_unref(top); + return err; } /** -- 2.5.5 From d942498bfbd315c4c4559ccd573685e09aa03383 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 May 2016 10:38:27 +0200 Subject: [PATCH 29/34] pcm: Remove resume support from dmix & co PCM dmix and other plugins inherit the resume behavior from the slave PCM. However, the resume on dmix can't work reliably even if the slave PCM may do resume. The running state of each dmix stream is individual and may be PREPARED or RUN_PENDING while the slave PCM is already in RUNNING. And, when the slave PCM is resumed, the whole samples that have been already mapped are also played back, even if the corresponding dmix stream is still in SUSPENDED. Such inconsistencies can't be avoided as long as we manage each stream individually. That said, dmix & co can't provide the proper resume support "by design". For aligning with it, we should drop the whole resume code and clear the PCM SND_PCM_INFO_RESUME flag. Reported-by: Shengjiu Wang Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index ac082f1..53c4992 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -837,27 +837,7 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int snd_pcm_direct_resume(snd_pcm_t *pcm) { - snd_pcm_direct_t *dmix = pcm->private_data; - int err; - - snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); - /* resume only when the slave PCM is still in suspended state */ - if (snd_pcm_state(dmix->spcm) != SND_PCM_STATE_SUSPENDED) { - err = 0; - goto out; - } - - err = snd_pcm_resume(dmix->spcm); - if (err == -ENOSYS) { - /* FIXME: error handling? */ - snd_pcm_prepare(dmix->spcm); - snd_pcm_start(dmix->spcm); - err = 0; - } - out: - dmix->state = snd_pcm_state(dmix->spcm); - snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); - return err; + return -ENOSYS; } #define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field) @@ -865,7 +845,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) /* copy the slave setting */ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) { - spcm->info &= ~SND_PCM_INFO_PAUSE; + spcm->info &= ~(SND_PCM_INFO_PAUSE | SND_PCM_INFO_RESUME); COPY_SLAVE(access); COPY_SLAVE(format); -- 2.5.5 From 2fa36eb03c000560128f7abce701536546b4a618 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 28 May 2016 10:37:26 +0200 Subject: [PATCH 30/34] pcm: Fix secondary retry in dsnoop and dshare The commit [fdba9e1bad8f: pcm: Fallback open as the first instance for dmix & co] introduced a mechanism to retry the open of slave PCM for the secondary streams, but this also introduced a regression in dsnoop and dshare plugins: since the retry goto-tag was placed at a wrong position, it retries to re-fetch the shm unnecessarily and eventually leads to the fatal error. The bug can be easily reproduced by starting arecord and killing it via SIGKILL, then starting arecord again. The second arecord fails. The fix is obviously to move the wrong retry goto-tags to the right positions. Signed-off-by: Takashi Iwai --- src/pcm/pcm_dshare.c | 2 +- src/pcm/pcm_dsnoop.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index adb3587..05854de 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -690,7 +690,6 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, break; } - retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -705,6 +704,7 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, dshare->max_periods = opts->max_periods; dshare->sync_ptr = snd_pcm_dshare_sync_ptr; + retry: if (first_instance) { /* recursion is already checked in snd_pcm_direct_get_slave_ipc_offset() */ diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index 8ff0ba5..2d45171 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -583,7 +583,6 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, break; } - retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -598,6 +597,7 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, dsnoop->max_periods = opts->max_periods; dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr; + retry: if (first_instance) { /* recursion is already checked in snd_pcm_direct_get_slave_ipc_offset() */ -- 2.5.5 From 6d1d620eadf32c6d963468ce56ff52cc3a2f32e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 25 May 2016 15:03:51 +0200 Subject: [PATCH 31/34] pcm: dmix: resume workaround for buggy driver The previous commit removed the whole handling of resume in dmix, but this seems causing another regression; some buggy drivers assume that the device-resume needs to be triggered before transitioning to PREPARED state. As an ugly workaround, in this patch, when the slave PCM supports resume, snd_pcm_direct_resume() does resume of the slave PCM but immediately drop the stream after that. In that way, the device is brought to the sane active state, then the apps can prepare and restart the stream properly. Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 53c4992..343fd3c 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -837,6 +837,27 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int snd_pcm_direct_resume(snd_pcm_t *pcm) { + snd_pcm_direct_t *dmix = pcm->private_data; + snd_pcm_t *spcm = dmix->spcm; + + snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); + /* some buggy drivers require the device resumed before prepared; + * when a device has RESUME flag and is in SUSPENDED state, resume + * here but immediately drop to bring it to a sane active state. + */ + if ((spcm->info & SND_PCM_INFO_RESUME) && + snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) { + snd_pcm_resume(spcm); + snd_pcm_drop(spcm); + snd_pcm_direct_timer_stop(dmix); + snd_pcm_direct_clear_timer_queue(dmix); + snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0, + spcm->channels, spcm->buffer_size, + spcm->format); + snd_pcm_prepare(spcm); + snd_pcm_start(spcm); + } + snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); return -ENOSYS; } @@ -845,7 +866,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) /* copy the slave setting */ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) { - spcm->info &= ~(SND_PCM_INFO_PAUSE | SND_PCM_INFO_RESUME); + spcm->info &= ~SND_PCM_INFO_PAUSE; COPY_SLAVE(access); COPY_SLAVE(format); @@ -874,6 +895,8 @@ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) COPY_SLAVE(buffer_time); COPY_SLAVE(sample_bits); COPY_SLAVE(frame_bits); + + dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME; } #undef COPY_SLAVE -- 2.5.5 From 8feb96ed9b457c2aa62ddea2c48651475b7c3411 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 31 May 2016 12:46:03 +0200 Subject: [PATCH 32/34] pcm: dmix: Prepare slave when it's in SETUP, too SETUP is an unusual state, but it's still possible. Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 343fd3c..fbf9a59 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -819,6 +819,7 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int err; switch (snd_pcm_state(dmix->spcm)) { + case SND_PCM_STATE_SETUP: case SND_PCM_STATE_XRUN: case SND_PCM_STATE_SUSPENDED: case SND_PCM_STATE_DISCONNECTED: -- 2.5.5 From 614ce73d3d6eba13946f863bec24981d355902e1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 31 May 2016 12:48:40 +0200 Subject: [PATCH 33/34] pcm: dmix: Return error when slave is in OPEN or DISCONNECTED A slave PCM in OPEN or DISCONNECTED state can't be used properly at all, so the best option is to return -EBADFD error. Signed-off-by: Takashi Iwai --- src/pcm/pcm_direct.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index fbf9a59..21f98e7 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -822,12 +822,14 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) case SND_PCM_STATE_SETUP: case SND_PCM_STATE_XRUN: case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_DISCONNECTED: err = snd_pcm_prepare(dmix->spcm); if (err < 0) return err; snd_pcm_start(dmix->spcm); break; + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_DISCONNECTED: + return -EBADFD; } snd_pcm_direct_check_interleave(dmix, pcm); dmix->state = SND_PCM_STATE_PREPARED; -- 2.5.5 From d39e1879b9c72d51fe1ca4aeb5ba742e97b2175a Mon Sep 17 00:00:00 2001 From: Eliot Miranda Date: Wed, 1 Jun 2016 08:16:31 +0200 Subject: [PATCH 34/34] async: Handle previously installed signal handler The issue is with the signal handler installed and deinstalled in alsa-lib async handler. This code makes no attempt to remember any previously installed signal handlers for SIGIO, if SIGIO is used. Consequently it does not call any previous handlers from its own handler once installed, and does not reinstall any previous handler when deinstalling its handler. Consequently, use of also-lib within applications that depend on SIGIO will break those applications, rendering them inoperative once alsa-lib is running because their signal handlers are no longer called. This patch does remember and restore any previous handler, and chains calls to the handler if it exists. Signed-off-by: Takashi Iwai --- src/async.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/async.c b/src/async.c index 98aec78..0e133c3 100644 --- a/src/async.c +++ b/src/async.c @@ -28,6 +28,9 @@ #include "control/control_local.h" #include +static struct sigaction previous_action; +#define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ + #ifdef SND_ASYNC_RT_SIGNAL /** async signal number */ static int snd_async_signo; @@ -54,6 +57,9 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo int fd; struct list_head *i; //assert(siginfo->si_code == SI_SIGIO); + if (signo == SIGIO + && (unsigned long)(previous_action.sa_sigaction) > MAX_SIG_FUNCTION_CODE) + previous_action.sa_sigaction(signo, siginfo, context); fd = siginfo->si_fd; list_for_each(i, &snd_async_handlers) { snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); @@ -114,7 +120,8 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, act.sa_flags = SA_RESTART | SA_SIGINFO; act.sa_sigaction = snd_async_handler; sigemptyset(&act.sa_mask); - err = sigaction(snd_async_signo, &act, NULL); + assert(!previous_action.sa_sigaction); + err = sigaction(snd_async_signo, &act, &previous_action); if (err < 0) { SYSERR("sigaction"); return -errno; @@ -131,18 +138,17 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, int snd_async_del_handler(snd_async_handler_t *handler) { int err = 0; + int was_empty = list_empty(&snd_async_handlers); assert(handler); list_del(&handler->glist); - if (list_empty(&snd_async_handlers)) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_flags = 0; - act.sa_handler = SIG_DFL; - err = sigaction(snd_async_signo, &act, NULL); + if (!was_empty + && list_empty(&snd_async_handlers)) { + err = sigaction(snd_async_signo, &previous_action, NULL); if (err < 0) { SYSERR("sigaction"); return -errno; } + memset(&previous_action, 0, sizeof(previous_action)); } if (handler->type == SND_ASYNC_HANDLER_GENERIC) goto _end; -- 2.5.5