Message ID | 20240517-a2b-v1-0-b8647554c67b@bang-olufsen.dk |
---|---|
Headers | show |
Series | Analog Devices Inc. Automotive Audio Bus (A2B) support | expand |
… > A2B buses consist of a seires of daisy-chained A2B transceiver ICs known … > This is the main feature of A2B, whence the name Audio Bus. Each node … I suggest to avoid a few typos also in such a detailed cover letter. Regards, Markus
On Fri, May 17, 2024 at 04:57:57PM GMT, Wolfram Sang wrote: > > > On subordinate nodes the I2C interface functions in controller > > (master) mode, providing an additional I2C adapter to the host for > > each subordinate node connected to the A2B bus. > > I am not sure I got this right? That would mean for an I2C adapter on a > subordinate node, that there might be targets connected to only that > subordinate node? How do its messages go to the host machine? Is I2C > encapsulated over I2C? I probably missed something. > Yes, each subordinate node exposes a whole discrete I2C segment, so you can have two targets with the same address connected to two different subordinate nodes respectively. Hence a new i2c_adapter for each subordinate node. The way it works is that the main node responds on two I2C addresses, typically 0x68 and 0x69, labelled "BASE" and "BUS" respectively. BASE always addresses the register map of the main node. BUS is a "proxy" and addresses either the register map of a subordinate node, or a given peripheral connected to the targetted subordinate node. This is configured in some register fields of the main/subordinate nodes. When addressing a target connected to the I2C controller of a subordinate node, a register field of the given subordinate node must be populated with the target's I2C address. The hardware will then proxy I2C messages towards the main node's BUS address over the A2B bus through to the selected subordinate node's I2C controller interface, and will rewrite the address (BUS, 0x69) to whatever target address was programmed into that subordinate node. The I2C interface of the main node performs clock stretching so that the transfer is synchronous and transparent to the host. You can see the implementation details in the ad24xx-i2c.c "interface driver": (a) main/subordinate node register map access: __ad24xx_i2c_read() and __ad24xx_i2c_write() (b) subordinate node I2C target access: __ad24xx_i2c_xfer() A more detailed description of the logic can be found in [1] section 3-1 "I2C Port Programming Concepts": [1] https://www.analog.com/media/en/technical-documentation/user-guides/ad242x-trm.pdf Hope that clears it up.
This series adds support for Analog Devices' Automotive Audio Bus (A2B) transceivers in the AD242x, AD241x and AD240x series. The series is based on today's linux-next. INTRODUCTION ============ A2B buses consist of a seires of daisy-chained A2B transceiver ICs known as nodes. Every bus consists of a "main" node (attached to the host processor via I2C) and, typically, a series of "subordinate" nodes connected in a daisy-chained manner via UTP (unshielded twisted pair). Digital audio transport is delivered via a standard I2S/TDM interface between the host and main node. The UTP links may extend for several meters, exposing peripheral function to the host over a distance. ┌──────┐ I2C ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ ├─────┤ │ UTP │ │ │ │ │ │ │ host │ │ main │XXXXX│ sub1 │XXXXX│ sub2 │XX ... XX│ subN │ │ ├─────┤ │ │ │ │ │ │ │ └──────┘ TDM └──────┘ └──────┘ └──────┘ └──────┘ \______________________________ ... ______/ A2B bus All A2B nodes expose a number of peripheral functions, and the A2B protocol allows for them to be controlled directly by the host processor over I2C: ┌──────────────────────┐ ├─────┐ ┌─────┤ <--- ... \/\/\/│ TRX │ AD2428 │ TRX │\/\/\/ ... ---> /\/\/\│ A │ │ B │/\/\/\ towards ├─────┘ └─────┤ towards last main node │ ┌──────┐ ┌─────────┐ │ subordinate node │ │ I2C │ │ I2S/TDM │ │ │ │ │ │ PDM │ │ │ └──────┘ └─────────┘ │ │ ┌──────┐ ┌─────────┐ │ │ │ GPIO │ │ CLOCK │ │ │ └──────┘ └─────────┘ │ └──────────────────────┘ The peripheral functions are as follows: I2C interface ------------- On main nodes the I2C interface must by definition function in target (slave) mode, as it is attached to the host for management. On subordinate nodes the I2C interface functions in controller (master) mode, providing an additional I2C adapter to the host for each subordinate node connected to the A2B bus. The control registers of each node, both main and subordinate, can be accessed directly (resp. indirectly) via the I2C interface between the host and main node. I2S/TDM and PDM interface ------------------------- This is the main feature of A2B, whence the name Audio Bus. Each node has two TX and RX pins for I2S function. On the main node the interface functions as a bit- and frame-clock consumer. On the subordinate nodes it functions as a provider. PCM data is transported "upstream" (towards the main node) and "downstream" (towards the last subordinate node) via the A2B protocol in so-called A2B superframes. Through control registers of the respective nodes, a user can configure specific TDM slots to be placed into the superframes for transport up or down the A2B bus. Similarly, slots can be pulled from the superframes and retransmitted on a given node's I2S/TDM interface. In addition, the pins on a node can be reconfigured to run in PDM mode for applications such as microphone recording. The data is placed into the superframe for forwarding up or down the bus. GPIO ---- Unused pins of a node can be muxed to GPIO mode. The GPIO block supports interrupts as well, so each node connected to the bus can expose a gpiochip/irqchip to the host. CLOCK ----- Certain pins can also be muxed to become clock outputs. The clock outputs divide the internal PLL, which runs at 2048 times the frame sync clock frequency of the main node. For further details on A2B architecture, check the documentation from Analog Devices, which is freely available online; this documentation was used to write the driver: AD2420(W)/6(W)/7(W)/8(W)/9(W) Automotive Audio Bus A2B Transceiver Technical Reference, Revision 1.1, October 2019, Part Number 82-100138-01 DRIVER DESIGN ============= To control these chips, a bus driver core has been added under the drivers/a2b/ directory. Drivers for peripheral functionality are instead placed in their relevant subsystem directory. What follows is a brief description of the design. The A2B core introduces a bus for two device types, and a class type to represent the overall bus: * struct bus_type a2b_bus: A bus type for A2B buses. There are two device types: * struct device_type a2b_node: an A2B node, main or subordinate * struct device_type a2b_func: a peripheral function of a node, e.g. gpio or codec * struct class a2b_bus_class: A class type representing the overall A2B bus instance. There is one device type: * struct device_type a2b_bus: an A2B bus instance Interrupts can be raised by any node and are multiplexed via the IRQ line of the main node connected to the host processor. The interrupts are then dispatched to the relevant node, or, in the case of GPIO, to the a2b_func device representing the irqchip. At the top level there is the i2c driver, ad24xx-i2c, hereby called an A2B interface driver. It sets up the bare necessities required to interface with the nodes and to demultiplex interrupts. In principle there exist other interfaces for A2B - newer series chips, for example, support also SPI, but the hardware is not available for me to test on, so this series only implements the I2C interface. The role of the interface driver is to register a class device of type a2b_bus. The interface driver should implement relevant a2b_bus_ops, which provide the core with a way to talk to the nodes in an interface-agnostic fashion. When an a2b_bus device is registered, the core will traverse the OF node of the top-level i2c node and look for a child node named node@0. This node is used to create a new bus device of type a2b_node. It corresponds to the main node, and so it is always assumed to be present. The main node is probed by the node driver, ad24xx-node. This is a generic driver and supports both main and subordinate nodes, as the setup is very similar. The A2B core takes a specific interest in the initialization of a2b_node devices because it must administer the "discovery" process. Discovery can be thought of as enumeration, and involves programming specific control registers of the main node and waiting for a specific interrupt to determine whether there is another subordinate node attached. According to the design of A2B, the discovery process must happen in a serial manner, which is to say that it is not possible to discover subordinate node N until subordinate node N-1 has been discovered. Moreover, the main node must also be set up before any discovery can take place. To address this, the node driver must configure certain a2b_node_ops before registering its a2b_node with the core via a2b_register_node(). Most of the node setup then takes place in the a2b_node_ops::setup() function, which is called synchronously by the core. When a node has been successfully set up, the core will then attempt (another) discovery. This algorithm will proceed until there are no further nodes to discover. When a node is set up by the node driver, it can then optionally add peripheral function a2b_func devices. These devices are probed by their respective driver, be it for the GPIO subsystem, sound subsystem, etc. As was alluded to earlier, the driver relies on OF/device tree for the hardware description. Here is a simplified example: i2c { a2b@68 { interface compatible = "adi,ad2428"; reg = <0x68>; #interrupt-cells = <1>; interrupt-controller; node@0 { # main node compatible = "adi,ad2428-node"; reg = <0> interrupts = <0>; clock { # main node clock compatible = "adi,ad2428-clk"; #clock-cells = <1>; clock-output-names = "clkout1", "clkout2"; }; codec { # main node audio codec compatible = "adi,ad2428-codec"; }; }; node@1 { # subordinate node 1 compatible = "adi,ad2428-node"; reg = <1>; interrupts = <1>; gpio { # subordinate node 1 GPIO compatible = "adi,ad2428-gpio"; #interrupt-cells = <2>; interrupt-controller; #gpio-cells = <2>; gpio-controller; }; codec { # subordinate node 1 audio codec compatible = "adi,ad2428-codec"; }; }; node@2 { # subordinate node 2 ... compatible = "adi,ad2428-node"; reg = <2>; interrupts = <2>; codec { # subordinate node 2 audio codec compatible = "adi,ad2428-codec"; }; }; ... more nodes can follow here }; }; It is not a requirement that all described nodes are actually connected to the bus. While the device tree structure is flat, the device topology is nested. That means a parent-child device topology as follows: 1-0068 └─ a2b-0 └─ a2b-0.0 ├─ a2b-0.1 │ ├─ a2b-0.2 │ │ └─ a2b-0.2-codec │ ├─ a2b-0.1-gpio │ └─ a2b-0.1-codec ├─ a2b-0.0-clk └─ a2b-0.0-codec Where: * 1-0068 is the top-level I2C device * a2b-0 is the a2b_bus class device * a2b-0.0 is the main node a2b_node device * a2b-0.N is the Nth subordinate node a2b_node device * a2b-0.N-codec is the Nth node's audio codec a2b_func device * ditto for a2b-0.N-{clk,gpio} etc. PATCH ORDER =========== The series is split up into per-subsystem pieces to make review easier for relevant maintainers. Broadly speaking the series goes as follows: 1. Add the driver core 2. Add device tree bindings for AD24xx chips 3. Add device drivers for AD24xx chips a) I2C interface driver b) node driver c) per-subsystem functional block drivers 4. Add an example "custom" A2B node driver for a Bang & Olufsen speaker (Beosound Shape) 5. Add relevant entries to the MAINTAINERS file HELP WANTED =========== I am aware that the current implementation may not be entirely correct from a driver model perspective, and there are also some shortcomings which are already evident. Having said that, I am in need of people to bounce ideas off, so I figure it is OK to submit the code in its current form. Below I want to identify a few issues and get some feedback. How will this get merged? ------------------------- I am a bit clueless as to the review and merge process for a patch series that affects multiple subsystems. If somebody can point me to an explanation (perhaps already stated on the mailing list?), that would be great. I made some assumptions on how to structure this series but I am happy to amend if things are not correct. fw_devlink=on and device registration during discovery ------------------------------------------------------ In the current architecture, device_register() is called for each A2B node if and only if the node has been discovered. Since the driver is using OF, this seems to violate some assumptions of fw_devlink. Let me quote a comment from device_links_driver_bound(): | If a device binds successfully, it's expected to have created all | the device links it needs to or make new device links as it needs | them. So, fw_devlink no longer needs to create device links to any | of the device's suppliers. When using the audio-graph-card2 ASoC machine driver, I have experienced fw_devlink=on preventing both the subordinate nodes' codecs and the machine driver from being probed, due to a cyclic dependency. I have been able to work around this by using Saravana's new post-init-providers device tree property, whereby I reference by phandle the corresponding OF graph endpoint of the machine driver in each codec's device tree node. But this seems like a bit of a hack. And I suspect that the logic (and benefits) of fw_devlink=on will be broken in other places by this behaviour as well. Looking at other subsystems, I see that i2c will register a device for each child node, and allow the matching driver's probe function to return -ENODEV/-ENXIO if the device doesn't respond. In that case, the devices are still registered but remain unbound. A similar strategy could be done for A2B as well I think. First it is important to note that discovery must happen sequentially, i.e. node N cannot be discovered until node N-1 has been discovered and its driver bound. This is because node N-1's driver is needed to access some of its registers (to enable B-side transceiver switching, etc.). Node discovery could instead be wired into the a2b_bus_probe() function, such that if called for a struct device corresponding to node N, and if nodes 0..N-1 have already been probed, then the code currently in a2b_bus_discovery_work() can be performed to try and discover node N. If discovered, the a2b_drv->probe() call can then be performed. If not discovered, a2b_bus_probe() can return -ENODEV and we can conclude that the initial discovery algorithm has completed. Here I need some help though. As already stated, there is a strict ordering dependency, so I need to ensure that nodes 0..N-1 have actually been probed. If I am already calling device_register() on all nodes with an OF node to begin with, then I need to establish some managed device links to work with this properly. Correct? If that works fine, then there is at least one remaining problem. Namely, we at B&O are already using this driver in a product which has an OF description for up to 10 A2B nodes, but where the customer may not necessarily have all nodes connected. This is the Beosound Shape setup described in the last patches of this series. To handle this, I use a modified audio-graph-card2 driver which defers its probe until the A2B bus has finished its discovery. But in the case where some are not connected, I will get -ENODEV. What I want to understand here is, how will fw_devlink respond? To understand, here is a simplified representation of the device tree: sound { compatible = "adi,a2b-audio-graph-card2"; ... ports { port@0 { snd_0_ep: endpoint { remote-endpoint = <&a2b0_codec_ep>; }; }; port@1 { snd_1_ep: endpoint { remote-endpoint = <&a2b1_codec_ep>; }; }; ... port@10 { snd_10_ep: endpoint { remote-endpoint = <&a2b10_codec_ep>; }; }; }; }; a2b@68 { compatible = "adi,ad2428"; ... node@0 { codec { port { a2b0_codec_ep: endpoint { remote-endpoint = <&snd_0_ep>; }; }; }; }; node@1 { codec { port { a2b1_codec_ep: endpoint { remote-endpoint = <&snd_1_ep>; }; }; }; }; ... node@10 { codec { port { a2b1_codec_ep: endpoint { remote-endpoint = <&snd_10_ep>; }; }; }; }; }; The question I have is, how can I ensure that the machine driver will probe (sound node) when discovery has completed, even if not all codecs are available because not all A2B nodes are connected? I understand this might be pushing the limits of device tree somewhat, but ASoC is quite device tree heavy and I would like to get some advice on what the best way to proceed is. And if going for the proposed amendment above (in the style of i2c, with discovery integrated into a2b_bus_probe()), will post-init-providers still save me in the above situation? Perhaps there is a simpler approach here if the assumption I quoted at the top of this section is not so crucial. In that case, maybe the current implementation is OK? Device dependencies and discovery --------------------------------- Yet another complication that we have recently come upon is what to do when the discovery of a node N requires probing devices which topologically are beneath the I2C controller of node N. Concretely, we have two boards 1 and 2 where the B-side of board 1's A2B node is routed through a mux. The mux is controlled via GPIO, and those GPIOs are exported by an I2C GPIO expander connected to board 1's A2B's I2C controller. Suppose board 1's A2B node is called sub1 and board 2's is called sub2. Then the situation looks like this: X ┌──────┐ X ┌──────┐ │ │ ┌─────┐ │ │ ... XXX│ sub1 │XXXXX│ mux │XXXXX│ sub2 │ │ │ └──▲──┘ │ │ └──────┘ │ └──────┘ │ │ │ I2C │ │ │ ┌──────┐ │ │ GPIO │────────┘ └──────┘ The mux itself can be modelled as a simple GPIO mux and hence a platform driver. But in order to discover node sub2, it must get probed and configured appropriately; the default hardware strapping would cause discovery to fail. This is not a crucial issue right now and is something we can work around, and I don't think it's necessarily a blocker for getting this series merged upstream. But feedback on how to approach this might inform some useful changes to this series. The issue here is that some driver needs to acquire the mux and set it. The discovery process must be delayed as long as this returns -EPROBE_DEFER. Would it be appropriate for the A2B core to acquire this mux based on device tree properties of sub1? Thank you in advance for all review comments and feedback. Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk> --- Alvin Šipraga (13): a2b: add A2B driver core regmap: add A2B support dt-bindings: a2b: Analog Devices AD24xx devices a2b: add AD24xx I2C interface driver a2b: add AD24xx node driver gpio: add AD24xx GPIO driver ASoC: codecs: add AD24xx codec driver clk: add AD24xx clock driver i2c: add AD24xx I2C controller driver dt-bindings: vendor-prefixes: add Bang & Olufsen a/s dt-bindings: a2b: add compatible string for Beosound Shape node a2b: add Beosound Shape node driver MAINTAINERS: add maintainership for A2B drivers .../devicetree/bindings/a2b/adi,ad24xx-clk.yaml | 53 + .../devicetree/bindings/a2b/adi,ad24xx-codec.yaml | 52 + .../devicetree/bindings/a2b/adi,ad24xx-gpio.yaml | 76 ++ .../devicetree/bindings/a2b/adi,ad24xx-i2c.yaml | 55 + .../devicetree/bindings/a2b/adi,ad24xx.yaml | 254 ++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + MAINTAINERS | 12 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/a2b/Kconfig | 60 + drivers/a2b/Makefile | 13 + drivers/a2b/a2b.c | 1252 ++++++++++++++++++++ drivers/a2b/ad24xx-i2c.c | 532 +++++++++ drivers/a2b/ad24xx-node.c | 887 ++++++++++++++ drivers/a2b/ad24xx-node.h | 42 + drivers/a2b/beo-shape-node.c | 584 +++++++++ drivers/base/regmap/Kconfig | 6 +- drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-a2b.c | 82 ++ drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-ad24xx.c | 341 ++++++ drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ad24xx.c | 302 +++++ drivers/i2c/busses/Kconfig | 7 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ad24xx.c | 121 ++ include/linux/a2b/a2b.h | 444 +++++++ include/linux/a2b/ad24xx.h | 892 ++++++++++++++ include/linux/regmap.h | 38 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ad24xx-codec.c | 665 +++++++++++ 34 files changed, 6798 insertions(+), 1 deletion(-) --- base-commit: c75962170e49f24399141276ae119e6a879f36dc change-id: 20231212-a2b-e54ba46a9e05