xref: /aosp_15_r20/external/coreboot/util/hda-decoder/main.go (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1// SPDX-License-Identifier: GPL-2.0-only
2package main
3
4import (
5	"bufio"
6	"flag"
7	"fmt"
8	"log"
9	"os"
10	"path/filepath"
11	"regexp"
12	"review.coreboot.org/coreboot.git/util/hda-decoder/decoder"
13	"strconv"
14	"strings"
15)
16
17var indentLevel int = 0
18
19func indentedPrintf(format string, args ...interface{}) (n int, err error) {
20	s := fmt.Sprintf("%s%s", strings.Repeat("\t", indentLevel), format)
21	return fmt.Printf(s, args...)
22}
23
24func stringToUint32(s string) uint32 {
25	s = strings.Replace(s, "0x", "", -1)
26	v, err := strconv.ParseUint(s, 16, 32)
27	if err != nil {
28		log.Fatal(err)
29	}
30	return uint32(v)
31}
32
33func decodeConfig(config uint32) {
34	out := decoder.ToHumanReadable(decoder.Decode(config))
35
36	indentedPrintf("%s,\n", out.PortConnectivity)
37	indentedPrintf("%s,\n", out.Location)
38	indentedPrintf("%s,\n", out.DefaultDevice)
39	indentedPrintf("%s,\n", out.ConnectionType)
40	indentedPrintf("%s,\n", out.Color)
41	indentedPrintf("%s,\n", out.Misc)
42	indentedPrintf("%s, %s\n", out.DefaultAssociation, out.Sequence)
43}
44
45func printDisconnectedPort(config uint32) {
46	// The value 0x411111f0 is not defined in the specification, but is a
47	// common value vendors use to indicate "not connected".
48	const nc uint32 = 0x411111f0
49
50	// Setting some values (e.g. 0x40000000) as `AZALIA_PIN_CFG_NC(0)` is
51	// probably harmless. However, we will stay on the safe side for now.
52	if (config & 0xfffffff0) != nc {
53		// Do not decode these values, as they would likely describe a
54		// bogus device which could be slighly confusing.
55		fmt.Printf("0x%08x), // does not describe a jack or internal device\n", config)
56	} else {
57		fmt.Printf("AZALIA_PIN_CFG_NC(%d)),\n", (config & 0x0000000f))
58	}
59}
60
61func decodeFile(path string, codec uint32) {
62	file, err := os.Open(path)
63	if err != nil {
64		log.Fatal(err)
65	}
66	defer file.Close()
67
68	scanner := bufio.NewScanner(file)
69
70	for scanner.Scan() {
71		fields := strings.Fields(scanner.Text())
72		pin := stringToUint32(fields[0])
73		config := stringToUint32(fields[1])
74
75		indentedPrintf("AZALIA_PIN_CFG(%d, 0x%02x, ", codec, pin)
76		if decoder.PortIsConnected(config) {
77			fmt.Printf("AZALIA_PIN_DESC(\n")
78			indentLevel += 1
79			decodeConfig(config)
80			indentLevel -= 1
81			indentedPrintf(")),\n")
82		} else {
83			printDisconnectedPort(config)
84		}
85	}
86}
87
88func getFileContents(path string) string {
89	contents, err := os.ReadFile(path)
90	if err != nil {
91		log.Fatal(err)
92	}
93	return strings.TrimSpace(string(contents))
94}
95
96func getLineCount(path string) int {
97	return len(strings.Split(getFileContents(path), "\n"))
98}
99
100func decodeDeviceCodec(path string, codec uint32, isLastCodec bool, generate bool) {
101	if generate {
102		vendorId := getFileContents(path + "/vendor_id")
103		vendorName := getFileContents(path + "/vendor_name")
104		chipName := getFileContents(path + "/chip_name")
105		subsystemId := getFileContents(path + "/subsystem_id")
106		lineCount := getLineCount(path + "/init_pin_configs")
107
108		indentedPrintf("%s, // Vendor/Device ID: %s %s\n", vendorId, vendorName, chipName)
109		indentedPrintf("%s, // Subsystem ID\n", subsystemId)
110		indentedPrintf("%d,\n", lineCount+1)
111		indentedPrintf("AZALIA_SUBVENDOR(%d, %s),\n\n", codec, subsystemId)
112	}
113
114	decodeFile(path+"/init_pin_configs", codec)
115	if !isLastCodec {
116		fmt.Printf("\n")
117	}
118}
119
120func decodeDeviceCodecs(generate bool) {
121	matches, err := filepath.Glob("/sys/class/sound/hwC0D*")
122	if err != nil {
123		log.Fatal(err)
124	}
125	re := regexp.MustCompile(`D([0-9]+)$`)
126
127	for i, match := range matches {
128		codec := stringToUint32(re.FindStringSubmatch(match)[1])
129		isLastCodec := (i + 1) == len(matches)
130
131		decodeDeviceCodec(match, codec, isLastCodec, generate)
132	}
133}
134
135func isFlagPassed(name string) bool {
136	found := false
137
138	flag.Visit(func(f *flag.Flag) {
139		if f.Name == name {
140			found = true
141		}
142	})
143	return found
144}
145
146func main() {
147	codec := flag.Uint64("codec", 0, "Set the codec number when decoding a file\n"+
148		"This flag is only meaningful in combination with the 'file' flag")
149	config := flag.Uint64("config", 0, "Decode a single configuration")
150	file := flag.String("file", "", "Decode configurations in a file\n"+
151		"The decoder assumes each line in the file has the format: <pin> <config>")
152	generate := flag.Bool("generate", false, "Automatically generate hda_verb.c for the host device")
153	flag.Parse()
154
155	if isFlagPassed("config") {
156		decodeConfig(uint32(*config))
157	} else if isFlagPassed("file") {
158		decodeFile(*file, uint32(*codec))
159	} else {
160		if *generate {
161			fmt.Printf("/* SPDX-License-Identifier: GPL-2.0-only */\n\n")
162			fmt.Printf("#include <device/azalia_device.h>\n\n")
163			fmt.Printf("const u32 cim_verb_data[] = {\n")
164			indentLevel += 1
165		}
166		decodeDeviceCodecs(*generate)
167		if *generate {
168			indentLevel -= 1
169			fmt.Printf("};\n\n")
170			fmt.Printf("const u32 pc_beep_verbs[] = {};\n")
171			fmt.Printf("AZALIA_ARRAY_SIZES;\n")
172		}
173	}
174}
175