Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: dapm: improve debugfs output and introduce

Merge series from Luca Ceresoli <luca.ceresoli@bootlin.com>:

This patch series improves the tools available to understand and debug
DAPM.

+361 -2
+6
MAINTAINERS
··· 20669 20669 F: include/uapi/sound/asoc.h 20670 20670 F: sound/soc/ 20671 20671 20672 + SOUND - SOC LAYER / dapm-graph 20673 + M: Luca Ceresoli <luca.ceresoli@bootlin.com> 20674 + L: linux-sound@vger.kernel.org 20675 + S: Maintained 20676 + F: tools/sound/dapm-graph 20677 + 20672 20678 SOUND - SOUND OPEN FIRMWARE (SOF) DRIVERS 20673 20679 M: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 20674 20680 M: Liam Girdwood <lgirdwood@gmail.com>
+52 -2
sound/soc/soc-dapm.c
··· 2094 2094 } 2095 2095 2096 2096 #ifdef CONFIG_DEBUG_FS 2097 + 2098 + static const char * const snd_soc_dapm_type_name[] = { 2099 + [snd_soc_dapm_input] = "input", 2100 + [snd_soc_dapm_output] = "output", 2101 + [snd_soc_dapm_mux] = "mux", 2102 + [snd_soc_dapm_demux] = "demux", 2103 + [snd_soc_dapm_mixer] = "mixer", 2104 + [snd_soc_dapm_mixer_named_ctl] = "mixer_named_ctl", 2105 + [snd_soc_dapm_pga] = "pga", 2106 + [snd_soc_dapm_out_drv] = "out_drv", 2107 + [snd_soc_dapm_adc] = "adc", 2108 + [snd_soc_dapm_dac] = "dac", 2109 + [snd_soc_dapm_micbias] = "micbias", 2110 + [snd_soc_dapm_mic] = "mic", 2111 + [snd_soc_dapm_hp] = "hp", 2112 + [snd_soc_dapm_spk] = "spk", 2113 + [snd_soc_dapm_line] = "line", 2114 + [snd_soc_dapm_switch] = "switch", 2115 + [snd_soc_dapm_vmid] = "vmid", 2116 + [snd_soc_dapm_pre] = "pre", 2117 + [snd_soc_dapm_post] = "post", 2118 + [snd_soc_dapm_supply] = "supply", 2119 + [snd_soc_dapm_pinctrl] = "pinctrl", 2120 + [snd_soc_dapm_regulator_supply] = "regulator_supply", 2121 + [snd_soc_dapm_clock_supply] = "clock_supply", 2122 + [snd_soc_dapm_aif_in] = "aif_in", 2123 + [snd_soc_dapm_aif_out] = "aif_out", 2124 + [snd_soc_dapm_siggen] = "siggen", 2125 + [snd_soc_dapm_sink] = "sink", 2126 + [snd_soc_dapm_dai_in] = "dai_in", 2127 + [snd_soc_dapm_dai_out] = "dai_out", 2128 + [snd_soc_dapm_dai_link] = "dai_link", 2129 + [snd_soc_dapm_kcontrol] = "kcontrol", 2130 + [snd_soc_dapm_buffer] = "buffer", 2131 + [snd_soc_dapm_scheduler] = "scheduler", 2132 + [snd_soc_dapm_effect] = "effect", 2133 + [snd_soc_dapm_src] = "src", 2134 + [snd_soc_dapm_asrc] = "asrc", 2135 + [snd_soc_dapm_encoder] = "encoder", 2136 + [snd_soc_dapm_decoder] = "decoder", 2137 + }; 2138 + 2097 2139 static ssize_t dapm_widget_power_read_file(struct file *file, 2098 2140 char __user *user_buf, 2099 2141 size_t count, loff_t *ppos) ··· 2146 2104 int in, out; 2147 2105 ssize_t ret; 2148 2106 struct snd_soc_dapm_path *p = NULL; 2107 + const char *c_name; 2108 + 2109 + BUILD_BUG_ON(ARRAY_SIZE(snd_soc_dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT); 2149 2110 2150 2111 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 2151 2112 if (!buf) ··· 2181 2136 w->sname, 2182 2137 w->active ? "active" : "inactive"); 2183 2138 2139 + ret += scnprintf(buf + ret, PAGE_SIZE - ret, " widget-type %s\n", 2140 + snd_soc_dapm_type_name[w->id]); 2141 + 2184 2142 snd_soc_dapm_for_each_direction(dir) { 2185 2143 rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 2186 2144 snd_soc_dapm_widget_for_each_path(w, dir, p) { ··· 2193 2145 if (!p->connect) 2194 2146 continue; 2195 2147 2148 + c_name = p->node[rdir]->dapm->component ? 2149 + p->node[rdir]->dapm->component->name : NULL; 2196 2150 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 2197 - " %s \"%s\" \"%s\"\n", 2151 + " %s \"%s\" \"%s\" \"%s\"\n", 2198 2152 (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out", 2199 2153 p->name ? p->name : "static", 2200 - p->node[rdir]->name); 2154 + p->node[rdir]->name, c_name); 2201 2155 } 2202 2156 } 2203 2157
+303
tools/sound/dapm-graph
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Generate a graph of the current DAPM state for an audio card 5 + # 6 + # Copyright 2024 Bootlin 7 + # Author: Luca Ceresoli <luca.ceresol@bootlin.com> 8 + 9 + set -eu 10 + 11 + STYLE_NODE_ON="shape=box,style=bold,color=green4" 12 + STYLE_NODE_OFF="shape=box,style=filled,color=gray30,fillcolor=gray95" 13 + 14 + # Print usage and exit 15 + # 16 + # $1 = exit return value 17 + # $2 = error string (required if $1 != 0) 18 + usage() 19 + { 20 + if [ "${1}" -ne 0 ]; then 21 + echo "${2}" >&2 22 + fi 23 + 24 + echo " 25 + Generate a graph of the current DAPM state for an audio card. 26 + 27 + The DAPM state can be obtained via debugfs for a card on the local host or 28 + a remote target, or from a local copy of the debugfs tree for the card. 29 + 30 + Usage: 31 + $(basename $0) [options] -c CARD - Local sound card 32 + $(basename $0) [options] -c CARD -r REMOTE_TARGET - Card on remote system 33 + $(basename $0) [options] -d STATE_DIR - Local directory 34 + 35 + Options: 36 + -c CARD Sound card to get DAPM state of 37 + -r REMOTE_TARGET Get DAPM state from REMOTE_TARGET via SSH and SCP 38 + instead of using a local sound card 39 + -d STATE_DIR Get DAPM state from a local copy of a debugfs tree 40 + -o OUT_FILE Output file (default: dapm.dot) 41 + -D Show verbose debugging info 42 + -h Print this help and exit 43 + 44 + The output format is implied by the extension of OUT_FILE: 45 + 46 + * Use the .dot extension to generate a text graph representation in 47 + graphviz dot syntax. 48 + * Any other extension is assumed to be a format supported by graphviz for 49 + rendering, e.g. 'png', 'svg', and will produce both the .dot file and a 50 + picture from it. This requires the 'dot' program from the graphviz 51 + package. 52 + " 53 + 54 + exit ${1} 55 + } 56 + 57 + # Connect to a remote target via SSH, collect all DAPM files from debufs 58 + # into a tarball and get the tarball via SCP into $3/dapm.tar 59 + # 60 + # $1 = target as used by ssh and scp, e.g. "root@192.168.1.1" 61 + # $2 = sound card name 62 + # $3 = temp dir path (present on the host, created on the target) 63 + # $4 = local directory to extract the tarball into 64 + # 65 + # Requires an ssh+scp server, find and tar+gz on the target 66 + # 67 + # Note: the tarball is needed because plain 'scp -r' from debugfs would 68 + # copy only empty files 69 + grab_remote_files() 70 + { 71 + echo "Collecting DAPM state from ${1}" 72 + dbg_echo "Collected DAPM state in ${3}" 73 + 74 + ssh "${1}" " 75 + set -eu && 76 + cd \"/sys/kernel/debug/asoc/${2}\" && 77 + find * -type d -exec mkdir -p ${3}/dapm-tree/{} \; && 78 + find * -type f -exec cp \"{}\" \"${3}/dapm-tree/{}\" \; && 79 + cd ${3}/dapm-tree && 80 + tar cf ${3}/dapm.tar ." 81 + scp -q "${1}:${3}/dapm.tar" "${3}" 82 + 83 + mkdir -p "${4}" 84 + tar xf "${tmp_dir}/dapm.tar" -C "${4}" 85 + } 86 + 87 + # Parse a widget file and generate graph description in graphviz dot format 88 + # 89 + # Skips any file named "bias_level". 90 + # 91 + # $1 = temporary work dir 92 + # $2 = component name 93 + # $3 = widget filename 94 + process_dapm_widget() 95 + { 96 + local tmp_dir="${1}" 97 + local c_name="${2}" 98 + local w_file="${3}" 99 + local dot_file="${tmp_dir}/main.dot" 100 + local links_file="${tmp_dir}/links.dot" 101 + 102 + local w_name="$(basename "${w_file}")" 103 + local w_tag="${c_name}_${w_name}" 104 + 105 + if [ "${w_name}" = "bias_level" ]; then 106 + return 0 107 + fi 108 + 109 + dbg_echo " + Widget: ${w_name}" 110 + 111 + cat "${w_file}" | ( 112 + read line 113 + 114 + if echo "${line}" | grep -q ': On ' 115 + then local node_style="${STYLE_NODE_ON}" 116 + else local node_style="${STYLE_NODE_OFF}" 117 + fi 118 + 119 + local w_type="" 120 + while read line; do 121 + # Collect widget type if present 122 + if echo "${line}" | grep -q '^widget-type '; then 123 + local w_type_raw="$(echo "$line" | cut -d ' ' -f 2)" 124 + dbg_echo " - Widget type: ${w_type_raw}" 125 + 126 + # Note: escaping '\n' is tricky to get working with both 127 + # bash and busybox ash, so use a '%' here and replace it 128 + # later 129 + local w_type="%n[${w_type_raw}]" 130 + fi 131 + 132 + # Collect any links. We could use "in" links or "out" links, 133 + # let's use "in" links 134 + if echo "${line}" | grep -q '^in '; then 135 + local w_src=$(echo "$line" | 136 + awk -F\" '{print $6 "_" $4}' | 137 + sed 's/^(null)_/ROOT_/') 138 + dbg_echo " - Input route from: ${w_src}" 139 + echo " \"${w_src}\" -> \"$w_tag\"" >> "${links_file}" 140 + fi 141 + done 142 + 143 + echo " \"${w_tag}\" [label=\"${w_name}${w_type}\",${node_style}]" | 144 + tr '%' '\\' >> "${dot_file}" 145 + ) 146 + } 147 + 148 + # Parse the DAPM tree for a sound card component and generate graph 149 + # description in graphviz dot format 150 + # 151 + # $1 = temporary work dir 152 + # $2 = component directory 153 + # $3 = forced component name (extracted for path if empty) 154 + process_dapm_component() 155 + { 156 + local tmp_dir="${1}" 157 + local c_dir="${2}" 158 + local c_name="${3}" 159 + local dot_file="${tmp_dir}/main.dot" 160 + local links_file="${tmp_dir}/links.dot" 161 + 162 + if [ -z "${c_name}" ]; then 163 + # Extract directory name into component name: 164 + # "./cs42l51.0-004a/dapm" -> "cs42l51.0-004a" 165 + c_name="$(basename $(dirname "${c_dir}"))" 166 + fi 167 + 168 + dbg_echo " * Component: ${c_name}" 169 + 170 + echo "" >> "${dot_file}" 171 + echo " subgraph \"${c_name}\" {" >> "${dot_file}" 172 + echo " cluster = true" >> "${dot_file}" 173 + echo " label = \"${c_name}\"" >> "${dot_file}" 174 + echo " color=dodgerblue" >> "${dot_file}" 175 + 176 + # Create empty file to ensure it will exist in all cases 177 + >"${links_file}" 178 + 179 + # Iterate over widgets in the component dir 180 + for w_file in ${c_dir}/*; do 181 + process_dapm_widget "${tmp_dir}" "${c_name}" "${w_file}" 182 + done 183 + 184 + echo " }" >> "${dot_file}" 185 + 186 + cat "${links_file}" >> "${dot_file}" 187 + } 188 + 189 + # Parse the DAPM tree for a sound card and generate graph description in 190 + # graphviz dot format 191 + # 192 + # $1 = temporary work dir 193 + # $2 = directory tree with DAPM state (either in debugfs or a mirror) 194 + process_dapm_tree() 195 + { 196 + local tmp_dir="${1}" 197 + local dapm_dir="${2}" 198 + local dot_file="${tmp_dir}/main.dot" 199 + 200 + echo "digraph G {" > "${dot_file}" 201 + echo " fontname=\"sans-serif\"" >> "${dot_file}" 202 + echo " node [fontname=\"sans-serif\"]" >> "${dot_file}" 203 + 204 + 205 + # Process root directory (no component) 206 + process_dapm_component "${tmp_dir}" "${dapm_dir}/dapm" "ROOT" 207 + 208 + # Iterate over components 209 + for c_dir in "${dapm_dir}"/*/dapm 210 + do 211 + process_dapm_component "${tmp_dir}" "${c_dir}" "" 212 + done 213 + 214 + echo "}" >> "${dot_file}" 215 + } 216 + 217 + main() 218 + { 219 + # Parse command line 220 + local out_file="dapm.dot" 221 + local card_name="" 222 + local remote_target="" 223 + local dapm_tree="" 224 + local dbg_on="" 225 + while getopts "c:r:d:o:Dh" arg; do 226 + case $arg in 227 + c) card_name="${OPTARG}" ;; 228 + r) remote_target="${OPTARG}" ;; 229 + d) dapm_tree="${OPTARG}" ;; 230 + o) out_file="${OPTARG}" ;; 231 + D) dbg_on="1" ;; 232 + h) usage 0 ;; 233 + *) usage 1 ;; 234 + esac 235 + done 236 + shift $(($OPTIND - 1)) 237 + 238 + if [ -n "${dapm_tree}" ]; then 239 + if [ -n "${card_name}${remote_target}" ]; then 240 + usage 1 "Cannot use -c and -r with -d" 241 + fi 242 + echo "Using local tree: ${dapm_tree}" 243 + elif [ -n "${remote_target}" ]; then 244 + if [ -z "${card_name}" ]; then 245 + usage 1 "-r requires -c" 246 + fi 247 + echo "Using card ${card_name} from remote target ${remote_target}" 248 + elif [ -n "${card_name}" ]; then 249 + echo "Using local card: ${card_name}" 250 + else 251 + usage 1 "Please choose mode using -c, -r or -d" 252 + fi 253 + 254 + # Define logging function 255 + if [ "${dbg_on}" ]; then 256 + dbg_echo() { 257 + echo "$*" >&2 258 + } 259 + else 260 + dbg_echo() { 261 + : 262 + } 263 + fi 264 + 265 + # Filename must have a dot in order the infer the format from the 266 + # extension 267 + if ! echo "${out_file}" | grep -qE '\.'; then 268 + echo "Missing extension in output filename ${out_file}" >&2 269 + usage 270 + exit 1 271 + fi 272 + 273 + local out_fmt="${out_file##*.}" 274 + local dot_file="${out_file%.*}.dot" 275 + 276 + dbg_echo "dot file: $dot_file" 277 + dbg_echo "Output file: $out_file" 278 + dbg_echo "Output format: $out_fmt" 279 + 280 + tmp_dir="$(mktemp -d /tmp/$(basename $0).XXXXXX)" 281 + trap "{ rm -fr ${tmp_dir}; }" INT TERM EXIT 282 + 283 + if [ -z "${dapm_tree}" ] 284 + then 285 + dapm_tree="/sys/kernel/debug/asoc/${card_name}" 286 + fi 287 + if [ -n "${remote_target}" ]; then 288 + dapm_tree="${tmp_dir}/dapm-tree" 289 + grab_remote_files "${remote_target}" "${card_name}" "${tmp_dir}" "${dapm_tree}" 290 + fi 291 + # In all cases now ${dapm_tree} contains the DAPM state 292 + 293 + process_dapm_tree "${tmp_dir}" "${dapm_tree}" 294 + cp "${tmp_dir}/main.dot" "${dot_file}" 295 + 296 + if [ "${out_file}" != "${dot_file}" ]; then 297 + dot -T"${out_fmt}" "${dot_file}" -o "${out_file}" 298 + fi 299 + 300 + echo "Generated file ${out_file}" 301 + } 302 + 303 + main "${@}"