Nicholas Sudsgaard has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/80470?usp=email )
Change subject: util: Add hda-decoder ......................................................................
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) + } + } +}