Nicholas Sudsgaard has uploaded this change for review.

View Change

util: Add hda-decoder

This tool helps take off the burden of manually decoding default
configuration registers. Using decoded values can make code more
self-documenting compared to shrouding it with magic numbers.

This is also written as a module which allows easy integration with
other tools written in Go (e.g. autoport).

Change-Id: Ib4fb652e178517b2b7aceaac8be005c5b2d3b03e
Signed-off-by: Nicholas Sudsgaard <devel+coreboot@nsudsgaard.com>
---
A util/hda-decoder/.gitignore
A util/hda-decoder/Makefile
A util/hda-decoder/decoder/lib.go
A util/hda-decoder/decoder/lib_test.go
A util/hda-decoder/go.mod
A util/hda-decoder/main.go
6 files changed, 374 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/70/80470/1
diff --git a/util/hda-decoder/.gitignore b/util/hda-decoder/.gitignore
new file mode 100644
index 0000000..1390027
--- /dev/null
+++ b/util/hda-decoder/.gitignore
@@ -0,0 +1 @@
+hda-decoder
diff --git a/util/hda-decoder/Makefile b/util/hda-decoder/Makefile
new file mode 100644
index 0000000..c67aaf8
--- /dev/null
+++ b/util/hda-decoder/Makefile
@@ -0,0 +1,10 @@
+## SPDX-License-Identifier: GPL-2.0-only
+
+PROJECT_NAME = hda-decoder
+
+default:
+ go version
+ go build -v -o $(PROJECT_NAME)
+
+clean:
+ rm -Rf $(PROJECT_NAME)
diff --git a/util/hda-decoder/decoder/lib.go b/util/hda-decoder/decoder/lib.go
new file mode 100644
index 0000000..0fe0d62
--- /dev/null
+++ b/util/hda-decoder/decoder/lib.go
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+package decoder
+
+import (
+ "fmt"
+ "math/bits"
+)
+
+type section uint
+type description map[uint8]string
+
+const (
+ ConnectivityType section = iota
+ GrossLocation
+ GeometricLocation
+ DefaultDeviceType
+ ConnectionType
+ Colors
+ Misc
+ DefaultAssociation
+ Sequence
+)
+
+const (
+ connectivityTypeMask uint32 = 0xC0000000
+ grossLocationMask uint32 = 0x30000000
+ geometricLocationMask uint32 = 0x0F000000
+ defaultDeviceTypeMask uint32 = 0x00F00000
+ connectionTypeMask uint32 = 0x000F0000
+ colorMask uint32 = 0x0000F000
+ miscMask uint32 = 0x00000F00
+ defaultAssociationMask uint32 = 0x000000F0
+ sequenceMask uint32 = 0x0000000F
+)
+
+func splitIntoSections(reg uint32) []uint8 {
+ masks := []uint32{
+ connectivityTypeMask,
+ grossLocationMask,
+ geometricLocationMask,
+ defaultDeviceTypeMask,
+ connectionTypeMask,
+ colorMask,
+ miscMask,
+ defaultAssociationMask,
+ sequenceMask,
+ }
+ var sections = []uint8{}
+
+ for _, mask := range masks {
+ data := (reg & mask) >> bits.TrailingZeros32(mask)
+ sections = append(sections, uint8(data))
+ }
+ return sections
+}
+
+func PinIsConnected(reg uint32) bool {
+ return splitIntoSections(reg)[ConnectivityType] != 1
+}
+
+var connectivityTypes = description{
+ 0x00: "JACK",
+ 0x01: "NC",
+ 0x02: "INTEGRATED",
+ 0x03: "JACK_AND_INTEGRATED",
+}
+
+var grossLocations = description{
+ 0x00: "EXTERNAL_PRIMARY_CHASSIS",
+ 0x01: "INTERNAL",
+ 0x02: "SEPARATE_CHASSIS",
+ 0x03: "LOCATION_OTHER",
+}
+
+var geometricLocations = description{
+ 0x00: "NA",
+ 0x01: "REAR",
+ 0x02: "FRONT",
+ 0x03: "LEFT",
+ 0x04: "RIGHT",
+ 0x05: "TOP",
+ 0x06: "BOTTOM",
+ 0x07: "SPECIAL7",
+ 0x08: "SPECIAL8",
+ 0x09: "SPECIAL9",
+}
+
+var defaultDeviceTypes = description{
+ 0x00: "LINE_OUT",
+ 0x01: "SPEAKER",
+ 0x02: "HP_OUT",
+ 0x03: "CD",
+ 0x04: "SPDIF_OUT",
+ 0x05: "DIGITAL_OTHER_OUT",
+ 0x06: "MODEM_LINE_SIDE",
+ 0x07: "MODEM_HANDSET_SIDE",
+ 0x08: "LINE_IN",
+ 0x09: "AUX",
+ 0x0a: "MIC_IN",
+ 0x0b: "TELEPHONY",
+ 0x0c: "SPDIF_IN",
+ 0x0d: "DIGITAL_OTHER_IN",
+ 0x0f: "DEVICE_OTHER",
+}
+
+var connectionTypes = description{
+ 0x00: "TYPE_UNKNOWN",
+ 0x01: "STEREO_MONO_1_8",
+ 0x02: "STEREO_MONO_1_4",
+ 0x03: "ATAPI",
+ 0x04: "RCA",
+ 0x05: "OPTICAL",
+ 0x06: "OTHER_DIGITAL",
+ 0x07: "OTHER_ANALOG",
+ 0x08: "MULTICHANNEL_ANALOG",
+ 0x09: "XLR",
+ 0x0a: "RJ_11",
+ 0x0b: "COMBINATION",
+ 0x0f: "TYPE_OTHER",
+}
+
+var colors = description{
+ 0x00: "COLOR_UNKNOWN",
+ 0x01: "BLACK",
+ 0x02: "GREY",
+ 0x03: "BLUE",
+ 0x04: "GREEN",
+ 0x05: "RED",
+ 0x06: "ORANGE",
+ 0x07: "YELLOW",
+ 0x08: "PURPLE",
+ 0x09: "PINK",
+ 0x0e: "WHITE",
+ 0x0f: "COLOR_OTHER",
+}
+
+var misc = description{
+ 0b0000: "JACK_PRESENCE_DETECT",
+ 0b0001: "NO_JACK_PRESENCE_DETECT",
+}
+
+var namedSections = []description{
+ connectivityTypes,
+ grossLocations,
+ geometricLocations,
+ defaultDeviceTypes,
+ connectionTypes,
+ colors,
+ misc,
+}
+
+func getMiscDescription(data uint8) string {
+ presenseConfig := data & 0b0001
+ reservedConfig := data & 0b1110
+ desc := ""
+
+ desc += misc[presenseConfig]
+ if bits.OnesCount8(reservedConfig) > 0 {
+ desc += fmt.Sprintf(" | 0x%x", reservedConfig)
+ }
+ return desc
+}
+
+func GetDescription(reg uint32, section section) string {
+ data := splitIntoSections(reg)[section]
+
+ switch section {
+ case Misc:
+ return getMiscDescription(data)
+ default:
+ name, exists := namedSections[section][data]
+ if exists {
+ return name
+ }
+ fallthrough
+ case DefaultAssociation:
+ fallthrough
+ case Sequence:
+ return fmt.Sprintf("0x%x", data)
+ }
+}
diff --git a/util/hda-decoder/decoder/lib_test.go b/util/hda-decoder/decoder/lib_test.go
new file mode 100644
index 0000000..2fed756
--- /dev/null
+++ b/util/hda-decoder/decoder/lib_test.go
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+package decoder
+
+import (
+ "reflect"
+ "testing"
+)
+
+type pinIsConnectedTest struct {
+ arg1 uint32
+ expected bool
+}
+
+var pinIsConnectedTests = []pinIsConnectedTest{
+ pinIsConnectedTest{0x20000000, true},
+ pinIsConnectedTest{0xC0000000, true},
+ pinIsConnectedTest{0x40000000, false},
+}
+
+func TestPinIsConnected(t *testing.T) {
+ for _, test := range pinIsConnectedTests {
+ output := PinIsConnected(test.arg1)
+ if output != test.expected {
+ t.Errorf("Expected %v, received %v", test.expected, output)
+ }
+ }
+}
+
+type splitIntoSectionsTest struct {
+ arg1 uint32
+ expected []uint8
+}
+
+var splitIntoSectionsTests = []splitIntoSectionsTest{
+ splitIntoSectionsTest{0xe23d1a0e, []uint8{0x3, 0x2, 0x2, 0x3, 0xd, 0x1, 0xa, 0x0, 0xe}},
+ splitIntoSectionsTest{0x66a8a2e4, []uint8{0x1, 0x2, 0x6, 0xa, 0x8, 0xa, 0x2, 0xe, 0x4}},
+ splitIntoSectionsTest{0x2e00a164, []uint8{0x0, 0x2, 0xe, 0x0, 0x0, 0xa, 0x1, 0x6, 0x4}},
+ splitIntoSectionsTest{0x3b83dfe9, []uint8{0x0, 0x3, 0xb, 0x8, 0x3, 0xd, 0xf, 0xe, 0x9}},
+ splitIntoSectionsTest{0x51708701, []uint8{0x1, 0x1, 0x1, 0x7, 0x0, 0x8, 0x7, 0x0, 0x1}},
+}
+
+func TestSplitIntoSections(t *testing.T) {
+ for _, test := range splitIntoSectionsTests {
+ output := splitIntoSections(test.arg1)
+ if !reflect.DeepEqual(output, test.expected) {
+ t.Errorf("Expected %v, received %v", test.expected, output)
+ }
+ }
+}
+
+type getDescriptionTest struct {
+ arg1 uint32
+ arg2 section
+ expected string
+}
+
+var getDescriptionTests = []getDescriptionTest{
+ getDescriptionTest{0xe23d1a0e, ConnectivityType, "JACK_AND_INTEGRATED"},
+ getDescriptionTest{0xe23d1a0e, GrossLocation, "SEPARATE_CHASSIS"},
+ getDescriptionTest{0xe23d1a0e, GeometricLocation, "FRONT"},
+ getDescriptionTest{0xe23d1a0e, DefaultDeviceType, "CD"},
+ getDescriptionTest{0xe23d1a0e, ConnectionType, "0xd"},
+ getDescriptionTest{0xe23d1a0e, Colors, "BLACK"},
+ getDescriptionTest{0xe23d1a0e, Misc, "JACK_PRESENCE_DETECT | 0xa"},
+ getDescriptionTest{0xe23d1a0e, DefaultAssociation, "0x0"},
+ getDescriptionTest{0xe23d1a0e, Sequence, "0xe"},
+
+ getDescriptionTest{0x51708701, ConnectivityType, "NC"},
+ getDescriptionTest{0x51708701, GrossLocation, "INTERNAL"},
+ getDescriptionTest{0x51708701, GeometricLocation, "REAR"},
+ getDescriptionTest{0x51708701, DefaultDeviceType, "MODEM_HANDSET_SIDE"},
+ getDescriptionTest{0x51708701, ConnectionType, "TYPE_UNKNOWN"},
+ getDescriptionTest{0x51708701, Colors, "PURPLE"},
+ getDescriptionTest{0x51708701, Misc, "NO_JACK_PRESENCE_DETECT | 0x6"},
+ getDescriptionTest{0x51708701, DefaultAssociation, "0x0"},
+ getDescriptionTest{0x51708701, Sequence, "0x1"},
+
+ getDescriptionTest{0x2e00a164, ConnectivityType, "JACK"},
+ getDescriptionTest{0x2e00a164, GrossLocation, "SEPARATE_CHASSIS"},
+ getDescriptionTest{0x2e00a164, GeometricLocation, "0xe"},
+ getDescriptionTest{0x2e00a164, DefaultDeviceType, "LINE_OUT"},
+ getDescriptionTest{0x2e00a164, ConnectionType, "TYPE_UNKNOWN"},
+ getDescriptionTest{0x2e00a164, Colors, "0xa"},
+ getDescriptionTest{0x2e00a164, Misc, "NO_JACK_PRESENCE_DETECT"},
+ getDescriptionTest{0x2e00a164, DefaultAssociation, "0x6"},
+ getDescriptionTest{0x2e00a164, Sequence, "0x4"},
+}
+
+func TestGetDescription(t *testing.T) {
+ for _, test := range getDescriptionTests {
+ output := GetDescription(test.arg1, test.arg2)
+ if output != test.expected {
+ t.Errorf("Expected %v, received %v", test.expected, output)
+ }
+ }
+}
diff --git a/util/hda-decoder/go.mod b/util/hda-decoder/go.mod
new file mode 100644
index 0000000..a3c611a
--- /dev/null
+++ b/util/hda-decoder/go.mod
@@ -0,0 +1,3 @@
+module review.coreboot.org/coreboot.git/util/hda-decoder
+
+go 1.18
diff --git a/util/hda-decoder/main.go b/util/hda-decoder/main.go
new file mode 100644
index 0000000..b7eca7b
--- /dev/null
+++ b/util/hda-decoder/main.go
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+package main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+
+ "review.coreboot.org/coreboot.git/util/hda-decoder/decoder"
+)
+
+func stringToHex32(str string) uint32 {
+ re := strings.NewReplacer("0x", "")
+ value, err := strconv.ParseUint(re.Replace(str), 16, 32)
+ if err != nil {
+ panic(err)
+ }
+ return uint32(value)
+}
+
+func printConnectedPinConfig(reg uint32) {
+ fmt.Println("AZALIA_PIN_DESC(")
+ for i := decoder.ConnectivityType; i <= decoder.Sequence; i++ {
+ fmt.Print("\t" + decoder.GetDescription(reg, i))
+ if i != decoder.Sequence {
+ fmt.Println(",")
+ }
+ }
+ fmt.Println("\n)),")
+}
+
+func printDisconnectedPinConfig(reg uint32) {
+ // The value 0x411111f0 is just a common values vendors use to indicate
+ // "not connected".
+ const nc uint32 = 0x411111f0;
+
+ // Setting some values (e.g. 0x40000000) as AZALIA_PIN_CFG_NC(0) is
+ // probably harmless. However, we will stay on the safe side for now.
+ if reg & 0xfffffff0 != nc {
+ // Keep it as the magic number as decoding this will probably
+ // just confuse the reader.
+ fmt.Printf("0x%08x), // does not describe a jack or internal device\n", reg);
+ return
+ }
+ fmt.Printf("AZALIA_PIN_CFG_NC(0x%x)),\n", reg & 0x0000000f)
+}
+
+func main() {
+ filePath := flag.String("file",
+ "/sys/class/sound/card0/hwC0D0/init_pin_configs",
+ "path to the pin config (format: <pin> <value>)")
+ codec := flag.Uint("codec",
+ 0,
+ "codec of the specified pin config file")
+ flag.Parse()
+
+ file, err := os.Open(*filePath)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ pin := stringToHex32(fields[0])
+ reg := stringToHex32(fields[1])
+
+ fmt.Printf("AZALIA_PIN_CFG(%d, 0x%02x, ", *codec, pin)
+ if decoder.PinIsConnected(reg) {
+ printConnectedPinConfig(reg)
+ } else {
+ printDisconnectedPinConfig(reg)
+ }
+ }
+}

To view, visit change 80470. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: main
Gerrit-Change-Id: Ib4fb652e178517b2b7aceaac8be005c5b2d3b03e
Gerrit-Change-Number: 80470
Gerrit-PatchSet: 1
Gerrit-Owner: Nicholas Sudsgaard <devel+coreboot@nsudsgaard.com>
Gerrit-MessageType: newchange