* [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 15:47 ` Takashi Iwai
2015-07-01 13:44 ` [PATCH v2 03/13] topology: Add topology core parser Liam Girdwood
` (10 subsequent siblings)
11 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/conf/Makefile.am | 2 +-
src/conf/topology/Makefile.am | 1 +
src/conf/topology/broadwell/Makefile.am | 4 +
src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++++++++++++++
4 files changed, 381 insertions(+), 1 deletion(-)
create mode 100644 src/conf/topology/Makefile.am
create mode 100644 src/conf/topology/broadwell/Makefile.am
create mode 100644 src/conf/topology/broadwell/broadwell.conf
diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am
index 948d5a1..a04f73f 100644
--- a/src/conf/Makefile.am
+++ b/src/conf/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=cards pcm alsa.conf.d ucm
+SUBDIRS=cards pcm alsa.conf.d ucm topology
cfg_files = alsa.conf
if BUILD_ALISP
diff --git a/src/conf/topology/Makefile.am b/src/conf/topology/Makefile.am
new file mode 100644
index 0000000..f56a96c
--- /dev/null
+++ b/src/conf/topology/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=broadwell
diff --git a/src/conf/topology/broadwell/Makefile.am b/src/conf/topology/broadwell/Makefile.am
new file mode 100644
index 0000000..35d1e83
--- /dev/null
+++ b/src/conf/topology/broadwell/Makefile.am
@@ -0,0 +1,4 @@
+alsaconfigdir = @ALSA_CONFIG_DIR@
+topologydir = $(alsaconfigdir)/topology/broadwell
+topology_DATA = broadwell.conf
+EXTRA_DIST = $(topology_DATA)
diff --git a/src/conf/topology/broadwell/broadwell.conf b/src/conf/topology/broadwell/broadwell.conf
new file mode 100644
index 0000000..05b3889
--- /dev/null
+++ b/src/conf/topology/broadwell/broadwell.conf
@@ -0,0 +1,375 @@
+# Dynamic Firmware Configuration for Broadwell
+
+# TLV
+SectionTLV."hsw_vol_tlv" {
+ Comment "TLV used by both global and stream volumes"
+
+ scale {
+ min "-9000"
+ step "300"
+ mute "1"
+ }
+}
+
+# Controls
+SectionControlMixer."Master Playback Volume" {
+ Comment "Global DSP volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "256"
+ put "256"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media0 Playback Volume" {
+ Comment "Offload 0 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "1"
+ shift "0"
+ }
+ channel."FR" {
+ reg "1"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Media1 Playback Volume" {
+ Comment "Offload 1 volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "2"
+ shift "0"
+ }
+ channel."FR" {
+ reg "2"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionControlMixer."Mic Capture Volume" {
+ Comment "Mic Capture volume"
+
+ # control belongs to this index group
+ index "1"
+
+ # Channel register and shift for Front Left/Right
+ channel."FL" {
+ reg "0"
+ shift "0"
+ }
+ channel."FR" {
+ reg "0"
+ shift "8"
+ }
+
+ # max control value and whether value is inverted
+ max "31"
+ invert "false"
+
+ # control uses bespoke driver get/put/info ID 0
+ ops."ctl" {
+ info "volsw"
+ get "257"
+ put "257"
+ }
+
+ # uses TLV data above
+ tlv "hsw_vol_tlv"
+}
+
+SectionWidget."SSP0 CODEC IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP0 CODEC OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT IN" {
+
+ index "1"
+ type "aif_in"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."SSP1 BT OUT" {
+
+ index "1"
+ type "aif_out"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+SectionWidget."Playback VMixer" {
+
+ index "1"
+ type "mixer"
+ no_pm "true"
+ shift "0"
+ invert "0"
+}
+
+# PCM Configurations supported by FW
+SectionPCMConfig."PCM 48k Stereo 24bit" {
+
+ config."playback" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S24_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k Stereo 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+}
+
+SectionPCMConfig."PCM 48k 2P/4C 16bit" {
+
+ config."playback" {
+ format "S16_LE"
+ rate "48000"
+ channels "2"
+ tdm_slot "0xf"
+ }
+
+ config."capture" {
+ format "S16_LE"
+ rate "48000"
+ channels "4"
+ tdm_slot "0xf"
+ }
+}
+
+# PCM capabilities supported by FW
+SectionPCMCapabilities."System Playback" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Analog Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "4"
+}
+
+SectionPCMCapabilities."Loopback Capture" {
+
+ formats "S24_LE,S16_LE"
+ rate_min "48000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload0 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "192000"
+ channels_min "2"
+ channels_max "2"
+}
+
+SectionPCMCapabilities."Offload1 Playback" {
+ formats "S24_LE,S16_LE"
+ rate_min "8000"
+ rate_max "48000"
+ channels_min "2"
+ channels_max "2"
+}
+
+# PCM devices exported by Firmware
+SectionPCM."System Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "0"
+
+ pcm."playback" {
+
+ capabilities "System Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+
+ pcm."capture" {
+
+ capabilities "Analog Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ "PCM 48k 2P/4C 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload0 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "1"
+
+ pcm."playback" {
+
+ capabilities "Offload0 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Offload1 Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "2"
+
+ pcm."playback" {
+
+ capabilities "Offload1 Playback"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionPCM."Loopback Pin" {
+
+ index "1"
+
+ # used for binding to the PCM
+ ID "3"
+
+ pcm."capture" {
+
+ capabilities "Loopback Capture"
+
+ configs [
+ "PCM 48k Stereo 24bit"
+ "PCM 48k Stereo 16bit"
+ ]
+ }
+}
+
+SectionGraph."dsp" {
+ index "1"
+
+ lines [
+ "Playback VMixer, , System Playback"
+ "Playback VMixer, , Offload0 Playback"
+ "Playback VMixer, , Offload1 Playback"
+ "SSP0 CODEC OUT, , Playback VMixer"
+ "Loopback Capture, , Playback VMixer"
+ "Analog Capture, , SSP0 CODEC IN"
+ ]
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP
2015-07-01 13:44 ` [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
@ 2015-07-01 15:47 ` Takashi Iwai
2015-07-01 16:16 ` Liam Girdwood
0 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-07-01 15:47 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, alsa-devel, Mark Brown
At Wed, 1 Jul 2015 14:44:24 +0100,
Liam Girdwood wrote:
>
> Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
It's a kind of example, so maybe better to put at the end of the
series?
Takashi
> ---
> src/conf/Makefile.am | 2 +-
> src/conf/topology/Makefile.am | 1 +
> src/conf/topology/broadwell/Makefile.am | 4 +
> src/conf/topology/broadwell/broadwell.conf | 375 +++++++++++++++++++++++++++++
> 4 files changed, 381 insertions(+), 1 deletion(-)
> create mode 100644 src/conf/topology/Makefile.am
> create mode 100644 src/conf/topology/broadwell/Makefile.am
> create mode 100644 src/conf/topology/broadwell/broadwell.conf
>
> diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am
> index 948d5a1..a04f73f 100644
> --- a/src/conf/Makefile.am
> +++ b/src/conf/Makefile.am
> @@ -1,4 +1,4 @@
> -SUBDIRS=cards pcm alsa.conf.d ucm
> +SUBDIRS=cards pcm alsa.conf.d ucm topology
>
> cfg_files = alsa.conf
> if BUILD_ALISP
> diff --git a/src/conf/topology/Makefile.am b/src/conf/topology/Makefile.am
> new file mode 100644
> index 0000000..f56a96c
> --- /dev/null
> +++ b/src/conf/topology/Makefile.am
> @@ -0,0 +1 @@
> +SUBDIRS=broadwell
> diff --git a/src/conf/topology/broadwell/Makefile.am b/src/conf/topology/broadwell/Makefile.am
> new file mode 100644
> index 0000000..35d1e83
> --- /dev/null
> +++ b/src/conf/topology/broadwell/Makefile.am
> @@ -0,0 +1,4 @@
> +alsaconfigdir = @ALSA_CONFIG_DIR@
> +topologydir = $(alsaconfigdir)/topology/broadwell
> +topology_DATA = broadwell.conf
> +EXTRA_DIST = $(topology_DATA)
> diff --git a/src/conf/topology/broadwell/broadwell.conf b/src/conf/topology/broadwell/broadwell.conf
> new file mode 100644
> index 0000000..05b3889
> --- /dev/null
> +++ b/src/conf/topology/broadwell/broadwell.conf
> @@ -0,0 +1,375 @@
> +# Dynamic Firmware Configuration for Broadwell
> +
> +# TLV
> +SectionTLV."hsw_vol_tlv" {
> + Comment "TLV used by both global and stream volumes"
> +
> + scale {
> + min "-9000"
> + step "300"
> + mute "1"
> + }
> +}
> +
> +# Controls
> +SectionControlMixer."Master Playback Volume" {
> + Comment "Global DSP volume"
> +
> + # control belongs to this index group
> + index "1"
> +
> + # Channel register and shift for Front Left/Right
> + channel."FL" {
> + reg "0"
> + shift "0"
> + }
> + channel."FR" {
> + reg "0"
> + shift "8"
> + }
> +
> + # max control value and whether value is inverted
> + max "31"
> + invert "false"
> +
> + # control uses bespoke driver get/put/info ID 0
> + ops."ctl" {
> + info "volsw"
> + get "256"
> + put "256"
> + }
> +
> + # uses TLV data above
> + tlv "hsw_vol_tlv"
> +}
> +
> +SectionControlMixer."Media0 Playback Volume" {
> + Comment "Offload 0 volume"
> +
> + # control belongs to this index group
> + index "1"
> +
> + # Channel register and shift for Front Left/Right
> + channel."FL" {
> + reg "1"
> + shift "0"
> + }
> + channel."FR" {
> + reg "1"
> + shift "8"
> + }
> +
> + # max control value and whether value is inverted
> + max "31"
> + invert "false"
> +
> + # control uses bespoke driver get/put/info ID 0
> + ops."ctl" {
> + info "volsw"
> + get "257"
> + put "257"
> + }
> +
> + # uses TLV data above
> + tlv "hsw_vol_tlv"
> +}
> +
> +SectionControlMixer."Media1 Playback Volume" {
> + Comment "Offload 1 volume"
> +
> + # control belongs to this index group
> + index "1"
> +
> + # Channel register and shift for Front Left/Right
> + channel."FL" {
> + reg "2"
> + shift "0"
> + }
> + channel."FR" {
> + reg "2"
> + shift "8"
> + }
> +
> + # max control value and whether value is inverted
> + max "31"
> + invert "false"
> +
> + # control uses bespoke driver get/put/info ID 0
> + ops."ctl" {
> + info "volsw"
> + get "257"
> + put "257"
> + }
> +
> + # uses TLV data above
> + tlv "hsw_vol_tlv"
> +}
> +
> +SectionControlMixer."Mic Capture Volume" {
> + Comment "Mic Capture volume"
> +
> + # control belongs to this index group
> + index "1"
> +
> + # Channel register and shift for Front Left/Right
> + channel."FL" {
> + reg "0"
> + shift "0"
> + }
> + channel."FR" {
> + reg "0"
> + shift "8"
> + }
> +
> + # max control value and whether value is inverted
> + max "31"
> + invert "false"
> +
> + # control uses bespoke driver get/put/info ID 0
> + ops."ctl" {
> + info "volsw"
> + get "257"
> + put "257"
> + }
> +
> + # uses TLV data above
> + tlv "hsw_vol_tlv"
> +}
> +
> +SectionWidget."SSP0 CODEC IN" {
> +
> + index "1"
> + type "aif_in"
> + no_pm "true"
> + shift "0"
> + invert "0"
> +}
> +
> +SectionWidget."SSP0 CODEC OUT" {
> +
> + index "1"
> + type "aif_out"
> + no_pm "true"
> + shift "0"
> + invert "0"
> +}
> +
> +SectionWidget."SSP1 BT IN" {
> +
> + index "1"
> + type "aif_in"
> + no_pm "true"
> + shift "0"
> + invert "0"
> +}
> +
> +SectionWidget."SSP1 BT OUT" {
> +
> + index "1"
> + type "aif_out"
> + no_pm "true"
> + shift "0"
> + invert "0"
> +}
> +
> +SectionWidget."Playback VMixer" {
> +
> + index "1"
> + type "mixer"
> + no_pm "true"
> + shift "0"
> + invert "0"
> +}
> +
> +# PCM Configurations supported by FW
> +SectionPCMConfig."PCM 48k Stereo 24bit" {
> +
> + config."playback" {
> + format "S24_LE"
> + rate "48000"
> + channels "2"
> + tdm_slot "0xf"
> + }
> +
> + config."capture" {
> + format "S24_LE"
> + rate "48000"
> + channels "2"
> + tdm_slot "0xf"
> + }
> +}
> +
> +SectionPCMConfig."PCM 48k Stereo 16bit" {
> +
> + config."playback" {
> + format "S16_LE"
> + rate "48000"
> + channels "2"
> + tdm_slot "0xf"
> + }
> +
> + config."capture" {
> + format "S16_LE"
> + rate "48000"
> + channels "2"
> + tdm_slot "0xf"
> + }
> +}
> +
> +SectionPCMConfig."PCM 48k 2P/4C 16bit" {
> +
> + config."playback" {
> + format "S16_LE"
> + rate "48000"
> + channels "2"
> + tdm_slot "0xf"
> + }
> +
> + config."capture" {
> + format "S16_LE"
> + rate "48000"
> + channels "4"
> + tdm_slot "0xf"
> + }
> +}
> +
> +# PCM capabilities supported by FW
> +SectionPCMCapabilities."System Playback" {
> +
> + formats "S24_LE,S16_LE"
> + rate_min "48000"
> + rate_max "48000"
> + channels_min "2"
> + channels_max "2"
> +}
> +
> +SectionPCMCapabilities."Analog Capture" {
> +
> + formats "S24_LE,S16_LE"
> + rate_min "48000"
> + rate_max "48000"
> + channels_min "2"
> + channels_max "4"
> +}
> +
> +SectionPCMCapabilities."Loopback Capture" {
> +
> + formats "S24_LE,S16_LE"
> + rate_min "48000"
> + rate_max "48000"
> + channels_min "2"
> + channels_max "2"
> +}
> +
> +SectionPCMCapabilities."Offload0 Playback" {
> + formats "S24_LE,S16_LE"
> + rate_min "8000"
> + rate_max "192000"
> + channels_min "2"
> + channels_max "2"
> +}
> +
> +SectionPCMCapabilities."Offload1 Playback" {
> + formats "S24_LE,S16_LE"
> + rate_min "8000"
> + rate_max "48000"
> + channels_min "2"
> + channels_max "2"
> +}
> +
> +# PCM devices exported by Firmware
> +SectionPCM."System Pin" {
> +
> + index "1"
> +
> + # used for binding to the PCM
> + ID "0"
> +
> + pcm."playback" {
> +
> + capabilities "System Playback"
> +
> + configs [
> + "PCM 48k Stereo 24bit"
> + "PCM 48k Stereo 16bit"
> + ]
> + }
> +
> + pcm."capture" {
> +
> + capabilities "Analog Capture"
> +
> + configs [
> + "PCM 48k Stereo 24bit"
> + "PCM 48k Stereo 16bit"
> + "PCM 48k 2P/4C 16bit"
> + ]
> + }
> +}
> +
> +SectionPCM."Offload0 Pin" {
> +
> + index "1"
> +
> + # used for binding to the PCM
> + ID "1"
> +
> + pcm."playback" {
> +
> + capabilities "Offload0 Playback"
> +
> + configs [
> + "PCM 48k Stereo 24bit"
> + "PCM 48k Stereo 16bit"
> + ]
> + }
> +}
> +
> +SectionPCM."Offload1 Pin" {
> +
> + index "1"
> +
> + # used for binding to the PCM
> + ID "2"
> +
> + pcm."playback" {
> +
> + capabilities "Offload1 Playback"
> +
> + configs [
> + "PCM 48k Stereo 24bit"
> + "PCM 48k Stereo 16bit"
> + ]
> + }
> +}
> +
> +SectionPCM."Loopback Pin" {
> +
> + index "1"
> +
> + # used for binding to the PCM
> + ID "3"
> +
> + pcm."capture" {
> +
> + capabilities "Loopback Capture"
> +
> + configs [
> + "PCM 48k Stereo 24bit"
> + "PCM 48k Stereo 16bit"
> + ]
> + }
> +}
> +
> +SectionGraph."dsp" {
> + index "1"
> +
> + lines [
> + "Playback VMixer, , System Playback"
> + "Playback VMixer, , Offload0 Playback"
> + "Playback VMixer, , Offload1 Playback"
> + "SSP0 CODEC OUT, , Playback VMixer"
> + "Loopback Capture, , Playback VMixer"
> + "Analog Capture, , SSP0 CODEC IN"
> + ]
> +}
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP
2015-07-01 15:47 ` Takashi Iwai
@ 2015-07-01 16:16 ` Liam Girdwood
0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 16:16 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Vinod Koul, alsa-devel, Mark Brown
On Wed, 2015-07-01 at 17:47 +0200, Takashi Iwai wrote:
> At Wed, 1 Jul 2015 14:44:24 +0100,
> Liam Girdwood wrote:
> >
> > Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
>
> It's a kind of example, so maybe better to put at the end of the
> series?
I did originally have it at the end, but moved it to show the schema
early for anyone reviewing the parser code. I'll stick it back for v3.
Liam
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 03/13] topology: Add topology core parser.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 16:00 ` Takashi Iwai
2015-07-01 13:44 ` [PATCH v2 04/13] topology: Add text section parser Liam Girdwood
` (9 subsequent siblings)
11 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
The topology core parses the high level topology file and calls the
individual object parsers when any new object element is detected at
the high level.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
include/topology.h | 75 ++++++++++
src/topology/elem.c | 187 ++++++++++++++++++++++++
src/topology/parser.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++
src/topology/tplg_local.h | 223 +++++++++++++++++++++++++++++
4 files changed, 842 insertions(+)
create mode 100644 include/topology.h
create mode 100644 src/topology/elem.c
create mode 100644 src/topology/parser.c
create mode 100644 src/topology/tplg_local.h
diff --git a/include/topology.h b/include/topology.h
new file mode 100644
index 0000000..d9b223f
--- /dev/null
+++ b/include/topology.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ */
+
+#ifndef __ALSA_TOPOLOGY_H
+#define __ALSA_TOPOLOGY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup topology Topology Interface
+ * The topology interface.
+ * See \ref Topology page for more details.
+ * \{
+ */
+
+/*! \page topology ALSA Topology Interface
+ *
+ * ALSA Topology Interface
+ *
+ */
+
+typedef struct snd_tplg snd_tplg_t;
+
+/**
+ * \brief Create a new topology parser instance.
+ * \return New topology parser instance
+ */
+snd_tplg_t *snd_tplg_new(void);
+
+/**
+ * \brief Free a topology parser instance.
+ * \param tplg Topology parser instance
+ */
+void snd_tplg_free(snd_tplg_t *tplg);
+
+/**
+ * \brief Parse and build topology text file into binary file.
+ * \param tplg Topology instance.
+ * \param infile Topology text input file to be parsed
+ * \param outfile Binary topology output file.
+ * \return Zero on sucess, otherwise a negative error code
+ */
+int snd_tplg_build(snd_tplg_t *tplg, const char *infile, const char *outfile);
+
+/**
+ * \brief Enable verbose reporting of binary file output
+ * \param tplg Topology Instance
+ * \param verbose Enable verbose output if non zero
+ */
+void snd_tplg_verbose(snd_tplg_t *tplg, int verbose);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_TOPOLOGY_H */
diff --git a/src/topology/elem.c b/src/topology/elem.c
new file mode 100644
index 0000000..0ce406b
--- /dev/null
+++ b/src/topology/elem.c
@@ -0,0 +1,187 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
+{
+ struct tplg_ref *ref;
+
+ ref = calloc(1, sizeof(*ref));
+ if (!ref)
+ return -ENOMEM;
+
+ strncpy(ref->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ ref->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+ ref->type = type;
+
+ list_add_tail(&ref->list, &elem->ref_list);
+ return 0;
+}
+
+void tplg_ref_free_list(struct list_head *base)
+{
+ struct list_head *pos, *npos;
+ struct tplg_ref *ref;
+
+ list_for_each_safe(pos, npos, base) {
+ ref = list_entry(pos, struct tplg_ref, list);
+ list_del(&ref->list);
+ free(ref);
+ }
+}
+
+struct tplg_elem *tplg_elem_new(void)
+{
+ struct tplg_elem *elem;
+
+ elem = calloc(1, sizeof(*elem));
+ if (!elem)
+ return NULL;
+
+ INIT_LIST_HEAD(&elem->ref_list);
+ return elem;
+}
+
+void tplg_elem_free(struct tplg_elem *elem)
+{
+ tplg_ref_free_list(&elem->ref_list);
+
+ /* free struct snd_tplg_ object,
+ * the union pointers share the same address
+ */
+ if (elem->mixer_ctrl)
+ free(elem->mixer_ctrl);
+
+ free(elem);
+}
+
+void tplg_elem_free_list(struct list_head *base)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+
+ list_for_each_safe(pos, npos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+ list_del(&elem->list);
+ tplg_elem_free(elem);
+ }
+}
+
+struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id,
+ unsigned int type)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!strcmp(elem->id, id) && elem->type == type)
+ return elem;
+ }
+
+ return NULL;
+}
+
+/* create a new common element and object */
+struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ snd_config_t *cfg, enum parser_type type)
+{
+ struct tplg_elem *elem;
+ const char *id;
+ int obj_size = 0;
+ void *obj;
+
+ elem = tplg_elem_new();
+ if (!elem)
+ return NULL;
+
+ snd_config_get_id(cfg, &id);
+ strncpy(elem->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ elem->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+ switch (type) {
+ case PARSER_TYPE_DATA:
+ list_add_tail(&elem->list, &tplg->pdata_list);
+ break;
+ case PARSER_TYPE_TEXT:
+ list_add_tail(&elem->list, &tplg->text_list);
+ break;
+ case PARSER_TYPE_TLV:
+ list_add_tail(&elem->list, &tplg->tlv_list);
+ elem->size = sizeof(struct snd_soc_tplg_ctl_tlv);
+ break;
+ case PARSER_TYPE_BYTES:
+ list_add_tail(&elem->list, &tplg->bytes_ext_list);
+ obj_size = sizeof(struct snd_soc_tplg_bytes_control);
+ break;
+ case PARSER_TYPE_ENUM:
+ list_add_tail(&elem->list, &tplg->enum_list);
+ obj_size = sizeof(struct snd_soc_tplg_enum_control);
+ break;
+ case SND_SOC_TPLG_TYPE_MIXER:
+ list_add_tail(&elem->list, &tplg->mixer_list);
+ obj_size = sizeof(struct snd_soc_tplg_mixer_control);
+ break;
+ case PARSER_TYPE_DAPM_WIDGET:
+ list_add_tail(&elem->list, &tplg->widget_list);
+ obj_size = sizeof(struct snd_soc_tplg_dapm_widget);
+ break;
+ case PARSER_TYPE_STREAM_CONFIG:
+ list_add_tail(&elem->list, &tplg->pcm_config_list);
+ obj_size = sizeof(struct snd_soc_tplg_stream_config);
+ break;
+ case PARSER_TYPE_STREAM_CAPS:
+ list_add_tail(&elem->list, &tplg->pcm_caps_list);
+ obj_size = sizeof(struct snd_soc_tplg_stream_caps);
+ break;
+ case PARSER_TYPE_PCM:
+ list_add_tail(&elem->list, &tplg->pcm_list);
+ obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+ break;
+ case PARSER_TYPE_BE:
+ list_add_tail(&elem->list, &tplg->be_list);
+ obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+ break;
+ case PARSER_TYPE_CC:
+ list_add_tail(&elem->list, &tplg->cc_list);
+ obj_size = sizeof(struct snd_soc_tplg_pcm_dai);
+ break;
+ default:
+ free(elem);
+ return NULL;
+ }
+
+ /* create new object too if required */
+ if (obj_size > 0) {
+ obj = calloc(1, obj_size);
+ if (obj == NULL) {
+ free(elem);
+ return NULL;
+ }
+
+ elem->obj = obj;
+ elem->size = obj_size;
+ }
+
+ elem->type = type;
+ return elem;
+}
diff --git a/src/topology/parser.c b/src/topology/parser.c
new file mode 100644
index 0000000..f813deb
--- /dev/null
+++ b/src/topology/parser.c
@@ -0,0 +1,357 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/*
+ * Parse compound
+ */
+int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+ void *private)
+{
+ const char *id;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ int err = -EINVAL;
+
+ if (snd_config_get_id(cfg, &id) < 0)
+ return -EINVAL;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ fprintf(stderr, "error: compound type expected for %s", id);
+ return -EINVAL;
+ }
+
+ /* parse compound */
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ fprintf(stderr, "error: compound type expected for %s, is %d",
+ id, snd_config_get_type(cfg));
+ return -EINVAL;
+ }
+
+ err = fcn(tplg, n, private);
+ if (err < 0)
+ return err;
+ }
+
+ return err;
+}
+
+static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ fprintf(stderr, "error: compound type expected at top level");
+ return -EINVAL;
+ }
+
+ /* parse topology config sections */
+ 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, "SectionTLV") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_tlv,
+ NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionControlMixer") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_control_mixer, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionControlEnum") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_control_enum, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionControlBytes") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_control_bytes, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionWidget") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_dapm_widget, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionPCMConfig") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_config, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionPCMCapabilities") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_caps, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionPCM") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionBE") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_be,
+ NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionCC") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_cc,
+ NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionGraph") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_dapm_graph, NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionText") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_text,
+ NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "SectionData") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_data,
+ NULL);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ fprintf(stderr, "error: unknown section %s\n", id);
+ }
+ return 0;
+}
+
+static int tplg_load_config(const char *file, snd_config_t **cfg)
+{
+ FILE *fp;
+ snd_input_t *in;
+ snd_config_t *top;
+ int ret;
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ fprintf(stdout, "error: could not open configuration file %s",
+ file);
+ return -errno;
+ }
+
+ ret = snd_input_stdio_attach(&in, fp, 1);
+ if (ret < 0) {
+ fprintf(stdout, "error: could not attach stdio %s", file);
+ goto err;
+ }
+ ret = snd_config_top(&top);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_config_load(top, in);
+ if (ret < 0) {
+ fprintf(stdout, "error: could not load configuration file %s",
+ file);
+ goto err_load;
+ }
+
+ ret = snd_input_close(in);
+ if (ret < 0)
+ goto err_load;
+
+ *cfg = top;
+ return 0;
+
+err_load:
+ snd_config_delete(top);
+err:
+ fclose(fp);
+ return ret;
+}
+
+static int tplg_build_integ(snd_tplg_t *tplg)
+{
+ int err;
+
+ err = tplg_build_controls(tplg);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_widgets(tplg);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_pcm_dai(tplg, PARSER_TYPE_PCM);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_pcm_dai(tplg, PARSER_TYPE_BE);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_pcm_dai(tplg, PARSER_TYPE_CC);
+ if (err < 0)
+ return err;
+
+ err = tplg_build_routes(tplg);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int snd_tplg_build(snd_tplg_t *tplg, const char *infile, const char *outfile)
+{
+ snd_config_t *cfg = NULL;
+ int err = 0;
+
+ /* delete any old output files */
+ unlink(outfile);
+
+ tplg->out_fd =
+ open(outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (tplg->out_fd < 0) {
+ fprintf(stderr, "error: failed to open %s err %d\n",
+ outfile, -errno);
+ return -errno;
+ }
+
+ err = tplg_load_config(infile, &cfg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to load topology file %s\n",
+ infile);
+ return err;
+ }
+
+ err = tplg_parse_config(tplg, cfg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse topology\n");
+ goto out;
+ }
+
+ err = tplg_build_integ(tplg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to check topology integrity\n");
+ goto out;
+ }
+
+ err = tplg_write_data(tplg);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to write data %d\n", err);
+ goto out;
+ }
+
+out:
+ snd_config_delete(cfg);
+ close(tplg->out_fd);
+ return err;
+}
+
+void snd_tplg_verbose(snd_tplg_t *tplg, int verbose)
+{
+ tplg->verbose = verbose;
+}
+
+snd_tplg_t *snd_tplg_new(void)
+{
+ snd_tplg_t *tplg;
+
+ tplg = calloc(1, sizeof(snd_tplg_t));
+ if (!tplg)
+ return NULL;
+
+ INIT_LIST_HEAD(&tplg->tlv_list);
+ INIT_LIST_HEAD(&tplg->widget_list);
+ INIT_LIST_HEAD(&tplg->pcm_list);
+ INIT_LIST_HEAD(&tplg->be_list);
+ INIT_LIST_HEAD(&tplg->cc_list);
+ INIT_LIST_HEAD(&tplg->route_list);
+ INIT_LIST_HEAD(&tplg->pdata_list);
+ INIT_LIST_HEAD(&tplg->text_list);
+ INIT_LIST_HEAD(&tplg->pcm_config_list);
+ INIT_LIST_HEAD(&tplg->pcm_caps_list);
+ INIT_LIST_HEAD(&tplg->mixer_list);
+ INIT_LIST_HEAD(&tplg->enum_list);
+ INIT_LIST_HEAD(&tplg->bytes_ext_list);
+
+ return tplg;
+}
+
+void snd_tplg_free(snd_tplg_t *tplg)
+{
+ tplg_elem_free_list(&tplg->tlv_list);
+ tplg_elem_free_list(&tplg->widget_list);
+ tplg_elem_free_list(&tplg->pcm_list);
+ tplg_elem_free_list(&tplg->be_list);
+ tplg_elem_free_list(&tplg->cc_list);
+ tplg_elem_free_list(&tplg->route_list);
+ tplg_elem_free_list(&tplg->pdata_list);
+ tplg_elem_free_list(&tplg->text_list);
+ tplg_elem_free_list(&tplg->pcm_config_list);
+ tplg_elem_free_list(&tplg->pcm_caps_list);
+ tplg_elem_free_list(&tplg->mixer_list);
+ tplg_elem_free_list(&tplg->enum_list);
+ tplg_elem_free_list(&tplg->bytes_ext_list);
+
+ free(tplg);
+}
diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h
new file mode 100644
index 0000000..ca47879
--- /dev/null
+++ b/src/topology/tplg_local.h
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+#include <limits.h>
+#include <stdint.h>
+#include <linux/types.h>
+
+#include "local.h"
+#include "list.h"
+#include "topology.h"
+
+#include <sound/asound.h>
+#include <sound/asoc.h>
+#include <sound/tlv.h>
+
+#define TPLG_DEBUG
+#ifdef TPLG_DEBUG
+#define tplg_dbg SNDERR
+#else
+#define tplg_dbg(fmt, arg...) do { } while (0)
+#endif
+
+#define MAX_FILE 256
+#define TPLG_MAX_PRIV_SIZE (1024 * 128)
+#define ALSA_TPLG_DIR ALSA_CONFIG_DIR "/topology"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+/** The name of the environment variable containing the tplg directory */
+#define ALSA_CONFIG_TPLG_VAR "ALSA_CONFIG_TPLG"
+
+struct tplg_ref;
+struct tplg_elem;
+
+/* internal topology object type not used by kernel */
+enum parser_type {
+ PARSER_TYPE_TLV = 0,
+ PARSER_TYPE_MIXER,
+ PARSER_TYPE_ENUM,
+ PARSER_TYPE_TEXT,
+ PARSER_TYPE_DATA,
+ PARSER_TYPE_BYTES,
+ PARSER_TYPE_STREAM_CONFIG,
+ PARSER_TYPE_STREAM_CAPS,
+ PARSER_TYPE_PCM,
+ PARSER_TYPE_DAPM_WIDGET,
+ PARSER_TYPE_DAPM_GRAPH,
+ PARSER_TYPE_BE,
+ PARSER_TYPE_CC,
+};
+
+struct snd_tplg {
+
+ /* opaque vendor data */
+ int vendor_fd;
+ char *vendor_name;
+
+ /* out file */
+ int out_fd;
+
+ int verbose;
+ unsigned int version;
+
+ /* runtime state */
+ unsigned int next_hdr_pos;
+ int index;
+ int channel_idx;
+
+ /* list of each element type */
+ struct list_head tlv_list;
+ struct list_head widget_list;
+ struct list_head pcm_list;
+ struct list_head be_list;
+ struct list_head cc_list;
+ struct list_head route_list;
+ struct list_head text_list;
+ struct list_head pdata_list;
+ struct list_head pcm_config_list;
+ struct list_head pcm_caps_list;
+
+ /* type-specific control lists */
+ struct list_head mixer_list;
+ struct list_head enum_list;
+ struct list_head bytes_ext_list;
+};
+
+/* object text references */
+struct tplg_ref {
+ unsigned int type;
+ struct tplg_elem *elem;
+ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct list_head list;
+};
+
+/* topology element */
+struct tplg_elem {
+
+ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ /* storage for texts and data if this is text or data elem*/
+ char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ int index;
+ enum parser_type type;
+
+ int size; /* total size of this object inc pdata and ref objects */
+ int compound_elem; /* dont write this element as individual elem */
+
+ /* UAPI object for this elem */
+ union {
+ void *obj;
+ struct snd_soc_tplg_mixer_control *mixer_ctrl;
+ struct snd_soc_tplg_enum_control *enum_ctrl;
+ struct snd_soc_tplg_bytes_control *bytes_ext;
+ struct snd_soc_tplg_dapm_widget *widget;
+ struct snd_soc_tplg_pcm_dai *pcm;
+ struct snd_soc_tplg_pcm_dai *be;
+ struct snd_soc_tplg_pcm_dai *cc;
+ struct snd_soc_tplg_dapm_graph_elem *route;
+ struct snd_soc_tplg_stream_config *stream_cfg;
+ struct snd_soc_tplg_stream_caps *stream_caps;
+
+ /* these do not map to UAPI structs but are internal only */
+ struct snd_soc_tplg_ctl_tlv *tlv;
+ struct snd_soc_tplg_private *data;
+ };
+
+ /* an element may refer to other elements:
+ * a mixer control may refer to a tlv,
+ * a widget may refer to a mixer control array,
+ * a graph may refer to some widgets.
+ */
+ struct list_head ref_list;
+ struct list_head list; /* list of all elements with same type */
+};
+
+struct map_elem {
+ const char *name;
+ int id;
+};
+
+int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
+ int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
+ void *private);
+
+int tplg_write_data(snd_tplg_t *tplg);
+
+int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_data(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);
+
+int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_pcm_config(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_pcm_caps(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_pcm_cap_cfg(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private);
+
+int tplg_parse_pcm(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_be(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_parse_cc(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+
+int tplg_build_controls(snd_tplg_t *tplg);
+int tplg_build_widgets(snd_tplg_t *tplg);
+int tplg_build_routes(snd_tplg_t *tplg);
+int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type);
+
+int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref);
+
+int tplg_ref_add(struct tplg_elem *elem, int type, const char* id);
+
+struct tplg_elem *tplg_elem_new(void);
+void tplg_elem_free(struct tplg_elem *elem);
+void tplg_elem_free_list(struct list_head *base);
+struct tplg_elem *tplg_elem_lookup(struct list_head *base,
+ const char* id,
+ unsigned int type);
+struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
+ snd_config_t *cfg, enum parser_type type);
+
+int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+
+int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private);
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base,
+ const char* id);
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v2 03/13] topology: Add topology core parser.
2015-07-01 13:44 ` [PATCH v2 03/13] topology: Add topology core parser Liam Girdwood
@ 2015-07-01 16:00 ` Takashi Iwai
0 siblings, 0 replies; 26+ messages in thread
From: Takashi Iwai @ 2015-07-01 16:00 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, alsa-devel, Mark Brown
At Wed, 1 Jul 2015 14:44:25 +0100,
Liam Girdwood wrote:
>
> +int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
> + int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
> + void *private)
> +{
> + const char *id;
> + snd_config_iterator_t i, next;
> + snd_config_t *n;
> + int err = -EINVAL;
> +
> + if (snd_config_get_id(cfg, &id) < 0)
> + return -EINVAL;
> +
> + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
> + fprintf(stderr, "error: compound type expected for %s", id);
It's not good to print an error unconditionally from a system
library. Better to use SNDERR() macro.
> +int snd_tplg_build(snd_tplg_t *tplg, const char *infile, const char *outfile)
> +{
> + snd_config_t *cfg = NULL;
> + int err = 0;
> +
> + /* delete any old output files */
> + unlink(outfile);
> +
> + tplg->out_fd =
> + open(outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
> + if (tplg->out_fd < 0) {
> + fprintf(stderr, "error: failed to open %s err %d\n",
> + outfile, -errno);
> + return -errno;
> + }
> +
> + err = tplg_load_config(infile, &cfg);
> + if (err < 0) {
> + fprintf(stderr, "error: failed to load topology file %s\n",
> + infile);
> + return err;
The outfile is left opened.
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 04/13] topology: Add text section parser.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 02/13] conf: topology: Add topology file for broadwell audio DSP Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 03/13] topology: Add topology core parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 16:03 ` Takashi Iwai
2015-07-01 13:44 ` [PATCH v2 05/13] topology: Add PCM parser Liam Girdwood
` (8 subsequent siblings)
11 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Parse text lists (like enum values) and store for later attachment
to other objects.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/text.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 src/topology/text.c
diff --git a/src/topology/text.c b/src/topology/text.c
new file mode 100644
index 0000000..d24d027
--- /dev/null
+++ b/src/topology/text.c
@@ -0,0 +1,99 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+#define TEXT_SIZE_MAX \
+ (SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+
+static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+ int j = 0;
+
+ tplg_dbg(" Text Values: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ if (j == SND_SOC_TPLG_NUM_TEXTS) {
+ tplg_dbg("error: text string number exceeds %d\n", j);
+ return -ENOMEM;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ strncpy(&elem->texts[j][0], value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ elem->texts[j][SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+ tplg_dbg("\t%s\n", &elem->texts[j][0]);
+
+ j++;
+ }
+
+ return 0;
+}
+
+/* Parse Text data.
+ *
+ * Object text strings.
+ *
+ * SectionText."text name" {
+ *
+ * Values [
+ *
+ * ]
+ * }
+ */
+int tplg_parse_text(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;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_TEXT);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "values") == 0) {
+ err = parse_text_values(n, elem);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse text values");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v2 04/13] topology: Add text section parser.
2015-07-01 13:44 ` [PATCH v2 04/13] topology: Add text section parser Liam Girdwood
@ 2015-07-01 16:03 ` Takashi Iwai
0 siblings, 0 replies; 26+ messages in thread
From: Takashi Iwai @ 2015-07-01 16:03 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, alsa-devel, Mark Brown
At Wed, 1 Jul 2015 14:44:26 +0100,
Liam Girdwood wrote:
>
> +static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem)
> +{
> + snd_config_iterator_t i, next;
> + snd_config_t *n;
> + const char *value = NULL;
> + int j = 0;
> +
> + tplg_dbg(" Text Values: %s\n", elem->id);
> +
> + snd_config_for_each(i, next, cfg) {
> + n = snd_config_iterator_entry(i);
> +
> + if (j == SND_SOC_TPLG_NUM_TEXTS) {
> + tplg_dbg("error: text string number exceeds %d\n", j);
> + return -ENOMEM;
> + }
> +
> + /* get value */
> + if (snd_config_get_string(n, &value) < 0)
> + continue;
> +
> + strncpy(&elem->texts[j][0], value,
> + SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
> + elem->texts[j][SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
There are multiple calls like this, so maybe it's worth to create a
simple helper to copy the ctl element id string instead of open coding
at each place.
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 05/13] topology: Add PCM parser.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (2 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 04/13] topology: Add text section parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 16:14 ` Takashi Iwai
2015-07-01 13:44 ` [PATCH v2 06/13] topology: Add operations parser Liam Girdwood
` (7 subsequent siblings)
11 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Parse PCM configurations and capabilities. These can then be used to define
the capabilities and config for FE DAI links, PCM devices and
codec <-> codec style links.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/pcm.c | 759 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 759 insertions(+)
create mode 100644 src/topology/pcm.c
diff --git a/src/topology/pcm.c b/src/topology/pcm.c
new file mode 100644
index 0000000..243472b
--- /dev/null
+++ b/src/topology/pcm.c
@@ -0,0 +1,759 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != PARSER_TYPE_PCM)
+ return NULL;
+
+ pcm_dai = elem->pcm;
+
+ if (pcm_dai && (!strcmp(pcm_dai->capconf[0].caps.name, id)
+ || !strcmp(pcm_dai->capconf[1].caps.name, id)))
+ return elem;
+ }
+
+ 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)
+{
+ struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
+
+ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*caps), ref_elem->id, id);
+
+ memcpy((void*)caps, ref_caps, sizeof(*caps));
+}
+
+/* copy referenced config to the pcm */
+static void copy_pcm_config(const char *id,
+ struct snd_soc_tplg_stream_config *cfg, struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_stream_config *ref_cfg = ref_elem->stream_cfg;
+
+ tplg_dbg("Copy pcm config (%ld bytes) from '%s' to '%s' \n",
+ sizeof(*cfg), ref_elem->id, id);
+
+ memcpy((void*)cfg, ref_cfg, sizeof(*cfg));
+}
+
+/* check referenced config and caps for a pcm */
+static int tplg_build_pcm_cfg_caps(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_elem *ref_elem = NULL;
+ struct snd_soc_tplg_pcm_cfg_caps *capconf;
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ unsigned int i, j;
+
+ switch (elem->type) {
+ case PARSER_TYPE_PCM:
+ pcm_dai = elem->pcm;
+ break;
+ case PARSER_TYPE_BE:
+ pcm_dai = elem->be;
+ break;
+ case PARSER_TYPE_CC:
+ pcm_dai = elem->cc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ capconf = &pcm_dai->capconf[i];
+
+ ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list,
+ capconf->caps.name, PARSER_TYPE_STREAM_CAPS);
+
+ if (ref_elem != NULL)
+ copy_pcm_caps(elem->id, &capconf->caps, ref_elem);
+
+ for (j = 0; j < capconf->num_configs; j++) {
+ ref_elem = tplg_elem_lookup(&tplg->pcm_config_list,
+ capconf->configs[j].name,
+ PARSER_TYPE_STREAM_CONFIG);
+
+ if (ref_elem != NULL)
+ copy_pcm_config(elem->id,
+ &capconf->configs[j],
+ ref_elem);
+ }
+ }
+
+ return 0;
+}
+
+int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type)
+{
+ struct list_head *base, *pos, *npos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ switch (type) {
+ case PARSER_TYPE_PCM:
+ base = &tplg->pcm_list;
+ break;
+ case PARSER_TYPE_BE:
+ base = &tplg->be_list;
+ break;
+ case PARSER_TYPE_CC:
+ base = &tplg->cc_list;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (elem->type != type) {
+ fprintf(stderr, "error: invalid elem '%s'\n", elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_pcm_cfg_caps(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* PCM stream configuration
+ *
+ * Describes the PCM configuration for playback and capture streams.
+ *
+ * config."name" {
+ * format "S24_LE"
+ * rate "48000"
+ * channels "2"
+ * tdm_slot "0xf"
+ * }
+ */
+static int tplg_parse_stream_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_stream_config *sc = private;
+ struct snd_soc_tplg_stream *stream;
+ const char *id, *val;
+ snd_pcm_format_t format;
+ int ret;
+
+ snd_config_get_id(cfg, &id);
+
+ if (strcmp(id, "playback") == 0)
+ stream = &sc->playback;
+ else if (strcmp(id, "capture") == 0)
+ stream = &sc->capture;
+ else
+ return -EINVAL;
+
+ tplg_dbg("\t%s:\n", id);
+
+ stream->size = sizeof(*stream);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ if (snd_config_get_id(n, &id) < 0)
+ return -EINVAL;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "format") == 0) {
+ format = snd_pcm_format_value(val);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+ fprintf(stderr, "error: unsupported stream format %s\n",
+ val);
+ return -EINVAL;
+ }
+
+ stream->format = format;
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate") == 0) {
+ stream->rate = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->rate);
+ continue;
+ }
+
+ if (strcmp(id, "channels") == 0) {
+ stream->channels = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, stream->channels);
+ continue;
+ }
+
+ if (strcmp(id, "tdm_slot") == 0) {
+ stream->tdm_slot = strtol(val, NULL, 16);
+ tplg_dbg("\t\t%s: 0x%x\n", id, stream->tdm_slot);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm configuration
+ *
+ * SectionPCMConfig."PCM config name" {
+ *
+ * config."playback" {
+ *
+ * }
+ *
+ * config."capture" {
+ *
+ * }
+ * }
+ */
+int tplg_parse_pcm_config(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_config *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_STREAM_CONFIG);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_cfg;
+ sc->size = elem->size;
+
+ tplg_dbg(" PCM Config: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "config") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_stream_cfg, sc);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str)
+{
+ char *s = NULL;
+ snd_pcm_format_t format;
+ int i = 0, ret;
+
+ s = strtok(str, ",");
+ while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) {
+ format = snd_pcm_format_value(s);
+ if (format == SND_PCM_FORMAT_UNKNOWN) {
+ fprintf(stderr, "error: unsupported stream format %s\n", s);
+ return -EINVAL;
+ }
+
+ caps->formats[i] = format;
+ s = strtok(NULL, ", ");
+ i++;
+ }
+
+ return 0;
+}
+
+/* Parse pcm Capabilities
+ *
+ * SectionPCMCapabilities." PCM capabilities name" {
+ *
+ * formats "S24_LE,S16_LE"
+ * rate_min "48000"
+ * rate_max "48000"
+ * channels_min "2"
+ * channels_max "2"
+ * }
+ */
+int tplg_parse_pcm_caps(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_stream_caps *sc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val;
+ char *s;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_STREAM_CAPS);
+ if (!elem)
+ return -ENOMEM;
+
+ sc = elem->stream_caps;
+ sc->size = elem->size;
+ strncpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ sc->name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+ tplg_dbg(" PCM Capabilities: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(id, "formats") == 0) {
+ s = strdup(val);
+ if (s == NULL)
+ return -ENOMEM;
+
+ err = split_format(sc, s);
+ free(s);
+
+ if (err < 0)
+ return err;
+
+ tplg_dbg("\t\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "rate_min") == 0) {
+ sc->rate_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_min);
+ continue;
+ }
+
+ if (strcmp(id, "rate_max") == 0) {
+ sc->rate_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->rate_max);
+ continue;
+ }
+
+ if (strcmp(id, "channels_min") == 0) {
+ sc->channels_min = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_min);
+ continue;
+ }
+
+ if (strcmp(id, "channels_max") == 0) {
+ sc->channels_max = atoi(val);
+ tplg_dbg("\t\t%s: %d\n", id, sc->channels_max);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int tplg_parse_pcm_cfg(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ struct snd_soc_tplg_pcm_cfg_caps *capconf = private;
+ struct snd_soc_tplg_stream_config *configs = capconf->configs;
+ unsigned int *num_configs = &capconf->num_configs;
+ const char *value;
+
+ if (*num_configs == SND_SOC_TPLG_STREAM_CONFIG_MAX)
+ return -EINVAL;
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return EINVAL;
+
+ strncpy(configs[*num_configs].name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ configs[*num_configs].name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+ *num_configs += 1;
+
+ tplg_dbg("\t\t\t%s\n", value);
+
+ return 0;
+}
+
+/* Parse the cap and config of a pcm.
+ *
+ * pcm."name" {
+ *
+ * capabilities "System playback"
+ *
+ * configs [
+ * "PCM 48k Stereo 24bit"
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ */
+int tplg_parse_pcm_cap_cfg(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_dai *pcm_dai;
+ const char *id, *value;
+ int err, stream;
+
+ if (elem->type == PARSER_TYPE_PCM)
+ pcm_dai = elem->pcm;
+ else if (elem->type == PARSER_TYPE_BE)
+ pcm_dai = elem->be;
+ else if (elem->type == PARSER_TYPE_CC)
+ pcm_dai = elem->cc;
+ else
+ return -EINVAL;
+
+ snd_config_get_id(cfg, &id);
+
+ tplg_dbg("\t%s:\n", id);
+
+ if (strcmp(id, "playback") == 0) {
+ stream = SND_SOC_TPLG_STREAM_PLAYBACK;
+ pcm_dai->playback = 1;
+ } else if (strcmp(id, "capture") == 0) {
+ stream = SND_SOC_TPLG_STREAM_CAPTURE;
+ pcm_dai->capture = 1;
+ } else
+ return -EINVAL;
+
+ 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, "capabilities") == 0) {
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ strncpy(pcm_dai->capconf[stream].caps.name, value,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ pcm_dai->capconf[stream].caps.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+ tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value);
+ continue;
+ }
+
+ if (strcmp(id, "configs") == 0) {
+ tplg_dbg("\t\tconfigs:\n");
+ err = tplg_parse_compound(tplg, n, tplg_parse_pcm_cfg,
+ &pcm_dai->capconf[stream]);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse pcm
+ *
+ * SectionPCM."System Pin" {
+ *
+ * index "1"
+ *
+ * # used for binding to the PCM
+ * ID "0"
+ *
+ * pcm."playback" {
+ * capabilities "System Playback"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * }
+ *
+ * pcm."capture" {
+ * capabilities "Analog Capture"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * config "PCM 48k 2P/4C 16bit"
+ * }
+ * }
+ */
+int tplg_parse_pcm(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_PCM);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->pcm;
+ pcm_dai->size = elem->size;
+ strncpy(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ pcm_dai->name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+
+ tplg_dbg(" PCM: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "ID") == 0) {
+ 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);
+ continue;
+ }
+
+ if (strcmp(id, "pcm") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse be
+ *
+ * SectionBE."SSP0-Codec" {
+ *
+ * index "1"
+ *
+ * # used for binding to the PCM
+ * ID "0"
+ *
+ * be."playback" {
+ * capabilities "System Playback"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * }
+ *
+ * be."capture" {
+ * capabilities "Analog Capture"
+ * config "PCM 48k Stereo 24bit"
+ * config "PCM 48k Stereo 16bit"
+ * config "PCM 48k 2P/4C 16bit"
+ * }
+ * }
+ */
+int tplg_parse_be(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_BE);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->be;
+ pcm_dai->size = elem->size;
+ strncpy(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ pcm_dai->name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN] = 0;
+
+ tplg_dbg(" BE: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "ID") == 0) {
+ 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);
+ continue;
+ }
+
+ if (strcmp(id, "be") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse cc
+ *
+ * SectionCC."FM-Codec" {
+ *
+ * index "1"
+ *
+ * # used for binding to the CC link
+ * ID "0"
+ *
+ * # CC DAI link capabilities and supported configs
+ * cc."playback" {
+ *
+ * capabilities "System playback"
+ *
+ * configs [
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ *
+ * cc."capture" {
+ *
+ * capabilities "Analog capture"
+ *
+ * configs [
+ * "PCM 48k Stereo 16bit"
+ * ]
+ * }
+ * }
+ */
+int tplg_parse_cc(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_pcm_dai *pcm_dai;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_CC);
+ if (!elem)
+ return -ENOMEM;
+
+ pcm_dai = elem->cc;
+ pcm_dai->size = elem->size;
+
+ tplg_dbg(" CC: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "ID") == 0) {
+ 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);
+ continue;
+ }
+
+ if (strcmp(id, "cc") == 0) {
+ err = tplg_parse_compound(tplg, n,
+ tplg_parse_pcm_cap_cfg, elem);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v2 05/13] topology: Add PCM parser.
2015-07-01 13:44 ` [PATCH v2 05/13] topology: Add PCM parser Liam Girdwood
@ 2015-07-01 16:14 ` Takashi Iwai
2015-07-01 16:20 ` Liam Girdwood
0 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-07-01 16:14 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, alsa-devel, Mark Brown
At Wed, 1 Jul 2015 14:44:27 +0100,
Liam Girdwood wrote:
>
> +/* 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)
> +{
> + struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
> +
> + tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n",
> + sizeof(*caps), ref_elem->id, id);
> +
> + memcpy((void*)caps, ref_caps, sizeof(*caps));
This can be simply
*caps = *ref_caps;
> +/* copy referenced config to the pcm */
> +static void copy_pcm_config(const char *id,
> + struct snd_soc_tplg_stream_config *cfg, struct tplg_elem *ref_elem)
> +{
> + struct snd_soc_tplg_stream_config *ref_cfg = ref_elem->stream_cfg;
> +
> + tplg_dbg("Copy pcm config (%ld bytes) from '%s' to '%s' \n",
> + sizeof(*cfg), ref_elem->id, id);
> +
> + memcpy((void*)cfg, ref_cfg, sizeof(*cfg));
Ditto.
> +int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type)
> +{
> + struct list_head *base, *pos, *npos;
> + struct tplg_elem *elem;
> + int err = 0;
> +
> + switch (type) {
> + case PARSER_TYPE_PCM:
> + base = &tplg->pcm_list;
> + break;
> + case PARSER_TYPE_BE:
> + base = &tplg->be_list;
> + break;
> + case PARSER_TYPE_CC:
> + base = &tplg->cc_list;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + list_for_each_safe(pos, npos, base) {
Does it need to be the safe version?
> + elem = list_entry(pos, struct tplg_elem, list);
> + if (elem->type != type) {
> + fprintf(stderr, "error: invalid elem '%s'\n", elem->id);
> + return -EINVAL;
> + }
> +
> + err = tplg_build_pcm_cfg_caps(tplg, elem);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +/* PCM stream configuration
> + *
> + * Describes the PCM configuration for playback and capture streams.
> + *
> + * config."name" {
> + * format "S24_LE"
> + * rate "48000"
> + * channels "2"
> + * tdm_slot "0xf"
> + * }
Such a description should go to doxygen comment, no?
Also, it'd be better to mention more clearly that "name" here
corresponds to the stream direction.
> +int tplg_parse_pcm(snd_tplg_t *tplg,
> + snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
> +{
> + struct snd_soc_tplg_pcm_dai *pcm_dai;
> + struct tplg_elem *elem;
> + snd_config_iterator_t i, next;
> + snd_config_t *n;
> + const char *id, *val = NULL;
> + int err;
> +
> + elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_PCM);
> + if (!elem)
> + return -ENOMEM;
> +
> + pcm_dai = elem->pcm;
> + pcm_dai->size = elem->size;
> + strncpy(pcm_dai->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
> + pcm_dai->name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
> +
> + tplg_dbg(" PCM: %s\n", elem->id);
> +
> + snd_config_for_each(i, next, cfg) {
> +
> + n = snd_config_iterator_entry(i);
> + if (snd_config_get_id(n, &id) < 0)
> + continue;
> +
> + /* skip comments */
> + if (strcmp(id, "comment") == 0)
> + continue;
> + if (id[0] == '#')
> + continue;
> +
> + if (strcmp(id, "index") == 0) {
> + if (snd_config_get_string(n, &val) < 0)
> + return -EINVAL;
> +
> + elem->index = atoi(val);
> + tplg_dbg("\t%s: %d\n", id, elem->index);
> + continue;
> + }
> +
> + if (strcmp(id, "ID") == 0) {
Only this word is capital letters while others are not?
I don't mind too much, but just wondered, because currently the parser
is case-sensitive.
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 05/13] topology: Add PCM parser.
2015-07-01 16:14 ` Takashi Iwai
@ 2015-07-01 16:20 ` Liam Girdwood
0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 16:20 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Vinod Koul, alsa-devel, Mark Brown
On Wed, 2015-07-01 at 18:14 +0200, Takashi Iwai wrote:
> At Wed, 1 Jul 2015 14:44:27 +0100,
> Liam Girdwood wrote:
> >
> inue;
> > + }
> > +
> > + if (strcmp(id, "ID") == 0) {
>
> Only this word is capital letters while others are not?
> I don't mind too much, but just wondered, because currently the parser
> is case-sensitive.
Oh, I think this is just historical. It's probably likely that one of
the other developers had a preference for upper case or because it was
the shortened form of the word. I change this to lower case though.
Liam
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 06/13] topology: Add operations parser
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (3 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 05/13] topology: Add PCM parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 07/13] topology: Add private data parser Liam Girdwood
` (6 subsequent siblings)
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Parse operations so we can bind them to kcontrols in the kernel.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/ops.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 src/topology/ops.c
diff --git a/src/topology/ops.c b/src/topology/ops.c
new file mode 100644
index 0000000..d3f4a28
--- /dev/null
+++ b/src/topology/ops.c
@@ -0,0 +1,90 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of kcontrol text names to types */
+static const struct map_elem control_map[] = {
+ {"volsw", SND_SOC_TPLG_CTL_VOLSW},
+ {"volsw_sx", SND_SOC_TPLG_CTL_VOLSW_SX},
+ {"volsw_xr_sx", SND_SOC_TPLG_CTL_VOLSW_XR_SX},
+ {"enum", SND_SOC_TPLG_CTL_ENUM},
+ {"bytes", SND_SOC_TPLG_CTL_BYTES},
+ {"enum_value", SND_SOC_TPLG_CTL_ENUM_VALUE},
+ {"range", SND_SOC_TPLG_CTL_RANGE},
+ {"strobe", SND_SOC_TPLG_CTL_STROBE},
+};
+
+static int lookup_ops(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(control_map); i++) {
+ if (strcmp(control_map[i].name, c) == 0)
+ return control_map[i].id;
+ }
+
+ /* cant find string name in our table so we use its ID number */
+ return atoi(c);
+}
+
+/* Parse Control operations. Ops can come from standard names above or
+ * bespoke driver controls with numbers >= 256
+ *
+ * ops."name" {
+ * info "volsw"
+ * get "256"
+ * put "256"
+ * }
+ */
+int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_hdr *hdr = private;
+ const char *id, *value;
+
+ tplg_dbg("\tOps\n");
+ hdr->size = sizeof(*hdr);
+
+ 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 - try strings then ints */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ if (strcmp(id, "info") == 0)
+ hdr->ops.info = lookup_ops(value);
+ else if (strcmp(id, "put") == 0)
+ hdr->ops.put = lookup_ops(value);
+ else if (strcmp(id, "get") == 0)
+ hdr->ops.get = lookup_ops(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 07/13] topology: Add private data parser
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (4 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 06/13] topology: Add operations parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 16:20 ` Takashi Iwai
2015-07-01 13:44 ` [PATCH v2 08/13] topology: Add DAPM object parser Liam Girdwood
` (5 subsequent siblings)
11 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Parse private data and store for attachment to other objects. Data can come
file or be locally defined as bytes, shorts or words.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/data.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 358 insertions(+)
create mode 100644 src/topology/data.c
diff --git a/src/topology/data.c b/src/topology/data.c
new file mode 100644
index 0000000..7de62b4
--- /dev/null
+++ b/src/topology/data.c
@@ -0,0 +1,358 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* Get Private data from a file. */
+static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ struct snd_soc_tplg_private *priv = NULL;
+ const char *value = NULL;
+ char filename[MAX_FILE];
+ char *env = getenv(ALSA_CONFIG_TPLG_VAR);
+ FILE *fp;
+ size_t size, bytes_read;
+ int ret = 0;
+
+ tplg_dbg("data DataFile: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ /* prepend alsa config directory to path */
+ snprintf(filename, sizeof(filename), "%s/%s",
+ env ? env : ALSA_TPLG_DIR, value);
+ filename[sizeof(filename)-1] = '\0';
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "error: invalid data file path '%s'\n",
+ filename);
+ ret = -errno;
+ goto err;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+ if (size <= 0) {
+ fprintf(stderr, "error: invalid data file size %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (size > TPLG_MAX_PRIV_SIZE) {
+ fprintf(stderr, "error: data file too big %zu\n", size);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv = calloc(1, sizeof(*priv) + size);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ bytes_read = fread(&priv->data, 1, size, fp);
+ if (bytes_read != size) {
+ ret = -errno;
+ goto err;
+ }
+
+ elem->data = priv;
+ priv->size = size;
+ elem->size = sizeof(*priv) + size;
+ return 0;
+
+err:
+ if (priv)
+ free(priv);
+ return ret;
+}
+
+static void dump_priv_data(struct tplg_elem *elem)
+{
+ struct snd_soc_tplg_private *priv = elem->data;
+ unsigned char *p = (unsigned char *)priv->data;
+ unsigned int i, j = 0;
+
+ tplg_dbg(" elem size = %d, priv data size = %d\n",
+ elem->size, priv->size);
+
+ for (i = 0; i < priv->size; i++) {
+ if (j++ % 8 == 0)
+ tplg_dbg("\n");
+
+ tplg_dbg(" 0x%x", *p++);
+ }
+
+ tplg_dbg("\n\n");
+}
+
+static int get_hex_num(const char *str)
+{
+ char *tmp, *s = NULL;
+ int i = 0;
+
+ tmp = strdup(str);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ s = strtok(tmp, ",");
+ while (s != NULL) {
+ s = strtok(NULL, ",");
+ i++;
+ }
+
+ free(tmp);
+ return i;
+}
+
+static int write_hex(char *buf, char *str, int width)
+{
+ long val;
+ void *p = &val;
+
+ errno = 0;
+ val = strtol(str, NULL, 16);
+
+ if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+ || (errno != 0 && val == 0)) {
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case 1:
+ *(unsigned char *)buf = *(unsigned char *)p;
+ break;
+ case 2:
+ *(unsigned short *)buf = *(unsigned short *)p;
+ break;
+ case 4:
+ *(unsigned int *)buf = *(unsigned int *)p;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int copy_data_hex(char *data, int off, const char *str, int width)
+{
+ char *tmp, *s = NULL, *p = data;
+ int ret;
+
+ tmp = strdup(str);
+ if (tmp == NULL)
+ return -ENOMEM;
+
+ p += off;
+ s = strtok(tmp, ",");
+
+ while (s != NULL) {
+ ret = write_hex(p, s, width);
+ if (ret < 0) {
+ free(tmp);
+ return ret;
+ }
+
+ s = strtok(NULL, ",");
+ p += width;
+ }
+
+ free(tmp);
+ return 0;
+}
+
+static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem,
+ int width)
+{
+ struct snd_soc_tplg_private *priv;
+ const char *value = NULL;
+ int size, esize, off, num;
+ int ret;
+
+ tplg_dbg(" data: %s\n", elem->id);
+
+ if (snd_config_get_string(cfg, &value) < 0)
+ return -EINVAL;
+
+ num = get_hex_num(value);
+ size = num * width;
+ priv = elem->data;
+
+ if (esize > TPLG_MAX_PRIV_SIZE) {
+ fprintf(stderr, "error: data too big %d\n", esize);
+ return -EINVAL;
+ }
+
+ if (priv != NULL) {
+ off = priv->size;
+ esize = elem->size + size;
+ priv = realloc(priv, esize);
+ } else {
+ off = 0;
+ esize = sizeof(*priv) + size;
+ priv = calloc(1, esize);
+ }
+
+ if (!priv)
+ return -ENOMEM;
+
+ elem->data = priv;
+ priv->size += size;
+ elem->size = esize;
+
+ ret = copy_data_hex(priv->data, off, value, width);
+
+ dump_priv_data(elem);
+ return ret;
+}
+
+
+/* Parse Private data.
+ *
+ * Object private data can either be from file or defined as bytes, shorts,
+ * words.
+ *
+ * SectionData."data name" {
+ *
+ * DataFile "filename"
+ * bytes "0x12,0x34,0x56,0x78"
+ * shorts "0x1122,0x3344,0x5566,0x7788"
+ * words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234"
+ * }
+ */
+int tplg_parse_data(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;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_DATA);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "file") == 0) {
+ err = tplg_parse_data_file(n, elem);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse data file\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "bytes") == 0) {
+ err = tplg_parse_data_hex(n, elem, 1);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse data bytes\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "shorts") == 0) {
+ err = tplg_parse_data_hex(n, elem, 2);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse data shorts\n");
+ return err;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "words") == 0) {
+ err = tplg_parse_data_hex(n, elem, 4);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse data words\n");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* copy private data into the bytes extended control */
+int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_private *priv;
+ int priv_data_size;
+
+ if (!ref)
+ return -EINVAL;
+
+ tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id);
+ priv_data_size = ref->data->size;
+
+ switch (elem->type) {
+ case PARSER_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 PARSER_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 PARSER_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 PARSER_TYPE_DAPM_WIDGET:
+ elem->widget = realloc(elem->widget,
+ elem->size + priv_data_size);
+ if (!elem->widget)
+ return -ENOMEM;
+ priv = &elem->widget->priv;
+ break;
+
+ default:
+ fprintf(stderr, "elem '%s': type %d shall not have private data\n",
+ elem->id, elem->type);
+ return -EINVAL;
+ }
+
+ elem->size += priv_data_size;
+ priv->size = priv_data_size;
+ memcpy(priv->data, ref->data->data, priv_data_size);
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-01 13:44 ` [PATCH v2 07/13] topology: Add private data parser Liam Girdwood
@ 2015-07-01 16:20 ` Takashi Iwai
2015-07-07 15:54 ` Liam Girdwood
0 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-07-01 16:20 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, alsa-devel, Mark Brown
At Wed, 1 Jul 2015 14:44:29 +0100,
Liam Girdwood wrote:
>
> +static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem)
> +{
> + struct snd_soc_tplg_private *priv = NULL;
> + const char *value = NULL;
> + char filename[MAX_FILE];
> + char *env = getenv(ALSA_CONFIG_TPLG_VAR);
> + FILE *fp;
> + size_t size, bytes_read;
> + int ret = 0;
> +
> + tplg_dbg("data DataFile: %s\n", elem->id);
> +
> + if (snd_config_get_string(cfg, &value) < 0)
> + return -EINVAL;
> +
> + /* prepend alsa config directory to path */
> + snprintf(filename, sizeof(filename), "%s/%s",
> + env ? env : ALSA_TPLG_DIR, value);
> + filename[sizeof(filename)-1] = '\0';
Unlike strncpy(), snprintf() puts the NUL-character by itself, so this
is superfluous.
> +static int get_hex_num(const char *str)
> +{
> + char *tmp, *s = NULL;
> + int i = 0;
> +
> + tmp = strdup(str);
> + if (tmp == NULL)
> + return -ENOMEM;
> +
> + s = strtok(tmp, ",");
> + while (s != NULL) {
> + s = strtok(NULL, ",");
> + i++;
> + }
> +
> + free(tmp);
> + return i;
Hmm, this just counts the number of comma + 1, so you don't need to
duplicate the string?
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-01 16:20 ` Takashi Iwai
@ 2015-07-07 15:54 ` Liam Girdwood
2015-07-07 16:19 ` Takashi Iwai
0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-07 15:54 UTC (permalink / raw)
To: Takashi Iwai, Jin, Yao; +Cc: Vinod Koul, alsa-devel, Mark Brown
On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
>
> > +static int get_hex_num(const char *str)
> > +{
> > + char *tmp, *s = NULL;
> > + int i = 0;
> > +
> > + tmp = strdup(str);
> > + if (tmp == NULL)
> > + return -ENOMEM;
> > +
> > + s = strtok(tmp, ",");
> > + while (s != NULL) {
> > + s = strtok(NULL, ",");
> > + i++;
> > + }
> > +
> > + free(tmp);
> > + return i;
>
> Hmm, this just counts the number of comma + 1, so you don't need to
> duplicate the string?
>
The string here is duplicated since strtok is destructive (it overwrites
the delimiters with NULL) and the string is being used later on by the
calling function.
Liam
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-07 15:54 ` Liam Girdwood
@ 2015-07-07 16:19 ` Takashi Iwai
2015-07-08 8:57 ` Liam Girdwood
0 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-07-07 16:19 UTC (permalink / raw)
To: Liam Girdwood; +Cc: Vinod Koul, Jin, Yao, Mark Brown, alsa-devel
On Tue, 07 Jul 2015 17:54:02 +0200,
Liam Girdwood wrote:
>
> On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
>
> >
> > > +static int get_hex_num(const char *str)
> > > +{
> > > + char *tmp, *s = NULL;
> > > + int i = 0;
> > > +
> > > + tmp = strdup(str);
> > > + if (tmp == NULL)
> > > + return -ENOMEM;
> > > +
> > > + s = strtok(tmp, ",");
> > > + while (s != NULL) {
> > > + s = strtok(NULL, ",");
> > > + i++;
> > > + }
> > > +
> > > + free(tmp);
> > > + return i;
> >
> > Hmm, this just counts the number of comma + 1, so you don't need to
> > duplicate the string?
> >
>
> The string here is duplicated since strtok is destructive (it overwrites
> the delimiters with NULL) and the string is being used later on by the
> calling function.
Yes, but what I meant is something like below:
static int get_hex_num(const char *str)
{
int i = 0;
if (!*str)
return 0;
for (;;) {
i++;
str = strchr(str, ',');
if (!str)
return i;
str++;
}
}
... so that it works without strdup().
But it seems that strtok() skips the starting delimiter or handles
multiple delimiters as a single, so the result will become
inconsistent. That is, all the following strings will give "a" and
"b" via strtok():
a,b
a,,b
,a,b
a,b,
I guess you don't want to have an empty list element, right?
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-07 16:19 ` Takashi Iwai
@ 2015-07-08 8:57 ` Liam Girdwood
2015-07-08 13:31 ` Jin, Yao
0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-07-08 8:57 UTC (permalink / raw)
To: Takashi Iwai, Jin, Yao; +Cc: Vinod Koul, alsa-devel, Mark Brown
On Tue, 2015-07-07 at 18:19 +0200, Takashi Iwai wrote:
> On Tue, 07 Jul 2015 17:54:02 +0200,
> Liam Girdwood wrote:
> >
> > On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
> >
> > >
> > > > +static int get_hex_num(const char *str)
> > > > +{
> > > > + char *tmp, *s = NULL;
> > > > + int i = 0;
> > > > +
> > > > + tmp = strdup(str);
> > > > + if (tmp == NULL)
> > > > + return -ENOMEM;
> > > > +
> > > > + s = strtok(tmp, ",");
> > > > + while (s != NULL) {
> > > > + s = strtok(NULL, ",");
> > > > + i++;
> > > > + }
> > > > +
> > > > + free(tmp);
> > > > + return i;
> > >
> > > Hmm, this just counts the number of comma + 1, so you don't need to
> > > duplicate the string?
> > >
> >
> > The string here is duplicated since strtok is destructive (it overwrites
> > the delimiters with NULL) and the string is being used later on by the
> > calling function.
>
> Yes, but what I meant is something like below:
>
> static int get_hex_num(const char *str)
> {
> int i = 0;
>
> if (!*str)
> return 0;
> for (;;) {
> i++;
> str = strchr(str, ',');
> if (!str)
> return i;
> str++;
> }
> }
>
> ... so that it works without strdup().
>
> But it seems that strtok() skips the starting delimiter or handles
> multiple delimiters as a single, so the result will become
> inconsistent. That is, all the following strings will give "a" and
> "b" via strtok():
> a,b
> a,,b
> ,a,b
> a,b,
>
> I guess you don't want to have an empty list element, right?
>
Lets ask the author :) but IMO an empty list should be skipped here.
Yao, what's your rational behind this code ?
Liam
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-08 8:57 ` Liam Girdwood
@ 2015-07-08 13:31 ` Jin, Yao
2015-07-08 14:14 ` Takashi Iwai
0 siblings, 1 reply; 26+ messages in thread
From: Jin, Yao @ 2015-07-08 13:31 UTC (permalink / raw)
To: Liam Girdwood, Takashi Iwai; +Cc: Vinod Koul, alsa-devel, Mark Brown
On 2015/7/8 16:57, Liam Girdwood wrote:
> On Tue, 2015-07-07 at 18:19 +0200, Takashi Iwai wrote:
>> On Tue, 07 Jul 2015 17:54:02 +0200,
>> Liam Girdwood wrote:
>>>
>>> On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
>>>
>>>>
>>>>> +static int get_hex_num(const char *str)
>>>>> +{
>>>>> + char *tmp, *s = NULL;
>>>>> + int i = 0;
>>>>> +
>>>>> + tmp = strdup(str);
>>>>> + if (tmp == NULL)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + s = strtok(tmp, ",");
>>>>> + while (s != NULL) {
>>>>> + s = strtok(NULL, ",");
>>>>> + i++;
>>>>> + }
>>>>> +
>>>>> + free(tmp);
>>>>> + return i;
>>>>
>>>> Hmm, this just counts the number of comma + 1, so you don't need to
>>>> duplicate the string?
>>>>
>>>
>>> The string here is duplicated since strtok is destructive (it overwrites
>>> the delimiters with NULL) and the string is being used later on by the
>>> calling function.
>>
>> Yes, but what I meant is something like below:
>>
>> static int get_hex_num(const char *str)
>> {
>> int i = 0;
>>
>> if (!*str)
>> return 0;
>> for (;;) {
>> i++;
>> str = strchr(str, ',');
>> if (!str)
>> return i;
>> str++;
>> }
>> }
>>
>> ... so that it works without strdup().
>>
>> But it seems that strtok() skips the starting delimiter or handles
>> multiple delimiters as a single, so the result will become
>> inconsistent. That is, all the following strings will give "a" and
>> "b" via strtok():
>> a,b
>> a,,b
>> ,a,b
>> a,b,
>>
>> I guess you don't want to have an empty list element, right?
>>
>
> Lets ask the author :) but IMO an empty list should be skipped here.
>
> Yao, what's your rational behind this code ?
Sorry for replying late, I just see this mail.
The get_hex_num() returns the number of hexadecimal in string.
Say the string is "0x12,0x34,0x56,0x78" or "0x12,0x34,0x56,0x78,",
the get_hex_num() returns 4.
But if I use strchr, for above string, I get different number (3 or 4).
That's the reason I choose the strtok().
I do this is because I think user may append the comma at the end of
string.
>
> Liam
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-08 13:31 ` Jin, Yao
@ 2015-07-08 14:14 ` Takashi Iwai
2015-07-08 14:21 ` Liam Girdwood
0 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-07-08 14:14 UTC (permalink / raw)
To: Jin, Yao; +Cc: Liam Girdwood, Vinod Koul, alsa-devel, Mark Brown
On Wed, 08 Jul 2015 15:31:27 +0200,
Jin, Yao wrote:
>
>
>
> On 2015/7/8 16:57, Liam Girdwood wrote:
> > On Tue, 2015-07-07 at 18:19 +0200, Takashi Iwai wrote:
> >> On Tue, 07 Jul 2015 17:54:02 +0200,
> >> Liam Girdwood wrote:
> >>>
> >>> On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
> >>>
> >>>>
> >>>>> +static int get_hex_num(const char *str)
> >>>>> +{
> >>>>> + char *tmp, *s = NULL;
> >>>>> + int i = 0;
> >>>>> +
> >>>>> + tmp = strdup(str);
> >>>>> + if (tmp == NULL)
> >>>>> + return -ENOMEM;
> >>>>> +
> >>>>> + s = strtok(tmp, ",");
> >>>>> + while (s != NULL) {
> >>>>> + s = strtok(NULL, ",");
> >>>>> + i++;
> >>>>> + }
> >>>>> +
> >>>>> + free(tmp);
> >>>>> + return i;
> >>>>
> >>>> Hmm, this just counts the number of comma + 1, so you don't need to
> >>>> duplicate the string?
> >>>>
> >>>
> >>> The string here is duplicated since strtok is destructive (it overwrites
> >>> the delimiters with NULL) and the string is being used later on by the
> >>> calling function.
> >>
> >> Yes, but what I meant is something like below:
> >>
> >> static int get_hex_num(const char *str)
> >> {
> >> int i = 0;
> >>
> >> if (!*str)
> >> return 0;
> >> for (;;) {
> >> i++;
> >> str = strchr(str, ',');
> >> if (!str)
> >> return i;
> >> str++;
> >> }
> >> }
> >>
> >> ... so that it works without strdup().
> >>
> >> But it seems that strtok() skips the starting delimiter or handles
> >> multiple delimiters as a single, so the result will become
> >> inconsistent. That is, all the following strings will give "a" and
> >> "b" via strtok():
> >> a,b
> >> a,,b
> >> ,a,b
> >> a,b,
> >>
> >> I guess you don't want to have an empty list element, right?
> >>
> >
> > Lets ask the author :) but IMO an empty list should be skipped here.
> >
> > Yao, what's your rational behind this code ?
>
> Sorry for replying late, I just see this mail.
>
> The get_hex_num() returns the number of hexadecimal in string.
>
> Say the string is "0x12,0x34,0x56,0x78" or "0x12,0x34,0x56,0x78,",
> the get_hex_num() returns 4.
>
> But if I use strchr, for above string, I get different number (3 or 4).
> That's the reason I choose the strtok().
>
> I do this is because I think user may append the comma at the end of
> string.
So, is this allowed intentionally as a valid syntax? As I showed,
a string like ",,,0x12,,0x34,,,,0x56,0x78,," would be handled as a
valid string.
The above may look strange, but the strtok() behavior is natural if
you imagine to replace "," with a space.
Takashi
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v2 07/13] topology: Add private data parser
2015-07-08 14:14 ` Takashi Iwai
@ 2015-07-08 14:21 ` Liam Girdwood
0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-08 14:21 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Vinod Koul, Jin, Yao, Mark Brown, alsa-devel
On Wed, 2015-07-08 at 16:14 +0200, Takashi Iwai wrote:
> On Wed, 08 Jul 2015 15:31:27 +0200,
> Jin, Yao wrote:
> >
> >
> >
> > On 2015/7/8 16:57, Liam Girdwood wrote:
> > > On Tue, 2015-07-07 at 18:19 +0200, Takashi Iwai wrote:
> > >> On Tue, 07 Jul 2015 17:54:02 +0200,
> > >> Liam Girdwood wrote:
> > >>>
> > >>> On Wed, 2015-07-01 at 18:20 +0200, Takashi Iwai wrote:
> > >>>
> > >>>>
> > >>>>> +static int get_hex_num(const char *str)
> > >>>>> +{
> > >>>>> + char *tmp, *s = NULL;
> > >>>>> + int i = 0;
> > >>>>> +
> > >>>>> + tmp = strdup(str);
> > >>>>> + if (tmp == NULL)
> > >>>>> + return -ENOMEM;
> > >>>>> +
> > >>>>> + s = strtok(tmp, ",");
> > >>>>> + while (s != NULL) {
> > >>>>> + s = strtok(NULL, ",");
> > >>>>> + i++;
> > >>>>> + }
> > >>>>> +
> > >>>>> + free(tmp);
> > >>>>> + return i;
> > >>>>
> > >>>> Hmm, this just counts the number of comma + 1, so you don't need to
> > >>>> duplicate the string?
> > >>>>
> > >>>
> > >>> The string here is duplicated since strtok is destructive (it overwrites
> > >>> the delimiters with NULL) and the string is being used later on by the
> > >>> calling function.
> > >>
> > >> Yes, but what I meant is something like below:
> > >>
> > >> static int get_hex_num(const char *str)
> > >> {
> > >> int i = 0;
> > >>
> > >> if (!*str)
> > >> return 0;
> > >> for (;;) {
> > >> i++;
> > >> str = strchr(str, ',');
> > >> if (!str)
> > >> return i;
> > >> str++;
> > >> }
> > >> }
> > >>
> > >> ... so that it works without strdup().
> > >>
> > >> But it seems that strtok() skips the starting delimiter or handles
> > >> multiple delimiters as a single, so the result will become
> > >> inconsistent. That is, all the following strings will give "a" and
> > >> "b" via strtok():
> > >> a,b
> > >> a,,b
> > >> ,a,b
> > >> a,b,
> > >>
> > >> I guess you don't want to have an empty list element, right?
> > >>
> > >
> > > Lets ask the author :) but IMO an empty list should be skipped here.
> > >
> > > Yao, what's your rational behind this code ?
> >
> > Sorry for replying late, I just see this mail.
> >
> > The get_hex_num() returns the number of hexadecimal in string.
> >
> > Say the string is "0x12,0x34,0x56,0x78" or "0x12,0x34,0x56,0x78,",
> > the get_hex_num() returns 4.
> >
> > But if I use strchr, for above string, I get different number (3 or 4).
> > That's the reason I choose the strtok().
> >
> > I do this is because I think user may append the comma at the end of
> > string.
>
Ok, lets make commas at the end illegal.
> So, is this allowed intentionally as a valid syntax? As I showed,
> a string like ",,,0x12,,0x34,,,,0x56,0x78,," would be handled as a
> valid string.
>
> The above may look strange, but the strtok() behavior is natural if
> you imagine to replace "," with a space.
>
So I'll fix this to be "0x01, 0x02, 0x03" syntax only. i.e. comma
between each value and no comma at the end.
Liam
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v2 08/13] topology: Add DAPM object parser
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (5 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 07/13] topology: Add private data parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 09/13] topology: Add CTL parser Liam Girdwood
` (4 subsequent siblings)
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Parse DAPM objects including widgets and graph elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/dapm.c | 562 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 562 insertions(+)
create mode 100644 src/topology/dapm.c
diff --git a/src/topology/dapm.c b/src/topology/dapm.c
new file mode 100644
index 0000000..1131f03
--- /dev/null
+++ b/src/topology/dapm.c
@@ -0,0 +1,562 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of widget text names to types */
+static const struct map_elem widget_map[] = {
+ {"input", SND_SOC_TPLG_DAPM_INPUT},
+ {"output", SND_SOC_TPLG_DAPM_OUTPUT},
+ {"mux", SND_SOC_TPLG_DAPM_MUX},
+ {"mixer", SND_SOC_TPLG_DAPM_MIXER},
+ {"pga", SND_SOC_TPLG_DAPM_PGA},
+ {"out_drv", SND_SOC_TPLG_DAPM_OUT_DRV},
+ {"adc", SND_SOC_TPLG_DAPM_ADC},
+ {"dac", SND_SOC_TPLG_DAPM_DAC},
+ {"switch", SND_SOC_TPLG_DAPM_SWITCH},
+ {"pre", SND_SOC_TPLG_DAPM_PRE},
+ {"post", SND_SOC_TPLG_DAPM_POST},
+ {"aif_in", SND_SOC_TPLG_DAPM_AIF_IN},
+ {"aif_out", SND_SOC_TPLG_DAPM_AIF_OUT},
+ {"dai_in", SND_SOC_TPLG_DAPM_DAI_IN},
+ {"dai_out", SND_SOC_TPLG_DAPM_DAI_OUT},
+ {"dai_link", SND_SOC_TPLG_DAPM_DAI_LINK},
+};
+
+/* mapping of widget kcontrol text names to types */
+static const struct map_elem widget_control_map[] = {
+ {"volsw", SND_SOC_TPLG_DAPM_CTL_VOLSW},
+ {"enum_double", SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE},
+ {"enum_virt", SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT},
+ {"enum_value", SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE},
+};
+
+static int lookup_widget(const char *w)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
+ if (strcmp(widget_map[i].name, w) == 0)
+ return widget_map[i].id;
+ }
+
+ return -EINVAL;
+}
+
+static int tplg_parse_dapm_mixers(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+
+ tplg_dbg(" DAPM Mixer Controls: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_ref_add(elem, PARSER_TYPE_MIXER, value);
+ tplg_dbg("\t\t %s\n", value);
+ }
+
+ return 0;
+}
+
+static int tplg_parse_dapm_enums(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *value = NULL;
+
+ tplg_dbg(" DAPM Enum Controls: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_ref_add(elem, PARSER_TYPE_ENUM, value);
+ tplg_dbg("\t\t %s\n", value);
+ }
+
+ return 0;
+}
+
+/* move referenced controls to the widget */
+static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_dapm_widget *widget = elem->widget;
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = ref->mixer_ctrl;
+ struct snd_soc_tplg_enum_control *enum_ctrl = ref->enum_ctrl;
+
+ tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id);
+ tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n",
+ elem->size, ref->size, elem->size + ref->size,
+ widget->priv.size);
+
+ widget = realloc(widget, elem->size + ref->size);
+ if (!widget)
+ return -ENOMEM;
+
+ elem->widget = widget;
+
+ /* copy new widget at the end */
+ if (ref->type == PARSER_TYPE_MIXER)
+ memcpy((void*)widget + elem->size, mixer_ctrl, ref->size);
+ else if (ref->type == PARSER_TYPE_ENUM)
+ memcpy((void*)widget + elem->size, enum_ctrl, ref->size);
+
+ elem->size += ref->size;
+ widget->num_kcontrols++;
+ ref->compound_elem = 1;
+ return 0;
+}
+
+/* check referenced controls for a widget */
+static int tplg_build_widget(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ switch (ref->type) {
+ case PARSER_TYPE_MIXER:
+ ref->elem = tplg_elem_lookup(&tplg->mixer_list,
+ ref->id, PARSER_TYPE_MIXER);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case PARSER_TYPE_ENUM:
+ ref->elem = tplg_elem_lookup(&tplg->enum_list,
+ ref->id, PARSER_TYPE_ENUM);
+ if (ref->elem)
+ err = copy_dapm_control(elem, ref->elem);
+ break;
+
+ case PARSER_TYPE_DATA:
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ if (ref->elem)
+ err = tplg_copy_data(elem, ref->elem);
+ break;
+ default:
+ break;
+ }
+
+ if (!ref->elem) {
+ fprintf(stderr, "error: cannot find control '%s'"
+ " referenced by widget '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int tplg_build_widgets(snd_tplg_t *tplg)
+{
+
+ struct list_head *base, *pos, *npos;
+ struct tplg_elem *elem;
+ int err;
+
+ base = &tplg->widget_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ if (!elem->widget || elem->type != PARSER_TYPE_DAPM_WIDGET) {
+ fprintf(stderr, "error: invalid widget '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_build_widget(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int tplg_build_routes(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos, *npos;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *route;
+
+ base = &tplg->route_list;
+
+ list_for_each_safe(pos, npos, base) {
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ if (!elem->route || elem->type != PARSER_TYPE_DAPM_GRAPH) {
+ fprintf(stderr, "error: invalid route '%s'\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ route = elem->route;
+ tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n",
+ route->sink, route->control, route->source);
+
+ /* validate sink */
+ if (strlen(route->sink) <= 0) {
+ fprintf(stderr, "error: no sink\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
+ PARSER_TYPE_DAPM_WIDGET)) {
+ fprintf(stderr, "warning: undefined sink widget/stream '%s'\n",
+ route->sink);
+ }
+
+ /* validate control name */
+ if (strlen(route->control)) {
+ if (!tplg_elem_lookup(&tplg->mixer_list,
+ route->control, PARSER_TYPE_MIXER) &&
+ !tplg_elem_lookup(&tplg->enum_list,
+ route->control, PARSER_TYPE_ENUM)) {
+ fprintf(stderr, "warning: Undefined mixer/enum control '%s'\n",
+ route->control);
+ }
+ }
+
+ /* validate source */
+ if (strlen(route->source) <= 0) {
+ fprintf(stderr, "error: no source\n");
+ return -EINVAL;
+
+ }
+ if (!tplg_elem_lookup(&tplg->widget_list, route->source,
+ PARSER_TYPE_DAPM_WIDGET)) {
+ fprintf(stderr, "warning: Undefined source widget/stream '%s'\n",
+ route->source);
+ }
+ }
+
+ return 0;
+}
+
+#define LINE_SIZE 1024
+
+/* line is defined as '"source, control, sink"' */
+static int tplg_parse_line(const char *text,
+ struct snd_soc_tplg_dapm_graph_elem *line)
+{
+ char buf[LINE_SIZE];
+ unsigned int len, i;
+ const char *source = NULL, *sink = NULL, *control = NULL;
+
+ strncpy(buf, text, LINE_SIZE);
+ buf[LINE_SIZE - 1] = 0;
+
+ len = strlen(buf);
+ if (len <= 2) {
+ fprintf(stderr, "error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+ }
+
+ /* find first , */
+ for (i = 1; i < len; i++) {
+ if (buf[i] == ',')
+ goto second;
+ }
+ fprintf(stderr, "error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+second:
+ /* find second , */
+ sink = buf;
+ control = &buf[i + 2];
+ buf[i] = 0;
+
+ for (; i < len; i++) {
+ if (buf[i] == ',')
+ goto done;
+ }
+
+ fprintf(stderr, "error: invalid route \"%s\"\n", buf);
+ return -EINVAL;
+
+done:
+ buf[i] = 0;
+ source = &buf[i + 2];
+
+ strcpy(line->source, source);
+ strcpy(line->control, control);
+ strcpy(line->sink, sink);
+ return 0;
+}
+
+
+static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct tplg_elem *elem;
+ struct snd_soc_tplg_dapm_graph_elem *line = NULL;
+ int err;
+
+ snd_config_for_each(i, next, cfg) {
+ const char *val;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_string(n, &val) < 0)
+ continue;
+
+ elem = tplg_elem_new();
+ if (!elem)
+ return -ENOMEM;
+
+ list_add_tail(&elem->list, &tplg->route_list);
+ strcpy(elem->id, "line");
+ elem->type = PARSER_TYPE_DAPM_GRAPH;
+ elem->size = sizeof(*line);
+
+ line = calloc(1, sizeof(*line));
+ if (!line)
+ return -ENOMEM;
+
+ elem->route = line;
+
+ err = tplg_parse_line(val, line);
+ if (err < 0)
+ return err;
+
+ tplg_dbg("route: sink '%s', control '%s', source '%s'\n",
+ line->sink, line->control, line->source);
+ }
+
+ return 0;
+}
+
+int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ int err;
+ const char *graph_id;
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ fprintf(stderr, "error: compound is expected for dapm graph definition\n");
+ return -EINVAL;
+ }
+
+ snd_config_get_id(cfg, &graph_id);
+
+ snd_config_for_each(i, next, cfg) {
+ const char *id;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0) {
+ continue;
+ }
+
+ if (strcmp(id, "lines") == 0) {
+ err = tplg_parse_routes(tplg, n);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to parse dapm graph %s\n",
+ graph_id);
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* DAPM Widget.
+ *
+ * SectionWidget."widget name" {
+ *
+ * index "1"
+ * type "aif_in"
+ * no_pm "true"
+ * shift "0"
+ * invert "1
+ * mixer "name"
+ * enum "name"
+ * data "name"
+ * }
+ */
+int tplg_parse_dapm_widget(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_dapm_widget *widget;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int widget_type, err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_DAPM_WIDGET);
+ if (!elem)
+ return -ENOMEM;
+
+ tplg_dbg(" Widget: %s\n", elem->id);
+
+ widget = elem->widget;
+ strncpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ widget->name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+ widget->size = elem->size;
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget_type = lookup_widget(val);
+ if (widget_type < 0){
+ fprintf(stderr, "Widget '%s': Unsupported widget type %s\n",
+ elem->id, val);
+ return -EINVAL;
+ }
+
+ widget->id = widget_type;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "no_pm") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ widget->reg = -1;
+
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "shift") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->shift = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->shift);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->invert = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->invert);
+ continue;
+ }
+
+ if (strcmp(id, "subseq") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->subseq= atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->subseq);
+ continue;
+ }
+
+ if (strcmp(id, "event_type") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->event_type = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->event_type);
+ continue;
+ }
+
+ if (strcmp(id, "event_flags") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ widget->event_flags = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, widget->event_flags);
+ continue;
+ }
+
+ if (strcmp(id, "enum") == 0) {
+ err = tplg_parse_dapm_enums(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "mixer") == 0) {
+ err = tplg_parse_dapm_mixers(n, elem);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 09/13] topology: Add CTL parser
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (6 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 08/13] topology: Add DAPM object parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 10/13] topology: Add Channel map parser Liam Girdwood
` (3 subsequent siblings)
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Add support to parse mixers, enums and byte controls.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/ctl.c | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 674 insertions(+)
create mode 100644 src/topology/ctl.c
diff --git a/src/topology/ctl.c b/src/topology/ctl.c
new file mode 100644
index 0000000..5a797d1
--- /dev/null
+++ b/src/topology/ctl.c
@@ -0,0 +1,674 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* copy referenced TLV to the mixer control */
+static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
+{
+ struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl;
+ struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
+
+ tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id);
+
+ /* TLV has a fixed size */
+ memcpy(&mixer_ctrl->tlv, tlv, sizeof(*tlv));
+
+ /* set size of TLV data */
+ mixer_ctrl->hdr.tlv_size = tlv->count * sizeof(uint32_t);
+ return 0;
+}
+
+/* check referenced TLV for a mixer control */
+static int tplg_build_mixer_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ /* for each ref in this control elem */
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == PARSER_TYPE_TLV) {
+ ref->elem = tplg_elem_lookup(&tplg->tlv_list,
+ ref->id, PARSER_TYPE_TLV);
+ if (ref->elem)
+ err = copy_tlv(elem, ref->elem);
+
+ } else if (ref->type == PARSER_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+
+ if (!ref->elem) {
+ fprintf(stderr, "error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void copy_enum_texts(struct tplg_elem *enum_elem,
+ struct tplg_elem *ref_elem)
+{
+ struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
+
+ memcpy(ec->texts, ref_elem->texts,
+ SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+}
+
+/* check referenced text for a enum control */
+static int tplg_build_enum_control(snd_tplg_t *tplg,
+ struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos, *npos;
+ int err = 0;
+
+ base = &elem->ref_list;
+
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ if (ref->type == PARSER_TYPE_TEXT) {
+ ref->elem = tplg_elem_lookup(&tplg->text_list,
+ ref->id, PARSER_TYPE_TEXT);
+ if (ref->elem)
+ copy_enum_texts(elem, ref->elem);
+
+ } else if (ref->type == PARSER_TYPE_DATA) {
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ err = tplg_copy_data(elem, ref->elem);
+ }
+ if (!ref->elem) {
+ fprintf(stderr, "error: cannot find '%s' referenced by"
+ " control '%s'\n", ref->id, elem->id);
+ return -EINVAL;
+ } else if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* check referenced private data for a byte control */
+static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
+{
+ struct tplg_ref *ref;
+ struct list_head *base, *pos, *npos;
+
+ base = &elem->ref_list;
+
+ list_for_each_safe(pos, npos, base) {
+
+ ref = list_entry(pos, struct tplg_ref, list);
+ if (ref->id == NULL || ref->elem)
+ continue;
+
+ /* bytes control only reference one private data section */
+ ref->elem = tplg_elem_lookup(&tplg->pdata_list,
+ ref->id, PARSER_TYPE_DATA);
+ if (!ref->elem) {
+ fprintf(stderr, "error: cannot find data '%s'"
+ " referenced by control '%s'\n",
+ ref->id, elem->id);
+ return -EINVAL;
+ }
+
+ /* copy texts to enum elem */
+ return tplg_copy_data(elem, ref->elem);
+ }
+
+ return 0;
+}
+
+int tplg_build_controls(snd_tplg_t *tplg)
+{
+ struct list_head *base, *pos, *npos;
+ struct tplg_elem *elem;
+ int err = 0;
+
+ base = &tplg->mixer_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_mixer_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ base = &tplg->enum_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_enum_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ base = &tplg->bytes_ext_list;
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+ err = tplg_build_bytes_control(tplg, elem);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Parse TLV of DBScale type.
+ *
+ * Parse DBScale describing min, step, mute in DB.
+ *
+ * scale {
+ * min "-9000"
+ * step "300"
+ * mute "1"
+ * }
+ */
+static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+ const char *id = NULL, *value = NULL;
+ int *data;
+
+ tplg_dbg(" scale: %s\n", elem->id);
+
+ tplg_tlv = calloc(1, sizeof(*tplg_tlv));
+ if (!tplg_tlv)
+ return -ENOMEM;
+ data = (int*)(tplg_tlv->data);
+
+ elem->tlv = tplg_tlv;
+ tplg_tlv->numid = SNDRV_CTL_TLVT_DB_SCALE;
+ tplg_tlv->count = 8;
+ tplg_tlv->size = sizeof(*tplg_tlv);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+
+ /* get ID */
+ if (snd_config_get_id(n, &id) < 0) {
+ fprintf(stderr, "error: cant get ID\n");
+ return -EINVAL;
+ }
+
+ /* get value */
+ if (snd_config_get_string(n, &value) < 0)
+ continue;
+
+ tplg_dbg("\t%s = %s\n", id, value);
+
+ /* get TLV data */
+ if (strcmp(id, "min") == 0)
+ data[0] = atoi(value);
+ else if (strcmp(id, "step") == 0)
+ data[1] = atoi(value);
+ else if (strcmp(id, "mute") == 0)
+ data[2] = atoi(value);
+ else
+ fprintf(stderr, "error: unknown key %s\n", id);
+ }
+
+ return 0;
+}
+
+/* Parse TLV.
+ *
+ * Each TLV is described in new section
+ * Supported TLV types: scale.
+ *
+ * SectionTLV."tlv name" {
+ * type {
+ *
+ * }
+ * }
+ */
+int tplg_parse_tlv(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;
+ int err = 0;
+ struct tplg_elem *elem;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_TLV);
+ if (!elem)
+ return -ENOMEM;
+
+ 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, "scale") == 0) {
+ err = tplg_parse_tlv_dbscale(n, elem);
+ if (err < 0) {
+ fprintf(stderr, "error: failed to DBScale");
+ return err;
+ }
+ continue;
+ }
+ }
+
+ return err;
+}
+
+/* Parse Control Bytes
+ *
+ * Each Control is described in new section
+ * Supported control types: Byte
+ *
+ * SectionControlBytes."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ * base "0"
+ * num_regs "16"
+ * mask "0xff"
+ * max "255"
+ * }
+ */
+int tplg_parse_control_bytes(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_bytes_control *be;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_BYTES);
+ if (!elem)
+ return -ENOMEM;
+
+ be = elem->bytes_ext;
+ be->size = elem->size;
+ strncpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
+
+ tplg_dbg(" Control Bytes: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "base") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->base = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->base);
+ continue;
+ }
+
+ if (strcmp(id, "num_regs") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->num_regs = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, be->num_regs);
+ continue;
+ }
+
+ if (strcmp(id, "mask") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ be->mask = strtol(val, NULL, 16);
+ tplg_dbg("\t%s: %d\n", id, be->mask);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, PARSER_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ be->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Control Enums.
+ *
+ * Enumerated control. Supports mutiple channels.
+ *
+ * SectionControlMixer."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ * texts "EQU1"
+ *
+ * channel."name" {
+ * }
+ *
+ * ops."ctl" {
+ * }
+ *
+ * tlv "hsw_vol_tlv"
+ * }
+ */
+int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
+ void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_enum_control *ec;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_ENUM);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ ec = elem->enum_ctrl;
+ strncpy(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ ec->hdr.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+ ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
+ ec->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ ec->channel[j].reg = -1;
+
+ tplg_dbg(" Control Enum: %s\n", elem->id);
+
+ snd_config_for_each(i, next, cfg) {
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "texts") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, PARSER_TYPE_TEXT, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ fprintf(stderr, "error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ ec->channel);
+ if (err < 0)
+ return err;
+
+ ec->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &ec->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* Parse Controls.
+ *
+ * Mixer control. Supports multiple channels.
+ *
+ * SectionControlMixer."control name" {
+ * comment "optional comments"
+ *
+ * index "1"
+ *
+ * channel."name" {
+ * }
+ *
+ * ops."ctl" {
+ * }
+ *
+ * max "32"
+ * invert "0"
+ *
+ * ops."ctl" {
+ * }
+ *
+ * tlv "hsw_vol_tlv"
+ * }
+ */
+int tplg_parse_control_mixer(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
+{
+ struct snd_soc_tplg_mixer_control *mc;
+ struct tplg_elem *elem;
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ const char *id, *val = NULL;
+ int err, j;
+
+ elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_MIXER);
+ if (!elem)
+ return -ENOMEM;
+
+ /* init new mixer */
+ mc = elem->mixer_ctrl;
+ strncpy(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+ mc->hdr.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
+ mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
+ mc->size = elem->size;
+ tplg->channel_idx = 0;
+
+ /* set channel reg to default state */
+ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
+ mc->channel[j].reg = -1;
+
+ tplg_dbg(" Control Mixer: %s\n", elem->id);
+
+ /* giterate trough each mixer elment */
+ snd_config_for_each(i, next, cfg) {
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ /* skip comments */
+ if (strcmp(id, "comment") == 0)
+ continue;
+ if (id[0] == '#')
+ continue;
+
+ if (strcmp(id, "index") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ elem->index = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, elem->index);
+ continue;
+ }
+
+ if (strcmp(id, "channel") == 0) {
+ if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
+ fprintf(stderr, "error: too many channels %s\n",
+ elem->id);
+ return -EINVAL;
+ }
+
+ err = tplg_parse_compound(tplg, n, tplg_parse_channel,
+ mc->channel);
+ if (err < 0)
+ return err;
+
+ mc->num_channels = tplg->channel_idx;
+ continue;
+ }
+
+ if (strcmp(id, "max") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ mc->max = atoi(val);
+ tplg_dbg("\t%s: %d\n", id, mc->max);
+ continue;
+ }
+
+ if (strcmp(id, "invert") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(val, "true") == 0)
+ mc->invert = 1;
+ else if (strcmp(val, "false") == 0)
+ mc->invert = 0;
+
+ tplg_dbg("\t%s: %d\n", id, mc->invert);
+ continue;
+ }
+
+ if (strcmp(id, "ops") == 0) {
+ err = tplg_parse_compound(tplg, n, tplg_parse_ops,
+ &mc->hdr);
+ if (err < 0)
+ return err;
+ continue;
+ }
+
+ if (strcmp(id, "tlv") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ err = tplg_ref_add(elem, PARSER_TYPE_TLV, val);
+ if (err < 0)
+ return err;
+
+ mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+
+ if (strcmp(id, "data") == 0) {
+ if (snd_config_get_string(n, &val) < 0)
+ return -EINVAL;
+
+ tplg_ref_add(elem, PARSER_TYPE_DATA, val);
+ tplg_dbg("\t%s: %s\n", id, val);
+ continue;
+ }
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 10/13] topology: Add Channel map parser.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (7 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 09/13] topology: Add CTL parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 11/13] topology: Add binary file builder Liam Girdwood
` (2 subsequent siblings)
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Add support for parsing channel map to control registers.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/channel.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
create mode 100644 src/topology/channel.c
diff --git a/src/topology/channel.c b/src/topology/channel.c
new file mode 100644
index 0000000..e33c70d
--- /dev/null
+++ b/src/topology/channel.c
@@ -0,0 +1,128 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* mapping of channel text names to types */
+static const struct map_elem channel_map[] = {
+ {"mono", SNDRV_CHMAP_MONO}, /* mono stream */
+ {"fl", SNDRV_CHMAP_FL}, /* front left */
+ {"fr", SNDRV_CHMAP_FR}, /* front right */
+ {"rl", SNDRV_CHMAP_RL}, /* rear left */
+ {"rr", SNDRV_CHMAP_RR}, /* rear right */
+ {"fc", SNDRV_CHMAP_FC}, /* front center */
+ {"lfe", SNDRV_CHMAP_LFE}, /* LFE */
+ {"sl", SNDRV_CHMAP_SL}, /* side left */
+ {"sr", SNDRV_CHMAP_SR}, /* side right */
+ {"rc", SNDRV_CHMAP_RC}, /* rear center */
+ {"flc", SNDRV_CHMAP_FLC}, /* front left center */
+ {"frc", SNDRV_CHMAP_FRC}, /* front right center */
+ {"rlc", SNDRV_CHMAP_RLC}, /* rear left center */
+ {"rrc", SNDRV_CHMAP_RRC}, /* rear right center */
+ {"flw", SNDRV_CHMAP_FLW}, /* front left wide */
+ {"frw", SNDRV_CHMAP_FRW}, /* front right wide */
+ {"flh", SNDRV_CHMAP_FLH}, /* front left high */
+ {"fch", SNDRV_CHMAP_FCH}, /* front center high */
+ {"frh", SNDRV_CHMAP_FRH}, /* front right high */
+ {"tc", SNDRV_CHMAP_TC}, /* top center */
+ {"tfl", SNDRV_CHMAP_TFL}, /* top front left */
+ {"tfr", SNDRV_CHMAP_TFR}, /* top front right */
+ {"tfc", SNDRV_CHMAP_TFC}, /* top front center */
+ {"trl", SNDRV_CHMAP_TRL}, /* top rear left */
+ {"trr", SNDRV_CHMAP_TRR}, /* top rear right */
+ {"trc", SNDRV_CHMAP_TRC}, /* top rear center */
+ {"tflc", SNDRV_CHMAP_TFLC}, /* top front left center */
+ {"tfrc", SNDRV_CHMAP_TFRC}, /* top front right center */
+ {"tsl", SNDRV_CHMAP_TSL}, /* top side left */
+ {"tsr", SNDRV_CHMAP_TSR}, /* top side right */
+ {"llfe", SNDRV_CHMAP_LLFE}, /* left LFE */
+ {"rlfe", SNDRV_CHMAP_RLFE}, /* right LFE */
+ {"bc", SNDRV_CHMAP_BC}, /* bottom center */
+ {"blc", SNDRV_CHMAP_BLC}, /* bottom left center */
+ {"brc", SNDRV_CHMAP_BRC}, /* bottom right center */
+};
+
+
+static int lookup_channel(const char *c)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_map); i++) {
+ if (strcasecmp(channel_map[i].name, c) == 0) {
+ return channel_map[i].id;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Parse a channel.
+ *
+ * channel."channel_map.name" {
+ * reg "0" (register)
+ * shift "0" (shift)
+ * }
+ */
+int tplg_parse_channel(snd_tplg_t *tplg,
+ snd_config_t *cfg, void *private)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n;
+ struct snd_soc_tplg_channel *channel = private;
+ const char *id, *value;
+
+ if (tplg->channel_idx >= SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
+
+ channel += tplg->channel_idx;
+ snd_config_get_id(cfg, &id);
+ tplg_dbg("\tChannel %s at index %d\n", id, tplg->channel_idx);
+
+ channel->id = lookup_channel(id);
+ if (channel->id < 0) {
+ fprintf(stderr, "error: invalid channel %s\n", id);
+ return -EINVAL;
+ }
+
+ channel->size = sizeof(*channel);
+ tplg_dbg("\tChan %s = %d\n", id, channel->id);
+
+ 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;
+
+ if (strcmp(id, "reg") == 0)
+ channel->reg = atoi(value);
+ else if (strcmp(id, "shift") == 0)
+ channel->shift = atoi(value);
+
+ tplg_dbg("\t\t%s = %s\n", id, value);
+ }
+
+ tplg->channel_idx++;
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 11/13] topology: Add binary file builder.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (8 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 10/13] topology: Add Channel map parser Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 12/13] topology: autotools: Add build support for topology core Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 13/13] topology: doxygen: Add doxygen " Liam Girdwood
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Build the binary output file from all the locally parsed objects and elements.
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
src/topology/builder.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 275 insertions(+)
create mode 100644 src/topology/builder.c
diff --git a/src/topology/builder.c b/src/topology/builder.c
new file mode 100644
index 0000000..12adc81
--- /dev/null
+++ b/src/topology/builder.c
@@ -0,0 +1,275 @@
+/*
+ Copyright(c) 2014-2015 Intel Corporation
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License 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.
+
+ Authors: Mengdong Lin <mengdong.lin@intel.com>
+ Yao Jin <yao.jin@intel.com>
+ Liam Girdwood <liam.r.girdwood@linux.intel.com>
+*/
+
+#include "list.h"
+#include "tplg_local.h"
+
+/* verbose output detailing each object size and file position */
+static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
+{
+ int offset;
+ va_list va;
+
+ if (!tplg->verbose)
+ return;
+
+ offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ va_start(va, fmt);
+ fprintf(stdout, "0x%6.6x/%6.6d -", offset, offset);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+}
+
+/* write out block header to output file */
+static int write_block_header(snd_tplg_t *tplg, unsigned int type,
+ unsigned int vendor_type, unsigned int version, unsigned int index,
+ size_t payload_size, int count)
+{
+ struct snd_soc_tplg_hdr hdr;
+ size_t bytes;
+ int offset = lseek(tplg->out_fd, 0, SEEK_CUR);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.magic = SND_SOC_TPLG_MAGIC;
+ hdr.abi = SND_SOC_TPLG_ABI_VERSION;
+ hdr.type = type;
+ hdr.vendor_type = vendor_type;
+ hdr.version = version;
+ hdr.payload_size = payload_size;
+ hdr.index = index;
+ hdr.size = sizeof(hdr);
+ hdr.count = count;
+
+ /* make sure file offset is aligned with the calculated HDR offset */
+ if ((unsigned int)offset != tplg->next_hdr_pos) {
+ fprintf(stderr, "error: New header is at offset 0x%x but file"
+ " offset 0x%x is %s by %d bytes\n",
+ tplg->next_hdr_pos, offset,
+ (unsigned int)offset > tplg->next_hdr_pos ? "ahead" : "behind",
+ abs(offset - tplg->next_hdr_pos));
+ exit(-EINVAL);
+ }
+
+ verbose(tplg, " header type %d size 0x%lx/%ld vendor %d "
+ "version %d\n", type, (long unsigned int)payload_size,
+ (long int)payload_size, vendor_type, version);
+
+ tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
+
+ bytes = write(tplg->out_fd, &hdr, sizeof(hdr));
+ if (bytes != sizeof(hdr)) {
+ fprintf(stderr, "error: can't write section header %lu\n",
+ (long unsigned int)bytes);
+ return bytes;
+ }
+
+ return bytes;
+}
+
+static int write_elem_block(snd_tplg_t *tplg,
+ struct list_head *base, int size, int tplg_type, const char *obj_name)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+ int ret, wsize = 0, count = 0;
+
+ /* count number of elements */
+ list_for_each_safe(pos, npos, base)
+ count++;
+
+ /* write the header for this block */
+ ret = write_block_header(tplg, tplg_type, 0,
+ SND_SOC_TPLG_ABI_VERSION, 0, size, count);
+ if (ret < 0) {
+ fprintf(stderr, "error: failed to write %s block %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ /* write each elem to block */
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ /* compound elems have already been copied to other elems */
+ if (elem->compound_elem)
+ continue;
+
+ if (elem->type != PARSER_TYPE_DAPM_GRAPH)
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->id, elem->size);
+ else
+ verbose(tplg, " %s '%s': write %d bytes\n",
+ obj_name, elem->route->source, elem->size);
+
+ count = write(tplg->out_fd, elem->obj, elem->size);
+ if (count < 0) {
+ fprintf(stderr, "error: failed to write %s %d\n",
+ obj_name, ret);
+ return ret;
+ }
+
+ wsize += count;
+ }
+
+ /* make sure we have written the correct size */
+ if (wsize != size) {
+ fprintf(stderr, "error: size mismatch. Expected %d wrote %d\n",
+ size, wsize);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int calc_block_size(struct list_head *base)
+{
+ struct list_head *pos, *npos;
+ struct tplg_elem *elem;
+ int size = 0;
+
+ list_for_each_safe(pos, npos, base) {
+
+ elem = list_entry(pos, struct tplg_elem, list);
+
+ /* compound elems have already been copied to other elems */
+ if (elem->compound_elem)
+ continue;
+
+ size += elem->size;
+ }
+
+ return size;
+}
+
+static int write_block(snd_tplg_t *tplg, struct list_head *base,
+ int type)
+{
+ int size;
+
+ /* calculate the block size in bytes for all elems in this list */
+ size = calc_block_size(base);
+ if (size <= 0)
+ return size;
+
+ verbose(tplg, " block size for type %d is %d\n", type, size);
+
+ /* write each elem for this block */
+ switch (type) {
+ case PARSER_TYPE_MIXER:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_MIXER, "mixer");
+ case PARSER_TYPE_BYTES:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_BYTES, "bytes");
+ case PARSER_TYPE_ENUM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_ENUM, "enum");
+ case PARSER_TYPE_DAPM_GRAPH:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_GRAPH, "route");
+ case PARSER_TYPE_DAPM_WIDGET:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAPM_WIDGET, "widget");
+ case PARSER_TYPE_PCM:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_PCM, "pcm");
+ case PARSER_TYPE_BE:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "be");
+ case PARSER_TYPE_CC:
+ return write_elem_block(tplg, base, size,
+ SND_SOC_TPLG_TYPE_DAI_LINK, "cc");
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int tplg_write_data(snd_tplg_t *tplg)
+{
+ int ret;
+
+ /* write mixer elems. */
+ ret = write_block(tplg, &tplg->mixer_list,
+ PARSER_TYPE_MIXER);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write enum control elems. */
+ ret = write_block(tplg, &tplg->enum_list,
+ PARSER_TYPE_ENUM);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write bytes extended control elems. */
+ ret = write_block(tplg, &tplg->bytes_ext_list,
+ PARSER_TYPE_BYTES);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write control elems %d\n", ret);
+ return ret;
+ }
+
+ /* write widget elems */
+ ret = write_block(tplg, &tplg->widget_list,
+ PARSER_TYPE_DAPM_WIDGET);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write widget elems %d\n", ret);
+ return ret;
+ }
+
+ /* write pcm elems */
+ ret = write_block(tplg, &tplg->pcm_list,
+ PARSER_TYPE_PCM);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write pcm elems %d\n", ret);
+ return ret;
+ }
+
+ /* write be elems */
+ ret = write_block(tplg, &tplg->be_list,
+ PARSER_TYPE_BE);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write be elems %d\n", ret);
+ return ret;
+ }
+
+ /* write cc elems */
+ ret = write_block(tplg, &tplg->cc_list,
+ PARSER_TYPE_CC);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write cc elems %d\n", ret);
+ return ret;
+ }
+
+ /* write route elems */
+ ret = write_block(tplg, &tplg->route_list,
+ PARSER_TYPE_DAPM_GRAPH);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write graph elems %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 12/13] topology: autotools: Add build support for topology core
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (9 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 11/13] topology: Add binary file builder Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
2015-07-01 13:44 ` [PATCH v2 13/13] topology: doxygen: Add doxygen " Liam Girdwood
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
configure.ac | 11 ++++++++++-
include/Makefile.am | 4 ++++
src/Makefile.am | 7 +++++++
src/topology/Makefile.am | 19 +++++++++++++++++++
4 files changed, 40 insertions(+), 1 deletion(-)
create mode 100644 src/topology/Makefile.am
diff --git a/configure.ac b/configure.ac
index 9621d4e..a482b3e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -380,6 +380,9 @@ AC_ARG_ENABLE(seq,
AC_ARG_ENABLE(ucm,
AS_HELP_STRING([--disable-ucm], [disable the use-case-manager component]),
[build_ucm="$enableval"], [build_ucm="yes"])
+AC_ARG_ENABLE(topology,
+ AS_HELP_STRING([--disable-topology], [disable the DSP topology component]),
+ [build_topology="$enableval"], [build_topology="yes"])
AC_ARG_ENABLE(alisp,
AS_HELP_STRING([--disable-alisp], [disable the alisp component]),
[build_alisp="$enableval"], [build_alisp="yes"])
@@ -422,6 +425,7 @@ AM_CONDITIONAL([BUILD_RAWMIDI], [test x$build_rawmidi = xyes])
AM_CONDITIONAL([BUILD_HWDEP], [test x$build_hwdep = xyes])
AM_CONDITIONAL([BUILD_SEQ], [test x$build_seq = xyes])
AM_CONDITIONAL([BUILD_UCM], [test x$build_ucm = xyes])
+AM_CONDITIONAL([BUILD_TOPOLOGY], [test x$build_topology = xyes])
AM_CONDITIONAL([BUILD_ALISP], [test x$build_alisp = xyes])
AM_CONDITIONAL([BUILD_PYTHON], [test x$build_python = xyes])
@@ -443,6 +447,9 @@ fi
if test "$build_ucm" = "yes"; then
AC_DEFINE([BUILD_UCM], "1", [Build UCM component])
fi
+if test "$build_topology" = "yes"; then
+ AC_DEFINE([BUILD_TOPOLOGY], "1", [Build DSP Topology component])
+fi
dnl PCM Plugins
@@ -643,7 +650,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
src/pcm/Makefile src/pcm/scopes/Makefile \
src/rawmidi/Makefile src/timer/Makefile \
src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile \
- src/alisp/Makefile \
+ src/alisp/Makefile src/topology/Makefile \
src/conf/Makefile src/conf/alsa.conf.d/Makefile \
src/conf/cards/Makefile \
src/conf/pcm/Makefile \
@@ -656,6 +663,8 @@ 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/topology/Makefile \
+ src/conf/topology/broadwell/Makefile \
modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \
alsalisp/Makefile aserver/Makefile \
test/Makefile test/lsb/Makefile \
diff --git a/include/Makefile.am b/include/Makefile.am
index 4baa03a..ff931fd 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -50,6 +50,10 @@ if BUILD_UCM
alsainclude_HEADERS += use-case.h
endif
+if BUILD_TOPOLOGY
+alsainclude_HEADERS += topology.h
+endif
+
if BUILD_ALISP
alsainclude_HEADERS += alisp.h
endif
diff --git a/src/Makefile.am b/src/Makefile.am
index fa255ff..57686a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,6 +42,10 @@ if BUILD_UCM
SUBDIRS += ucm
libasound_la_LIBADD += ucm/libucm.la
endif
+if BUILD_TOPOLOGY
+SUBDIRS += topology
+libasound_la_LIBADD += topology/libtopology.la
+endif
if BUILD_ALISP
SUBDIRS += alisp
libasound_la_LIBADD += alisp/libalisp.la
@@ -81,6 +85,9 @@ seq/libseq.la:
ucm/libucm.la:
$(MAKE) -C ucm libucm.la
+topology/libtopology.la:
+ $(MAKE) -C topology libtopology.la
+
instr/libinstr.la:
$(MAKE) -C instr libinstr.la
diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
new file mode 100644
index 0000000..3fb8bf7
--- /dev/null
+++ b/src/topology/Makefile.am
@@ -0,0 +1,19 @@
+EXTRA_LTLIBRARIES = libtopology.la
+
+libtopology_la_SOURCES =\
+ parser.c \
+ builder.c \
+ ctl.c \
+ dapm.c \
+ pcm.c \
+ data.c \
+ text.c \
+ channel.c \
+ ops.c \
+ elem.c
+
+noinst_HEADERS = tplg_local.h
+
+all: libtopology.la
+
+AM_CPPFLAGS=-I$(top_srcdir)/include
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v2 13/13] topology: doxygen: Add doxygen support for topology core.
2015-07-01 13:44 [PATCH v2 01/13] topology: uapi: Add UAPI headers for topology ABI Liam Girdwood
` (10 preceding siblings ...)
2015-07-01 13:44 ` [PATCH v2 12/13] topology: autotools: Add build support for topology core Liam Girdwood
@ 2015-07-01 13:44 ` Liam Girdwood
11 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-07-01 13:44 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, Vinod Koul, Mark Brown, Liam Girdwood
Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
---
doc/doxygen.cfg.in | 7 +++++--
doc/index.doxygen | 1 +
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 043e75b..92bd52b 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -29,6 +29,7 @@ INPUT = @top_srcdir@/doc/index.doxygen \
@top_srcdir@/include/control_external.h \
@top_srcdir@/include/mixer.h \
@top_srcdir@/include/use-case.h \
+ @top_srcdir@/include/topology.h \
@top_srcdir@/src/error.c \
@top_srcdir@/src/dlmisc.c \
@top_srcdir@/src/async.c \
@@ -78,7 +79,8 @@ INPUT = @top_srcdir@/doc/index.doxygen \
@top_srcdir@/src/timer \
@top_srcdir@/src/hwdep \
@top_srcdir@/src/seq \
- @top_srcdir@/src/ucm
+ @top_srcdir@/src/ucm \
+ @top_srcdir@/src/topology
EXCLUDE = @top_srcdir@/src/control/control_local.h \
@top_srcdir@/src/pcm/atomic.h \
@top_srcdir@/src/pcm/interval.h \
@@ -94,7 +96,8 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \
@top_srcdir@/src/mixer/mixer_local.h \
@top_srcdir@/src/rawmidi/rawmidi_local.h \
@top_srcdir@/src/seq/seq_local.h \
- @top_srcdir@/src/ucm/ucm_local.h
+ @top_srcdir@/src/ucm/ucm_local.h \
+ @top_srcdir@/src/topology/tplg_local.h
RECURSIVE = YES
FILE_PATTERNS = *.c *.h
EXAMPLE_PATH = @top_srcdir@/test
diff --git a/doc/index.doxygen b/doc/index.doxygen
index 7d049fe..b40c75a 100644
--- a/doc/index.doxygen
+++ b/doc/index.doxygen
@@ -41,6 +41,7 @@ may be placed in the library code instead of the kernel driver.</P>
<LI>Page \ref timer explains the design of the Timer API.
<LI>Page \ref seq explains the design of the Sequencer API.
<LI>Page \ref ucm explains the use case API.
+ <LI>Page \ref topology explains the DSP topology API.
</UL>
<H2>Configuration</H2>
--
2.1.4
^ permalink raw reply related [flat|nested] 26+ messages in thread