diff --git a/src/drivers/i2c/cs35l53/Kconfig b/src/drivers/i2c/cs35l53/Kconfig new file mode 100644 index 00000000000..38d6617a39f --- /dev/null +++ b/src/drivers/i2c/cs35l53/Kconfig @@ -0,0 +1,3 @@ +config DRIVERS_I2C_CS35L53 + bool + depends on HAVE_ACPI_TABLES diff --git a/src/drivers/i2c/cs35l53/Makefile.inc b/src/drivers/i2c/cs35l53/Makefile.inc new file mode 100644 index 00000000000..21a749f1997 --- /dev/null +++ b/src/drivers/i2c/cs35l53/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_DRIVERS_I2C_CS35L53) += cs35l53.c diff --git a/src/drivers/i2c/cs35l53/chip.h b/src/drivers/i2c/cs35l53/chip.h new file mode 100644 index 00000000000..9aeb8740115 --- /dev/null +++ b/src/drivers/i2c/cs35l53/chip.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#define CS35L53_MAX_GPIOS 2 + +enum cs35l53_boost_type { + INTERNAL_BOOST = 0, + EXTERNAL_BOOST = 1, +}; + +enum cs35l53_boost_ind_nanohenrys { + BOOST_IND_1000_NH = 1000, + BOOST_IND_1200_NH = 1200, + BOOST_IND_1500_NH = 1500, + BOOST_IND_2200_NH = 2200, +}; + +enum cs35l53_asp_sdout_hiz { + ASP_SDOUT_LOGIC0_UNUSED_LOGIC0_DISABLED = 0, + ASP_SDOUT_HIZ_UNUSED_LOGIC0_DISABLED = 1, + ASP_SDOUT_LOGIC0_UNUSED_HIZ_DISABLED = 2, + ASP_SDOUT_HIZ_UNUSED_HIZ_DISABLED = 3, +}; + +enum cs35l53_gpio1_src { + GPIO1_SRC_HIGH_IMPEDANCE = 0, + GPIO1_SRC_GPIO = 1, + GPIO1_SRC_SYNC = 2, + GPIO1_SRC_MCLK_INPUT = 3, +}; + +enum cs35l53_gpio2_src { + GPIO2_SRC_HIGH_IMPEDANCE = 0, + GPIO2_SRC_GPIO = 1, + GPIO2_SRC_OPEN_DRAIN = 2, + GPIO2_SRC_MCLK_INPUT = 3, + GPIO2_SRC_PUSH_PULL_INTB = 4, + GPIO2_SRC_PUSH_PULL_INT = 5, +}; + +/* + * Cirrus Logic CS35L53 Audio Codec devicetree bindings + * linux/Documentation/devicetree/bindings/sound/cirrus,cs35l53.yaml + */ +struct drivers_i2c_cs35l53_config { + const char *name; /* ACPI Device Name */ + + const char *sub; /* SUB ID to uniquely identify system */ + + /* Interrupt configuration */ + struct acpi_irq irq; + + /* Use GPIO based interrupt instead of PIRQ */ + struct acpi_gpio irq_gpio; + + /* Use GPIO based reset gpio */ + struct acpi_gpio reset_gpio; + + /* I2C Bus Frequency in Hertz (default 400kHz) */ + unsigned int bus_speed; + + /* Define cs35l53 parameters */ + /* + * cirrus,boost-type : Configures the type of Boost being used. + * Internal boost requires boost-peak-milliamp, boost-ind-nanohenry and + * boost-cap-microfarad. + * External Boost must have GPIO1 as GPIO output. GPIO1 will be set high to + * enable boost voltage. + */ + enum cs35l53_boost_type boost_type; + + /* + * cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. + * Configures the peak current by monitoring the current through the boost FET. + * Range starts at 1600 mA and goes to a maximum of 4500 mA with increments + * of 50 mA. See section 4.3.6 of the datasheet for details. + */ + unsigned int boost_peak_milliamp; + + /* + * cirrus,boost-ind-nanohenry : Boost inductor value, expressed in nH. Valid + * values include 1000, 1200, 1500 and 2200. + */ + enum cs35l53_boost_ind_nanohenrys boost_ind_nanohenry; + + /* + * cirrus,boost-cap-microfarad : Total equivalent boost capacitance on the VBST + * and VAMP pins, derated at 11 volts DC. The value must be rounded to the + * nearest integer and expressed in uF. + */ + unsigned int boost_cap_microfarad; + + /* + * cirrus,asp-sdout-hiz : Audio serial port SDOUT Hi-Z control. Sets the Hi-Z + * configuration for SDOUT pin of amplifier. + * 0 = Logic 0 during unused slots, and while all transmit channels disabled + * 1 = Hi-Z during unused slots but logic 0 while all transmit channels disabled + * 2 = Logic 0 during unused slots, but Hi-Z while all transmit channels disabled + * 3 = Hi-Z during unused slots and while all transmit channels disabled + */ + enum cs35l53_asp_sdout_hiz asp_sdout_hiz; + + /* + * cirrus,gpio1-polarity-invert : Boolean which specifies whether the GPIO1 + * level is inverted. + */ + bool gpio1_polarity_invert; + + /* + * cirrus,gpio2-polarity-invert : Boolean which specifies whether the GPIO2 + * level is inverted. + */ + bool gpio2_polarity_invert; + + /* + * cirrus,gpio1-output-enable : Boolean which specifies whether the GPIO1 pin + * is configured as an output. + */ + bool gpio1_output_enable; + + /* + * cirrus,gpio2-output-enable : Boolean which specifies whether the GPIO2 pin + * is configured as an output. + */ + bool gpio2_output_enable; + + /* + * cirrus,gpio1-src-select : Configures the function of the GPIO1 pin. + * GPIO1: + * 0 = High Impedance (Default) + * 1 = GPIO + * 2 = Sync + * 3 = MCLK input + */ + enum cs35l53_gpio1_src gpio1_src_select; + + /* + * cirrus,gpio2-src-select : Configures the function of the GPIO2 pin. + * GPIO2: + * 0 = High Impedance (Default) + * 1 = GPIO + * 2 = Open Drain INTB + * 3 = MCLK input + * 4 = Push-pull INTB (active low) + * 5 = Push-pull INT (active high) + */ + enum cs35l53_gpio2_src gpio2_src_select; +}; diff --git a/src/drivers/i2c/cs35l53/cs35l53.c b/src/drivers/i2c/cs35l53/cs35l53.c new file mode 100644 index 00000000000..1909bef7b57 --- /dev/null +++ b/src/drivers/i2c/cs35l53/cs35l53.c @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +#include "chip.h" + +#define CS35L53_ACPI_HID "CSC3541" + +static void cs35l53_fill_ssdt(const struct device *dev) +{ + struct drivers_i2c_cs35l53_config *config = dev->chip_info; + const char *scope = acpi_device_scope(dev); + const char *path = acpi_device_path(dev); + struct acpi_i2c i2c = { + .address = dev->path.i2c.device, + .mode_10bit = dev->path.i2c.mode_10bit, + .speed = config->bus_speed ? : I2C_SPEED_FAST, + .resource = scope, + }; + struct acpi_dp *dsd; + int gpio_index = 0; + + if (!scope) + return; + + /* Device */ + acpigen_write_scope(scope); + acpigen_write_device(acpi_device_name(dev)); + acpigen_write_name_string("_HID", CS35L53_ACPI_HID); + acpigen_write_name_integer("_UID", 0); + acpigen_write_name_string("_DDN", dev->chip_ops->name); + acpigen_write_name_string("_SUB", config->sub); + acpigen_write_STA(acpi_device_status(dev)); + + /* Resources */ + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + acpi_device_write_i2c(&i2c); + /* 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 cs35l53 reset gpio */ + if (config->reset_gpio.pin_count) + acpi_device_write_gpio(&config->reset_gpio); + + acpigen_write_resourcetemplate_footer(); + + /* Add Child Device Properties */ + 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,boost-type", config->boost_type); + + switch (config->boost_type) { + case INTERNAL_BOOST: + if ((config->boost_peak_milliamp > 4500) || + (config->boost_peak_milliamp < 1600) || + (config->boost_peak_milliamp % 50)) { + printk(BIOS_ERR, + "%s: Incorrect boost_peak_milliamp(%d). Using default of 4500 mA\n", + __func__, config->boost_peak_milliamp); + config->boost_peak_milliamp = 4500; + } + acpi_dp_add_integer(dsd, "cirrus,boost-peak-milliamp", + config->boost_peak_milliamp); + acpi_dp_add_integer(dsd, "cirrus,boost-ind-nanohenry", + config->boost_ind_nanohenry); + acpi_dp_add_integer(dsd, "cirrus,boost-cap-microfarad", + config->boost_cap_microfarad); + break; + case EXTERNAL_BOOST: + config->gpio1_output_enable = true; + config->gpio1_src_select = GPIO1_SRC_GPIO; + break; + default: + break; + } + + acpi_dp_add_integer(dsd, "cirrus,asp-sdout-hiz", config->asp_sdout_hiz); + acpi_dp_add_integer(dsd, "cirrus,gpio1-polarity-invert", + config->gpio1_polarity_invert); + acpi_dp_add_integer(dsd, "cirrus,gpio1-output-enable", + config->gpio1_output_enable); + acpi_dp_add_integer(dsd, "cirrus,gpio1-src-select", config->gpio1_src_select); + acpi_dp_add_integer(dsd, "cirrus,gpio2-polarity-invert", + config->gpio2_polarity_invert); + acpi_dp_add_integer(dsd, "cirrus,gpio2-output-enable", + config->gpio2_output_enable); + acpi_dp_add_integer(dsd, "cirrus,gpio2-src-select", config->gpio2_src_select); + + /* Write Device Property Hierarchy */ + acpi_dp_write(dsd); + + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ + + printk(BIOS_INFO, "%s: %s address 0%xh irq %d\n", + acpi_device_path(dev), dev->chip_ops->name, + dev->path.i2c.device, config->irq.pin); +} + +static const char *cs35l53_acpi_name(const struct device *dev) +{ + struct drivers_i2c_cs35l53_config *config = dev->chip_info; + static char name[ACPI_NAME_BUFFER_SIZE]; + + if (config->name) + return config->name; + + snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device); + return name; +} + +static struct device_operations cs35l53_ops = { + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .acpi_name = cs35l53_acpi_name, + .acpi_fill_ssdt = cs35l53_fill_ssdt, +}; + +static void cs35l53_enable(struct device *dev) +{ + dev->ops = &cs35l53_ops; +} + +struct chip_operations drivers_i2c_cs35l53_ops = { + CHIP_NAME("Cirrus Logic CS35L53 Audio Codec") + .enable_dev = cs35l53_enable +};