summaryrefslogtreecommitdiff
path: root/src/drivers/soundwire/cs42l42/cs42l42.c
diff options
context:
space:
mode:
authorKapil Porwal <kapilporwal@google.com>2023-02-25 22:23:01 +0530
committerFelix Held <felix-coreboot@felixheld.de>2023-05-25 21:22:32 +0000
commit93b7fd1d4d45646e1338abf90b69c3e7c352e930 (patch)
treeadd695a4e2c73fc77b54e623cf666d52b0e9ce6a /src/drivers/soundwire/cs42l42/cs42l42.c
parent1fe5fcf30dc968ac782e9710257ec1bcddcef380 (diff)
drivers/soundwire/cs42l42: Support CS42L42 SoundWire device
The CS42L42 low power audio codec can be connected over SoundWire and be configured for mainboards to use: - Data Port 0 and Bulk Register Access - Data Port 1 is the 64bit data output for the headset - Data Port 2 is the 64bit data input for the headset - Data Port 3 is the 64bit data input for the headset The data port and audio mode properties are filled out as best as possible with the datasheet as a reference. The ACPI address for the codec is calculated with the information in the codec driver combined with the devicetree.cb hierarchy where the link and unique IDs are extracted from the device path. For example this device is connected to master link ID 0 and has strap settings configuring it for unique ID 0. chip drivers/soundwire/cs42l42 register "desc" = ""Headset Codec"" device generic 0.0 on end end This driver was tested with the rex0 reference design by booting and disassembling the runtime SSDT to ensure that the devices have the expected address and properties. Device (SW00) { Name (_ADR, 0x00001001FA424200) // _ADR: Address Name (_DDN, "Headset Codec") // _DDN: DOS Device Name Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { GpioInt (Edge, ActiveBoth, Exclusive, PullDefault, 0x0000, "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, , ) { // Pin list 0x0166 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, , ) { // Pin list 0x0167 } }) Name (_DSD, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "irq-gpios", Package () { \_SB.PCI0.HDAS.SNDW.SW00, Zero, Zero, Zero } }, Package () { "reset-gpios", Package () { \_SB.PCI0.HDAS.SNDW.SW00, One, Zero, Zero } }, Package () { "cirrus,ts-inv", One }, Package () { "cirrus,ts-dbnc-rise", 0x05 }, Package () { "cirrus,ts-dbnc-fall", Zero }, Package () { "cirrus,btn-det-init-dbnce", 0x64 }, Package () { "cirrus,btn-det-event-dbnce", 0x0A }, Package () { "cirrus,bias-lvls", Package () { 0x0F, 0x08, 0x04, One } }, Package () { "cirrus,hs-bias-ramp-rate", 0x02 }, Package () { "cirrus,hs-bias-sense-disable", One }, Package () { "mipi-sdw-sw-interface-revision", 0x00010000 }, [...] Package () { "mipi-sdw-source-port-list", 0x02 }, Package () { "mipi-sdw-sink-port-list", 0x0C } }, ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { Package () { "mipi-sdw-port-bra-mode-0", "BRA0" }, Package () { "mipi-sdw-dp-0-subproperties", "DP0" }, Package () { "mipi-sdw-port-audio-mode-0", "MOD0" }, Package () { "mipi-sdw-dp-1-source-subproperties", "SRC1" }, Package () { "mipi-sdw-dp-2-sink-subproperties", "SNK2" }, Package () { "mipi-sdw-dp-3-sink-subproperties", "SNK3"} } }) Name (BRA0, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-bra-mode-bus-frequency-configs", Package () { 0x00AC4400, ... } }, Package () { "mipi-sdw-bra-mode-max-data-per-frame", 0x1000 }, Package () { "mipi-sdw-bra-mode-min-us-between-transactions", Zero } } }) Name (DP0, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-port-max-wordlength", 0x40 }, [...] }, ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { Package () { "mipi-sdw-port-bra-mode-0", "BRA0" } } }) Name (MOD0, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-audio-mode-bus-frequency-configs", Package () { 0x00AC4400, ... } }, Package () { "mipi-sdw-audio-mode-max-sampling-frequency", 0x0002EE00 }, Package () { "mipi-sdw-audio-mode-min-sampling-frequency", 0x1F40 }, [...] } }) Name (SRC1, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-data-port-type", Zero }, [...] }, ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { Package () { "mipi-sdw-port-audio-mode-0", "MOD0" } } }) Name (SNK2, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-data-port-type", Zero }, [...] }, ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { Package () { "mipi-sdw-port-audio-mode-0", "MOD0" } } }) Name (SNK3, Package () { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "mipi-sdw-data-port-type", Zero }, [...] }, ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { Package () { "mipi-sdw-port-audio-mode-0", "MOD0" } } }) } BUG=b:269497731 TEST=Verified SSDT for SNDW in the OS Signed-off-by: Kapil Porwal <kapilporwal@google.com> Change-Id: Ic7cfe2a21c76ba01ad3dea2a5017b28743aeb9f1 Reviewed-on: https://review.coreboot.org/c/coreboot/+/73279 Reviewed-by: Subrata Banik <subratabanik@google.com> Reviewed-by: Sridhar Siricilla <sridhar.siricilla@intel.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/drivers/soundwire/cs42l42/cs42l42.c')
-rw-r--r--src/drivers/soundwire/cs42l42/cs42l42.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/drivers/soundwire/cs42l42/cs42l42.c b/src/drivers/soundwire/cs42l42/cs42l42.c
new file mode 100644
index 0000000000..111844c171
--- /dev/null
+++ b/src/drivers/soundwire/cs42l42/cs42l42.c
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_soundwire.h>
+#include <device/device.h>
+#include <device/path.h>
+#include <device/soundwire.h>
+#include <mipi/ids.h>
+#include <stdio.h>
+
+#include "chip.h"
+
+static struct soundwire_address cs42l42_address = {
+ .version = SOUNDWIRE_VERSION_1_0,
+ .manufacturer_id = MIPI_MFG_ID_CIRRUS,
+ .part_id = MIPI_DEV_ID_CIRRUS_CS42L42,
+ .class = MIPI_CLASS_NONE
+};
+
+static struct soundwire_slave cs42l42_slave = {
+ .wake_up_unavailable = false,
+ .test_mode_supported = false,
+ .clock_stop_mode1_supported = true,
+ .simplified_clockstopprepare_sm_supported = false,
+ .clockstopprepare_hard_reset_behavior = true,
+ .highPHY_capable = false,
+ .paging_supported = true,
+ .bank_delay_supported = true,
+ .port15_read_behavior = false,
+ .source_port_list = SOUNDWIRE_PORT(1),
+ .sink_port_list = SOUNDWIRE_PORT(2) | SOUNDWIRE_PORT(3)
+};
+
+static struct soundwire_bra_mode cs42l42_dp0_bra_mode = {
+ .bus_frequency_configs_count = 6,
+ .bus_frequency_configs = {
+ 11289600, /* 11.2896 MHz */
+ 12000 * KHz, /* 12 MHz */
+ 12288 * KHz, /* 12.288 MHz */
+ 22579200, /* 22.5792 MHz */
+ 24000 * KHz, /* 24 MHz */
+ 24576 * KHz, /* 24.576 MHz */
+ },
+ .max_data_per_frame = 4096, /* MaxRow*MaxCol = 256*16 = 4096 */
+ .min_us_between_transactions = 0
+};
+
+static struct soundwire_dp0 cs42l42_dp0 = {
+ .port_max_wordlength = 64,
+ .port_min_wordlength = 1,
+ .bra_imp_def_response_supported = false,
+ .simplified_channel_prepare_sm = true,
+ .imp_def_dp0_interrupts_supported = 0,
+ .imp_def_bpt_supported = true,
+ .bra_mode_count = 1,
+ .bra_mode_list = { 0 }
+};
+
+static struct soundwire_audio_mode cs42l42_audio_mode = {
+ .bus_frequency_configs_count = 6,
+ .bus_frequency_configs = {
+ 11289600, /* 11.2896 MHz */
+ 12000 * KHz, /* 12 MHz */
+ 12288 * KHz, /* 12.288 MHz */
+ 22579200, /* 22.5792 MHz */
+ 24000 * KHz, /* 24 MHz */
+ 24576 * KHz, /* 24.576 MHz */
+ },
+ /* Support 8 KHz to 192 KHz sampling frequency */
+ .max_sampling_frequency = 192 * KHz,
+ .min_sampling_frequency = 8 * KHz,
+ .prepare_channel_behavior = CHANNEL_PREPARE_ANY_FREQUENCY
+};
+
+static struct soundwire_dpn cs42l42_dp1 = {
+ .port_max_wordlength = 64,
+ .port_min_wordlength = 1,
+ .data_port_type = FULL_DATA_PORT,
+ .max_grouping_supported = BLOCK_GROUP_COUNT_1,
+ .simplified_channelprepare_sm = false,
+ .imp_def_dpn_interrupts_supported = 0,
+ .min_channel_number = 1,
+ .max_channel_number = 1,
+ .modes_supported = MODE_ISOCHRONOUS | MODE_TX_CONTROLLED |
+ MODE_RX_CONTROLLED | MODE_FULL_ASYNCHRONOUS,
+ .block_packing_mode = true,
+ .port_audio_mode_count = 1,
+ .port_audio_mode_list = { 0 }
+};
+
+static struct soundwire_dpn cs42l42_dpn = {
+ .port_max_wordlength = 64,
+ .port_min_wordlength = 1,
+ .data_port_type = FULL_DATA_PORT,
+ .max_grouping_supported = BLOCK_GROUP_COUNT_1,
+ .simplified_channelprepare_sm = false,
+ .imp_def_dpn_interrupts_supported = 0,
+ .min_channel_number = 1,
+ .max_channel_number = 2,
+ .modes_supported = MODE_ISOCHRONOUS | MODE_TX_CONTROLLED |
+ MODE_RX_CONTROLLED | MODE_FULL_ASYNCHRONOUS,
+ .block_packing_mode = true,
+ .port_audio_mode_count = 1,
+ .port_audio_mode_list = { 0 }
+};
+
+static const struct soundwire_codec cs42l42_codec = {
+ .slave = &cs42l42_slave,
+ .dp0_bra_mode = { &cs42l42_dp0_bra_mode },
+ .dp0 = &cs42l42_dp0,
+ .audio_mode = { &cs42l42_audio_mode },
+ .dpn = {
+ {
+ .port = 1,
+ .source = &cs42l42_dp1,
+ },
+ {
+ .port = 2,
+ .sink = &cs42l42_dpn,
+ },
+ {
+ .port = 3,
+ .sink = &cs42l42_dpn,
+ }
+ }
+};
+
+static void soundwire_cs42l42_fill_ssdt(const struct device *dev)
+{
+ struct drivers_soundwire_cs42l42_config *config = dev->chip_info;
+ const char *scope = acpi_device_scope(dev);
+ const char *path = acpi_device_path(dev);
+ struct acpi_dp *dsd;
+ int gpio_index = 0;
+
+ if (!scope)
+ return;
+
+ acpigen_write_scope(scope);
+ acpigen_write_device(acpi_device_name(dev));
+
+ /* Set codec address IDs. */
+ cs42l42_address.link_id = dev->path.generic.id;
+ cs42l42_address.unique_id = dev->path.generic.subid;
+
+ acpigen_write_ADR_soundwire_device(&cs42l42_address);
+ acpigen_write_name_string("_DDN", config->desc ? : dev->chip_ops->name);
+ acpigen_write_STA(acpi_device_status(dev));
+
+ /* Resources */
+ acpigen_write_name("_CRS");
+ acpigen_write_resourcetemplate_header();
+
+ /* Use either Interrupt() or GpioInt() */
+ if (config->irq_gpio.pin_count)
+ acpi_device_write_gpio(&config->irq_gpio);
+ else
+ acpi_device_write_interrupt(&config->irq);
+
+ /* for cs42l42 reset gpio */
+ if (config->reset_gpio.pin_count)
+ acpi_device_write_gpio(&config->reset_gpio);
+
+ acpigen_write_resourcetemplate_footer();
+
+ dsd = acpi_dp_new_table("_DSD");
+
+ if (config->irq_gpio.pin_count)
+ acpi_dp_add_gpio(dsd, "irq-gpios", path,
+ gpio_index++, /* Index = 0 */
+ 0, /* Pin = 0 (There is a single pin in the GPIO resource). */
+ config->irq_gpio.active_low);
+ if (config->reset_gpio.pin_count)
+ acpi_dp_add_gpio(dsd, "reset-gpios", path,
+ gpio_index++, /* Index = 0 or 1 (if irq gpio is written). */
+ 0, /* Pin = 0 (There is a single pin in the GPIO resource). */
+ config->reset_gpio.active_low);
+ acpi_dp_add_integer(dsd, "cirrus,ts-inv", config->ts_inv ? 1 : 0);
+ acpi_dp_add_integer(dsd, "cirrus,ts-dbnc-rise", config->ts_dbnc_rise);
+ acpi_dp_add_integer(dsd, "cirrus,ts-dbnc-fall", config->ts_dbnc_fall);
+ acpi_dp_add_integer(dsd, "cirrus,btn-det-init-dbnce", config->btn_det_init_dbnce);
+ if (config->btn_det_init_dbnce > 200) {
+ printk(BIOS_ERR, "%s: Incorrect btn_det_init_dbnce(%d). Using default of 100ms\n",
+ __func__, config->btn_det_init_dbnce);
+ config->btn_det_init_dbnce = 100;
+ }
+ acpi_dp_add_integer(dsd, "cirrus,btn-det-event-dbnce", config->btn_det_event_dbnce);
+ if (config->btn_det_event_dbnce > 100) {
+ printk(BIOS_ERR, "%s: Incorrect btn_det_event_dbnce(%d). Using default of 10ms\n",
+ __func__, config->btn_det_event_dbnce);
+ config->btn_det_event_dbnce = 10;
+ }
+ acpi_dp_add_integer_array(dsd, "cirrus,bias-lvls", config->bias_lvls, 4);
+ acpi_dp_add_integer(dsd, "cirrus,hs-bias-ramp-rate", config->hs_bias_ramp_rate);
+ if (config->hs_bias_sense_disable)
+ acpi_dp_add_integer(dsd, "cirrus,hs-bias-sense-disable", 1);
+
+ soundwire_gen_codec(dsd, &cs42l42_codec, NULL);
+ acpi_dp_write(dsd);
+
+ acpigen_pop_len(); /* Device */
+ acpigen_pop_len(); /* Scope */
+}
+
+static const char *soundwire_cs42l42_acpi_name(const struct device *dev)
+{
+ struct drivers_soundwire_cs42l42_config *config = dev->chip_info;
+ if (config->acpi_name[0] != 0)
+ return config->acpi_name;
+ snprintf(config->acpi_name, sizeof(config->acpi_name), "SW%1X%1X",
+ dev->path.generic.id, dev->path.generic.subid);
+ return config->acpi_name;
+}
+
+static struct device_operations soundwire_cs42l42_ops = {
+ .read_resources = noop_read_resources,
+ .set_resources = noop_set_resources,
+ .acpi_name = soundwire_cs42l42_acpi_name,
+ .acpi_fill_ssdt = soundwire_cs42l42_fill_ssdt,
+};
+
+static void soundwire_cs42l42_enable(struct device *dev)
+{
+ dev->ops = &soundwire_cs42l42_ops;
+}
+
+struct chip_operations drivers_soundwire_cs42l42_ops = {
+ CHIP_NAME("Cirrus Logic CS42L42 SoundWire Codec")
+ .enable_dev = soundwire_cs42l42_enable
+};