Skip to content

Commit

Permalink
drivers/i2c/cs35l53: Add driver for generating device in SSDT
Browse files Browse the repository at this point in the history
This patch is adding support for Cirrus Logic CS35l41/CS35l53
smart amplifier. This part is now used in number of new chromebook's
HW designs by several vendors.

This driver uses the ACPI Device Property interface to generate
the required parameters into the _DSD table format expected by
the kernel. For detailed information about these properties, please
check Linux kernel documentation:
/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml

Change-Id: I2cbb1cef89f8d56ee73fab06c68933a2ab8c3606
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitaly.rodionov@cirrus.corp-partner.google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/61448
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
  • Loading branch information
Stefan Binding authored and felixheld committed Mar 18, 2022
1 parent 92dc7d2 commit 7cd5058
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/drivers/i2c/cs35l53/Kconfig
@@ -0,0 +1,3 @@
config DRIVERS_I2C_CS35L53
bool
depends on HAVE_ACPI_TABLES
1 change: 1 addition & 0 deletions src/drivers/i2c/cs35l53/Makefile.inc
@@ -0,0 +1 @@
ramstage-$(CONFIG_DRIVERS_I2C_CS35L53) += cs35l53.c
149 changes: 149 additions & 0 deletions src/drivers/i2c/cs35l53/chip.h
@@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi_device.h>

#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;
};
147 changes: 147 additions & 0 deletions src/drivers/i2c/cs35l53/cs35l53.c
@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi.h>
#include <acpi/acpi_device.h>
#include <acpi/acpigen.h>
#include <console/console.h>
#include <device/i2c_simple.h>
#include <device/device.h>
#include <device/path.h>

#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
};

0 comments on commit 7cd5058

Please sign in to comment.